diff -Nru a/COPYING b/COPYING --- a/COPYING Thu Mar 7 18:17:36 2002 +++ b/COPYING Thu Mar 7 18:17:36 2002 @@ -307,7 +307,7 @@ the "copyright" line and a pointer to where the full notice is found. - Copyright (C) 19yy + Copyright (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 @@ -329,7 +329,7 @@ If the program is interactive, make it output a short notice like this when it starts in an interactive mode: - Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff -Nru a/CREDITS b/CREDITS --- a/CREDITS Thu Mar 7 18:17:39 2002 +++ b/CREDITS Thu Mar 7 18:17:39 2002 @@ -1219,10 +1219,10 @@ S: Germany N: Christoph Hellwig -E: hch@caldera.de E: hch@infradead.org D: misc driver & makefile hacking D: freevxfs driver +D: sysvfs maintainer S: Triftstraße 26 S: 38644 Goslar S: Germany @@ -1579,8 +1579,10 @@ S: Luxembourg N: Gerd Knorr -E: kraxel@goldbach.in-berlin.de -D: SCSI CD-ROM driver hacking, vesafb, v4l, minor bug fixes +W: http://bytesex.org +E: kraxel@bytesex.org +E: kraxel@suse.de +D: video4linux, bttv, vesafb, some scsi, misc fixes N: Harald Koenig E: koenig@tat.physik.uni-tuebingen.de @@ -2672,6 +2674,16 @@ D: Linux-Support, -Mailbox, -Stammtisch D: several improvements to system programs S: Oldenburg +S: Germany + +N: Robert Schwebel +E: robert@schwebel.de +W: http://www.schwebel.de +D: Embedded hacker and book author, +D: AMD Elan support for Linux +S: Pengutronix +S: Braunschweiger Strasse 79 +S: 31134 Hildesheim S: Germany N: Darren Senn diff -Nru a/Documentation/Changes b/Documentation/Changes --- a/Documentation/Changes Thu Mar 7 18:17:46 2002 +++ b/Documentation/Changes Thu Mar 7 18:17:46 2002 @@ -54,6 +54,7 @@ o util-linux 2.10o # fdformat --version o modutils 2.4.2 # insmod -V o e2fsprogs 1.25 # tune2fs +o jfsutils 1.0.14 # fsck.jfs -V o reiserfsprogs 3.x.0j # reiserfsck 2>&1|grep reiserfsprogs o pcmcia-cs 3.1.21 # cardmgr -V o PPP 2.4.0 # pppd --version @@ -106,8 +107,8 @@ your kernel. This change does, however, mean that you need a recent release of binutils. -System utilities -================ +System utililities +================== Architectural changes --------------------- @@ -165,6 +166,16 @@ The latest version of e2fsprogs fixes several bugs in fsck and debugfs. Obviously, it's a good idea to upgrade. +JFSutils +-------- + +The jfsutils package contains the utilities for the file system. +The following utilities are available: +o fsck.jfs - initiate replay of the transaction log, and check + and repair a JFS formatted partition. +o mkfs.jfs - create a JFS formatted partition. +o other file system utilities are also available in this package. + Reiserfsprogs ------------- @@ -302,6 +313,10 @@ E2fsprogs --------- o + +JFSutils +-------- +o Reiserfsprogs ------------- diff -Nru a/Documentation/DocBook/kernel-locking.tmpl b/Documentation/DocBook/kernel-locking.tmpl --- a/Documentation/DocBook/kernel-locking.tmpl Thu Mar 7 18:17:42 2002 +++ b/Documentation/DocBook/kernel-locking.tmpl Thu Mar 7 18:17:42 2002 @@ -788,8 +788,18 @@ - Note that the atomic operations are defined to act as both - read and write barriers on all platforms. + Note that the atomic operations do in general not act as memory + barriers. Instead you can insert a memory barrier before or + after atomic_inc() or + atomic_dec() by inserting + smp_mb__before_atomic_inc(), + smp_mb__after_atomic_inc(), + smp_mb__before_atomic_dec() or + smp_mb__after_atomic_dec() + respectively. The advantage of using those macros instead of + smp_mb() is, that they are cheaper on some + platforms. + diff -Nru a/Documentation/arm/Interrupts b/Documentation/arm/Interrupts --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/arm/Interrupts Thu Mar 7 18:17:46 2002 @@ -0,0 +1,173 @@ +2.5.2-rmk5 +---------- + +This is the first kernel that contains a major shake up of some of the +major architecture-specific subsystems. + +Firstly, it contains some pretty major changes to the way we handle the +MMU TLB. Each MMU TLB variant is now handled completely separately - +we have TLB v3, TLB v4 (without write buffer), TLB v4 (with write buffer), +and finally TLB v4 (with write buffer, with I TLB invalidate entry). +There is more assembly code inside each of these functions, mainly to +allow more flexible TLB handling for the future. + +Secondly, the IRQ subsystem. + +The 2.5 kernels will be having major changes to the way IRQs are handled. +Unfortunately, this means that machine types that touch the irq_desc[] +array (basically all machine types) will break, and this means every +machine type that we currently have. + +Lets take an example. On the Assabet with Neponset, we have: + + GPIO25 IRR:2 + SA1100 ------------> Neponset -----------> SA1111 + IIR:1 + -----------> USAR + IIR:0 + -----------> SMC9196 + +The way stuff currently works, all SA1111 interrupts are mutually +exclusive of each other - if you're processing one interrupt from the +SA1111 and another comes in, you have to wait for that interrupt to +finish processing before you can service the new interrupt. Eg, an +IDE PIO-based interrupt on the SA1111 excludes all other SA1111 and +SMC9196 interrupts until it has finished transferring its multi-sector +data, which can be a long time. Note also that since we loop in the +SA1111 IRQ handler, SA1111 IRQs can hold off SMC9196 IRQs indefinitely. + + +The new approach brings several new ideas... + +We introduce the concept of a "parent" and a "child". For example, +to the Neponset handler, the "parent" is GPIO25, and the "children"d +are SA1111, SMC9196 and USAR. + +We also bring the idea of an IRQ "chip" (mainly to reduce the size of +the irqdesc array). This doesn't have to be a real "IC"; indeed the +SA11x0 IRQs are handled by two separate "chip" structures, one for +GPIO0-10, and another for all the rest. It is just a container for +the various operations (maybe this'll change to a better name). +This structure has the following operations: + +struct irqchip { + /* + * Acknowledge the IRQ. + * If this is a level-based IRQ, then it is expected to mask the IRQ + * as well. + */ + void (*ack)(unsigned int irq); + /* + * Mask the IRQ in hardware. + */ + void (*mask)(unsigned int irq); + /* + * Unmask the IRQ in hardware. + */ + void (*unmask)(unsigned int irq); + /* + * Re-run the IRQ + */ + void (*rerun)(unsigned int irq); + /* + * Set the type of the IRQ. + */ + int (*type)(unsigned int irq, unsigned int, type); +}; + +ack - required. May be the same function as mask for IRQs + handled by do_level_IRQ. +mask - required. +unmask - required. +rerun - optional. Not required if you're using do_level_IRQ for all + IRQs that use this 'irqchip'. Generally expected to re-trigger + the hardware IRQ if possible. If not, may call the handler + directly. +type - optional. If you don't support changing the type of an IRQ, + it should be null so people can detect if they are unable to + set the IRQ type. + +For each IRQ, we keep the following information: + + - "disable" depth (number of disable_irq()s without enable_irq()s) + - flags indicating what we can do with this IRQ (valid, probe, + noautounmask) as before + - status of the IRQ (probing, enable, etc) + - chip + - per-IRQ handler + - irqaction structure list + +The handler can be one of the 3 standard handlers - "level", "edge" and +"simple", or your own specific handler if you need to do something special. + +The "level" handler is what we currently have - its pretty simple. +"edge" knows about the brokenness of such IRQ implementations - that you +need to leave the hardware IRQ enabled while processing it, and queueing +further IRQ events should the IRQ happen again while processing. The +"simple" handler is very basic, and does not perform any hardware +manipulation, nor state tracking. This is useful for things like the +SMC9196 and USAR above. + +So, what's changed? + +1. Machine implementations must not write to the irqdesc array. + +2. New functions to manipulate the irqdesc array. The first 4 are expected + to be useful only to machine specific code. The last is recommended to + only be used by machine specific code, but may be used in drivers if + absolutely necessary. + + set_irq_chip(irq,chip) + + Set the mask/unmask methods for handling this IRQ + + set_irq_handler(irq,handler) + + Set the handler for this IRQ (level, edge, simple) + + set_irq_chained_handler(irq,handler) + + Set a "chained" handler for this IRQ - automatically + enables this IRQ (eg, Neponset and SA1111 handlers). + + set_irq_flags(irq,flags) + + Set the valid/probe/noautoenable flags. + + set_irq_type(irq,type) + + Set active the IRQ edge(s)/level. This replaces the + SA1111 INTPOL manipulation, and the set_GPIO_IRQ_edge() + function. Type should be one of the following: + + #define IRQT_NOEDGE (0) + #define IRQT_RISING (__IRQT_RISEDGE) + #define IRQT_FALLING (__IRQT_FALEDGE) + #define IRQT_BOTHEDGE (__IRQT_RISEDGE|__IRQT_FALEDGE) + #define IRQT_LOW (__IRQT_LOWLVL) + #define IRQT_HIGH (__IRQT_HIGHLVL) + +3. set_GPIO_IRQ_edge() is obsolete, and should be replaced by set_irq_type. + +4. Direct access to SA1111 INTPOL is depreciated. Use set_irq_type instead. + +5. A handler is expected to perform any necessary acknowledgement of the + parent IRQ via the correct chip specific function. For instance, if + the SA1111 is directly connected to a SA1110 GPIO, then you should + acknowledge the SA1110 IRQ each time you re-read the SA1111 IRQ status. + +6. For any child which doesn't have its own IRQ enable/disable controls + (eg, SMC9196), the handler must mask or acknowledge the parent IRQ + while the child handler is called, and the child handler should be the + "simple" handler (not "edge" nor "level"). After the handler completes, + the parent IRQ should be unmasked, and the status of all children must + be re-checked for pending events. (see the Neponset IRQ handler for + details). + +7. fixup_irq() is gone, as is include/asm-arm/arch-*/irq.h + +Please note that this will not solve all problems - some of them are +hardware based. Mixing level-based and edge-based IRQs on the same +parent signal (eg neponset) is one such area where a software based +solution can't provide the full answer to low IRQ latency. + diff -Nru a/Documentation/arm/mem_alignment b/Documentation/arm/mem_alignment --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/arm/mem_alignment Thu Mar 7 18:17:46 2002 @@ -0,0 +1,58 @@ +Too many problems poped up because of unnoticed misaligned memory access in +kernel code lately. Therefore the alignment fixup is now unconditionally +configured in for SA11x0 based targets. According to Alan Cox, this is a +bad idea to configure it out, but Russell King has some good reasons for +doing so on some f***ed up ARM architectures like the EBSA110. However +this is not the case on many design I'm aware of, like all SA11x0 based +ones. + +Of course this is a bad idea to rely on the alignment trap to perform +unaligned memory access in general. If those access are predictable, you +are better to use the macros provided by include/asm/unaligned.h. The +alignment trap can fixup misaligned access for the exception cases, but at +a high performance cost. It better be rare. + +Now for user space applications, it is possible to configure the alignment +trap to SIGBUS any code performing unaligned access (good for debugging bad +code), or even fixup the access by software like for kernel code. The later +mode isn't recommended for performance reasons (just think about the +floating point emulation that works about the same way). Fix your code +instead! + +Please note that randomly changing the behaviour without good thought is +real bad - it changes the behaviour of all unaligned instructions in user +space, and might cause programs to fail unexpectedly. + +To change the alignment trap behavior, simply echo a number into +/proc/sys/debug/alignment. The number is made up from various bits: + +bit behavior when set +--- ----------------- + +0 A user process performing an unaligned memory access + will cause the kernel to print a message indicating + process name, pid, pc, instruction, address, and the + fault code. + +1 The kernel will attempt to fix up the user process + performing the unaligned access. This is of course + slow (think about the floating point emulator) and + not recommended for production use. + +2 The kernel will send a SIGBUS signal to the user process + performing the unaligned access. + +Note that not all combinations are supported - only values 0 through 5. +(6 and 7 don't make sense). + +For example, the following will turn on the warnings, but without +fixing up or sending SIGBUS signals: + + echo 1 > /proc/sys/debug/alignment + +You can also read the content of the same file to get statistical +information on unaligned access occurences plus the current mode of +operation for user space code. + + +Nicolas Pitre, Mar 13, 2001. Modified Russell King, Nov 30, 2001. diff -Nru a/Documentation/driver-model.txt b/Documentation/driver-model.txt --- a/Documentation/driver-model.txt Thu Mar 7 18:17:41 2002 +++ b/Documentation/driver-model.txt Thu Mar 7 18:17:41 2002 @@ -118,7 +118,18 @@ By virtue of having a complete hierarchical view of all the devices in the system, exporting a complete hierarchical view to userspace becomes relatively -easy. +easy. This has been accomplished by implementing a special purpose virtual +file system named driverfs. It is hence possible for the user to mount the +whole driverfs on a particular mount point in the unified UNIX file hierarchy. + +This can be done permanently by providing the following entry into the +/dev/fstab (under the provision that the mount point does exist, of course): + +none /devices driverfs defaults 0 0 + +Or by hand on the command line: + +~: mount -t driverfs none /devices Whenever a device is inserted into the tree, a directory is created for it. This directory may be populated at each layer of discovery - the global layer, @@ -342,6 +353,8 @@ user requests the system to suspend, it will walk the device tree, as exported via driverfs, and tell each device to go to sleep. It will do this multiple times based on what the system policy is. + +[ FIXME: URL pointer to the corresponding utility is missing here! ] Device resume should happen in the same manner when the system awakens. diff -Nru a/Documentation/filesystems/00-INDEX b/Documentation/filesystems/00-INDEX --- a/Documentation/filesystems/00-INDEX Thu Mar 7 18:17:44 2002 +++ b/Documentation/filesystems/00-INDEX Thu Mar 7 18:17:44 2002 @@ -22,6 +22,8 @@ - info and mount options for the OS/2 HPFS. isofs.txt - info and mount options for the ISO 9660 (CDROM) filesystem. +jfs.txt + - info and mount options for the JFS filesystem. ncpfs.txt - info on Novell Netware(tm) filesystem using NCP protocol. ntfs.txt diff -Nru a/Documentation/filesystems/cramfs.txt b/Documentation/filesystems/cramfs.txt --- a/Documentation/filesystems/cramfs.txt Thu Mar 7 18:17:46 2002 +++ b/Documentation/filesystems/cramfs.txt Thu Mar 7 18:17:46 2002 @@ -10,7 +10,7 @@ You can't write to a cramfs filesystem (making it compressible and compact also makes it _very_ hard to update on-the-fly), so you have to -create the disk image with the "mkcramfs" utility in scripts/cramfs. +create the disk image with the "mkcramfs" utility. Usage Notes @@ -19,9 +19,7 @@ File sizes are limited to less than 16MB. Maximum filesystem size is a little over 256MB. (The last file on the -filesystem is allowed to extend past 256MB.) (Comments in mkcramfs.c -suggest that ROM sizes may be limited to 64MB, though that's not a -limitation in cramfs code.) +filesystem is allowed to extend past 256MB.) Only the low 8 bits of gid are stored. The current version of mkcramfs simply truncates to 8 bits, which is a potential security @@ -48,18 +46,28 @@ For /usr/share/magic ------------------- +-------------------- -0 long 0x28cd3d45 Linux cramfs ->4 long x size %d ->8 long x flags 0x%x ->12 long x future 0x%x +0 ulelong 0x28cd3d45 Linux cramfs offset 0 +>4 ulelong x size %d +>8 ulelong x flags 0x%x +>12 ulelong x future 0x%x >16 string >\0 signature "%.16s" ->32 long x fsid.crc 0x%x ->36 long x fsid.edition %d ->40 long x fsid.blocks %d ->44 long x fsid.files %d +>32 ulelong x fsid.crc 0x%x +>36 ulelong x fsid.edition %d +>40 ulelong x fsid.blocks %d +>44 ulelong x fsid.files %d >48 string >\0 name "%.16s" +512 ulelong 0x28cd3d45 Linux cramfs offset 512 +>516 ulelong x size %d +>520 ulelong x flags 0x%x +>524 ulelong x future 0x%x +>528 string >\0 signature "%.16s" +>544 ulelong x fsid.crc 0x%x +>548 ulelong x fsid.edition %d +>552 ulelong x fsid.blocks %d +>556 ulelong x fsid.files %d +>560 string >\0 name "%.16s" Hacker Notes diff -Nru a/Documentation/filesystems/jfs.txt b/Documentation/filesystems/jfs.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/filesystems/jfs.txt Thu Mar 7 18:17:47 2002 @@ -0,0 +1,136 @@ +IBM's Journaled File System (JFS) for Linux version 1.0.15 +Team members +Steve Best sbest@us.ibm.com +Dave Kleikamp shaggy@austin.ibm.com +Barry Arndt barndt@us.ibm.com +Christoph Hellwig hch@caldera.de + + +Release February 15, 2002 (version 1.0.15) + +This is our fifty-third release of IBM's Enterprise JFS technology port to Linux. +Beta 1 was release 0.1.0 on 12/8/2000, Beta 2 was release 0.2.0 on 3/7/2001, +Beta 3 was release 0.3.0 on 4/30/2001, and release 1.0.0 on 6/28/2001. + +The changelog.jfs file contains detailed information of changes done in each source +code drop. + +JFS has a source tree that can be built on 2.4.3 - 2.4.17 and 2.5.4 kernel.org +source trees. + +Our current goal on the 2.5.x series of the kernel is to update to the latest +2.5.x version and only support the latest version of this kernel. +This will change when the distros start shipping the 2.5.x series of the kernel. + +Our current goal on the 2.4.x series of the kernel is to continue to support +all of the kernels in this series as we do today. + +There is an anonymous cvs access available for the JFS tree. The steps below are +what is needed to pull the JFS cvs tree from the oss.software.ibm.com server. + +id anoncvs +password anoncvs + +To checkout 2.4.x series of the JFS files do the following: +CVSROOT should be set to :pserver:anoncvs@oss.software.ibm.com:/usr/cvs/jfs +cvs checkout linux24 + +To checkout 2.5.2 series of the JFS files do the following: +CVSROOT should be set to :pserver:anoncvs@oss.software.ibm.com:/usr/cvs/jfs +cvs checkout linux25 + +To checkout the JFS utilities do the following: +CVSROOT should be set to :pserver:anoncvs@oss.software.ibm.com:/usr/cvs/jfs +cvs checkout jfsutils + +The cvs tree contains the latest changes being done to JFS. To receive notification +of commits to the cvs tree, please send e-mail to linuxjfs@us.ibm.com stating that +you would like notifications sent to you. + +The jfs-2.4-1.0.15-patch.tar.gz is the easiest way to get the latest file system +source code on your system. There are also patch files that can move your jfs source +code from one release to another. If you have release 1.0.14 and would like to move +to release 1.0.15 the patch file named jfs-2.4-1_0_14-to-1_0_15-patch.gz will do that. + +The jfs-2.4-1.0.15-patch.tar.gz file contains a readme and patch files for different +levels of the 2.4 kernel. Please see the README in the jfs-2.4-1.0.15-patch.tar.gz +file for help on applying the two patch files. + + +The following files in the kernel source tree have been changed so JFS can be built. +The jfs-2.4-1.0.15.tar.gz source tar ball contains each of the files below with +the extension of the kernel level it is associated with. As an example, there are now +four Config.in files named Config.in-2.4.0, Config.in-2.4.5, Config.in-2.4.7 and +Config.in-2.4.17. + + +If you use the jfs-2.4-1.0.15.tar.gz to build JFS you must rename each of the +kernel files to the file names listed below. The standard kernel from www.kernel.org +is the source of the kernel files that are included in the jfs tar file. + + +In sub dir fs Config.in, Makefile +In sub dir fs/nls Config.in +In sub dir Documentation Configure.help, Changes +In sub dir Documentation/filesystems 00-INDEX +In sub dir linux MAINTAINERS + +Please backup the above files before the JFS tar file is added to the kernel source +tree. All JFS files are located in the include/linux/jfs or fs/jfs sub dirs. + +Our development team has used the Linux kernel levels 2.4.3 - 2.4.17 kernels +with gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release) +for our port so far. A goal of the JFS team is to have JFS run on all architectures +that Linux supports, there is no architecture specific code in JFS. JFS has been run +on the following architectures (x86, PowerPC, Alpha, s/390, ARM) so far. + +To make JFS build, during the "make config" step of building the kernel answer y to +the Prompt for development and/or incomplete code/drivers in the Code maturity level +options section. In the Filesystems section use the m for the answer to +JFS filesystem support (experimental) (CONFIG_JFS_FS) [Y/m/n?] + + +Build in /usr/src/linux with the command: + + +make modules +make modules_install + +If you rebuild jfs.o after having mounted and unmounted a partition, "modprobe -r jfs" +will unload the old module. + +For the file system debugging messages are being written to /var/log/messages. + +Please see the readme in the utilities package for information about building +the JFS utilities. + +JFS TODO list: + +Plans for our near term development items + + - get defrag capabilities operational in the FS + - get extendfs capabilities operational in the FS + - test EXTENDFS utility, for growing JFS partitions + - test defrag utility, calls file system to defrag the file system. + - add support for block sizes (512,1024,2048) + - add support for logfile on dedicated partition + + +Longer term work items + + - get access control list functionality operational + - get extended attributes functionality operational + - add quota support + +Please send bugs, comments, cards and letters to linuxjfs@us.ibm.com. + +The JFS mailing list can be subscribed to by using the link labeled "Mail list Subscribe" +at our web page http://oss.software.ibm.com/jfs/. + + + + + + + + diff -Nru a/Documentation/filesystems/porting b/Documentation/filesystems/porting --- a/Documentation/filesystems/porting Thu Mar 7 18:17:41 2002 +++ b/Documentation/filesystems/porting Thu Mar 7 18:17:41 2002 @@ -81,11 +81,12 @@ [mandatory] ->lookup(), ->truncate(), ->create(), ->unlink(), ->mknod(), ->mkdir(), -->rmdir(), ->link(), ->symlink() and ->rename() are called without BKL now. -Grab it on the entry, drop upon return - that will guarantee the same -locking you used to have. If your method or its parts do not need BKL - -better yet, now you can shift lock_kernel() / unlock_kernel() so that -they would protect exactly what needs to be protected. +->rmdir(), ->link(), ->lseek(), ->symlink() and ->rename() are called +without BKL now. Grab it on the entry, drop upon return - that will +guarantee the same locking you used to have. If your method or its +parts do not need BKL - better yet, now you can shift lock_kernel() and +unlock_kernel() so that they would protect exactly what needs to be +protected. --- [informational] diff -Nru a/Documentation/kbuild/config-language.txt b/Documentation/kbuild/config-language.txt --- a/Documentation/kbuild/config-language.txt Thu Mar 7 18:17:43 2002 +++ b/Documentation/kbuild/config-language.txt Thu Mar 7 18:17:43 2002 @@ -30,7 +30,7 @@ scripts/Configure make config, make oldconfig scripts/Menuconfig make menuconfig scripts/tkparse make xconfig - mconfig (in development) + mconfig ftp.kernel.org/pub/linux/kernel/people/hch/mconfig/ 'Configure' is a bash script which interprets Config.in files by sourcing them. Some of the Config Language commands are native bash commands; @@ -52,9 +52,6 @@ into an internal syntax tree and then hands the syntax tree to one of several user-interface front ends. -This document describes the behaviour of all four interpreters, even though -mconfig has not been released at the time of writing. - === Statements @@ -489,7 +486,7 @@ Configure: implemented Menuconfig: implemented XConfig: implemented -mconfig: not implemented +mconfig: implemented Example: diff -Nru a/Documentation/networking/3c359.txt b/Documentation/networking/3c359.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/networking/3c359.txt Thu Mar 7 18:17:47 2002 @@ -0,0 +1,58 @@ + +3COM PCI TOKEN LINK VELOCITY XL TOKEN RING CARDS README + +Release 0.9.0 - Release + Jul 17th 2000 Mike Phillips + + 1.2.0 - Final + Feb 17th 2002 Mike Phillips + Updated for submission to the 2.4.x kernel. + +Thanks: + Terry Murphy from 3Com for tech docs and support, + Adam D. Ligas for testing the driver. + +Note: + This driver will NOT work with the 3C339 Token Ring cards, you need +to use the tms380 driver instead. + +Options: + +The driver accepts three options: ringspeed, pkt_buf_sz and message_level. + +These options can be specified differently for each card found. + +ringspeed: Has one of three settings 0 (default), 4 or 16. 0 will +make the card autosense the ringspeed and join at the appropriate speed, +this will be the default option for most people. 4 or 16 allow you to +explicitly force the card to operate at a certain speed. The card will fail +if you try to insert it at the wrong speed. (Although some hubs will allow +this so be *very* careful). The main purpose for explicitly setting the ring +speed is for when the card is first on the ring. In autosense mode, if the card +cannot detect any active monitors on the ring it will open at the same speed as +its last opening. This can be hazardous if this speed does not match the speed +you want the ring to operate at. + +pkt_buf_sz: This is this initial receive buffer allocation size. This will +default to 4096 if no value is entered. You may increase performance of the +driver by setting this to a value larger than the network packet size, although +the driver now re-sizes buffers based on MTU settings as well. + +message_level: Controls level of messages created by the driver. Defaults to 0: +which only displays start-up and critical messages. Presently any non-zero +value will display all soft messages as well. NB This does not turn +debuging messages on, that must be done by modified the source code. + +Variable MTU size: + +The driver can handle a MTU size upto either 4500 or 18000 depending upon +ring speed. The driver also changes the size of the receive buffers as part +of the mtu re-sizing, so if you set mtu = 18000, you will need to be able +to allocate 16 * (sk_buff with 18000 buffer size) call it 18500 bytes per ring +position = 296,000 bytes of memory space, plus of course anything +necessary for the tx sk_buff's. Remember this is per card, so if you are +building routers, gateway's etc, you could start to use a lot of memory +real fast. + +2/17/02 Mike Phillips + diff -Nru a/Documentation/networking/arcnet-hardware.txt b/Documentation/networking/arcnet-hardware.txt --- a/Documentation/networking/arcnet-hardware.txt Thu Mar 7 18:17:44 2002 +++ b/Documentation/networking/arcnet-hardware.txt Thu Mar 7 18:17:44 2002 @@ -2256,7 +2256,7 @@ When I got my two cards, one of them had this switch set to "enhanced". That card didn't work at all, it wasn't even recognized by the driver. The other card had this switch set to "compatible" and it behaved absolutely normally. I -guess that the switch on one of the cards, must have been changed accidently +guess that the switch on one of the cards, must have been changed accidentally when the card was taken out of its former host. The question remains unanswered, what is the purpose of the "enhanced" position? diff -Nru a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt --- a/Documentation/networking/bonding.txt Thu Mar 7 18:17:40 2002 +++ b/Documentation/networking/bonding.txt Thu Mar 7 18:17:40 2002 @@ -503,7 +503,7 @@ Resources and links =================== -Current developement on this driver is posted to: +Current development on this driver is posted to: - http://www.sourceforge.net/projects/bonding/ Donald Becker's Ethernet Drivers and diag programs may be found at : diff -Nru a/Documentation/networking/driver.txt b/Documentation/networking/driver.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/networking/driver.txt Thu Mar 7 18:17:47 2002 @@ -0,0 +1,84 @@ +Documents about softnet driver issues in general can be found +at: + + http://www.firstfloor.org/~andi/softnet/ + +Transmit path guidelines: + +1) The hard_start_xmit method must never return '1' under any + normal circumstances. It is considered a hard error unless + there is no way your device can tell ahead of time when it's + transmit function will become busy. + + Instead it must maintain the queue properly. For example, + for a driver implementing scatter-gather this means: + + static int drv_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) + { + struct drv *dp = dev->priv; + + lock_tx(dp); + ... + /* This is a hard error log it. */ + if (TX_BUFFS_AVAIL(dp) <= (skb_shinfo(skb)->nr_frags + 1)) { + netif_stop_queue(dev); + unlock_tx(dp); + printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n", + dev->name); + return 1; + } + + ... queue packet to card ... + ... update tx consumer index ... + + if (TX_BUFFS_AVAIL(dp) <= (MAX_SKB_FRAGS + 1)) + netif_stop_queue(dev); + + ... + unlock_tx(dp); + ... + } + + And then at the end of your TX reclaimation event handling: + + if (netif_queue_stopped(dp->dev) && + TX_BUFFS_AVAIL(dp) > (MAX_SKB_FRAGS + 1)) + netif_wake_queue(dp->dev); + + For a non-scatter-gather supporting card, the three tests simply become: + + /* This is a hard error log it. */ + if (TX_BUFFS_AVAIL(dp) <= 0) + + and: + + if (TX_BUFFS_AVAIL(dp) == 0) + + and: + + if (netif_queue_stopped(dp->dev) && + TX_BUFFS_AVAIL(dp) > 0) + netif_wake_queue(dp->dev); + +2) Do not forget to update netdev->trans_start to jiffies after + each new tx packet is given to the hardware. + +3) Do not forget that once you return 0 from your hard_start_xmit + method, it is your driver's responsibility to free up the SKB + and in some finite amount of time. + + For example, this means that it is not allowed for your TX + mitigation scheme to let TX packets "hang out" in the TX + ring unreclaimed forever if no new TX packets are sent. + This error can deadlock sockets waiting for send buffer room + to be freed up. + + If you return 1 from the hard_start_xmit method, you must not keep + any reference to that SKB and you must not attempt to free it up. + +Probing guidelines: + +1) Any hardware layer address you obtain for your device should + be verified. For example, for ethernet check it with + linux/etherdevice.h:is_valid_ether_addr() diff -Nru a/Documentation/networking/e1000.txt b/Documentation/networking/e1000.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/networking/e1000.txt Thu Mar 7 18:17:46 2002 @@ -0,0 +1,245 @@ +Linux* Base Driver for the Intel(R) PRO/1000 Family of Adapters +=============================================================== + +February 5, 2002 + + +Contents +======== + +- In This Release +- Supported Adapters +- Command Line Parameters +- Speed and Duplex Configuration +- Known Issues +- Support + + +In This Release +=============== + +This file describes the Linux* Base Driver for the Intel(R) PRO/1000 Family +of Adapters, version 4.2.x. +This driver includes support for Itanium(TM)-based systems. + +This release version includes the following: + + - support for the ethtool 1.4 interface. A third-party application can use + the ethtool interface to get and set driver parameters. + + - the zero copy feature. Zero copy provides faster information throughput. + By default, this feature is enabled if using a kernel that supports it. + Zero copy is not supported on the original PWLA8490 (plain) adapter. + + +Supported Adapters +================== + +The following Intel network adapters are compatible with the drivers in this +release: + + Controller Adapter Name Board IDs + ---------- ------------ --------- + + 82542 PRO/1000 Gigabit Server Adapter 700262-xxx, 717037-xxx + + 82543 PRO/1000 F Server Adapter 738640-xxx, A38888-xxx, + A06512-xxx + + 82543 PRO/1000 T Server Adapter A19845-xxx, A33948-xxx + + 82544 PRO/1000 XT Server Adapter A51580-xxx + + 82544 PRO/1000 XF Server Adapter A50484-xxx + + 82544 PRO/1000 T Desktop Adapter A62947-xxx + + +To verify your Intel adapter is supported, find the board ID number on the +adapter. Look for a label that has a barcode and a number in the format of +123456-001 (six digits hyphen three digits). Match this to the list of +numbers above. + +For more information on how to identify your adapter, go to the Adapter & +Driver ID Guide at: + + http://support.intel.com/support/network/adapter/pro100/21397.htm + +For the latest Intel network drivers for Linux, go to: + + http://appsr.intel.com/scripts-df/support_intel.asp + + +Command Line Parameters +======================= + +If the driver is built as a module, the following parameters are used by +entering them on the command line with the modprobe or insmod command. +For example, with two PRO/1000 PCI adapters, entering: + + insmod e1000 TxDescriptors=80,128 + +loads the e1000 driver with 80 TX resources for the first adapter and 128 TX +resources for the second adapter. + +For more information about the AutoNeg, Duplex, and Speed parameters, see the +"Speed and Duplex Configuration" section in this document. + + +AutoNeg (Intel PRO/1000 T and PRO/1000 XT server adapters only) +Valid Range: 0-0x0F, 0x20-0x2F +Default Value: 0x2F + This parameter is a bit mask that specifies which speed and duplex + settings the board advertises. When this parameter is used, the Speed and + Duplex parameters must not be specified. + +Duplex (Intel PRO/1000 T and PRO/1000 XT server adapters only) +Valid Range: 0-2 (0=auto-negotiate, 1=half, 2=full) +Default Value: 0 + Defines the direction in which data is allowed to flow. Can by either one + or two-directional. If both Duplex and the link partner are set to auto- + negotiate, the board auto-detects the correct duplex. If the link partner + is forced (either full or half), Duplex defaults to half-duplex. + +FlowControl +Valid Range: 0-3 (0=none, 1=Rx only, 2=Tx only, 3=Rx&Tx) +Default: Read flow control settings from the EEPROM + This parameter controls the automatic generation(Tx) and response(Rx) to + Ethernet PAUSE frames. + +RxDescriptors +Valid Range: 80-256 for 82542 and 82543-based adapters + 80-4096 for 82544-based adapters +Default Value: 256 + This value is the number of receive descriptors allocated by the driver. + Increasing this value allows the driver to buffer more incoming packets. + Each descriptor is 16 bytes. A receive buffer is also allocated for each + descriptor and can be either 2048, 4096, 8192, or 16384 bytes, depending + on the MTU setting. + +RxIntDelay +Valid Range: 0-65535 (0=off) +Default Value: 64 + This value delays the generation of receive interrupts in units of 1.024 + microseconds. Receive interrupt reduction can improve CPU efficiency if + properly tuned for specific network traffic. Increasing this value adds + extra latency to frame reception and can end up decreasing the throughput + of TCP traffic. If the system is reporting dropped receives, this value + may be set too high, causing the driver to run out of available receive + descriptors. + +Speed (Intel PRO/1000 T and PRO/1000 XT server adapters only) +Valid Settings: 0, 10, 100, 1000 +Default Value: 0 (auto-negotiate at all supported speeds) + Speed forces the line speed to the specified value in megabits per second + (Mbps). If this parameter is not specified or is set to 0 and the link + partner is set to auto-negotiate, the board will auto-detect the correct + speed. Duplex must also be set when Speed is set to either 10 or 100. + +TxDescriptors +Valid Range: 80-256 for 82542 and 82543-based adapters + 80-4096 for 82544-based adapters +Default Value: 256 + This value is the number of transmit descriptors allocated by the driver. + Increasing this value allows the driver to queue more transmits. Each + descriptor is 16 bytes. + +TxIntDelay +Valid Range: 0-65535 (0=off) +Default Value: 64 + This value delays the generation of transmit interrupts in units of 1.024 + microseconds. Transmit interrupt reduction can improve CPU efficiency if + properly tuned for specific network traffic. If the system is reporting + dropped transmits, this value may be set too high causing the driver to + run out of available transmit descriptors. + +XsumRX (not available on the PRO/1000 Gigabit Server Adapter) +Valid Range: 0-1 +Default Value: 1 + A value of '1' indicates that the driver should enable IP checksum + offload for received packets (both UDP and TCP) to the adapter hardware. + + +Speed and Duplex Configuration +============================== + +Three keywords are used to control the speed and duplex configuration of the +PRO/1000 T and PRO/1000 XT server adapters. These keywords are Speed, Duplex, +and AutoNeg. + +If the board uses a fiber interface, these keywords are ignored, and the +fiber interface board only links at 1000 Mbps full-duplex. + +For copper-based boards, the keywords interact as follows: + + The default operation is auto-negotiate. The board advertises all supported + speed and duplex combinations, and it links at the highest common speed and + duplex mode IF the link partner is set to auto-negotiate. + + If Speed = 1000, limited auto-negotiation is enabled and only 1000 Mbps is + advertised (The 1000BaseT spec requires auto-negotiation.) + + If Speed = 10 or 100, then both Speed and Duplex must be set. Auto- + negotiation is disabled, and the AutoNeg parameter is ignored. Partner MUST + also be forced. + +The AutoNeg parameter is used when more control is required over the auto- +negotiation process. When this parameter is used, Speed and Duplex must not +be specified. This parameter is a bitmap that specifies which speed and +duplex settings are advertised to the link partner. + +Bit 7 6 5 4 3 2 1 0 +Speed (Mbps) N/A N/A 1000 N/A 100 100 10 10 +Duplex Full Full Half Full Half + +Note that setting AutoNeg does not guarantee that the board will link at the +highest specified speed or duplex mode, but the board will link at the +highest possible speed/duplex of the link partner IF the link partner is also +set to auto-negotiate. If the link partner is forced speed/duplex, the +adapter MUST be forced to the same speed/duplex. + + +Known Issues +============ + + Driver Hangs Under Heavy Traffic Loads + -------------------------------------- + + Intel is aware that previously released e1000 drivers may hang under very + specific types of heavy traffic loads. This version includes a workaround + that resets the adapter automatically if a hang condition is detected. This + workaround ensures network traffic flow is not affected when a hang occurs. + + Jumbo Frames System Requirement + ------------------------------- + + Memory allocation failures have been observed on Linux systems with 64 MB + of RAM or less that are running Jumbo Frames. If you are using Jumbo + Frames, your system may require more than the advertised minimum + requirement of 64 MB of system memory. + + +Support +======= + +For general information and support, go to the Intel support website at: + + http://support.intel.com + +If an issue is identified with the released source code on the supported +kernel with a supported adapter, email the specific information related to +the issue to linux.nics@intel.com. + + +License +======= + +This software program is released under the terms of a license agreement +between you ('Licensee') and Intel. Do not use or load this software or any +associated materials (collectively, the 'Software') until you have carefully +read the full terms and conditions of the LICENSE located in this software +package. By loading or using the Software, you agree to the terms of this +Agreement. If you do not agree with the terms of this Agreement, do not +install or use the Software. + +* Other names and brands may be claimed as the property of others. diff -Nru a/Documentation/networking/generic-hdlc.txt b/Documentation/networking/generic-hdlc.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/networking/generic-hdlc.txt Thu Mar 7 18:17:46 2002 @@ -0,0 +1,115 @@ +Generic HDLC layer for Linux kernel 2.4/2.5 +Krzysztof Halasa +May, 2001 + + +Generic HDLC layer currently supports: +- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP), +- raw HDLC (IPv4 only), +- Cisco HDLC, +- PPP (uses syncppp.c), +- X.25 (uses X.25 routines). + +There are hardware drivers for the following cards: +- C101 by Moxa Technologies Co., Ltd. +- RISCom/N2 by SDL Communications Inc. +- and others, some not in the official kernel. + +Make sure the hdlc.o and the hardware driver are loaded. It should +create a number of "hdlc" (hdlc0 etc) network devices, one for each +WAN port. You'll need the "sethdlc" utility, get it from: + http://hq.pm.waw.pl/hdlc/ + +Compile sethdlc.c utility: + gcc -O2 -Wall -o sethdlc sethdlc.c +Make sure you're using a correct version of sethdlc for your kernel. + +Use sethdlc to set physical interface, clock rate, HDLC mode used, +and add any required PVCs if using Frame Relay. +Usually you want something like: + + sethdlc hdlc0 clock int rate 128000 + sethdlc hdlc0 cisco interval 10 timeout 25 +or + sethdlc hdlc0 rs232 clock ext + sethdlc fr lmi ansi + sethdlc create 99 + +In Frame Relay mode, ifconfig master hdlc device up (without assigning +any IP address to it) before using pvc devices. + + +Setting interface: + +* v35 | rs232 | x21 | t1 | e1 - sets physical interface for a given port + if the card has software-selectable interfaces + loopback - activate hardware loopback (for testing only) +* clock ext - external clock (uses DTE RX and TX clock) +* clock int - internal clock (provides clock signal on DCE clock output) +* clock txint - TX internal, RX external (provides TX clock on DCE output) +* clock txfromrx - TX clock derived from RX clock (TX clock on DCE output) +* rate - sets clock rate in bps (not required for external clock or + for txfromrx) + +Setting protocol: + +* hdlc - sets raw HDLC (IP-only) mode + nrz / nrzi / fm-mark / fm-space / manchester - sets transmission code + no-parity / crc16 / crc16-pr0 (CRC16 with preset zeros) / crc32-itu + crc16-itu (CRC16 with ITU-T polynomial) / crc16-itu-pr0 - sets parity + +* cisco - sets Cisco HDLC mode (IP, IPv6 and IPX supported) + interval - time in seconds between keepalive packets + timeout - time in seconds after last received keepalive packet before + we assume the link is down + +* ppp - sets synchronous PPP mode + +* x25 - sets X.25 mode + +* fr - Frame Relay mode + lmi ansi / ccitt / none - LMI (link management) type + dce - Frame Relay DCE (network) side LMI instead of default DTE (user). + It has nothing to do with clocks! + t391 - link integrity verification polling timer (in seconds) - user + t392 - polling verification timer (in seconds) - network + n391 - full status polling counter - user + n392 - error threshold - both user and network + n393 - monitored events count - both user and network + +* create | delete n - FR only - adds / deletes PVC interface with DLCI #n. + + + + +Board-specific issues +--------------------- + +n2.o and c101.o need parameters to work (note double quotes): + + insmod n2 hw='"io,irq,ram,ports[:io,irq,...]"' +example: + insmod n2 hw='"0x300,10,0xD0000,01"' + +or + insmod c101 hw='"irq,ram[:irq,...]" +example: + insmod c101 hw='"9,0xdc000"' + +If built into the kernel, these drivers need kernel (command line) parameters: + n2=io,irq,ram,ports:... +or + c101=irq,ram:... + + + +If you have a problem with N2 or C101 card, you can issue the "private" +command to see port's packet descriptor rings: + + sethdlc hdlc0 private + +The hardware driver have to be build with CONFIG_HDLC_DEBUG_RINGS. +Attaching this info to bug reports would be helpful. Anyway, let me know +if you have problems using this. + +For patches and other info look at http://hq.pm.waw.pl/hdlc/ diff -Nru a/Documentation/pci.txt b/Documentation/pci.txt --- a/Documentation/pci.txt Thu Mar 7 18:17:44 2002 +++ b/Documentation/pci.txt Thu Mar 7 18:17:44 2002 @@ -156,6 +156,11 @@ which enables the bus master bit in PCI_COMMAND register and also fixes the latency timer value if it's set to something bogus by the BIOS. + If you want to use the PCI Memory-Write-Invalidate transaction, +call pci_set_mwi(). This enables bit PCI_COMMAND bit for Mem-Wr-Inval +and also ensures that the cache line size register is set correctly. +Make sure to check the return value of pci_set_mwi(), not all architectures +may support Memory-Write-Invalidate. 4. How to access PCI config space ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -202,6 +207,8 @@ pci_resource_len() Returns the byte length of a PCI region pci_set_drvdata() Set private driver data pointer for a pci_dev pci_get_drvdata() Return private driver data pointer for a pci_dev +pci_set_mwi() Enable Memory-Write-Invalidate transactions. +pci_clear_mwi() Disable Memory-Write-Invalidate transactions. 7. Miscellaneous hints diff -Nru a/Documentation/power/pci.txt b/Documentation/power/pci.txt --- a/Documentation/power/pci.txt Thu Mar 7 18:17:41 2002 +++ b/Documentation/power/pci.txt Thu Mar 7 18:17:41 2002 @@ -63,7 +63,7 @@ callbacks. This currently takes place only during APM state transitions. Upon going to sleep, the PCI subsystem walks its device tree twice. Both times, it does -a depth first walk of the device tree. The first walk saves each of the device's state +a depth first walk of the device tree. The first walk saves each of the device's state and checks for devices that will prevent the system from entering a global power state. The next walk then places the devices in a low power state. @@ -104,7 +104,7 @@ ----------------- Usage: - pci_restore_state(dev,buffer); + pci_restore_state(dev, buffer); Description: Restore previously saved config space. (First 64 bytes only); @@ -117,7 +117,7 @@ ------------------- Usage: - pci_set_power_state(dev,state); + pci_set_power_state(dev, state); Description: Transition device to low power state using PCI PM Capabilities registers. @@ -132,7 +132,7 @@ --------------- Usage: - pci_enable_wake(dev,state,enable); + pci_enable_wake(dev, state, enable); Description: Enable device to generate PME# during low power state using PCI PM @@ -155,7 +155,7 @@ struct pci_driver: int (*save_state) (struct pci_dev *dev, u32 state); - int (*suspend)(struct pci_dev *dev, u32 state); + int (*suspend) (struct pci_dev *dev, u32 state); int (*resume) (struct pci_dev *dev); int (*enable_wake) (struct pci_dev *dev, u32 state, int enable); @@ -178,7 +178,7 @@ The driver can also interpret this function as a notification that it may be entering a sleep state in the near future. If it knows that the device cannot enter the -requested state, either because of lack of support for it, or because the devices is +requested state, either because of lack of support for it, or because the device is middle of some critical operation, then it should fail. This function should not be used to set any state in the device or the driver because diff -Nru a/Documentation/sound/AD1816 b/Documentation/sound/AD1816 --- a/Documentation/sound/AD1816 Thu Mar 7 18:17:39 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,84 +0,0 @@ -Documentation for the AD1816(A) sound driver -============================================ - -Installation: -------------- - -To get your AD1816(A) based sound card work, you'll have to enable support for -experimental code ("Prompt for development and/or incomplete code/drivers") -and isapnp ("Plug and Play support", "ISA Plug and Play support"). Enable -"Sound card support", "OSS modules support" and "Support for AD1816(A) based -cards (EXPERIMENTAL)" in the sound configuration menu, too. Now build, install -and reboot the new kernel as usual. - -Features: ---------- - -List of features supported by this driver: -- full-duplex support -- supported audio formats: unsigned 8bit, signed 16bit little endian, - signed 16bit big endian, µ-law, A-law -- supported channels: mono and stereo -- supported recording sources: Master, CD, Line, Line1, Line2, Mic -- supports phat 3d stereo circuit (Line 3) - - -Supported cards: ----------------- - -The following cards are known to work with this driver: -- Terratec Base 1 -- Terratec Base 64 -- HP Kayak -- Acer FX-3D -- SY-1816 -- Highscreen Sound-Boostar 32 Wave 3D -- Highscreen Sound-Boostar 16 -- AVM Apex Pro card -- (Aztech SC-16 3D) -- (Newcom SC-16 3D) -- (Terratec EWS64S) - -Cards listed in brackets are not supported reliable. If you have such a card -you should add the extra parameter: - options=1 -when loading the ad1816 module via modprobe. - - -Troubleshooting: ----------------- - -First of all you should check, if the driver has been loaded -properly. - -If loading of the driver succeeds, but playback/capture fails, check -if you used the correct values for irq, dma and dma2 when loading the module. -If one of them is wrong you usually get the following error message: - -Nov 6 17:06:13 tek01 kernel: Sound: DMA (output) timed out - IRQ/DRQ config error? - -If playback/capture is too fast or to slow, you should have a look at -the clock chip of your sound card. The AD1816 was designed for a 33MHz -oscillator, however most sound card manufacturer use slightly -different oscillators as they are cheaper than 33MHz oscillators. If -you have such a card you have to adjust the ad1816_clockfreq parameter -above. For example: For a card using a 32.875MHz oscillator use -ad1816_clockfreq=32875 instead of ad1816_clockfreq=33000. - - -Updates, bugfixes and bugreports: --------------------------------- - -As the driver is still experimental and under development, you should -watch out for updates. Updates of the driver are available on the -Internet from one of my home pages: - http://www.student.informatik.tu-darmstadt.de/~tek/projects/linux.html -or: - http://www.tu-darmstadt.de/~tek01/projects/linux.html - -Bugreports, bugfixes and related questions should be sent via E-Mail to: - tek@rbg.informatik.tu-darmstadt.de - -Thorsten Knabe -Christoph Hellwig - Last modified: 2000/09/20 diff -Nru a/Documentation/sound/ALS b/Documentation/sound/ALS --- a/Documentation/sound/ALS Thu Mar 7 18:17:40 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,66 +0,0 @@ -ALS-007/ALS-100/ALS-200 based sound cards -========================================= - -Support for sound cards based around the Avance Logic -ALS-007/ALS-100/ALS-200 chip is included. These chips are a single -chip PnP sound solution which is mostly hardware compatible with the -Sound Blaster 16 card, with most differences occurring in the use of -the mixer registers. For this reason the ALS code is integrated -as part of the Sound Blaster 16 driver (adding only 800 bytes to the -SB16 driver). - -To use an ALS sound card under Linux, enable the following options as -modules in the sound configuration section of the kernel config: - - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support - - FM synthesizer (YM3812/OPL-3) support - - standalone MPU401 support may be required for some cards; for the - ALS-007, when using isapnptools, it is required -Since the ALS-007/100/200 are PnP cards, ISAPnP support should probably be -compiled in. If kernel level PnP support is not included, isapnptools will -be required to configure the card before the sound modules are loaded. - -When using kernel level ISAPnP, the kernel should correctly identify and -configure all resources required by the card when the "sb" module is -inserted. Note that the ALS-007 does not have a 16 bit DMA channel and that -the MPU401 interface on this card uses a different interrupt to the audio -section. This should all be correctly configured by the kernel; if problems -with the MPU401 interface surface, try using the standalone MPU401 module, -passing "0" as the "sb" module's "mpu_io" module parameter to prevent the -soundblaster driver attempting to register the MPU401 itself. The onboard -synth device can be accessed using the "opl3" module. - -If isapnptools is used to wake up the sound card (as in 2.2.x), the settings -of the card's resources should be passed to the kernel modules ("sb", "opl3" -and "mpu401") using the module parameters. When configuring an ALS-007, be -sure to specify different IRQs for the audio and MPU401 sections - this card -requires they be different. For "sb", "io", "irq" and "dma" should be set -to the same values used to configure the audio section of the card with -isapnp. "dma16" should be explicitly set to "-1" for an ALS-007 since this -card does not have a 16 bit dma channel; if not specified the kernel will -default to using channel 5 anyway which will cause audio not to work. -"mpu_io" should be set to 0. The "io" parameter of the "opl3" module should -also agree with the setting used by isapnp. To get the MPU401 interface -working on an ALS-007 card, the "mpu401" module will be required since this -card uses separate IRQs for the audio and MPU401 sections and there is no -parameter available to pass a different IRQ to the "sb" driver (whose -inbuilt MPU401 driver would otherwise be fine). Insert the mpu401 module -passing appropriate values using the "io" and "irq" parameters. - -The resulting sound driver will provide the following capabilities: - - 8 and 16 bit audio playback - - 8 and 16 bit audio recording - - Software selection of record source (line in, CD, FM, mic, master) - - Record and playback of midi data via the external MPU-401 - - Playback of midi data using inbuilt FM synthesizer - - Control of the ALS-007 mixer via any OSS-compatible mixer programs. - Controls available are Master (L&R), Line in (L&R), CD (L&R), - DSP/PCM/audio out (L&R), FM (L&R) and Mic in (mono). - -Jonathan Woithe -jwoithe@physics.adelaide.edu.au -30 March 1998 - -Modified 2000-02-26 by Dave Forrest, drf5n@virginia.edu to add ALS100/ALS200 -Modified 2000-04-10 by Paul Laufer, pelaufer@csupomona.edu to add ISAPnP info. -Modified 2000-11-19 by Jonathan Woithe, jwoithe@physics.adelaide.edu.au - - updated information for kernel 2.4.x. diff -Nru a/Documentation/sound/AWE32 b/Documentation/sound/AWE32 --- a/Documentation/sound/AWE32 Thu Mar 7 18:17:38 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,76 +0,0 @@ - Installing and using Creative AWE midi sound under Linux. - -This documentation is devoted to the Creative Sound Blaster AWE32, AWE64 and -SB32. - -1) Make sure you have an ORIGINAL Creative SB32, AWE32 or AWE64 card. This - is important, because the driver works only with real Creative cards. - -2) The first thing you need to do is re-compile your kernel with support for - your sound card. Run your favourite tool to configure the kernel and when - you get to the "Sound" menu you should enable support for the following: - - Sound card support, - OSS sound modules, - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support, - AWE32 synth - - If your card is "Plug and Play" you will also need to enable these two - options, found under the "Plug and Play configuration" menu: - - Plug and Play support - ISA Plug and Play support - - Now compile and install the kernel in normal fashion. If you don't know - how to do this you can find instructions for this in the README file - located in the root directory of the kernel source. - -3) Before you can start playing midi files you will have to load a sound - bank file. The utility needed for doing this is called "sfxload", and it - is one of the utilities found in a package called "awesfx". If this - package is not available in your distribution you can download the AWE - snapshot from Creative Labs Open Source website: - - http://www.opensource.creative.com/snapshot.html - - Once you have unpacked the AWE snapshot you will see a "awesfx" - directory. Follow the instructions in awesfx/docs/INSTALL to install the - utilities in this package. After doing this, sfxload should be installed - as: - - /usr/local/bin/sfxload - - To enable AWE general midi synthesis you should also get the sound bank - file for general midi from: - - http://members.xoom.com/yar/synthgm.sbk.gz - - Copy it to a directory of your choice, and unpack it there. - -4) Edit /etc/modules.conf, and insert the following lines at the end of the - file: - - alias sound-slot-0 sb - alias sound-service-0-1 awe_wave - post-install awe_wave /usr/local/bin/sfxload PATH_TO_SOUND_BANK_FILE - - You will of course have to change "PATH_TO_SOUND_BANK_FILE" to the full - path of of the sound bank file. That will enable the Sound Blaster and AWE - wave synthesis. To play midi files you should get one of these programs if - you don't already have them: - - Playmidi: http://playmidi.openprojects.net - - AWEMidi Player (drvmidi) Included in the previously mentioned AWE - snapshot. - - You will probably have to pass the "-e" switch to playmidi to have it use - your midi device. drvmidi should work without switches. - - If something goes wrong please e-mail me. All comments and suggestions are - welcome. - - Yaroslav Rosomakho (alons55@dialup.ptt.ru) - http://www.yar.opennet.ru - -Last Updated: Feb 3 2001 diff -Nru a/Documentation/sound/AudioExcelDSP16 b/Documentation/sound/AudioExcelDSP16 --- a/Documentation/sound/AudioExcelDSP16 Thu Mar 7 18:17:36 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,101 +0,0 @@ -Driver ------- - -Informations about Audio Excel DSP 16 driver can be found in the source -file lowlevel/aedsp16.c -Please, read the head of the source before using it. It contain useful -informations. - -Configuration -------------- - -The Audio Excel configuration, is now done with the standard Linux setup. -You have to configure the sound card (Sound Blaster or Microsoft Sound System) -and, if you want it, the Roland MPU-401 (do not use the Sound Blaster MPU-401, -SB-MPU401) in the main driver menu. Activate the lowlevel drivers then select -the Audio Excel hardware that you want to initialize. Check the IRQ/DMA/MIRQ -of the Audio Excel initialization: it must be the same as the SBPRO (or MSS) -setup. If the parameters are different, correct it. -I you own a Gallant's audio card based on SC-6600, activate the SC-6600 support. -If you want to change the configuration of the sound board, be sure to -check off all the configuration items before re-configure it. - -Module parameters ------------------ -To use this driver as a module, you must configure some module parameters, to -set up I/O addresses, IRQ lines and DMA channels. Some parameters are -mandatory while some others are optional. Here a list of parameters you can -use with this module: - -Name Description -==== =========== -MANDATORY -io I/O base address (0x220 or 0x240) -irq irq line (5, 7, 9, 10 or 11) -dma dma channel (0, 1 or 3) - -OPTIONAL -mss_base I/O base address for activate MSS mode (default SBPRO) - (0x530 or 0xE80) -mpu_base I/O base address for activate MPU-401 mode - (0x300, 0x310, 0x320 or 0x330) -mpu_irq MPU-401 irq line (5, 7, 9, 10 or 0) - -The /etc/modules.conf will have lines like this: - -options opl3 io=0x388 -options ad1848 io=0x530 irq=11 dma=3 -options aedsp16 io=0x220 irq=11 dma=3 mss_base=0x530 - -Where the aedsp16 options are the options for this driver while opl3 and -ad1848 are the corresponding options for the MSS and OPL3 modules. - -Loading MSS and OPL3 needs to pre load the aedsp16 module to set up correctly -the sound card. Installation dependencies must be written in the modules.conf -file: - -pre-install ad1848 modprobe aedsp16 -pre-install opl3 modprobe aedsp16 - -Then you must load the sound modules stack in this order: -sound -> aedsp16 -> [ ad1848, opl3 ] - -With the above configuration, loading ad1848 or opl3 modules, will -automatically load all the sound stack. - -Sound cards supported ---------------------- -This driver supports the SC-6000 and SC-6600 based Gallant's sound card. -It don't support the Audio Excel DSP 16 III (try the SC-6600 code). -I'm working on the III version of the card: if someone have useful -informations about it, please let me know. -For all the non-supported audio cards, you have to boot MS-DOS (or WIN95) -activating the audio card with the MS-DOS device driver, then you have to --- and boot Linux. -Follow these steps: - -1) Compile Linux kernel with standard sound driver, using the emulation - you want, with the parameters of your audio card, - e.g. Microsoft Sound System irq10 dma3 -2) Install your new kernel as the default boot kernel. -3) Boot MS-DOS and configure the audio card with the boot time device - driver, for MSS irq10 dma3 in our example. -4) -- and boot Linux. This will maintain the DOS configuration - and will boot the new kernel with sound driver. The sound driver will find - the audio card and will recognize and attach it. - -Reports on User successes -------------------------- - -> Date: Mon, 29 Jul 1996 08:35:40 +0100 -> From: Mr S J Greenaway -> To: riccardo@cdc8g5.cdc.polimi.it (Riccardo Facchetti) -> Subject: Re: Audio Excel DSP 16 initialization code -> -> Just to let you know got my Audio Excel (emulating a MSS) working -> with my original SB16, thanks for the driver! - - -Last revised: 20 August 1998 -Riccardo Facchetti -fizban@tin.it diff -Nru a/Documentation/sound/CMI8330 b/Documentation/sound/CMI8330 --- a/Documentation/sound/CMI8330 Thu Mar 7 18:17:38 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,153 +0,0 @@ -Documentation for CMI 8330 (SoundPRO) -------------------------------------- -Alessandro Zummo - -( Be sure to read Documentation/sound/SoundPro too ) - - -This adapter is now directly supported by the sb driver. - - The only thing you have to do is to compile the kernel sound -support as a module and to enable kernel ISAPnP support, -as shown below. - - -CONFIG_SOUND=m -CONFIG_SOUND_SB=m - -CONFIG_PNP=y -CONFIG_ISAPNP=y - - -and optionally: - - -CONFIG_SOUND_MPU401=m - - for MPU401 support. - - -(I suggest you to use "make menuconfig" or "make xconfig" - for a more comfortable configuration editing) - - - -Then you can do - - modprobe sb - -and everything will be (hopefully) configured. - -You should get something similar in syslog: - -sb: CMI8330 detected. -sb: CMI8330 sb base located at 0x220 -sb: CMI8330 mpu base located at 0x330 -sb: CMI8330 mail reports to Alessandro Zummo -sb: ISAPnP reports CMI 8330 SoundPRO at i/o 0x220, irq 7, dma 1,5 - - - - -The old documentation file follows for reference -purposes. - - -How to enable CMI 8330 (SOUNDPRO) soundchip on Linux ------------------------------------------- -Stefan Laudat - -[Note: The CMI 8338 is unrelated and is supported by cmpci.o] - - - In order to use CMI8330 under Linux you just have to use a proper isapnp.conf, a good isapnp and a little bit of patience. I use isapnp 1.17, but -you may get a better one I guess at http://www.roestock.demon.co.uk/isapnptools/. - - Of course you will have to compile kernel sound support as module, as shown below: - -CONFIG_SOUND=m -CONFIG_SOUND_OSS=m -CONFIG_SOUND_SB=m -CONFIG_SOUND_ADLIB=m -CONFIG_SOUND_MPU401=m -# Mikro$chaft sound system (kinda useful here ;)) -CONFIG_SOUND_MSS=m - - The /etc/isapnp.conf file will be: - - - - -(READPORT 0x0203) -(ISOLATE PRESERVE) -(IDENTIFY *) -(VERBOSITY 2) -(CONFLICT (IO FATAL)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # or WARNING -(VERIFYLD N) - - -# WSS - -(CONFIGURE CMI0001/16777472 (LD 0 -(IO 0 (SIZE 8) (BASE 0x0530)) -(IO 1 (SIZE 8) (BASE 0x0388)) -(INT 0 (IRQ 7 (MODE +E))) -(DMA 0 (CHANNEL 0)) -(NAME "CMI0001/16777472[0]{CMI8330/C3D Audio Adapter}") -(ACT Y) -)) - -# MPU - -(CONFIGURE CMI0001/16777472 (LD 1 -(IO 0 (SIZE 2) (BASE 0x0330)) -(INT 0 (IRQ 11 (MODE +E))) -(NAME "CMI0001/16777472[1]{CMI8330/C3D Audio Adapter}") -(ACT Y) -)) - -# Joystick - -(CONFIGURE CMI0001/16777472 (LD 2 -(IO 0 (SIZE 8) (BASE 0x0200)) -(NAME "CMI0001/16777472[2]{CMI8330/C3D Audio Adapter}") -(ACT Y) -)) - -# SoundBlaster - -(CONFIGURE CMI0001/16777472 (LD 3 -(IO 0 (SIZE 16) (BASE 0x0220)) -(INT 0 (IRQ 5 (MODE +E))) -(DMA 0 (CHANNEL 1)) -(DMA 1 (CHANNEL 5)) -(NAME "CMI0001/16777472[3]{CMI8330/C3D Audio Adapter}") -(ACT Y) -)) - - -(WAITFORKEY) - - - - The module sequence is trivial: - -/sbin/insmod soundcore -/sbin/insmod sound -/sbin/insmod uart401 -# insert this first -/sbin/insmod ad1848 io=0x530 irq=7 dma=0 soundpro=1 -# The sb module is an alternative to the ad1848 (Microsoft Sound System) -# Anyhow, this is full duplex and has MIDI -/sbin/insmod sb io=0x220 dma=1 dma16=5 irq=5 mpu_io=0x330 - - - -Alma Chao suggests the following /etc/modules.conf: - -alias sound ad1848 -alias synth0 opl3 -options ad1848 io=0x530 irq=7 dma=0 soundpro=1 -options opl3 io=0x388 - - diff -Nru a/Documentation/sound/CMI8338 b/Documentation/sound/CMI8338 --- a/Documentation/sound/CMI8338 Thu Mar 7 18:17:43 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,85 +0,0 @@ -Audio driver for CM8338/CM8738 chips by Chen-Li Tien - - -HARDWARE SUPPORTED -================================================================================ -C-Media CMI8338 -C-Media CMI8738 -On-board C-Media chips - - -STEPS TO BUILD DRIVER -================================================================================ - - 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. - - 3. Change directory to /usr/src/linux - - 4. Config cm8338 driver by 'make menuconfig', 'make config' or - 'make xconfig' 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' - - 6. Compile the kernel if necessary. - - 7. Compile the modules by 'make modules'. - - 8. Install the modules by 'make modules_install' - - -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: - - 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. - - 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. - - mpuio: I/O ports base for MPU-401, 0 if disabled. - fmio: 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. - 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. - joystick: Enable joystick. You will need to install Linux joystick - driver. - diff -Nru a/Documentation/sound/CS4232 b/Documentation/sound/CS4232 --- a/Documentation/sound/CS4232 Thu Mar 7 18:17:40 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,23 +0,0 @@ -To configure the Crystal CS423x sound chip and activate its DSP functions, -modules may be loaded in this order: - - modprobe sound - insmod ad1848 - insmod uart401 - insmod cs4232 io=* irq=* dma=* dma2=* - -This is the meaning of the parameters: - - io--I/O address of the Windows Sound System (normally 0x534) - irq--IRQ of this device - dma and dma2--DMA channels (DMA2 may be 0) - -On some cards, the board attempts to do non-PnP setup, and fails. If you -have problems, use Linux' PnP facilities. - -To get MIDI facilities add - - insmod opl3 io=* - -where "io" is the I/O address of the OPL3 synthesizer. This will be shown -in /proc/sys/pnp and is normally 0x388. diff -Nru a/Documentation/sound/ChangeLog.awe b/Documentation/sound/ChangeLog.awe --- a/Documentation/sound/ChangeLog.awe Thu Mar 7 18:17:45 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,230 +0,0 @@ -ver.0.4.3p4 - - Bug fix for invalid memory detection when initialized twice - - Add sample sharing function - works together with awesfx-0.4.3p3 - - Add AWE_PROBE_DATA for probing sample id - -ver.0.4.3p3 - - Replace memset to MEMSET (for FreeBSD) - - Add PAN_EXCHANGE switch - -ver.0.4.3p2 - - MIDI emulation device is added - - Controls volume and filter targets - - Include chorus/reverb/equalizer values in MISC_MODE - -ver.0.4.3p1 - - Change the volume calculation method - - Support for Tom Lees' PnP driver (v0.3) - -ver.0.4.2d - - Support for OSS/Free 3.8 on 2.0 kernels. - - Support for Linux PnP driver - - Support for module (for recent 2.1 kernels and RH5.0) - - Support for FreeBSD-3.0 system - -ver.0.4.2c - - Add a mode to enable drum channel toggle via bank number - change. - -ver.0.4.2b - - Clear voice position after note on - - Change nrvoices according to the current playing mode - -ver.0.4.2a - - Fix a bug in pitch calculation with scale parameter - - Change default chorus & reverb modes - -ver.0.4.2 - - Use indirect voice allocation mode; used as default mode - - Add preset mapping - - Free buffers when resetting samples - - Set default preset/bank/drumset as variable - - Fix a bug in exclusive note-off - - Add channel reset control macro - - Change modwheel sensitivity as variable - - Add lock option in open_patch - - Add channel priority mode macro, and disable it as default - - Add unset effect macro - - Add user defined chorus/reverb modes - - Do not initialize effect parameters when allocating voices - - Accept realtime filter-Q parameter change - - Check value range of set/add effects - - Change drum flags automatically when receiving bank #128 - -ver.0.4.1 development versions - -ver.0.4.0c - - Fix kernel oops when setting AWE_FX_ATTEN - -ver.0.4.0b - - Do not kill_note in start_note when velocity is zero - -ver.0.4.0a - - Fix a bug in channel pressure effects - -ver.0.4.0 - - Support dynamic buffer allocation - - Add functions to open/close/unload a patch - - Change from pointer to integer index in voice/sample lists - - Support for Linux/Alpha-AXP - - Fix for FreeBSD - - Add sostenuto control - - Add midi channel priority - - Fix a bug in all notes off control - - Use AWE_DEFAULT_MEMSIZE always if defined - - Fix a bug in awe_reset causes seg fault when no DRAM onboard - - Use awe_mem_start variable instead of constant - -ver.0.3.3c - - Fix IOCTL_TO_USER for OSS-3.8 (on Linux-2.1.25) - - Fix i/o macros for mixer controls - -ver.0.3.3b - - Fix version number in awe_version.h - - Fix a small bug in noteoff/release all - -ver.0.3.3a - - Fix all notes/sounds off - - Add layer effect control - - Add misc mode controls; realtime pan, version number, etc. - - Move gus bank control in misc mode control - - Modify awe_operations for OSS3.8b5 - - Fix installation script - -ver.0.3.3 - - Add bass/treble control in Emu8000 chip - - Add mixer device - - Fix sustain on to value 127 - -ver.0.3.2 - - Refuse linux-2.0.0 at installation - - Move awe_voice.h to /usr/include/linux - -ver.0.3.1b (not released) - - Rewrite chorus/reverb mode change functions - - Rewrite awe_detect & awe_check_dram routines - -ver.0.3.1a - - Fix a bug to reset voice counter in awe_reset - - Fix voice balance on GUS mode - - Make symlink on /usr/include/asm in install script - -ver.0.3.1 - - Remove zero size arrays from awe_voice.h - - Fix init_fm routine - - Remove all samples except primary samples in REMOVE_LAST_SAMPLES - -ver.0.3.0a - - Add AWE_NOTEOFF_ALL control - - Remove AWE_INIT_ATTEN control - -ver.0.3.0 - - Fix decay time table - - Add exclusive sounds mode - - Add capability to get current status - -ver.0.2.99e - - Add #ifdef for all sounds/notes off controls. - - Fix bugs on searching the default drumset/preset. - - Fix usslite patch to modify the default Config.in. - -ver.0.2.99d - - Fix bugs of attack/hold parameters - - Fix attack & decay time table - -ver.0.2.99c - - Change volume control messages (main & expression volume) - to accesspt normal MIDI parameters in channel mode. - - Use channel mode in SEQ2 controls. - -ver.0.2.99b - - #ifdef patch manager functions (for OSS-3.7) - -ver.0.2.99a - - Fix sustain bug - -ver.0.2.99 (0.3 beta) - - Support multiple instruments - -ver.0.2.0c - - Add copyright notice - - FreeBSD 2.2-ALPHA integration - -ver.0.2.0b - - Remove buffered reading appended in v0.2.0a - - Remove SMAxW register check on writing - - Support Linux 2.1.x kernel - - Rewrite installation script - -ver.0.2.0a - - Define SEQUENCER_C for tuning.h for FreeBSD system - - Improvement of sample loading speed - - Fix installation script - - Add PnP driver functions for ISA PnP driver support - -ver.0.2.0 - - Includes FreeBSD port - - Can load GUS compatible patches - - Change values of hardware control parameters for compatibility - with GUS driver - - Accept 8bit or unsigned wave data - - Accept no blank loop data - - Add sample mode flags in sample_info - -ver.0.1.6 - - Add voice effects control - - Fix awe_voice.h for word alignment - -ver.0.1.5c - - Fix FM(OPL) playback problem - -ver.0.1.5b - - Fix pitch calculation for fixed midi key - -ver.0.1.5a - - Fix bugs in removing samples from linked list. - -ver.0.1.5 - - Add checksum verification for sample uploading - (not compatible from older sample_info structure) - - Fix sample offset pointers to (actual value - 1) - - Add sequencer command to initialize awe32 - -ver.0.1.4c - - Fix card detection and memory check function to avoid system crash - at booting - -ver.0.1.4b - - Add release sustain mode - - Initialize FM each time after loading samples - -ver.0.1.4a - - Fix AWE card detection code - - Correct FM initialize position - - Add non-releasing mode on voice info - -ver.0.1.4 - - Add AWE card and DRAM detection codes - - Add FM initialization code - - Modify volume control - - Remove linear volume mode - - Change memory management; not using malloc dynamically - - Add remove-samples command - - Use internal id implicitly at loading samples - -ver.0.1.3 - - Fix a bug on patch uploading to RAM - -ver.0.1.2 - - Divide to separated packages - - Fix disagreed macro conditions - - Fix unresolved function bugs - - Integrate VoxWare and USS-Lite driver source (awe_voice.c) - and remove awe_card.c - -ver.0.1.1 - - Fix wrong sample numbers in sbktext - - Fix txt2sfx bug - - Fix pan parameter calculation - - Append USS-Lite/Linux2.0 driver - diff -Nru a/Documentation/sound/ChangeLog.multisound b/Documentation/sound/ChangeLog.multisound --- a/Documentation/sound/ChangeLog.multisound Thu Mar 7 18:17:41 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,213 +0,0 @@ -1998-12-04 Andrew T. Veliath - - * Update version to 0.8.2.2 - - * Add msndreset program to shell archive. - -1998-11-11 Andrew T. Veliath - - * msnd_pinnacle.c (mixer_ioctl): Add a mixer ioctl for - SOUND_MIXER_PRIVATE1 which does a full reset on the card. - (mixer_set): Move line in recording source to input monitor, aux - input level added, some mixer fixes. - -1998-09-10 Andrew Veliath - - * Update version to 0.8.2 - - * Add SNDCTL_DSP_GETOSPACE and SNDCTL_DSP_GETISPACE ioctls. - -1998-09-09 Andrew Veliath - - * Update version to 0.8.1 - - * msnd_pinnacle.c: Fix resetting of default audio parameters. Turn - flush code from dsp_halt into dsp_write_flush, and use that for - SNDCTL_DSP_SYNC. - -1998-09-07 Andrew Veliath - - * Update version to 0.8.0 - - * Provide separate signal parameters for play and record. - - * Cleanups to locking and interrupt handling, change default - fifosize to 128kB. - - * Update version to 0.7.15 - - * Interprocess full-duplex support (ie `cat /dev/dsp > /dev/dsp'). - - * More mutex sections for read and write fifos (read + write locks - added). - -1998-09-05 Andrew Veliath - - * msnd_pinnacle.c: (chk_send_dsp_cmd) Do full DSP reset upon DSP - timeout (when not in interrupt; maintains mixer settings). Fixes - to flushing and IRQ ref counting. Rewrote queuing for smoother - playback and fixed initial playback cutoff problem. - -1998-09-03 Andrew Veliath - - * Replaced packed structure accesses with standard C equivalents. - -1998-09-01 Andrew Veliath - - * msnd_pinnacle.c: Add non-PnP configuration to driver code, which - will facilitate compiled-in operation. - -1998-08-29 Andrew Veliath - - * Update version to 0.7.6 - - * msnd_pinnacle.c (dsp_ioctl): Add DSP_GETFMTS, change SAMPLESIZE - to DSP_SETFMT. - - * Update version to 0.7.5 - - * Create pinnaclecfg.c and turn MultiSound doc into a shell - archive with pinnaclecfg.c included. pinnaclecfg.c can - now fully configure the card in non-PnP mode, including the - joystick and IDE controller. Also add an isapnp conf - example. - - * Reduce DSP reset timeout from 20000 to 100 - -1998-08-06 Andrew Veliath - - * Update version to 0.7.2 - - * After A/D calibration, do an explicit set to the line input, - rather than using set_recsrc - -1998-07-20 Andrew Veliath - - * Update version to 0.7.1 - - * Add more OSS ioctls - -1998-07-19 Andrew Veliath - - * Update doc file - - * Bring back DIGITAL1 with digital parameter to msnd_pinnacle.c - and CONFIG_MSNDPIN_DIGITAL. I'm not sure this actually works, - since I find audio playback goes into a very speeded mode of - operation, however it might be due to a lack of a digital - source, which I don't have to test. - -1998-07-18 Andrew Veliath - - * Update version to 0.7.0 - - * Can now compile with Alan Cox' 2.0.34-modular-sound patch (so - now it requires >= 2.1.106 or 2.0.34-ms) (note for 2.0.34-ms it - is in the Experimental section) - - * More modularization, consolidation, also some MIDI hooks - installed for future MIDI modules - - * Write flush - - * Change default speed, channels, bit size to OSS/Free defaults - -1998-06-02 Andrew Veliath - - * Update version to 0.5b - - * Fix version detection - - * Remove underflow and overflow resets (delay was too long) - - * Replace spinlocked bitops with atomic bit ops - -1998-05-27 Andrew Veliath - - * Update version to 0.5a - - * Better recovery from underflow or overflow conditions - - * Fix a deadlock condition with one thread reading and the other - writing - -1998-05-26 Andrew Veliath - - * Update version to 0.5 - - * Separate reset queue functions for play and record - - * Add delays in dsp_halt - -1998-05-24 Andrew Veliath - - * Add a check for Linux >= 2.1.95 - - * Remove DIGITAL1 input until I figure out how to make it work - - * Add HAVE_DSPCODEH which when not defined will load firmware from - files using mod_firmware_load, then release memory after they - are uploaded (requires reorganized OSS). - -1998-05-22 Andrew Veliath - - * Update version to 0.4c - - * Hopefully fix the mixer volume problem - -1998-05-19 Andrew Veliath - - * Add __initfuncs and __initdatas to reduce resident code size - - * Move bunch of code around, remove some protos - - * Integrate preliminary changes for Alan Cox's OSS reorganization - for non-OSS drivers to coexist with OSS devices on the same - major. To compile standalone, must now define STANDALONE. - -1998-05-16 Andrew Veliath - - * Update version to 0.4b - - * Integrated older card support into a unified driver, tested on a - MultiSound Classic c/o Kendrick Vargas. - -1998-05-15 Andrew Veliath - - * Update version to 0.4 - - * Fix read/write return values - -1998-05-13 Andrew Veliath - - * Update version to 0.3 - - * Stop play gracefully - - * Add busy flag - - * Add major and calibrate_signal module parameters - - * Add ADC calibration - - * Add some OSS compatibility ioctls - - * Add mixer record selection - - * Add O_NONBLOCK support, separate read/write wait queues - - * Add sample bit size ioctl, expanded sample rate ioctl - - * Playback suspension now resumes - - * Use signal_pending after interruptible_sleep_on - - * Add recording, change ints to bit flags - -1998-05-11 Andrew Veliath - - * Update version to 0.2 - - * Add preliminary playback support - - * Use new Turtle Beach DSP code \ No newline at end of file diff -Nru a/Documentation/sound/ESS b/Documentation/sound/ESS --- a/Documentation/sound/ESS Thu Mar 7 18:17:44 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,34 +0,0 @@ -Documentation for the ESS AudioDrive chips - -In 2.4 kernels the SoundBlaster driver not only tries to detect an ESS chip, it -tries to detect the type of ESS chip too. The correct detection of the chip -doesn't always succeed however, so unless you use the kernel isapnp facilities -(and you chip is pnp capable) the default behaviour is 2.0 behaviour which -means: only detect ES688 and ES1688. - -All ESS chips now have a recording level setting. This is a need-to-have for -people who want to use their ESS for recording sound. - -Every chip that's detected as a later-than-es1688 chip has a 6 bits logarithmic -master volume control. - -Every chip that's detected as a ES1887 now has Full Duplex support. Made a -little testprogram that shows that is works, haven't seen a real program that -needs this however. - -For ESS chips an additional parameter "esstype" can be specified. This controls -the (auto) detection of the ESS chips. It can have 3 kinds of values: - --1 Act like 2.0 kernels: only detect ES688 or ES1688. -0 Try to auto-detect the chip (may fail for ES1688) -688 The chip will be treated as ES688 -1688 ,, ,, ,, ,, ,, ,, ES1688 -1868 ,, ,, ,, ,, ,, ,, ES1868 -1869 ,, ,, ,, ,, ,, ,, ES1869 -1788 ,, ,, ,, ,, ,, ,, ES1788 -1887 ,, ,, ,, ,, ,, ,, ES1887 -1888 ,, ,, ,, ,, ,, ,, ES1888 - -Because Full Duplex is supported for ES1887 you can specify a second DMA -channel by specifying module parameter dma16. It can be one of: 0, 1, 3 or 5. - diff -Nru a/Documentation/sound/ESS1868 b/Documentation/sound/ESS1868 --- a/Documentation/sound/ESS1868 Thu Mar 7 18:17:40 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,55 +0,0 @@ -Documentation for the ESS1868F AudioDrive PnP sound card - -The ESS1868 sound card is a PnP ESS1688-compatible 16-bit sound card. - -It should be automatically detected by the Linux Kernel isapnp support when you -load the sb.o module. Otherwise you should take care of: - - * The ESS1868 does not allow use of a 16-bit DMA, thus DMA 0, 1, 2, and 3 - may only be used. - - * isapnptools version 1.14 does work with ESS1868. Earlier versions might - not. - - * Sound support MUST be compiled as MODULES, not statically linked - into the kernel. - - -NOTE: this is only needed when not using the kernel isapnp support! - -For configuring the sound card's I/O addresses, IRQ and DMA, here is a -sample copy of the isapnp.conf directives regarding the ESS1868: - -(CONFIGURE ESS1868/-1 (LD 1 -(IO 0 (BASE 0x0220)) -(IO 1 (BASE 0x0388)) -(IO 2 (BASE 0x0330)) -(DMA 0 (CHANNEL 1)) -(INT 0 (IRQ 5 (MODE +E))) -(ACT Y) -)) - -(for a full working isapnp.conf file, remember the -(ISOLATE) -(IDENTIFY *) -at the beginning and the -(WAITFORKEY) -at the end.) - -In this setup, the main card I/O is 0x0220, FM synthesizer is 0x0388, and -the MPU-401 MIDI port is located at 0x0330. IRQ is IRQ 5, DMA is channel 1. - -After configuring the sound card via isapnp, to use the card you must load -the sound modules with the proper I/O information. Here is my setup: - -# ESS1868F AudioDrive initialization - -/sbin/modprobe sound -/sbin/insmod uart401 -/sbin/insmod sb io=0x220 irq=5 dma=1 dma16=-1 -/sbin/insmod mpu401 io=0x330 -/sbin/insmod opl3 io=0x388 -/sbin/insmod v_midi - -opl3 is the FM synthesizer -/sbin/insmod opl3 io=0x388 diff -Nru a/Documentation/sound/INSTALL.awe b/Documentation/sound/INSTALL.awe --- a/Documentation/sound/INSTALL.awe Thu Mar 7 18:17:39 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,134 +0,0 @@ -================================================================ - INSTALLATION OF AWE32 SOUND DRIVER FOR LINUX - Takashi Iwai -================================================================ - ----------------------------------------------------------------- -* Attention to SB-PnP Card Users - -If you're using PnP cards, the initialization of PnP is required -before loading this driver. You have now three options: - 1. Use isapnptools. - 2. Use in-kernel isapnp support. - 3. Initialize PnP on DOS/Windows, then boot linux by loadlin. -In this document, only the case 1 case is treated. - ----------------------------------------------------------------- -* Installation on Red Hat 5.0 Sound Driver - -Please use install-rh.sh under RedHat5.0 directory. -DO NOT USE install.sh below. -See INSTALL.RH for more details. - ----------------------------------------------------------------- -* Installation/Update by Shell Script - - 1. Become root - - % su - - 2. If you have never configured the kernel tree yet, run make config - once (to make dependencies and symlinks). - - # cd /usr/src/linux - # make xconfig - - 3. Run install.sh script - - # sh ./install.sh - - 4. Configure your kernel - - (for Linux 2.[01].x user) - # cd /usr/src/linux - # make xconfig (or make menuconfig) - - (for Linux 1.2.x user) - # cd /usr/src/linux - # make config - - Answer YES to both "lowlevel drivers" and "AWE32 wave synth" items - in Sound menu. ("lowlevel drivers" will appear only in 2.x - kernel.) - - 5. Make your kernel (and modules), and install them as usual. - - 5a. make kernel image - # make zImage - - 5b. make modules and install them - # make modules && make modules_install - - 5c. If you're using lilo, copy the kernel image and run lilo. - Otherwise, copy the kernel image to suitable directory or - media for your system. - - 6. Reboot the kernel if necessary. - - If you updated only the modules, you don't have to reboot - the system. Just remove the old sound modules here. - in - # rmmod sound.o (linux-2.0 or OSS/Free) - # rmmod awe_wave.o (linux-2.1) - - 7. If your AWE card is a PnP and not initialized yet, you'll have to - do it by isapnp tools. Otherwise, skip to 8. - - This section described only a brief explanation. For more - details, please see the AWE64-Mini-HOWTO or isapnp tools FAQ. - - 7a. If you have no isapnp.conf file, generate it by pnpdump. - Otherwise, skip to 7d. - # pnpdump > /etc/isapnp.conf - - 7b. Edit isapnp.conf file. Comment out the appropriate - lines containing desirable I/O ports, DMA and IRQs. - Don't forget to enable (ACT Y) line. - - 7c. Add two i/o ports (0xA20 and 0xE20) in WaveTable part. - ex) - (CONFIGURE CTL0048/58128 (LD 2 - # ANSI string -->WaveTable<-- - (IO 0 (BASE 0x0620)) - (IO 1 (BASE 0x0A20)) - (IO 2 (BASE 0x0E20)) - (ACT Y) - )) - - 7d. Load the config file. - CAUTION: This will reset all PnP cards! - - # isapnp /etc/isapnp.conf - - 8. Load the sound module (if you configured it as a module): - - for 2.0 kernel or OSS/Free monolithic module: - - # modprobe sound.o - - for 2.1 kernel: - - # modprobe sound - # insmod uart401 - # insmod sb io=0x220 irq=5 dma=1 dma16=5 mpu_io=0x330 - (These values depend on your settings.) - # insmod awe_wave - (Be sure to load awe_wave after sb!) - - See /usr/src/linux/Documentation/sound/AWE32 for - more details. - - 9. (only for obsolete systems) If you don't have /dev/sequencer - device file, make it according to Readme.linux file on - /usr/src/linux/drivers/sound. (Run a shell script included in - that file). <-- This file no longer exists in the recent kernels! - - 10. OK, load your own soundfont file, and enjoy MIDI! - - % sfxload synthgm.sbk - % drvmidi foo.mid - - 11. For more advanced use (eg. dynamic loading, virtual bank and - etc.), please read the awedrv FAQ or the instructions in awesfx - and awemidi packages. - -Good luck! diff -Nru a/Documentation/sound/Introduction b/Documentation/sound/Introduction --- a/Documentation/sound/Introduction Thu Mar 7 18:17:45 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,461 +0,0 @@ -Introduction Notes on Modular Sound Drivers and Soundcore -Wade Hampton -2/14/2001 - -Purpose: -======== -This document provides some general notes on the modular -sound drivers and their configuration, along with the -support modules sound.o and soundcore.o. - -Note, some of this probably should be added to the Sound-HOWTO! - -Note, soundlow.o was present with 2.2 kernels but is not -required for 2.4.x kernels. References have been removed -to this. - - -Copying: -======== -none - - -History: -======== -0.1.0 11/20/1998 First version, draft -1.0.0 11/1998 Alan Cox changes, incorporation in 2.2.0 - as /usr/src/linux/Documentation/sound/Introduction -1.1.0 6/30/1999 Second version, added notes on making the drivers, - added info on multiple sound cards of similar types,] - added more diagnostics info, added info about esd. - added info on OSS and ALSA. -1.1.1 19991031 Added notes on sound-slot- and sound-service. - (Alan Cox) -1.1.2 20000920 Modified for Kernel 2.4 (Christoph Hellwig) -1.1.3 20010214 Minor notes and corrections (Wade Hampton) - Added examples of sound-slot-0, etc. - - -Modular Sound Drivers: -====================== - -Thanks to the GREAT work by Alan Cox (alan@lxorguk.ukuu.org.uk), - -[And Oleg Drokin, Thomas Sailer, Andrew Veliath and more than a few - others - not to mention Hannu's original code being designed well - enough to cope with that kind of chopping up](Alan) - -the standard Linux kernels support a modular sound driver. From -Alan's comments in linux/drivers/sound/README.FIRST: - - The modular sound driver patches were funded by Red Hat Software - (www.redhat.com). The sound driver here is thus a modified version of - Hannu's code. Please bear that in mind when considering the appropriate - forums for bug reporting. - -The modular sound drivers may be loaded via insmod or modprobe. -To support all the various sound modules, there are two general -support modules that must be loaded first: - - soundcore.o: Top level handler for the sound system, provides - a set of functions for registration of devices - by type. - - sound.o: Common sound functions required by all modules. - -For the specific sound modules (e.g., sb.o for the Soundblaster), -read the documentation on that module to determine what options -are available, for example IRQ, address, DMA. - -Warning, the options for different cards sometime use different names -for the same or a similar feature (dma1= versus dma16=). As a last -resort, inspect the code (search for MODULE_PARM). - -Notes: - -1. There is a new OpenSource sound driver called ALSA which is - currently under development: http://www.alsa-project.org/ - The ALSA drivers support some newer hardware that may not - be supported by this sound driver and also provide some - additional features. - -2. The commercial OSS driver may be obtained from the site: - http://www/opensound.com. This may be used for cards that - are unsupported by the kernel driver, or may be used - by other operating systems. - -3. The enlightenment sound daemon may be used for playing - multiple sounds at the same time via a single card, eliminating - some of the requirements for multiple sound card systems. For - more information, see: http://www.tux.org/~ricdude/EsounD.html - The "esd" program may be used with the real-player and mpeg - players like mpg123 and x11amp. The newer real-player - and some games even include built-in support for ESD! - - -Building the Modules: -===================== - -This document does not provide full details on building the -kernel, etc. The notes below apply only to making the kernel -sound modules. If this conflicts with the kernel's README, -the README takes precedence. - -1. To make the kernel sound modules, cd to your /usr/src/linux - directory (typically) and type make config, make menuconfig, - or make xconfig (to start the command line, dialog, or x-based - configuration tool). - -2. Select the Sound option and a dialog will be displayed. - -3. Select M (module) for "Sound card support". - -4. Select your sound driver(s) as a module. For ProAudio, Sound - Blaster, etc., select M (module) for OSS sound modules. - [thanks to Marvin Stodolsky ]A - -5. Make the kernel (e.g., make dep ; make bzImage), and install - the kernel. - -6. Make the modules and install them (make modules; make modules_install). - -Note, for 2.4.x kernels, make sure you have the newer modutils -loaded or modules will not be loaded properly. 2.4.x changed the -layout of /lib/modules/2.4.x and requires an updated modutils. - - -Plug and Play (PnP: -=================== - -If the sound card is an ISA PnP card, isapnp may be used -to configure the card. See the file isapnp.txt in the -directory one level up (e.g., /usr/src/linux/Documentation). - -Also the 2.4.x kernels provide PnP capabilities, see the -file NEWS in this directory. - -PCI sound cards are highly recommended, as they are far -easier to configure and from what I have read, they use -less resources and are more CPU efficient. - - -INSMOD: -======= - -If loading via insmod, the common modules must be loaded in the -order below BEFORE loading the other sound modules. The card-specific -modules may then be loaded (most require parameters). For example, -I use the following via a shell script to load my SoundBlaster: - -SB_BASE=0x240 -SB_IRQ=9 -SB_DMA=3 -SB_DMA2=5 -SB_MPU=0x300 -# -echo Starting sound -/sbin/insmod soundcore -/sbin/insmod sound -# -echo Starting sound blaster.... -/sbin/insmod uart401 -/sbin/insmod sb io=$SB_BASE irq=$SB_IRQ dma=$SB_DMA dma16=$SB_DMA2 mpu_io=$SB_MP - -When using sound as a module, I typically put these commands -in a file such as /root/soundon.sh. - - -MODPROBE: -========= - -If loading via modprobe, these common files are automatically loaded -when requested by modprobe. For example, my /etc/modules.conf contains: - -alias sound sb -options sb io=0x240 irq=9 dma=3 dma16=5 mpu_io=0x300 - -All you need to do to load the module is: - - /sbin/modprobe sb - - -Sound Status: -============= - -The status of sound may be read/checked by: - cat (anyfile).au >/dev/audio - -[WWH: This may not work properly for SoundBlaster PCI 128 cards -such as the es1370/1 (see the es1370/1 files in this directory) -as they do not automatically support uLaw on /dev/audio.] - -The status of the modules and which modules depend on -which other modules may be checked by: - /sbin/lsmod - -/sbin/lsmod should show something like the following: - sb 26280 0 - uart401 5640 0 [sb] - sound 57112 0 [sb uart401] - soundcore 1968 8 [sb sound] - - -Removing Sound: -=============== - -Sound may be removed by using /sbin/rmmod in the reverse order -in which you load the modules. Note, if a program has a sound device -open (e.g., xmixer), that module (and the modules on which it -depends) may not be unloaded. - -For example, I use the following to remove my Soundblaster (rmmod -in the reverse order in which I loaded the modules): - -/sbin/rmmod sb -/sbin/rmmod uart401 -/sbin/rmmod sound -/sbin/rmmod soundcore - -When using sound as a module, I typically put these commands -in a script such as /root/soundoff.sh. - - -Removing Sound for use with OSS: -================================ - -If you get really stuck or have a card that the kernel modules -will not support, you can get a commercial sound driver from -http://www.opensound.com. Before loading the commercial sound -driver, you should do the following: - -1. remove sound modules (detailed above) -2. remove the sound modules from /etc/modules.conf -3. move the sound modules from /lib/modules//misc - (for example, I make a /lib/modules//misc/tmp - directory and copy the sound module files to that - directory). - - -Multiple Sound Cards: -===================== - -The sound drivers will support multiple sound cards and there -are some great applications like multitrack that support them. -Typically, you need two sound cards of different types. Note, this -uses more precious interrupts and DMA channels and sometimes -can be a configuration nightmare. I have heard reports of 3-4 -sound cards (typically I only use 2). You can sometimes use -multiple PCI sound cards of the same type. - -On my machine I have two sound cards (cs4232 and Soundblaster Vibra -16). By loading sound as modules, I can control which is the first -sound device (/dev/dsp, /dev/audio, /dev/mixer) and which is -the second. Normally, the cs4232 (Dell sound on the motherboard) -would be the first sound device, but I prefer the Soundblaster. -All you have to do is to load the one you want as /dev/dsp -first (in my case "sb") and then load the other one -(in my case "cs4232"). - -If you have two cards of the same type that are jumpered -cards or different PnP revisions, you may load the same -module twice. For example, I have a SoundBlaster vibra 16 -and an older SoundBlaster 16 (jumpers). To load the module -twice, you need to do the following: - -1. Copy the sound modules to a new name. For example - sb.o could be copied (or symlinked) to sb1.o for the - second SoundBlaster. - -2. Make a second entry in /etc/modules.conf, for example, - sound1 or sb1. This second entry should refer to the - new module names for example sb1, and should include - the I/O, etc. for the second sound card. - -3. Update your soundon.sh script, etc. - -Warning: I have never been able to get two PnP sound cards of the -same type to load at the same time. I have tried this several times -with the Soundblaster Vibra 16 cards. OSS has indicated that this -is a PnP problem.... If anyone has any luck doing this, please -send me an E-MAIL. PCI sound cards should not have this problem.a -Since this was originally release, I have received a couple of -mails from people who have accomplished this! - -NOTE: In Linux 2.4 the Sound Blaster driver (and only this one yet) -supports multiple cards with one module by default. -Read the file 'Soundblaster' in this directory for details. - - -Sound Problems: -=============== - -First RTFM (including the troubleshooting section -in the Sound-HOWTO). - -1) If you are having problems loading the modules (for - example, if you get device conflict errors) try the - following: - - A) If you have Win95 or NT on the same computer, - write down what addresses, IRQ, and DMA channels - those were using for the same hardware. You probably - can use these addresses, IRQs, and DMA channels. - You should really do this BEFORE attempting to get - sound working! - - B) Check (cat) /proc/interrupts, /proc/ioports, - and /proc/dma. Are you trying to use an address, - IRQ or DMA port that another device is using? - - C) Check (cat) /proc/isapnp - - D) Inspect your /var/log/messages file. Often that will - indicate what IRQ or IO port could not be obtained. - - E) Try another port or IRQ. Note this may involve - using the PnP tools to move the sound card to - another location. Sometimes this is the only way - and it is more or less trial and error. - -2) If you get motor-boating (the same sound or part of a - sound clip repeated), you probably have either an IRQ - or DMA conflict. Move the card to another IRQ or DMA - port. This has happened to me when playing long files - when I had an IRQ conflict. - -3. If you get dropouts or pauses when playing high sample - rate files such as using mpg123 or x11amp/xmms, you may - have too slow of a CPU and may have to use the options to - play the files at 1/2 speed. For example, you may use - the -2 or -4 option on mpg123. You may also get this - when trying to play mpeg files stored on a CD-ROM - (my Toshiba T8000 PII/366 sometimes has this problem). - -4. If you get "cannot access device" errors, your /dev/dsp - files, etc. may be set to owner root, mode 600. You - may have to use the command: - chmod 666 /dev/dsp /dev/mixer /dev/audio - -5. If you get "device busy" errors, another program has the - sound device open. For example, if using the Enlightenment - sound daemon "esd", the "esd" program has the sound device. - If using "esd", please RTFM the docs on ESD. For example, - esddsp may be used to play files via a non-esd - aware program. - -6) Ask for help on the sound list or send E-MAIL to the - sound driver author/maintainer. - -7) Turn on debug in drivers/sound/sound_config.h (DEB, DDB, MDB). - -8) If the system reports insufficient DMA memory then you may want to - load sound with the "dmabufs=1" option. Or in /etc/conf.modules add - - preinstall sound dmabufs=1 - - This makes the sound system allocate its buffers and hang onto them. - - You may also set persistent DMA when building a 2.4.x kernel. - - -Configuring Sound: -================== - -There are several ways of configuring your sound: - -1) On the kernel command line (when using the sound driver(s) - compiled in the kernel). Check the driver source and - documentation for details. - -2) On the command line when using insmod or in a bash script - using command line calls to load sound. - -3) In /etc/modules.conf when using modprobe. - -4) Via Red Hat's GPL'd /usr/sbin/sndconfig program (text based). - -5) Via the OSS soundconf program (with the commercial version - of the OSS driver. - -6) By just loading the module and let isapnp do everything relevant - for you. This works only with a few drivers yet and - of course - - only with isapnp hardware. - -And I am sure, several other ways. - -Anyone want to write a linuxconf module for configuring sound? - - -Module Loading: -=============== - -When a sound card is first referenced and sound is modular, the sound system -will ask for the sound devices to be loaded. Initially it requests that -the driver for the sound system is loaded. It then will ask for -sound-slot-0, where 0 is the first sound card. (sound-slot-1 the second and -so on). Thus you can do - -alias sound-slot-0 sb - -To load a soundblaster at this point. If the slot loading does not provide -the desired device - for example a soundblaster does not directly provide -a midi synth in all cases then it will request "sound-service-0-n" where n -is - - 0 Mixer - - 2 MIDI - - 3, 4 DSP audio - - -For example, I use the following to load my Soundblaster PCI 128 -(ES 1371) card first, followed by my SoundBlaster Vibra 16 card, -then by my TV card: - -# Load the Soundblaster PCI 128 as /dev/dsp, /dev/dsp1, /dev/mixer -alias sound-slot-0 es1371 - -# Load the Soundblaster Vibra 16 as /dev/dsp2, /dev/mixer1 -alias sound-slot-1 sb -options sb io=0x240 irq=5 dma=1 dma16=5 mpu_io=0x330 - -# Load the BTTV (TV card) as /dev/mixer2 -alias sound-slot-2 bttv -alias sound-service-2-0 tvmixer - -pre-install bttv modprobe tuner ; modprobe tvmixer -pre-install tvmixer modprobe msp3400; modprobe tvaudio -options tuner debug=0 type=8 -options bttv card=0 radio=0 pll=0 - - -For More Information (RTFM): -============================ -1) Information on kernel modules: linux/Documentation/modules.txt - and manual pages for insmod and modprobe. - -2) Information on PnP, RTFM manual pages for isapnp. - -3) Sound-HOWTO and Sound-Playing-HOWTO. - -4) OSS's WWW site at http://www.opensound.com. - -5) All the files in linux/Documentation/sound. - -6) The comments and code in linux/drivers/sound. - -7) The sndconfig and rhsound documentation from Red Hat. - -8) The Linux-sound mailing list: sound-list@redhat.com. - -9) Enlightenment documentation (for info on esd) - http://www.tux.org/~ricdude/EsounD.html. - -10) ALSA home page: http://www.alsa-project.org/ - - -Contact Information: -==================== -Wade Hampton: (whampton@staffnet.com) - diff -Nru a/Documentation/sound/MAD16 b/Documentation/sound/MAD16 --- a/Documentation/sound/MAD16 Thu Mar 7 18:17:44 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,55 +0,0 @@ -(This recipe has been edited to update the configuration symbols.) - -From: Shaw Carruthers - -I have been using mad16 sound for some time now with no problems, current -kernel 2.1.89 - -lsmod shows: - -mad16 5176 0 -sb 22044 0 [mad16] -uart401 5576 0 [mad16 sb] -ad1848 14176 1 [mad16] -sound 61928 0 [mad16 sb uart401 ad1848] - -.config has: - -CONFIG_SOUND=m -CONFIG_SOUND_ADLIB=m -CONFIG_SOUND_MAD16=m -CONFIG_SOUND_YM3812=m - -modules.conf has: - -alias char-major-14 mad16 -options sb mad16=1 -options mad16 io=0x530 irq=7 dma=0 dma16=1 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 - - -To get the built in mixer to work this needs to be: - -options adlib_card io=0x388 # FM synthesizer -options sb mad16=1 -options mad16 io=0x530 irq=7 dma=0 dma16=1 mpu_io=816 mpu_irq=5 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 - -The addition of the "mpu_io=816 mpu_irq=5" to the mad16 options line is - ------------------------------------------------------------------------- -The mad16 module in addition supports the following options: - -option: meaning: default: -joystick=0,1 disabled, enabled disabled -cdtype=0x00,0x02,0x04, disabled, Sony CDU31A, disabled - 0x06,0x08,0x0a Mitsumi, Panasonic, - Secondary IDE, Primary IDE -cdport=0x340,0x320, 0x340 - 0x330,0x360 -cdirq=0,3,5,7,9,10,11 disabled, IRQ3, ... disabled -cddma=0,5,6,7 disabled, DMA5, ... DMA5 for Mitsumi or IDE -cddma=0,1,2,3 disabled, DMA1, ... DMA3 for Sony or Panasonic -opl4=0,1 OPL3, OPL4 OPL3 - -for more details see linux/drivers/sound/mad16.c - -Rui Sousa diff -Nru a/Documentation/sound/Maestro b/Documentation/sound/Maestro --- a/Documentation/sound/Maestro Thu Mar 7 18:17:44 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,123 +0,0 @@ - An OSS/Lite Driver for the ESS Maestro family of sound cards - - Zach Brown, December 1999 - -Driver Status and Availability ------------------------------- - -The most recent version of this driver will hopefully always be available at - http://www.zabbo.net/maestro/ - -I will try and maintain the most recent stable version of the driver -in both the stable and development kernel lines. - -ESS Maestro Chip Family ------------------------ - -There are 3 main variants of the ESS Maestro PCI sound chip. The first -is the Maestro 1. It was originally produced by Platform Tech as the -'AGOGO'. It can be recognized by Platform Tech's PCI ID 0x1285 with -0x0100 as the device ID. It was put on some sound boards and a few laptops. -ESS bought the design and cleaned it up as the Maestro 2. This starts -their marking with the ESS vendor ID 0x125D and the 'year' device IDs. -The Maestro 2 claims 0x1968 while the Maestro 2e has 0x1978. - -The various families of Maestro are mostly identical as far as this -driver is concerned. It doesn't touch the DSP parts that differ (though -it could for FM synthesis). - -Driver OSS Behavior --------------------- - -This OSS driver exports /dev/mixer and /dev/dsp to applications, which -mostly adhere to the OSS spec. This driver doesn't register itself -with /dev/sndstat, so don't expect information to appear there. - -The /dev/dsp device exported behaves almost as expected. Playback is -supported in all the various lovely formats. 8/16bit stereo/mono from -8khz to 48khz, and mmap()ing for playback behaves. Capture/recording -is limited due to oddities with the Maestro hardware. One can only -record in 16bit stereo. For recording the maestro uses non interleaved -stereo buffers so that mmap()ing the incoming data does not result in -a ring buffer of LRLR data. mmap()ing of the read buffers is therefore -disallowed until this can be cleaned up. - -/dev/mixer is an interface to the AC'97 codec on the Maestro. It is -worth noting that there are a variety of AC'97s that can be wired to -the Maestro. Which is used is entirely up to the hardware implementor. -This should only be visible to the user by the presence, or lack, of -'Bass' and 'Treble' sliders in the mixer. Not all AC'97s have them. - -The driver doesn't support MIDI or FM playback at the moment. Typically -the Maestro is wired to an MPU MIDI chip, but some hardware implementations -don't. We need to assemble a white list of hardware implementations that -have MIDI wired properly before we can claim to support it safely. - -Compiling and Installing ------------------------- - -With the drivers inclusion into the kernel, compiling and installing -is the same as most OSS/Lite modular sound drivers. Compilation -of the driver is enabled through the CONFIG_SOUND_MAESTRO variable -in the config system. - -It may be modular or statically linked. If it is modular it should be -installed with the rest of the modules for the kernel on the system. -Typically this will be in /lib/modules/ somewhere. 'alias sound maestro' -should also be added to your module configs (typically /etc/conf.modules) -if you're using modular OSS/Lite sound and want to default to using a -maestro chip. - -As this is a PCI device, the module does not need to be informed of -any IO or IRQ resources it should use, it devines these from the -system. Sometimes, on sucky PCs, the BIOS fails to allocated resources -for the maestro. This will result in a message like: - maestro: PCI subsystem reports IRQ 0, this might not be correct. -from the kernel. Should this happen the sound chip most likely will -not operate correctly. To solve this one has to dig through their BIOS -(typically entered by hitting a hot key at boot time) and figure out -what magic needs to happen so that the BIOS will reward the maestro with -an IRQ. This operation is incredibly system specific, so you're on your -own. Sometimes the magic lies in 'PNP Capable Operating System' settings. - -There are very few options to the driver. One is 'debug' which will -tell the driver to print minimal debugging information as it runs. This -can be collected with 'dmesg' or through the klogd daemon. - -The other, more interesting option, is 'dsps_order'. Typically at -install time the driver will only register one available /dev/dsp device -for its use. The 'dsps_order' module parameter allows for more devices -to be allocated, as a power of two. Up to 4 devices can be registered -( dsps_order=2 ). These devices act as fully distinct units and use -separate channels in the maestro. - -Power Management ----------------- - -As of version 0.14, this driver has a minimal understanding of PCI -Power Management. If it finds a valid power management capability -on the PCI device it will attempt to use the power management -functions of the maestro. It will only do this on Maestro 2Es and -only on machines that are known to function well. You can -force the use of power management by setting the 'use_pm' module -option to 1, or can disable it entirely by setting it to 0. - -When using power management, the driver does a few things -differently. It will keep the chip in a lower power mode -when the module is inserted but /dev/dsp is not open. This -allows the mixer to function but turns off the clocks -on other parts of the chip. When /dev/dsp is opened the chip -is brought into full power mode, and brought back down -when it is closed. It also powers down the chip entirely -when the module is removed or the machine is shutdown. This -can have nonobvious consequences. CD audio may not work -after a power managing driver is removed. Also, software that -doesn't understand power management may not be able to talk -to the powered down chip until the machine goes through a hard -reboot to bring it back. - -.. more details .. ------------------- - -drivers/sound/maestro.c contains comments that hopefully explain -the maestro implementation. diff -Nru a/Documentation/sound/Maestro3 b/Documentation/sound/Maestro3 --- a/Documentation/sound/Maestro3 Thu Mar 7 18:17:41 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,84 +0,0 @@ - An OSS/Lite Driver for the ESS Maestro3 family of sound chips - - Zach Brown, January 2001 - -Driver Status and Availability ------------------------------- - -The most recent version of this driver will hopefully always be available at - http://www.zabbo.net/maestro3/ - -I will try and maintain the most recent stable version of the driver -in both the stable and development kernel lines. - -Historically I've sucked pretty hard at actually doing that, however. - -ESS Maestro3 Chip Family ------------------------ - -The 'Maestro3' is much like the Maestro2 chip. The noted improvement -is the removal of the silicon in the '2' that did PCM mixing. All that -work is now done through a custom DSP called the ASSP, the Asynchronus -Specific Signal Processor. - -The 'Allegro' is a baby version of the Maestro3. I'm not entirely clear -on the extent of the differences, but the driver supports them both :) - -The 'Allegro' shows up as PCI ID 0x1988 and the Maestro3 as 0x1998, -both under ESS's vendor ID of 0x125D. The Maestro3 can also show up as -0x199a when hardware strapping is used. - -The chip can also act as a multi function device. The modem IDs follow -the audio multimedia device IDs. (so the modem part of an Allegro shows -up as 0x1989) - -Driver OSS Behavior --------------------- - -This OSS driver exports /dev/mixer and /dev/dsp to applications, which -mostly adhere to the OSS spec. This driver doesn't register itself -with /dev/sndstat, so don't expect information to appear there. - -The /dev/dsp device exported behaves as expected. Playback is -supported in all the various lovely formats. 8/16bit stereo/mono from -8khz to 48khz, with both read()/write(), and mmap(). - -/dev/mixer is an interface to the AC'97 codec on the Maestro3. It is -worth noting that there are a variety of AC'97s that can be wired to -the Maestro3. Which is used is entirely up to the hardware implementor. -This should only be visible to the user by the presence, or lack, of -'Bass' and 'Treble' sliders in the mixer. Not all AC'97s have them. -The Allegro has an onchip AC'97. - -The driver doesn't support MIDI or FM playback at the moment. - -Compiling and Installing ------------------------- - -With the drivers inclusion into the kernel, compiling and installing -is the same as most OSS/Lite modular sound drivers. Compilation -of the driver is enabled through the CONFIG_SOUND_MAESTRO3 variable -in the config system. - -It may be modular or statically linked. If it is modular it should be -installed with the rest of the modules for the kernel on the system. -Typically this will be in /lib/modules/ somewhere. 'alias sound-slot-0 -maestro3' should also be added to your module configs (typically -/etc/modules.conf) if you're using modular OSS/Lite sound and want to -default to using a maestro3 chip. - -There are very few options to the driver. One is 'debug' which will -tell the driver to print minimal debugging information as it runs. This -can be collected with 'dmesg' or through the klogd daemon. - -The other is 'external_amp', which tells the driver to attempt to enable -an external amplifier. This defaults to '1', you can tell the driver -not to bother enabling such an amplifier by setting it to '0'. - -Power Management ----------------- - -This driver has a minimal understanding of PCI Power Management. It will -try and power down the chip when the system is suspended, and power -it up with it is resumed. It will also try and power down the chip -when the machine is shut down. diff -Nru a/Documentation/sound/MultiSound b/Documentation/sound/MultiSound --- a/Documentation/sound/MultiSound Thu Mar 7 18:17:45 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1137 +0,0 @@ -#! /bin/sh -# -# Turtle Beach MultiSound Driver Notes -# -- Andrew Veliath -# -# Last update: September 10, 1998 -# Corresponding msnd driver: 0.8.3 -# -# ** This file is a README (top part) and shell archive (bottom part). -# The corresponding archived utility sources can be unpacked by -# running `sh MultiSound' (the utilities are only needed for the -# Pinnacle and Fiji cards). ** -# -# -# -=-=- Getting Firmware -=-=- -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# See the section `Obtaining and Creating Firmware Files' in this -# document for instructions on obtaining the necessary firmware -# files. -# -# -# Supported Features -# ~~~~~~~~~~~~~~~~~~ -# -# Currently, full-duplex digital audio (/dev/dsp only, /dev/audio is -# not currently available) and mixer functionality (/dev/mixer) are -# supported (memory mapped digital audio is not yet supported). -# Digital transfers and monitoring can be done as well if you have -# the digital daughterboard (see the section on using the S/PDIF port -# for more information). -# -# Support for the Turtle Beach MultiSound Hurricane architecture is -# composed of the following modules (these can also operate compiled -# into the kernel): -# -# msnd - MultiSound base (requires soundcore) -# -# msnd_classic - Base audio/mixer support for Classic, Monetery and -# Tahiti cards -# -# msnd_pinnacle - Base audio/mixer support for Pinnacle and Fiji cards -# -# -# Important Notes - Read Before Using -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# The firmware files are not included (may change in future). You -# must obtain these images from Turtle Beach (they are included in -# the MultiSound Development Kits), and place them in /etc/sound for -# example, and give the full paths in the Linux configuration. If -# you are compiling in support for the MultiSound driver rather than -# using it as a module, these firmware files must be accessible -# during kernel compilation. -# -# Please note these files must be binary files, not assembler. See -# the section later in this document for instructions to obtain these -# files. -# -# -# Configuring Card Resources -# ~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# ** This section is very important, as your card may not work at all -# or your machine may crash if you do not do this correctly. ** -# -# * Classic/Monterey/Tahiti -# -# These cards are configured through the driver msnd_classic. You must -# know the io port, then the driver will select the irq and memory resources -# on the card. It is up to you to know if these are free locations or now, -# a conflict can lock the machine up. -# -# * Pinnacle/Fiji -# -# The Pinnacle and Fiji cards have an extra config port, either -# 0x250, 0x260 or 0x270. This port can be disabled to have the card -# configured strictly through PnP, however you lose the ability to -# access the IDE controller and joystick devices on this card when -# using PnP. The included pinnaclecfg program in this shell archive -# can be used to configure the card in non-PnP mode, and in PnP mode -# you can use isapnptools. These are described briefly here. -# -# pinnaclecfg is not required; you can use the msnd_pinnacle module -# to fully configure the card as well. However, pinnaclecfg can be -# used to change the resource values of a particular device after the -# msnd_pinnacle module has been loaded. If you are compiling the -# driver into the kernel, you must set these values during compile -# time, however other peripheral resource values can be changed with -# the pinnaclecfg program after the kernel is loaded. -# -# -# *** PnP mode -# -# Use pnpdump to obtain a sample configuration if you can; I was able -# to obtain one with the command `pnpdump 1 0x203' -- this may vary -# for you (running pnpdump by itself did not work for me). Then, -# edit this file and use isapnp to uncomment and set the card values. -# Use these values when inserting the msnd_pinnacle module. Using -# this method, you can set the resources for the DSP and the Kurzweil -# synth (Pinnacle). Since Linux does not directly support PnP -# devices, you may have difficulty when using the card in PnP mode -# when it the driver is compiled into the kernel. Using non-PnP mode -# is preferable in this case. -# -# Here is an example mypinnacle.conf for isapnp that sets the card to -# io base 0x210, irq 5 and mem 0xd8000, and also sets the Kurzweil -# synth to 0x330 and irq 9 (may need editing for your system): -# -# (READPORT 0x0203) -# (CSN 2) -# (IDENTIFY *) -# -# # DSP -# (CONFIGURE BVJ0440/-1 (LD 0 -# (INT 0 (IRQ 5 (MODE +E))) (IO 0 (BASE 0x0210)) (MEM 0 (BASE 0x0d8000)) -# (ACT Y))) -# -# # Kurzweil Synth (Pinnacle Only) -# (CONFIGURE BVJ0440/-1 (LD 1 -# (IO 0 (BASE 0x0330)) (INT 0 (IRQ 9 (MODE +E))) -# (ACT Y))) -# -# (WAITFORKEY) -# -# -# *** Non-PnP mode -# -# The second way is by running the card in non-PnP mode. This -# actually has some advantages in that you can access some other -# devices on the card, such as the joystick and IDE controller. To -# configure the card, unpack this shell archive and build the -# pinnaclecfg program. Using this program, you can assign the -# resource values to the card's devices, or disable the devices. As -# an alternative to using pinnaclecfg, you can specify many of the -# configuration values when loading the msnd_pinnacle module (or -# during kernel configuration when compiling the driver into the -# kernel). -# -# If you specify cfg=0x250 for the msnd_pinnacle module, it -# automatically configure the card to the given io, irq and memory -# values using that config port (the config port is jumper selectable -# on the card to 0x250, 0x260 or 0x270). -# -# See the `msnd_pinnacle Additional Options' section below for more -# information on these parameters (also, if you compile the driver -# directly into the kernel, these extra parameters can be useful -# here). -# -# -# ** It is very easy to cause problems in your machine if you choose a -# resource value which is incorrect. ** -# -# -# Examples -# ~~~~~~~~ -# -# * MultiSound Classic/Monterey/Tahiti: -# -# modprobe soundcore -# insmod msnd -# insmod msnd_classic io=0x290 irq=7 mem=0xd0000 -# -# * MultiSound Pinnacle in PnP mode: -# -# modprobe soundcore -# insmod msnd -# isapnp mypinnacle.conf -# insmod msnd_pinnacle io=0x210 irq=5 mem=0xd8000 <-- match mypinnacle.conf values -# -# * MultiSound Pinnacle in non-PnP mode (replace 0x250 with your configuration port, -# one of 0x250, 0x260 or 0x270): -# -# insmod soundcore -# insmod msnd -# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 -# -# * To use the MPU-compatible Kurzweil synth on the Pinnacle in PnP -# mode, add the following (assumes you did `isapnp mypinnacle.conf'): -# -# insmod sound -# insmod mpu401 io=0x330 irq=9 <-- match mypinnacle.conf values -# -# * To use the MPU-compatible Kurzweil synth on the Pinnacle in non-PnP -# mode, add the following. Note how we first configure the peripheral's -# resources, _then_ install a Linux driver for it: -# -# insmod sound -# pinnaclecfg 0x250 mpu 0x330 9 -# insmod mpu401 io=0x330 irq=9 -# -# -- OR you can use the following sequence without pinnaclecfg in non-PnP mode: -# -# insmod soundcore -# insmod msnd -# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 mpu_io=0x330 mpu_irq=9 -# insmod sound -# insmod mpu401 io=0x330 irq=9 -# -# * To setup the joystick port on the Pinnacle in non-PnP mode (though -# you have to find the actual Linux joystick driver elsewhere), you -# can use pinnaclecfg: -# -# pinnaclecfg 0x250 joystick 0x200 -# -# -- OR you can configure this using msnd_pinnacle with the following: -# -# insmod soundcore -# insmod msnd -# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 joystick_io=0x200 -# -# -# msnd_classic, msnd_pinnacle Required Options -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# If the following options are not given, the module will not load. -# Examine the kernel message log for informative error messages. -# WARNING--probing isn't supported so try to make sure you have the -# correct shared memory area, otherwise you may experience problems. -# -# io I/O base of DSP, e.g. io=0x210 -# irq IRQ number, e.g. irq=5 -# mem Shared memory area, e.g. mem=0xd8000 -# -# -# msnd_classic, msnd_pinnacle Additional Options -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# fifosize The digital audio FIFOs, in kilobytes. If not -# specified, the default will be used. Increasing -# this value will reduce the chance of a FIFO -# underflow at the expense of increasing overall -# latency. For example, fifosize=512 will -# allocate 512kB read and write FIFOs (1MB total). -# While this may reduce dropouts, a heavy machine -# load will undoubtedly starve the FIFO of data -# and you will eventually get dropouts. One -# option is to alter the scheduling priority of -# the playback process, using `nice' or some form -# of POSIX soft real-time scheduling. -# -# calibrate_signal Setting this to one calibrates the ADCs to the -# signal, zero calibrates to the card (defaults -# to zero). -# -# -# msnd_pinnacle Additional Options -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# digital Specify digital=1 to enable the S/PDIF input -# if you have the digital daughterboard -# adapter. This will enable access to the -# DIGITAL1 input for the soundcard in the mixer. -# Some mixer programs might have trouble setting -# the DIGITAL1 source as an input. If you have -# trouble, you can try the setdigital.c program -# at the bottom of this document. -# -# cfg Non-PnP configuration port for the Pinnacle -# and Fiji (typically 0x250, 0x260 or 0x270, -# depending on the jumper configuration). If -# this option is omitted, then it is assumed -# that the card is in PnP mode, and that the -# specified DSP resource values are already -# configured with PnP (i.e. it won't attempt to -# do any sort of configuration). -# -# When the Pinnacle is in non-PnP mode, you can use the following -# options to configure particular devices. If a full specification -# for a device is not given, then the device is not configured. Note -# that you still must use a Linux driver for any of these devices -# once their resources are setup (such as the Linux joystick driver, -# or the MPU401 driver from OSS for the Kurzweil synth). -# -# mpu_io I/O port of MPU (on-board Kurzweil synth) -# mpu_irq IRQ of MPU (on-board Kurzweil synth) -# ide_io0 First I/O port of IDE controller -# ide_io1 Second I/O port of IDE controller -# ide_irq IRQ IDE controller -# joystick_io I/O port of joystick -# -# -# Obtaining and Creating Firmware Files -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For the Classic/Tahiti/Monterey -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Download to /tmp and unzip the following file from Turtle Beach: -# -# ftp://ftp.voyetra.com/pub/tbs/msndcl/msndvkit.zip -# -# When unzipped, unzip the file named MsndFiles.zip. Then copy the -# following firmware files to /etc/sound (note the file renaming): -# -# cp DSPCODE/MSNDINIT.BIN /etc/sound/msndinit.bin -# cp DSPCODE/MSNDPERM.REB /etc/sound/msndperm.bin -# -# When configuring the Linux kernel, specify /etc/sound/msndinit.bin and -# /etc/sound/msndperm.bin for the two firmware files (Linux kernel -# versions older than 2.2 do not ask for firmware paths, and are -# hardcoded to /etc/sound). -# -# If you are compiling the driver into the kernel, these files must -# be accessible during compilation, but will not be needed later. -# The files must remain, however, if the driver is used as a module. -# -# -# For the Pinnacle/Fiji -# ~~~~~~~~~~~~~~~~~~~~~ -# -# Download to /tmp and unzip the following file from Turtle Beach (be -# sure to use the entire URL; some have had trouble navigating to the -# URL): -# -# ftp://ftp.voyetra.com/pub/tbs/pinn/pnddk100.zip -# -# Unpack this shell archive, and run make in the created directory -# (you need a C compiler and flex to build the utilities). This -# should give you the executables conv, pinnaclecfg and setdigital. -# conv is only used temporarily here to create the firmware files, -# while pinnaclecfg is used to configure the Pinnacle or Fiji card in -# non-PnP mode, and setdigital can be used to set the S/PDIF input on -# the mixer (pinnaclecfg and setdigital should be copied to a -# convenient place, possibly run during system initialization). -# -# To generating the firmware files with the `conv' program, we create -# the binary firmware files by doing the following conversion -# (assuming the archive unpacked into a directory named PINNDDK): -# -# ./conv < PINNDDK/dspcode/pndspini.asm > /etc/sound/pndspini.bin -# ./conv < PINNDDK/dspcode/pndsperm.asm > /etc/sound/pndsperm.bin -# -# The conv (and conv.l) program is not needed after conversion and can -# be safely deleted. Then, when configuring the Linux kernel, specify -# /etc/sound/pndspini.bin and /etc/sound/pndsperm.bin for the two -# firmware files (Linux kernel versions older than 2.2 do not ask for -# firmware paths, and are hardcoded to /etc/sound). -# -# If you are compiling the driver into the kernel, these files must -# be accessible during compilation, but will not be needed later. -# The files must remain, however, if the driver is used as a module. -# -# -# Using Digital I/O with the S/PDIF Port -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# If you have a Pinnacle or Fiji with the digital daughterboard and -# want to set it as the input source, you can use this program if you -# have trouble trying to do it with a mixer program (be sure to -# insert the module with the digital=1 option, or say Y to the option -# during compiled-in kernel operation). Upon selection of the S/PDIF -# port, you should be able monitor and record from it. -# -# There is something to note about using the S/PDIF port. Digital -# timing is taken from the digital signal, so if a signal is not -# connected to the port and it is selected as recording input, you -# will find PCM playback to be distorted in playback rate. Also, -# attempting to record at a sampling rate other than the DAT rate may -# be problematic (i.e. trying to record at 8000Hz when the DAT signal -# is 44100Hz). If you have a problem with this, set the recording -# input to analog if you need to record at a rate other than that of -# the DAT rate. -# -# -# -- Shell archive attached below, just run `sh MultiSound' to extract. -# Contains Pinnacle/Fiji utilities to convert firmware, configure -# in non-PnP mode, and select the DIGITAL1 input for the mixer. -# -# -#!/bin/sh -# This is a shell archive (produced by GNU sharutils 4.2). -# To extract the files from this archive, save it to some FILE, remove -# everything before the `!/bin/sh' line above, then type `sh FILE'. -# -# Made on 1998-12-04 10:07 EST by . -# Source directory was `/home/andrewtv/programming/pinnacle/pinnacle'. -# -# Existing files will *not* be overwritten unless `-c' is specified. -# -# This shar contains: -# length mode name -# ------ ---------- ------------------------------------------ -# 2046 -rw-rw-r-- MultiSound.d/setdigital.c -# 10235 -rw-rw-r-- MultiSound.d/pinnaclecfg.c -# 106 -rw-rw-r-- MultiSound.d/Makefile -# 141 -rw-rw-r-- MultiSound.d/conv.l -# 1472 -rw-rw-r-- MultiSound.d/msndreset.c -# -save_IFS="${IFS}" -IFS="${IFS}:" -gettext_dir=FAILED -locale_dir=FAILED -first_param="$1" -for dir in $PATH -do - if test "$gettext_dir" = FAILED && test -f $dir/gettext \ - && ($dir/gettext --version >/dev/null 2>&1) - then - set `$dir/gettext --version 2>&1` - if test "$3" = GNU - then - gettext_dir=$dir - fi - fi - if test "$locale_dir" = FAILED && test -f $dir/shar \ - && ($dir/shar --print-text-domain-dir >/dev/null 2>&1) - then - locale_dir=`$dir/shar --print-text-domain-dir` - fi -done -IFS="$save_IFS" -if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED -then - echo=echo -else - TEXTDOMAINDIR=$locale_dir - export TEXTDOMAINDIR - TEXTDOMAIN=sharutils - export TEXTDOMAIN - echo="$gettext_dir/gettext -s" -fi -touch -am 1231235999 $$.touch >/dev/null 2>&1 -if test ! -f 1231235999 && test -f $$.touch; then - shar_touch=touch -else - shar_touch=: - echo - $echo 'WARNING: not restoring timestamps. Consider getting and' - $echo "installing GNU \`touch', distributed in GNU File Utilities..." - echo -fi -rm -f 1231235999 $$.touch -# -if mkdir _sh01426; then - $echo 'x -' 'creating lock directory' -else - $echo 'failed to create lock directory' - exit 1 -fi -# ============= MultiSound.d/setdigital.c ============== -if test ! -d 'MultiSound.d'; then - $echo 'x -' 'creating directory' 'MultiSound.d' - mkdir 'MultiSound.d' -fi -if test -f 'MultiSound.d/setdigital.c' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/setdigital.c' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/setdigital.c' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/setdigital.c' && -/********************************************************************* -X * -X * setdigital.c - sets the DIGITAL1 input for a mixer -X * -X * Copyright (C) 1998 Andrew Veliath -X * -X * This program is free software; you can redistribute it and/or modify -X * it under the terms of the GNU General Public License as published by -X * the Free Software Foundation; either version 2 of the License, or -X * (at your option) any later version. -X * -X * This program is distributed in the hope that it will be useful, -X * but WITHOUT ANY WARRANTY; without even the implied warranty of -X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -X * GNU General Public License for more details. -X * -X * You should have received a copy of the GNU General Public License -X * along with this program; if not, write to the Free Software -X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -X * -X ********************************************************************/ -X -#include -#include -#include -#include -#include -#include -#include -X -int main(int argc, char *argv[]) -{ -X int fd; -X unsigned long recmask, recsrc; -X -X if (argc != 2) { -X fprintf(stderr, "usage: setdigital \n"); -X exit(1); -X } -X -X if ((fd = open(argv[1], O_RDWR)) < 0) { -X perror(argv[1]); -X exit(1); -X } -X -X if (ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask) < 0) { -X fprintf(stderr, "error: ioctl read recording mask failed\n"); -X perror("ioctl"); -X close(fd); -X exit(1); -X } -X -X if (!(recmask & SOUND_MASK_DIGITAL1)) { -X fprintf(stderr, "error: cannot find DIGITAL1 device in mixer\n"); -X close(fd); -X exit(1); -X } -X -X if (ioctl(fd, SOUND_MIXER_READ_RECSRC, &recsrc) < 0) { -X fprintf(stderr, "error: ioctl read recording source failed\n"); -X perror("ioctl"); -X close(fd); -X exit(1); -X } -X -X recsrc |= SOUND_MASK_DIGITAL1; -X -X if (ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &recsrc) < 0) { -X fprintf(stderr, "error: ioctl write recording source failed\n"); -X perror("ioctl"); -X close(fd); -X exit(1); -X } -X -X close(fd); -X -X return 0; -} -SHAR_EOF - $shar_touch -am 1204092598 'MultiSound.d/setdigital.c' && - chmod 0664 'MultiSound.d/setdigital.c' || - $echo 'restore of' 'MultiSound.d/setdigital.c' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/setdigital.c:' 'MD5 check failed' -e87217fc3e71288102ba41fd81f71ec4 MultiSound.d/setdigital.c -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/setdigital.c'`" - test 2046 -eq "$shar_count" || - $echo 'MultiSound.d/setdigital.c:' 'original size' '2046,' 'current size' "$shar_count!" - fi -fi -# ============= MultiSound.d/pinnaclecfg.c ============== -if test -f 'MultiSound.d/pinnaclecfg.c' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/pinnaclecfg.c' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/pinnaclecfg.c' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/pinnaclecfg.c' && -/********************************************************************* -X * -X * pinnaclecfg.c - Pinnacle/Fiji Device Configuration Program -X * -X * This is for NON-PnP mode only. For PnP mode, use isapnptools. -X * -X * This is Linux-specific, and must be run with root permissions. -X * -X * Part of the Turtle Beach MultiSound Sound Card Driver for Linux -X * -X * Copyright (C) 1998 Andrew Veliath -X * -X * This program is free software; you can redistribute it and/or modify -X * it under the terms of the GNU General Public License as published by -X * the Free Software Foundation; either version 2 of the License, or -X * (at your option) any later version. -X * -X * This program is distributed in the hope that it will be useful, -X * but WITHOUT ANY WARRANTY; without even the implied warranty of -X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -X * GNU General Public License for more details. -X * -X * You should have received a copy of the GNU General Public License -X * along with this program; if not, write to the Free Software -X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -X * -X ********************************************************************/ -X -#include -#include -#include -#include -#include -#include -#include -X -#define IREG_LOGDEVICE 0x07 -#define IREG_ACTIVATE 0x30 -#define LD_ACTIVATE 0x01 -#define LD_DISACTIVATE 0x00 -#define IREG_EECONTROL 0x3F -#define IREG_MEMBASEHI 0x40 -#define IREG_MEMBASELO 0x41 -#define IREG_MEMCONTROL 0x42 -#define IREG_MEMRANGEHI 0x43 -#define IREG_MEMRANGELO 0x44 -#define MEMTYPE_8BIT 0x00 -#define MEMTYPE_16BIT 0x02 -#define MEMTYPE_RANGE 0x00 -#define MEMTYPE_HIADDR 0x01 -#define IREG_IO0_BASEHI 0x60 -#define IREG_IO0_BASELO 0x61 -#define IREG_IO1_BASEHI 0x62 -#define IREG_IO1_BASELO 0x63 -#define IREG_IRQ_NUMBER 0x70 -#define IREG_IRQ_TYPE 0x71 -#define IRQTYPE_HIGH 0x02 -#define IRQTYPE_LOW 0x00 -#define IRQTYPE_LEVEL 0x01 -#define IRQTYPE_EDGE 0x00 -X -#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF)) -#define LOBYTE(w) ((BYTE)(w)) -#define MAKEWORD(low,hi) ((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8))) -X -typedef __u8 BYTE; -typedef __u16 USHORT; -typedef __u16 WORD; -X -static int config_port = -1; -X -static int msnd_write_cfg(int cfg, int reg, int value) -{ -X outb(reg, cfg); -X outb(value, cfg + 1); -X if (value != inb(cfg + 1)) { -X fprintf(stderr, "error: msnd_write_cfg: I/O error\n"); -X return -EIO; -X } -X return 0; -} -X -static int msnd_read_cfg(int cfg, int reg) -{ -X outb(reg, cfg); -X return inb(cfg + 1); -} -X -static int msnd_write_cfg_io0(int cfg, int num, WORD io) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io))) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io))) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_io0(int cfg, int num, WORD *io) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X -X *io = MAKEWORD(msnd_read_cfg(cfg, IREG_IO0_BASELO), -X msnd_read_cfg(cfg, IREG_IO0_BASEHI)); -X -X return 0; -} -X -static int msnd_write_cfg_io1(int cfg, int num, WORD io) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io))) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io))) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_io1(int cfg, int num, WORD *io) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X -X *io = MAKEWORD(msnd_read_cfg(cfg, IREG_IO1_BASELO), -X msnd_read_cfg(cfg, IREG_IO1_BASEHI)); -X -X return 0; -} -X -static int msnd_write_cfg_irq(int cfg, int num, WORD irq) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq))) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE)) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_irq(int cfg, int num, WORD *irq) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X -X *irq = msnd_read_cfg(cfg, IREG_IRQ_NUMBER); -X -X return 0; -} -X -static int msnd_write_cfg_mem(int cfg, int num, int mem) -{ -X WORD wmem; -X -X mem >>= 8; -X mem &= 0xfff; -X wmem = (WORD)mem; -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem))) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem))) -X return -EIO; -X if (wmem && msnd_write_cfg(cfg, IREG_MEMCONTROL, (MEMTYPE_HIADDR | MEMTYPE_16BIT))) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_mem(int cfg, int num, int *mem) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X -X *mem = MAKEWORD(msnd_read_cfg(cfg, IREG_MEMBASELO), -X msnd_read_cfg(cfg, IREG_MEMBASEHI)); -X *mem <<= 8; -X -X return 0; -} -X -static int msnd_activate_logical(int cfg, int num) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE)) -X return -EIO; -X return 0; -} -X -static int msnd_write_cfg_logical(int cfg, int num, WORD io0, WORD io1, WORD irq, int mem) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg_io0(cfg, num, io0)) -X return -EIO; -X if (msnd_write_cfg_io1(cfg, num, io1)) -X return -EIO; -X if (msnd_write_cfg_irq(cfg, num, irq)) -X return -EIO; -X if (msnd_write_cfg_mem(cfg, num, mem)) -X return -EIO; -X if (msnd_activate_logical(cfg, num)) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_logical(int cfg, int num, WORD *io0, WORD *io1, WORD *irq, int *mem) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_read_cfg_io0(cfg, num, io0)) -X return -EIO; -X if (msnd_read_cfg_io1(cfg, num, io1)) -X return -EIO; -X if (msnd_read_cfg_irq(cfg, num, irq)) -X return -EIO; -X if (msnd_read_cfg_mem(cfg, num, mem)) -X return -EIO; -X return 0; -} -X -static void usage(void) -{ -X fprintf(stderr, -X "\n" -X "pinnaclecfg 1.0\n" -X "\n" -X "usage: pinnaclecfg [device config]\n" -X "\n" -X "This is for use with the card in NON-PnP mode only.\n" -X "\n" -X "Available devices (not all available for Fiji):\n" -X "\n" -X " Device Description\n" -X " -------------------------------------------------------------------\n" -X " reset Reset all devices (i.e. disable)\n" -X " show Display current device configurations\n" -X "\n" -X " dsp Audio device\n" -X " mpu Internal Kurzweil synth\n" -X " ide On-board IDE controller\n" -X " joystick Joystick port\n" -X "\n"); -X exit(1); -} -X -static int cfg_reset(void) -{ -X int i; -X -X for (i = 0; i < 4; ++i) -X msnd_write_cfg_logical(config_port, i, 0, 0, 0, 0); -X -X return 0; -} -X -static int cfg_show(void) -{ -X int i; -X int count = 0; -X -X for (i = 0; i < 4; ++i) { -X WORD io0, io1, irq; -X int mem; -X msnd_read_cfg_logical(config_port, i, &io0, &io1, &irq, &mem); -X switch (i) { -X case 0: -X if (io0 || irq || mem) { -X printf("dsp 0x%x %d 0x%x\n", io0, irq, mem); -X ++count; -X } -X break; -X case 1: -X if (io0 || irq) { -X printf("mpu 0x%x %d\n", io0, irq); -X ++count; -X } -X break; -X case 2: -X if (io0 || io1 || irq) { -X printf("ide 0x%x 0x%x %d\n", io0, io1, irq); -X ++count; -X } -X break; -X case 3: -X if (io0) { -X printf("joystick 0x%x\n", io0); -X ++count; -X } -X break; -X } -X } -X -X if (count == 0) -X fprintf(stderr, "no devices configured\n"); -X -X return 0; -} -X -static int cfg_dsp(int argc, char *argv[]) -{ -X int io, irq, mem; -X -X if (argc < 3 || -X sscanf(argv[0], "0x%x", &io) != 1 || -X sscanf(argv[1], "%d", &irq) != 1 || -X sscanf(argv[2], "0x%x", &mem) != 1) -X usage(); -X -X if (!(io == 0x290 || -X io == 0x260 || -X io == 0x250 || -X io == 0x240 || -X io == 0x230 || -X io == 0x220 || -X io == 0x210 || -X io == 0x3e0)) { -X fprintf(stderr, "error: io must be one of " -X "210, 220, 230, 240, 250, 260, 290, or 3E0\n"); -X usage(); -X } -X -X if (!(irq == 5 || -X irq == 7 || -X irq == 9 || -X irq == 10 || -X irq == 11 || -X irq == 12)) { -X fprintf(stderr, "error: irq must be one of " -X "5, 7, 9, 10, 11 or 12\n"); -X usage(); -X } -X -X if (!(mem == 0xb0000 || -X mem == 0xc8000 || -X mem == 0xd0000 || -X mem == 0xd8000 || -X mem == 0xe0000 || -X mem == 0xe8000)) { -X fprintf(stderr, "error: mem must be one of " -X "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n"); -X usage(); -X } -X -X return msnd_write_cfg_logical(config_port, 0, io, 0, irq, mem); -} -X -static int cfg_mpu(int argc, char *argv[]) -{ -X int io, irq; -X -X if (argc < 2 || -X sscanf(argv[0], "0x%x", &io) != 1 || -X sscanf(argv[1], "%d", &irq) != 1) -X usage(); -X -X return msnd_write_cfg_logical(config_port, 1, io, 0, irq, 0); -} -X -static int cfg_ide(int argc, char *argv[]) -{ -X int io0, io1, irq; -X -X if (argc < 3 || -X sscanf(argv[0], "0x%x", &io0) != 1 || -X sscanf(argv[0], "0x%x", &io1) != 1 || -X sscanf(argv[1], "%d", &irq) != 1) -X usage(); -X -X return msnd_write_cfg_logical(config_port, 2, io0, io1, irq, 0); -} -X -static int cfg_joystick(int argc, char *argv[]) -{ -X int io; -X -X if (argc < 1 || -X sscanf(argv[0], "0x%x", &io) != 1) -X usage(); -X -X return msnd_write_cfg_logical(config_port, 3, io, 0, 0, 0); -} -X -int main(int argc, char *argv[]) -{ -X char *device; -X int rv = 0; -X -X --argc; ++argv; -X -X if (argc < 2) -X usage(); -X -X sscanf(argv[0], "0x%x", &config_port); -X if (config_port != 0x250 && config_port != 0x260 && config_port != 0x270) { -X fprintf(stderr, "error: must be 0x250, 0x260 or 0x270\n"); -X exit(1); -X } -X if (ioperm(config_port, 2, 1)) { -X perror("ioperm"); -X fprintf(stderr, "note: pinnaclecfg must be run as root\n"); -X exit(1); -X } -X device = argv[1]; -X -X argc -= 2; argv += 2; -X -X if (strcmp(device, "reset") == 0) -X rv = cfg_reset(); -X else if (strcmp(device, "show") == 0) -X rv = cfg_show(); -X else if (strcmp(device, "dsp") == 0) -X rv = cfg_dsp(argc, argv); -X else if (strcmp(device, "mpu") == 0) -X rv = cfg_mpu(argc, argv); -X else if (strcmp(device, "ide") == 0) -X rv = cfg_ide(argc, argv); -X else if (strcmp(device, "joystick") == 0) -X rv = cfg_joystick(argc, argv); -X else { -X fprintf(stderr, "error: unknown device %s\n", device); -X usage(); -X } -X -X if (rv) -X fprintf(stderr, "error: device configuration failed\n"); -X -X return 0; -} -SHAR_EOF - $shar_touch -am 1204092598 'MultiSound.d/pinnaclecfg.c' && - chmod 0664 'MultiSound.d/pinnaclecfg.c' || - $echo 'restore of' 'MultiSound.d/pinnaclecfg.c' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/pinnaclecfg.c:' 'MD5 check failed' -366bdf27f0db767a3c7921d0a6db20fe MultiSound.d/pinnaclecfg.c -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/pinnaclecfg.c'`" - test 10235 -eq "$shar_count" || - $echo 'MultiSound.d/pinnaclecfg.c:' 'original size' '10235,' 'current size' "$shar_count!" - fi -fi -# ============= MultiSound.d/Makefile ============== -if test -f 'MultiSound.d/Makefile' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/Makefile' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/Makefile' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/Makefile' && -CC = gcc -CFLAGS = -O -PROGS = setdigital msndreset pinnaclecfg conv -X -all: $(PROGS) -X -clean: -X rm -f $(PROGS) -SHAR_EOF - $shar_touch -am 1204092398 'MultiSound.d/Makefile' && - chmod 0664 'MultiSound.d/Makefile' || - $echo 'restore of' 'MultiSound.d/Makefile' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/Makefile:' 'MD5 check failed' -76ca8bb44e3882edcf79c97df6c81845 MultiSound.d/Makefile -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/Makefile'`" - test 106 -eq "$shar_count" || - $echo 'MultiSound.d/Makefile:' 'original size' '106,' 'current size' "$shar_count!" - fi -fi -# ============= MultiSound.d/conv.l ============== -if test -f 'MultiSound.d/conv.l' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/conv.l' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/conv.l' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/conv.l' && -%% -[ \n\t,\r] -\;.* -DB -[0-9A-Fa-f]+H { int n; sscanf(yytext, "%xH", &n); printf("%c", n); } -%% -int yywrap() { return 1; } -main() { yylex(); } -SHAR_EOF - $shar_touch -am 0828231798 'MultiSound.d/conv.l' && - chmod 0664 'MultiSound.d/conv.l' || - $echo 'restore of' 'MultiSound.d/conv.l' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/conv.l:' 'MD5 check failed' -d2411fc32cd71a00dcdc1f009e858dd2 MultiSound.d/conv.l -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/conv.l'`" - test 141 -eq "$shar_count" || - $echo 'MultiSound.d/conv.l:' 'original size' '141,' 'current size' "$shar_count!" - fi -fi -# ============= MultiSound.d/msndreset.c ============== -if test -f 'MultiSound.d/msndreset.c' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/msndreset.c' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/msndreset.c' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/msndreset.c' && -/********************************************************************* -X * -X * msndreset.c - resets the MultiSound card -X * -X * Copyright (C) 1998 Andrew Veliath -X * -X * This program is free software; you can redistribute it and/or modify -X * it under the terms of the GNU General Public License as published by -X * the Free Software Foundation; either version 2 of the License, or -X * (at your option) any later version. -X * -X * This program is distributed in the hope that it will be useful, -X * but WITHOUT ANY WARRANTY; without even the implied warranty of -X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -X * GNU General Public License for more details. -X * -X * You should have received a copy of the GNU General Public License -X * along with this program; if not, write to the Free Software -X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -X * -X ********************************************************************/ -X -#include -#include -#include -#include -#include -#include -#include -X -int main(int argc, char *argv[]) -{ -X int fd; -X -X if (argc != 2) { -X fprintf(stderr, "usage: msndreset \n"); -X exit(1); -X } -X -X if ((fd = open(argv[1], O_RDWR)) < 0) { -X perror(argv[1]); -X exit(1); -X } -X -X if (ioctl(fd, SOUND_MIXER_PRIVATE1, 0) < 0) { -X fprintf(stderr, "error: msnd ioctl reset failed\n"); -X perror("ioctl"); -X close(fd); -X exit(1); -X } -X -X close(fd); -X -X return 0; -} -SHAR_EOF - $shar_touch -am 1204100698 'MultiSound.d/msndreset.c' && - chmod 0664 'MultiSound.d/msndreset.c' || - $echo 'restore of' 'MultiSound.d/msndreset.c' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/msndreset.c:' 'MD5 check failed' -c52f876521084e8eb25e12e01dcccb8a MultiSound.d/msndreset.c -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/msndreset.c'`" - test 1472 -eq "$shar_count" || - $echo 'MultiSound.d/msndreset.c:' 'original size' '1472,' 'current size' "$shar_count!" - fi -fi -rm -fr _sh01426 -exit 0 diff -Nru a/Documentation/sound/NEWS b/Documentation/sound/NEWS --- a/Documentation/sound/NEWS Thu Mar 7 18:17:46 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,42 +0,0 @@ -Linux 2.4 Sound Changes -2000-September-25 -Christoph Hellwig, - - - -=== isapnp support - -The Linux 2.4 Kernel does have reliable in-kernel isapnp support. -Some drivers (sb.o, ad1816.o awe_wave.o) do now support automatically -detecting and configuring isapnp devices. -If you have a not yet supported isapnp soundcard, mail me the content -of '/proc/isapnp' on your system and some information about your card -and its driver(s) so I can try to get isapnp working for it. - - - -=== soundcard resources on kernel commandline - -Before Linux 2.4 you had to specify the resources for sounddrivers -statically linked into the kernel at compile time -(in make config/menuconfig/xconfig). In Linux 2.4 the ressources are -now specified at the boot-time kernel commandline (e.g. the lilo -'append=' line or everything that's after the kernel name in grub). -Read the Configure.help entry for your card for the parameters. - - -=== softoss is gone - -In Linux 2.4 the softoss in-kernel software synthesizer is no more aviable. -Use a user space software synthesizer like timidity instead. - - - -=== /dev/sndstat and /proc/sound are gone - -In older Linux versions those files exported some information about the -OSS/Free configuration to userspace. In Linux 2.3 they were removed because -they did not support the growing number of pci soundcards and there were -some general problems with this interface. - - diff -Nru a/Documentation/sound/NM256 b/Documentation/sound/NM256 --- a/Documentation/sound/NM256 Thu Mar 7 18:17:39 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,280 +0,0 @@ -======================================================= -Documentation for the NeoMagic 256AV/256ZX sound driver -======================================================= - -You're looking at version 1.1 of the driver. (Woohoo!) It has been -successfully tested against the following laptop models: - - Sony Z505S/Z505SX/Z505DX/Z505RX - Sony F150, F160, F180, F250, F270, F280, PCG-F26 - Dell Latitude CPi, CPt (various submodels) - -There are a few caveats, which is why you should read the entirety of -this document first. - -This driver was developed without any support or assistance from -NeoMagic. There is no warranty, expressed, implied, or otherwise. It -is free software in the public domain; feel free to use it, sell it, -give it to your best friends, even claim that you wrote it (but why?!) -but don't go whining to me, NeoMagic, Sony, Dell, or anyone else -when it blows up your computer. - -Version 1.1 contains a change to try and detect non-AC97 versions of -the hardware, and not install itself appropriately. It should also -reinitialize the hardware on an APM resume event, assuming that APM -was configured into your kernel. - -============ -Installation -============ - -Enable the sound drivers, the OSS sound drivers, and then the NM256 -driver. The NM256 driver *must* be configured as a module (it won't -give you any other choice). - -Next, do the usual "make modules" and "make modules_install". -Finally, insmod the soundcore, sound and nm256 modules. - -When the nm256 driver module is loaded, you should see a couple of -confirmation messages in the kernel logfile indicating that it found -the device (the device does *not* use any I/O ports or DMA channels). -Now try playing a wav file, futz with the CD-ROM if you have one, etc. - -The NM256 is entirely a PCI-based device, and all the necessary -information is automatically obtained from the card. It can only be -configured as a module in a vain attempt to prevent people from -hurting themselves. It works correctly if it shares an IRQ with -another device (it normally shares IRQ 9 with the builtin eepro100 -ethernet on the Sony Z505 laptops). - -It does not run the card in any sort of compatibility mode. It will -not work on laptops that have the SB16-compatible, AD1848-compatible -or CS4232-compatible codec/mixer; you will want to use the appropriate -compatible OSS driver with these chipsets. I cannot provide any -assistance with machines using the SB16, AD1848 or CS4232 compatible -versions. (The driver now attempts to detect the mixer version, and -will refuse to load if it believes the hardware is not -AC97-compatible.) - -The sound support is very basic, but it does include simultaneous -playback and record capability. The mixer support is also quite -simple, although this is in keeping with the rather limited -functionality of the chipset. - -There is no hardware synthesizer available, as the Losedows OPL-3 and -MIDI support is done via hardware emulation. - -Only three recording devices are available on the Sony: the -microphone, the CD-ROM input, and the volume device (which corresponds -to the stereo output). (Other devices may be available on other -models of laptops.) The Z505 series does not have a builtin CD-ROM, -so of course the CD-ROM input doesn't work. It does work on laptops -with a builtin CD-ROM drive. - -The mixer device does not appear to have any tone controls, at least -on the Z505 series. The mixer module checks for tone controls in the -AC97 mixer, and will enable them if they are available. - -============== -Known problems -============== - - * There are known problems with PCMCIA cards and the eepro100 ethernet - driver on the Z505S/Z505SX/Z505DX. Keep reading. - - * There are also potential problems with using a virtual X display, and - also problems loading the module after the X server has been started. - Keep reading. - - * The volume control isn't anywhere near linear. Sorry. This will be - fixed eventually, when I get sufficiently annoyed with it. (I doubt - it will ever be fixed now, since I've never gotten sufficiently - annoyed with it and nobody else seems to care.) - - * There are reports that the CD-ROM volume is very low. Since I do not - have a CD-ROM equipped laptop, I cannot test this (it's kinda hard to - do remotely). - - * Only 8 fixed-rate speeds are supported. This is mainly a chipset - limitation. It may be possible to support other speeds in the future. - - * There is no support for the telephone mixer/codec. There is support - for a phonein/phoneout device in the mixer driver; whether or not - it does anything is anyone's guess. (Reports on this would be - appreciated. You'll have to figure out how to get the phone to - go off-hook before it'll work, tho.) - - * This driver was not written with any cooperation or support from - NeoMagic. If you have any questions about this, see their website - for their official stance on supporting open source drivers. - -============ -Video memory -============ - -The NeoMagic sound engine uses a portion of the display memory to hold -the sound buffer. (Crazy, eh?) The NeoMagic video BIOS sets up a -special pointer at the top of video RAM to indicate where the top of -the audio buffer should be placed. - -At the present time XFree86 is apparently not aware of this. It will -thus write over either the pointer or the sound buffer with abandon. -(Accelerated-X seems to do a better job here.) - -This implies a few things: - - * Sometimes the NM256 driver has to guess at where the buffer - should be placed, especially if the module is loaded after the - X server is started. It's usually correct, but it will consistently - fail on the Sony F250. - - * Virtual screens greater than 1024x768x16 under XFree86 are - problematic on laptops with only 2.5MB of screen RAM. This - includes all of the 256AV-equipped laptops. (Virtual displays - may or may not work on the 256ZX, which has at least 4MB of - video RAM.) - -If you start having problems with random noise being output either -constantly (this is the usual symptom on the F250), or when windows -are moved around (this is the usual symptom when using a virtual -screen), the best fix is to - - * Don't use a virtual frame buffer. - * Make sure you load the NM256 module before the X server is - started. - -On the F250, it is possible to force the driver to load properly even -after the XFree86 server is started by doing: - - insmod nm256 buffertop=0x25a800 - -This forces the audio buffers to the correct offset in screen RAM. - -One user has reported a similar problem on the Sony F270, although -others apparently aren't seeing any problems. His suggested command -is - - insmod nm256 buffertop=0x272800 - -================= -Official WWW site -================= - -The official site for the NM256 driver is: - - http://www.uglx.org/sony.html - -You should always be able to get the latest version of the driver there, -and the driver will be supported for the foreseeable future. - -============== -Z505RX and IDE -============== - -There appears to be a problem with the IDE chipset on the Z505RX; one -of the symptoms is that sound playback periodically hangs (when the -disk is accessed). The user reporting the problem also reported that -enabling all of the IDE chipset workarounds in the kernel solved the -problem, tho obviously only one of them should be needed--if someone -can give me more details I would appreciate it. - -============================== -Z505S/Z505SX on-board Ethernet -============================== - -If you're using the on-board Ethernet Pro/100 ethernet support on the Z505 -series, I strongly encourage you to download the latest eepro100 driver from -Donald Becker's site: - - ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/test/eepro100.c - -There was a reported problem on the Z505SX that if the ethernet -interface is disabled and reenabled while the sound driver is loaded, -the machine would lock up. I have included a workaround that is -working satisfactorily. However, you may occasionally see a message -about "Releasing interrupts, over 1000 bad interrupts" which indicates -that the workaround is doing its job. - -================================== -PCMCIA and the Z505S/Z505SX/Z505DX -================================== - -There is also a known problem with the Sony Z505S and Z505SX hanging -if a PCMCIA card is inserted while the ethernet driver is loaded, or -in some cases if the laptop is suspended. This is caused by tons of -spurious IRQ 9s, probably generated from the PCMCIA or ACPI bridges. - -There is currently no fix for the problem that works in every case. -The only known workarounds are to disable the ethernet interface -before inserting or removing a PCMCIA card, or with some cards -disabling the PCMCIA card before ejecting it will also help the -problem with the laptop hanging when the card is ejected. - -One user has reported that setting the tcic's cs_irq to some value -other than 9 (like 11) fixed the problem. This doesn't work on my -Z505S, however--changing the value causes the cardmgr to stop seeing -card insertions and removals, cards don't seem to work correctly, and -I still get hangs if a card is inserted when the kernel is booted. - -Using the latest ethernet driver and pcmcia package allows me to -insert an Adaptec 1480A SlimScsi card without the laptop hanging, -although I still have to shut down the card before ejecting or -powering down the laptop. However, similar experiments with a DE-660 -ethernet card still result in hangs when the card is inserted. I am -beginning to think that the interrupts are CardBus-related, since the -Adaptec card is a CardBus card, and the DE-660 is not; however, I -don't have any other CardBus cards to test with. - -====== -Thanks -====== - -First, I want to thank everyone (except NeoMagic of course) for their -generous support and encouragement. I'd like to list everyone's name -here that replied during the development phase, but the list is -amazingly long. - -I will be rather unfair and single out a few people, however: - - Justin Maurer, for being the first random net.person to try it, - and for letting me login to his Z505SX to get it working there - - Edi Weitz for trying out several different versions, and giving - me a lot of useful feedback - - Greg Rumple for letting me login remotely to get the driver - functional on the 256ZX, for his assistance on tracking - down all sorts of random stuff, and for trying out Accel-X - - Zach Brown, for the initial AC97 mixer interface design - - Jeff Garzik, for various helpful suggestions on the AC97 - interface - - "Mr. Bumpy" for feedback on the Z505RX - - Bill Nottingham, for generous assistance in getting the mixer ID - code working - -================= -Previous versions -================= - -Versions prior to 0.3 (aka `noname') had problems with weird artifacts -in the output and failed to set the recording rate properly. These -problems have long since been fixed. - -Versions prior to 0.5 had problems with clicks in the output when -anything other than 16-bit stereo sound was being played, and also had -periodic clicks when recording. - -Version 0.7 first incorporated support for the NM256ZX chipset, which -is found on some Dell Latitude laptops (the CPt, and apparently -some CPi models as well). It also included the generic AC97 -mixer module. - -Version 0.75 renamed all the functions and files with slightly more -generic names. - -Note that previous versions of this document claimed that recording was -8-bit only; it actually has been working for 16-bits all along. diff -Nru a/Documentation/sound/OPL3 b/Documentation/sound/OPL3 --- a/Documentation/sound/OPL3 Thu Mar 7 18:17:36 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,6 +0,0 @@ -A pure OPL3 card is nice and easy to configure. Simply do - -insmod opl3 io=0x388 - -Change the I/O address in the very unlikely case this card is differently -configured diff -Nru a/Documentation/sound/OPL3-SA b/Documentation/sound/OPL3-SA --- a/Documentation/sound/OPL3-SA Thu Mar 7 18:17:45 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,52 +0,0 @@ -OPL3-SA1 sound driver (opl3sa.o) - ---- -Note: This howto only describes how to setup the OPL3-SA1 chip; this info -does not apply to the SA2, SA3, or SA4. ---- - -The Yamaha OPL3-SA1 sound chip is usually found built into motherboards, and -it's a decent little chip offering a WSS mode, a SB Pro emulation mode, MPU401 -and OPL3 FM Synth capabilities. - -You can enable inclusion of the driver via CONFIG_SOUND_OPL3SA1=m, or -CONFIG_SOUND_OPL3SA1=y through 'make config/xconfig/menuconfig'. - -You'll need to know all of the relevant info (irq, dma, and io port) for the -chip's WSS mode, since that is the mode the kernel sound driver uses, and of -course you'll also need to know about where the MPU401 and OPL3 ports and -IRQs are if you want to use those. - -Here's the skinny on how to load it as a module: - - modprobe opl3sa io=0x530 irq=11 dma=0 dma2=1 mpu_io=0x330 mpu_irq=5 - -Module options in detail: - - io: This is the WSS's port base. - irq: This is the WSS's IRQ. - dma: This is the WSS's DMA line. In my BIOS setup screen this was - listed as "WSS Play DMA" - dma2: This is the WSS's secondary DMA line. My BIOS calls it the - "WSS capture DMA" - - mpu_io: This is the MPU401's port base. - mpu_irq: This is the MPU401's IRQ. - -If you'd like to use the OPL3 FM Synthesizer, make sure you enable -CONFIG_YM3812 (in 'make config'). That'll build the opl3.o module. - -Then a simple 'insmod opl3 io=0x388', and you now have FM Synth. - -You can also use the SoftOSS software synthesizer instead of the builtin OPL3. -Here's how: - -Say 'y' or 'm' to "SoftOSS software wave table engine" in make config. - -If you said yes, the software synth is available once you boot your new -kernel. - -If you chose to build it as a module, just insmod the resulting softoss2.o - -Questions? Comments? - diff -Nru a/Documentation/sound/OPL3-SA2 b/Documentation/sound/OPL3-SA2 --- a/Documentation/sound/OPL3-SA2 Thu Mar 7 18:17:38 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,210 +0,0 @@ -Documentation for the OPL3-SA2, SA3, and SAx driver (opl3sa2.o) ---------------------------------------------------------------- - -Scott Murray, scott@spiteful.org -January 7, 2001 - -NOTE: All trade-marked terms mentioned below are properties of their - respective owners. - - -Supported Devices ------------------ - -This driver is for PnP soundcards based on the following Yamaha audio -controller chipsets: - -YMF711 aka OPL3-SA2 -YMF715 and YMF719 aka OPL3-SA3 - -Up until recently (December 2000), I'd thought the 719 to be a -different chipset, the OPL3-SAx. After an email exhange with -Yamaha, however, it turns out that the 719 is just a re-badged -715, and the chipsets are identical. The chipset detection code -has been updated to reflect this. - -Anyways, all of these chipsets implement the following devices: - -OPL3 FM synthesizer -Soundblaster Pro -Microsoft/Windows Sound System -MPU401 MIDI interface - -Note that this driver uses the MSS device, and to my knowledge these -chipsets enforce an either/or situation with the Soundblaster Pro -device and the MSS device. Since the MSS device has better -capabilities, I have implemented the driver to use it. - - -Mixer Channels --------------- - -Older versions of this driver (pre-December 2000) had two mixers, -an OPL3-SA2 or SA3 mixer and a MSS mixer. The OPL3-SA[23] mixer -device contained a superset of mixer channels consisting of its own -channels and all of the MSS mixer channels. To simplify the driver -considerably, and to partition functionality better, the OPL3-SA[23] -mixer device now contains has its own specific mixer channels. They -are: - -Volume - Hardware master volume control -Bass - SA3 only, now supports left and right channels -Treble - SA3 only, now supports left and right channels -Microphone - Hardware microphone input volume control -Digital1 - Yamaha 3D enhancement "Wide" mixer - -All other mixer channels (e.g. "PCM", "CD", etc.) now have to be -controlled via the "MS Sound System (CS4231)" mixer. To facilitate -this, the mixer device creation order has been switched so that -the MSS mixer is created first. This allows accessing the majority -of the useful mixer channels even via single mixer-aware tools -such as "aumix". - - -Plug 'n Play ------------- - -In previous kernels (2.2.x), some configuration was required to -get the driver to talk to the card. Being the new millenium and -all, the 2.4.x kernels now support auto-configuration if ISA PnP -support is configured in. Theoretically, the driver even supports -having more than one card in this case. - -With the addition of PnP support to the driver, two new parameters -have been added to control it: - -isapnp - set to 0 to disable ISA PnP card detection - -multiple - set to 0 to disable multiple PnP card detection - - -Optional Parameters -------------------- - -Recent (December 2000) additions to the driver (based on a patch -provided by Peter Englmaier) are two new parameters: - -ymode - Set Yamaha 3D enhancement mode: - 0 = Desktop/Normal 5-12 cm speakers - 1 = Notebook PC (1) 3 cm speakers - 2 = Notebook PC (2) 1.5 cm speakers - 3 = Hi-Fi 16-38 cm speakers - -loopback - Set A/D input source. Useful for echo cancellation: - 0 = Mic Right channel (default) - 1 = Mono output loopback - -The ymode parameter has been tested and does work. The loopback -parameter, however, is untested. Any feedback on its usefulness -would be appreciated. - - -Manual Configuration --------------------- - -If for some reason you decide not to compile ISA PnP support into -your kernel, or disabled the driver's usage of it by setting the -isapnp parameter as discussed above, then you will need to do some -manual configuration. There are two ways of doing this. The most -common is to use the isapnptools package to initialize the card, and -use the kernel module form of the sound subsystem and sound drivers. -Alternatively, some BIOS's allow manual configuration of installed -PnP devices in a BIOS menu, which should allow using the non-modular -sound drivers, i.e. built into the kernel. - -I personally use isapnp and modules, and do not have access to a PnP -BIOS machine to test. If you have such a beast, configuring the -driver to be built into the kernel should just work (thanks to work -done by David Luyer ). You will still need -to specify settings, which can be done by adding: - -opl3sa2=,,,,, - -to the kernel command line. For example: - -opl3sa2=0x370,5,0,1,0x530,0x330 - -If you are instead using the isapnp tools (as most people have been -before Linux 2.4.x), follow the directions in their documentation to -produce a configuration file. Here is the relevant excerpt I used to -use for my SA3 card from my isapnp.conf: - -(CONFIGURE YMH0800/-1 (LD 0 - -# NOTE: IO 0 is for the unused SoundBlaster part of the chipset. -(IO 0 (BASE 0x0220)) -(IO 1 (BASE 0x0530)) -(IO 2 (BASE 0x0388)) -(IO 3 (BASE 0x0330)) -(IO 4 (BASE 0x0370)) -(INT 0 (IRQ 5 (MODE +E))) -(DMA 0 (CHANNEL 0)) -(DMA 1 (CHANNEL 1)) - -Here, note that: - -Port Acceptable Range Purpose ----- ---------------- ------- -IO 0 0x0220 - 0x0280 SB base address, unused. -IO 1 0x0530 - 0x0F48 MSS base address -IO 2 0x0388 - 0x03F8 OPL3 base address -IO 3 0x0300 - 0x0334 MPU base address -IO 4 0x0100 - 0x0FFE card's own base address for its control I/O ports - -The IRQ and DMA values can be any that are considered acceptable for a -MSS. Assuming you've got isapnp all happy, then you should be able to -do something like the following (which matches up with the isapnp -configuration above): - -modprobe mpu401 -modprobe ad1848 -modprobe opl3sa2 io=0x370 mss_io=0x530 mpu_io=0x330 irq=5 dma=0 dma2=1 -modprobe opl3 io=0x388 - -See the section "Automatic Module Loading" below for how to set up -/etc/modules.conf to automate this. - -An important thing to remember that the opl3sa2 module's io argument is -for it's own control port, which handles the card's master mixer for -volume (on all cards), and bass and treble (on SA3 cards). - - -Troubleshooting ---------------- - -If all goes well and you see no error messages, you should be able to -start using the sound capabilities of your system. If you get an -error message while trying to insert the opl3sa2 module, then make -sure that the values of the various arguments match what you specified -in your isapnp configuration file, and that there is no conflict with -another device for an I/O port or interrupt. Checking the contents of -/proc/ioports and /proc/interrupts can be useful to see if you're -butting heads with another device. - -If you still cannot get the module to load, look at the contents of -your system log file, usually /var/log/messages. If you see the -message "opl3sa2: Unknown Yamaha audio controller version", then you -have a different chipset version than I've encountered so far. Look -for all messages in the log file that start with "opl3sa2: " and see -if they provide any clues. If you do not see the chipset version -message, and none of the other messages present in the system log are -helpful, email me some details and I'll try my best to help. - - -Automatic Module Loading ------------------------- - -Lastly, if you're using modules and want to set up automatic module -loading with kmod, the kernel module loader, here is the section I -currently use in my modules.conf file: - -# Sound -alias sound-slot-0 opl3sa2 -options opl3sa2 io=0x370 mss_io=0x530 mpu_io=0x330 irq=7 dma=0 dma2=3 -options opl3 io=0x388 - -That's all it currently takes to get an OPL3-SA3 card working on my -system. Once again, if you have any other problems, email me at the -address listed above. - -Scott diff -Nru a/Documentation/sound/Opti b/Documentation/sound/Opti --- a/Documentation/sound/Opti Thu Mar 7 18:17:42 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,222 +0,0 @@ -Support for the OPTi 82C931 chip --------------------------------- -Note: parts of this README file apply also to other -cards that use the mad16 driver. - -Some items in this README file are based on features -added to the sound driver after Linux-2.1.91 was out. -By the time of writing this I do not know which official -kernel release will include these features. -Please do not report inconsistencies on older Linux -kernels. - -The OPTi 82C931 is supported in its non-PnP mode. -Usually you do not need to set jumpers, etc. The sound driver -will check the card status and if it is required it will -force the card into a mode in which it can be programmed. - -If you have another OS installed on your computer it is recommended -that Linux and the other OS use the same resources. - -Also, it is recommended that resources specified in /etc/modules.conf -and resources specified in /etc/isapnp.conf agree. - -Compiling the sound driver --------------------------- -I highly recommend that you build a modularized sound driver. -This document does not cover a sound-driver which is built in -the kernel. - -Sound card support should be enabled as a module (chose m). -Answer 'm' for these items: - Generic OPL2/OPL3 FM synthesizer support (CONFIG_SOUND_ADLIB) - Microsoft Sound System support (CONFIG_SOUND_MSS) - Support for OPTi MAD16 and/or Mozart based cards (CONFIG_SOUND_MAD16) - FM synthesizer (YM3812/OPL-3) support (CONFIG_SOUND_YM3812) - -The configuration menu may ask for addresses, IRQ lines or DMA -channels. If the card is used as a module the module loading -options will override these values. - -For the OPTi 931 you can answer 'n' to: - Support MIDI in older MAD16 based cards (requires SB) (CONFIG_SOUND_MAD16_OLDCARD) -If you do need MIDI support in a Mozart or C928 based card you -need to answer 'm' to the above question. In that case you will -also need to answer 'm' to: - '100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' (CONFIG_SOUND_SB) - -Go on and compile your kernel and modules. Install the modules. Run depmod -a. - -Using isapnptools ------------------ -In most systems with a PnP BIOS you do not need to use isapnp. The -initialization provided by the BIOS is sufficient for the driver -to pick up the card and continue initialization. - -If that fails, or if you have other PnP cards, you need to use isapnp -to initialize the card. -This was tested with isapnptools-1.11 but I recommend that you use -isapnptools-1.13 (or newer). Run pnpdump to dump the information -about your PnP cards. Then edit the resulting file and select -the options of your choice. This file is normally installed as -/etc/isapnp.conf. - -The driver has one limitation with respect to I/O port resources: -IO3 base must be 0x0E0C. Although isapnp allows other ports, this -address is hard-coded into the driver. - -Using kmod and autoloading the sound driver -------------------------------------------- -Comment: as of linux-2.1.90 kmod is replacing kerneld. -The config file '/etc/modules.conf' is used as before. - -This is the sound part of my /etc/modules.conf file. -Following that I will explain each line. - -alias mixer0 mad16 -alias audio0 mad16 -alias midi0 mad16 -alias synth0 opl3 -options sb mad16=1 -options mad16 irq=10 dma=0 dma16=1 io=0x530 joystick=1 cdtype=0 -options opl3 io=0x388 -post-install mad16 /sbin/ad1848_mixer_reroute 14 8 15 3 16 6 - -If you have an MPU daughtercard or onboard MPU you will want to add to the -"options mad16" line - eg - -options mad16 irq=5 dma=0 dma16=3 io=0x530 mpu_io=0x330 mpu_irq=9 - -To set the I/O and IRQ of the MPU. - - -Explain: - -alias mixer0 mad16 -alias audio0 mad16 -alias midi0 mad16 -alias synth0 opl3 - -When any sound device is opened the kernel requests auto-loading -of char-major-14. There is a built-in alias that translates this -request to loading the main sound module. - -The sound module in its turn will request loading of a sub-driver -for mixer, audio, midi or synthesizer device. The first 3 are -supported by the mad16 driver. The synth device is supported -by the opl3 driver. - -There is currently no way to autoload the sound device driver -if more than one card is installed. - -options sb mad16=1 - -This is left for historical reasons. If you enable the -config option 'Support MIDI in older MAD16 based cards (requires SB)' -or if you use an older mad16 driver it will force loading of the -SoundBlaster driver. This option tells the SB driver not to look -for a SB card but to wait for the mad16 driver. - -options mad16 irq=10 dma=0 dma16=1 io=0x530 joystick=1 cdtype=0 -options opl3 io=0x388 - -post-install mad16 /sbin/ad1848_mixer_reroute 14 8 15 3 16 6 - -This sets resources and options for the mad16 and opl3 drivers. -I use two DMA channels (only one is required) to enable full duplex. -joystick=1 enables the joystick port. cdtype=0 disables the cd port. -You can also set mpu_io and mpu_irq in the mad16 options for the -uart401 driver. - -This tells modprobe to run /sbin/ad1848_mixer_reroute after -mad16 is successfully loaded and initialized. The source -for ad1848_mixer_reroute is appended to the end of this readme -file. It is impossible for the sound driver to know the actual -connections to the mixer. The 3 inputs intended for cd, synth -and line-in are mapped to the generic inputs line1, line2 and -line3. This program reroutes these mixer channels to their -right names (note the right mapping depends on the actual sound -card that you use). -The numeric parameters mean: - 14=line1 8=cd - reroute line1 to the CD input. - 15=line2 3=synth - reroute line2 to the synthesizer input. - 16=line3 6=line - reroute line3 to the line input. -For reference on other input names look at the file -/usr/include/linux/soundcard.h. - -Using a joystick ------------------ -You must enable a joystick in the mad16 options. (also -in /etc/isapnp.conf if you use it). -Tested with regular analog joysticks. - -A CDROM drive connected to the sound card ------------------------------------------ -The 82C931 chip has support only for secondary ATAPI cdrom. -(cdtype=8). Loading the mad16 driver resets the C931 chip -and if a cdrom was already mounted it may cause a complete -system hang. Do not use the sound card if you have an alternative. -If you do use the sound card it is important that you load -the mad16 driver (use "modprobe mad16" to prevent auto-unloading) -before the cdrom is accessed the first time. - -Using the sound driver built-in to the kernel may help here, but... -Most new systems have a PnP BIOS and also two IDE controllers. -The IDE controller on the sound card may be needed only on older -systems (which have only one IDE controller) but these systems -also do not have a PnP BIOS - requiring isapnptools and a modularized -driver. - -Known problems --------------- -1. See the section on "A CDROM drive connected to the sound card". - -2. On my system the codec cannot capture companded sound samples. - (eg., recording from /dev/audio). When any companded capture is - requested I get stereo-16 bit samples instead. Playback of - companded samples works well. Apparently this problem is not common - to all C931 based cards. I do not know how to identify cards that - have this problem. - -Source for ad1848_mixer_reroute.c ---------------------------------- -#include -#include -#include - -static char *mixer_names[SOUND_MIXER_NRDEVICES] = - SOUND_DEVICE_LABELS; - -int -main(int argc, char **argv) { - int val, from, to; - int i, fd; - - fd = open("/dev/mixer", O_RDWR); - if(fd < 0) { - perror("/dev/mixer"); - return 1; - } - - for(i = 2; i < argc; i += 2) { - from = atoi(argv[i-1]); - to = atoi(argv[i]); - - if(to == SOUND_MIXER_NONE) - fprintf(stderr, "%s: turning off mixer %s\n", - argv[0], mixer_names[to]); - else - fprintf(stderr, "%s: rerouting mixer %s to %s\n", - argv[0], mixer_names[from], mixer_names[to]); - - val = from << 8 | to; - - if(ioctl(fd, SOUND_MIXER_PRIVATE2, &val)) { - perror("AD1848 mixer reroute"); - return 1; - } - } - - return 0; -} - diff -Nru a/Documentation/sound/PAS16 b/Documentation/sound/PAS16 --- a/Documentation/sound/PAS16 Thu Mar 7 18:17:43 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,163 +0,0 @@ -Pro Audio Spectrum 16 for 2.3.99 and later -========================================= -by Thomas Molina (tmolina@home.com) -last modified 3 Mar 2001 -Acknowledgement to Axel Boldt (boldt@math.ucsb.edu) for stuff taken -from Configure.help, Riccardo Facchetti for stuff from README.OSS, -and others whose names I could not find. - -This documentation is relevant for the PAS16 driver (pas2_card.c and -friends) under kernel version 2.3.99 and later. If you are -unfamiliar with configuring sound under Linux, please read the -Sound-HOWTO, linux/Documentation/sound/Introduction and other -relevant docs first. - -The following information is relevant information from README.OSS -and legacy docs for the Pro Audio Spectrum 16 (PAS16): -================================================================== - -The pas2_card.c driver supports the following cards -- -Pro Audio Spectrum 16 (PAS16) and compatibles: - Pro Audio Spectrum 16 - Pro Audio Studio 16 - Logitech Sound Man 16 - NOTE! The original Pro Audio Spectrum as well as the PAS+ are not - and will not be supported by the driver. - -The sound driver configuration dialog -------------------------------------- - -Sound configuration starts by making some yes/no questions. Be careful -when answering to these questions since answering y to a question may -prevent some later ones from being asked. For example don't answer y to -the question about (PAS16) if you don't really have a PAS16. Sound -configuration may also be made modular by answering m to configuration -options presented. - -Note also that all questions may not be asked. The configuration program -may disable some questions depending on the earlier choices. It may also -select some options automatically as well. - - "ProAudioSpectrum 16 support", - - Answer 'y'_ONLY_ if you have a Pro Audio Spectrum _16_, - Pro Audio Studio 16 or Logitech SoundMan 16 (be sure that - you read the above list correctly). Don't answer 'y' if you - have some other card made by Media Vision or Logitech since they - are not PAS16 compatible. - NOTE! Since 3.5-beta10 you need to enable SB support (next question) - if you want to use the SB emulation of PAS16. It's also possible to - the emulation if you want to use a true SB card together with PAS16 - (there is another question about this that is asked later). - - "Generic OPL2/OPL3 FM synthesizer support", - - Answer 'y' if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). - The PAS16 has an OPL3-compatible FM chip. - -With PAS16 you can use two audio device files at the same time. /dev/dsp (and -/dev/audio) is connected to the 8/16 bit native codec and the /dev/dsp1 (and -/dev/audio1) is connected to the SB emulation (8 bit mono only). - - -The new stuff for 2.3.99 and later -============================================================================ -The following configuration options from linux/Documentation/Configure.help -are relevant to configuring the PAS16: - -Sound card support -CONFIG_SOUND - If you have a sound card in your computer, i.e. if it can say more - than an occasional beep, say Y. Be sure to have all the information - about your sound card and its configuration down (I/O port, - 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 - about the modular sound system is contained in the files - Documentation/sound/Introduction. The file - Documentation/sound/README.OSS contains some slightly outdated but - still useful information as well. - -OSS sound modules -CONFIG_SOUND_OSS - OSS is the Open Sound System suite of sound card drivers. They make - sound programming easier since they provide a common API. Say Y or M - here (the module will be called sound.o) if you haven't found a - driver for your sound card above, then pick your driver from the - list below. - -Persistent DMA buffers -CONFIG_SOUND_DMAP - Linux can often have problems allocating DMA buffers for ISA sound - cards on machines with more than 16MB of RAM. This is because ISA - DMA buffers must exist below the 16MB boundary and it is quite - possible that a large enough free block in this region cannot be - found after the machine has been running for a while. If you say Y - here the DMA buffers (64Kb) will be allocated at boot time and kept - until the shutdown. This option is only useful if you said Y to - "OSS sound modules", above. If you said M to "OSS sound modules" - then you can get the persistent DMA buffer functionality by passing - the command-line argument "dmabuf=1" to the sound.o module. - - Say y here for PAS16. - -ProAudioSpectrum 16 support -CONFIG_SOUND_PAS - Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio - 16 or Logitech SoundMan 16 sound card. Don't answer Y if you have - some other card made by Media Vision or Logitech since they are not - PAS16 compatible. It is not necessary to enable the separate - Sound Blaster support; it is included in the PAS driver. - - If you compile the driver into the kernel, you have to add - "pas2=,,,,,,, - to the kernel command line. - -FM Synthesizer (YM3812/OPL-3) support -CONFIG_SOUND_YM3812 - Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). - Answering Y is usually a safe and recommended choice, however some - cards may have software (TSR) FM emulation. Enabling FM support with - these cards may cause trouble (I don't currently know of any such - cards, however). - Please read the file Documentation/sound/OPL3 if your card has an - OPL3 chip. - If you compile the driver into the kernel, you have to add - "opl3=" to the kernel command line. - - If you compile your drivers into the kernel, you MUST configure - OPL3 support as a module for PAS16 support to work properly. - You can then get OPL3 functionality by issuing the command: - insmod opl3 - In addition, you must either add the following line to - /etc/modules.conf: - options opl3 io=0x388 - or else add the following line to /etc/lilo.conf: - opl3=0x388 - - -EXAMPLES -=================================================================== -To use the PAS16 in my computer I have enabled the following sound -configuration options: - -CONFIG_SOUND=y -CONFIG_SOUND_OSS=y -CONFIG_SOUND_TRACEINIT=y -CONFIG_SOUND_DMAP=y -CONFIG_SOUND_PAS=y -CONFIG_SOUND_SB=n -CONFIG_SOUND_YM3812=m - -I have also included the following append line in /etc/lilo.conf: -append="pas2=0x388,10,3,-1,0x220,5,1,-1 sb=0x220,5,1,-1 opl3=0x388" - -The io address of 0x388 is default configuration on the PAS16. The -irq of 10 and dma of 3 may not match your installation. The above -configuration enables PAS16, 8-bit Soundblaster and OPL3 -functionality. If Soundblaster functionality is not desired, the -following line would be appropriate: -append="pas2=0x388,10,3,-1,0,-1,-1,-1 opl3=0x388" - -If sound is built totally modular, the above options may be -specified in /etc/modules.conf for pas2.o, sb.o and opl3.o -respectively. diff -Nru a/Documentation/sound/PSS b/Documentation/sound/PSS --- a/Documentation/sound/PSS Thu Mar 7 18:17:43 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,41 +0,0 @@ -The PSS cards and other ECHO based cards provide an onboard DSP with -downloadable programs and also has an AD1848 "Microsoft Sound System" -device. The PSS driver enables MSS and MPU401 modes of the card. SB -is not enabled since it doesn't work concurrently with MSS. - -If you build this driver as a module then the driver takes the following -parameters - -pss_io. The I/O base the PSS card is configured at (normally 0x220 - or 0x240) - -mss_io The base address of the Microsoft Sound System interface. - This is normally 0x530, but may be 0x604 or other addresses. - -mss_irq The interrupt assigned to the Microsoft Sound System - emulation. IRQ's 3,5,7,9,10,11 and 12 are available. If you - get IRQ errors be sure to check the interrupt is set to - "ISA/Legacy" in the BIOS on modern machines. - -mss_dma The DMA channel used by the Microsoft Sound System. - This can be 0, 1, or 3. DMA 0 is not available on older - machines and will cause a crash on them. - -mpu_io The MPU emulation base address. This sets the base of the - synthesizer. It is typically 0x330 but can be altered. - -mpu_irq The interrupt to use for the synthesizer. It must differ - from the IRQ used by the Microsoft Sound System port. - - -The mpu_io/mpu_irq fields are optional. If they are not specified the -synthesizer parts are not configured. - -When the module is loaded it looks for a file called -/etc/sound/pss_synth. This is the firmware file from the DOS install disks. -This fil holds a general MIDI emulation. The file expected is called -genmidi.ld on newer DOS driver install disks and synth.ld on older ones. - -You can also load alternative DSP algorithms into the card if you wish. One -alternative driver can be found at http://www.mpg123.de/ - diff -Nru a/Documentation/sound/PSS-updates b/Documentation/sound/PSS-updates --- a/Documentation/sound/PSS-updates Thu Mar 7 18:17:44 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,88 +0,0 @@ - This file contains notes for users of PSS sound cards who wish to use the -newly added features of the newest version of this driver. - - The major enhancements present in this new revision of this driver is the -addition of two new module parameters that allow you to take full advantage of -all the features present on your PSS sound card. These features include the -ability to enable both the builtin CDROM and joystick ports. - -pss_enable_joystick - - This parameter is basically a flag. A 0 will leave the joystick port -disabled, while a non-zero value would enable the joystick port. The default -setting is pss_enable_joystick=0 as this keeps this driver fully compatable -with systems that were using previous versions of this driver. If you wish to -enable the joystick port you will have to add pss_enable_joystick=1 as an -argument to the driver. To actually use the joystick port you will then have -to load the joystick driver itself. Just remember to load the joystick driver -AFTER the pss sound driver. - -pss_cdrom_port - - This parameter takes a port address as its parameter. Any available port -address can be specified to enable the CDROM port, except for 0x0 and -1 as -these values would leave the port disabled. Like the joystick port, the cdrom -port will require that an appropiate CDROM driver be loaded before you can make -use of the newly enabled CDROM port. Like the joystick port option above, -remember to load the CDROM driver AFTER the pss sound driver. While it may -differ on some PSS sound cards, all the PSS sound cards that I have seen have a -builtin Wearnes CDROM port. If this is the case with your PSS sound card you -should load aztcd with the appropiate port option that matches the port you -assigned to the CDROM port when you loaded your pss sound driver. (ex. -modprobe pss pss_cdrom_port=0x340 && modprobe aztcd aztcd=0x340) The default -setting of this parameter leaves the CDROM port disabled to maintain full -compatability with systems using previous versions of this driver. - - Other options have also been added for the added convenience and utility -of the user. These options are only available if this driver is loaded as a -module. - -pss_no_sound - - This module parameter is a flag that can be used to tell the driver to -just configure non-sound components. 0 configures all components, a non-0 -value will only attept to configure the CDROM and joystick ports. This -parameter can be used by a user who only wished to use the builtin joystick -and/or CDROM port(s) of his PSS sound card. If this driver is loaded with this -parameter and with the paramter below set to true then a user can safely unload -this driver with the following command "rmmod pss && rmmod ad1848 && rmmod -mpu401 && rmmod sound && rmmod soundcore" and retain the full functionality of -his CDROM and/or joystick port(s) while gaining back the memory previously used -by the sound drivers. This default setting of this parameter is 0 to retain -full behavioral compatability with previous versions of this driver. - -pss_keep_settings - - This parameter can be used to specify whether you want the driver to reset -all emulations whenever its unloaded. This can be useful for those who are -sharing resources (io ports, IRQ's, DMA's) between different ISA cards. This -flag can also be useful in that future versions of this driver may reset all -emulations by default on the driver's unloading (as it probably should), so -specifying it now will ensure that all future versions of this driver will -continue to work as expected. The default value of this parameter is 1 to -retain full behavioral compatability with previous versions of this driver. - -pss_firmware - - This parameter can be used to specify the file containing the firmware -code so that a user could tell the driver where that file is located instead -of having to put it in a predefined location with a predefined name. The -default setting of this parameter is "/etc/sound/pss_synth" as this was the -path and filename the hardcoded value in the previous versions of this driver. - -Examples: - -# Normal PSS sound card system, loading of drivers. -# Should be specified in an rc file (ex. Slackware uses /etc/rc.d/rc.modules). - -/sbin/modprobe pss pss_io=0x220 mpu_io=0x338 mpu_irq=9 mss_io=0x530 mss_irq=10 mss_dma=1 pss_cdrom_port=0x340 pss_enable_joystick=1 -/sbin/modprobe aztcd aztcd=0x340 -/sbin/modprobe joystick - -# System using the PSS sound card just for its CDROM and joystick ports. -# Should be specified in an rc file (ex. Slackware uses /etc/rc.d/rc.modules). - -/sbin/modprobe pss pss_io=0x220 pss_cdrom_port=0x340 pss_enable_joystick=1 pss_no_sound=1 -/sbin/rmmod pss && /sbin/rmmod ad1848 && /sbin/rmmod mpu401 && /sbin/rmmod sound && /sbin/rmmod soundcore # This line not needed, but saves memory. -/sbin/modprobe aztcd aztcd=0x340 -/sbin/modprobe joystick diff -Nru a/Documentation/sound/README.OSS b/Documentation/sound/README.OSS --- a/Documentation/sound/README.OSS Thu Mar 7 18:17:37 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1456 +0,0 @@ -Introduction ------------- - -This file is a collection of all the old Readme files distributed with -OSS/Lite by Hannu Savolainen. Since the new Linux sound driver is founded -on it I think these information may still be interesting for users that -have to configure their sound system. - -Be warned: Alan Cox is the current maintainer of the Linux sound driver so if -you have problems with it, please contact him or the current device-specific -driver maintainer (e.g. for aedsp16 specific problems contact me). If you have -patches, contributions or suggestions send them to Alan: I'm sure they are -welcome. - -In this document you will find a lot of references about OSS/Lite or ossfree: -they are gone forever. Keeping this in mind and with a grain of salt this -document can be still interesting and very helpful. - -[ File edited 17.01.1999 - Riccardo Facchetti ] -[ Edited miroSOUND section 19.04.2001 - Robert Siemer ] - -OSS/Free version 3.8 release notes ----------------------------------- - -Please read the SOUND-HOWTO (available from sunsite.unc.edu and other Linux FTP -sites). It gives instructions about using sound with Linux. It's bit out of -date but still very useful. Information about bug fixes and such things -is available from the web page (see above). - -Please check http://www.opensound.com/pguide for more info about programming -with OSS API. - - ==================================================== -- THIS VERSION ____REQUIRES____ Linux 2.1.57 OR LATER. - ==================================================== - -Packages "snd-util-3.8.tar.gz" and "snd-data-0.1.tar.Z" -contain useful utilities to be used with this driver. -See http://www.opensound.com/ossfree/getting.html for -download instructions. - -If you are looking for the installation instructions, please -look forward into this document. - -Supported sound cards ---------------------- - -See below. - -Contributors ------------- - -This driver contains code by several contributors. In addition several other -persons have given useful suggestions. The following is a list of major -contributors. (I could have forgotten some names.) - - Craig Metz 1/2 of the PAS16 Mixer and PCM support - Rob Hooft Volume computation algorithm for the FM synth. - Mika Liljeberg uLaw encoding and decoding routines - Jeff Tranter Linux SOUND HOWTO document - Greg Lee Volume computation algorithm for the GUS and - lots of valuable suggestions. - Andy Warner ISC port - Jim Lowe, - Amancio Hasty Jr FreeBSD/NetBSD port - Anders Baekgaard Bug hunting and valuable suggestions. - Joerg Schubert SB16 DSP support (initial version). - Andrew Robinson Improvements to the GUS driver - Megens SA MIDI recording for SB and SB Pro (initial version). - Mikael Nordqvist Linear volume support for GUS and - nonblocking /dev/sequencer. - Ian Hartas SVR4.2 port - Markus Aroharju and - Risto Kankkunen Major contributions to the mixer support - of GUS v3.7. - Hunyue Yau Mixer support for SG NX Pro. - Marc Hoffman PSS support (initial version). - Rainer Vranken Initialization for Jazz16 (initial version). - Peter Trattler Initial version of loadable module support for Linux. - JRA Gibson 16 bit mode for Jazz16 (initial version) - Davor Jadrijevic MAD16 support (initial version) - Gregor Hoffleit Mozart support (initial version) - Riccardo Facchetti Audio Excel DSP 16 (aedsp16) support - James Hightower Spotting a tiny but important bug in CS423x support. - Denis Sablic OPTi 82C924 specific enhancements (non PnP mode) - Tim MacKenzie Full duplex support for OPTi 82C930. - - Please look at lowlevel/README for more contributors. - -There are probably many other names missing. If you have sent me some -patches and your name is not in the above list, please inform me. - -Sending your contributions or patches -------------------------------------- - -First of all it's highly recommended to contact me before sending anything -or before even starting to do any work. Tell me what you suggest to be -changed or what you have planned to do. Also ensure you are using the -very latest (development) version of OSS/Free since the change may already be -implemented there. In general it's a major waste of time to try to improve a -several months old version. Information about the latest version can be found -from http://www.opensound.com/ossfree. In general there is no point in -sending me patches relative to production kernels. - -Sponsors etc. -------------- - -The following companies have greatly helped development of this driver -in form of a free copy of their product: - -Novell, Inc. UnixWare personal edition + SDK -The Santa Cruz Operation, Inc. A SCO OpenServer + SDK -Ensoniq Corp, a SoundScape card and extensive amount of assistance -MediaTrix Peripherals Inc, a AudioTrix Pro card + SDK -Acer, Inc. a pair of AcerMagic S23 cards. - -In addition the following companies have provided me sufficient amount -of technical information at least some of their products (free or $$$): - -Advanced Gravis Computer Technology Ltd. -Media Vision Inc. -Analog Devices Inc. -Logitech Inc. -Aztech Labs Inc. -Crystal Semiconductor Corporation, -Integrated Circuit Systems Inc. -OAK Technology -OPTi -Turtle Beach -miro -Ad Lib Inc. ($$) -Music Quest Inc. ($$) -Creative Labs ($$$) - -If you have some problems -========================= - -Read the sound HOWTO (sunsite.unc.edu:/pub/Linux/docs/...?). -Also look at the home page (http://www.opensound.com/ossfree). It may -contain info about some recent bug fixes. - -It's likely that you have some problems when trying to use the sound driver -first time. Sound cards don't have standard configuration so there are no -good default configuration to use. Please try to use same I/O, DMA and IRQ -values for the sound card than with DOS. - -If you get an error message when trying to use the driver, please look -at /var/adm/messages for more verbose error message. - - -The following errors are likely with /dev/dsp and /dev/audio. - - - "No such device or address". - This error indicates that there are no suitable hardware for the - device file or the sound driver has been compiled without support for - this particular device. For example /dev/audio and /dev/dsp will not - work if "digitized voice support" was not enabled during "make config". - - - "Device or resource busy". Probably the IRQ (or DMA) channel - required by the sound card is in use by some other device/driver. - - - "I/O error". Almost certainly (99%) it's an IRQ or DMA conflict. - Look at the kernel messages in /var/adm/notice for more info. - - - "Invalid argument". The application is calling ioctl() - with impossible parameters. Check that the application is - for sound driver version 2.X or later. - -Linux installation -================== - -IMPORTANT! Read this if you are installing a separately - distributed version of this driver. - - Check that your kernel version works with this - release of the driver (see Readme). Also verify - that your current kernel version doesn't have more - recent sound driver version than this one. IT'S HIGHLY - RECOMMENDED THAT YOU USE THE SOUND DRIVER VERSION THAT - IS DISTRIBUTED WITH KERNEL SOURCES. - -- When installing separately distributed sound driver you should first - read the above notice. Then try to find proper directory where and how - to install the driver sources. You should not try to install a separately - distributed driver version if you are not able to find the proper way - yourself (in this case use the version that is distributed with kernel - sources). Remove old version of linux/drivers/sound directory before - installing new files. - -- To build the device files you need to run the enclosed shell script - (see below). You need to do this only when installing sound driver - first time or when upgrading to much recent version than the earlier - one. - -- Configure and compile Linux as normally (remember to include the - sound support during "make config"). Please refer to kernel documentation - for instructions about configuring and compiling kernel. File Readme.cards - contains card specific instructions for configuring this driver for - use with various sound cards. - -Boot time configuration (using lilo and insmod) ------------------------------------------------ - -This information has been removed. Too many users didn't believe -that it's really not necessary to use this method. Please look at -Readme of sound driver version 3.0.1 if you still want to use this method. - -Problems --------- - -Common error messages: - -- /dev/???????: No such file or directory. -Run the script at the end of this file. - -- /dev/???????: No such device. -You are not running kernel which contains the sound driver. When using -modularized sound driver this error means that the sound driver is not -loaded. - -- /dev/????: No such device or address. -Sound driver didn't detect suitable card when initializing. Please look at -Readme.cards for info about configuring the driver with your card. Also -check for possible boot (insmod) time error messages in /var/adm/messages. - -- Other messages or problems -Please check http://www.opensound.com/ossfree for more info. - -Configuring version 3.8 (for Linux) with some common sound cards -================================================================ - -This document describes configuring sound cards with the freeware version of -Open Sound Systems (OSS/Free). Information about the commercial version -(OSS/Linux) and its configuration is available from -http://www.opensound.com/linux.html. Information presented here is -not valid for OSS/Linux. - -If you are unsure about how to configure OSS/Free -you can download the free evaluation version of OSS/Linux from the above -address. There is a chance that it can autodetect your sound card. In this case -you can use the information included in soundon.log when configuring OSS/Free. - - -IMPORTANT! This document covers only cards that were "known" when - this driver version was released. Please look at - http://www.opensound.com/ossfree for info about - cards introduced recently. - - When configuring the sound driver, you should carefully - check each sound configuration option (particularly - "Support for /dev/dsp and /dev/audio"). The default values - offered by these programs are not necessarily valid. - - -THE BIGGEST MISTAKES YOU CAN MAKE -================================= - -1. Assuming that the card is Sound Blaster compatible when it's not. --------------------------------------------------------------------- - -The number one mistake is to assume that your card is compatible with -Sound Blaster. Only the cards made by Creative Technology or which have -one or more chips labeled by Creative are SB compatible. In addition there -are few sound chipsets which are SB compatible in Linux such as ESS1688 or -Jazz16. Note that SB compatibility in DOS/Windows does _NOT_ mean anything -in Linux. - -IF YOU REALLY ARE 150% SURE YOU HAVE A SOUND BLASTER YOU CAN SKIP THE REST OF -THIS CHAPTER. - -For most other "supposed to be SB compatible" cards you have to use other -than SB drivers (see below). It is possible to get most sound cards to work -in SB mode but in general it's a complete waste of time. There are several -problems which you will encounter by using SB mode with cards that are not -truly SB compatible: - -- The SB emulation is at most SB Pro (DSP version 3.x) which means that -you get only 8 bit audio (there is always an another ("native") mode which -gives the 16 bit capability). The 8 bit only operation is the reason why -many users claim that sound quality in Linux is much worse than in DOS. -In addition some applications require 16 bit mode and they produce just -noise with a 8 bit only device. -- The card may work only in some cases but refuse to work most of the -time. The SB compatible mode always requires special initialization which is -done by the DOS/Windows drivers. This kind of cards work in Linux after -you have warm booted it after DOS but they don't work after cold boot -(power on or reset). -- You get the famous "DMA timed out" messages. Usually all SB clones have -software selectable IRQ and DMA settings. If the (power on default) values -currently used by the card don't match configuration of the driver you will -get the above error message whenever you try to record or play. There are -few other reasons to the DMA timeout message but using the SB mode seems -to be the most common cause. - -2. Trying to use a PnP (Plug & Play) card just like an ordinary sound card --------------------------------------------------------------------------- - -Plug & Play is a protocol defined by Intel and Microsoft. It lets operating -systems to easily identify and reconfigure I/O ports, IRQs and DMAs of ISA -cards. The problem with PnP cards is that the standard Linux doesn't currently -(versions 2.1.x and earlier) don't support PnP. This means that you will have -to use some special tricks (see later) to get a PnP card alive. Many PnP cards -work after they have been initialized but this is not always the case. - -There are sometimes both PnP and non-PnP versions of the same sound card. -The non-PnP version is the original model which usually has been discontinued -more than an year ago. The PnP version has the same name but with "PnP" -appended to it (sometimes not). This causes major confusion since the non-PnP -model works with Linux but the PnP one doesn't. - -You should carefully check if "Plug & Play" or "PnP" is mentioned in the name -of the card or in the documentation or package that came with the card. -Everything described in the rest of this document is not necessarily valid for -PnP models of sound cards even you have managed to wake up the card properly. -Many PnP cards are simply too different from their non-PnP ancestors which are -covered by this document. - - -Cards that are not (fully) supported by this driver -=================================================== - -See http://www.opensound.com/ossfree for information about sound cards -to be supported in future. - - -How to use sound without recompiling kernel and/or sound driver -=============================================================== - -There is a commercial sound driver which comes in precompiled form and doesn't -require recompiling of the kernel. See http://www.4Front-tech.com/oss.html for -more info. - - -Configuring PnP cards -===================== - -New versions of most sound cards use the so-called ISA PnP protocol for -soft configuring their I/O, IRQ, DMA and shared memory resources. -Currently at least cards made by Creative Technology (SB32 and SB32AWE -PnP), Gravis (GUS PnP and GUS PnP Pro), Ensoniq (Soundscape PnP) and -Aztech (some Sound Galaxy models) use PnP technology. The CS4232/4236 audio -chip by Crystal Semiconductor (Intel Atlantis, HP Pavilion and many other -motherboards) is also based on PnP technology but there is a "native" driver -available for it (see information about CS4232 later in this document). - -PnP sound cards (as well as most other PnP ISA cards) are not supported -by this version of the driver . Proper -support for them should be released during 97 once the kernel level -PnP support is available. - -There is a method to get most of the PnP cards to work. The basic method -is the following: - -1) Boot DOS so the card's DOS drivers have a chance to initialize it. -2) _Cold_ boot to Linux by using "loadlin.exe". Hitting ctrl-alt-del -works with older machines but causes a hard reset of all cards on recent -(Pentium) machines. -3) If you have the sound driver in Linux configured properly, the card should -work now. "Proper" means that I/O, IRQ and DMA settings are the same as in -DOS. The hard part is to find which settings were used. See the documentation of -your card for more info. - -Windows 95 could work as well as DOS but running loadlin may be difficult. -Probably you should "shut down" your machine to MS-DOS mode before running it. - -Some machines have a BIOS utility for setting PnP resources. This is a good -way to configure some cards. In this case you don't need to boot DOS/Win95 -before starting Linux. - -Another way to initialize PnP cards without DOS/Win95 is a Linux based -PnP isolation tool. When writing this there is a pre alpha test version -of such a tool available from ftp://ftp.demon.co.uk/pub/unix/linux/utils. The -file is called isapnptools-*. Please note that this tool is just a temporary -solution which may be incompatible with future kernel versions having proper -support for PnP cards. There are bugs in setting DMA channels in earlier -versions of isapnptools so at least version 1.6 is required with sound cards. - -Yet another way to use PnP cards is to use (commercial) OSS/Linux drivers. See -http://www.opensound.com/linux.html for more info. This is probably the way you -should do it if you don't want to spend time recompiling the kernel and -required tools. - - -Read this before trying to configure the driver -=============================================== - -There are currently many cards that work with this driver. Some of the cards -have native support while others work since they emulate some other -card (usually SB, MSS/WSS and/or MPU401). The following cards have native -support in the driver. Detailed instructions for configuring these cards -will be given later in this document. - -Pro Audio Spectrum 16 (PAS16) and compatibles: - Pro Audio Spectrum 16 - Pro Audio Studio 16 - Logitech Sound Man 16 - NOTE! The original Pro Audio Spectrum as well as the PAS+ are not - and will not be supported by the driver. - -Media Vision Jazz16 based cards - Pro Sonic 16 - Logitech SoundMan Wave - (Other Jazz based cards should work but I don't have any reports - about them). - -Sound Blasters - SB 1.0 to 2.0 - SB Pro - SB 16 - SB32/64/AWE - Configure SB32/64/AWE just like SB16. See lowlevel/README.awe - for information about using the wave table synth. - NOTE! AWE63/Gold and 16/32/AWE "PnP" cards need to be activated - using isapnptools before they work with OSS/Free. - SB16 compatible cards by other manufacturers than Creative. - You have been fooled since there are _no_ SB16 compatible - cards on the market (as of May 1997). It's likely that your card - is compatible just with SB Pro but there is also a non-SB- - compatible 16 bit mode. Usually it's MSS/WSS but it could also - be a proprietary one like MV Jazz16 or ESS ES688. OPTi - MAD16 chips are very common in so called "SB 16 bit cards" - (try with the MAD16 driver). - - ====================================================================== - "Supposed to be SB compatible" cards. - Forget the SB compatibility and check for other alternatives - first. The only cards that work with the SB driver in - Linux have been made by Creative Technology (there is at least - one chip on the card with "CREATIVE" printed on it). The - only other SB compatible chips are ESS and Jazz16 chips - (maybe ALSxxx chips too but they probably don't work). - Most other "16 bit SB compatible" cards such as "OPTi/MAD16" or - "Crystal" are _NOT_ SB compatible in Linux. - - Practically all sound cards have some kind of SB emulation mode - in addition to their native (16 bit) mode. In most cases this - (8 bit only) SB compatible mode doesn't work with Linux. If - you get it working it may cause problems with games and - applications which require 16 bit audio. Some 16 bit only - applications don't check if the card actually supports 16 bits. - They just dump 16 bit data to a 8 bit card which produces just - noise. - - In most cases the 16 bit native mode is supported by Linux. - Use the SB mode with "clones" only if you don't find anything - better from the rest of this doc. - ====================================================================== - -Gravis Ultrasound (GUS) - GUS - GUS + the 16 bit option - GUS MAX - GUS ACE (No MIDI port and audio recording) - GUS PnP (with RAM) - -MPU-401 and compatibles - The driver works both with the full (intelligent mode) MPU-401 - cards (such as MPU IPC-T and MQX-32M) and with the UART only - dumb MIDI ports. MPU-401 is currently the most common MIDI - interface. Most sound cards are compatible with it. However, - don't enable MPU401 mode blindly. Many cards with native support - in the driver have their own MPU401 driver. Enabling the standard one - will cause a conflict with these cards. So check if your card is - in the list of supported cards before enabling MPU401. - -Windows Sound System (MSS/WSS) - Even when Microsoft has discontinued their own Sound System card - they managed to make it a standard. MSS compatible cards are based on - a codec chip which is easily available from at least two manufacturers - (AD1848 by Analog Devices and CS4231/CS4248 by Crystal Semiconductor). - Currently most sound cards are based on one of the MSS compatible codec - chips. The CS4231 is used in the high quality cards such as GUS MAX, - MediaTrix AudioTrix Pro and TB Tropez (GUS MAX is not MSS compatible). - - Having a AD1848, CS4248 or CS4231 codec chip on the card is a good - sign. Even if the card is not MSS compatible, it could be easy to write - support for it. Note also that most MSS compatible cards - require special boot time initialization which may not be present - in the driver. Also, some MSS compatible cards have native support. - Enabling the MSS support with these cards is likely to - cause a conflict. So check if your card is listed in this file before - enabling the MSS support. - -Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4) - Most sound cards have a FM synthesizer chip. The OPL2 is a 2 - operator chip used in the original AdLib card. Currently it's used - only in the cheapest (8 bit mono) cards. The OPL3 is a 4 operator - FM chip which provides better sound quality and/or more available - voices than the OPL2. The OPL4 is a new chip that has an OPL3 and - a wave table synthesizer packed onto the same chip. The driver supports - just the OPL3 mode directly. Most cards with an OPL4 (like - SM Wave and AudioTrix Pro) support the OPL4 mode using MPU401 - emulation. Writing a native OPL4 support is difficult - since Yamaha doesn't give information about their sample ROM chip. - - Enable the generic OPL2/OPL3 FM synthesizer support if your - card has a FM chip made by Yamaha. Don't enable it if your card - has a software (TRS) based FM emulator. - - ---------------------------------------------------------------- - NOTE! OPL3-SA is different chip than the ordinary OPL3. In addition - to the FM synth this chip has also digital audio (WSS) and - MIDI (MPU401) capabilities. Support for OPL3-SA is described below. - ---------------------------------------------------------------- - -Yamaha OPL3-SA1 - - Yamaha OPL3-SA1 (YMF701) is an audio controller chip used on some - (Intel) motherboards and on cheap sound cards. It should not be - confused with the original OPL3 chip (YMF278) which is entirely - different chip. OPL3-SA1 has support for MSS, MPU401 and SB Pro - (not used in OSS/Free) in addition to the OPL3 FM synth. - - There are also chips called OPL3-SA2, OPL3-SA3, ..., OPL3SA-N. They - are PnP chips and will not work with the OPL3-SA1 driver. You should - use the standard MSS, MPU401 and OPL3 options with these chips and to - activate the card using isapnptools. - -4Front Technologies SoftOSS - - SoftOSS is a software based wave table emulation which works with - any 16 bit stereo sound card. Due to its nature a fast CPU is - required (P133 is minimum). Although SoftOSS does _not_ use MMX - instructions it has proven out that recent processors (which appear - to have MMX) perform significantly better with SoftOSS than earlier - ones. For example a P166MMX beats a PPro200. SoftOSS should not be used - on 486 or 386 machines. - - The amount of CPU load caused by SoftOSS can be controlled by - selecting the CONFIG_SOFTOSS_RATE and CONFIG_SOFTOSS_VOICES - parameters properly (they will be prompted by make config). It's - recommended to set CONFIG_SOFTOSS_VOICES to 32. If you have a - P166MMX or faster (PPro200 is not faster) you can set - CONFIG_SOFTOSS_RATE to 44100 (kHz). However with slower systems it - recommended to use sampling rates around 22050 or even 16000 kHz. - Selecting too high values for these parameters may hang your - system when playing MIDI files with hight degree of polyphony - (number of concurrently playing notes). It's also possible to - decrease CONFIG_SOFTOSS_VOICES. This makes it possible to use - higher sampling rates. However using fewer voices decreases - playback quality more than decreasing the sampling rate. - - SoftOSS keeps the samples loaded on the system's RAM so much RAM is - required. SoftOSS should never be used on machines with less than 16 MB - of RAM since this is potentially dangerous (you may accidently run out - of memory which probably crashes the machine). - - SoftOSS implements the wave table API originally designed for GUS. For - this reason all applications designed for GUS should work (at least - after minor modifications). For example gmod/xgmod and playmidi -g are - known to work. - - To work SoftOSS will require GUS compatible - patch files to be installed on the system (in /dos/ultrasnd/midi). You - can use the public domain MIDIA patchset available from several ftp - sites. - - ********************************************************************* - IMPORTANT NOTICE! The original patch set distributed with the Gravis - Ultrasound card is not in public domain (even though it's available from - some FTP sites). You should contact Voice Crystal (www.voicecrystal.com) - if you like to use these patches with SoftOSS included in OSS/Free. - ********************************************************************* - -PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC) - Analog Devices and Echo Speech have together defined a sound card - architecture based on the above chips. The DSP chip is used - for emulation of SB Pro, FM and General MIDI/MT32. - - There are several cards based on this architecture. The most known - ones are Orchid SW32 and Cardinal DSP16. - - The driver supports downloading DSP algorithms to these cards. - - NOTE! You will have to use the "old" config script when configuring - PSS cards. - -MediaTrix AudioTrix Pro - The ATP card is built around a CS4231 codec and an OPL4 synthesizer - chips. The OPL4 mode is supported by a microcontroller running a - General MIDI emulator. There is also a SB 1.5 compatible playback mode. - -Ensoniq SoundScape and compatibles - Ensoniq has designed a sound card architecture based on the - OTTO synthesizer chip used in their professional MIDI synthesizers. - Several companies (including Ensoniq, Reveal and Spea) are selling - cards based on this architecture. - - NOTE! The SoundScape PnP is not supported by OSS/Free. Ensoniq VIVO and - VIVO90 cards are not compatible with Soundscapes so the Soundscape - driver will not work with them. You may want to use OSS/Linux with these - cards. - -OPTi MAD16 and Mozart based cards - The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929), - OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface - chips are used in many different sound cards, including some - cards by Reveal miro and Turtle Beach (Tropez). The purpose of these - chips is to connect other audio components to the PC bus. The - interface chip performs address decoding for the other chips. - NOTE! Tropez Plus is not MAD16 but CS4232 based. - NOTE! MAD16 PnP cards (82C924, 82C925, 82C931) are not MAD16 compatible - in the PnP mode. You will have to use them in MSS mode after having - initialized them using isapnptools or DOS. 82C931 probably requires - initialization using DOS/Windows (running isapnptools is not enough). - It's possible to use 82C931 with OSS/Free by jumpering it to non-PnP - mode (provided that the card has a jumper for this). In non-PnP mode - 82C931 is compatible with 82C930 and should work with the MAD16 driver - (without need to use isapnptools or DOS to initialize it). All OPTi - chips are supported by OSS/Linux (both in PnP and non-PnP modes). - -Audio Excel DSP16 - Support for this card was written by Riccardo Faccetti - (riccardo@cdc8g5.cdc.polimi.it). The AEDSP16 driver included in - the lowlevel/ directory. To use it you should enable the - "Additional low level drivers" option. - -Crystal CS4232 and CS4236 based cards such as AcerMagic S23, TB Tropez _Plus_ and - many PC motherboards (Compaq, HP, Intel, ...) - CS4232 is a PnP multimedia chip which contains a CS3231A codec, - SB and MPU401 emulations. There is support for OPL3 too. - Unfortunately the MPU401 mode doesn't work (I don't know how to - initialize it). CS4236 is an enhanced (compatible) version of CS4232. - NOTE! Don't ever try to use isapnptools with CS4232 since this will just - freeze your machine (due to chip bugs). If you have problems in getting - CS4232 working you could try initializing it with DOS (CS4232C.EXE) and - then booting Linux using loadlin. CS4232C.EXE loads a secret firmware - patch which is not documented by Crystal. - -Turtle Beach Maui and Tropez "classic" - This driver version supports sample, patch and program loading commands - described in the Maui/Tropez User's manual. - There is now full initialization support too. The audio side of - the Tropez is based on the MAD16 chip (see above). - NOTE! Tropez Plus is different card than Tropez "classic" and will not - work fully in Linux. You can get audio features working by configuring - the card as a CS4232 based card (above). - - -Jumpers and software configuration -================================== - -Some of the earliest sound cards were jumper configurable. You have to -configure the driver use I/O, IRQ and DMA settings -that match the jumpers. Just few 8 bit cards are fully jumper -configurable (SB 1.x/2.x, SB Pro and clones). -Some cards made by Aztech have an EEPROM which contains the -config info. These cards behave much like hardware jumpered cards. - -Most cards have jumper for the base I/O address but other parameters -are software configurable. Sometimes there are few other jumpers too. - -Latest cards are fully software configurable or they are PnP ISA -compatible. There are no jumpers on the board. - -The driver handles software configurable cards automatically. Just configure -the driver to use I/O, IRQ and DMA settings which are known to work. -You could usually use the same values than with DOS and/or Windows. -Using different settings is possible but not recommended since it may cause -some trouble (for example when warm booting from an OS to another or -when installing new hardware to the machine). - -Sound driver sets the soft configurable parameters of the card automatically -during boot. Usually you don't need to run any extra initialization -programs when booting Linux but there are some exceptions. See the -card-specific instructions below for more info. - -The drawback of software configuration is that the driver needs to know -how the card must be initialized. It cannot initialize unknown cards -even if they are otherwise compatible with some other cards (like SB, -MPU401 or Windows Sound System). - - -What if your card was not listed above? -======================================= - -The first thing to do is to look at the major IC chips on the card. -Many of the latest sound cards are based on some standard chips. If you -are lucky, all of them could be supported by the driver. The most common ones -are the OPTi MAD16, Mozart, SoundScape (Ensoniq) and the PSS architectures -listed above. Also look at the end of this file for list of unsupported -cards and the ones which could be supported later. - -The last resort is to send _exact_ name and model information of the card -to me together with a list of the major IC chips (manufactured, model) to -me. I could then try to check if your card looks like something familiar. - -There are many more cards in the world than listed above. The first thing to -do with these cards is to check if they emulate some other card or interface -such as SB, MSS and/or MPU401. In this case there is a chance to get the -card to work by booting DOS before starting Linux (boot DOS, hit ctrl-alt-del -and boot Linux without hard resetting the machine). In this method the -DOS based driver initializes the hardware to use known I/O, IRQ and DMA -settings. If sound driver is configured to use the same settings, everything -should work OK. - - -Configuring sound driver (with Linux) -===================================== - -The sound driver is currently distributed as part of the Linux kernel. The -files are in /usr/src/linux/drivers/sound/. - -**************************************************************************** -* ALWAYS USE THE SOUND DRIVER VERSION WHICH IS DISTRIBUTED WITH * -* THE KERNEL SOURCE PACKAGE YOU ARE USING. SOME ALPHA AND BETA TEST * -* VERSIONS CAN BE INSTALLED FROM A SEPARATELY DISTRIBUTED PACKAGE * -* BUT CHECK THAT THE PACKAGE IS NOT MUCH OLDER (OR NEWER) THAN THE * -* KERNEL YOU ARE USING. IT'S POSSIBLE THAT THE KERNEL/DRIVER * -* INTERFACE CHANGES BETWEEN KERNEL RELEASES WHICH MAY CAUSE SOME * -* INCOMPATIBILITY PROBLEMS. * -* * -* IN CASE YOU INSTALL A SEPARATELY DISTRIBUTED SOUND DRIVER VERSION, * -* BE SURE TO REMOVE OR RENAME THE OLD SOUND DRIVER DIRECTORY BEFORE * -* INSTALLING THE NEW ONE. LEAVING OLD FILES TO THE SOUND DRIVER * -* DIRECTORY _WILL_ CAUSE PROBLEMS WHEN THE DRIVER IS USED OR * -* COMPILED. * -**************************************************************************** - -To configure the driver, run "make config" in the kernel source directory -(/usr/src/linux). Answer "y" or "m" to the question about Sound card support -(after the questions about mouse, CD-ROM, ftape, etc. support). Questions -about options for sound will then be asked. - -After configuring the kernel and sound driver, run "make dep" and compile -the kernel following instructions in the kernel README. - -The sound driver configuration dialog -------------------------------------- - -Sound configuration starts by making some yes/no questions. Be careful -when answering to these questions since answering y to a question may -prevent some later ones from being asked. For example don't answer y to -the first question (PAS16) if you don't really have a PAS16. Don't enable -more cards than you really need since they just consume memory. Also -some drivers (like MPU401) may conflict with your SCSI controller and -prevent kernel from booting. If you card was in the list of supported -cards (above), please look at the card specific config instructions -(later in this file) before starting to configure. Some cards must be -configured in way which is not obvious. - -So here is the beginning of the config dialog. Answer 'y' or 'n' to these -questions. The default answer is shown so that (y/n) means 'y' by default and -(n/y) means 'n'. To use the default value, just hit ENTER. But be careful -since using the default _doesn't_ guarantee anything. - -Note also that all questions may not be asked. The configuration program -may disable some questions depending on the earlier choices. It may also -select some options automatically as well. - - "ProAudioSpectrum 16 support", - - Answer 'y'_ONLY_ if you have a Pro Audio Spectrum _16_, - Pro Audio Studio 16 or Logitech SoundMan 16 (be sure that - you read the above list correctly). Don't answer 'y' if you - have some other card made by Media Vision or Logitech since they - are not PAS16 compatible. - NOTE! Since 3.5-beta10 you need to enable SB support (next question) - if you want to use the SB emulation of PAS16. It's also possible to - the emulation if you want to use a true SB card together with PAS16 - (there is another question about this that is asked later). - "Sound Blaster support", - - Answer 'y' if you have an original SB card made by Creative Labs - or a full 100% hardware compatible clone (like Thunderboard or - SM Games). If your card was in the list of supported cards (above), - please look at the card specific instructions later in this file - before answering this question. For an unknown card you may answer - 'y' if the card claims to be SB compatible. - Enable this option also with PAS16 (changed since v3.5-beta9). - - Don't enable SB if you have a MAD16 or Mozart compatible card. - - "Generic OPL2/OPL3 FM synthesizer support", - - Answer 'y' if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). - Answering 'y' is usually a safe and recommended choice. However some - cards may have software (TSR) FM emulation. Enabling FM support - with these cards may cause trouble. However I don't currently know - such cards. - "Gravis Ultrasound support", - - Answer 'y' if you have GUS or GUS MAX. Answer 'n' if you don't - have GUS since the GUS driver consumes much memory. - Currently I don't have experiences with the GUS ACE so I don't - know what to answer with it. - "MPU-401 support (NOT for SB16)", - - Be careful with this question. The MPU401 interface is supported - by almost any sound card today. However some natively supported cards - have their own driver for MPU401. Enabling the MPU401 option with - these cards will cause a conflict. Also enabling MPU401 on a system - that doesn't really have a MPU401 could cause some trouble. If your - card was in the list of supported cards (above), please look at - the card specific instructions later in this file. - - In MOST cases this MPU401 driver should only be used with "true" - MIDI-only MPU401 professional cards. In most other cases there - is another way to get the MPU401 compatible interface of a - sound card to work. - Support for the MPU401 compatible MIDI port of SB16, ESS1688 - and MV Jazz16 cards is included in the SB driver. Use it instead - of this separate MPU401 driver with these cards. As well - Soundscape, PSS and Maui drivers include their own MPU401 - options. - - It's safe to answer 'y' if you have a true MPU401 MIDI interface - card. - "6850 UART Midi support", - - It's safe to answer 'n' to this question in all cases. The 6850 - UART interface is so rarely used. - "PSS (ECHO-ADI2111) support", - - Answer 'y' only if you have Orchid SW32, Cardinal DSP16 or some - other card based on the PSS chipset (AD1848 codec + ADSP-2115 - DSP chip + Echo ESC614 ASIC CHIP). - "16 bit sampling option of GUS (_NOT_ GUS MAX)", - - Answer 'y' if you have installed the 16 bit sampling daughtercard - to your GUS. Answer 'n' if you have GUS MAX. Enabling this option - disables GUS MAX support. - "GUS MAX support", - - Answer 'y' only if you have a GUS MAX. - "Microsoft Sound System support", - - Again think carefully before answering 'y' to this question. It's - safe to answer 'y' in case you have the original Windows Sound - System card made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). - Also you may answer 'y' in case your card was not listed earlier - in this file. For cards having native support in the driver, consult - the card specific instructions later in this file. Some drivers - have their own MSS support and enabling this option will cause a - conflict. - Note! The MSS driver permits configuring two DMA channels. This is a - "nonstandard" feature and works only with very few cards (if any). - In most cases the second DMA channel should be disabled or set to - the same channel than the first one. Trying to configure two separate - channels with cards that don't support this feature will prevent - audio (at least recording) from working. - "Ensoniq Soundscape support", - - Answer 'y' if you have a sound card based on the Ensoniq SoundScape - chipset. Such cards are being manufactured at least by Ensoniq, - Spea and Reveal (note that Reveal makes other cards also). The oldest - cards made by Spea don't work properly with Linux. - Soundscape PnP as well as Ensoniq VIVO work only with the commercial - OSS/Linux version. - "MediaTrix AudioTrix Pro support", - - Answer 'y' if you have the AudioTrix Pro. - "Support for MAD16 and/or Mozart based cards", - - Answer y if your card has a Mozart (OAK OTI-601) or MAD16 - (OPTi 82C928, 82C929, 82C924/82C925 or 82C930) audio interface chip. - These chips are - currently quite common so it's possible that many no-name cards - have one of them. In addition the MAD16 chip is used in some - cards made by known manufacturers such as Turtle Beach (Tropez), - Reveal (some models) and Diamond (some recent models). - Note OPTi 82C924 and 82C925 are MAD16 compatible only in non PnP - mode (jumper selectable on many cards). - "Support for TB Maui" - - This enables TB Maui specific initialization. Works with TB Maui - and TB Tropez (may not work with Tropez Plus). - - -Then the configuration program asks some y/n questions about the higher -level services. It's recommended to answer 'y' to each of these questions. -Answer 'n' only if you know you will not need the option. - - "MIDI interface support", - - Answering 'n' disables /dev/midi## devices and access to any - MIDI ports using /dev/sequencer and /dev/music. This option - also affects any MPU401 and/or General MIDI compatible devices. - "FM synthesizer (YM3812/OPL-3) support", - - Answer 'y' here. - "/dev/sequencer support", - - Answering 'n' disables /dev/sequencer and /dev/music. - -Entering the I/O, IRQ and DMA config parameters ------------------------------------------------ - -After the above questions the configuration program prompts for the -card specific configuration information. Usually just a set of -I/O address, IRQ and DMA numbers are asked. With some cards the program -asks for some files to be used during initialization of the card. For example -many cards have a DSP chip or microprocessor which must be initialized by -downloading a program (microcode) file to the card. - -Instructions for answering these questions are given in the next section. - - -Card specific information -========================= - -This section gives additional instructions about configuring some cards. -Please refer manual of your card for valid I/O, IRQ and DMA numbers. Using -the same settings with DOS/Windows and Linux is recommended. Using -different values could cause some problems when switching between -different operating systems. - -Sound Blasters (the original ones by Creative) ---------------------------------------------- - -NOTE! Check if you have a PnP Sound Blaster (cards sold after summer 1995 - are almost certainly PnP ones). With PnP cards you should use isapnptools - to activate them (see above). - -It's possible to configure these cards to use different I/O, IRQ and -DMA settings. Since the possible/default settings have changed between various -models, you have to consult manual of your card for the proper ones. It's -a good idea to use the same values than with DOS/Windows. With SB and SB Pro -it's the only choice. SB16 has software selectable IRQ and DMA channels but -using different values with DOS and Linux is likely to cause troubles. The -DOS driver is not able to reset the card properly after warm boot from Linux -if Linux has used different IRQ or DMA values. - -The original (steam) Sound Blaster (versions 1.x and 2.x) use always -DMA1. There is no way to change it. - -The SB16 needs two DMA channels. A 8 bit one (1 or 3) is required for -8 bit operation and a 16 bit one (5, 6 or 7) for the 16 bit mode. In theory -it's possible to use just one (8 bit) DMA channel by answering the 8 bit -one when the configuration program asks for the 16 bit one. This may work -in some systems but is likely to cause terrible noise on some other systems. - -It's possible to use two SB16/32/64 at the same time. To do this you should -first configure OSS/Free for one card. Then edit local.h manually and define -SB2_BASE, SB2_IRQ, SB2_DMA and SB2_DMA2 for the second one. You can't get -the OPL3, MIDI and EMU8000 devices of the second card to work. If you are -going to use two PnP Sound Blasters, ensure that they are of different model -and have different PnP IDs. There is no way to get two cards with the same -card ID and serial number to work. The easiest way to check this is trying -if isapnptools can see both cards or just one. - -NOTE! Don't enable the SM Games option (asked by the configuration program) - if you are not 101% sure that your card is a Logitech Soundman Games - (not a SM Wave or SM16). - -SB Clones ---------- - -First of all: There are no SB16 clones. There are SB Pro clones with a -16 bit mode which is not SB16 compatible. The most likely alternative is that -the 16 bit mode means MSS/WSS. - -There are just a few fully 100% hardware SB or SB Pro compatible cards. -I know just Thunderboard and SM Games. Other cards require some kind of -hardware initialization before they become SB compatible. Check if your card -was listed in the beginning of this file. In this case you should follow -instructions for your card later in this file. - -For other not fully SB clones you may try initialization using DOS in -the following way: - - - Boot DOS so that the card specific driver gets run. - - Hit ctrl-alt-del (or use loadlin) to boot Linux. Don't - switch off power or press the reset button. - - If you use the same I/O, IRQ and DMA settings in Linux, the - card should work. - -If your card is both SB and MSS compatible, I recommend using the MSS mode. -Most cards of this kind are not able to work in the SB and the MSS mode -simultaneously. Using the MSS mode provides 16 bit recording and playback. - -ProAudioSpectrum 16 and compatibles ------------------------------------ - -PAS16 has a SB emulation chip which can be used together with the native -(16 bit) mode of the card. To enable this emulation you should configure -the driver to have SB support too (this has been changed since version -3.5-beta9 of this driver). - -With current driver versions it's also possible to use PAS16 together with -another SB compatible card. In this case you should configure SB support -for the other card and to disable the SB emulation of PAS16 (there is a -separate questions about this). - -With PAS16 you can use two audio device files at the same time. /dev/dsp (and -/dev/audio) is connected to the 8/16 bit native codec and the /dev/dsp1 (and -/dev/audio1) is connected to the SB emulation (8 bit mono only). - -Gravis Ultrasound ------------------ - -There are many different revisions of the Ultrasound card (GUS). The -earliest ones (pre 3.7) don't have a hardware mixer. With these cards -the driver uses a software emulation for synth and pcm playbacks. It's -also possible to switch some of the inputs (line in, mic) off by setting -mixer volume of the channel level below 10%. For recording you have -to select the channel as a recording source and to use volume above 10%. - -GUS 3.7 has a hardware mixer. - -GUS MAX and the 16 bit sampling daughtercard have a CS4231 codec chip which -also contains a mixer. - -Configuring GUS is simple. Just enable the GUS support and GUS MAX or -the 16 bit daughtercard if you have them. Note that enabling the daughter -card disables GUS MAX driver. - -NOTE for owners of the 16 bit daughtercard: By default the daughtercard -uses /dev/dsp (and /dev/audio). Command "ln -sf /dev/dsp1 /dev/dsp" -selects the daughter card as the default device. - -With just the standard GUS enabled the configuration program prompts -for the I/O, IRQ and DMA numbers for the card. Use the same values than -with DOS. - -With the daughter card option enabled you will be prompted for the I/O, -IRQ and DMA numbers for the daughter card. You have to use different I/O -and DMA values than for the standard GUS. The daughter card permits -simultaneous recording and playback. Use /dev/dsp (the daughtercard) for -recording and /dev/dsp1 (GUS GF1) for playback. - -GUS MAX uses the same I/O address and IRQ settings than the original GUS -(GUS MAX = GUS + a CS4231 codec). In addition an extra DMA channel may be used. -Using two DMA channels permits simultaneous playback using two devices -(dev/dsp0 and /dev/dsp1). The second DMA channel is required for -full duplex audio. -To enable the second DMA channels, give a valid DMA channel when the config -program asks for the GUS MAX DMA (entering -1 disables the second DMA). -Using 16 bit DMA channels (5,6 or 7) is recommended. - -If you have problems in recording with GUS MAX, you could try to use -just one 8 bit DMA channel. Recording will not work with one DMA -channel if it's a 16 bit one. - -Microphone input of GUS MAX is connected to mixer in little bit nonstandard -way. There is actually two microphone volume controls. Normal "mic" controls -only recording level. Mixer control "speaker" is used to control volume of -microphone signal connected directly to line/speaker out. So just decrease -volume of "speaker" if you have problems with microphone feedback. - -GUS ACE works too but any attempt to record or to use the MIDI port -will fail. - -GUS PnP (with RAM) is partially supported but it needs to be initialized using -DOS or isapnptools before starting the driver. - -MPU401 and Windows Sound System -------------------------------- - -Again. Don't enable these options in case your card is listed -somewhere else in this file. - -Configuring these cards is obvious (or it should be). With MSS -you should probably enable the OPL3 synth also since -most MSS compatible cards have it. However check that this is true -before enabling OPL3. - -Sound driver supports more than one MPU401 compatible cards at the same time -but the config program asks config info for just the first of them. -Adding the second or third MPU interfaces must be done manually by -editing sound/local.h (after running the config program). Add defines for -MPU2_BASE & MPU2_IRQ (and MPU3_BASE & MPU3_IRQ) to the file. - -CAUTION! - -The default I/O base of Adaptec AHA-1542 SCSI controller is 0x330 which -is also the default of the MPU401 driver. Don't configure the sound driver to -use 0x330 as the MPU401 base if you have a AHA1542. The kernel will not boot -if you make this mistake. - -PSS ---- - -Even the PSS cards are compatible with SB, MSS and MPU401, you must not -enable these options when configuring the driver. The configuration -program handles these options itself. (You may use the SB, MPU and MSS options -together with PSS if you have another card on the system). - -The PSS driver enables MSS and MPU401 modes of the card. SB is not enabled -since it doesn't work concurrently with MSS. The driver loads also a -DSP algorithm which is used to for the general MIDI emulation. The -algorithm file (.ld) is read by the config program and written to a -file included when the pss.c is compiled. For this reason the config -program asks if you want to download the file. Use the genmidi.ld file -distributed with the DOS/Windows drivers of the card (don't use the mt32.ld). -With some cards the file is called 'synth.ld'. You must have access to -the file when configuring the driver. The easiest way is to mount the DOS -partition containing the file with Linux. - -It's possible to load your own DSP algorithms and run them with the card. -Look at the directory pss_test of snd-util-3.0.tar.gz for more info. - -AudioTrix Pro -------------- - -You have to enable the OPL3 and SB (not SB Pro or SB16) drivers in addition -to the native AudioTrix driver. Don't enable MSS or MPU drivers. - -Configuring ATP is little bit tricky since it uses so many I/O, IRQ and -DMA numbers. Using the same values than with DOS/Win is a good idea. Don't -attempt to use the same IRQ or DMA channels twice. - -The SB mode of ATP is implemented so the ATP driver just enables SB -in the proper address. The SB driver handles the rest. You have to configure -both the SB driver and the SB mode of ATP to use the same IRQ, DMA and I/O -settings. - -Also the ATP has a microcontroller for the General MIDI emulation (OPL4). -For this reason the driver asks for the name of a file containing the -microcode (TRXPRO.HEX). This file is usually located in the directory -where the DOS drivers were installed. You must have access to this file -when configuring the driver. - -If you have the effects daughtercard, it must be initialized by running -the setfx program of snd-util-3.0.tar.gz package. This step is not required -when using the (future) binary distribution version of the driver. - -Ensoniq SoundScape ------------------- - -NOTE! The new PnP SoundScape is not supported yet. Soundscape compatible - cards made by Reveal don't work with Linux. They use older revision - of the Soundscape chipset which is not fully compatible with - newer cards made by Ensoniq. - -The SoundScape driver handles initialization of MSS and MPU supports -itself so you don't need to enable other drivers than SoundScape -(enable also the /dev/dsp, /dev/sequencer and MIDI supports). - -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!! !!!! -!!!!! NOTE! Before version 3.5-beta6 there WERE two sets of audio !!!! -!!!!! device files (/dev/dsp0 and /dev/dsp1). The first one WAS !!!! -!!!!! used only for card initialization and the second for audio !!!! -!!!!! purposes. It WAS required to change /dev/dsp (a symlink) to !!!! -!!!!! point to /dev/dsp1. !!!! -!!!!! !!!! -!!!!! This is not required with OSS versions 3.5-beta6 and later !!!! -!!!!! since there is now just one audio device file. Please !!!! -!!!!! change /dev/dsp to point back to /dev/dsp0 if you are !!!! -!!!!! upgrading from an earlier driver version using !!!! -!!!!! (cd /dev;rm dsp;ln -s dsp0 dsp). !!!! -!!!!! !!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -The configuration program asks one DMA channel and two interrupts. One IRQ -and one DMA is used by the MSS codec. The second IRQ is required for the -MPU401 mode (you have to use different IRQs for both purposes). -There were earlier two DMA channels for SoundScape but the current driver -version requires just one. - -The SoundScape card has a Motorola microcontroller which must initialized -_after_ boot (the driver doesn't initialize it during boot). -The initialization is done by running the 'ssinit' program which is -distributed in the snd-util-3.0.tar.gz package. You have to edit two -defines in the ssinit.c and then compile the program. You may run ssinit -manually (after each boot) or add it to /etc/rc.d/rc.local. - -The ssinit program needs the microcode file that comes with the DOS/Windows -driver of the card. You will need to use version 1.30.00 or later -of the microcode file (sndscape.co0 or sndscape.co1 depending on -your card model). THE OLD sndscape.cod WILL NOT WORK. IT WILL HANG YOUR -MACHINE. The only way to get the new microcode file is to download -and install the DOS/Windows driver from ftp://ftp.ensoniq.com/pub. - -Then you have to select the proper microcode file to use: soundscape.co0 -is the right one for most cards and sndscape.co1 is for few (older) cards -made by Reveal and/or Spea. The driver has capability to detect the card -version during boot. Look at the boot log messages in /var/adm/messages -and locate the sound driver initialization message for the SoundScape -card. If the driver displays string , you have -an old card and you will need to use sndscape.co1. For other cards use -soundscape.co0. New Soundscape revisions such as Elite and PnP use -code files with higher numbers (.co2, .co3, etc.). - -NOTE! Ensoniq Soundscape VIVO is not compatible with other Soundscape cards. - Currently it's possible to use it in Linux only with OSS/Linux - drivers. - -Check /var/adm/messages after running ssinit. The driver prints -the board version after downloading the microcode file. That version -number must match the number in the name of the microcode file (extension). - -Running ssinit with a wrong version of the sndscape.co? file is not -dangerous as long as you don't try to use a file called sndscape.cod. -If you have initialized the card using a wrong microcode file (sounds -are terrible), just modify ssinit.c to use another microcode file and try -again. It's possible to use an earlier version of sndscape.co[01] but it -may sound weird. - -MAD16 (Pro) and Mozart ----------------------- - -You need to enable just the MAD16 /Mozart support when configuring -the driver. _Don't_ enable SB, MPU401 or MSS. However you will need the -/dev/audio, /dev/sequencer and MIDI supports. - -Mozart and OPTi 82C928 (the original MAD16) chips don't support -MPU401 mode so enter just 0 when the configuration program asks the -MPU/MIDI I/O base. The MAD16 Pro (OPTi 82C929) and 82C930 chips have MPU401 -mode. - -TB Tropez is based on the 82C929 chip. It has two MIDI ports. -The one connected to the MAD16 chip is the second one (there is a second -MIDI connector/pins somewhere??). If you have not connected the second MIDI -port, just disable the MIDI port of MAD16. The 'Maui' compatible synth of -Tropez is jumper configurable and not connected to the MAD16 chip (the -Maui driver can be used with it). - -Some MAD16 based cards may cause feedback, whistle or terrible noise if the -line3 mixer channel is turned too high. This happens at least with Shuttle -Sound System. Current driver versions set volume of line3 low enough so -this should not be a problem. - -If you have a MAD16 card which have an OPL4 (FM + Wave table) synthesizer -chip (_not_ an OPL3), you have to append a line containing #define MAD16_OPL4 -to the file linux/drivers/sound/local.h (after running make config). - -MAD16 cards having a CS4231 codec support full duplex mode. This mode -can be enabled by configuring the card to use two DMA channels. Possible -DMA channel pairs are: 0&1, 1&0 and 3&0. - -NOTE! Cards having an OPTi 82C924/82C925 chip work with OSS/Free only in -non-PnP mode (usually jumper selectable). The PnP mode is supported only -by OSS/Linux. - -MV Jazz (ProSonic) ------------------- - -The Jazz16 driver is just a hack made to the SB Pro driver. However it works -fairly well. You have to enable SB, SB Pro (_not_ SB16) and MPU401 supports -when configuring the driver. The configuration program asks later if you -want support for MV Jazz16 based cards (after asking SB base address). Answer -'y' here and the driver asks the second (16 bit) DMA channel. - -The Jazz16 driver uses the MPU401 driver in a way which will cause -problems if you have another MPU401 compatible card. In this case you must -give address of the Jazz16 based MPU401 interface when the config -program prompts for the MPU401 information. Then look at the MPU401 -specific section for instructions about configuring more than one MPU401 cards. - -Logitech Soundman Wave ----------------------- - -Read the above MV Jazz specific instructions first. - -The Logitech SoundMan Wave (don't confuse this with the SM16 or SM Games) is -a MV Jazz based card which has an additional OPL4 based wave table -synthesizer. The OPL4 chip is handled by an on board microcontroller -which must be initialized during boot. The config program asks if -you have a SM Wave immediately after asking the second DMA channel of jazz16. -If you answer 'y', the config program will ask name of the file containing -code to be loaded to the microcontroller. The file is usually called -MIDI0001.BIN and it's located in the DOS/Windows driver directory. The file -may also be called as TSUNAMI.BIN or something else (older cards?). - -The OPL4 synth will be inaccessible without loading the microcontroller code. - -Also remember to enable SB MPU401 support if you want to use the OPL4 mode. -(Don't enable the 'normal' MPU401 device as with some earlier driver -versions (pre 3.5-alpha8)). - -NOTE! Don't answer 'y' when the driver asks about SM Games support - (the next question after the MIDI0001.BIN name). However - answering 'y' doesn't cause damage your computer so don't panic. - -Sound Galaxies --------------- - -There are many different Sound Galaxy cards made by Aztech. The 8 bit -ones are fully SB or SB Pro compatible and there should be no problems -with them. - -The older 16 bit cards (SG Pro16, SG NX Pro16, Nova and Lyra) have -an EEPROM chip for storing the configuration data. There is a microcontroller -which initializes the card to match the EEPROM settings when the machine -is powered on. These cards actually behave just like they have jumpers -for all of the settings. Configure driver for MSS, MPU, SB/SB Pro and OPL3 -supports with these cards. - -There are some new Sound Galaxies in the market. I have no experience with -them so read the card's manual carefully. - -ESS ES1688 and ES688 'AudioDrive' based cards ---------------------------------------------- - -Support for these two ESS chips is embedded in the SB driver. -Configure these cards just like SB. Enable the 'SB MPU401 MIDI port' -if you want to use MIDI features of ES1688. ES688 doesn't have MPU mode -so you don't need to enable it (the driver uses normal SB MIDI automatically -with ES688). - -NOTE! ESS cards are not compatible with MSS/WSS so don't worry if MSS support -of OSS doesn't work with it. - -There are some ES1688/688 based sound cards and (particularly) motherboards -which use software configurable I/O port relocation feature of the chip. -This ESS proprietary feature is supported only by OSS/Linux. - -There are ES1688 based cards which use different interrupt pin assignment than -recommended by ESS (5, 7, 9/2 and 10). In this case all IRQs don't work. -At least a card called (Pearl?) Hypersound 16 supports IRQ 15 but it doesn't -work. - -ES1868 is a PnP chip which is (supposed to be) compatible with ESS1688 -probably works with OSS/Free after initialization using isapnptools. - -Reveal cards ------------- - -There are several different cards made/marketed by Reveal. Some of them -are compatible with SoundScape and some use the MAD16 chip. You may have -to look at the card and try to identify its origin. - -Diamond -------- - -The oldest (Sierra Aria based) sound cards made by Diamond are not supported -(they may work if the card is initialized using DOS). The recent (LX?) -models are based on the MAD16 chip which is supported by the driver. - -Audio Excel DSP16 ------------------ - -Support for this card is currently not functional. A new driver for it -should be available later this year. - -PCMCIA cards ------------- - -Sorry, can't help. Some cards may work and some don't. - -TI TM4000M notebooks --------------------- - -These computers have a built in sound support based on the Jazz chipset. -Look at the instructions for MV Jazz (above). It's also important to note -that there is something wrong with the mouse port and sound at least on -some TM models. Don't enable the "C&T 82C710 mouse port support" when -configuring Linux. Having it enabled is likely to cause mysterious problems -and kernel failures when sound is used. - -miroSOUND ---------- - -The miroSOUND PCM1-pro, PCM12 and PCM20 radio has been used -successfully. These cards are based on the MAD16, OPL4, and CS4231A chips -and everything said in the section about MAD16 cards applies here, -too. The only major difference between the PCMxx and other MAD16 cards -is that instead of the mixer in the CS4231 codec a separate mixer -controlled by an on-board 80C32 microcontroller is used. Control of -the mixer takes place via the ACI (miro's audio control interface) -protocol that is implemented in a separate lowlevel driver. Make sure -you compile this ACI driver together with the normal MAD16 support -when you use a miroSOUND PCMxx card. The ACI mixer is controlled by -/dev/mixer and the CS4231 mixer by /dev/mixer1 (depends on load -time). Only in special cases you want to change something regularly on -the CS4231 mixer. - -The miroSOUND PCM12 and PCM20 radio is capable of full duplex -operation (simultaneous PCM replay and recording), which allows you to -implement nice real-time signal processing audio effect software and -network telephones. The ACI mixer has to be switched into the "solo" -mode for duplex operation in order to avoid feedback caused by the -mixer (input hears output signal). You can de-/activate this mode -through toggleing the record button for the wave controller with an -OSS-mixer. - -The PCM20 contains a radio tuner, which is also controlled by -ACI. This radio tuner is supported by the ACI driver together with the -miropcm20.o module. Also the 7-band equalizer is integrated -(limited by the OSS-design). Developement has started and maybe -finished for the RDS decoder on this card, too. You will be able to -read RadioText, the Programme Service name, Programme TYpe and -others. Even the v4l radio module benefits from it with a refined -strength value. See aci.[ch] and miropcm20*.[ch] for more details. - -The following configuration parameters have worked fine for the PCM12 -in Markus Kuhn's system, many other configurations might work, too: -CONFIG_MAD16_BASE=0x530, CONFIG_MAD16_IRQ=11, CONFIG_MAD16_DMA=3, -CONFIG_MAD16_DMA2=0, CONFIG_MAD16_MPU_BASE=0x330, CONFIG_MAD16_MPU_IRQ=10, -DSP_BUFFSIZE=65536, SELECTED_SOUND_OPTIONS=0x00281000. - -Bas van der Linden is using his PCM1-pro with a configuration that -differs in: CONFIG_MAD16_IRQ=7, CONFIG_MAD16_DMA=1, CONFIG_MAD16_MPU_IRQ=9 - -Compaq Deskpro XL ------------------ - -The builtin sound hardware of Compaq Deskpro XL is now supported. -You need to configure the driver with MSS and OPL3 supports enabled. -In addition you need to manually edit linux/drivers/sound/local.h and -to add a line containing "#define DESKPROXL" if you used -make menuconfig/xconfig. - -Others? -------- - -Since there are so many different sound cards, it's likely that I have -forgotten to mention many of them. Please inform me if you know yet another -card which works with Linux, please inform me (or is anybody else -willing to maintain a database of supported cards (just like in XF86)?). - -Cards not supported yet -======================= - -Please check the version of sound driver you are using before -complaining that your card is not supported. It's possible you are -using a driver version which was released months before your card was -introduced. - -First of all, there is an easy way to make most sound cards work with Linux. -Just use the DOS based driver to initialize the card to a known state, then use -loadlin.exe to boot Linux. If Linux is configured to use the same I/O, IRQ and -DMA numbers as DOS, the card could work. -(ctrl-alt-del can be used in place of loadlin.exe but it doesn't work with -new motherboards). This method works also with all/most PnP sound cards. - -Don't get fooled with SB compatibility. Most cards are compatible with -SB but that may require a TSR which is not possible with Linux. If -the card is compatible with MSS, it's a better choice. Some cards -don't work in the SB and MSS modes at the same time. - -Then there are cards which are no longer manufactured and/or which -are relatively rarely used (such as the 8 bit ProAudioSpectrum -models). It's extremely unlikely that such cards ever get supported. -Adding support for a new card requires much work and increases time -required in maintaining the driver (some changes need to be done -to all low level drivers and be tested too, maybe with multiple -operating systems). For this reason I have made a decision to not support -obsolete cards. It's possible that someone else makes a separately -distributed driver (diffs) for the card. - -Writing a driver for a new card is not possible if there are no -programming information available about the card. If you don't -find your new card from this file, look from the home page -(http://www.opensound.com/ossfree). Then please contact -manufacturer of the card and ask if they have (or are willing to) -released technical details of the card. Do this before contacting me. I -can only answer 'no' if there are no programming information available. - -I have made decision to not accept code based on reverse engineering -to the driver. There are three main reasons: First I don't want to break -relationships to sound card manufacturers. The second reason is that -maintaining and supporting a driver without any specs will be a pain. -The third reason is that companies have freedom to refuse selling their -products to other than Windows users. - -Some companies don't give low level technical information about their -products to public or at least their require signing a NDA. It's not -possible to implement a freeware driver for them. However it's possible -that support for such cards become available in the commercial version -of this driver (see http://www.4Front-tech.com/oss.html for more info). - -There are some common audio chipsets that are not supported yet. For example -Sierra Aria and IBM Mwave. It's possible that these architectures -get some support in future but I can't make any promises. Just look -at the home page (http://www.opensound.com/ossfree/new_cards.html) -for latest info. - -Information about unsupported sound cards and chipsets is welcome as well -as free copies of sound cards, SDKs and operating systems. - -If you have any corrections and/or comments, please contact me. - -Hannu Savolainen -hannu@opensound.com - -Personal home page: http://www.compusonic.fi/~hannu -home page of OSS/Free: http://www.opensound.com/ossfree - -home page of commercial OSS -(Open Sound System) drivers: http://www.opensound.com/oss.html diff -Nru a/Documentation/sound/README.awe b/Documentation/sound/README.awe --- a/Documentation/sound/README.awe Thu Mar 7 18:17:45 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,218 +0,0 @@ -================================================================ - AWE32 Sound Driver for Linux / FreeBSD - version 0.4.3; Nov. 1, 1998 - - Takashi Iwai -================================================================ - -* GENERAL NOTES - -This is a sound driver extension for SoundBlaster AWE32 and other -compatible cards (AWE32-PnP, SB32, SB32-PnP, AWE64 & etc) to enable -the wave synth operations. The driver is provided for Linux 1.2.x -and 2.[012].x kernels, as well as FreeBSD, on Intel x86 and DEC -Alpha systems. - -This driver was written by Takashi Iwai , -and provided "as is". The original source (awedrv-0.4.3.tar.gz) and -binary packages are available on the following URL: - http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/ -Note that since the author is apart from this web site, the update is -not frequent now. - - -* NOTE TO LINUX USERS - -To enable this driver on linux-2.[01].x kernels, you need turn on -"AWE32 synth" options in sound menu when configure your linux kernel -and modules. The precise installation procedure is described in the -AWE64-Mini-HOWTO and linux-kernel/Documetation/sound/AWE32. - -If you're using PnP cards, the card must be initialized before loading -the sound driver. There're several options to do this: - - Initialize the card via ISA PnP tools, and load the sound module. - - Initialize the card on DOS, and load linux by loadlin.exe - - Use PnP kernel driver (for Linux-2.x.x) -The detailed instruction for the solution using isapnp tools is found -in many documents like above. A brief instruction is also included in -the installation document of this package. -For PnP driver project, please refer to the following URL: - http://www-jcr.lmh.ox.ac.uk/~pnp/ - - -* USING THE DRIVER - -The awedrv has several different playing modes to realize easy channel -allocation for MIDI songs. To hear the exact sound quality, you need -to obtain the extended sequencer program, drvmidi or playmidi-2.5. - -For playing MIDI files, you *MUST* load the soundfont file on the -driver previously by sfxload utility. Otherwise you'll here no sounds -at all! All the utilities and driver source packages are found in the -above URL. The sfxload program is included in the package -awesfx-0.4.3.tgz. Binary packages are available there, too. See the -instruction in each package for installation. - -Loading a soundfont file is very simple. Just execute the command - - % sfxload synthgm.sbk - -Then, sfxload transfers the file "synthgm.sbk" to the driver. -Both SF1 and SF2 formats are accepted. - -Now you can hear midi musics by a midi player. - - % drvmidi foo.mid - -If you run MIDI player after MOD player, you need to load soundfont -files again, since MOD player programs clear the previous loaded -samples by their own data. - -If you have only 512kb on the sound card, I recommend to use dynamic -sample loading via -L option of drvmidi. 2MB GM/GS soundfont file is -available in most midi files. - - % sfxload synthgm - % drvmidi -L 2mbgmgs foo.mid - -This makes a big difference (believe me)! For more details, please -refer to the FAQ list which is available on the URL above. - -The current chorus, reverb and equalizer status can be changed by -aweset utility program (included in awesfx package). Note that -some awedrv-native programs (like drvmidi and xmp) will change the -current settings by themselves. The aweset program is effective -only for other programs like playmidi. - -Enjoy. - - -* COMPILE FLAGS - -Compile conditions are defined in awe_config.h. - -[Compatibility Conditions] -The following flags are defined automatically when using installation -shell script. - -- AWE_MODULE_SUPPORT - indicates your Linux kernel supports module for each sound card - (in recent 2.1 or 2.2 kernels and unofficial patched 2.0 kernels - as distributed in the RH5.0 package). - This flag is automatically set when you're using 2.1.x kernels. - You can pass the base address and memory size via the following - module options, - io = base I/O port address (eg. 0x620) - memsize = DRAM size in kilobytes (eg. 512) - As default, AWE driver probes these values automatically. - - -[Hardware Conditions] -You DON'T have to define the following two values. -Define them only when the driver couldn't detect the card properly. - -- AWE_DEFAULT_BASE_ADDR (default: not defined) - specifies the base port address of your AWE32 card. - 0 means to autodetect the address. - -- AWE_DEFAULT_MEM_SIZE (default: not defined) - specifies the memory size of your AWE32 card in kilobytes. - -1 means to autodetect its size. - - -[Sample Table Size] -From ver.0.4.0, sample tables are allocated dynamically (except -Linux-1.2.x system), so you need NOT to touch these parameters. -Linux-1.2.x users may need to increase these values to appropriate size -if the sound card is equipped with more DRAM. - -- AWE_MAX_SF_LISTS, AWE_MAX_SAMPLES, AWE_MAX_INFOS - - -[Other Conditions] - -- AWE_ALWAYS_INIT_FM (default: not defined) - indicates the AWE driver always initialize FM passthrough even - without DRAM on board. Emu8000 chip has a restriction for playing - samples on DRAM that at least two channels must be occupied as - passthrough channels. - -- AWE_DEBUG_ON (default: defined) - turns on debugging messages if defined. - -- AWE_HAS_GUS_COMPATIBILITY (default: defined) - Enables GUS compatibility mode if defined, reading GUS patches and - GUS control commands. Define this option to use GMOD or other - GUS module players. - -- CONFIG_AWE32_MIDIEMU (default: defined) - Adds a MIDI emulation device by Emu8000 wavetable. The emulation - device can be accessed as an external MIDI, and sends the MIDI - control codes directly. XG and GS sysex/NRPN are accepted. - No MIDI input is supported. - -- CONFIG_AWE32_MIXER (default: not defined) - Adds a mixer device for AWE32 bass/treble equalizer control. - You can access this device using /dev/mixer?? (usually mixer01). - -- AWE_USE_NEW_VOLUME_CALC (default: defined) - Use the new method to calculate the volume change as compatible - with DOS/Win drivers. This option can be toggled via aweset - program, or drvmidi player. - -- AWE_CHECK_VTARGET (default: defined) - Check the current volume target value when searching for an - empty channel to allocate a new voice. This is experimentally - implemented in this version. (probably, this option doesn't - affect the sound quality severely...) - -- AWE_ALLOW_SAMPLE_SHARING (default: defined) - Allow sample sharing for differently loaded patches. - This function is available only together with awesfx-0.4.3p3. - Note that this is still an experimental option. - -- DEF_FM_CHORUS_DEPTH (default: 0x10) - The default strength to be sent to the chorus effect engine. - From 0 to 0xff. Larger numbers may often cause weird sounds. - -- DEF_FM_REVERB_DEPTH (default: 0x10) - The default strength to be sent to the reverb effect engine. - From 0 to 0xff. Larger numbers may often cause weird sounds. - - -* ACKNOWLEDGMENTS - -Thanks to Witold Jachimczyk (witek@xfactor.wpi.edu) for much advice -on programming of AWE32. Much code is brought from his AWE32-native -MOD player, ALMP. -The port of awedrv to FreeBSD is done by Randall Hopper -(rhh@ct.picker.com). -The new volume calculation routine was derived from Mark Weaver's -ADIP compatible routines. -I also thank linux-awe-ml members for their efforts -to reboot their system many times :-) - - -* TODO'S - -- Complete DOS/Win compatibility -- DSP-like output - - -* COPYRIGHT - -Copyright (C) 1996-1998 Takashi Iwai - -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. diff -Nru a/Documentation/sound/README.modules b/Documentation/sound/README.modules --- a/Documentation/sound/README.modules Thu Mar 7 18:17:42 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,105 +0,0 @@ -Building a modular sound driver -================================ - - The following information is current as of linux-2.1.85. Check the other -readme files, especially README.OSS, for information not specific to -making sound modular. - - First, configure your kernel. This is an idea of what you should be -setting in the sound section: - - Sound card support - - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support - - I have SoundBlaster. Select your card from the list. - - Generic OPL2/OPL3 FM synthesizer support - FM synthesizer (YM3812/OPL-3) support - - If you don't set these, you will probably find you can play .wav files -but not .midi. As the help for them says, set them unless you know your -card does not use one of these chips for FM support. - - Once you are configured, make zlilo, modules, modules_install; reboot. -Note that it is no longer necessary or possible to configure sound in the -drivers/sound dir. Now one simply configures and makes one's kernel and -modules in the usual way. - - Then, add to your /etc/modules.conf something like: - -alias char-major-14 sb -post-install sb /sbin/modprobe "-k" "adlib_card" -options sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330 -options adlib_card io=0x388 # FM synthesizer - - Alternatively, if you have compiled in kernel level ISAPnP support: - -alias char-major-14 sb -post-install sb /sbin/modprobe "-k" "adlib_card" -options adlib_card io=0x388 - - The effect of this is that the sound driver and all necessary bits and -pieces autoload on demand, assuming you use kerneld (a sound choice) and -autoclean when not in use. Also, options for the device drivers are -set. They will not work without them. Change as appropriate for your card. -If you are not yet using the very cool kerneld, you will have to "modprobe --k sb" yourself to get things going. Eventually things may be fixed so -that this kludgery is not necessary; for the time being, it seems to work -well. - - Replace 'sb' with the driver for your card, and give it the right -options. To find the filename of the driver, look in -/lib/modules//misc. Mine looks like: - -adlib_card.o # This is the generic OPLx driver -opl3.o # The OPL3 driver -sb.o # <> -sound.o # The sound driver -uart401.o # Used by sb, maybe other cards - - Whichever card you have, try feeding it the options that would be the -default if you were making the driver wired, not as modules. You can look -at the init_module() code for the card to see what args are expected. - - Note that at present there is no way to configure the io, irq and other -parameters for the modular drivers as one does for the wired drivers.. One -needs to pass the modules the necessary parameters as arguments, either -with /etc/modules.conf or with command-line args to modprobe, e.g. - -modprobe -k sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330 -modprobe -k adlib_card io=0x388 - - recommend using /etc/modules.conf. - -Persistent DMA Buffers: - -The sound modules normally allocate DMA buffers during open() and -deallocate them during close(). Linux can often have problems allocating -DMA buffers for ISA cards on machines with more than 16MB RAM. This is -because ISA DMA buffers must exist below the 16MB boundary and it is quite -possible that we can't find a large enough free block in this region after -the machine has been running for any amount of time. The way to avoid this -problem is to allocate the DMA buffers during module load and deallocate -them when the module is unloaded. For this to be effective we need to load -the sound modules right after the kernel boots, either manually or by an -init script, and keep them around until we shut down. This is a little -wasteful of RAM, but it guarantees that sound always works. - -To make the sound driver use persistent DMA buffers we need to pass the -sound.o module a "dmabuf=1" command-line argument. This is normally done -in /etc/modules.conf like so: - -options sound dmabuf=1 - -If you have 16MB or less RAM or a PCI sound card, this is wasteful and -unnecessary. It is possible that machine with 16MB or less RAM will find -this option useful, but if your machine is so memory-starved that it -cannot find a 64K block free, you will be wasting even more RAM by keeping -the sound modules loaded and the DMA buffers allocated when they are not -needed. The proper solution is to upgrade your RAM. But you do also have -this improper solution as well. Use it wisely. - - I'm afraid I know nothing about anything but my setup, being more of a -text-mode guy anyway. If you have options for other cards or other helpful -hints, send them to me, Jim Bray, jb@as220.org, http://as220.org/jb. diff -Nru a/Documentation/sound/README.ymfsb b/Documentation/sound/README.ymfsb --- a/Documentation/sound/README.ymfsb Thu Mar 7 18:17:43 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,107 +0,0 @@ -Legacy audio driver for YMF7xx PCI cards. - - -FIRST OF ALL -============ - - This code references YAMAHA's sample codes and data sheets. - I respect and thank for all people they made open the informations - about YMF7xx cards. - - And this codes heavily based on Jeff Garzik 's - old VIA 82Cxxx driver (via82cxxx.c). I also respect him. - - -DISCLIMER -========= - - This driver is currently at early ALPHA stage. It may cause serious - damage to your computer when used. - PLEASE USE IT AT YOUR OWN RISK. - - -ABOUT THIS DRIVER -================= - - This code enables you to use your YMF724[A-F], YMF740[A-C], YMF744, YMF754 - cards. When enabled, your card acts as "SoundBlaster Pro" compatible card. - It can only play 22.05kHz / 8bit / Stereo samples, control external MIDI - port. - If you want to use your card as recent "16-bit" card, you should use - Alsa or OSS/Linux driver. Of course you can write native PCI driver for - your cards :) - - -USAGE -===== - - # modprobe ymfsb (options) - - -OPTIONS FOR MODULE -================== - - io : SB base address (0x220, 0x240, 0x260, 0x280) - synth_io : OPL3 base address (0x388, 0x398, 0x3a0, 0x3a8) - dma : DMA number (0,1,3) - master_volume: AC'97 PCM out Vol (0-100) - spdif_out : SPDIF-out flag (0:disable 1:enable) - - These options will change in future... - - -FREQUENCY -========= - - When playing sounds via this driver, you will hear its pitch is slightly - lower than original sounds. Since this driver recognizes your card acts - with 21.739kHz sample rates rather than 22.050kHz (I think it must be - hardware restriction). So many players become tone deafness. - To prevent this, you should express some options to your sound player - that specify correct sample frequency. For example, to play your MP3 file - correctly with mpg123, specify the frequency like following: - - % mpg123 -r 21739 foo.mp3 - - -SPDIF OUT -========= - - With installing modules with option 'spdif_out=1', you can enjoy your - sounds from SPDIF-out of your card (if it had). - Its Fs is fixed to 48kHz (It never means the sample frequency become - up to 48kHz. All sounds via SPDIF-out also 22kHz samples). So your - digital-in capable components has to be able to handle 48kHz Fs. - - -COPYING -======= - - 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. - - -TODO -==== - * support for multiple cards - (set the different SB_IO,MPU_IO,OPL_IO for each cards) - - * support for OPL (dmfm) : There will be no requirements... :-< - - -AUTHOR -====== - - Daisuke Nagano - diff -Nru a/Documentation/sound/SoundPro b/Documentation/sound/SoundPro --- a/Documentation/sound/SoundPro Thu Mar 7 18:17:44 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,105 +0,0 @@ -Documentation for the SoundPro CMI8330 extensions in the WSS driver (ad1848.o) ------------------------------------------------------------------------------- - -( Be sure to read Documentation/sound/CMI8330 too ) - -Ion Badulescu, ionut@cs.columbia.edu -February 24, 1999 - -(derived from the OPL3-SA2 documentation by Scott Murray) - -The SoundPro CMI8330 (ISA) is a chip usually found on some Taiwanese -motherboards. The official name in the documentation is CMI8330, SoundPro -is the nickname and the big inscription on the chip itself. - -The chip emulates a WSS as well as a SB16, but it has certain differences -in the mixer section which require separate support. It also emulates an -MPU401 and an OPL3 synthesizer, so you probably want to enable support -for these, too. - -The chip identifies itself as an AD1848, but its mixer is significantly -more advanced than the original AD1848 one. If your system works with -either WSS or SB16 and you are having problems with some mixer controls -(no CD audio, no line-in, etc), you might want to give this driver a try. -Detection should work, but it hasn't been widely tested, so it might still -mis-identify the chip. You can still force soundpro=1 in the modprobe -parameters for ad1848. Please let me know if it happens to you, so I can -adjust the detection routine. - -The chip is capable of doing full-duplex, but since the driver sees it as an -AD1848, it cannot take advantage of this. Moreover, the full-duplex mode is -not achievable through the WSS interface, b/c it needs a dma16 line which is -assigned only to the SB16 subdevice (with isapnp). Windows documentation -says the user must use WSS Playback and SB16 Recording for full-duplex, so -it might be possible to do the same thing under Linux. You can try loading -up both ad1848 and sb then use one for playback and the other for -recording. I don't know if this works, b/c I haven't tested it. Anyway, if -you try it, be very careful: the SB16 mixer *mostly* works, but certain -settings can have unexpected effects. Use the WSS mixer for best results. - -There is also a PCI SoundPro chip. I have not seen this chip, so I have -no idea if the driver will work with it. I suspect it won't. - -As with PnP cards, some configuration is required. There are two ways -of doing this. The most common is to use the isapnptools package to -initialize the card, and use the kernel module form of the sound -subsystem and sound drivers. Alternatively, some BIOS's allow manual -configuration of installed PnP devices in a BIOS menu, which should -allow using the non-modular sound drivers, i.e. built into the kernel. -Since in this latter case you cannot use module parameters, you will -have to enable support for the SoundPro at compile time. - -The IRQ and DMA values can be any that are considered acceptable for a -WSS. Assuming you've got isapnp all happy, then you should be able to -do something like the following (which *must* match the isapnp/BIOS -configuration): - -modprobe ad1848 io=0x530 irq=11 dma=0 soundpro=1 --and maybe- -modprobe sb io=0x220 irq=5 dma=1 dma16=5 - --then- -modprobe mpu401 io=0x330 irq=9 -modprobe opl3 io=0x388 - -If all goes well and you see no error messages, you should be able to -start using the sound capabilities of your system. If you get an -error message while trying to insert the module(s), then make -sure that the values of the various arguments match what you specified -in your isapnp configuration file, and that there is no conflict with -another device for an I/O port or interrupt. Checking the contents of -/proc/ioports and /proc/interrupts can be useful to see if you're -butting heads with another device. - -If you do not see the chipset version message, and none of the other -messages present in the system log are helpful, try adding 'debug=1' -to the ad1848 parameters, email me the syslog results and I'll do -my best to help. - -Lastly, if you're using modules and want to set up automatic module -loading with kmod, the kernel module loader, here is the section I -currently use in my conf.modules file: - -# Sound -post-install sound modprobe -k ad1848; modprobe -k mpu401; modprobe -k opl3 -options ad1848 io=0x530 irq=11 dma=0 -options sb io=0x220 irq=5 dma=1 dma16=5 -options mpu401 io=0x330 irq=9 -options opl3 io=0x388 - -The above ensures that ad1848 will be loaded whenever the sound system -is being used. - -Good luck. - -Ion - -NOT REALLY TESTED: -- recording -- recording device selection -- full-duplex - -TODO: -- implement mixer support for surround, loud, digital CD switches. -- come up with a scheme which allows recording volumes for each subdevice. -This is a major OSS API change. diff -Nru a/Documentation/sound/Soundblaster b/Documentation/sound/Soundblaster --- a/Documentation/sound/Soundblaster Thu Mar 7 18:17:46 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,53 +0,0 @@ -modprobe sound -insmod uart401 -insmod sb ... - -This loads the driver for the Sound Blaster and assorted clones. Cards that -are covered by other drivers should not be using this driver. - -The Sound Blaster module takes the following arguments - -io I/O address of the Sound Blaster chip (0x220,0x240,0x260,0x280) -irq IRQ of the Sound Blaster chip (5,7,9,10) -dma 8-bit DMA channel for the Sound Blaster (0,1,3) -dma16 16-bit DMA channel for SB16 and equivalent cards (5,6,7) -mpu_io I/O for MPU chip if present (0x300,0x330) - -sm_games=1 Set if you have a Logitech soundman games -acer=1 Set this to detect cards in some ACER notebooks -mwave_bug=1 Set if you are trying to use this driver with mwave (see on) -type Use this to specify a specific card type - -The following arguments are taken if ISAPnP support is compiled in - -isapnp=0 Set this to disable ISAPnP detection (use io=0xXXX etc. above) -multiple=0 Set to disable detection of multiple Soundblaster cards. - Consider it a bug if this option is needed, and send in a - report. -pnplegacy=1 Set this to be able to use a PnP card(s) along with a single - non-PnP (legacy) card. Above options for io, irq, etc. are - needed, and will apply only to the legacy card. -reverse=1 Reverses the order of the search in the PnP table. -uart401=1 Set to enable detection of mpu devices on some clones. -isapnpjump=n Jumps to slot n in the driver's PnP table. Use the source, - Luke. - -You may well want to load the opl3 driver for synth music on most SB and -clone SB devices - -insmod opl3 io=0x388 - -Using Mwave - -To make this driver work with Mwave you must set mwave_bug. You also need -to warm boot from DOS/Windows with the required firmware loaded under this -OS. IBM are being difficult about documenting how to load this firmware. - -Avance Logic ALS007 - -This card is supported; see the separate file ALS007 for full details. - -Avance Logic ALS100 - -This card is supported; setup should be as for a standard Sound Blaster 16. -The driver will identify the audio device as a "Sound Blaster 16 (ALS-100)". diff -Nru a/Documentation/sound/Tropez+ b/Documentation/sound/Tropez+ --- a/Documentation/sound/Tropez+ Thu Mar 7 18:17:45 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,26 +0,0 @@ -From: Paul Barton-Davis - -Here is the configuration I use with a Tropez+ and my modular -driver: - - alias char-major-14 wavefront - alias synth0 wavefront - alias mixer0 cs4232 - alias audio0 cs4232 - pre-install wavefront modprobe "-k" "cs4232" - post-install wavefront modprobe "-k" "opl3" - options wavefront io=0x200 irq=9 - options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 - options opl3 io=0x388 - -Things to note: - - the wavefront options "io" and "irq" ***MUST*** match the "synthio" - and "synthirq" cs4232 options. - - you can do without the opl3 module if you don't - want to use the OPL/[34] synth on the soundcard - - the opl3 io parameter is conventionally not adjustable. - -Please see drivers/sound/README.wavefront for more details. diff -Nru a/Documentation/sound/VIA-chipset b/Documentation/sound/VIA-chipset --- a/Documentation/sound/VIA-chipset Thu Mar 7 18:17:45 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,43 +0,0 @@ -Running sound cards on VIA chipsets - -o There are problems with VIA chipsets and sound cards that appear to - lock the hardware solidly. Test programs under DOS have verified the - problem exists on at least some (but apparently not all) VIA boards - -o VIA have so far failed to bother to answer support mail on the subject - so if you are a VIA engineer feeling aggrieved as you read this - document go chase your own people. If there is a workaround please - let us know so we can implement it. - - -Certain patterns of ISA DMA access used for most PC sound cards cause the -VIA chipsets to lock up. From the collected reports this appears to cover a -wide range of boards. Some also lock up with sound cards under Win* as well. - -Linux implements a workaround providing your chipset is PCI and you compiled -with PCI Quirks enabled. If so you will see a message - "Activating ISA DMA bug workarounds" - -during booting. If you have a VIA PCI chipset that hangs when you use the -sound and is not generating this message even with PCI quirks enabled -please report the information to the linux-kernel list (see REPORTING-BUGS). - -If you are one of the tiny number of unfortunates with a 486 ISA/VLB VIA -chipset board you need to do the following to build a special kernel for -your board - - edit linux/include/asm-i386/dma.h - -change - -#define isa_dma_bridge_buggy (0) - -to - -#define isa_dma_bridge_buggy (1) - -and rebuild a kernel without PCI quirk support. - - -Other than this particular glitch the VIA [M]VP* chipsets appear to work -perfectly with Linux. diff -Nru a/Documentation/sound/VIBRA16 b/Documentation/sound/VIBRA16 --- a/Documentation/sound/VIBRA16 Thu Mar 7 18:17:39 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,80 +0,0 @@ -Sound Blaster 16X Vibra addendum --------------------------------- -by Marius Ilioaea - Stefan Laudat - -Sat Mar 6 23:55:27 EET 1999 - - Hello again, - - Playing with a SB Vibra 16x soundcard we found it very difficult -to setup because the kernel reported a lot of DMA errors and wouldn't -simply play any sound. - A good starting point is that the vibra16x chip full-duplex facility -is neither still exploited by the sb driver found in the linux kernel -(tried it with a 2.2.2-ac7), nor in the commercial OSS package (it reports -it as half-duplex soundcard). Oh, I almost forgot, the RedHat sndconfig -failed detecting it ;) - So, the big problem still remains, because the sb module wants a -8-bit and a 16-bit dma, which we could not allocate for vibra... it supports -only two 8-bit dma channels, the second one will be passed to the module -as a 16 bit channel, the kernel will yield about that but everything will -be okay, trust us. - The only inconvenient you may find is that you will have -some sound playing jitters if you have HDD dma support enabled - but this -will happen with almost all soundcards... - - A fully working isapnp.conf is just here: - - - -(READPORT 0x0203) -(ISOLATE PRESERVE) -(IDENTIFY *) -(VERBOSITY 2) -(CONFLICT (IO FATAL)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # or WARNING -# SB 16 and OPL3 devices -(CONFIGURE CTL00f0/-1 (LD 0 -(INT 0 (IRQ 5 (MODE +E))) -(DMA 0 (CHANNEL 1)) -(DMA 1 (CHANNEL 3)) -(IO 0 (SIZE 16) (BASE 0x0220)) -(IO 2 (SIZE 4) (BASE 0x0388)) -(NAME "CTL00f0/-1[0]{Audio }") -(ACT Y) -)) - -# Joystick device - only if you need it :-/ - -(CONFIGURE CTL00f0/-1 (LD 1 -(IO 0 (SIZE 1) (BASE 0x0200)) -(NAME "CTL00f0/-1[1]{Game }") -(ACT Y) -)) -(WAITFORKEY) - - - - So, after a good kernel modules compilation and a 'depmod -a kernel_ver' -you may want to: - -modprobe sb io=0x220 irq=5 dma=1 dma16=3 - - Or, take the hard way: - -insmod souncore -insmod sound -insmod uart401 -insmod sb io=0x220 irq=5 dma=1 dma16=3 -# do you need MIDI? -insmod opl3=0x388 - - Just in case, the kernel sound support should be: - -CONFIG_SOUND=m -CONFIG_SOUND_OSS=m -CONFIG_SOUND_SB=m - - Enjoy your new noisy Linux box! ;) - - diff -Nru a/Documentation/sound/WaveArtist b/Documentation/sound/WaveArtist --- a/Documentation/sound/WaveArtist Thu Mar 7 18:17:42 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,170 +0,0 @@ - - (the following is from the armlinux CVS) - - WaveArtist mixer and volume levels can be accessed via these commands: - - nn30 read registers nn, where nn = 00 - 09 for mixer settings - 0a - 13 for channel volumes - mm31 write the volume setting in pairs, where mm = (nn - 10) / 2 - rr32 write the mixer settings in pairs, where rr = nn/2 - xx33 reset all settings to default - 0y34 select mono source, y=0 = left, y=1 = right - - bits - nn 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 00 | 0 | 0 0 1 1 | left line mixer gain | left aux1 mixer gain |lmute| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 01 | 0 | 0 1 0 1 | left aux2 mixer gain | right 2 left mic gain |mmute| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 02 | 0 | 0 1 1 1 | left mic mixer gain | left mic | left mixer gain |dith | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 03 | 0 | 1 0 0 1 | left mixer input select |lrfg | left ADC gain | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 04 | 0 | 1 0 1 1 | right line mixer gain | right aux1 mixer gain |rmute| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 05 | 0 | 1 1 0 1 | right aux2 mixer gain | left 2 right mic gain |test | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 06 | 0 | 1 1 1 1 | right mic mixer gain | right mic |right mixer gain |rbyps| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 07 | 1 | 0 0 0 1 | right mixer select |rrfg | right ADC gain | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 08 | 1 | 0 0 1 1 | mono mixer gain |right ADC mux sel|left ADC mux sel | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 09 | 1 | 0 1 0 1 |loopb|left linout|loop|ADCch|TxFch|OffCD|test |loopb|loopb|osamp| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0a | 0 | left PCM channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0b | 0 | right PCM channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0c | 0 | left FM channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0d | 0 | right FM channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0e | 0 | left wavetable channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0f | 0 | right wavetable channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 10 | 0 | left PCM expansion channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 11 | 0 | right PCM expansion channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 12 | 0 | left FM expansion channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 13 | 0 | right FM expansion channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - - lmute: left mute - mmute: mono mute - dith: dithds - lrfg: - rmute: right mute - rbyps: right bypass - rrfg: - ADCch: - TxFch: - OffCD: - osamp: - - And the following diagram is derived from the description in the CVS archive: - - MIC L (mouthpiece) - +------+ - -->PreAmp>-\ - +--^---+ | - | | - r2b4-5 | +--------+ - /----*-------------------------------->5 | - | | | - | /----------------------------------->4 | - | | | | - | | /--------------------------------->3 1of5 | +---+ - | | | | mux >-->AMP>--> ADC L - | | | /------------------------------->2 | +-^-+ - | | | | | | | - Line | | | | +----+ +------+ +---+ /---->1 | r3b3-0 - ------------*->mute>--> Gain >--> | | | | - L | | | +----+ +------+ | | | *->0 | - | | | | | | +---^----+ - Aux2 | | | +----+ +------+ | | | | - ----------*--->mute>--> Gain >--> M | | r8b0-2 - L | | +----+ +------+ | | | - | | | | \------\ - Aux1 | | +----+ +------+ | | | - --------*----->mute>--> Gain >--> I | | - L | +----+ +------+ | | | - | | | | - | +----+ +------+ | | +---+ | - *------->mute>--> Gain >--> X >-->AMP>--* - | +----+ +------+ | | +-^-+ | - | | | | | - | +----+ +------+ | | r2b1-3 | - | /----->mute>--> Gain >--> E | | - | | +----+ +------+ | | | - | | | | | - | | +----+ +------+ | | | - | | /--->mute>--> Gain >--> R | | - | | | +----+ +------+ | | | - | | | | | | r9b8-9 - | | | +----+ +------+ | | | | - | | | /->mute>--> Gain >--> | | +---v---+ - | | | | +----+ +------+ +---+ /-*->0 | - DAC | | | | | | | - ------------*----------------------------------->? | +----+ - L | | | | | Mux >-->mute>--> L output - | | | | /->? | +--^-+ - | | | | | | | | - | | | /--------->? | r0b0 - | | | | | | +-------+ - | | | | | | - Mono | | | | | | +-------+ - ----------* | \---> | +----+ - | | | | | | Mix >-->mute>--> Mono output - | | | | *-> | +--^-+ - | | | | | +-------+ | - | | | | | r1b0 - DAC | | | | | +-------+ - ------------*-------------------------*--------->1 | +----+ - R | | | | | | Mux >-->mute>--> R output - | | | | +----+ +------+ +---+ *->0 | +--^-+ - | | | \->mute>--> Gain >--> | | +---^---+ | - | | | +----+ +------+ | | | | r5b0 - | | | | | | r6b0 - | | | +----+ +------+ | | | - | | \--->mute>--> Gain >--> M | | - | | +----+ +------+ | | | - | | | | | - | | +----+ +------+ | | | - | *----->mute>--> Gain >--> I | | - | | +----+ +------+ | | | - | | | | | - | | +----+ +------+ | | +---+ | - \------->mute>--> Gain >--> X >-->AMP>--* - | +----+ +------+ | | +-^-+ | - /--/ | | | | - Aux1 | +----+ +------+ | | r6b1-3 | - -------*------>mute>--> Gain >--> E | | - R | | +----+ +------+ | | | - | | | | | - Aux2 | | +----+ +------+ | | /------/ - ---------*---->mute>--> Gain >--> R | | - R | | | +----+ +------+ | | | - | | | | | | +--------+ - Line | | | +----+ +------+ | | | *->0 | - -----------*-->mute>--> Gain >--> | | | | - R | | | | +----+ +------+ +---+ \---->1 | - | | | | | | - | | | \-------------------------------->2 | +---+ - | | | | Mux >-->AMP>--> ADC R - | | \---------------------------------->3 | +-^-+ - | | | | | - | \------------------------------------>4 | r7b3-0 - | | | - \-----*-------------------------------->5 | - | +---^----+ - r6b4-5 | | - | | r8b3-5 - +--v---+ | - -->PreAmp>-/ - +------+ - MIC R (electret mic) diff -Nru a/Documentation/sound/Wavefront b/Documentation/sound/Wavefront --- a/Documentation/sound/Wavefront Thu Mar 7 18:17:38 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,341 +0,0 @@ - An OSS/Free Driver for WaveFront soundcards - (Turtle Beach Maui, Tropez, Tropez Plus) - - Paul Barton-Davis, July 1998 - - VERSION 0.2.5 - -Driver Status -------------- - -Requires: Kernel 2.1.106 or later (the driver is included with kernels -2.1.109 and above) - -As of 7/22/1998, this driver is currently in *BETA* state. This means -that it compiles and runs, and that I use it on my system (Linux -2.1.106) with some reasonably demanding applications and uses. I -believe the code is approaching an initial "finished" state that -provides bug-free support for the Tropez Plus. - -Please note that to date, the driver has ONLY been tested on a Tropez -Plus. I would very much like to hear (and help out) people with Tropez -and Maui cards, since I think the driver can support those cards as -well. - -Finally, the driver has not been tested (or even compiled) as a static -(non-modular) part of the kernel. Alan Cox's good work in modularizing -OSS/Free for Linux makes this rather unnecessary. - -Some Questions --------------- - -********************************************************************** -0) What does this driver do that the maui driver did not ? -********************************************************************** - -* can fully initialize a WaveFront card from cold boot - no DOS - utilities needed -* working patch/sample/program loading and unloading (the maui - driver didn't document how to make this work, and assumed - user-level preparation of the patch data for writing - to the board. ick.) -* full user-level access to all WaveFront commands -* for the Tropez Plus, (primitive) control of the YSS225 FX processor -* Virtual MIDI mode supported - 2 MIDI devices accessible via the - WaveFront's MPU401/UART emulation. One - accesses the WaveFront synth, the other accesses the - external MIDI connector. Full MIDI read/write semantics - for both devices. -* OSS-compliant /dev/sequencer interface for the WaveFront synth, - including native and GUS-format patch downloading. -* semi-intelligent patch management (prototypical at this point) - -********************************************************************** -1) What to do about MIDI interfaces ? -********************************************************************** - -The Tropez Plus (and perhaps other WF cards) can in theory support up -to 2 physical MIDI interfaces. One of these is connected to the -ICS2115 chip (the WaveFront synth itself) and is controlled by -MPU/UART-401 emulation code running as part of the WaveFront OS. The -other is controlled by the CS4232 chip present on the board. However, -physical access to the CS4232 connector is difficult, and it is -unlikely (though not impossible) that you will want to use it. - -An older version of this driver introduced an additional kernel config -variable which controlled whether or not the CS4232 MIDI interface was -configured. Because of Alan Cox's work on modularizing the sound -drivers, and now backporting them to 2.0.34 kernels, there seems to be -little reason to support "static" configuration variables, and so this -has been abandoned in favor of *only* module parameters. Specifying -"mpuio" and "mpuirq" for the cs4232 parameter will result in the -CS4232 MIDI interface being configured; leaving them unspecified will -leave it unconfigured (and thus unusable). - -BTW, I have heard from one Tropez+ user that the CS4232 interface is -more reliable than the ICS2115 one. I have had no problems with the -latter, and I don't have the right cable to test the former one -out. Reports welcome. - -********************************************************************** -2) Why does line XXX of the code look like this .... ? -********************************************************************** - -Either because its not finished yet, or because you're a better coder -than I am, or because you don't understand some aspect of how the card -or the code works. - -I absolutely welcome comments, criticisms and suggestions about the -design and implementation of the driver. - -********************************************************************** -3) What files are included ? -********************************************************************** - - drivers/sound/README.wavefront -- this file - - drivers/sound/wavefront.patch -- patches for the 2.1.106 sound drivers - needed to make the rest of this work - DO NOT USE IF YOU'VE APPLIED THEM - BEFORE, OR HAVE 2.1.109 OR ABOVE - - drivers/sound/wavfront.c -- the driver - drivers/sound/ys225.h -- data declarations for FX config - drivers/sound/ys225.c -- data definitions for FX config - drivers/sound/wf_midi.c -- the "uart401" driver - to support virtual MIDI mode. - include/wavefront.h -- the header file - Documentation/sound/Tropez+ -- short docs on configuration - -********************************************************************** -4) How do I compile/install/use it ? -********************************************************************** - -PART ONE: install the source code into your sound driver directory - - cd - tar -zxvf - -PART TWO: apply the patches - - DO THIS ONLY IF YOU HAVE A KERNEL VERSION BELOW 2.1.109 - AND HAVE NOT ALREADY INSTALLED THE PATCH(ES). - - cd drivers/sound - patch < wavefront.patch - -PART THREE: configure your kernel - - cd - make xconfig (or whichever config option you use) - - - choose YES for Sound Support - - choose MODULE (M) for OSS Sound Modules - - choose MODULE(M) to YM3812/OPL3 support - - choose MODULE(M) for WaveFront support - - choose MODULE(M) for CS4232 support - - - choose "N" for everything else (unless you have other - soundcards you want support for) - - - make dep - make boot - . - . - . - - make modules - . - . - . - make modules_install - -Here's my autoconf.h SOUND section: - -/* - * Sound - */ -#define CONFIG_SOUND 1 -#undef CONFIG_SOUND_OSS -#define CONFIG_SOUND_OSS_MODULE 1 -#undef CONFIG_SOUND_PAS -#undef CONFIG_SOUND_SB -#undef CONFIG_SOUND_ADLIB -#undef CONFIG_SOUND_GUS -#undef CONFIG_SOUND_MPU401 -#undef CONFIG_SOUND_PSS -#undef CONFIG_SOUND_MSS -#undef CONFIG_SOUND_SSCAPE -#undef CONFIG_SOUND_TRIX -#undef CONFIG_SOUND_MAD16 -#undef CONFIG_SOUND_WAVEFRONT -#define CONFIG_SOUND_WAVEFRONT_MODULE 1 -#undef CONFIG_SOUND_CS4232 -#define CONFIG_SOUND_CS4232_MODULE 1 -#undef CONFIG_SOUND_MAUI -#undef CONFIG_SOUND_SGALAXY -#undef CONFIG_SOUND_OPL3SA1 -#undef CONFIG_SOUND_SOFTOSS -#undef CONFIG_SOUND_YM3812 -#define CONFIG_SOUND_YM3812_MODULE 1 -#undef CONFIG_SOUND_VMIDI -#undef CONFIG_SOUND_UART6850 -/* - * Additional low level sound drivers - */ -#undef CONFIG_LOWLEVEL_SOUND - -************************************************************ -6) How do I configure my card ? -************************************************************ - -You need to edit /etc/modules.conf. Here's mine (edited to show the -relevant details): - - # Sound system - alias char-major-14 wavefront - alias synth0 wavefront - alias mixer0 cs4232 - alias audio0 cs4232 - pre-install wavefront modprobe "-k" "cs4232" - post-install wavefront modprobe "-k" "opl3" - options wavefront io=0x200 irq=9 - options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 - options opl3 io=0x388 - -Things to note: - - the wavefront options "io" and "irq" ***MUST*** match the "synthio" - and "synthirq" cs4232 options. - - you can do without the opl3 module if you don't - want to use the OPL/[34] FM synth on the soundcard - - the opl3 io parameter is conventionally not adjustable. - In theory, any not-in-use IO port address would work, but - just use 0x388 and stick with the crowd. - -********************************************************************** -7) What about firmware ? -********************************************************************** - -Turtle Beach have not given me permission to distribute their firmware -for the ICS2115. However, if you have a WaveFront card, then you -almost certainly have the firmware, and if not, its freely available -on their website, at: - - http://www.tbeach.com/tbs/downloads/scardsdown.htm#tropezplus - -The file is called WFOS2001.MOT (for the Tropez+). - -This driver, however, doesn't use the pure firmware as distributed, -but instead relies on a somewhat processed form of it. You can -generate this very easily. Following an idea from Andrew Veliath's -Pinnacle driver, the following flex program will generate the -processed version: - ----- cut here ------------------------- -%option main -%% -^S[28].*\r$ printf ("%c%.*s", yyleng-1,yyleng-1,yytext); -<> { fputc ('\0', stdout); return; } -\n {} -. {} ----- cut here ------------------------- - -To use it, put the above in file (say, ws.l) compile it like this: - - shell> flex -ows.c ws.l - shell> cc -o ws ws.c - -and then use it like this: - - ws < my-copy-of-the-oswf.mot-file > /etc/sound/wavefront.os - -If you put it somewhere else, you'll always have to use the wf_ospath -module parameter (see below) or alter the source code. - -********************************************************************** -7) How do I get it working ? -********************************************************************** - -Optionally, you can reboot with the "new" kernel (even though the only -changes have really been made to a module). - -Then, as root do: - - modprobe wavefront - -You should get something like this in /var/log/messages: - - WaveFront: firmware 1.20 already loaded. - -or - - WaveFront: no response to firmware probe, assume raw. - -then: - - WaveFront: waiting for memory configuration ... - WaveFront: hardware version 1.64 - WaveFront: available DRAM 8191k - WaveFront: 332 samples used (266 real, 13 aliases, 53 multi), 180 empty - WaveFront: 128 programs slots in use - WaveFront: 256 patch slots filled, 142 in use - -The whole process takes about 16 seconds, the longest waits being -after reporting the hardware version (during the firmware download), -and after reporting program status (during patch status inquiry). Its -shorter (about 10 secs) if the firmware is already loaded (i.e. only -warm reboots since the last firmware load). - -The "available DRAM" line will vary depending on how much added RAM -your card has. Mine has 8MB. - -To check basically functionality, use play(1) or splay(1) to send a -.WAV or other audio file through the audio portion. Then use playmidi -to play a General MIDI file. Try the "-D 0" to hear the -difference between sending MIDI to the WaveFront and using the OPL/3, -which is the default (I think ...). If you have an external synth(s) -hooked to the soundcard, you can use "-e" to route to the -external synth(s) (in theory, -D 1 should work as well, but I think -there is a bug in playmidi which prevents this from doing what it -should). - -********************************************************************** -8) What are the module parameters ? -********************************************************************** - -Its best to read wavefront.c for this, but here is a summary: - -integers: - wf_raw - if set, ignore apparent presence of firmware - loaded onto the ICS2115, reset the whole - board, and initialize it from scratch. (default = 0) - - fx_raw - if set, always initialize the YSS225 processor - on the Tropez plus. (default = 1) - - < The next 4 are basically for kernel hackers to allow - tweaking the driver for testing purposes. > - - wait_usecs - loop timer used when waiting for - status conditions on the board. - The default is 150. - - debug_default - debugging flags. See sound/wavefront.h - for WF_DEBUG_* values. Default is zero. - Setting this allows you to debug the - driver during module installation. -strings: - ospath - path to get to the pre-processed OS firmware. - (default: /etc/sound/wavefront.os) - -********************************************************************** -9) Who should I contact if I have problems? -********************************************************************** - -Just me: Paul Barton-Davis - - diff -Nru a/Documentation/sound/alsa/CMIPCI.txt b/Documentation/sound/alsa/CMIPCI.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/alsa/CMIPCI.txt Thu Mar 7 18:17:46 2002 @@ -0,0 +1,228 @@ + Brief Notes on C-Media 8738/8338 Driver + ======================================= + + Takashi Iwai + + +Front/Rear Multi-channel Playback +--------------------------------- + +CM8x38 chip can use ADC as the second DAC so that two different stereo +channels can be used for front/rear playbacks. Since there are two +DACs, both streams are handled independently unlike the 4/6ch multi- +channel playbacks in the section below. + +As default, ALSA driver assigns the first PCM device (i.e. hw:0,0 for +card#0) for front and 4/6ch playbacks, while the second PCM device +(hw:0,1) is assigned to the second DAC for rear playback. + +There are slight difference between two DACs. + +- The first DAC supports U8 and S16LE formats, while the second DAC + supports only S16LE. +- The second DAC supports only two channel stereo. + +Please note that the CM8x38 DAC doesn't support continuous playback +rate but only fixed rates: 5512, 8000, 11025, 16000, 22050, 32000, +44100 and 48000 Hz. + +The rear output can be heard only when "Four Channel Mode" switch is +disabled. Otherwise no signal will be routed to the rear speakers. +As default it's turned on. + +*** WARNING *** +When "Four Channel Mode" switch is off, the output from rear speakers +will be FULL VOLUME regardless of Master and PCM volumes. +This might damage your audio equipment. Please disconnect speakers +before your turn off this switch. +*** WARNING *** + +[ Well.. I once got the output with correct volume (i.e. same with the + front one) and was so excited. It was even with "Four Channel" bit + on and "double DAC" mode. Actually I could hear separate 4 channels + from front and rear speakers! But.. after reboot, all was gone. + It's a very pity that I didn't save the register dump at that + time.. Maybe there is an unknown register to achieve this... ] + +If your card has an extra output jack for the rear output, the rear +playback should be routed there as default. If not, there is a +control switch in the driver "Line-In As Rear", which you can change +via alsamixer or somewhat else. When this switch is on, line-in jack +is used as rear output. + +There are two more controls regarding to the rear output. +The "Exchange DAC" switch is used to exchange front and rear playback +routes, i.e. the 2nd DAC is output from front output. + + +4/6 Multi-Channel Playback +-------------------------- + +The recent CM8738 chips support for the 4/6 multi-channel playback +function. This is useful especially for AC3 decoding. + +When the multi-channel is supported, the driver name has a suffix +"-MC" such like "CMI8738-MC6". You can check this name from +/proc/asound/cards. + +When the 4/6-ch output is enabled, the front DAC accepts up to 6 (or +4) channels. This is different from the dual DACs described in the +previous section. While the dual DAC supports two different rates or +formats, the 4/6-ch playback supports only the same condition for all +channels. + +For using 4/6 channel playback, you need to specify the PCM channels +as you like and set the format S16LE. For example, for playback with +4 channels, + + snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED); + // or mmap if you like + snd_pcm_hw_params_set_format(pcm, hw, SND_PCM_FORMAT_S16_LE); + snd_pcm_hw_params_set_channels(pcm, hw, 4); + +and use the interleaved 4 channel data. + +There is a control switch, "Line-In As Bass". As you can imagine from +its name, the line-in jack is used for the bass (5th and 6th channels) +output. + + +Digital I/O +----------- + +The CM8x38 provides the excellent SPDIF capability with very chip +price (yes, that's the reason I bought the card :) + +The SPDIF playback and capture are done via the third PCM device +(hw:0,2). Usually this is assigned to the PCM device "spdif". +The available rates are 44100 and 48000 Hz. +For playback with aplay, you can run like below: + + % aplay -Dhw:0,2 foo.wav + +or + + % aplay -Dspdif foo.wav + +So far, only S16LE format is supported. Still no 24bit. Sorry, not +enough info for this. + +The playback and capture over SPDIF use normal DAC and ADC, +respectively, so you cannot playback both analog and digital streams +simultaneously. + +To enable SPDIF output, you need to turn on "IEC958 Output Switch" +control via mixer or alsactl. Then you'll see the red light on from +the card so you know that's working obviously :) +The SPDIF input is always enabled, so you can hear SPDIF input data +from line-out with "IEC958 In Monitor" switch at any time (see +below). + +You can play via SPDIF even with the first device (hw:0,0), +but SPDIF is enabled only when the proper format (S16LE), sample rate +(441100 or 48000) and channels (2) are used. Otherwise it's turned +off. (Also don't forget to turn on "IEC958 Output Switch", too.) + + +Additionally there are relevant control switches: + +"IEC958 Mix Analog" - Mix analog PCM playback and FM-OPL/3 streams and + output through SPDIF. This switch appears only on old chip + models (CM8738 033 and 037). + Note: without this control you can output PCM to SPDIF. + This is "mixing" of streams, so e.g. it's not for AC3 output + (see the next section). + +"IEC958 In Select" - Select SPDIF input, the internal CD-in (false) + and the external input (true). This switch appears only on + the chip models 039 or later. + +"IEC958 Loop" - SPDIF input data is loop back into SPDIF + output (aka bypass) + +"IEC958 Copyright" - Set the copyright bit. + +"IEC958 5V" - Select 0.5V (coax) or 5V (optical) interface. + On some cards this doesn't work and you need to change the + configuration with hardware dip-switch. + +"IEC958 In Monitor" - SPDIF input is routed to DAC. + +"IEC958 In Phase Inverse" - Set SPDIF input format as inverse. + [FIXME: this doesn't work on all chips..] + +"IEC958 In Valid" - Set input validity flag detection. + +Note: When "PCM Playback Switch" is on, you'll hear the digital output +stream through analog line-out. + + +The AC3 (RAW DIGITAL) OUTPUT +---------------------------- + +The driver supports raw digital (typically AC3) i/o over SPDIF. This +can be toggled via IEC958 playback control, but usually you need to +access it via alsa-lib. See alsa-lib documents for more details. + +On the raw digital mode, the "PCM Playback Switch" is automatically +turned off so that non-audio data is heard from the analog line-out. +Similarly the following switches are off: "IEC958 Mix Analog" and +"IEC958 Loop". The switches are resumed after closing the SPDIF PCM +device automatically to the previous state. + + +ANALOG MIXER INTERFACE +---------------------- + +The mixer interface on CM8x38 is similar to SB16. +There are Master, PCM, Synth, CD, Line, Mic and PC Speaker playback +volumes. Synth, CD, Line and Mic have playback and capture switches, +too, as well as SB16. + +In addition to the standard SB mixer, CM8x38 provides more functions. +- PCM playback switch +- PCM capture switch (to capture the data sent to DAC) +- Mic Boost switch +- Mic capture volume +- Aux playback volume/switch and capture switch +- 3D control switch + + +MIDI CONTROLLER +--------------- + +The MPU401-UART interface is enabled as default only for the first +(CMIPCI) card. You need to set module option "snd_midi_port" properly +for the 2nd (CMIPCI) card. + +There is _no_ hardware wavetable function on this chip (except for +OPL3 synth below). +What's said as MIDI synth on Windows is a software synthesizer +emulation. On Linux use TiMidity or other softsynth program for +playing MIDI music. + + +FM OPL/3 Synth +-------------- + +The FM OPL/3 is also enabled as default only for the first card. +Set "snd_fm_port" module option for more cards. + +The output quality of FM OPL/3 is, however, very weird. +I don't know why.. + + +Joystick and Modem +------------------ + +The joystick and modem should be available by enabling the control +switch "Joystick" and "Modem" respectively. But I myself have never +tested them yet. + + +Debugging Information +--------------------- + +The registers are shown in /proc/asound/cardX/cmipci. If you have any +problem (especially unexpected behavior of mixer), please attach the +output of this proc file together with the bug report. diff -Nru a/Documentation/sound/alsa/SB-Live-mixer.txt b/Documentation/sound/alsa/SB-Live-mixer.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/alsa/SB-Live-mixer.txt Thu Mar 7 18:17:46 2002 @@ -0,0 +1,356 @@ + + Sound Blaster Live mixer / default DSP code + =========================================== + + +The EMU10K1 chips have a DSP part which can be programmed to support +various ways of sample processing, which is described here. +(This acticle does not deal with the overall functionality of the +EMU10K1 chips. See the manuals section for further details.) + +The ALSA driver programs this portion of chip by default code +(can be altered later) which offers the following functionality: + + +1) IEC958 (S/PDIF) raw PCM +-------------------------- + +This PCM device (it's the 4th PCM device (index 3!) and first subdevice +(index 0) for a given card) allows to forward 48kHz, stereo, 16-bit +little endian streams without any modifications to the digital output +(coaxial or optical). The universal interface allows the creation of up +to 8 raw PCM devices operating at 48kHz, 16-bit little endian. It would +be easy to add support for multichannel devices to the current code, +but the conversion routines exist only for stereo (2-channel streams) +at the time. + +Look to tram_poke routines in lowlevel/emu10k1/emufx.c for more details. + + +2) Digital mixer controls +------------------------- + +These controls are built using the DSP instructions. They offer extended +functionality. Only the default build-in code in the ALSA driver is described +here. Note that the controls work as attenuators: the maximum value is the +neutral position leaving the signal unchanged. Note that if the same destination +is mentioned in multiple controls, the signal is accumulated and can be wrapped +(set to maximal or minimal value without checking of overflow). + + +Explanation of used abbreviations: + +DAC - digital to analog converter +ADC - analog to digital converter +I2S - one-way three wire serial bus for digital sound by Philips Semiconductors + (this standard is used for connecting standalone DAC and ADC converters) +LFE - low frequency effects (subwoofer signal) +AC97 - a chip containing an analog mixer, DAC and ADC converters +IEC958 - S/PDIF +FX-bus - the EMU10K1 chip has an effect bus containing 16 accumulators. + Each of the synthesizer voices can feed its output to these accumulators + and the DSP microcontroller can operate with the resulting sum. + + +name='Wave Playback Volume',index=0 + +This control is used to attenuate samples for left and right PCM FX-bus +accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples. +The result samples are forwarded to the front DAC PCM slots of the AC97 codec. + +name='Wave Surround Playback Volume',index=0 + +This control is used to attenuate samples for left and right PCM FX-bus +accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples. +The result samples are forwarded to the rear I2S DACs. These DACs operates +separately (they are not inside the AC97 codec). + +name='Wave Center Playback Volume',index=0 + +This control is used to attenuate samples for left and right PCM FX-bus +accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples. +The result is mixed to mono signal (single channel) and forwarded to +the ??rear?? right DAC PCM slot of the AC97 codec. + +name='Wave LFE Playback Volume',index=0 + +This control is used to attenuate samples for left and right PCM FX-bus +accumulators. ALSA uses accumulators 0 and 1 for left and right PCM. +The result is mixed to mono signal (single channel) and forwarded to +the ??rear?? left DAC PCM slot of the AC97 codec. + +name='Wave Capture Volume',index=0 +name='Wave Capture Switch',index=0 + +These controls are used to attenuate samples for left and right PCM FX-bus +accumulator. ALSA uses accumulators 0 and 1 for left and right PCM. +The result is forwarded to the ADC capture FIFO (thus to the standard capture +PCM device). + +name='Music Playback Volume',index=0 + +This control is used to attenuate samples for left and right MIDI FX-bus +accumulators. ALSA uses accumulators 4 and 5 for left and right MIDI samples. +The result samples are forwarded to the front DAC PCM slots of the AC97 codec. + +name='Music Capture Volume',index=0 +name='Music Capture Switch',index=0 + +These controls are used to attenuate samples for left and right MIDI FX-bus +accumulator. ALSA uses accumulators 4 and 5 for left and right PCM. +The result is forwarded to the ADC capture FIFO (thus to the standard capture +PCM device). + +name='Surround Digital Playback Volume',index=0 + +This control is used to attenuate samples for left and right rear PCM FX-bus +accumulators. ALSA uses accumulators 2 and 3 for left and right rear PCM samples. +The result samples are forwarded to the rear I2S DACs. These DACs operate +separately (they are not inside the AC97 codec). + +name='Surround Digital Capture Volume',index=0 +name='Surround Digital Capture Switch',index=0 + +These controls are used to attenuate samples for left and right rear PCM FX-bus +accumulators. ALSA uses accumulators 2 and 3 for left and right rear PCM samples. +The result is forwarded to the ADC capture FIFO (thus to the standard capture +PCM device). + +name='Center Playback Volume',index=0 + +This control is used to attenuate sample for center PCM FX-bus accumulator. +ALSA uses accumulator 6 for center PCM sample. The result sample is forwarded +to the ??rear?? right DAC PCM slot of the AC97 codec. + +name='LFE Playback Volume',index=0 + +This control is used to attenuate sample for center PCM FX-bus accumulator. +ALSA uses accumulator 6 for center PCM sample. The result sample is forwarded +to the ??rear?? left DAC PCM slot of the AC97 codec. + +name='AC97 Playback Volume',index=0 + +This control is used to attenuate samples for left and right front ADC PCM slots +of the AC97 codec. The result samples are forwarded to the front DAC PCM +slots of the AC97 codec. +******************************************************************************** +*** Note: This control should be zero for the standard operations, otherwise *** +*** a digital loopback is activated. *** +******************************************************************************** + +name='AC97 Capture Volume',index=0 + +This control is used to attenuate samples for left and right front ADC PCM slots +of the AC97 codec. The result is forwarded to the ADC capture FIFO (thus to +the standard capture PCM device). +******************************************************************************** +*** Note: This control should be 100 (maximal value), otherwise no analog *** +*** inputs of the AC97 codec can be captured (recorded). *** +******************************************************************************** + +name='IEC958 TTL Playback Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 TTL +digital inputs (usually used by a CDROM drive). The result samples are +forwarded to the front DAC PCM slots of the AC97 codec. + +name='IEC958 TTL Capture Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 TTL +digital inputs (usually used by a CDROM drive). The result samples are +forwarded to the ADC capture FIFO (thus to the standard capture PCM device). + +name='Zoom Video Playback Volume',index=0 + +This control is used to attenuate samples from left and right zoom video +digital inputs (usually used by a CDROM drive). The result samples are +forwarded to the front DAC PCM slots of the AC97 codec. + +name='Zoom Video Capture Volume',index=0 + +This control is used to attenuate samples from left and right zoom video +digital inputs (usually used by a CDROM drive). The result samples are +forwarded to the ADC capture FIFO (thus to the standard capture PCM device). + +name='IEC958 Optical Playback Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 optical +digital input. The result samples are forwarded to the front DAC PCM slots +of the AC97 codec. + +name='IEC958 Optical Capture Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 optical +digital inputs. The result samples are forwarded to the ADC capture FIFO +(thus to the standard capture PCM device). + +name='IEC958 Coaxial Playback Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 coaxial +digital inputs. The result samples are forwarded to the front DAC PCM slots +of the AC97 codec. + +name='IEC958 Coaxial Capture Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 coaxial +digital inputs. The result samples are forwarded to the ADC capture FIFO +(thus to the standard capture PCM device). + +name='Line LiveDrive Playback Volume',index=0 +name='Line LiveDrive Playback Volume',index=1 + +This control is used to attenuate samples from left and right I2S ADC +inputs (on the LiveDrive). The result samples are forwarded to the front +DAC PCM slots of the AC97 codec. + +name='Line LiveDrive Capture Volume',index=1 +name='Line LiveDrive Capture Volume',index=1 + +This control is used to attenuate samples from left and right I2S ADC +inputs (on the LiveDrive). The result samples are forwarded to the ADC +capture FIFO (thus to the standard capture PCM device). + +name='Tone Control - Switch',index=0 + +This control turns the tone control on or off. The samples for front, rear +and center / LFE outputs are affected. + +name='Tone Control - Bass',index=0 + +This control sets the bass intensity. There is no neutral value!! +When the tone control code is activated, the samples are always modified. +The closest value to pure signal is 20. + +name='Tone Control - Treble',index=0 + +This control sets the treble intensity. There is no neutral value!! +When the tone control code is activated, the samples are always modified. +The closest value to pure signal is 20. + +name='IEC958 Optical Raw Playback Switch',index=0 + +If this switch is on, then the samples for the IEC958 (S/PDIF) digital +output are taken only from the raw FX8010 PCM, otherwise standard front +PCM samples are taken. + +name='Headphone Playback Volume',index=1 + +This control attenuates the samples for the headphone output. + +name='Headphone Center Playback Switch',index=1 + +If this switch is on, then the sample for the center PCM is put to the +left headphone output (useful for SB Live cards without separate center/LFE +output). + +name='Headphone LFE Playback Switch',index=1 + +If this switch is on, then the sample for the center PCM is put to the +right headphone output (useful for SB Live cards without separate center/LFE +output). + + +3) PCM stream related controls +------------------------------ + +name='EMU10K1 PCM Volume',index 0-31 + +Channel volume attenuation in range 0-0xffff. The maximum value (no +attenuation) is default. The channel mapping for three values is +as follows: + + 0 - mono, default 0xffff (no attenuation) + 1 - left, default 0xffff (no attenuation) + 2 - right, default 0xffff (no attenuation) + +name='EMU10K1 PCM Send Routing',index 0-31 + +This control specifies the destination - FX-bus accumulators. There are +twelve values with this mapping: + + 0 - mono, A destination (FX-bus 0-15), default 0 + 1 - mono, B destination (FX-bus 0-15), default 1 + 2 - mono, C destination (FX-bus 0-15), default 2 + 3 - mono, D destination (FX-bus 0-15), default 3 + 4 - left, A destination (FX-bus 0-15), default 0 + 5 - left, B destination (FX-bus 0-15), default 1 + 6 - left, C destination (FX-bus 0-15), default 2 + 7 - left, D destination (FX-bus 0-15), default 3 + 8 - right, A destination (FX-bus 0-15), default 0 + 9 - right, B destination (FX-bus 0-15), default 1 + 10 - right, C destination (FX-bus 0-15), default 2 + 11 - right, D destination (FX-bus 0-15), default 3 + +Don't forget that it's illegal to assign a channel to the same FX-bus accumulator +more than once (it means 0=0 && 1=0 is an invalid combination). + +name='EMU10K1 PCM Send Volume',index 0-31 + +It specifies the attenuation (amount) for given destination in range 0-255. +The channel mapping is following: + + 0 - mono, A destination attn, default 255 (no attenuation) + 1 - mono, B destination attn, default 255 (no attenuation) + 2 - mono, C destination attn, default 0 (mute) + 3 - mono, D destination attn, default 0 (mute) + 4 - left, A destination attn, default 255 (no attenuation) + 5 - left, B destination attn, default 0 (mute) + 6 - left, C destination attn, default 0 (mute) + 7 - left, D destination attn, default 0 (mute) + 8 - right, A destination attn, default 0 (mute) + 9 - right, B destination attn, default 255 (no attenuation) + 10 - right, C destination attn, default 0 (mute) + 11 - right, D destination attn, default 0 (mute) + + + +4) MANUALS/PATENTS: +------------------- + +ftp://opensource.creative.com/pub/doc +------------------------------------- + + Files: + LM4545.pdf AC97 Codec + + m2049.pdf The EMU10K1 Digital Audio Processor + + hog63.ps FX8010 - A DSP Chip Architecture for Audio Effects + + +WIPO Patents +------------ + Patent numbers: + WO 9901813 (A1) Audio Effects Processor with multiple asynchronous (Jan. 14, 1999) + streams + + WO 9901814 (A1) Processor with Instruction Set for Audio Effects (Jan. 14, 1999) + + WO 9901953 (A1) Audio Effects Processor having Decoupled Instruction + Execution and Audio Data Sequencing (Jan. 14, 1999) + + +US Patents (http://www.uspto.gov/) +---------------------------------- + + US 5925841 Digital Sampling Instrument employing cache memory (Jul. 20, 1999) + + US 5928342 Audio Effects Processor integrated on a single chip (Jul. 27, 1999) + with a multiport memory onto which multiple asynchronous + digital sound samples can be concurrently loaded + + US 5930158 Processor with Instruction Set for Audio Effects (Jul. 27, 1999) + + US 6032235 Memory initialization circuit (Tram) (Feb. 29, 2000) + + US 6138207 Interpolation looping of audio samples in cache connected to (Oct. 24, 2000) + system bus with prioritization and modification of bus transfers + in accordance with loop ends and minimum block sizes + + US 6151670 Method for conserving memory storage using a (Nov. 21, 2000) + pool of short term memory registers + + US 6195715 Interrupt control for multiple programs communicating with (Feb. 27, 2001) + a common interrupt by associating programs to GP registers, + defining interrupt register, polling GP registers, and invoking + callback routine associated with defined interrupt register diff -Nru a/Documentation/sound/alsa/seq_oss.html b/Documentation/sound/alsa/seq_oss.html --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/alsa/seq_oss.html Thu Mar 7 18:17:46 2002 @@ -0,0 +1,409 @@ + + + + OSS Sequencer Emulation on ALSA + + + +
+

+ +

+ +
+

+OSS Sequencer Emulation on ALSA

+ +
+

Copyright (c) 1998,1999 by Takashi Iwai +<iwai@ww.uni-erlangen.de> +

ver.0.1.8; Nov. 16, 1999 +

+ +

+ +

+1. Description

+This directory contains the OSS sequencer emulation driver on ALSA. Note +that this program is still in the development state. +

What this does - it provides the emulation of the OSS sequencer, access +via +/dev/sequencer and /dev/music devices. +The most of applications using OSS can run if the appropriate ALSA +sequencer is prepared. +

The following features are emulated by this driver: +

    +
  • +Normal sequencer and MIDI events:
  • + +
    They are converted to the ALSA sequencer events, and sent to the corresponding +port. +
  • +Timer events:
  • + +
    The timer is not selectable by ioctl. The control rate is fixed to +100 regardless of HZ. That is, even on Alpha system, a tick is always +1/100 second. The base rate and tempo can be changed in /dev/music. + +
  • +Patch loading:
  • + +
    It purely depends on the synth drivers whether it's supported since +the patch loading is realized by callback to the synth driver. +
  • +I/O controls:
  • + +
    Most of controls are accepted. Some controls +are dependent on the synth driver, as well as even on original OSS.
+Furthermore, you can find the following advanced features: +
    +
  • +Better queue mechanism:
  • + +
    The events are queued before processing them. +
  • +Multiple applications:
  • + +
    You can run two or more applications simultaneously (even for OSS sequencer)! +However, each MIDI device is exclusive - that is, if a MIDI device is opened +once by some application, other applications can't use it. No such a restriction +in synth devices. +
  • +Real-time event processing:
  • + +
    The events can be processed in real time without using out of bound +ioctl. To switch to real-time mode, send ABSTIME 0 event. The followed +events will be processed in real-time without queued. To switch off the +real-time mode, send RELTIME 0 event. +
  • +/proc interface:
  • + +
    The status of applications and devices can be shown via /proc/asound/seq/oss +at any time. In the later version, configuration will be changed via /proc +interface, too.
+ +

+2. Installation

+Run configure script with both sequencer support (--with-sequencer=yes) +and OSS emulation (--with-oss=yes) options. A module snd-seq-oss.o +will be created. If the synth module of your sound card supports for OSS +emulation (so far, only Emu8000 driver), this module will be loaded automatically. +Otherwise, you need to load this module manually. +

At beginning, this module probes all the MIDI ports which have been +already connected to the sequencer. Once after that, the creation and deletion +of ports are watched by announcement mechanism of ALSA sequencer. +

The available synth and MIDI devices can be found in proc interface. +Run "cat /proc/asound/seq/oss", and check the devices. For example, +if you use an AWE64 card, you'll see like the following: +

        OSS sequencer emulation version 0.1.8
+        ALSA client number 63
+        ALSA receiver port 0
+
+        Number of applications: 0
+
+        Number of synth devices: 1
+
+        synth 0: [EMU8000]
+          type 0x1 : subtype 0x20 : voices 32
+          capabilties : ioctl enabled / load_patch enabled
+
+        Number of MIDI devices: 3
+
+        midi 0: [Emu8000 Port-0] ALSA port 65:0
+          capability write / opened none
+
+        midi 1: [Emu8000 Port-1] ALSA port 65:1
+          capability write / opened none
+
+        midi 2: [0: MPU-401 (UART)] ALSA port 64:0
+          capability read/write / opened none
+Note that the device number may be different from the information of +/proc/asound/oss-devices +or ones of the original OSS driver. Use the device number listed in /proc/asound/seq/oss +to play via OSS sequencer emulation. +

+3. Using Synthesizer Devices

+Run your favorite program. I've tested playmidi-2.4, awemidi-0.4.3, gmod-3.1 +and xmp-1.1.5. You can load samples via /dev/sequencer like sfxload, +too. +

If the lowlevel driver supports multiple access to synth devices (like +Emu8000 driver), two or more applications are allowed to run at the same +time. +

+4. Using MIDI Devices

+So far, only MIDI output was tested. MIDI input was not checked at all, +but hopefully it will work. Use the device number listed in /proc/asound/seq/oss. +Be aware that these numbers are mostly different from the list in +/proc/asound/oss-devices. +

+5. Module Options

+The following module options are available: +
    +
  • +maxqlen
  • + +
    specifies the maximum read/write queue length. This queue is private +for OSS sequencer, so that it is independent from the queue length of ALSA +sequencer. Default value is 1024. +
  • +seq_oss_debug
  • + +
    specifies the debug level and accepts zero (= no debug message) or +positive integer. Default value is 0.
+ +

+6. Queue Mechanism

+OSS sequencer emulation uses an ALSA priority queue. The +events from /dev/sequencer are processed and put onto the queue +specified by module option. +

All the events from /dev/sequencer are parsed at beginning. +The timing events are also parsed at this moment, so that the events may +be processed in real-time. Sending an event ABSTIME 0 switches the operation +mode to real-time mode, and sending an event RELTIME 0 switches it off. +In the real-time mode, all events are dispatched immediately. +

The queued events are dispatched to the corresponding ALSA sequencer +ports after scheduled time by ALSA sequencer dispatcher. +

If the write-queue is full, the application sleeps until a certain amount +(as default one half) becomes empty in blocking mode. The synchronization +to write timing was implemented, too. +

The input from MIDI devices or echo-back events are stored on read FIFO +queue. If application reads /dev/sequencer in blocking mode, the +process will be awaked. + +

+7. Interface to Synthesizer Device

+ +

+7.1. Registration

+To register an OSS synthesizer device, use snd_seq_oss_synth_register +function. +
int snd_seq_oss_synth_register(char *name, int type, int subtype, int nvoices,
+                              snd_seq_oss_callback_t *oper, void *private_data)
+The arguments name, type, subtype and +nvoices +are used for making the appropriate synth_info structure for ioctl. The +return value is an index number of this device. This index must be remembered +for unregister. If registration is failed, -errno will be returned. +

To release this device, call snd_seq_oss_synth_unregister function: +

int snd_seq_oss_synth_unregister(int index),
+where the index is the index number returned by register function. +

+7.2. Callbacks

+OSS synthesizer devices have capability for sample downloading and ioctls +like sample reset. In OSS emulation, these special features are realized +by using callbacks. The registration argument oper is used to specify these +callbacks. The following callback functions must be defined: +
snd_seq_oss_callback_t:
+        int (*open)(snd_seq_oss_arg_t *p, void *closure);
+        int (*close)(snd_seq_oss_arg_t *p);
+        int (*ioctl)(snd_seq_oss_arg_t *p, unsigned int cmd, unsigned long arg);
+        int (*load_patch)(snd_seq_oss_arg_t *p, int format, const char *buf, int offs, int count);
+        int (*reset)(snd_seq_oss_arg_t *p);
+Except for open and close callbacks, they are allowed
+to be NULL.
+

Each callback function takes the argument type snd_seq_oss_arg_t as the +first argument. +

struct snd_seq_oss_arg_t {
+        int app_index;
+        int file_mode;
+        int seq_mode;
+        snd_seq_addr_t addr;
+        void *private_data;
+        int event_passing;
+};
+The first three fields, app_index, file_mode and +seq_mode +are initialized by OSS sequencer. The app_index is the application +index which is unique to each application opening OSS sequencer. The +file_mode +is bit-flags indicating the file operation mode. See +seq_oss.h +for its meaning. The seq_mode is sequencer operation mode. In +the current version, only SND_OSSSEQ_MODE_SYNTH is used. +

The next two fields, addr and private_data, must be +filled by the synth driver at open callback. The addr contains +the address of ALSA sequencer port which is assigned to this device. If +the driver allocates memory for private_data, it must be released +in close callback by itself. +

The last field, event_passing, indicates how to translate note-on +/ off events. In PROCESS_EVENTS mode, the note 255 is regarded +as velocity change, and key pressure event is passed to the port. In PASS_EVENTS +mode, all note on/off events are passed to the port without modified. PROCESS_KEYPRESS +mode checks the note above 128 and regards it as key pressure event (mainly +for Emu8000 driver). +

+7.2.1. Open Callback

+The open is called at each time this device is opened by an application +using OSS sequencer. This must not be NULL. Typically, the open callback +does the following procedure: +
    +
  1. +Allocate private data record.
  2. + +
  3. +Create an ALSA sequencer port.
  4. + +
  5. +Set the new port address on arg->addr.
  6. + +
  7. +Set the private data record pointer on arg->private_data.
  8. +
+Note that the type bit-flags in port_info of this synth port must NOT contain +TYPE_MIDI_GENERIC +bit. Instead, TYPE_SPECIFIC should be used. Also, CAP_SUBSCRIPTION +bit should NOT be included, too. This is necessary to tell it from other +normal MIDI devices. If the open procedure succeeded, return zero. Otherwise, +return -errno. +

+7.2.2 Ioctl Callback

+The ioctl callback is called when the sequencer receives device-specific +ioctls. The following two ioctls should be processed by this callback: +
    +
  • +IOCTL_SEQ_RESET_SAMPLES
  • + +
    reset all samples on memory -- return 0 +
  • +IOCTL_SYNTH_MEMAVL
  • + +
    return the available memory size +
  • +FM_4OP_ENABLE
  • + +
    can be ignored usually
+The other ioctls are processed inside the sequencer without passing to +the lowlevel driver. +

+7.2.3 Load_Patch Callback

+The load_patch callback is used for sample-downloading. This callback +must read the data on user-space and transfer to each device. Return 0 +if succeeded, and -errno if failed. The format argument is the patch key +in patch_info record. The buf is user-space pointer where patch_info record +is stored. The offs can be ignored. The count is total data size of this +sample data. +

+7.2.4 Close Callback

+The close callback is called when this device is closed by the +applicaion. If any private data was allocated in open callback, it must +be released in the close callback. The deletion of ALSA port should be +done here, too. This callback must not be NULL. +

+7.2.5 Reset Callback

+The reset callback is called when sequencer device is reset or +closed by applications. The callback should turn off the sounds on the +relevant port immediately, and initialize the status of the port. If this +callback is undefined, OSS seq sends a HEARTBEAT event to the +port. +

+7.3 Events

+Most of the events are processed by sequencer and translated to the adequate +ALSA sequencer events, so that each synth device can receive by input_event +callback of ALSA sequencer port. The following ALSA events should be implemented +by the driver: +
  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ALSA eventOriginal OSS events
NOTEONSEQ_NOTEON +
MIDI_NOTEON
NOTESEQ_NOTEOFF +
MIDI_NOTEOFF
KEYPRESSMIDI_KEY_PRESSURE
CHANPRESSSEQ_AFTERTOUCH +
MIDI_CHN_PRESSURE
PGMCHANGESEQ_PGMCHANGE +
MIDI_PGM_CHANGE
PITCHBENDSEQ_CONTROLLER(CTRL_PITCH_BENDER) +
MIDI_PITCH_BEND
CONTROLLERMIDI_CTL_CHANGE +
SEQ_BALANCE (with CTL_PAN)
CONTROL14SEQ_CONTROLLER
REGPARAMSEQ_CONTROLLER(CTRL_PITCH_BENDER_RANGE)
SYSEXSEQ_SYSEX
+ +

The most of these behavior can be realized by MIDI emulation driver +included in the Emu8000 lowlevel driver. In the future release, this module +will be independent. +

Some OSS events (SEQ_PRIVATE and SEQ_VOLUME events) are passed as event +type SND_SEQ_OSS_PRIVATE. The OSS sequencer passes these event 8 byte +packets without any modification. The lowlevel driver should process these +events appropriately. +

+8. Interface to MIDI Device

+Since the OSS emulation probes the creation and deletion of ALSA MIDI sequencer +ports automatically by receiving announcement from ALSA sequencer, the +MIDI devices don't need to be registered explicitly like synth devices. +However, the MIDI port_info registered to ALSA sequencer must include a group +name SND_SEQ_GROUP_DEVICE and a capability-bit CAP_READ or +CAP_WRITE. Also, subscription capabilities, CAP_SUBS_READ or CAP_SUBS_WRITE, +must be defined, too. If these conditions are not satisfied, the port is not +registered as OSS sequencer MIDI device. +

The events via MIDI devices are parsed in OSS sequencer and converted +to the corresponding ALSA sequencer events. The input from MIDI sequencer +is also converted to MIDI byte events by OSS sequencer. This works just +a reverse way of seq_midi module. +

+9. Known Problems / TODO's

+ +
    +
  • +Patch loading via ALSA instrument layer is not implemented yet.
  • +
+ + + diff -Nru a/Documentation/sound/btaudio b/Documentation/sound/btaudio --- a/Documentation/sound/btaudio Thu Mar 7 18:17:39 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,92 +0,0 @@ - -Intro -===== - -people start bugging me about this with questions, looks like I -should write up some documentation for this beast. That way I -don't have to answer that much mails I hope. Yes, I'm lazy... - - -You might have noticed that the bt878 grabber cards have actually -_two_ PCI functions: - -$ lspci -[ ... ] -00:0a.0 Multimedia video controller: Brooktree Corporation Bt878 (rev 02) -00:0a.1 Multimedia controller: Brooktree Corporation Bt878 (rev 02) -[ ... ] - -The first does video, it is backward compatible to the bt848. The second -does audio. btaudio is a driver for the second function. It's a sound -driver which can be used for recording sound (and _only_ recording, no -playback). As most TV cards come with a short cable which can be plugged -into your sound card's line-in you probably don't need this driver if all -you want to do is just watching TV... - - -Driver Status -============= - -Still somewhat experimental. The driver should work stable, i.e. it -should'nt crash your box. It might not work as expected, have bugs, -not being fully OSS API compilant, ... - -Latest versions are available from http://bytesex.org/bttv/, the -driver is in the bttv tarball. Kernel patches might be available too, -have a look at http://bytesex.org/bttv/listing.html. - -The chip knows two different modes. btaudio registers two dsp -devices, one for each mode. They can not be used at the same time. - - -Digital audio mode -================== - -The chip gives you 16 bit stereo sound. The sample rate depends on -the external source which feeds the bt878 with digital sound via I2S -interface. There is a insmod option (rate) to tell the driver which -sample rate the hardware uses (32000 is the default). - -One possible source for digital sound is the msp34xx audio processor -chip which provides digital sound via I2S with 32 kHz sample rate. My -Hauppauge board works this way. - -The Osprey-200 reportly gives you digital sound with 44100 Hz sample -rate. It is also possible that you get no sound at all. - - -analog mode (A/D) -================= - -You can tell the driver to use this mode with the insmod option "analog=1". -The chip has three analog inputs. Consequently you'll get a mixer device -to control these. - -The analog mode supports mono only. Both 8 + 16 bit. Both are _signed_ -int, which is uncommon for the 8 bit case. Sample rate range is 119 kHz -to 448 kHz. Yes, the number of digits is correct. The driver supports -downsampling by powers of two, so you can ask for more usual sample rates -like 44 kHz too. - -With my Hauppauge I get noisy sound on the second input (mapped to line2 -by the mixer device). Others get a useable signal on line1. - - -some examples -============= - -* read audio data from btaudio (dsp2), send to es1730 (dsp,dsp1): - $ sox -w -r 32000 -t ossdsp /dev/dsp2 -t ossdsp /dev/dsp - -* read audio data from btaudio, send to esound daemon (which might be - running on another host): - $ sox -c 2 -w -r 32000 -t ossdsp /dev/dsp2 -t sw - | esdcat -r 32000 - $ sox -c 1 -w -r 32000 -t ossdsp /dev/dsp2 -t sw - | esdcat -m -r 32000 - - -Have fun, - - Gerd - --- -Gerd Knorr diff -Nru a/Documentation/sound/cs46xx b/Documentation/sound/cs46xx --- a/Documentation/sound/cs46xx Thu Mar 7 18:17:38 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,138 +0,0 @@ - -Documentation for the Cirrus Logic/Crystal SoundFusion cs46xx/cs4280 audio -controller chips (2001/05/11) - -The cs46xx audio driver supports the DSP line of Cirrus controllers. -Specifically, the cs4610, cs4612, cs4614, cs4622, cs4624, cs4630 and the cs4280 -products. This driver uses the generic ac97_codec driver for AC97 codec -support. - - -Features: - -Full Duplex Playback/Capture supported from 8k-48k. -16Bit Signed LE & 8Bit Unsigned, with Mono or Stereo supported. - -APM/PM - 2.2.x PM is enabled and functional. APM can also -be enabled for 2.4.x by modifying the CS46XX_ACPI_SUPPORT macro -definition. - -DMA playback buffer size is configurable from 16k (defaultorder=2) up to 2Meg -(defaultorder=11). DMA capture buffer size is fixed at a single 4k page as -two 2k fragments. - -MMAP seems to work well with QuakeIII, and test XMMS plugin. - -Myth2 works, but the polling logic is not fully correct, but is functional. - -The 2.4.4-ac6 gameport code in the cs461x joystick driver has been tested -with a Microsoft Sidewinder joystick (cs461x.o and sidewinder.o). This -audio driver must be loaded prior to the joystick driver to enable the -DSP task image supporting the joystick device. - - -Limitations: - -SPDIF is currently not supported. - -Primary codec support only. No secondary codec support is implemented. - - - -NOTES: - -Hercules Game Theatre XP - the EGPIO2 pin controls the external Amp, -and has been tested. -Module parameter hercules_egpio_disable set to 1, will force a 0 to EGPIODR -to disable the external amplifier. - -VTB Santa Cruz - the GPIO7/GPIO8 on the Secondary Codec control -the external amplifier for the "back" speakers, since we do not -support the secondary codec then this external amp is not -turned on. The primary codec external amplifier is supported but -note that the AC97 EAPD bit is inverted logic (amp_voyetra()). - -DMA buffer size - there are issues with many of the Linux applications -concerning the optimal buffer size. Several applications request a -certain fragment size and number and then do not verify that the driver -has the ability to support the requested configuration. -SNDCTL_DSP_SETFRAGMENT ioctl is used to request a fragment size and -number of fragments. Some applications exit if an error is returned -on this particular ioctl. Therefore, in alignment with the other OSS audio -drivers, no error is returned when a SETFRAGs IOCTL is received, but the -values passed from the app are not used in any buffer calculation -(ossfragshift/ossmaxfrags are not used). -Use the "defaultorder=N" module parameter to change the buffer size if -you have an application that requires a specific number of fragments -or a specific buffer size (see below). - -Debug Interface ---------------- -There is an ioctl debug interface to allow runtime modification of the -debug print levels. This debug interface code can be disabled from the -compilation process with commenting the following define: -#define CSDEBUG_INTERFACE 1 -There is also a debug print methodolgy to select printf statements from -different areas of the driver. A debug print level is also used to allow -additional printfs to be active. Comment out the following line in the -driver to disable compilation of the CS_DBGOUT print statements: -#define CSDEBUG 1 - -Please see the defintions for cs_debuglevel and cs_debugmask for additional -information on the debug levels and sections. - -There is also a csdbg executable to allow runtime manipulation of these -parameters. for a copy email: twoller@crystal.cirrus.com - - - -MODULE_PARMS definitions ------------------------- -MODULE_PARM(defaultorder, "i"); -defaultorder=N -where N is a value from 1 to 12 -The buffer order determines the size of the dma buffer for the driver. -under Linux, a smaller buffer allows more responsiveness from many of the -applications (e.g. games). A larger buffer allows some of the apps (esound) -to not underrun the dma buffer as easily. As default, use 32k (order=3) -rather than 64k as some of the games work more responsively. -(2^N) * PAGE_SIZE = allocated buffer size - -MODULE_PARM(cs_debuglevel, "i"); -MODULE_PARM(cs_debugmask, "i"); -cs_debuglevel=N -cs_debugmask=0xMMMMMMMM -where N is a value from 0 (no debug printfs), to 9 (maximum) -0xMMMMMMMM is a debug mask corresponding to the CS_xxx bits (see driver source). - -MODULE_PARM(hercules_egpio_disable, "i"); -hercules_egpio_disable=N -where N is a 0 (enable egpio), or a 1 (disable egpio support) - -MODULE_PARM(initdelay, "i"); -initdelay=N -This value is used to determine the millescond delay during the initialization -code prior to powering up the PLL. On laptops this value can be used to -assist with errors on resume, mostly with IBM laptops. Basically, if the -system is booted under battery power then the mdelay()/udelay() functions fail to -properly delay the required time. Also, if the system is booted under AC power -and then the power removed, the mdelay()/udelay() functions will not delay properly. - -MODULE_PARM(powerdown, "i"); -powerdown=N -where N is 0 (disable any powerdown of the internal blocks) or 1 (enable powerdown) - - -MODULE_PARM(external_amp, "i"); -external_amp=1 -if N is set to 1, then force enabling the EAPD support in the primary AC97 codec. -override the detection logic and force the external amp bit in the AC97 0x26 register -to be reset (0). EAPD should be 0 for powerup, and 1 for powerdown. The VTB Santa Cruz -card has inverted logic, so there is a special function for these cards. - -MODULE_PARM(thinkpad, "i"); -thinkpad=1 -if N is set to 1, then force enabling the clkrun functionality. -Currently, when the part is being used, then clkrun is disabled for the entire system, -but re-enabled when the driver is released or there is no outstanding open count. - diff -Nru a/Documentation/sound/es1370 b/Documentation/sound/es1370 --- a/Documentation/sound/es1370 Thu Mar 7 18:17:40 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,70 +0,0 @@ -/proc/sound, /dev/sndstat -------------------------- - -/proc/sound and /dev/sndstat is not supported by the -driver. To find out whether the driver succeeded loading, -check the kernel log (dmesg). - - -ALaw/uLaw sample formats ------------------------- - -This driver does not support the ALaw/uLaw sample formats. -ALaw is the default mode when opening a sound device -using OSS/Free. The reason for the lack of support is -that the hardware does not support these formats, and adding -conversion routines to the kernel would lead to very ugly -code in the presence of the mmap interface to the driver. -And since xquake uses mmap, mmap is considered important :-) -and no sane application uses ALaw/uLaw these days anyway. -In short, playing a Sun .au file as follows: - -cat my_file.au > /dev/dsp - -does not work. Instead, you may use the play script from -Chris Bagwell's sox-12.14 package (available from the URL -below) to play many different audio file formats. -The script automatically determines the audio format -and does do audio conversions if necessary. -http://home.sprynet.com/sprynet/cbagwell/projects.html - - -Blocking vs. nonblocking IO ---------------------------- - -Unlike OSS/Free this driver honours the O_NONBLOCK file flag -not only during open, but also during read and write. -This is an effort to make the sound driver interface more -regular. Timidity has problems with this; a patch -is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. -(Timidity patched will also run on OSS/Free). - - -MIDI UART ---------- - -The driver supports a simple MIDI UART interface, with -no ioctl's supported. - - -MIDI synthesizer ----------------- - -This soundcard does not have any hardware MIDI synthesizer; -MIDI synthesis has to be done in software. To allow this -the driver/soundcard supports two PCM (/dev/dsp) interfaces. -The second one goes to the mixer "synth" setting and supports -only a limited set of sampling rates (44100, 22050, 11025, 5512). -By setting lineout to 1 on the driver command line -(eg. insmod es1370 lineout=1) it is even possible on some -cards to convert the LINEIN jack into a second LINEOUT jack, thus -making it possible to output four independent audio channels! - -There is a freely available software package that allows -MIDI file playback on this soundcard called Timidity. -See http://www.cgs.fi/~tt/timidity/. - - - -Thomas Sailer -t.sailer@alumni.ethz.ch diff -Nru a/Documentation/sound/es1371 b/Documentation/sound/es1371 --- a/Documentation/sound/es1371 Thu Mar 7 18:17:41 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,64 +0,0 @@ -/proc/sound, /dev/sndstat -------------------------- - -/proc/sound and /dev/sndstat is not supported by the -driver. To find out whether the driver succeeded loading, -check the kernel log (dmesg). - - -ALaw/uLaw sample formats ------------------------- - -This driver does not support the ALaw/uLaw sample formats. -ALaw is the default mode when opening a sound device -using OSS/Free. The reason for the lack of support is -that the hardware does not support these formats, and adding -conversion routines to the kernel would lead to very ugly -code in the presence of the mmap interface to the driver. -And since xquake uses mmap, mmap is considered important :-) -and no sane application uses ALaw/uLaw these days anyway. -In short, playing a Sun .au file as follows: - -cat my_file.au > /dev/dsp - -does not work. Instead, you may use the play script from -Chris Bagwell's sox-12.14 package (available from the URL -below) to play many different audio file formats. -The script automatically determines the audio format -and does do audio conversions if necessary. -http://home.sprynet.com/sprynet/cbagwell/projects.html - - -Blocking vs. nonblocking IO ---------------------------- - -Unlike OSS/Free this driver honours the O_NONBLOCK file flag -not only during open, but also during read and write. -This is an effort to make the sound driver interface more -regular. Timidity has problems with this; a patch -is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. -(Timidity patched will also run on OSS/Free). - - -MIDI UART ---------- - -The driver supports a simple MIDI UART interface, with -no ioctl's supported. - - -MIDI synthesizer ----------------- - -This soundcard does not have any hardware MIDI synthesizer; -MIDI synthesis has to be done in software. To allow this -the driver/soundcard supports two PCM (/dev/dsp) interfaces. - -There is a freely available software package that allows -MIDI file playback on this soundcard called Timidity. -See http://www.cgs.fi/~tt/timidity/. - - - -Thomas Sailer -t.sailer@alumni.ethz.ch diff -Nru a/Documentation/sound/mwave b/Documentation/sound/mwave --- a/Documentation/sound/mwave Thu Mar 7 18:17:42 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,185 +0,0 @@ - How to try to survive an IBM Mwave under Linux SB drivers - - -+ IBM have now released documentation of sorts and Torsten is busy - trying to make the Mwave work. This is not however a trivial task. - ----------------------------------------------------------------------------- - -OK, first thing - the IRQ problem IS a problem, whether the test is bypassed or -not. It is NOT a Linux problem, but an MWAVE problem that is fixed with the -latest MWAVE patches. So, in other words, don't bypass the test for MWAVES! - -I have Windows 95 on /dev/hda1, swap on /dev/hda2, and Red Hat 5 on /dev/hda3. - -The steps, then: - - Boot to Linux. - Mount Windows 95 file system (assume mount point = /dos95). - mkdir /dos95/linux - mkdir /dos95/linux/boot - mkdir /dos95/linux/boot/parms - - Copy the kernel, any initrd image, and loadlin to /dos95/linux/boot/. - - Reboot to Windows 95. - - Edit C:/msdos.sys and add or change the following: - - Logo=0 - BootGUI=0 - - Note that msdos.sys is a text file but it needs to be made 'unhidden', - readable and writable before it can be edited. This can be done with - DOS' "attrib" command. - - Edit config.sys to have multiple config menus. I have one for windows 95 and - five for Linux, like this: ------------- -[menu] -menuitem=W95, Windows 95 -menuitem=LINTP, Linux - ThinkPad -menuitem=LINTP3, Linux - ThinkPad Console -menuitem=LINDOC, Linux - Docked -menuitem=LINDOC3, Linux - Docked Console -menuitem=LIN1, Linux - Single User Mode -REM menudefault=W95,10 - -[W95] - -[LINTP] - -[LINDOC] - -[LINTP3] - -[LINDOC3] - -[LIN1] - -[COMMON] -FILES=30 -REM Please read README.TXT in C:\MWW subdirectory before changing the DOS= statement. -DOS=HIGH,UMB -DEVICE=C:\MWW\MANAGER\MWD50430.EXE -SHELL=c:\command.com /e:2048 -------------------- - -The important things are the SHELL and DEVICE statements. - - Then change autoexec.bat. Basically everything in there originally should be - done ONLY when Windows 95 is booted. Then you add new things specifically - for Linux. Mine is as follows - ---------------- -@ECHO OFF -if "%CONFIG%" == "W95" goto W95 - -REM -REM Linux stuff -REM -SET MWPATH=C:\MWW\DLL;C:\MWW\MWGAMES;C:\MWW\DSP -SET BLASTER=A220 I5 D1 -SET MWROOT=C:\MWW -SET LIBPATH=C:\MWW\DLL -SET PATH=C:\WINDOWS;C:\MWW\DLL; -CALL MWAVE START NOSHOW -c:\linux\boot\loadlin.exe @c:\linux\boot\parms\%CONFIG%.par - -:W95 -REM -REM Windows 95 stuff -REM -c:\toolkit\guard -SET MSINPUT=C:\MSINPUT -SET MWPATH=C:\MWW\DLL;C:\MWW\MWGAMES;C:\MWW\DSP -REM The following is used by DOS games to recognize Sound Blaster hardware. -REM If hardware settings are changed, please change this line as well. -REM See the Mwave README file for instructions. -SET BLASTER=A220 I5 D1 -SET MWROOT=C:\MWW -SET LIBPATH=C:\MWW\DLL -SET PATH=C:\WINDOWS;C:\WINDOWS\COMMAND;E:\ORAWIN95\BIN;f:\msdev\bin;e:\v30\bin.dbg;v:\devt\v30\bin;c:\JavaSDK\Bin;C:\MWW\DLL; -SET INCLUDE=f:\MSDEV\INCLUDE;F:\MSDEV\MFC\INCLUDE -SET LIB=F:\MSDEV\LIB;F:\MSDEV\MFC\LIB -win - ------------------------- - -Now build a file in c:\linux\boot\parms for each Linux config that you have. - -For example, my LINDOC3 config is for a docked Thinkpad at runlevel 3 with no -initrd image, and has a parameter file named LINDOC3.PAR in c:\linux\boot\parms: - ------------------------ -# LOADLIN @param_file image=other_image root=/dev/other -# -# Linux Console in docking station -# -c:\linux\boot\zImage.krn # First value must be filename of Linux kernel. -root=/dev/hda3 # device which gets mounted as root FS -ro # Other kernel arguments go here. -apm=off -doc=yes -3 ------------------------ - -The doc=yes parameter is an environment variable used by my init scripts, not -a kernel argument. - -However, the apm=off parameter IS a kernel argument! APM, at least in my setup, -causes the kernel to crash when loaded via loadlin (but NOT when loaded via -LILO). The APM stuff COULD be forced out of the kernel via the kernel compile -options. Instead, I got an unofficial patch to the APM drivers that allows them -to be dynamically deactivated via kernel arguments. Whatever you chose to -document, APM, it seems, MUST be off for setups like mine. - -Now make sure C:\MWW\MWCONFIG.REF looks like this: - ----------------------- -[NativeDOS] -Default=SB1.5 -SBInputSource=CD -SYNTH=FM -QSound=OFF -Reverb=OFF -Chorus=OFF -ReverbDepth=5 -ChorusDepth=5 -SBInputVolume=5 -SBMainVolume=10 -SBWaveVolume=10 -SBSynthVolume=10 -WaveTableVolume=10 -AudioPowerDriver=ON - -[FastCFG] -Show=No -HideOption=Off ------------------------------ - -OR the Default= line COULD be - -Default=SBPRO - -Reboot to Windows 95 and choose Linux. When booted, use sndconfig to configure -the sound modules and voilà - ThinkPad sound with Linux. - -Now the gotchas - you can either have CD sound OR Mixers but not both. That's a -problem with the SB1.5 (CD sound) or SBPRO (Mixers) settings. No one knows why -this is! - -For some reason MPEG3 files, when played through mpg123, sound like they -are playing at 1/8th speed - not very useful! If you have ANY insight -on why this second thing might be happening, I would be grateful. - -=========================================================== - _/ _/_/_/_/ - _/_/ _/_/ _/ - _/ _/_/ _/_/_/_/ Martin John Bartlett - _/ _/ _/ _/ (martin@nitram.demon.co.uk) -_/ _/_/_/_/ - _/ -_/ _/ - _/_/ -=========================================================== diff -Nru a/Documentation/sound/oss/AD1816 b/Documentation/sound/oss/AD1816 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/AD1816 Thu Mar 7 18:17:39 2002 @@ -0,0 +1,84 @@ +Documentation for the AD1816(A) sound driver +============================================ + +Installation: +------------- + +To get your AD1816(A) based sound card work, you'll have to enable support for +experimental code ("Prompt for development and/or incomplete code/drivers") +and isapnp ("Plug and Play support", "ISA Plug and Play support"). Enable +"Sound card support", "OSS modules support" and "Support for AD1816(A) based +cards (EXPERIMENTAL)" in the sound configuration menu, too. Now build, install +and reboot the new kernel as usual. + +Features: +--------- + +List of features supported by this driver: +- full-duplex support +- supported audio formats: unsigned 8bit, signed 16bit little endian, + signed 16bit big endian, µ-law, A-law +- supported channels: mono and stereo +- supported recording sources: Master, CD, Line, Line1, Line2, Mic +- supports phat 3d stereo circuit (Line 3) + + +Supported cards: +---------------- + +The following cards are known to work with this driver: +- Terratec Base 1 +- Terratec Base 64 +- HP Kayak +- Acer FX-3D +- SY-1816 +- Highscreen Sound-Boostar 32 Wave 3D +- Highscreen Sound-Boostar 16 +- AVM Apex Pro card +- (Aztech SC-16 3D) +- (Newcom SC-16 3D) +- (Terratec EWS64S) + +Cards listed in brackets are not supported reliable. If you have such a card +you should add the extra parameter: + options=1 +when loading the ad1816 module via modprobe. + + +Troubleshooting: +---------------- + +First of all you should check, if the driver has been loaded +properly. + +If loading of the driver succeeds, but playback/capture fails, check +if you used the correct values for irq, dma and dma2 when loading the module. +If one of them is wrong you usually get the following error message: + +Nov 6 17:06:13 tek01 kernel: Sound: DMA (output) timed out - IRQ/DRQ config error? + +If playback/capture is too fast or to slow, you should have a look at +the clock chip of your sound card. The AD1816 was designed for a 33MHz +oscillator, however most sound card manufacturer use slightly +different oscillators as they are cheaper than 33MHz oscillators. If +you have such a card you have to adjust the ad1816_clockfreq parameter +above. For example: For a card using a 32.875MHz oscillator use +ad1816_clockfreq=32875 instead of ad1816_clockfreq=33000. + + +Updates, bugfixes and bugreports: +-------------------------------- + +As the driver is still experimental and under development, you should +watch out for updates. Updates of the driver are available on the +Internet from one of my home pages: + http://www.student.informatik.tu-darmstadt.de/~tek/projects/linux.html +or: + http://www.tu-darmstadt.de/~tek01/projects/linux.html + +Bugreports, bugfixes and related questions should be sent via E-Mail to: + tek@rbg.informatik.tu-darmstadt.de + +Thorsten Knabe +Christoph Hellwig + Last modified: 2000/09/20 diff -Nru a/Documentation/sound/oss/ALS b/Documentation/sound/oss/ALS --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/ALS Thu Mar 7 18:17:40 2002 @@ -0,0 +1,66 @@ +ALS-007/ALS-100/ALS-200 based sound cards +========================================= + +Support for sound cards based around the Avance Logic +ALS-007/ALS-100/ALS-200 chip is included. These chips are a single +chip PnP sound solution which is mostly hardware compatible with the +Sound Blaster 16 card, with most differences occurring in the use of +the mixer registers. For this reason the ALS code is integrated +as part of the Sound Blaster 16 driver (adding only 800 bytes to the +SB16 driver). + +To use an ALS sound card under Linux, enable the following options as +modules in the sound configuration section of the kernel config: + - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support + - FM synthesizer (YM3812/OPL-3) support + - standalone MPU401 support may be required for some cards; for the + ALS-007, when using isapnptools, it is required +Since the ALS-007/100/200 are PnP cards, ISAPnP support should probably be +compiled in. If kernel level PnP support is not included, isapnptools will +be required to configure the card before the sound modules are loaded. + +When using kernel level ISAPnP, the kernel should correctly identify and +configure all resources required by the card when the "sb" module is +inserted. Note that the ALS-007 does not have a 16 bit DMA channel and that +the MPU401 interface on this card uses a different interrupt to the audio +section. This should all be correctly configured by the kernel; if problems +with the MPU401 interface surface, try using the standalone MPU401 module, +passing "0" as the "sb" module's "mpu_io" module parameter to prevent the +soundblaster driver attempting to register the MPU401 itself. The onboard +synth device can be accessed using the "opl3" module. + +If isapnptools is used to wake up the sound card (as in 2.2.x), the settings +of the card's resources should be passed to the kernel modules ("sb", "opl3" +and "mpu401") using the module parameters. When configuring an ALS-007, be +sure to specify different IRQs for the audio and MPU401 sections - this card +requires they be different. For "sb", "io", "irq" and "dma" should be set +to the same values used to configure the audio section of the card with +isapnp. "dma16" should be explicitly set to "-1" for an ALS-007 since this +card does not have a 16 bit dma channel; if not specified the kernel will +default to using channel 5 anyway which will cause audio not to work. +"mpu_io" should be set to 0. The "io" parameter of the "opl3" module should +also agree with the setting used by isapnp. To get the MPU401 interface +working on an ALS-007 card, the "mpu401" module will be required since this +card uses separate IRQs for the audio and MPU401 sections and there is no +parameter available to pass a different IRQ to the "sb" driver (whose +inbuilt MPU401 driver would otherwise be fine). Insert the mpu401 module +passing appropriate values using the "io" and "irq" parameters. + +The resulting sound driver will provide the following capabilities: + - 8 and 16 bit audio playback + - 8 and 16 bit audio recording + - Software selection of record source (line in, CD, FM, mic, master) + - Record and playback of midi data via the external MPU-401 + - Playback of midi data using inbuilt FM synthesizer + - Control of the ALS-007 mixer via any OSS-compatible mixer programs. + Controls available are Master (L&R), Line in (L&R), CD (L&R), + DSP/PCM/audio out (L&R), FM (L&R) and Mic in (mono). + +Jonathan Woithe +jwoithe@physics.adelaide.edu.au +30 March 1998 + +Modified 2000-02-26 by Dave Forrest, drf5n@virginia.edu to add ALS100/ALS200 +Modified 2000-04-10 by Paul Laufer, pelaufer@csupomona.edu to add ISAPnP info. +Modified 2000-11-19 by Jonathan Woithe, jwoithe@physics.adelaide.edu.au + - updated information for kernel 2.4.x. diff -Nru a/Documentation/sound/oss/AWE32 b/Documentation/sound/oss/AWE32 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/AWE32 Thu Mar 7 18:17:38 2002 @@ -0,0 +1,76 @@ + Installing and using Creative AWE midi sound under Linux. + +This documentation is devoted to the Creative Sound Blaster AWE32, AWE64 and +SB32. + +1) Make sure you have an ORIGINAL Creative SB32, AWE32 or AWE64 card. This + is important, because the driver works only with real Creative cards. + +2) The first thing you need to do is re-compile your kernel with support for + your sound card. Run your favourite tool to configure the kernel and when + you get to the "Sound" menu you should enable support for the following: + + Sound card support, + OSS sound modules, + 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support, + AWE32 synth + + If your card is "Plug and Play" you will also need to enable these two + options, found under the "Plug and Play configuration" menu: + + Plug and Play support + ISA Plug and Play support + + Now compile and install the kernel in normal fashion. If you don't know + how to do this you can find instructions for this in the README file + located in the root directory of the kernel source. + +3) Before you can start playing midi files you will have to load a sound + bank file. The utility needed for doing this is called "sfxload", and it + is one of the utilities found in a package called "awesfx". If this + package is not available in your distribution you can download the AWE + snapshot from Creative Labs Open Source website: + + http://www.opensource.creative.com/snapshot.html + + Once you have unpacked the AWE snapshot you will see a "awesfx" + directory. Follow the instructions in awesfx/docs/INSTALL to install the + utilities in this package. After doing this, sfxload should be installed + as: + + /usr/local/bin/sfxload + + To enable AWE general midi synthesis you should also get the sound bank + file for general midi from: + + http://members.xoom.com/yar/synthgm.sbk.gz + + Copy it to a directory of your choice, and unpack it there. + +4) Edit /etc/modules.conf, and insert the following lines at the end of the + file: + + alias sound-slot-0 sb + alias sound-service-0-1 awe_wave + post-install awe_wave /usr/local/bin/sfxload PATH_TO_SOUND_BANK_FILE + + You will of course have to change "PATH_TO_SOUND_BANK_FILE" to the full + path of of the sound bank file. That will enable the Sound Blaster and AWE + wave synthesis. To play midi files you should get one of these programs if + you don't already have them: + + Playmidi: http://playmidi.openprojects.net + + AWEMidi Player (drvmidi) Included in the previously mentioned AWE + snapshot. + + You will probably have to pass the "-e" switch to playmidi to have it use + your midi device. drvmidi should work without switches. + + If something goes wrong please e-mail me. All comments and suggestions are + welcome. + + Yaroslav Rosomakho (alons55@dialup.ptt.ru) + http://www.yar.opennet.ru + +Last Updated: Feb 3 2001 diff -Nru a/Documentation/sound/oss/AudioExcelDSP16 b/Documentation/sound/oss/AudioExcelDSP16 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/AudioExcelDSP16 Thu Mar 7 18:17:36 2002 @@ -0,0 +1,101 @@ +Driver +------ + +Informations about Audio Excel DSP 16 driver can be found in the source +file lowlevel/aedsp16.c +Please, read the head of the source before using it. It contain useful +informations. + +Configuration +------------- + +The Audio Excel configuration, is now done with the standard Linux setup. +You have to configure the sound card (Sound Blaster or Microsoft Sound System) +and, if you want it, the Roland MPU-401 (do not use the Sound Blaster MPU-401, +SB-MPU401) in the main driver menu. Activate the lowlevel drivers then select +the Audio Excel hardware that you want to initialize. Check the IRQ/DMA/MIRQ +of the Audio Excel initialization: it must be the same as the SBPRO (or MSS) +setup. If the parameters are different, correct it. +I you own a Gallant's audio card based on SC-6600, activate the SC-6600 support. +If you want to change the configuration of the sound board, be sure to +check off all the configuration items before re-configure it. + +Module parameters +----------------- +To use this driver as a module, you must configure some module parameters, to +set up I/O addresses, IRQ lines and DMA channels. Some parameters are +mandatory while some others are optional. Here a list of parameters you can +use with this module: + +Name Description +==== =========== +MANDATORY +io I/O base address (0x220 or 0x240) +irq irq line (5, 7, 9, 10 or 11) +dma dma channel (0, 1 or 3) + +OPTIONAL +mss_base I/O base address for activate MSS mode (default SBPRO) + (0x530 or 0xE80) +mpu_base I/O base address for activate MPU-401 mode + (0x300, 0x310, 0x320 or 0x330) +mpu_irq MPU-401 irq line (5, 7, 9, 10 or 0) + +The /etc/modules.conf will have lines like this: + +options opl3 io=0x388 +options ad1848 io=0x530 irq=11 dma=3 +options aedsp16 io=0x220 irq=11 dma=3 mss_base=0x530 + +Where the aedsp16 options are the options for this driver while opl3 and +ad1848 are the corresponding options for the MSS and OPL3 modules. + +Loading MSS and OPL3 needs to pre load the aedsp16 module to set up correctly +the sound card. Installation dependencies must be written in the modules.conf +file: + +pre-install ad1848 modprobe aedsp16 +pre-install opl3 modprobe aedsp16 + +Then you must load the sound modules stack in this order: +sound -> aedsp16 -> [ ad1848, opl3 ] + +With the above configuration, loading ad1848 or opl3 modules, will +automatically load all the sound stack. + +Sound cards supported +--------------------- +This driver supports the SC-6000 and SC-6600 based Gallant's sound card. +It don't support the Audio Excel DSP 16 III (try the SC-6600 code). +I'm working on the III version of the card: if someone have useful +informations about it, please let me know. +For all the non-supported audio cards, you have to boot MS-DOS (or WIN95) +activating the audio card with the MS-DOS device driver, then you have to +-- and boot Linux. +Follow these steps: + +1) Compile Linux kernel with standard sound driver, using the emulation + you want, with the parameters of your audio card, + e.g. Microsoft Sound System irq10 dma3 +2) Install your new kernel as the default boot kernel. +3) Boot MS-DOS and configure the audio card with the boot time device + driver, for MSS irq10 dma3 in our example. +4) -- and boot Linux. This will maintain the DOS configuration + and will boot the new kernel with sound driver. The sound driver will find + the audio card and will recognize and attach it. + +Reports on User successes +------------------------- + +> Date: Mon, 29 Jul 1996 08:35:40 +0100 +> From: Mr S J Greenaway +> To: riccardo@cdc8g5.cdc.polimi.it (Riccardo Facchetti) +> Subject: Re: Audio Excel DSP 16 initialization code +> +> Just to let you know got my Audio Excel (emulating a MSS) working +> with my original SB16, thanks for the driver! + + +Last revised: 20 August 1998 +Riccardo Facchetti +fizban@tin.it diff -Nru a/Documentation/sound/oss/CMI8330 b/Documentation/sound/oss/CMI8330 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/CMI8330 Thu Mar 7 18:17:38 2002 @@ -0,0 +1,153 @@ +Documentation for CMI 8330 (SoundPRO) +------------------------------------- +Alessandro Zummo + +( Be sure to read Documentation/sound/SoundPro too ) + + +This adapter is now directly supported by the sb driver. + + The only thing you have to do is to compile the kernel sound +support as a module and to enable kernel ISAPnP support, +as shown below. + + +CONFIG_SOUND=m +CONFIG_SOUND_SB=m + +CONFIG_PNP=y +CONFIG_ISAPNP=y + + +and optionally: + + +CONFIG_SOUND_MPU401=m + + for MPU401 support. + + +(I suggest you to use "make menuconfig" or "make xconfig" + for a more comfortable configuration editing) + + + +Then you can do + + modprobe sb + +and everything will be (hopefully) configured. + +You should get something similar in syslog: + +sb: CMI8330 detected. +sb: CMI8330 sb base located at 0x220 +sb: CMI8330 mpu base located at 0x330 +sb: CMI8330 mail reports to Alessandro Zummo +sb: ISAPnP reports CMI 8330 SoundPRO at i/o 0x220, irq 7, dma 1,5 + + + + +The old documentation file follows for reference +purposes. + + +How to enable CMI 8330 (SOUNDPRO) soundchip on Linux +------------------------------------------ +Stefan Laudat + +[Note: The CMI 8338 is unrelated and is supported by cmpci.o] + + + In order to use CMI8330 under Linux you just have to use a proper isapnp.conf, a good isapnp and a little bit of patience. I use isapnp 1.17, but +you may get a better one I guess at http://www.roestock.demon.co.uk/isapnptools/. + + Of course you will have to compile kernel sound support as module, as shown below: + +CONFIG_SOUND=m +CONFIG_SOUND_OSS=m +CONFIG_SOUND_SB=m +CONFIG_SOUND_ADLIB=m +CONFIG_SOUND_MPU401=m +# Mikro$chaft sound system (kinda useful here ;)) +CONFIG_SOUND_MSS=m + + The /etc/isapnp.conf file will be: + + + + +(READPORT 0x0203) +(ISOLATE PRESERVE) +(IDENTIFY *) +(VERBOSITY 2) +(CONFLICT (IO FATAL)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # or WARNING +(VERIFYLD N) + + +# WSS + +(CONFIGURE CMI0001/16777472 (LD 0 +(IO 0 (SIZE 8) (BASE 0x0530)) +(IO 1 (SIZE 8) (BASE 0x0388)) +(INT 0 (IRQ 7 (MODE +E))) +(DMA 0 (CHANNEL 0)) +(NAME "CMI0001/16777472[0]{CMI8330/C3D Audio Adapter}") +(ACT Y) +)) + +# MPU + +(CONFIGURE CMI0001/16777472 (LD 1 +(IO 0 (SIZE 2) (BASE 0x0330)) +(INT 0 (IRQ 11 (MODE +E))) +(NAME "CMI0001/16777472[1]{CMI8330/C3D Audio Adapter}") +(ACT Y) +)) + +# Joystick + +(CONFIGURE CMI0001/16777472 (LD 2 +(IO 0 (SIZE 8) (BASE 0x0200)) +(NAME "CMI0001/16777472[2]{CMI8330/C3D Audio Adapter}") +(ACT Y) +)) + +# SoundBlaster + +(CONFIGURE CMI0001/16777472 (LD 3 +(IO 0 (SIZE 16) (BASE 0x0220)) +(INT 0 (IRQ 5 (MODE +E))) +(DMA 0 (CHANNEL 1)) +(DMA 1 (CHANNEL 5)) +(NAME "CMI0001/16777472[3]{CMI8330/C3D Audio Adapter}") +(ACT Y) +)) + + +(WAITFORKEY) + + + + The module sequence is trivial: + +/sbin/insmod soundcore +/sbin/insmod sound +/sbin/insmod uart401 +# insert this first +/sbin/insmod ad1848 io=0x530 irq=7 dma=0 soundpro=1 +# The sb module is an alternative to the ad1848 (Microsoft Sound System) +# Anyhow, this is full duplex and has MIDI +/sbin/insmod sb io=0x220 dma=1 dma16=5 irq=5 mpu_io=0x330 + + + +Alma Chao suggests the following /etc/modules.conf: + +alias sound ad1848 +alias synth0 opl3 +options ad1848 io=0x530 irq=7 dma=0 soundpro=1 +options opl3 io=0x388 + + diff -Nru a/Documentation/sound/oss/CMI8338 b/Documentation/sound/oss/CMI8338 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/CMI8338 Thu Mar 7 18:17:43 2002 @@ -0,0 +1,85 @@ +Audio driver for CM8338/CM8738 chips by Chen-Li Tien + + +HARDWARE SUPPORTED +================================================================================ +C-Media CMI8338 +C-Media CMI8738 +On-board C-Media chips + + +STEPS TO BUILD DRIVER +================================================================================ + + 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. + + 3. Change directory to /usr/src/linux + + 4. Config cm8338 driver by 'make menuconfig', 'make config' or + 'make xconfig' 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' + + 6. Compile the kernel if necessary. + + 7. Compile the modules by 'make modules'. + + 8. Install the modules by 'make modules_install' + + +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: + + 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. + + 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. + + mpuio: I/O ports base for MPU-401, 0 if disabled. + fmio: 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. + 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. + joystick: Enable joystick. You will need to install Linux joystick + driver. + diff -Nru a/Documentation/sound/oss/CS4232 b/Documentation/sound/oss/CS4232 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/CS4232 Thu Mar 7 18:17:40 2002 @@ -0,0 +1,23 @@ +To configure the Crystal CS423x sound chip and activate its DSP functions, +modules may be loaded in this order: + + modprobe sound + insmod ad1848 + insmod uart401 + insmod cs4232 io=* irq=* dma=* dma2=* + +This is the meaning of the parameters: + + io--I/O address of the Windows Sound System (normally 0x534) + irq--IRQ of this device + dma and dma2--DMA channels (DMA2 may be 0) + +On some cards, the board attempts to do non-PnP setup, and fails. If you +have problems, use Linux' PnP facilities. + +To get MIDI facilities add + + insmod opl3 io=* + +where "io" is the I/O address of the OPL3 synthesizer. This will be shown +in /proc/sys/pnp and is normally 0x388. diff -Nru a/Documentation/sound/oss/ChangeLog.awe b/Documentation/sound/oss/ChangeLog.awe --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/ChangeLog.awe Thu Mar 7 18:17:45 2002 @@ -0,0 +1,230 @@ +ver.0.4.3p4 + - Bug fix for invalid memory detection when initialized twice + - Add sample sharing function - works together with awesfx-0.4.3p3 + - Add AWE_PROBE_DATA for probing sample id + +ver.0.4.3p3 + - Replace memset to MEMSET (for FreeBSD) + - Add PAN_EXCHANGE switch + +ver.0.4.3p2 + - MIDI emulation device is added + - Controls volume and filter targets + - Include chorus/reverb/equalizer values in MISC_MODE + +ver.0.4.3p1 + - Change the volume calculation method + - Support for Tom Lees' PnP driver (v0.3) + +ver.0.4.2d + - Support for OSS/Free 3.8 on 2.0 kernels. + - Support for Linux PnP driver + - Support for module (for recent 2.1 kernels and RH5.0) + - Support for FreeBSD-3.0 system + +ver.0.4.2c + - Add a mode to enable drum channel toggle via bank number + change. + +ver.0.4.2b + - Clear voice position after note on + - Change nrvoices according to the current playing mode + +ver.0.4.2a + - Fix a bug in pitch calculation with scale parameter + - Change default chorus & reverb modes + +ver.0.4.2 + - Use indirect voice allocation mode; used as default mode + - Add preset mapping + - Free buffers when resetting samples + - Set default preset/bank/drumset as variable + - Fix a bug in exclusive note-off + - Add channel reset control macro + - Change modwheel sensitivity as variable + - Add lock option in open_patch + - Add channel priority mode macro, and disable it as default + - Add unset effect macro + - Add user defined chorus/reverb modes + - Do not initialize effect parameters when allocating voices + - Accept realtime filter-Q parameter change + - Check value range of set/add effects + - Change drum flags automatically when receiving bank #128 + +ver.0.4.1 development versions + +ver.0.4.0c + - Fix kernel oops when setting AWE_FX_ATTEN + +ver.0.4.0b + - Do not kill_note in start_note when velocity is zero + +ver.0.4.0a + - Fix a bug in channel pressure effects + +ver.0.4.0 + - Support dynamic buffer allocation + - Add functions to open/close/unload a patch + - Change from pointer to integer index in voice/sample lists + - Support for Linux/Alpha-AXP + - Fix for FreeBSD + - Add sostenuto control + - Add midi channel priority + - Fix a bug in all notes off control + - Use AWE_DEFAULT_MEMSIZE always if defined + - Fix a bug in awe_reset causes seg fault when no DRAM onboard + - Use awe_mem_start variable instead of constant + +ver.0.3.3c + - Fix IOCTL_TO_USER for OSS-3.8 (on Linux-2.1.25) + - Fix i/o macros for mixer controls + +ver.0.3.3b + - Fix version number in awe_version.h + - Fix a small bug in noteoff/release all + +ver.0.3.3a + - Fix all notes/sounds off + - Add layer effect control + - Add misc mode controls; realtime pan, version number, etc. + - Move gus bank control in misc mode control + - Modify awe_operations for OSS3.8b5 + - Fix installation script + +ver.0.3.3 + - Add bass/treble control in Emu8000 chip + - Add mixer device + - Fix sustain on to value 127 + +ver.0.3.2 + - Refuse linux-2.0.0 at installation + - Move awe_voice.h to /usr/include/linux + +ver.0.3.1b (not released) + - Rewrite chorus/reverb mode change functions + - Rewrite awe_detect & awe_check_dram routines + +ver.0.3.1a + - Fix a bug to reset voice counter in awe_reset + - Fix voice balance on GUS mode + - Make symlink on /usr/include/asm in install script + +ver.0.3.1 + - Remove zero size arrays from awe_voice.h + - Fix init_fm routine + - Remove all samples except primary samples in REMOVE_LAST_SAMPLES + +ver.0.3.0a + - Add AWE_NOTEOFF_ALL control + - Remove AWE_INIT_ATTEN control + +ver.0.3.0 + - Fix decay time table + - Add exclusive sounds mode + - Add capability to get current status + +ver.0.2.99e + - Add #ifdef for all sounds/notes off controls. + - Fix bugs on searching the default drumset/preset. + - Fix usslite patch to modify the default Config.in. + +ver.0.2.99d + - Fix bugs of attack/hold parameters + - Fix attack & decay time table + +ver.0.2.99c + - Change volume control messages (main & expression volume) + to accesspt normal MIDI parameters in channel mode. + - Use channel mode in SEQ2 controls. + +ver.0.2.99b + - #ifdef patch manager functions (for OSS-3.7) + +ver.0.2.99a + - Fix sustain bug + +ver.0.2.99 (0.3 beta) + - Support multiple instruments + +ver.0.2.0c + - Add copyright notice + - FreeBSD 2.2-ALPHA integration + +ver.0.2.0b + - Remove buffered reading appended in v0.2.0a + - Remove SMAxW register check on writing + - Support Linux 2.1.x kernel + - Rewrite installation script + +ver.0.2.0a + - Define SEQUENCER_C for tuning.h for FreeBSD system + - Improvement of sample loading speed + - Fix installation script + - Add PnP driver functions for ISA PnP driver support + +ver.0.2.0 + - Includes FreeBSD port + - Can load GUS compatible patches + - Change values of hardware control parameters for compatibility + with GUS driver + - Accept 8bit or unsigned wave data + - Accept no blank loop data + - Add sample mode flags in sample_info + +ver.0.1.6 + - Add voice effects control + - Fix awe_voice.h for word alignment + +ver.0.1.5c + - Fix FM(OPL) playback problem + +ver.0.1.5b + - Fix pitch calculation for fixed midi key + +ver.0.1.5a + - Fix bugs in removing samples from linked list. + +ver.0.1.5 + - Add checksum verification for sample uploading + (not compatible from older sample_info structure) + - Fix sample offset pointers to (actual value - 1) + - Add sequencer command to initialize awe32 + +ver.0.1.4c + - Fix card detection and memory check function to avoid system crash + at booting + +ver.0.1.4b + - Add release sustain mode + - Initialize FM each time after loading samples + +ver.0.1.4a + - Fix AWE card detection code + - Correct FM initialize position + - Add non-releasing mode on voice info + +ver.0.1.4 + - Add AWE card and DRAM detection codes + - Add FM initialization code + - Modify volume control + - Remove linear volume mode + - Change memory management; not using malloc dynamically + - Add remove-samples command + - Use internal id implicitly at loading samples + +ver.0.1.3 + - Fix a bug on patch uploading to RAM + +ver.0.1.2 + - Divide to separated packages + - Fix disagreed macro conditions + - Fix unresolved function bugs + - Integrate VoxWare and USS-Lite driver source (awe_voice.c) + and remove awe_card.c + +ver.0.1.1 + - Fix wrong sample numbers in sbktext + - Fix txt2sfx bug + - Fix pan parameter calculation + - Append USS-Lite/Linux2.0 driver + diff -Nru a/Documentation/sound/oss/ChangeLog.multisound b/Documentation/sound/oss/ChangeLog.multisound --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/ChangeLog.multisound Thu Mar 7 18:17:41 2002 @@ -0,0 +1,213 @@ +1998-12-04 Andrew T. Veliath + + * Update version to 0.8.2.2 + + * Add msndreset program to shell archive. + +1998-11-11 Andrew T. Veliath + + * msnd_pinnacle.c (mixer_ioctl): Add a mixer ioctl for + SOUND_MIXER_PRIVATE1 which does a full reset on the card. + (mixer_set): Move line in recording source to input monitor, aux + input level added, some mixer fixes. + +1998-09-10 Andrew Veliath + + * Update version to 0.8.2 + + * Add SNDCTL_DSP_GETOSPACE and SNDCTL_DSP_GETISPACE ioctls. + +1998-09-09 Andrew Veliath + + * Update version to 0.8.1 + + * msnd_pinnacle.c: Fix resetting of default audio parameters. Turn + flush code from dsp_halt into dsp_write_flush, and use that for + SNDCTL_DSP_SYNC. + +1998-09-07 Andrew Veliath + + * Update version to 0.8.0 + + * Provide separate signal parameters for play and record. + + * Cleanups to locking and interrupt handling, change default + fifosize to 128kB. + + * Update version to 0.7.15 + + * Interprocess full-duplex support (ie `cat /dev/dsp > /dev/dsp'). + + * More mutex sections for read and write fifos (read + write locks + added). + +1998-09-05 Andrew Veliath + + * msnd_pinnacle.c: (chk_send_dsp_cmd) Do full DSP reset upon DSP + timeout (when not in interrupt; maintains mixer settings). Fixes + to flushing and IRQ ref counting. Rewrote queuing for smoother + playback and fixed initial playback cutoff problem. + +1998-09-03 Andrew Veliath + + * Replaced packed structure accesses with standard C equivalents. + +1998-09-01 Andrew Veliath + + * msnd_pinnacle.c: Add non-PnP configuration to driver code, which + will facilitate compiled-in operation. + +1998-08-29 Andrew Veliath + + * Update version to 0.7.6 + + * msnd_pinnacle.c (dsp_ioctl): Add DSP_GETFMTS, change SAMPLESIZE + to DSP_SETFMT. + + * Update version to 0.7.5 + + * Create pinnaclecfg.c and turn MultiSound doc into a shell + archive with pinnaclecfg.c included. pinnaclecfg.c can + now fully configure the card in non-PnP mode, including the + joystick and IDE controller. Also add an isapnp conf + example. + + * Reduce DSP reset timeout from 20000 to 100 + +1998-08-06 Andrew Veliath + + * Update version to 0.7.2 + + * After A/D calibration, do an explicit set to the line input, + rather than using set_recsrc + +1998-07-20 Andrew Veliath + + * Update version to 0.7.1 + + * Add more OSS ioctls + +1998-07-19 Andrew Veliath + + * Update doc file + + * Bring back DIGITAL1 with digital parameter to msnd_pinnacle.c + and CONFIG_MSNDPIN_DIGITAL. I'm not sure this actually works, + since I find audio playback goes into a very speeded mode of + operation, however it might be due to a lack of a digital + source, which I don't have to test. + +1998-07-18 Andrew Veliath + + * Update version to 0.7.0 + + * Can now compile with Alan Cox' 2.0.34-modular-sound patch (so + now it requires >= 2.1.106 or 2.0.34-ms) (note for 2.0.34-ms it + is in the Experimental section) + + * More modularization, consolidation, also some MIDI hooks + installed for future MIDI modules + + * Write flush + + * Change default speed, channels, bit size to OSS/Free defaults + +1998-06-02 Andrew Veliath + + * Update version to 0.5b + + * Fix version detection + + * Remove underflow and overflow resets (delay was too long) + + * Replace spinlocked bitops with atomic bit ops + +1998-05-27 Andrew Veliath + + * Update version to 0.5a + + * Better recovery from underflow or overflow conditions + + * Fix a deadlock condition with one thread reading and the other + writing + +1998-05-26 Andrew Veliath + + * Update version to 0.5 + + * Separate reset queue functions for play and record + + * Add delays in dsp_halt + +1998-05-24 Andrew Veliath + + * Add a check for Linux >= 2.1.95 + + * Remove DIGITAL1 input until I figure out how to make it work + + * Add HAVE_DSPCODEH which when not defined will load firmware from + files using mod_firmware_load, then release memory after they + are uploaded (requires reorganized OSS). + +1998-05-22 Andrew Veliath + + * Update version to 0.4c + + * Hopefully fix the mixer volume problem + +1998-05-19 Andrew Veliath + + * Add __initfuncs and __initdatas to reduce resident code size + + * Move bunch of code around, remove some protos + + * Integrate preliminary changes for Alan Cox's OSS reorganization + for non-OSS drivers to coexist with OSS devices on the same + major. To compile standalone, must now define STANDALONE. + +1998-05-16 Andrew Veliath + + * Update version to 0.4b + + * Integrated older card support into a unified driver, tested on a + MultiSound Classic c/o Kendrick Vargas. + +1998-05-15 Andrew Veliath + + * Update version to 0.4 + + * Fix read/write return values + +1998-05-13 Andrew Veliath + + * Update version to 0.3 + + * Stop play gracefully + + * Add busy flag + + * Add major and calibrate_signal module parameters + + * Add ADC calibration + + * Add some OSS compatibility ioctls + + * Add mixer record selection + + * Add O_NONBLOCK support, separate read/write wait queues + + * Add sample bit size ioctl, expanded sample rate ioctl + + * Playback suspension now resumes + + * Use signal_pending after interruptible_sleep_on + + * Add recording, change ints to bit flags + +1998-05-11 Andrew Veliath + + * Update version to 0.2 + + * Add preliminary playback support + + * Use new Turtle Beach DSP code \ No newline at end of file diff -Nru a/Documentation/sound/oss/ESS b/Documentation/sound/oss/ESS --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/ESS Thu Mar 7 18:17:44 2002 @@ -0,0 +1,34 @@ +Documentation for the ESS AudioDrive chips + +In 2.4 kernels the SoundBlaster driver not only tries to detect an ESS chip, it +tries to detect the type of ESS chip too. The correct detection of the chip +doesn't always succeed however, so unless you use the kernel isapnp facilities +(and you chip is pnp capable) the default behaviour is 2.0 behaviour which +means: only detect ES688 and ES1688. + +All ESS chips now have a recording level setting. This is a need-to-have for +people who want to use their ESS for recording sound. + +Every chip that's detected as a later-than-es1688 chip has a 6 bits logarithmic +master volume control. + +Every chip that's detected as a ES1887 now has Full Duplex support. Made a +little testprogram that shows that is works, haven't seen a real program that +needs this however. + +For ESS chips an additional parameter "esstype" can be specified. This controls +the (auto) detection of the ESS chips. It can have 3 kinds of values: + +-1 Act like 2.0 kernels: only detect ES688 or ES1688. +0 Try to auto-detect the chip (may fail for ES1688) +688 The chip will be treated as ES688 +1688 ,, ,, ,, ,, ,, ,, ES1688 +1868 ,, ,, ,, ,, ,, ,, ES1868 +1869 ,, ,, ,, ,, ,, ,, ES1869 +1788 ,, ,, ,, ,, ,, ,, ES1788 +1887 ,, ,, ,, ,, ,, ,, ES1887 +1888 ,, ,, ,, ,, ,, ,, ES1888 + +Because Full Duplex is supported for ES1887 you can specify a second DMA +channel by specifying module parameter dma16. It can be one of: 0, 1, 3 or 5. + diff -Nru a/Documentation/sound/oss/ESS1868 b/Documentation/sound/oss/ESS1868 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/ESS1868 Thu Mar 7 18:17:40 2002 @@ -0,0 +1,55 @@ +Documentation for the ESS1868F AudioDrive PnP sound card + +The ESS1868 sound card is a PnP ESS1688-compatible 16-bit sound card. + +It should be automatically detected by the Linux Kernel isapnp support when you +load the sb.o module. Otherwise you should take care of: + + * The ESS1868 does not allow use of a 16-bit DMA, thus DMA 0, 1, 2, and 3 + may only be used. + + * isapnptools version 1.14 does work with ESS1868. Earlier versions might + not. + + * Sound support MUST be compiled as MODULES, not statically linked + into the kernel. + + +NOTE: this is only needed when not using the kernel isapnp support! + +For configuring the sound card's I/O addresses, IRQ and DMA, here is a +sample copy of the isapnp.conf directives regarding the ESS1868: + +(CONFIGURE ESS1868/-1 (LD 1 +(IO 0 (BASE 0x0220)) +(IO 1 (BASE 0x0388)) +(IO 2 (BASE 0x0330)) +(DMA 0 (CHANNEL 1)) +(INT 0 (IRQ 5 (MODE +E))) +(ACT Y) +)) + +(for a full working isapnp.conf file, remember the +(ISOLATE) +(IDENTIFY *) +at the beginning and the +(WAITFORKEY) +at the end.) + +In this setup, the main card I/O is 0x0220, FM synthesizer is 0x0388, and +the MPU-401 MIDI port is located at 0x0330. IRQ is IRQ 5, DMA is channel 1. + +After configuring the sound card via isapnp, to use the card you must load +the sound modules with the proper I/O information. Here is my setup: + +# ESS1868F AudioDrive initialization + +/sbin/modprobe sound +/sbin/insmod uart401 +/sbin/insmod sb io=0x220 irq=5 dma=1 dma16=-1 +/sbin/insmod mpu401 io=0x330 +/sbin/insmod opl3 io=0x388 +/sbin/insmod v_midi + +opl3 is the FM synthesizer +/sbin/insmod opl3 io=0x388 diff -Nru a/Documentation/sound/oss/INSTALL.awe b/Documentation/sound/oss/INSTALL.awe --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/INSTALL.awe Thu Mar 7 18:17:39 2002 @@ -0,0 +1,134 @@ +================================================================ + INSTALLATION OF AWE32 SOUND DRIVER FOR LINUX + Takashi Iwai +================================================================ + +---------------------------------------------------------------- +* Attention to SB-PnP Card Users + +If you're using PnP cards, the initialization of PnP is required +before loading this driver. You have now three options: + 1. Use isapnptools. + 2. Use in-kernel isapnp support. + 3. Initialize PnP on DOS/Windows, then boot linux by loadlin. +In this document, only the case 1 case is treated. + +---------------------------------------------------------------- +* Installation on Red Hat 5.0 Sound Driver + +Please use install-rh.sh under RedHat5.0 directory. +DO NOT USE install.sh below. +See INSTALL.RH for more details. + +---------------------------------------------------------------- +* Installation/Update by Shell Script + + 1. Become root + + % su + + 2. If you have never configured the kernel tree yet, run make config + once (to make dependencies and symlinks). + + # cd /usr/src/linux + # make xconfig + + 3. Run install.sh script + + # sh ./install.sh + + 4. Configure your kernel + + (for Linux 2.[01].x user) + # cd /usr/src/linux + # make xconfig (or make menuconfig) + + (for Linux 1.2.x user) + # cd /usr/src/linux + # make config + + Answer YES to both "lowlevel drivers" and "AWE32 wave synth" items + in Sound menu. ("lowlevel drivers" will appear only in 2.x + kernel.) + + 5. Make your kernel (and modules), and install them as usual. + + 5a. make kernel image + # make zImage + + 5b. make modules and install them + # make modules && make modules_install + + 5c. If you're using lilo, copy the kernel image and run lilo. + Otherwise, copy the kernel image to suitable directory or + media for your system. + + 6. Reboot the kernel if necessary. + - If you updated only the modules, you don't have to reboot + the system. Just remove the old sound modules here. + in + # rmmod sound.o (linux-2.0 or OSS/Free) + # rmmod awe_wave.o (linux-2.1) + + 7. If your AWE card is a PnP and not initialized yet, you'll have to + do it by isapnp tools. Otherwise, skip to 8. + + This section described only a brief explanation. For more + details, please see the AWE64-Mini-HOWTO or isapnp tools FAQ. + + 7a. If you have no isapnp.conf file, generate it by pnpdump. + Otherwise, skip to 7d. + # pnpdump > /etc/isapnp.conf + + 7b. Edit isapnp.conf file. Comment out the appropriate + lines containing desirable I/O ports, DMA and IRQs. + Don't forget to enable (ACT Y) line. + + 7c. Add two i/o ports (0xA20 and 0xE20) in WaveTable part. + ex) + (CONFIGURE CTL0048/58128 (LD 2 + # ANSI string -->WaveTable<-- + (IO 0 (BASE 0x0620)) + (IO 1 (BASE 0x0A20)) + (IO 2 (BASE 0x0E20)) + (ACT Y) + )) + + 7d. Load the config file. + CAUTION: This will reset all PnP cards! + + # isapnp /etc/isapnp.conf + + 8. Load the sound module (if you configured it as a module): + + for 2.0 kernel or OSS/Free monolithic module: + + # modprobe sound.o + + for 2.1 kernel: + + # modprobe sound + # insmod uart401 + # insmod sb io=0x220 irq=5 dma=1 dma16=5 mpu_io=0x330 + (These values depend on your settings.) + # insmod awe_wave + (Be sure to load awe_wave after sb!) + + See /usr/src/linux/Documentation/sound/AWE32 for + more details. + + 9. (only for obsolete systems) If you don't have /dev/sequencer + device file, make it according to Readme.linux file on + /usr/src/linux/drivers/sound. (Run a shell script included in + that file). <-- This file no longer exists in the recent kernels! + + 10. OK, load your own soundfont file, and enjoy MIDI! + + % sfxload synthgm.sbk + % drvmidi foo.mid + + 11. For more advanced use (eg. dynamic loading, virtual bank and + etc.), please read the awedrv FAQ or the instructions in awesfx + and awemidi packages. + +Good luck! diff -Nru a/Documentation/sound/oss/Introduction b/Documentation/sound/oss/Introduction --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/Introduction Thu Mar 7 18:17:45 2002 @@ -0,0 +1,461 @@ +Introduction Notes on Modular Sound Drivers and Soundcore +Wade Hampton +2/14/2001 + +Purpose: +======== +This document provides some general notes on the modular +sound drivers and their configuration, along with the +support modules sound.o and soundcore.o. + +Note, some of this probably should be added to the Sound-HOWTO! + +Note, soundlow.o was present with 2.2 kernels but is not +required for 2.4.x kernels. References have been removed +to this. + + +Copying: +======== +none + + +History: +======== +0.1.0 11/20/1998 First version, draft +1.0.0 11/1998 Alan Cox changes, incorporation in 2.2.0 + as /usr/src/linux/Documentation/sound/Introduction +1.1.0 6/30/1999 Second version, added notes on making the drivers, + added info on multiple sound cards of similar types,] + added more diagnostics info, added info about esd. + added info on OSS and ALSA. +1.1.1 19991031 Added notes on sound-slot- and sound-service. + (Alan Cox) +1.1.2 20000920 Modified for Kernel 2.4 (Christoph Hellwig) +1.1.3 20010214 Minor notes and corrections (Wade Hampton) + Added examples of sound-slot-0, etc. + + +Modular Sound Drivers: +====================== + +Thanks to the GREAT work by Alan Cox (alan@lxorguk.ukuu.org.uk), + +[And Oleg Drokin, Thomas Sailer, Andrew Veliath and more than a few + others - not to mention Hannu's original code being designed well + enough to cope with that kind of chopping up](Alan) + +the standard Linux kernels support a modular sound driver. From +Alan's comments in linux/drivers/sound/README.FIRST: + + The modular sound driver patches were funded by Red Hat Software + (www.redhat.com). The sound driver here is thus a modified version of + Hannu's code. Please bear that in mind when considering the appropriate + forums for bug reporting. + +The modular sound drivers may be loaded via insmod or modprobe. +To support all the various sound modules, there are two general +support modules that must be loaded first: + + soundcore.o: Top level handler for the sound system, provides + a set of functions for registration of devices + by type. + + sound.o: Common sound functions required by all modules. + +For the specific sound modules (e.g., sb.o for the Soundblaster), +read the documentation on that module to determine what options +are available, for example IRQ, address, DMA. + +Warning, the options for different cards sometime use different names +for the same or a similar feature (dma1= versus dma16=). As a last +resort, inspect the code (search for MODULE_PARM). + +Notes: + +1. There is a new OpenSource sound driver called ALSA which is + currently under development: http://www.alsa-project.org/ + The ALSA drivers support some newer hardware that may not + be supported by this sound driver and also provide some + additional features. + +2. The commercial OSS driver may be obtained from the site: + http://www/opensound.com. This may be used for cards that + are unsupported by the kernel driver, or may be used + by other operating systems. + +3. The enlightenment sound daemon may be used for playing + multiple sounds at the same time via a single card, eliminating + some of the requirements for multiple sound card systems. For + more information, see: http://www.tux.org/~ricdude/EsounD.html + The "esd" program may be used with the real-player and mpeg + players like mpg123 and x11amp. The newer real-player + and some games even include built-in support for ESD! + + +Building the Modules: +===================== + +This document does not provide full details on building the +kernel, etc. The notes below apply only to making the kernel +sound modules. If this conflicts with the kernel's README, +the README takes precedence. + +1. To make the kernel sound modules, cd to your /usr/src/linux + directory (typically) and type make config, make menuconfig, + or make xconfig (to start the command line, dialog, or x-based + configuration tool). + +2. Select the Sound option and a dialog will be displayed. + +3. Select M (module) for "Sound card support". + +4. Select your sound driver(s) as a module. For ProAudio, Sound + Blaster, etc., select M (module) for OSS sound modules. + [thanks to Marvin Stodolsky ]A + +5. Make the kernel (e.g., make dep ; make bzImage), and install + the kernel. + +6. Make the modules and install them (make modules; make modules_install). + +Note, for 2.4.x kernels, make sure you have the newer modutils +loaded or modules will not be loaded properly. 2.4.x changed the +layout of /lib/modules/2.4.x and requires an updated modutils. + + +Plug and Play (PnP: +=================== + +If the sound card is an ISA PnP card, isapnp may be used +to configure the card. See the file isapnp.txt in the +directory one level up (e.g., /usr/src/linux/Documentation). + +Also the 2.4.x kernels provide PnP capabilities, see the +file NEWS in this directory. + +PCI sound cards are highly recommended, as they are far +easier to configure and from what I have read, they use +less resources and are more CPU efficient. + + +INSMOD: +======= + +If loading via insmod, the common modules must be loaded in the +order below BEFORE loading the other sound modules. The card-specific +modules may then be loaded (most require parameters). For example, +I use the following via a shell script to load my SoundBlaster: + +SB_BASE=0x240 +SB_IRQ=9 +SB_DMA=3 +SB_DMA2=5 +SB_MPU=0x300 +# +echo Starting sound +/sbin/insmod soundcore +/sbin/insmod sound +# +echo Starting sound blaster.... +/sbin/insmod uart401 +/sbin/insmod sb io=$SB_BASE irq=$SB_IRQ dma=$SB_DMA dma16=$SB_DMA2 mpu_io=$SB_MP + +When using sound as a module, I typically put these commands +in a file such as /root/soundon.sh. + + +MODPROBE: +========= + +If loading via modprobe, these common files are automatically loaded +when requested by modprobe. For example, my /etc/modules.conf contains: + +alias sound sb +options sb io=0x240 irq=9 dma=3 dma16=5 mpu_io=0x300 + +All you need to do to load the module is: + + /sbin/modprobe sb + + +Sound Status: +============= + +The status of sound may be read/checked by: + cat (anyfile).au >/dev/audio + +[WWH: This may not work properly for SoundBlaster PCI 128 cards +such as the es1370/1 (see the es1370/1 files in this directory) +as they do not automatically support uLaw on /dev/audio.] + +The status of the modules and which modules depend on +which other modules may be checked by: + /sbin/lsmod + +/sbin/lsmod should show something like the following: + sb 26280 0 + uart401 5640 0 [sb] + sound 57112 0 [sb uart401] + soundcore 1968 8 [sb sound] + + +Removing Sound: +=============== + +Sound may be removed by using /sbin/rmmod in the reverse order +in which you load the modules. Note, if a program has a sound device +open (e.g., xmixer), that module (and the modules on which it +depends) may not be unloaded. + +For example, I use the following to remove my Soundblaster (rmmod +in the reverse order in which I loaded the modules): + +/sbin/rmmod sb +/sbin/rmmod uart401 +/sbin/rmmod sound +/sbin/rmmod soundcore + +When using sound as a module, I typically put these commands +in a script such as /root/soundoff.sh. + + +Removing Sound for use with OSS: +================================ + +If you get really stuck or have a card that the kernel modules +will not support, you can get a commercial sound driver from +http://www.opensound.com. Before loading the commercial sound +driver, you should do the following: + +1. remove sound modules (detailed above) +2. remove the sound modules from /etc/modules.conf +3. move the sound modules from /lib/modules//misc + (for example, I make a /lib/modules//misc/tmp + directory and copy the sound module files to that + directory). + + +Multiple Sound Cards: +===================== + +The sound drivers will support multiple sound cards and there +are some great applications like multitrack that support them. +Typically, you need two sound cards of different types. Note, this +uses more precious interrupts and DMA channels and sometimes +can be a configuration nightmare. I have heard reports of 3-4 +sound cards (typically I only use 2). You can sometimes use +multiple PCI sound cards of the same type. + +On my machine I have two sound cards (cs4232 and Soundblaster Vibra +16). By loading sound as modules, I can control which is the first +sound device (/dev/dsp, /dev/audio, /dev/mixer) and which is +the second. Normally, the cs4232 (Dell sound on the motherboard) +would be the first sound device, but I prefer the Soundblaster. +All you have to do is to load the one you want as /dev/dsp +first (in my case "sb") and then load the other one +(in my case "cs4232"). + +If you have two cards of the same type that are jumpered +cards or different PnP revisions, you may load the same +module twice. For example, I have a SoundBlaster vibra 16 +and an older SoundBlaster 16 (jumpers). To load the module +twice, you need to do the following: + +1. Copy the sound modules to a new name. For example + sb.o could be copied (or symlinked) to sb1.o for the + second SoundBlaster. + +2. Make a second entry in /etc/modules.conf, for example, + sound1 or sb1. This second entry should refer to the + new module names for example sb1, and should include + the I/O, etc. for the second sound card. + +3. Update your soundon.sh script, etc. + +Warning: I have never been able to get two PnP sound cards of the +same type to load at the same time. I have tried this several times +with the Soundblaster Vibra 16 cards. OSS has indicated that this +is a PnP problem.... If anyone has any luck doing this, please +send me an E-MAIL. PCI sound cards should not have this problem.a +Since this was originally release, I have received a couple of +mails from people who have accomplished this! + +NOTE: In Linux 2.4 the Sound Blaster driver (and only this one yet) +supports multiple cards with one module by default. +Read the file 'Soundblaster' in this directory for details. + + +Sound Problems: +=============== + +First RTFM (including the troubleshooting section +in the Sound-HOWTO). + +1) If you are having problems loading the modules (for + example, if you get device conflict errors) try the + following: + + A) If you have Win95 or NT on the same computer, + write down what addresses, IRQ, and DMA channels + those were using for the same hardware. You probably + can use these addresses, IRQs, and DMA channels. + You should really do this BEFORE attempting to get + sound working! + + B) Check (cat) /proc/interrupts, /proc/ioports, + and /proc/dma. Are you trying to use an address, + IRQ or DMA port that another device is using? + + C) Check (cat) /proc/isapnp + + D) Inspect your /var/log/messages file. Often that will + indicate what IRQ or IO port could not be obtained. + + E) Try another port or IRQ. Note this may involve + using the PnP tools to move the sound card to + another location. Sometimes this is the only way + and it is more or less trial and error. + +2) If you get motor-boating (the same sound or part of a + sound clip repeated), you probably have either an IRQ + or DMA conflict. Move the card to another IRQ or DMA + port. This has happened to me when playing long files + when I had an IRQ conflict. + +3. If you get dropouts or pauses when playing high sample + rate files such as using mpg123 or x11amp/xmms, you may + have too slow of a CPU and may have to use the options to + play the files at 1/2 speed. For example, you may use + the -2 or -4 option on mpg123. You may also get this + when trying to play mpeg files stored on a CD-ROM + (my Toshiba T8000 PII/366 sometimes has this problem). + +4. If you get "cannot access device" errors, your /dev/dsp + files, etc. may be set to owner root, mode 600. You + may have to use the command: + chmod 666 /dev/dsp /dev/mixer /dev/audio + +5. If you get "device busy" errors, another program has the + sound device open. For example, if using the Enlightenment + sound daemon "esd", the "esd" program has the sound device. + If using "esd", please RTFM the docs on ESD. For example, + esddsp may be used to play files via a non-esd + aware program. + +6) Ask for help on the sound list or send E-MAIL to the + sound driver author/maintainer. + +7) Turn on debug in drivers/sound/sound_config.h (DEB, DDB, MDB). + +8) If the system reports insufficient DMA memory then you may want to + load sound with the "dmabufs=1" option. Or in /etc/conf.modules add + + preinstall sound dmabufs=1 + + This makes the sound system allocate its buffers and hang onto them. + + You may also set persistent DMA when building a 2.4.x kernel. + + +Configuring Sound: +================== + +There are several ways of configuring your sound: + +1) On the kernel command line (when using the sound driver(s) + compiled in the kernel). Check the driver source and + documentation for details. + +2) On the command line when using insmod or in a bash script + using command line calls to load sound. + +3) In /etc/modules.conf when using modprobe. + +4) Via Red Hat's GPL'd /usr/sbin/sndconfig program (text based). + +5) Via the OSS soundconf program (with the commercial version + of the OSS driver. + +6) By just loading the module and let isapnp do everything relevant + for you. This works only with a few drivers yet and - of course - + only with isapnp hardware. + +And I am sure, several other ways. + +Anyone want to write a linuxconf module for configuring sound? + + +Module Loading: +=============== + +When a sound card is first referenced and sound is modular, the sound system +will ask for the sound devices to be loaded. Initially it requests that +the driver for the sound system is loaded. It then will ask for +sound-slot-0, where 0 is the first sound card. (sound-slot-1 the second and +so on). Thus you can do + +alias sound-slot-0 sb + +To load a soundblaster at this point. If the slot loading does not provide +the desired device - for example a soundblaster does not directly provide +a midi synth in all cases then it will request "sound-service-0-n" where n +is + + 0 Mixer + + 2 MIDI + + 3, 4 DSP audio + + +For example, I use the following to load my Soundblaster PCI 128 +(ES 1371) card first, followed by my SoundBlaster Vibra 16 card, +then by my TV card: + +# Load the Soundblaster PCI 128 as /dev/dsp, /dev/dsp1, /dev/mixer +alias sound-slot-0 es1371 + +# Load the Soundblaster Vibra 16 as /dev/dsp2, /dev/mixer1 +alias sound-slot-1 sb +options sb io=0x240 irq=5 dma=1 dma16=5 mpu_io=0x330 + +# Load the BTTV (TV card) as /dev/mixer2 +alias sound-slot-2 bttv +alias sound-service-2-0 tvmixer + +pre-install bttv modprobe tuner ; modprobe tvmixer +pre-install tvmixer modprobe msp3400; modprobe tvaudio +options tuner debug=0 type=8 +options bttv card=0 radio=0 pll=0 + + +For More Information (RTFM): +============================ +1) Information on kernel modules: linux/Documentation/modules.txt + and manual pages for insmod and modprobe. + +2) Information on PnP, RTFM manual pages for isapnp. + +3) Sound-HOWTO and Sound-Playing-HOWTO. + +4) OSS's WWW site at http://www.opensound.com. + +5) All the files in linux/Documentation/sound. + +6) The comments and code in linux/drivers/sound. + +7) The sndconfig and rhsound documentation from Red Hat. + +8) The Linux-sound mailing list: sound-list@redhat.com. + +9) Enlightenment documentation (for info on esd) + http://www.tux.org/~ricdude/EsounD.html. + +10) ALSA home page: http://www.alsa-project.org/ + + +Contact Information: +==================== +Wade Hampton: (whampton@staffnet.com) + diff -Nru a/Documentation/sound/oss/MAD16 b/Documentation/sound/oss/MAD16 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/MAD16 Thu Mar 7 18:17:44 2002 @@ -0,0 +1,55 @@ +(This recipe has been edited to update the configuration symbols.) + +From: Shaw Carruthers + +I have been using mad16 sound for some time now with no problems, current +kernel 2.1.89 + +lsmod shows: + +mad16 5176 0 +sb 22044 0 [mad16] +uart401 5576 0 [mad16 sb] +ad1848 14176 1 [mad16] +sound 61928 0 [mad16 sb uart401 ad1848] + +.config has: + +CONFIG_SOUND=m +CONFIG_SOUND_ADLIB=m +CONFIG_SOUND_MAD16=m +CONFIG_SOUND_YM3812=m + +modules.conf has: + +alias char-major-14 mad16 +options sb mad16=1 +options mad16 io=0x530 irq=7 dma=0 dma16=1 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 + + +To get the built in mixer to work this needs to be: + +options adlib_card io=0x388 # FM synthesizer +options sb mad16=1 +options mad16 io=0x530 irq=7 dma=0 dma16=1 mpu_io=816 mpu_irq=5 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 + +The addition of the "mpu_io=816 mpu_irq=5" to the mad16 options line is + +------------------------------------------------------------------------ +The mad16 module in addition supports the following options: + +option: meaning: default: +joystick=0,1 disabled, enabled disabled +cdtype=0x00,0x02,0x04, disabled, Sony CDU31A, disabled + 0x06,0x08,0x0a Mitsumi, Panasonic, + Secondary IDE, Primary IDE +cdport=0x340,0x320, 0x340 + 0x330,0x360 +cdirq=0,3,5,7,9,10,11 disabled, IRQ3, ... disabled +cddma=0,5,6,7 disabled, DMA5, ... DMA5 for Mitsumi or IDE +cddma=0,1,2,3 disabled, DMA1, ... DMA3 for Sony or Panasonic +opl4=0,1 OPL3, OPL4 OPL3 + +for more details see linux/drivers/sound/mad16.c + +Rui Sousa diff -Nru a/Documentation/sound/oss/Maestro b/Documentation/sound/oss/Maestro --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/Maestro Thu Mar 7 18:17:44 2002 @@ -0,0 +1,123 @@ + An OSS/Lite Driver for the ESS Maestro family of sound cards + + Zach Brown, December 1999 + +Driver Status and Availability +------------------------------ + +The most recent version of this driver will hopefully always be available at + http://www.zabbo.net/maestro/ + +I will try and maintain the most recent stable version of the driver +in both the stable and development kernel lines. + +ESS Maestro Chip Family +----------------------- + +There are 3 main variants of the ESS Maestro PCI sound chip. The first +is the Maestro 1. It was originally produced by Platform Tech as the +'AGOGO'. It can be recognized by Platform Tech's PCI ID 0x1285 with +0x0100 as the device ID. It was put on some sound boards and a few laptops. +ESS bought the design and cleaned it up as the Maestro 2. This starts +their marking with the ESS vendor ID 0x125D and the 'year' device IDs. +The Maestro 2 claims 0x1968 while the Maestro 2e has 0x1978. + +The various families of Maestro are mostly identical as far as this +driver is concerned. It doesn't touch the DSP parts that differ (though +it could for FM synthesis). + +Driver OSS Behavior +-------------------- + +This OSS driver exports /dev/mixer and /dev/dsp to applications, which +mostly adhere to the OSS spec. This driver doesn't register itself +with /dev/sndstat, so don't expect information to appear there. + +The /dev/dsp device exported behaves almost as expected. Playback is +supported in all the various lovely formats. 8/16bit stereo/mono from +8khz to 48khz, and mmap()ing for playback behaves. Capture/recording +is limited due to oddities with the Maestro hardware. One can only +record in 16bit stereo. For recording the maestro uses non interleaved +stereo buffers so that mmap()ing the incoming data does not result in +a ring buffer of LRLR data. mmap()ing of the read buffers is therefore +disallowed until this can be cleaned up. + +/dev/mixer is an interface to the AC'97 codec on the Maestro. It is +worth noting that there are a variety of AC'97s that can be wired to +the Maestro. Which is used is entirely up to the hardware implementor. +This should only be visible to the user by the presence, or lack, of +'Bass' and 'Treble' sliders in the mixer. Not all AC'97s have them. + +The driver doesn't support MIDI or FM playback at the moment. Typically +the Maestro is wired to an MPU MIDI chip, but some hardware implementations +don't. We need to assemble a white list of hardware implementations that +have MIDI wired properly before we can claim to support it safely. + +Compiling and Installing +------------------------ + +With the drivers inclusion into the kernel, compiling and installing +is the same as most OSS/Lite modular sound drivers. Compilation +of the driver is enabled through the CONFIG_SOUND_MAESTRO variable +in the config system. + +It may be modular or statically linked. If it is modular it should be +installed with the rest of the modules for the kernel on the system. +Typically this will be in /lib/modules/ somewhere. 'alias sound maestro' +should also be added to your module configs (typically /etc/conf.modules) +if you're using modular OSS/Lite sound and want to default to using a +maestro chip. + +As this is a PCI device, the module does not need to be informed of +any IO or IRQ resources it should use, it devines these from the +system. Sometimes, on sucky PCs, the BIOS fails to allocated resources +for the maestro. This will result in a message like: + maestro: PCI subsystem reports IRQ 0, this might not be correct. +from the kernel. Should this happen the sound chip most likely will +not operate correctly. To solve this one has to dig through their BIOS +(typically entered by hitting a hot key at boot time) and figure out +what magic needs to happen so that the BIOS will reward the maestro with +an IRQ. This operation is incredibly system specific, so you're on your +own. Sometimes the magic lies in 'PNP Capable Operating System' settings. + +There are very few options to the driver. One is 'debug' which will +tell the driver to print minimal debugging information as it runs. This +can be collected with 'dmesg' or through the klogd daemon. + +The other, more interesting option, is 'dsps_order'. Typically at +install time the driver will only register one available /dev/dsp device +for its use. The 'dsps_order' module parameter allows for more devices +to be allocated, as a power of two. Up to 4 devices can be registered +( dsps_order=2 ). These devices act as fully distinct units and use +separate channels in the maestro. + +Power Management +---------------- + +As of version 0.14, this driver has a minimal understanding of PCI +Power Management. If it finds a valid power management capability +on the PCI device it will attempt to use the power management +functions of the maestro. It will only do this on Maestro 2Es and +only on machines that are known to function well. You can +force the use of power management by setting the 'use_pm' module +option to 1, or can disable it entirely by setting it to 0. + +When using power management, the driver does a few things +differently. It will keep the chip in a lower power mode +when the module is inserted but /dev/dsp is not open. This +allows the mixer to function but turns off the clocks +on other parts of the chip. When /dev/dsp is opened the chip +is brought into full power mode, and brought back down +when it is closed. It also powers down the chip entirely +when the module is removed or the machine is shutdown. This +can have nonobvious consequences. CD audio may not work +after a power managing driver is removed. Also, software that +doesn't understand power management may not be able to talk +to the powered down chip until the machine goes through a hard +reboot to bring it back. + +.. more details .. +------------------ + +drivers/sound/maestro.c contains comments that hopefully explain +the maestro implementation. diff -Nru a/Documentation/sound/oss/Maestro3 b/Documentation/sound/oss/Maestro3 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/Maestro3 Thu Mar 7 18:17:41 2002 @@ -0,0 +1,84 @@ + An OSS/Lite Driver for the ESS Maestro3 family of sound chips + + Zach Brown, January 2001 + +Driver Status and Availability +------------------------------ + +The most recent version of this driver will hopefully always be available at + http://www.zabbo.net/maestro3/ + +I will try and maintain the most recent stable version of the driver +in both the stable and development kernel lines. + +Historically I've sucked pretty hard at actually doing that, however. + +ESS Maestro3 Chip Family +----------------------- + +The 'Maestro3' is much like the Maestro2 chip. The noted improvement +is the removal of the silicon in the '2' that did PCM mixing. All that +work is now done through a custom DSP called the ASSP, the Asynchronus +Specific Signal Processor. + +The 'Allegro' is a baby version of the Maestro3. I'm not entirely clear +on the extent of the differences, but the driver supports them both :) + +The 'Allegro' shows up as PCI ID 0x1988 and the Maestro3 as 0x1998, +both under ESS's vendor ID of 0x125D. The Maestro3 can also show up as +0x199a when hardware strapping is used. + +The chip can also act as a multi function device. The modem IDs follow +the audio multimedia device IDs. (so the modem part of an Allegro shows +up as 0x1989) + +Driver OSS Behavior +-------------------- + +This OSS driver exports /dev/mixer and /dev/dsp to applications, which +mostly adhere to the OSS spec. This driver doesn't register itself +with /dev/sndstat, so don't expect information to appear there. + +The /dev/dsp device exported behaves as expected. Playback is +supported in all the various lovely formats. 8/16bit stereo/mono from +8khz to 48khz, with both read()/write(), and mmap(). + +/dev/mixer is an interface to the AC'97 codec on the Maestro3. It is +worth noting that there are a variety of AC'97s that can be wired to +the Maestro3. Which is used is entirely up to the hardware implementor. +This should only be visible to the user by the presence, or lack, of +'Bass' and 'Treble' sliders in the mixer. Not all AC'97s have them. +The Allegro has an onchip AC'97. + +The driver doesn't support MIDI or FM playback at the moment. + +Compiling and Installing +------------------------ + +With the drivers inclusion into the kernel, compiling and installing +is the same as most OSS/Lite modular sound drivers. Compilation +of the driver is enabled through the CONFIG_SOUND_MAESTRO3 variable +in the config system. + +It may be modular or statically linked. If it is modular it should be +installed with the rest of the modules for the kernel on the system. +Typically this will be in /lib/modules/ somewhere. 'alias sound-slot-0 +maestro3' should also be added to your module configs (typically +/etc/modules.conf) if you're using modular OSS/Lite sound and want to +default to using a maestro3 chip. + +There are very few options to the driver. One is 'debug' which will +tell the driver to print minimal debugging information as it runs. This +can be collected with 'dmesg' or through the klogd daemon. + +The other is 'external_amp', which tells the driver to attempt to enable +an external amplifier. This defaults to '1', you can tell the driver +not to bother enabling such an amplifier by setting it to '0'. + +Power Management +---------------- + +This driver has a minimal understanding of PCI Power Management. It will +try and power down the chip when the system is suspended, and power +it up with it is resumed. It will also try and power down the chip +when the machine is shut down. diff -Nru a/Documentation/sound/oss/MultiSound b/Documentation/sound/oss/MultiSound --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/MultiSound Thu Mar 7 18:17:45 2002 @@ -0,0 +1,1137 @@ +#! /bin/sh +# +# Turtle Beach MultiSound Driver Notes +# -- Andrew Veliath +# +# Last update: September 10, 1998 +# Corresponding msnd driver: 0.8.3 +# +# ** This file is a README (top part) and shell archive (bottom part). +# The corresponding archived utility sources can be unpacked by +# running `sh MultiSound' (the utilities are only needed for the +# Pinnacle and Fiji cards). ** +# +# +# -=-=- Getting Firmware -=-=- +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# See the section `Obtaining and Creating Firmware Files' in this +# document for instructions on obtaining the necessary firmware +# files. +# +# +# Supported Features +# ~~~~~~~~~~~~~~~~~~ +# +# Currently, full-duplex digital audio (/dev/dsp only, /dev/audio is +# not currently available) and mixer functionality (/dev/mixer) are +# supported (memory mapped digital audio is not yet supported). +# Digital transfers and monitoring can be done as well if you have +# the digital daughterboard (see the section on using the S/PDIF port +# for more information). +# +# Support for the Turtle Beach MultiSound Hurricane architecture is +# composed of the following modules (these can also operate compiled +# into the kernel): +# +# msnd - MultiSound base (requires soundcore) +# +# msnd_classic - Base audio/mixer support for Classic, Monetery and +# Tahiti cards +# +# msnd_pinnacle - Base audio/mixer support for Pinnacle and Fiji cards +# +# +# Important Notes - Read Before Using +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# The firmware files are not included (may change in future). You +# must obtain these images from Turtle Beach (they are included in +# the MultiSound Development Kits), and place them in /etc/sound for +# example, and give the full paths in the Linux configuration. If +# you are compiling in support for the MultiSound driver rather than +# using it as a module, these firmware files must be accessible +# during kernel compilation. +# +# Please note these files must be binary files, not assembler. See +# the section later in this document for instructions to obtain these +# files. +# +# +# Configuring Card Resources +# ~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# ** This section is very important, as your card may not work at all +# or your machine may crash if you do not do this correctly. ** +# +# * Classic/Monterey/Tahiti +# +# These cards are configured through the driver msnd_classic. You must +# know the io port, then the driver will select the irq and memory resources +# on the card. It is up to you to know if these are free locations or now, +# a conflict can lock the machine up. +# +# * Pinnacle/Fiji +# +# The Pinnacle and Fiji cards have an extra config port, either +# 0x250, 0x260 or 0x270. This port can be disabled to have the card +# configured strictly through PnP, however you lose the ability to +# access the IDE controller and joystick devices on this card when +# using PnP. The included pinnaclecfg program in this shell archive +# can be used to configure the card in non-PnP mode, and in PnP mode +# you can use isapnptools. These are described briefly here. +# +# pinnaclecfg is not required; you can use the msnd_pinnacle module +# to fully configure the card as well. However, pinnaclecfg can be +# used to change the resource values of a particular device after the +# msnd_pinnacle module has been loaded. If you are compiling the +# driver into the kernel, you must set these values during compile +# time, however other peripheral resource values can be changed with +# the pinnaclecfg program after the kernel is loaded. +# +# +# *** PnP mode +# +# Use pnpdump to obtain a sample configuration if you can; I was able +# to obtain one with the command `pnpdump 1 0x203' -- this may vary +# for you (running pnpdump by itself did not work for me). Then, +# edit this file and use isapnp to uncomment and set the card values. +# Use these values when inserting the msnd_pinnacle module. Using +# this method, you can set the resources for the DSP and the Kurzweil +# synth (Pinnacle). Since Linux does not directly support PnP +# devices, you may have difficulty when using the card in PnP mode +# when it the driver is compiled into the kernel. Using non-PnP mode +# is preferable in this case. +# +# Here is an example mypinnacle.conf for isapnp that sets the card to +# io base 0x210, irq 5 and mem 0xd8000, and also sets the Kurzweil +# synth to 0x330 and irq 9 (may need editing for your system): +# +# (READPORT 0x0203) +# (CSN 2) +# (IDENTIFY *) +# +# # DSP +# (CONFIGURE BVJ0440/-1 (LD 0 +# (INT 0 (IRQ 5 (MODE +E))) (IO 0 (BASE 0x0210)) (MEM 0 (BASE 0x0d8000)) +# (ACT Y))) +# +# # Kurzweil Synth (Pinnacle Only) +# (CONFIGURE BVJ0440/-1 (LD 1 +# (IO 0 (BASE 0x0330)) (INT 0 (IRQ 9 (MODE +E))) +# (ACT Y))) +# +# (WAITFORKEY) +# +# +# *** Non-PnP mode +# +# The second way is by running the card in non-PnP mode. This +# actually has some advantages in that you can access some other +# devices on the card, such as the joystick and IDE controller. To +# configure the card, unpack this shell archive and build the +# pinnaclecfg program. Using this program, you can assign the +# resource values to the card's devices, or disable the devices. As +# an alternative to using pinnaclecfg, you can specify many of the +# configuration values when loading the msnd_pinnacle module (or +# during kernel configuration when compiling the driver into the +# kernel). +# +# If you specify cfg=0x250 for the msnd_pinnacle module, it +# automatically configure the card to the given io, irq and memory +# values using that config port (the config port is jumper selectable +# on the card to 0x250, 0x260 or 0x270). +# +# See the `msnd_pinnacle Additional Options' section below for more +# information on these parameters (also, if you compile the driver +# directly into the kernel, these extra parameters can be useful +# here). +# +# +# ** It is very easy to cause problems in your machine if you choose a +# resource value which is incorrect. ** +# +# +# Examples +# ~~~~~~~~ +# +# * MultiSound Classic/Monterey/Tahiti: +# +# modprobe soundcore +# insmod msnd +# insmod msnd_classic io=0x290 irq=7 mem=0xd0000 +# +# * MultiSound Pinnacle in PnP mode: +# +# modprobe soundcore +# insmod msnd +# isapnp mypinnacle.conf +# insmod msnd_pinnacle io=0x210 irq=5 mem=0xd8000 <-- match mypinnacle.conf values +# +# * MultiSound Pinnacle in non-PnP mode (replace 0x250 with your configuration port, +# one of 0x250, 0x260 or 0x270): +# +# insmod soundcore +# insmod msnd +# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 +# +# * To use the MPU-compatible Kurzweil synth on the Pinnacle in PnP +# mode, add the following (assumes you did `isapnp mypinnacle.conf'): +# +# insmod sound +# insmod mpu401 io=0x330 irq=9 <-- match mypinnacle.conf values +# +# * To use the MPU-compatible Kurzweil synth on the Pinnacle in non-PnP +# mode, add the following. Note how we first configure the peripheral's +# resources, _then_ install a Linux driver for it: +# +# insmod sound +# pinnaclecfg 0x250 mpu 0x330 9 +# insmod mpu401 io=0x330 irq=9 +# +# -- OR you can use the following sequence without pinnaclecfg in non-PnP mode: +# +# insmod soundcore +# insmod msnd +# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 mpu_io=0x330 mpu_irq=9 +# insmod sound +# insmod mpu401 io=0x330 irq=9 +# +# * To setup the joystick port on the Pinnacle in non-PnP mode (though +# you have to find the actual Linux joystick driver elsewhere), you +# can use pinnaclecfg: +# +# pinnaclecfg 0x250 joystick 0x200 +# +# -- OR you can configure this using msnd_pinnacle with the following: +# +# insmod soundcore +# insmod msnd +# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 joystick_io=0x200 +# +# +# msnd_classic, msnd_pinnacle Required Options +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# If the following options are not given, the module will not load. +# Examine the kernel message log for informative error messages. +# WARNING--probing isn't supported so try to make sure you have the +# correct shared memory area, otherwise you may experience problems. +# +# io I/O base of DSP, e.g. io=0x210 +# irq IRQ number, e.g. irq=5 +# mem Shared memory area, e.g. mem=0xd8000 +# +# +# msnd_classic, msnd_pinnacle Additional Options +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# fifosize The digital audio FIFOs, in kilobytes. If not +# specified, the default will be used. Increasing +# this value will reduce the chance of a FIFO +# underflow at the expense of increasing overall +# latency. For example, fifosize=512 will +# allocate 512kB read and write FIFOs (1MB total). +# While this may reduce dropouts, a heavy machine +# load will undoubtedly starve the FIFO of data +# and you will eventually get dropouts. One +# option is to alter the scheduling priority of +# the playback process, using `nice' or some form +# of POSIX soft real-time scheduling. +# +# calibrate_signal Setting this to one calibrates the ADCs to the +# signal, zero calibrates to the card (defaults +# to zero). +# +# +# msnd_pinnacle Additional Options +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# digital Specify digital=1 to enable the S/PDIF input +# if you have the digital daughterboard +# adapter. This will enable access to the +# DIGITAL1 input for the soundcard in the mixer. +# Some mixer programs might have trouble setting +# the DIGITAL1 source as an input. If you have +# trouble, you can try the setdigital.c program +# at the bottom of this document. +# +# cfg Non-PnP configuration port for the Pinnacle +# and Fiji (typically 0x250, 0x260 or 0x270, +# depending on the jumper configuration). If +# this option is omitted, then it is assumed +# that the card is in PnP mode, and that the +# specified DSP resource values are already +# configured with PnP (i.e. it won't attempt to +# do any sort of configuration). +# +# When the Pinnacle is in non-PnP mode, you can use the following +# options to configure particular devices. If a full specification +# for a device is not given, then the device is not configured. Note +# that you still must use a Linux driver for any of these devices +# once their resources are setup (such as the Linux joystick driver, +# or the MPU401 driver from OSS for the Kurzweil synth). +# +# mpu_io I/O port of MPU (on-board Kurzweil synth) +# mpu_irq IRQ of MPU (on-board Kurzweil synth) +# ide_io0 First I/O port of IDE controller +# ide_io1 Second I/O port of IDE controller +# ide_irq IRQ IDE controller +# joystick_io I/O port of joystick +# +# +# Obtaining and Creating Firmware Files +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For the Classic/Tahiti/Monterey +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Download to /tmp and unzip the following file from Turtle Beach: +# +# ftp://ftp.voyetra.com/pub/tbs/msndcl/msndvkit.zip +# +# When unzipped, unzip the file named MsndFiles.zip. Then copy the +# following firmware files to /etc/sound (note the file renaming): +# +# cp DSPCODE/MSNDINIT.BIN /etc/sound/msndinit.bin +# cp DSPCODE/MSNDPERM.REB /etc/sound/msndperm.bin +# +# When configuring the Linux kernel, specify /etc/sound/msndinit.bin and +# /etc/sound/msndperm.bin for the two firmware files (Linux kernel +# versions older than 2.2 do not ask for firmware paths, and are +# hardcoded to /etc/sound). +# +# If you are compiling the driver into the kernel, these files must +# be accessible during compilation, but will not be needed later. +# The files must remain, however, if the driver is used as a module. +# +# +# For the Pinnacle/Fiji +# ~~~~~~~~~~~~~~~~~~~~~ +# +# Download to /tmp and unzip the following file from Turtle Beach (be +# sure to use the entire URL; some have had trouble navigating to the +# URL): +# +# ftp://ftp.voyetra.com/pub/tbs/pinn/pnddk100.zip +# +# Unpack this shell archive, and run make in the created directory +# (you need a C compiler and flex to build the utilities). This +# should give you the executables conv, pinnaclecfg and setdigital. +# conv is only used temporarily here to create the firmware files, +# while pinnaclecfg is used to configure the Pinnacle or Fiji card in +# non-PnP mode, and setdigital can be used to set the S/PDIF input on +# the mixer (pinnaclecfg and setdigital should be copied to a +# convenient place, possibly run during system initialization). +# +# To generating the firmware files with the `conv' program, we create +# the binary firmware files by doing the following conversion +# (assuming the archive unpacked into a directory named PINNDDK): +# +# ./conv < PINNDDK/dspcode/pndspini.asm > /etc/sound/pndspini.bin +# ./conv < PINNDDK/dspcode/pndsperm.asm > /etc/sound/pndsperm.bin +# +# The conv (and conv.l) program is not needed after conversion and can +# be safely deleted. Then, when configuring the Linux kernel, specify +# /etc/sound/pndspini.bin and /etc/sound/pndsperm.bin for the two +# firmware files (Linux kernel versions older than 2.2 do not ask for +# firmware paths, and are hardcoded to /etc/sound). +# +# If you are compiling the driver into the kernel, these files must +# be accessible during compilation, but will not be needed later. +# The files must remain, however, if the driver is used as a module. +# +# +# Using Digital I/O with the S/PDIF Port +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# If you have a Pinnacle or Fiji with the digital daughterboard and +# want to set it as the input source, you can use this program if you +# have trouble trying to do it with a mixer program (be sure to +# insert the module with the digital=1 option, or say Y to the option +# during compiled-in kernel operation). Upon selection of the S/PDIF +# port, you should be able monitor and record from it. +# +# There is something to note about using the S/PDIF port. Digital +# timing is taken from the digital signal, so if a signal is not +# connected to the port and it is selected as recording input, you +# will find PCM playback to be distorted in playback rate. Also, +# attempting to record at a sampling rate other than the DAT rate may +# be problematic (i.e. trying to record at 8000Hz when the DAT signal +# is 44100Hz). If you have a problem with this, set the recording +# input to analog if you need to record at a rate other than that of +# the DAT rate. +# +# +# -- Shell archive attached below, just run `sh MultiSound' to extract. +# Contains Pinnacle/Fiji utilities to convert firmware, configure +# in non-PnP mode, and select the DIGITAL1 input for the mixer. +# +# +#!/bin/sh +# This is a shell archive (produced by GNU sharutils 4.2). +# To extract the files from this archive, save it to some FILE, remove +# everything before the `!/bin/sh' line above, then type `sh FILE'. +# +# Made on 1998-12-04 10:07 EST by . +# Source directory was `/home/andrewtv/programming/pinnacle/pinnacle'. +# +# Existing files will *not* be overwritten unless `-c' is specified. +# +# This shar contains: +# length mode name +# ------ ---------- ------------------------------------------ +# 2046 -rw-rw-r-- MultiSound.d/setdigital.c +# 10235 -rw-rw-r-- MultiSound.d/pinnaclecfg.c +# 106 -rw-rw-r-- MultiSound.d/Makefile +# 141 -rw-rw-r-- MultiSound.d/conv.l +# 1472 -rw-rw-r-- MultiSound.d/msndreset.c +# +save_IFS="${IFS}" +IFS="${IFS}:" +gettext_dir=FAILED +locale_dir=FAILED +first_param="$1" +for dir in $PATH +do + if test "$gettext_dir" = FAILED && test -f $dir/gettext \ + && ($dir/gettext --version >/dev/null 2>&1) + then + set `$dir/gettext --version 2>&1` + if test "$3" = GNU + then + gettext_dir=$dir + fi + fi + if test "$locale_dir" = FAILED && test -f $dir/shar \ + && ($dir/shar --print-text-domain-dir >/dev/null 2>&1) + then + locale_dir=`$dir/shar --print-text-domain-dir` + fi +done +IFS="$save_IFS" +if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED +then + echo=echo +else + TEXTDOMAINDIR=$locale_dir + export TEXTDOMAINDIR + TEXTDOMAIN=sharutils + export TEXTDOMAIN + echo="$gettext_dir/gettext -s" +fi +touch -am 1231235999 $$.touch >/dev/null 2>&1 +if test ! -f 1231235999 && test -f $$.touch; then + shar_touch=touch +else + shar_touch=: + echo + $echo 'WARNING: not restoring timestamps. Consider getting and' + $echo "installing GNU \`touch', distributed in GNU File Utilities..." + echo +fi +rm -f 1231235999 $$.touch +# +if mkdir _sh01426; then + $echo 'x -' 'creating lock directory' +else + $echo 'failed to create lock directory' + exit 1 +fi +# ============= MultiSound.d/setdigital.c ============== +if test ! -d 'MultiSound.d'; then + $echo 'x -' 'creating directory' 'MultiSound.d' + mkdir 'MultiSound.d' +fi +if test -f 'MultiSound.d/setdigital.c' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'MultiSound.d/setdigital.c' '(file already exists)' +else + $echo 'x -' extracting 'MultiSound.d/setdigital.c' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/setdigital.c' && +/********************************************************************* +X * +X * setdigital.c - sets the DIGITAL1 input for a mixer +X * +X * Copyright (C) 1998 Andrew Veliath +X * +X * This program is free software; you can redistribute it and/or modify +X * it under the terms of the GNU General Public License as published by +X * the Free Software Foundation; either version 2 of the License, or +X * (at your option) any later version. +X * +X * This program is distributed in the hope that it will be useful, +X * but WITHOUT ANY WARRANTY; without even the implied warranty of +X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +X * GNU General Public License for more details. +X * +X * You should have received a copy of the GNU General Public License +X * along with this program; if not, write to the Free Software +X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +X * +X ********************************************************************/ +X +#include +#include +#include +#include +#include +#include +#include +X +int main(int argc, char *argv[]) +{ +X int fd; +X unsigned long recmask, recsrc; +X +X if (argc != 2) { +X fprintf(stderr, "usage: setdigital \n"); +X exit(1); +X } +X +X if ((fd = open(argv[1], O_RDWR)) < 0) { +X perror(argv[1]); +X exit(1); +X } +X +X if (ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask) < 0) { +X fprintf(stderr, "error: ioctl read recording mask failed\n"); +X perror("ioctl"); +X close(fd); +X exit(1); +X } +X +X if (!(recmask & SOUND_MASK_DIGITAL1)) { +X fprintf(stderr, "error: cannot find DIGITAL1 device in mixer\n"); +X close(fd); +X exit(1); +X } +X +X if (ioctl(fd, SOUND_MIXER_READ_RECSRC, &recsrc) < 0) { +X fprintf(stderr, "error: ioctl read recording source failed\n"); +X perror("ioctl"); +X close(fd); +X exit(1); +X } +X +X recsrc |= SOUND_MASK_DIGITAL1; +X +X if (ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &recsrc) < 0) { +X fprintf(stderr, "error: ioctl write recording source failed\n"); +X perror("ioctl"); +X close(fd); +X exit(1); +X } +X +X close(fd); +X +X return 0; +} +SHAR_EOF + $shar_touch -am 1204092598 'MultiSound.d/setdigital.c' && + chmod 0664 'MultiSound.d/setdigital.c' || + $echo 'restore of' 'MultiSound.d/setdigital.c' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'MultiSound.d/setdigital.c:' 'MD5 check failed' +e87217fc3e71288102ba41fd81f71ec4 MultiSound.d/setdigital.c +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/setdigital.c'`" + test 2046 -eq "$shar_count" || + $echo 'MultiSound.d/setdigital.c:' 'original size' '2046,' 'current size' "$shar_count!" + fi +fi +# ============= MultiSound.d/pinnaclecfg.c ============== +if test -f 'MultiSound.d/pinnaclecfg.c' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'MultiSound.d/pinnaclecfg.c' '(file already exists)' +else + $echo 'x -' extracting 'MultiSound.d/pinnaclecfg.c' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/pinnaclecfg.c' && +/********************************************************************* +X * +X * pinnaclecfg.c - Pinnacle/Fiji Device Configuration Program +X * +X * This is for NON-PnP mode only. For PnP mode, use isapnptools. +X * +X * This is Linux-specific, and must be run with root permissions. +X * +X * Part of the Turtle Beach MultiSound Sound Card Driver for Linux +X * +X * Copyright (C) 1998 Andrew Veliath +X * +X * This program is free software; you can redistribute it and/or modify +X * it under the terms of the GNU General Public License as published by +X * the Free Software Foundation; either version 2 of the License, or +X * (at your option) any later version. +X * +X * This program is distributed in the hope that it will be useful, +X * but WITHOUT ANY WARRANTY; without even the implied warranty of +X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +X * GNU General Public License for more details. +X * +X * You should have received a copy of the GNU General Public License +X * along with this program; if not, write to the Free Software +X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +X * +X ********************************************************************/ +X +#include +#include +#include +#include +#include +#include +#include +X +#define IREG_LOGDEVICE 0x07 +#define IREG_ACTIVATE 0x30 +#define LD_ACTIVATE 0x01 +#define LD_DISACTIVATE 0x00 +#define IREG_EECONTROL 0x3F +#define IREG_MEMBASEHI 0x40 +#define IREG_MEMBASELO 0x41 +#define IREG_MEMCONTROL 0x42 +#define IREG_MEMRANGEHI 0x43 +#define IREG_MEMRANGELO 0x44 +#define MEMTYPE_8BIT 0x00 +#define MEMTYPE_16BIT 0x02 +#define MEMTYPE_RANGE 0x00 +#define MEMTYPE_HIADDR 0x01 +#define IREG_IO0_BASEHI 0x60 +#define IREG_IO0_BASELO 0x61 +#define IREG_IO1_BASEHI 0x62 +#define IREG_IO1_BASELO 0x63 +#define IREG_IRQ_NUMBER 0x70 +#define IREG_IRQ_TYPE 0x71 +#define IRQTYPE_HIGH 0x02 +#define IRQTYPE_LOW 0x00 +#define IRQTYPE_LEVEL 0x01 +#define IRQTYPE_EDGE 0x00 +X +#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF)) +#define LOBYTE(w) ((BYTE)(w)) +#define MAKEWORD(low,hi) ((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8))) +X +typedef __u8 BYTE; +typedef __u16 USHORT; +typedef __u16 WORD; +X +static int config_port = -1; +X +static int msnd_write_cfg(int cfg, int reg, int value) +{ +X outb(reg, cfg); +X outb(value, cfg + 1); +X if (value != inb(cfg + 1)) { +X fprintf(stderr, "error: msnd_write_cfg: I/O error\n"); +X return -EIO; +X } +X return 0; +} +X +static int msnd_read_cfg(int cfg, int reg) +{ +X outb(reg, cfg); +X return inb(cfg + 1); +} +X +static int msnd_write_cfg_io0(int cfg, int num, WORD io) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io))) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io))) +X return -EIO; +X return 0; +} +X +static int msnd_read_cfg_io0(int cfg, int num, WORD *io) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X +X *io = MAKEWORD(msnd_read_cfg(cfg, IREG_IO0_BASELO), +X msnd_read_cfg(cfg, IREG_IO0_BASEHI)); +X +X return 0; +} +X +static int msnd_write_cfg_io1(int cfg, int num, WORD io) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io))) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io))) +X return -EIO; +X return 0; +} +X +static int msnd_read_cfg_io1(int cfg, int num, WORD *io) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X +X *io = MAKEWORD(msnd_read_cfg(cfg, IREG_IO1_BASELO), +X msnd_read_cfg(cfg, IREG_IO1_BASEHI)); +X +X return 0; +} +X +static int msnd_write_cfg_irq(int cfg, int num, WORD irq) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq))) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE)) +X return -EIO; +X return 0; +} +X +static int msnd_read_cfg_irq(int cfg, int num, WORD *irq) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X +X *irq = msnd_read_cfg(cfg, IREG_IRQ_NUMBER); +X +X return 0; +} +X +static int msnd_write_cfg_mem(int cfg, int num, int mem) +{ +X WORD wmem; +X +X mem >>= 8; +X mem &= 0xfff; +X wmem = (WORD)mem; +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem))) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem))) +X return -EIO; +X if (wmem && msnd_write_cfg(cfg, IREG_MEMCONTROL, (MEMTYPE_HIADDR | MEMTYPE_16BIT))) +X return -EIO; +X return 0; +} +X +static int msnd_read_cfg_mem(int cfg, int num, int *mem) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X +X *mem = MAKEWORD(msnd_read_cfg(cfg, IREG_MEMBASELO), +X msnd_read_cfg(cfg, IREG_MEMBASEHI)); +X *mem <<= 8; +X +X return 0; +} +X +static int msnd_activate_logical(int cfg, int num) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE)) +X return -EIO; +X return 0; +} +X +static int msnd_write_cfg_logical(int cfg, int num, WORD io0, WORD io1, WORD irq, int mem) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X if (msnd_write_cfg_io0(cfg, num, io0)) +X return -EIO; +X if (msnd_write_cfg_io1(cfg, num, io1)) +X return -EIO; +X if (msnd_write_cfg_irq(cfg, num, irq)) +X return -EIO; +X if (msnd_write_cfg_mem(cfg, num, mem)) +X return -EIO; +X if (msnd_activate_logical(cfg, num)) +X return -EIO; +X return 0; +} +X +static int msnd_read_cfg_logical(int cfg, int num, WORD *io0, WORD *io1, WORD *irq, int *mem) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X if (msnd_read_cfg_io0(cfg, num, io0)) +X return -EIO; +X if (msnd_read_cfg_io1(cfg, num, io1)) +X return -EIO; +X if (msnd_read_cfg_irq(cfg, num, irq)) +X return -EIO; +X if (msnd_read_cfg_mem(cfg, num, mem)) +X return -EIO; +X return 0; +} +X +static void usage(void) +{ +X fprintf(stderr, +X "\n" +X "pinnaclecfg 1.0\n" +X "\n" +X "usage: pinnaclecfg [device config]\n" +X "\n" +X "This is for use with the card in NON-PnP mode only.\n" +X "\n" +X "Available devices (not all available for Fiji):\n" +X "\n" +X " Device Description\n" +X " -------------------------------------------------------------------\n" +X " reset Reset all devices (i.e. disable)\n" +X " show Display current device configurations\n" +X "\n" +X " dsp Audio device\n" +X " mpu Internal Kurzweil synth\n" +X " ide On-board IDE controller\n" +X " joystick Joystick port\n" +X "\n"); +X exit(1); +} +X +static int cfg_reset(void) +{ +X int i; +X +X for (i = 0; i < 4; ++i) +X msnd_write_cfg_logical(config_port, i, 0, 0, 0, 0); +X +X return 0; +} +X +static int cfg_show(void) +{ +X int i; +X int count = 0; +X +X for (i = 0; i < 4; ++i) { +X WORD io0, io1, irq; +X int mem; +X msnd_read_cfg_logical(config_port, i, &io0, &io1, &irq, &mem); +X switch (i) { +X case 0: +X if (io0 || irq || mem) { +X printf("dsp 0x%x %d 0x%x\n", io0, irq, mem); +X ++count; +X } +X break; +X case 1: +X if (io0 || irq) { +X printf("mpu 0x%x %d\n", io0, irq); +X ++count; +X } +X break; +X case 2: +X if (io0 || io1 || irq) { +X printf("ide 0x%x 0x%x %d\n", io0, io1, irq); +X ++count; +X } +X break; +X case 3: +X if (io0) { +X printf("joystick 0x%x\n", io0); +X ++count; +X } +X break; +X } +X } +X +X if (count == 0) +X fprintf(stderr, "no devices configured\n"); +X +X return 0; +} +X +static int cfg_dsp(int argc, char *argv[]) +{ +X int io, irq, mem; +X +X if (argc < 3 || +X sscanf(argv[0], "0x%x", &io) != 1 || +X sscanf(argv[1], "%d", &irq) != 1 || +X sscanf(argv[2], "0x%x", &mem) != 1) +X usage(); +X +X if (!(io == 0x290 || +X io == 0x260 || +X io == 0x250 || +X io == 0x240 || +X io == 0x230 || +X io == 0x220 || +X io == 0x210 || +X io == 0x3e0)) { +X fprintf(stderr, "error: io must be one of " +X "210, 220, 230, 240, 250, 260, 290, or 3E0\n"); +X usage(); +X } +X +X if (!(irq == 5 || +X irq == 7 || +X irq == 9 || +X irq == 10 || +X irq == 11 || +X irq == 12)) { +X fprintf(stderr, "error: irq must be one of " +X "5, 7, 9, 10, 11 or 12\n"); +X usage(); +X } +X +X if (!(mem == 0xb0000 || +X mem == 0xc8000 || +X mem == 0xd0000 || +X mem == 0xd8000 || +X mem == 0xe0000 || +X mem == 0xe8000)) { +X fprintf(stderr, "error: mem must be one of " +X "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n"); +X usage(); +X } +X +X return msnd_write_cfg_logical(config_port, 0, io, 0, irq, mem); +} +X +static int cfg_mpu(int argc, char *argv[]) +{ +X int io, irq; +X +X if (argc < 2 || +X sscanf(argv[0], "0x%x", &io) != 1 || +X sscanf(argv[1], "%d", &irq) != 1) +X usage(); +X +X return msnd_write_cfg_logical(config_port, 1, io, 0, irq, 0); +} +X +static int cfg_ide(int argc, char *argv[]) +{ +X int io0, io1, irq; +X +X if (argc < 3 || +X sscanf(argv[0], "0x%x", &io0) != 1 || +X sscanf(argv[0], "0x%x", &io1) != 1 || +X sscanf(argv[1], "%d", &irq) != 1) +X usage(); +X +X return msnd_write_cfg_logical(config_port, 2, io0, io1, irq, 0); +} +X +static int cfg_joystick(int argc, char *argv[]) +{ +X int io; +X +X if (argc < 1 || +X sscanf(argv[0], "0x%x", &io) != 1) +X usage(); +X +X return msnd_write_cfg_logical(config_port, 3, io, 0, 0, 0); +} +X +int main(int argc, char *argv[]) +{ +X char *device; +X int rv = 0; +X +X --argc; ++argv; +X +X if (argc < 2) +X usage(); +X +X sscanf(argv[0], "0x%x", &config_port); +X if (config_port != 0x250 && config_port != 0x260 && config_port != 0x270) { +X fprintf(stderr, "error: must be 0x250, 0x260 or 0x270\n"); +X exit(1); +X } +X if (ioperm(config_port, 2, 1)) { +X perror("ioperm"); +X fprintf(stderr, "note: pinnaclecfg must be run as root\n"); +X exit(1); +X } +X device = argv[1]; +X +X argc -= 2; argv += 2; +X +X if (strcmp(device, "reset") == 0) +X rv = cfg_reset(); +X else if (strcmp(device, "show") == 0) +X rv = cfg_show(); +X else if (strcmp(device, "dsp") == 0) +X rv = cfg_dsp(argc, argv); +X else if (strcmp(device, "mpu") == 0) +X rv = cfg_mpu(argc, argv); +X else if (strcmp(device, "ide") == 0) +X rv = cfg_ide(argc, argv); +X else if (strcmp(device, "joystick") == 0) +X rv = cfg_joystick(argc, argv); +X else { +X fprintf(stderr, "error: unknown device %s\n", device); +X usage(); +X } +X +X if (rv) +X fprintf(stderr, "error: device configuration failed\n"); +X +X return 0; +} +SHAR_EOF + $shar_touch -am 1204092598 'MultiSound.d/pinnaclecfg.c' && + chmod 0664 'MultiSound.d/pinnaclecfg.c' || + $echo 'restore of' 'MultiSound.d/pinnaclecfg.c' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'MultiSound.d/pinnaclecfg.c:' 'MD5 check failed' +366bdf27f0db767a3c7921d0a6db20fe MultiSound.d/pinnaclecfg.c +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/pinnaclecfg.c'`" + test 10235 -eq "$shar_count" || + $echo 'MultiSound.d/pinnaclecfg.c:' 'original size' '10235,' 'current size' "$shar_count!" + fi +fi +# ============= MultiSound.d/Makefile ============== +if test -f 'MultiSound.d/Makefile' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'MultiSound.d/Makefile' '(file already exists)' +else + $echo 'x -' extracting 'MultiSound.d/Makefile' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/Makefile' && +CC = gcc +CFLAGS = -O +PROGS = setdigital msndreset pinnaclecfg conv +X +all: $(PROGS) +X +clean: +X rm -f $(PROGS) +SHAR_EOF + $shar_touch -am 1204092398 'MultiSound.d/Makefile' && + chmod 0664 'MultiSound.d/Makefile' || + $echo 'restore of' 'MultiSound.d/Makefile' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'MultiSound.d/Makefile:' 'MD5 check failed' +76ca8bb44e3882edcf79c97df6c81845 MultiSound.d/Makefile +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/Makefile'`" + test 106 -eq "$shar_count" || + $echo 'MultiSound.d/Makefile:' 'original size' '106,' 'current size' "$shar_count!" + fi +fi +# ============= MultiSound.d/conv.l ============== +if test -f 'MultiSound.d/conv.l' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'MultiSound.d/conv.l' '(file already exists)' +else + $echo 'x -' extracting 'MultiSound.d/conv.l' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/conv.l' && +%% +[ \n\t,\r] +\;.* +DB +[0-9A-Fa-f]+H { int n; sscanf(yytext, "%xH", &n); printf("%c", n); } +%% +int yywrap() { return 1; } +main() { yylex(); } +SHAR_EOF + $shar_touch -am 0828231798 'MultiSound.d/conv.l' && + chmod 0664 'MultiSound.d/conv.l' || + $echo 'restore of' 'MultiSound.d/conv.l' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'MultiSound.d/conv.l:' 'MD5 check failed' +d2411fc32cd71a00dcdc1f009e858dd2 MultiSound.d/conv.l +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/conv.l'`" + test 141 -eq "$shar_count" || + $echo 'MultiSound.d/conv.l:' 'original size' '141,' 'current size' "$shar_count!" + fi +fi +# ============= MultiSound.d/msndreset.c ============== +if test -f 'MultiSound.d/msndreset.c' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'MultiSound.d/msndreset.c' '(file already exists)' +else + $echo 'x -' extracting 'MultiSound.d/msndreset.c' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/msndreset.c' && +/********************************************************************* +X * +X * msndreset.c - resets the MultiSound card +X * +X * Copyright (C) 1998 Andrew Veliath +X * +X * This program is free software; you can redistribute it and/or modify +X * it under the terms of the GNU General Public License as published by +X * the Free Software Foundation; either version 2 of the License, or +X * (at your option) any later version. +X * +X * This program is distributed in the hope that it will be useful, +X * but WITHOUT ANY WARRANTY; without even the implied warranty of +X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +X * GNU General Public License for more details. +X * +X * You should have received a copy of the GNU General Public License +X * along with this program; if not, write to the Free Software +X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +X * +X ********************************************************************/ +X +#include +#include +#include +#include +#include +#include +#include +X +int main(int argc, char *argv[]) +{ +X int fd; +X +X if (argc != 2) { +X fprintf(stderr, "usage: msndreset \n"); +X exit(1); +X } +X +X if ((fd = open(argv[1], O_RDWR)) < 0) { +X perror(argv[1]); +X exit(1); +X } +X +X if (ioctl(fd, SOUND_MIXER_PRIVATE1, 0) < 0) { +X fprintf(stderr, "error: msnd ioctl reset failed\n"); +X perror("ioctl"); +X close(fd); +X exit(1); +X } +X +X close(fd); +X +X return 0; +} +SHAR_EOF + $shar_touch -am 1204100698 'MultiSound.d/msndreset.c' && + chmod 0664 'MultiSound.d/msndreset.c' || + $echo 'restore of' 'MultiSound.d/msndreset.c' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'MultiSound.d/msndreset.c:' 'MD5 check failed' +c52f876521084e8eb25e12e01dcccb8a MultiSound.d/msndreset.c +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/msndreset.c'`" + test 1472 -eq "$shar_count" || + $echo 'MultiSound.d/msndreset.c:' 'original size' '1472,' 'current size' "$shar_count!" + fi +fi +rm -fr _sh01426 +exit 0 diff -Nru a/Documentation/sound/oss/NEWS b/Documentation/sound/oss/NEWS --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/NEWS Thu Mar 7 18:17:46 2002 @@ -0,0 +1,42 @@ +Linux 2.4 Sound Changes +2000-September-25 +Christoph Hellwig, + + + +=== isapnp support + +The Linux 2.4 Kernel does have reliable in-kernel isapnp support. +Some drivers (sb.o, ad1816.o awe_wave.o) do now support automatically +detecting and configuring isapnp devices. +If you have a not yet supported isapnp soundcard, mail me the content +of '/proc/isapnp' on your system and some information about your card +and its driver(s) so I can try to get isapnp working for it. + + + +=== soundcard resources on kernel commandline + +Before Linux 2.4 you had to specify the resources for sounddrivers +statically linked into the kernel at compile time +(in make config/menuconfig/xconfig). In Linux 2.4 the ressources are +now specified at the boot-time kernel commandline (e.g. the lilo +'append=' line or everything that's after the kernel name in grub). +Read the Configure.help entry for your card for the parameters. + + +=== softoss is gone + +In Linux 2.4 the softoss in-kernel software synthesizer is no more aviable. +Use a user space software synthesizer like timidity instead. + + + +=== /dev/sndstat and /proc/sound are gone + +In older Linux versions those files exported some information about the +OSS/Free configuration to userspace. In Linux 2.3 they were removed because +they did not support the growing number of pci soundcards and there were +some general problems with this interface. + + diff -Nru a/Documentation/sound/oss/NM256 b/Documentation/sound/oss/NM256 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/NM256 Thu Mar 7 18:17:39 2002 @@ -0,0 +1,280 @@ +======================================================= +Documentation for the NeoMagic 256AV/256ZX sound driver +======================================================= + +You're looking at version 1.1 of the driver. (Woohoo!) It has been +successfully tested against the following laptop models: + + Sony Z505S/Z505SX/Z505DX/Z505RX + Sony F150, F160, F180, F250, F270, F280, PCG-F26 + Dell Latitude CPi, CPt (various submodels) + +There are a few caveats, which is why you should read the entirety of +this document first. + +This driver was developed without any support or assistance from +NeoMagic. There is no warranty, expressed, implied, or otherwise. It +is free software in the public domain; feel free to use it, sell it, +give it to your best friends, even claim that you wrote it (but why?!) +but don't go whining to me, NeoMagic, Sony, Dell, or anyone else +when it blows up your computer. + +Version 1.1 contains a change to try and detect non-AC97 versions of +the hardware, and not install itself appropriately. It should also +reinitialize the hardware on an APM resume event, assuming that APM +was configured into your kernel. + +============ +Installation +============ + +Enable the sound drivers, the OSS sound drivers, and then the NM256 +driver. The NM256 driver *must* be configured as a module (it won't +give you any other choice). + +Next, do the usual "make modules" and "make modules_install". +Finally, insmod the soundcore, sound and nm256 modules. + +When the nm256 driver module is loaded, you should see a couple of +confirmation messages in the kernel logfile indicating that it found +the device (the device does *not* use any I/O ports or DMA channels). +Now try playing a wav file, futz with the CD-ROM if you have one, etc. + +The NM256 is entirely a PCI-based device, and all the necessary +information is automatically obtained from the card. It can only be +configured as a module in a vain attempt to prevent people from +hurting themselves. It works correctly if it shares an IRQ with +another device (it normally shares IRQ 9 with the builtin eepro100 +ethernet on the Sony Z505 laptops). + +It does not run the card in any sort of compatibility mode. It will +not work on laptops that have the SB16-compatible, AD1848-compatible +or CS4232-compatible codec/mixer; you will want to use the appropriate +compatible OSS driver with these chipsets. I cannot provide any +assistance with machines using the SB16, AD1848 or CS4232 compatible +versions. (The driver now attempts to detect the mixer version, and +will refuse to load if it believes the hardware is not +AC97-compatible.) + +The sound support is very basic, but it does include simultaneous +playback and record capability. The mixer support is also quite +simple, although this is in keeping with the rather limited +functionality of the chipset. + +There is no hardware synthesizer available, as the Losedows OPL-3 and +MIDI support is done via hardware emulation. + +Only three recording devices are available on the Sony: the +microphone, the CD-ROM input, and the volume device (which corresponds +to the stereo output). (Other devices may be available on other +models of laptops.) The Z505 series does not have a builtin CD-ROM, +so of course the CD-ROM input doesn't work. It does work on laptops +with a builtin CD-ROM drive. + +The mixer device does not appear to have any tone controls, at least +on the Z505 series. The mixer module checks for tone controls in the +AC97 mixer, and will enable them if they are available. + +============== +Known problems +============== + + * There are known problems with PCMCIA cards and the eepro100 ethernet + driver on the Z505S/Z505SX/Z505DX. Keep reading. + + * There are also potential problems with using a virtual X display, and + also problems loading the module after the X server has been started. + Keep reading. + + * The volume control isn't anywhere near linear. Sorry. This will be + fixed eventually, when I get sufficiently annoyed with it. (I doubt + it will ever be fixed now, since I've never gotten sufficiently + annoyed with it and nobody else seems to care.) + + * There are reports that the CD-ROM volume is very low. Since I do not + have a CD-ROM equipped laptop, I cannot test this (it's kinda hard to + do remotely). + + * Only 8 fixed-rate speeds are supported. This is mainly a chipset + limitation. It may be possible to support other speeds in the future. + + * There is no support for the telephone mixer/codec. There is support + for a phonein/phoneout device in the mixer driver; whether or not + it does anything is anyone's guess. (Reports on this would be + appreciated. You'll have to figure out how to get the phone to + go off-hook before it'll work, tho.) + + * This driver was not written with any cooperation or support from + NeoMagic. If you have any questions about this, see their website + for their official stance on supporting open source drivers. + +============ +Video memory +============ + +The NeoMagic sound engine uses a portion of the display memory to hold +the sound buffer. (Crazy, eh?) The NeoMagic video BIOS sets up a +special pointer at the top of video RAM to indicate where the top of +the audio buffer should be placed. + +At the present time XFree86 is apparently not aware of this. It will +thus write over either the pointer or the sound buffer with abandon. +(Accelerated-X seems to do a better job here.) + +This implies a few things: + + * Sometimes the NM256 driver has to guess at where the buffer + should be placed, especially if the module is loaded after the + X server is started. It's usually correct, but it will consistently + fail on the Sony F250. + + * Virtual screens greater than 1024x768x16 under XFree86 are + problematic on laptops with only 2.5MB of screen RAM. This + includes all of the 256AV-equipped laptops. (Virtual displays + may or may not work on the 256ZX, which has at least 4MB of + video RAM.) + +If you start having problems with random noise being output either +constantly (this is the usual symptom on the F250), or when windows +are moved around (this is the usual symptom when using a virtual +screen), the best fix is to + + * Don't use a virtual frame buffer. + * Make sure you load the NM256 module before the X server is + started. + +On the F250, it is possible to force the driver to load properly even +after the XFree86 server is started by doing: + + insmod nm256 buffertop=0x25a800 + +This forces the audio buffers to the correct offset in screen RAM. + +One user has reported a similar problem on the Sony F270, although +others apparently aren't seeing any problems. His suggested command +is + + insmod nm256 buffertop=0x272800 + +================= +Official WWW site +================= + +The official site for the NM256 driver is: + + http://www.uglx.org/sony.html + +You should always be able to get the latest version of the driver there, +and the driver will be supported for the foreseeable future. + +============== +Z505RX and IDE +============== + +There appears to be a problem with the IDE chipset on the Z505RX; one +of the symptoms is that sound playback periodically hangs (when the +disk is accessed). The user reporting the problem also reported that +enabling all of the IDE chipset workarounds in the kernel solved the +problem, tho obviously only one of them should be needed--if someone +can give me more details I would appreciate it. + +============================== +Z505S/Z505SX on-board Ethernet +============================== + +If you're using the on-board Ethernet Pro/100 ethernet support on the Z505 +series, I strongly encourage you to download the latest eepro100 driver from +Donald Becker's site: + + ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/test/eepro100.c + +There was a reported problem on the Z505SX that if the ethernet +interface is disabled and reenabled while the sound driver is loaded, +the machine would lock up. I have included a workaround that is +working satisfactorily. However, you may occasionally see a message +about "Releasing interrupts, over 1000 bad interrupts" which indicates +that the workaround is doing its job. + +================================== +PCMCIA and the Z505S/Z505SX/Z505DX +================================== + +There is also a known problem with the Sony Z505S and Z505SX hanging +if a PCMCIA card is inserted while the ethernet driver is loaded, or +in some cases if the laptop is suspended. This is caused by tons of +spurious IRQ 9s, probably generated from the PCMCIA or ACPI bridges. + +There is currently no fix for the problem that works in every case. +The only known workarounds are to disable the ethernet interface +before inserting or removing a PCMCIA card, or with some cards +disabling the PCMCIA card before ejecting it will also help the +problem with the laptop hanging when the card is ejected. + +One user has reported that setting the tcic's cs_irq to some value +other than 9 (like 11) fixed the problem. This doesn't work on my +Z505S, however--changing the value causes the cardmgr to stop seeing +card insertions and removals, cards don't seem to work correctly, and +I still get hangs if a card is inserted when the kernel is booted. + +Using the latest ethernet driver and pcmcia package allows me to +insert an Adaptec 1480A SlimScsi card without the laptop hanging, +although I still have to shut down the card before ejecting or +powering down the laptop. However, similar experiments with a DE-660 +ethernet card still result in hangs when the card is inserted. I am +beginning to think that the interrupts are CardBus-related, since the +Adaptec card is a CardBus card, and the DE-660 is not; however, I +don't have any other CardBus cards to test with. + +====== +Thanks +====== + +First, I want to thank everyone (except NeoMagic of course) for their +generous support and encouragement. I'd like to list everyone's name +here that replied during the development phase, but the list is +amazingly long. + +I will be rather unfair and single out a few people, however: + + Justin Maurer, for being the first random net.person to try it, + and for letting me login to his Z505SX to get it working there + + Edi Weitz for trying out several different versions, and giving + me a lot of useful feedback + + Greg Rumple for letting me login remotely to get the driver + functional on the 256ZX, for his assistance on tracking + down all sorts of random stuff, and for trying out Accel-X + + Zach Brown, for the initial AC97 mixer interface design + + Jeff Garzik, for various helpful suggestions on the AC97 + interface + + "Mr. Bumpy" for feedback on the Z505RX + + Bill Nottingham, for generous assistance in getting the mixer ID + code working + +================= +Previous versions +================= + +Versions prior to 0.3 (aka `noname') had problems with weird artifacts +in the output and failed to set the recording rate properly. These +problems have long since been fixed. + +Versions prior to 0.5 had problems with clicks in the output when +anything other than 16-bit stereo sound was being played, and also had +periodic clicks when recording. + +Version 0.7 first incorporated support for the NM256ZX chipset, which +is found on some Dell Latitude laptops (the CPt, and apparently +some CPi models as well). It also included the generic AC97 +mixer module. + +Version 0.75 renamed all the functions and files with slightly more +generic names. + +Note that previous versions of this document claimed that recording was +8-bit only; it actually has been working for 16-bits all along. diff -Nru a/Documentation/sound/oss/OPL3 b/Documentation/sound/oss/OPL3 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/OPL3 Thu Mar 7 18:17:36 2002 @@ -0,0 +1,6 @@ +A pure OPL3 card is nice and easy to configure. Simply do + +insmod opl3 io=0x388 + +Change the I/O address in the very unlikely case this card is differently +configured diff -Nru a/Documentation/sound/oss/OPL3-SA b/Documentation/sound/oss/OPL3-SA --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/OPL3-SA Thu Mar 7 18:17:45 2002 @@ -0,0 +1,52 @@ +OPL3-SA1 sound driver (opl3sa.o) + +--- +Note: This howto only describes how to setup the OPL3-SA1 chip; this info +does not apply to the SA2, SA3, or SA4. +--- + +The Yamaha OPL3-SA1 sound chip is usually found built into motherboards, and +it's a decent little chip offering a WSS mode, a SB Pro emulation mode, MPU401 +and OPL3 FM Synth capabilities. + +You can enable inclusion of the driver via CONFIG_SOUND_OPL3SA1=m, or +CONFIG_SOUND_OPL3SA1=y through 'make config/xconfig/menuconfig'. + +You'll need to know all of the relevant info (irq, dma, and io port) for the +chip's WSS mode, since that is the mode the kernel sound driver uses, and of +course you'll also need to know about where the MPU401 and OPL3 ports and +IRQs are if you want to use those. + +Here's the skinny on how to load it as a module: + + modprobe opl3sa io=0x530 irq=11 dma=0 dma2=1 mpu_io=0x330 mpu_irq=5 + +Module options in detail: + + io: This is the WSS's port base. + irq: This is the WSS's IRQ. + dma: This is the WSS's DMA line. In my BIOS setup screen this was + listed as "WSS Play DMA" + dma2: This is the WSS's secondary DMA line. My BIOS calls it the + "WSS capture DMA" + + mpu_io: This is the MPU401's port base. + mpu_irq: This is the MPU401's IRQ. + +If you'd like to use the OPL3 FM Synthesizer, make sure you enable +CONFIG_YM3812 (in 'make config'). That'll build the opl3.o module. + +Then a simple 'insmod opl3 io=0x388', and you now have FM Synth. + +You can also use the SoftOSS software synthesizer instead of the builtin OPL3. +Here's how: + +Say 'y' or 'm' to "SoftOSS software wave table engine" in make config. + +If you said yes, the software synth is available once you boot your new +kernel. + +If you chose to build it as a module, just insmod the resulting softoss2.o + +Questions? Comments? + diff -Nru a/Documentation/sound/oss/OPL3-SA2 b/Documentation/sound/oss/OPL3-SA2 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/OPL3-SA2 Thu Mar 7 18:17:38 2002 @@ -0,0 +1,210 @@ +Documentation for the OPL3-SA2, SA3, and SAx driver (opl3sa2.o) +--------------------------------------------------------------- + +Scott Murray, scott@spiteful.org +January 7, 2001 + +NOTE: All trade-marked terms mentioned below are properties of their + respective owners. + + +Supported Devices +----------------- + +This driver is for PnP soundcards based on the following Yamaha audio +controller chipsets: + +YMF711 aka OPL3-SA2 +YMF715 and YMF719 aka OPL3-SA3 + +Up until recently (December 2000), I'd thought the 719 to be a +different chipset, the OPL3-SAx. After an email exhange with +Yamaha, however, it turns out that the 719 is just a re-badged +715, and the chipsets are identical. The chipset detection code +has been updated to reflect this. + +Anyways, all of these chipsets implement the following devices: + +OPL3 FM synthesizer +Soundblaster Pro +Microsoft/Windows Sound System +MPU401 MIDI interface + +Note that this driver uses the MSS device, and to my knowledge these +chipsets enforce an either/or situation with the Soundblaster Pro +device and the MSS device. Since the MSS device has better +capabilities, I have implemented the driver to use it. + + +Mixer Channels +-------------- + +Older versions of this driver (pre-December 2000) had two mixers, +an OPL3-SA2 or SA3 mixer and a MSS mixer. The OPL3-SA[23] mixer +device contained a superset of mixer channels consisting of its own +channels and all of the MSS mixer channels. To simplify the driver +considerably, and to partition functionality better, the OPL3-SA[23] +mixer device now contains has its own specific mixer channels. They +are: + +Volume - Hardware master volume control +Bass - SA3 only, now supports left and right channels +Treble - SA3 only, now supports left and right channels +Microphone - Hardware microphone input volume control +Digital1 - Yamaha 3D enhancement "Wide" mixer + +All other mixer channels (e.g. "PCM", "CD", etc.) now have to be +controlled via the "MS Sound System (CS4231)" mixer. To facilitate +this, the mixer device creation order has been switched so that +the MSS mixer is created first. This allows accessing the majority +of the useful mixer channels even via single mixer-aware tools +such as "aumix". + + +Plug 'n Play +------------ + +In previous kernels (2.2.x), some configuration was required to +get the driver to talk to the card. Being the new millennium and +all, the 2.4.x kernels now support auto-configuration if ISA PnP +support is configured in. Theoretically, the driver even supports +having more than one card in this case. + +With the addition of PnP support to the driver, two new parameters +have been added to control it: + +isapnp - set to 0 to disable ISA PnP card detection + +multiple - set to 0 to disable multiple PnP card detection + + +Optional Parameters +------------------- + +Recent (December 2000) additions to the driver (based on a patch +provided by Peter Englmaier) are two new parameters: + +ymode - Set Yamaha 3D enhancement mode: + 0 = Desktop/Normal 5-12 cm speakers + 1 = Notebook PC (1) 3 cm speakers + 2 = Notebook PC (2) 1.5 cm speakers + 3 = Hi-Fi 16-38 cm speakers + +loopback - Set A/D input source. Useful for echo cancellation: + 0 = Mic Right channel (default) + 1 = Mono output loopback + +The ymode parameter has been tested and does work. The loopback +parameter, however, is untested. Any feedback on its usefulness +would be appreciated. + + +Manual Configuration +-------------------- + +If for some reason you decide not to compile ISA PnP support into +your kernel, or disabled the driver's usage of it by setting the +isapnp parameter as discussed above, then you will need to do some +manual configuration. There are two ways of doing this. The most +common is to use the isapnptools package to initialize the card, and +use the kernel module form of the sound subsystem and sound drivers. +Alternatively, some BIOS's allow manual configuration of installed +PnP devices in a BIOS menu, which should allow using the non-modular +sound drivers, i.e. built into the kernel. + +I personally use isapnp and modules, and do not have access to a PnP +BIOS machine to test. If you have such a beast, configuring the +driver to be built into the kernel should just work (thanks to work +done by David Luyer ). You will still need +to specify settings, which can be done by adding: + +opl3sa2=,,,,, + +to the kernel command line. For example: + +opl3sa2=0x370,5,0,1,0x530,0x330 + +If you are instead using the isapnp tools (as most people have been +before Linux 2.4.x), follow the directions in their documentation to +produce a configuration file. Here is the relevant excerpt I used to +use for my SA3 card from my isapnp.conf: + +(CONFIGURE YMH0800/-1 (LD 0 + +# NOTE: IO 0 is for the unused SoundBlaster part of the chipset. +(IO 0 (BASE 0x0220)) +(IO 1 (BASE 0x0530)) +(IO 2 (BASE 0x0388)) +(IO 3 (BASE 0x0330)) +(IO 4 (BASE 0x0370)) +(INT 0 (IRQ 5 (MODE +E))) +(DMA 0 (CHANNEL 0)) +(DMA 1 (CHANNEL 1)) + +Here, note that: + +Port Acceptable Range Purpose +---- ---------------- ------- +IO 0 0x0220 - 0x0280 SB base address, unused. +IO 1 0x0530 - 0x0F48 MSS base address +IO 2 0x0388 - 0x03F8 OPL3 base address +IO 3 0x0300 - 0x0334 MPU base address +IO 4 0x0100 - 0x0FFE card's own base address for its control I/O ports + +The IRQ and DMA values can be any that are considered acceptable for a +MSS. Assuming you've got isapnp all happy, then you should be able to +do something like the following (which matches up with the isapnp +configuration above): + +modprobe mpu401 +modprobe ad1848 +modprobe opl3sa2 io=0x370 mss_io=0x530 mpu_io=0x330 irq=5 dma=0 dma2=1 +modprobe opl3 io=0x388 + +See the section "Automatic Module Loading" below for how to set up +/etc/modules.conf to automate this. + +An important thing to remember that the opl3sa2 module's io argument is +for it's own control port, which handles the card's master mixer for +volume (on all cards), and bass and treble (on SA3 cards). + + +Troubleshooting +--------------- + +If all goes well and you see no error messages, you should be able to +start using the sound capabilities of your system. If you get an +error message while trying to insert the opl3sa2 module, then make +sure that the values of the various arguments match what you specified +in your isapnp configuration file, and that there is no conflict with +another device for an I/O port or interrupt. Checking the contents of +/proc/ioports and /proc/interrupts can be useful to see if you're +butting heads with another device. + +If you still cannot get the module to load, look at the contents of +your system log file, usually /var/log/messages. If you see the +message "opl3sa2: Unknown Yamaha audio controller version", then you +have a different chipset version than I've encountered so far. Look +for all messages in the log file that start with "opl3sa2: " and see +if they provide any clues. If you do not see the chipset version +message, and none of the other messages present in the system log are +helpful, email me some details and I'll try my best to help. + + +Automatic Module Loading +------------------------ + +Lastly, if you're using modules and want to set up automatic module +loading with kmod, the kernel module loader, here is the section I +currently use in my modules.conf file: + +# Sound +alias sound-slot-0 opl3sa2 +options opl3sa2 io=0x370 mss_io=0x530 mpu_io=0x330 irq=7 dma=0 dma2=3 +options opl3 io=0x388 + +That's all it currently takes to get an OPL3-SA3 card working on my +system. Once again, if you have any other problems, email me at the +address listed above. + +Scott diff -Nru a/Documentation/sound/oss/Opti b/Documentation/sound/oss/Opti --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/Opti Thu Mar 7 18:17:42 2002 @@ -0,0 +1,222 @@ +Support for the OPTi 82C931 chip +-------------------------------- +Note: parts of this README file apply also to other +cards that use the mad16 driver. + +Some items in this README file are based on features +added to the sound driver after Linux-2.1.91 was out. +By the time of writing this I do not know which official +kernel release will include these features. +Please do not report inconsistencies on older Linux +kernels. + +The OPTi 82C931 is supported in its non-PnP mode. +Usually you do not need to set jumpers, etc. The sound driver +will check the card status and if it is required it will +force the card into a mode in which it can be programmed. + +If you have another OS installed on your computer it is recommended +that Linux and the other OS use the same resources. + +Also, it is recommended that resources specified in /etc/modules.conf +and resources specified in /etc/isapnp.conf agree. + +Compiling the sound driver +-------------------------- +I highly recommend that you build a modularized sound driver. +This document does not cover a sound-driver which is built in +the kernel. + +Sound card support should be enabled as a module (chose m). +Answer 'm' for these items: + Generic OPL2/OPL3 FM synthesizer support (CONFIG_SOUND_ADLIB) + Microsoft Sound System support (CONFIG_SOUND_MSS) + Support for OPTi MAD16 and/or Mozart based cards (CONFIG_SOUND_MAD16) + FM synthesizer (YM3812/OPL-3) support (CONFIG_SOUND_YM3812) + +The configuration menu may ask for addresses, IRQ lines or DMA +channels. If the card is used as a module the module loading +options will override these values. + +For the OPTi 931 you can answer 'n' to: + Support MIDI in older MAD16 based cards (requires SB) (CONFIG_SOUND_MAD16_OLDCARD) +If you do need MIDI support in a Mozart or C928 based card you +need to answer 'm' to the above question. In that case you will +also need to answer 'm' to: + '100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' (CONFIG_SOUND_SB) + +Go on and compile your kernel and modules. Install the modules. Run depmod -a. + +Using isapnptools +----------------- +In most systems with a PnP BIOS you do not need to use isapnp. The +initialization provided by the BIOS is sufficient for the driver +to pick up the card and continue initialization. + +If that fails, or if you have other PnP cards, you need to use isapnp +to initialize the card. +This was tested with isapnptools-1.11 but I recommend that you use +isapnptools-1.13 (or newer). Run pnpdump to dump the information +about your PnP cards. Then edit the resulting file and select +the options of your choice. This file is normally installed as +/etc/isapnp.conf. + +The driver has one limitation with respect to I/O port resources: +IO3 base must be 0x0E0C. Although isapnp allows other ports, this +address is hard-coded into the driver. + +Using kmod and autoloading the sound driver +------------------------------------------- +Comment: as of linux-2.1.90 kmod is replacing kerneld. +The config file '/etc/modules.conf' is used as before. + +This is the sound part of my /etc/modules.conf file. +Following that I will explain each line. + +alias mixer0 mad16 +alias audio0 mad16 +alias midi0 mad16 +alias synth0 opl3 +options sb mad16=1 +options mad16 irq=10 dma=0 dma16=1 io=0x530 joystick=1 cdtype=0 +options opl3 io=0x388 +post-install mad16 /sbin/ad1848_mixer_reroute 14 8 15 3 16 6 + +If you have an MPU daughtercard or onboard MPU you will want to add to the +"options mad16" line - eg + +options mad16 irq=5 dma=0 dma16=3 io=0x530 mpu_io=0x330 mpu_irq=9 + +To set the I/O and IRQ of the MPU. + + +Explain: + +alias mixer0 mad16 +alias audio0 mad16 +alias midi0 mad16 +alias synth0 opl3 + +When any sound device is opened the kernel requests auto-loading +of char-major-14. There is a built-in alias that translates this +request to loading the main sound module. + +The sound module in its turn will request loading of a sub-driver +for mixer, audio, midi or synthesizer device. The first 3 are +supported by the mad16 driver. The synth device is supported +by the opl3 driver. + +There is currently no way to autoload the sound device driver +if more than one card is installed. + +options sb mad16=1 + +This is left for historical reasons. If you enable the +config option 'Support MIDI in older MAD16 based cards (requires SB)' +or if you use an older mad16 driver it will force loading of the +SoundBlaster driver. This option tells the SB driver not to look +for a SB card but to wait for the mad16 driver. + +options mad16 irq=10 dma=0 dma16=1 io=0x530 joystick=1 cdtype=0 +options opl3 io=0x388 + +post-install mad16 /sbin/ad1848_mixer_reroute 14 8 15 3 16 6 + +This sets resources and options for the mad16 and opl3 drivers. +I use two DMA channels (only one is required) to enable full duplex. +joystick=1 enables the joystick port. cdtype=0 disables the cd port. +You can also set mpu_io and mpu_irq in the mad16 options for the +uart401 driver. + +This tells modprobe to run /sbin/ad1848_mixer_reroute after +mad16 is successfully loaded and initialized. The source +for ad1848_mixer_reroute is appended to the end of this readme +file. It is impossible for the sound driver to know the actual +connections to the mixer. The 3 inputs intended for cd, synth +and line-in are mapped to the generic inputs line1, line2 and +line3. This program reroutes these mixer channels to their +right names (note the right mapping depends on the actual sound +card that you use). +The numeric parameters mean: + 14=line1 8=cd - reroute line1 to the CD input. + 15=line2 3=synth - reroute line2 to the synthesizer input. + 16=line3 6=line - reroute line3 to the line input. +For reference on other input names look at the file +/usr/include/linux/soundcard.h. + +Using a joystick +----------------- +You must enable a joystick in the mad16 options. (also +in /etc/isapnp.conf if you use it). +Tested with regular analog joysticks. + +A CDROM drive connected to the sound card +----------------------------------------- +The 82C931 chip has support only for secondary ATAPI cdrom. +(cdtype=8). Loading the mad16 driver resets the C931 chip +and if a cdrom was already mounted it may cause a complete +system hang. Do not use the sound card if you have an alternative. +If you do use the sound card it is important that you load +the mad16 driver (use "modprobe mad16" to prevent auto-unloading) +before the cdrom is accessed the first time. + +Using the sound driver built-in to the kernel may help here, but... +Most new systems have a PnP BIOS and also two IDE controllers. +The IDE controller on the sound card may be needed only on older +systems (which have only one IDE controller) but these systems +also do not have a PnP BIOS - requiring isapnptools and a modularized +driver. + +Known problems +-------------- +1. See the section on "A CDROM drive connected to the sound card". + +2. On my system the codec cannot capture companded sound samples. + (eg., recording from /dev/audio). When any companded capture is + requested I get stereo-16 bit samples instead. Playback of + companded samples works well. Apparently this problem is not common + to all C931 based cards. I do not know how to identify cards that + have this problem. + +Source for ad1848_mixer_reroute.c +--------------------------------- +#include +#include +#include + +static char *mixer_names[SOUND_MIXER_NRDEVICES] = + SOUND_DEVICE_LABELS; + +int +main(int argc, char **argv) { + int val, from, to; + int i, fd; + + fd = open("/dev/mixer", O_RDWR); + if(fd < 0) { + perror("/dev/mixer"); + return 1; + } + + for(i = 2; i < argc; i += 2) { + from = atoi(argv[i-1]); + to = atoi(argv[i]); + + if(to == SOUND_MIXER_NONE) + fprintf(stderr, "%s: turning off mixer %s\n", + argv[0], mixer_names[to]); + else + fprintf(stderr, "%s: rerouting mixer %s to %s\n", + argv[0], mixer_names[from], mixer_names[to]); + + val = from << 8 | to; + + if(ioctl(fd, SOUND_MIXER_PRIVATE2, &val)) { + perror("AD1848 mixer reroute"); + return 1; + } + } + + return 0; +} + diff -Nru a/Documentation/sound/oss/PAS16 b/Documentation/sound/oss/PAS16 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/PAS16 Thu Mar 7 18:17:43 2002 @@ -0,0 +1,163 @@ +Pro Audio Spectrum 16 for 2.3.99 and later +========================================= +by Thomas Molina (tmolina@home.com) +last modified 3 Mar 2001 +Acknowledgement to Axel Boldt (boldt@math.ucsb.edu) for stuff taken +from Configure.help, Riccardo Facchetti for stuff from README.OSS, +and others whose names I could not find. + +This documentation is relevant for the PAS16 driver (pas2_card.c and +friends) under kernel version 2.3.99 and later. If you are +unfamiliar with configuring sound under Linux, please read the +Sound-HOWTO, linux/Documentation/sound/Introduction and other +relevant docs first. + +The following information is relevant information from README.OSS +and legacy docs for the Pro Audio Spectrum 16 (PAS16): +================================================================== + +The pas2_card.c driver supports the following cards -- +Pro Audio Spectrum 16 (PAS16) and compatibles: + Pro Audio Spectrum 16 + Pro Audio Studio 16 + Logitech Sound Man 16 + NOTE! The original Pro Audio Spectrum as well as the PAS+ are not + and will not be supported by the driver. + +The sound driver configuration dialog +------------------------------------- + +Sound configuration starts by making some yes/no questions. Be careful +when answering to these questions since answering y to a question may +prevent some later ones from being asked. For example don't answer y to +the question about (PAS16) if you don't really have a PAS16. Sound +configuration may also be made modular by answering m to configuration +options presented. + +Note also that all questions may not be asked. The configuration program +may disable some questions depending on the earlier choices. It may also +select some options automatically as well. + + "ProAudioSpectrum 16 support", + - Answer 'y'_ONLY_ if you have a Pro Audio Spectrum _16_, + Pro Audio Studio 16 or Logitech SoundMan 16 (be sure that + you read the above list correctly). Don't answer 'y' if you + have some other card made by Media Vision or Logitech since they + are not PAS16 compatible. + NOTE! Since 3.5-beta10 you need to enable SB support (next question) + if you want to use the SB emulation of PAS16. It's also possible to + the emulation if you want to use a true SB card together with PAS16 + (there is another question about this that is asked later). + + "Generic OPL2/OPL3 FM synthesizer support", + - Answer 'y' if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). + The PAS16 has an OPL3-compatible FM chip. + +With PAS16 you can use two audio device files at the same time. /dev/dsp (and +/dev/audio) is connected to the 8/16 bit native codec and the /dev/dsp1 (and +/dev/audio1) is connected to the SB emulation (8 bit mono only). + + +The new stuff for 2.3.99 and later +============================================================================ +The following configuration options from linux/Documentation/Configure.help +are relevant to configuring the PAS16: + +Sound card support +CONFIG_SOUND + If you have a sound card in your computer, i.e. if it can say more + than an occasional beep, say Y. Be sure to have all the information + about your sound card and its configuration down (I/O port, + 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 + about the modular sound system is contained in the files + Documentation/sound/Introduction. The file + Documentation/sound/README.OSS contains some slightly outdated but + still useful information as well. + +OSS sound modules +CONFIG_SOUND_OSS + OSS is the Open Sound System suite of sound card drivers. They make + sound programming easier since they provide a common API. Say Y or M + here (the module will be called sound.o) if you haven't found a + driver for your sound card above, then pick your driver from the + list below. + +Persistent DMA buffers +CONFIG_SOUND_DMAP + Linux can often have problems allocating DMA buffers for ISA sound + cards on machines with more than 16MB of RAM. This is because ISA + DMA buffers must exist below the 16MB boundary and it is quite + possible that a large enough free block in this region cannot be + found after the machine has been running for a while. If you say Y + here the DMA buffers (64Kb) will be allocated at boot time and kept + until the shutdown. This option is only useful if you said Y to + "OSS sound modules", above. If you said M to "OSS sound modules" + then you can get the persistent DMA buffer functionality by passing + the command-line argument "dmabuf=1" to the sound.o module. + + Say y here for PAS16. + +ProAudioSpectrum 16 support +CONFIG_SOUND_PAS + Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio + 16 or Logitech SoundMan 16 sound card. Don't answer Y if you have + some other card made by Media Vision or Logitech since they are not + PAS16 compatible. It is not necessary to enable the separate + Sound Blaster support; it is included in the PAS driver. + + If you compile the driver into the kernel, you have to add + "pas2=,,,,,,, + to the kernel command line. + +FM Synthesizer (YM3812/OPL-3) support +CONFIG_SOUND_YM3812 + Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). + Answering Y is usually a safe and recommended choice, however some + cards may have software (TSR) FM emulation. Enabling FM support with + these cards may cause trouble (I don't currently know of any such + cards, however). + Please read the file Documentation/sound/OPL3 if your card has an + OPL3 chip. + If you compile the driver into the kernel, you have to add + "opl3=" to the kernel command line. + + If you compile your drivers into the kernel, you MUST configure + OPL3 support as a module for PAS16 support to work properly. + You can then get OPL3 functionality by issuing the command: + insmod opl3 + In addition, you must either add the following line to + /etc/modules.conf: + options opl3 io=0x388 + or else add the following line to /etc/lilo.conf: + opl3=0x388 + + +EXAMPLES +=================================================================== +To use the PAS16 in my computer I have enabled the following sound +configuration options: + +CONFIG_SOUND=y +CONFIG_SOUND_OSS=y +CONFIG_SOUND_TRACEINIT=y +CONFIG_SOUND_DMAP=y +CONFIG_SOUND_PAS=y +CONFIG_SOUND_SB=n +CONFIG_SOUND_YM3812=m + +I have also included the following append line in /etc/lilo.conf: +append="pas2=0x388,10,3,-1,0x220,5,1,-1 sb=0x220,5,1,-1 opl3=0x388" + +The io address of 0x388 is default configuration on the PAS16. The +irq of 10 and dma of 3 may not match your installation. The above +configuration enables PAS16, 8-bit Soundblaster and OPL3 +functionality. If Soundblaster functionality is not desired, the +following line would be appropriate: +append="pas2=0x388,10,3,-1,0,-1,-1,-1 opl3=0x388" + +If sound is built totally modular, the above options may be +specified in /etc/modules.conf for pas2.o, sb.o and opl3.o +respectively. diff -Nru a/Documentation/sound/oss/PSS b/Documentation/sound/oss/PSS --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/PSS Thu Mar 7 18:17:43 2002 @@ -0,0 +1,41 @@ +The PSS cards and other ECHO based cards provide an onboard DSP with +downloadable programs and also has an AD1848 "Microsoft Sound System" +device. The PSS driver enables MSS and MPU401 modes of the card. SB +is not enabled since it doesn't work concurrently with MSS. + +If you build this driver as a module then the driver takes the following +parameters + +pss_io. The I/O base the PSS card is configured at (normally 0x220 + or 0x240) + +mss_io The base address of the Microsoft Sound System interface. + This is normally 0x530, but may be 0x604 or other addresses. + +mss_irq The interrupt assigned to the Microsoft Sound System + emulation. IRQ's 3,5,7,9,10,11 and 12 are available. If you + get IRQ errors be sure to check the interrupt is set to + "ISA/Legacy" in the BIOS on modern machines. + +mss_dma The DMA channel used by the Microsoft Sound System. + This can be 0, 1, or 3. DMA 0 is not available on older + machines and will cause a crash on them. + +mpu_io The MPU emulation base address. This sets the base of the + synthesizer. It is typically 0x330 but can be altered. + +mpu_irq The interrupt to use for the synthesizer. It must differ + from the IRQ used by the Microsoft Sound System port. + + +The mpu_io/mpu_irq fields are optional. If they are not specified the +synthesizer parts are not configured. + +When the module is loaded it looks for a file called +/etc/sound/pss_synth. This is the firmware file from the DOS install disks. +This fil holds a general MIDI emulation. The file expected is called +genmidi.ld on newer DOS driver install disks and synth.ld on older ones. + +You can also load alternative DSP algorithms into the card if you wish. One +alternative driver can be found at http://www.mpg123.de/ + diff -Nru a/Documentation/sound/oss/PSS-updates b/Documentation/sound/oss/PSS-updates --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/PSS-updates Thu Mar 7 18:17:44 2002 @@ -0,0 +1,88 @@ + This file contains notes for users of PSS sound cards who wish to use the +newly added features of the newest version of this driver. + + The major enhancements present in this new revision of this driver is the +addition of two new module parameters that allow you to take full advantage of +all the features present on your PSS sound card. These features include the +ability to enable both the builtin CDROM and joystick ports. + +pss_enable_joystick + + This parameter is basically a flag. A 0 will leave the joystick port +disabled, while a non-zero value would enable the joystick port. The default +setting is pss_enable_joystick=0 as this keeps this driver fully compatable +with systems that were using previous versions of this driver. If you wish to +enable the joystick port you will have to add pss_enable_joystick=1 as an +argument to the driver. To actually use the joystick port you will then have +to load the joystick driver itself. Just remember to load the joystick driver +AFTER the pss sound driver. + +pss_cdrom_port + + This parameter takes a port address as its parameter. Any available port +address can be specified to enable the CDROM port, except for 0x0 and -1 as +these values would leave the port disabled. Like the joystick port, the cdrom +port will require that an appropiate CDROM driver be loaded before you can make +use of the newly enabled CDROM port. Like the joystick port option above, +remember to load the CDROM driver AFTER the pss sound driver. While it may +differ on some PSS sound cards, all the PSS sound cards that I have seen have a +builtin Wearnes CDROM port. If this is the case with your PSS sound card you +should load aztcd with the appropiate port option that matches the port you +assigned to the CDROM port when you loaded your pss sound driver. (ex. +modprobe pss pss_cdrom_port=0x340 && modprobe aztcd aztcd=0x340) The default +setting of this parameter leaves the CDROM port disabled to maintain full +compatability with systems using previous versions of this driver. + + Other options have also been added for the added convenience and utility +of the user. These options are only available if this driver is loaded as a +module. + +pss_no_sound + + This module parameter is a flag that can be used to tell the driver to +just configure non-sound components. 0 configures all components, a non-0 +value will only attept to configure the CDROM and joystick ports. This +parameter can be used by a user who only wished to use the builtin joystick +and/or CDROM port(s) of his PSS sound card. If this driver is loaded with this +parameter and with the paramter below set to true then a user can safely unload +this driver with the following command "rmmod pss && rmmod ad1848 && rmmod +mpu401 && rmmod sound && rmmod soundcore" and retain the full functionality of +his CDROM and/or joystick port(s) while gaining back the memory previously used +by the sound drivers. This default setting of this parameter is 0 to retain +full behavioral compatability with previous versions of this driver. + +pss_keep_settings + + This parameter can be used to specify whether you want the driver to reset +all emulations whenever its unloaded. This can be useful for those who are +sharing resources (io ports, IRQ's, DMA's) between different ISA cards. This +flag can also be useful in that future versions of this driver may reset all +emulations by default on the driver's unloading (as it probably should), so +specifying it now will ensure that all future versions of this driver will +continue to work as expected. The default value of this parameter is 1 to +retain full behavioral compatability with previous versions of this driver. + +pss_firmware + + This parameter can be used to specify the file containing the firmware +code so that a user could tell the driver where that file is located instead +of having to put it in a predefined location with a predefined name. The +default setting of this parameter is "/etc/sound/pss_synth" as this was the +path and filename the hardcoded value in the previous versions of this driver. + +Examples: + +# Normal PSS sound card system, loading of drivers. +# Should be specified in an rc file (ex. Slackware uses /etc/rc.d/rc.modules). + +/sbin/modprobe pss pss_io=0x220 mpu_io=0x338 mpu_irq=9 mss_io=0x530 mss_irq=10 mss_dma=1 pss_cdrom_port=0x340 pss_enable_joystick=1 +/sbin/modprobe aztcd aztcd=0x340 +/sbin/modprobe joystick + +# System using the PSS sound card just for its CDROM and joystick ports. +# Should be specified in an rc file (ex. Slackware uses /etc/rc.d/rc.modules). + +/sbin/modprobe pss pss_io=0x220 pss_cdrom_port=0x340 pss_enable_joystick=1 pss_no_sound=1 +/sbin/rmmod pss && /sbin/rmmod ad1848 && /sbin/rmmod mpu401 && /sbin/rmmod sound && /sbin/rmmod soundcore # This line not needed, but saves memory. +/sbin/modprobe aztcd aztcd=0x340 +/sbin/modprobe joystick diff -Nru a/Documentation/sound/oss/README.OSS b/Documentation/sound/oss/README.OSS --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/README.OSS Thu Mar 7 18:17:37 2002 @@ -0,0 +1,1456 @@ +Introduction +------------ + +This file is a collection of all the old Readme files distributed with +OSS/Lite by Hannu Savolainen. Since the new Linux sound driver is founded +on it I think these information may still be interesting for users that +have to configure their sound system. + +Be warned: Alan Cox is the current maintainer of the Linux sound driver so if +you have problems with it, please contact him or the current device-specific +driver maintainer (e.g. for aedsp16 specific problems contact me). If you have +patches, contributions or suggestions send them to Alan: I'm sure they are +welcome. + +In this document you will find a lot of references about OSS/Lite or ossfree: +they are gone forever. Keeping this in mind and with a grain of salt this +document can be still interesting and very helpful. + +[ File edited 17.01.1999 - Riccardo Facchetti ] +[ Edited miroSOUND section 19.04.2001 - Robert Siemer ] + +OSS/Free version 3.8 release notes +---------------------------------- + +Please read the SOUND-HOWTO (available from sunsite.unc.edu and other Linux FTP +sites). It gives instructions about using sound with Linux. It's bit out of +date but still very useful. Information about bug fixes and such things +is available from the web page (see above). + +Please check http://www.opensound.com/pguide for more info about programming +with OSS API. + + ==================================================== +- THIS VERSION ____REQUIRES____ Linux 2.1.57 OR LATER. + ==================================================== + +Packages "snd-util-3.8.tar.gz" and "snd-data-0.1.tar.Z" +contain useful utilities to be used with this driver. +See http://www.opensound.com/ossfree/getting.html for +download instructions. + +If you are looking for the installation instructions, please +look forward into this document. + +Supported sound cards +--------------------- + +See below. + +Contributors +------------ + +This driver contains code by several contributors. In addition several other +persons have given useful suggestions. The following is a list of major +contributors. (I could have forgotten some names.) + + Craig Metz 1/2 of the PAS16 Mixer and PCM support + Rob Hooft Volume computation algorithm for the FM synth. + Mika Liljeberg uLaw encoding and decoding routines + Jeff Tranter Linux SOUND HOWTO document + Greg Lee Volume computation algorithm for the GUS and + lots of valuable suggestions. + Andy Warner ISC port + Jim Lowe, + Amancio Hasty Jr FreeBSD/NetBSD port + Anders Baekgaard Bug hunting and valuable suggestions. + Joerg Schubert SB16 DSP support (initial version). + Andrew Robinson Improvements to the GUS driver + Megens SA MIDI recording for SB and SB Pro (initial version). + Mikael Nordqvist Linear volume support for GUS and + nonblocking /dev/sequencer. + Ian Hartas SVR4.2 port + Markus Aroharju and + Risto Kankkunen Major contributions to the mixer support + of GUS v3.7. + Hunyue Yau Mixer support for SG NX Pro. + Marc Hoffman PSS support (initial version). + Rainer Vranken Initialization for Jazz16 (initial version). + Peter Trattler Initial version of loadable module support for Linux. + JRA Gibson 16 bit mode for Jazz16 (initial version) + Davor Jadrijevic MAD16 support (initial version) + Gregor Hoffleit Mozart support (initial version) + Riccardo Facchetti Audio Excel DSP 16 (aedsp16) support + James Hightower Spotting a tiny but important bug in CS423x support. + Denis Sablic OPTi 82C924 specific enhancements (non PnP mode) + Tim MacKenzie Full duplex support for OPTi 82C930. + + Please look at lowlevel/README for more contributors. + +There are probably many other names missing. If you have sent me some +patches and your name is not in the above list, please inform me. + +Sending your contributions or patches +------------------------------------- + +First of all it's highly recommended to contact me before sending anything +or before even starting to do any work. Tell me what you suggest to be +changed or what you have planned to do. Also ensure you are using the +very latest (development) version of OSS/Free since the change may already be +implemented there. In general it's a major waste of time to try to improve a +several months old version. Information about the latest version can be found +from http://www.opensound.com/ossfree. In general there is no point in +sending me patches relative to production kernels. + +Sponsors etc. +------------- + +The following companies have greatly helped development of this driver +in form of a free copy of their product: + +Novell, Inc. UnixWare personal edition + SDK +The Santa Cruz Operation, Inc. A SCO OpenServer + SDK +Ensoniq Corp, a SoundScape card and extensive amount of assistance +MediaTrix Peripherals Inc, a AudioTrix Pro card + SDK +Acer, Inc. a pair of AcerMagic S23 cards. + +In addition the following companies have provided me sufficient amount +of technical information at least some of their products (free or $$$): + +Advanced Gravis Computer Technology Ltd. +Media Vision Inc. +Analog Devices Inc. +Logitech Inc. +Aztech Labs Inc. +Crystal Semiconductor Corporation, +Integrated Circuit Systems Inc. +OAK Technology +OPTi +Turtle Beach +miro +Ad Lib Inc. ($$) +Music Quest Inc. ($$) +Creative Labs ($$$) + +If you have some problems +========================= + +Read the sound HOWTO (sunsite.unc.edu:/pub/Linux/docs/...?). +Also look at the home page (http://www.opensound.com/ossfree). It may +contain info about some recent bug fixes. + +It's likely that you have some problems when trying to use the sound driver +first time. Sound cards don't have standard configuration so there are no +good default configuration to use. Please try to use same I/O, DMA and IRQ +values for the sound card than with DOS. + +If you get an error message when trying to use the driver, please look +at /var/adm/messages for more verbose error message. + + +The following errors are likely with /dev/dsp and /dev/audio. + + - "No such device or address". + This error indicates that there are no suitable hardware for the + device file or the sound driver has been compiled without support for + this particular device. For example /dev/audio and /dev/dsp will not + work if "digitized voice support" was not enabled during "make config". + + - "Device or resource busy". Probably the IRQ (or DMA) channel + required by the sound card is in use by some other device/driver. + + - "I/O error". Almost certainly (99%) it's an IRQ or DMA conflict. + Look at the kernel messages in /var/adm/notice for more info. + + - "Invalid argument". The application is calling ioctl() + with impossible parameters. Check that the application is + for sound driver version 2.X or later. + +Linux installation +================== + +IMPORTANT! Read this if you are installing a separately + distributed version of this driver. + + Check that your kernel version works with this + release of the driver (see Readme). Also verify + that your current kernel version doesn't have more + recent sound driver version than this one. IT'S HIGHLY + RECOMMENDED THAT YOU USE THE SOUND DRIVER VERSION THAT + IS DISTRIBUTED WITH KERNEL SOURCES. + +- When installing separately distributed sound driver you should first + read the above notice. Then try to find proper directory where and how + to install the driver sources. You should not try to install a separately + distributed driver version if you are not able to find the proper way + yourself (in this case use the version that is distributed with kernel + sources). Remove old version of linux/drivers/sound directory before + installing new files. + +- To build the device files you need to run the enclosed shell script + (see below). You need to do this only when installing sound driver + first time or when upgrading to much recent version than the earlier + one. + +- Configure and compile Linux as normally (remember to include the + sound support during "make config"). Please refer to kernel documentation + for instructions about configuring and compiling kernel. File Readme.cards + contains card specific instructions for configuring this driver for + use with various sound cards. + +Boot time configuration (using lilo and insmod) +----------------------------------------------- + +This information has been removed. Too many users didn't believe +that it's really not necessary to use this method. Please look at +Readme of sound driver version 3.0.1 if you still want to use this method. + +Problems +-------- + +Common error messages: + +- /dev/???????: No such file or directory. +Run the script at the end of this file. + +- /dev/???????: No such device. +You are not running kernel which contains the sound driver. When using +modularized sound driver this error means that the sound driver is not +loaded. + +- /dev/????: No such device or address. +Sound driver didn't detect suitable card when initializing. Please look at +Readme.cards for info about configuring the driver with your card. Also +check for possible boot (insmod) time error messages in /var/adm/messages. + +- Other messages or problems +Please check http://www.opensound.com/ossfree for more info. + +Configuring version 3.8 (for Linux) with some common sound cards +================================================================ + +This document describes configuring sound cards with the freeware version of +Open Sound Systems (OSS/Free). Information about the commercial version +(OSS/Linux) and its configuration is available from +http://www.opensound.com/linux.html. Information presented here is +not valid for OSS/Linux. + +If you are unsure about how to configure OSS/Free +you can download the free evaluation version of OSS/Linux from the above +address. There is a chance that it can autodetect your sound card. In this case +you can use the information included in soundon.log when configuring OSS/Free. + + +IMPORTANT! This document covers only cards that were "known" when + this driver version was released. Please look at + http://www.opensound.com/ossfree for info about + cards introduced recently. + + When configuring the sound driver, you should carefully + check each sound configuration option (particularly + "Support for /dev/dsp and /dev/audio"). The default values + offered by these programs are not necessarily valid. + + +THE BIGGEST MISTAKES YOU CAN MAKE +================================= + +1. Assuming that the card is Sound Blaster compatible when it's not. +-------------------------------------------------------------------- + +The number one mistake is to assume that your card is compatible with +Sound Blaster. Only the cards made by Creative Technology or which have +one or more chips labeled by Creative are SB compatible. In addition there +are few sound chipsets which are SB compatible in Linux such as ESS1688 or +Jazz16. Note that SB compatibility in DOS/Windows does _NOT_ mean anything +in Linux. + +IF YOU REALLY ARE 150% SURE YOU HAVE A SOUND BLASTER YOU CAN SKIP THE REST OF +THIS CHAPTER. + +For most other "supposed to be SB compatible" cards you have to use other +than SB drivers (see below). It is possible to get most sound cards to work +in SB mode but in general it's a complete waste of time. There are several +problems which you will encounter by using SB mode with cards that are not +truly SB compatible: + +- The SB emulation is at most SB Pro (DSP version 3.x) which means that +you get only 8 bit audio (there is always an another ("native") mode which +gives the 16 bit capability). The 8 bit only operation is the reason why +many users claim that sound quality in Linux is much worse than in DOS. +In addition some applications require 16 bit mode and they produce just +noise with a 8 bit only device. +- The card may work only in some cases but refuse to work most of the +time. The SB compatible mode always requires special initialization which is +done by the DOS/Windows drivers. This kind of cards work in Linux after +you have warm booted it after DOS but they don't work after cold boot +(power on or reset). +- You get the famous "DMA timed out" messages. Usually all SB clones have +software selectable IRQ and DMA settings. If the (power on default) values +currently used by the card don't match configuration of the driver you will +get the above error message whenever you try to record or play. There are +few other reasons to the DMA timeout message but using the SB mode seems +to be the most common cause. + +2. Trying to use a PnP (Plug & Play) card just like an ordinary sound card +-------------------------------------------------------------------------- + +Plug & Play is a protocol defined by Intel and Microsoft. It lets operating +systems to easily identify and reconfigure I/O ports, IRQs and DMAs of ISA +cards. The problem with PnP cards is that the standard Linux doesn't currently +(versions 2.1.x and earlier) don't support PnP. This means that you will have +to use some special tricks (see later) to get a PnP card alive. Many PnP cards +work after they have been initialized but this is not always the case. + +There are sometimes both PnP and non-PnP versions of the same sound card. +The non-PnP version is the original model which usually has been discontinued +more than an year ago. The PnP version has the same name but with "PnP" +appended to it (sometimes not). This causes major confusion since the non-PnP +model works with Linux but the PnP one doesn't. + +You should carefully check if "Plug & Play" or "PnP" is mentioned in the name +of the card or in the documentation or package that came with the card. +Everything described in the rest of this document is not necessarily valid for +PnP models of sound cards even you have managed to wake up the card properly. +Many PnP cards are simply too different from their non-PnP ancestors which are +covered by this document. + + +Cards that are not (fully) supported by this driver +=================================================== + +See http://www.opensound.com/ossfree for information about sound cards +to be supported in future. + + +How to use sound without recompiling kernel and/or sound driver +=============================================================== + +There is a commercial sound driver which comes in precompiled form and doesn't +require recompiling of the kernel. See http://www.4Front-tech.com/oss.html for +more info. + + +Configuring PnP cards +===================== + +New versions of most sound cards use the so-called ISA PnP protocol for +soft configuring their I/O, IRQ, DMA and shared memory resources. +Currently at least cards made by Creative Technology (SB32 and SB32AWE +PnP), Gravis (GUS PnP and GUS PnP Pro), Ensoniq (Soundscape PnP) and +Aztech (some Sound Galaxy models) use PnP technology. The CS4232/4236 audio +chip by Crystal Semiconductor (Intel Atlantis, HP Pavilion and many other +motherboards) is also based on PnP technology but there is a "native" driver +available for it (see information about CS4232 later in this document). + +PnP sound cards (as well as most other PnP ISA cards) are not supported +by this version of the driver . Proper +support for them should be released during 97 once the kernel level +PnP support is available. + +There is a method to get most of the PnP cards to work. The basic method +is the following: + +1) Boot DOS so the card's DOS drivers have a chance to initialize it. +2) _Cold_ boot to Linux by using "loadlin.exe". Hitting ctrl-alt-del +works with older machines but causes a hard reset of all cards on recent +(Pentium) machines. +3) If you have the sound driver in Linux configured properly, the card should +work now. "Proper" means that I/O, IRQ and DMA settings are the same as in +DOS. The hard part is to find which settings were used. See the documentation of +your card for more info. + +Windows 95 could work as well as DOS but running loadlin may be difficult. +Probably you should "shut down" your machine to MS-DOS mode before running it. + +Some machines have a BIOS utility for setting PnP resources. This is a good +way to configure some cards. In this case you don't need to boot DOS/Win95 +before starting Linux. + +Another way to initialize PnP cards without DOS/Win95 is a Linux based +PnP isolation tool. When writing this there is a pre alpha test version +of such a tool available from ftp://ftp.demon.co.uk/pub/unix/linux/utils. The +file is called isapnptools-*. Please note that this tool is just a temporary +solution which may be incompatible with future kernel versions having proper +support for PnP cards. There are bugs in setting DMA channels in earlier +versions of isapnptools so at least version 1.6 is required with sound cards. + +Yet another way to use PnP cards is to use (commercial) OSS/Linux drivers. See +http://www.opensound.com/linux.html for more info. This is probably the way you +should do it if you don't want to spend time recompiling the kernel and +required tools. + + +Read this before trying to configure the driver +=============================================== + +There are currently many cards that work with this driver. Some of the cards +have native support while others work since they emulate some other +card (usually SB, MSS/WSS and/or MPU401). The following cards have native +support in the driver. Detailed instructions for configuring these cards +will be given later in this document. + +Pro Audio Spectrum 16 (PAS16) and compatibles: + Pro Audio Spectrum 16 + Pro Audio Studio 16 + Logitech Sound Man 16 + NOTE! The original Pro Audio Spectrum as well as the PAS+ are not + and will not be supported by the driver. + +Media Vision Jazz16 based cards + Pro Sonic 16 + Logitech SoundMan Wave + (Other Jazz based cards should work but I don't have any reports + about them). + +Sound Blasters + SB 1.0 to 2.0 + SB Pro + SB 16 + SB32/64/AWE + Configure SB32/64/AWE just like SB16. See lowlevel/README.awe + for information about using the wave table synth. + NOTE! AWE63/Gold and 16/32/AWE "PnP" cards need to be activated + using isapnptools before they work with OSS/Free. + SB16 compatible cards by other manufacturers than Creative. + You have been fooled since there are _no_ SB16 compatible + cards on the market (as of May 1997). It's likely that your card + is compatible just with SB Pro but there is also a non-SB- + compatible 16 bit mode. Usually it's MSS/WSS but it could also + be a proprietary one like MV Jazz16 or ESS ES688. OPTi + MAD16 chips are very common in so called "SB 16 bit cards" + (try with the MAD16 driver). + + ====================================================================== + "Supposed to be SB compatible" cards. + Forget the SB compatibility and check for other alternatives + first. The only cards that work with the SB driver in + Linux have been made by Creative Technology (there is at least + one chip on the card with "CREATIVE" printed on it). The + only other SB compatible chips are ESS and Jazz16 chips + (maybe ALSxxx chips too but they probably don't work). + Most other "16 bit SB compatible" cards such as "OPTi/MAD16" or + "Crystal" are _NOT_ SB compatible in Linux. + + Practically all sound cards have some kind of SB emulation mode + in addition to their native (16 bit) mode. In most cases this + (8 bit only) SB compatible mode doesn't work with Linux. If + you get it working it may cause problems with games and + applications which require 16 bit audio. Some 16 bit only + applications don't check if the card actually supports 16 bits. + They just dump 16 bit data to a 8 bit card which produces just + noise. + + In most cases the 16 bit native mode is supported by Linux. + Use the SB mode with "clones" only if you don't find anything + better from the rest of this doc. + ====================================================================== + +Gravis Ultrasound (GUS) + GUS + GUS + the 16 bit option + GUS MAX + GUS ACE (No MIDI port and audio recording) + GUS PnP (with RAM) + +MPU-401 and compatibles + The driver works both with the full (intelligent mode) MPU-401 + cards (such as MPU IPC-T and MQX-32M) and with the UART only + dumb MIDI ports. MPU-401 is currently the most common MIDI + interface. Most sound cards are compatible with it. However, + don't enable MPU401 mode blindly. Many cards with native support + in the driver have their own MPU401 driver. Enabling the standard one + will cause a conflict with these cards. So check if your card is + in the list of supported cards before enabling MPU401. + +Windows Sound System (MSS/WSS) + Even when Microsoft has discontinued their own Sound System card + they managed to make it a standard. MSS compatible cards are based on + a codec chip which is easily available from at least two manufacturers + (AD1848 by Analog Devices and CS4231/CS4248 by Crystal Semiconductor). + Currently most sound cards are based on one of the MSS compatible codec + chips. The CS4231 is used in the high quality cards such as GUS MAX, + MediaTrix AudioTrix Pro and TB Tropez (GUS MAX is not MSS compatible). + + Having a AD1848, CS4248 or CS4231 codec chip on the card is a good + sign. Even if the card is not MSS compatible, it could be easy to write + support for it. Note also that most MSS compatible cards + require special boot time initialization which may not be present + in the driver. Also, some MSS compatible cards have native support. + Enabling the MSS support with these cards is likely to + cause a conflict. So check if your card is listed in this file before + enabling the MSS support. + +Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4) + Most sound cards have a FM synthesizer chip. The OPL2 is a 2 + operator chip used in the original AdLib card. Currently it's used + only in the cheapest (8 bit mono) cards. The OPL3 is a 4 operator + FM chip which provides better sound quality and/or more available + voices than the OPL2. The OPL4 is a new chip that has an OPL3 and + a wave table synthesizer packed onto the same chip. The driver supports + just the OPL3 mode directly. Most cards with an OPL4 (like + SM Wave and AudioTrix Pro) support the OPL4 mode using MPU401 + emulation. Writing a native OPL4 support is difficult + since Yamaha doesn't give information about their sample ROM chip. + + Enable the generic OPL2/OPL3 FM synthesizer support if your + card has a FM chip made by Yamaha. Don't enable it if your card + has a software (TRS) based FM emulator. + + ---------------------------------------------------------------- + NOTE! OPL3-SA is different chip than the ordinary OPL3. In addition + to the FM synth this chip has also digital audio (WSS) and + MIDI (MPU401) capabilities. Support for OPL3-SA is described below. + ---------------------------------------------------------------- + +Yamaha OPL3-SA1 + + Yamaha OPL3-SA1 (YMF701) is an audio controller chip used on some + (Intel) motherboards and on cheap sound cards. It should not be + confused with the original OPL3 chip (YMF278) which is entirely + different chip. OPL3-SA1 has support for MSS, MPU401 and SB Pro + (not used in OSS/Free) in addition to the OPL3 FM synth. + + There are also chips called OPL3-SA2, OPL3-SA3, ..., OPL3SA-N. They + are PnP chips and will not work with the OPL3-SA1 driver. You should + use the standard MSS, MPU401 and OPL3 options with these chips and to + activate the card using isapnptools. + +4Front Technologies SoftOSS + + SoftOSS is a software based wave table emulation which works with + any 16 bit stereo sound card. Due to its nature a fast CPU is + required (P133 is minimum). Although SoftOSS does _not_ use MMX + instructions it has proven out that recent processors (which appear + to have MMX) perform significantly better with SoftOSS than earlier + ones. For example a P166MMX beats a PPro200. SoftOSS should not be used + on 486 or 386 machines. + + The amount of CPU load caused by SoftOSS can be controlled by + selecting the CONFIG_SOFTOSS_RATE and CONFIG_SOFTOSS_VOICES + parameters properly (they will be prompted by make config). It's + recommended to set CONFIG_SOFTOSS_VOICES to 32. If you have a + P166MMX or faster (PPro200 is not faster) you can set + CONFIG_SOFTOSS_RATE to 44100 (kHz). However with slower systems it + recommended to use sampling rates around 22050 or even 16000 kHz. + Selecting too high values for these parameters may hang your + system when playing MIDI files with hight degree of polyphony + (number of concurrently playing notes). It's also possible to + decrease CONFIG_SOFTOSS_VOICES. This makes it possible to use + higher sampling rates. However using fewer voices decreases + playback quality more than decreasing the sampling rate. + + SoftOSS keeps the samples loaded on the system's RAM so much RAM is + required. SoftOSS should never be used on machines with less than 16 MB + of RAM since this is potentially dangerous (you may accidentally run out + of memory which probably crashes the machine). + + SoftOSS implements the wave table API originally designed for GUS. For + this reason all applications designed for GUS should work (at least + after minor modifications). For example gmod/xgmod and playmidi -g are + known to work. + + To work SoftOSS will require GUS compatible + patch files to be installed on the system (in /dos/ultrasnd/midi). You + can use the public domain MIDIA patchset available from several ftp + sites. + + ********************************************************************* + IMPORTANT NOTICE! The original patch set distributed with the Gravis + Ultrasound card is not in public domain (even though it's available from + some FTP sites). You should contact Voice Crystal (www.voicecrystal.com) + if you like to use these patches with SoftOSS included in OSS/Free. + ********************************************************************* + +PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC) + Analog Devices and Echo Speech have together defined a sound card + architecture based on the above chips. The DSP chip is used + for emulation of SB Pro, FM and General MIDI/MT32. + + There are several cards based on this architecture. The most known + ones are Orchid SW32 and Cardinal DSP16. + + The driver supports downloading DSP algorithms to these cards. + + NOTE! You will have to use the "old" config script when configuring + PSS cards. + +MediaTrix AudioTrix Pro + The ATP card is built around a CS4231 codec and an OPL4 synthesizer + chips. The OPL4 mode is supported by a microcontroller running a + General MIDI emulator. There is also a SB 1.5 compatible playback mode. + +Ensoniq SoundScape and compatibles + Ensoniq has designed a sound card architecture based on the + OTTO synthesizer chip used in their professional MIDI synthesizers. + Several companies (including Ensoniq, Reveal and Spea) are selling + cards based on this architecture. + + NOTE! The SoundScape PnP is not supported by OSS/Free. Ensoniq VIVO and + VIVO90 cards are not compatible with Soundscapes so the Soundscape + driver will not work with them. You may want to use OSS/Linux with these + cards. + +OPTi MAD16 and Mozart based cards + The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929), + OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface + chips are used in many different sound cards, including some + cards by Reveal miro and Turtle Beach (Tropez). The purpose of these + chips is to connect other audio components to the PC bus. The + interface chip performs address decoding for the other chips. + NOTE! Tropez Plus is not MAD16 but CS4232 based. + NOTE! MAD16 PnP cards (82C924, 82C925, 82C931) are not MAD16 compatible + in the PnP mode. You will have to use them in MSS mode after having + initialized them using isapnptools or DOS. 82C931 probably requires + initialization using DOS/Windows (running isapnptools is not enough). + It's possible to use 82C931 with OSS/Free by jumpering it to non-PnP + mode (provided that the card has a jumper for this). In non-PnP mode + 82C931 is compatible with 82C930 and should work with the MAD16 driver + (without need to use isapnptools or DOS to initialize it). All OPTi + chips are supported by OSS/Linux (both in PnP and non-PnP modes). + +Audio Excel DSP16 + Support for this card was written by Riccardo Faccetti + (riccardo@cdc8g5.cdc.polimi.it). The AEDSP16 driver included in + the lowlevel/ directory. To use it you should enable the + "Additional low level drivers" option. + +Crystal CS4232 and CS4236 based cards such as AcerMagic S23, TB Tropez _Plus_ and + many PC motherboards (Compaq, HP, Intel, ...) + CS4232 is a PnP multimedia chip which contains a CS3231A codec, + SB and MPU401 emulations. There is support for OPL3 too. + Unfortunately the MPU401 mode doesn't work (I don't know how to + initialize it). CS4236 is an enhanced (compatible) version of CS4232. + NOTE! Don't ever try to use isapnptools with CS4232 since this will just + freeze your machine (due to chip bugs). If you have problems in getting + CS4232 working you could try initializing it with DOS (CS4232C.EXE) and + then booting Linux using loadlin. CS4232C.EXE loads a secret firmware + patch which is not documented by Crystal. + +Turtle Beach Maui and Tropez "classic" + This driver version supports sample, patch and program loading commands + described in the Maui/Tropez User's manual. + There is now full initialization support too. The audio side of + the Tropez is based on the MAD16 chip (see above). + NOTE! Tropez Plus is different card than Tropez "classic" and will not + work fully in Linux. You can get audio features working by configuring + the card as a CS4232 based card (above). + + +Jumpers and software configuration +================================== + +Some of the earliest sound cards were jumper configurable. You have to +configure the driver use I/O, IRQ and DMA settings +that match the jumpers. Just few 8 bit cards are fully jumper +configurable (SB 1.x/2.x, SB Pro and clones). +Some cards made by Aztech have an EEPROM which contains the +config info. These cards behave much like hardware jumpered cards. + +Most cards have jumper for the base I/O address but other parameters +are software configurable. Sometimes there are few other jumpers too. + +Latest cards are fully software configurable or they are PnP ISA +compatible. There are no jumpers on the board. + +The driver handles software configurable cards automatically. Just configure +the driver to use I/O, IRQ and DMA settings which are known to work. +You could usually use the same values than with DOS and/or Windows. +Using different settings is possible but not recommended since it may cause +some trouble (for example when warm booting from an OS to another or +when installing new hardware to the machine). + +Sound driver sets the soft configurable parameters of the card automatically +during boot. Usually you don't need to run any extra initialization +programs when booting Linux but there are some exceptions. See the +card-specific instructions below for more info. + +The drawback of software configuration is that the driver needs to know +how the card must be initialized. It cannot initialize unknown cards +even if they are otherwise compatible with some other cards (like SB, +MPU401 or Windows Sound System). + + +What if your card was not listed above? +======================================= + +The first thing to do is to look at the major IC chips on the card. +Many of the latest sound cards are based on some standard chips. If you +are lucky, all of them could be supported by the driver. The most common ones +are the OPTi MAD16, Mozart, SoundScape (Ensoniq) and the PSS architectures +listed above. Also look at the end of this file for list of unsupported +cards and the ones which could be supported later. + +The last resort is to send _exact_ name and model information of the card +to me together with a list of the major IC chips (manufactured, model) to +me. I could then try to check if your card looks like something familiar. + +There are many more cards in the world than listed above. The first thing to +do with these cards is to check if they emulate some other card or interface +such as SB, MSS and/or MPU401. In this case there is a chance to get the +card to work by booting DOS before starting Linux (boot DOS, hit ctrl-alt-del +and boot Linux without hard resetting the machine). In this method the +DOS based driver initializes the hardware to use known I/O, IRQ and DMA +settings. If sound driver is configured to use the same settings, everything +should work OK. + + +Configuring sound driver (with Linux) +===================================== + +The sound driver is currently distributed as part of the Linux kernel. The +files are in /usr/src/linux/drivers/sound/. + +**************************************************************************** +* ALWAYS USE THE SOUND DRIVER VERSION WHICH IS DISTRIBUTED WITH * +* THE KERNEL SOURCE PACKAGE YOU ARE USING. SOME ALPHA AND BETA TEST * +* VERSIONS CAN BE INSTALLED FROM A SEPARATELY DISTRIBUTED PACKAGE * +* BUT CHECK THAT THE PACKAGE IS NOT MUCH OLDER (OR NEWER) THAN THE * +* KERNEL YOU ARE USING. IT'S POSSIBLE THAT THE KERNEL/DRIVER * +* INTERFACE CHANGES BETWEEN KERNEL RELEASES WHICH MAY CAUSE SOME * +* INCOMPATIBILITY PROBLEMS. * +* * +* IN CASE YOU INSTALL A SEPARATELY DISTRIBUTED SOUND DRIVER VERSION, * +* BE SURE TO REMOVE OR RENAME THE OLD SOUND DRIVER DIRECTORY BEFORE * +* INSTALLING THE NEW ONE. LEAVING OLD FILES TO THE SOUND DRIVER * +* DIRECTORY _WILL_ CAUSE PROBLEMS WHEN THE DRIVER IS USED OR * +* COMPILED. * +**************************************************************************** + +To configure the driver, run "make config" in the kernel source directory +(/usr/src/linux). Answer "y" or "m" to the question about Sound card support +(after the questions about mouse, CD-ROM, ftape, etc. support). Questions +about options for sound will then be asked. + +After configuring the kernel and sound driver, run "make dep" and compile +the kernel following instructions in the kernel README. + +The sound driver configuration dialog +------------------------------------- + +Sound configuration starts by making some yes/no questions. Be careful +when answering to these questions since answering y to a question may +prevent some later ones from being asked. For example don't answer y to +the first question (PAS16) if you don't really have a PAS16. Don't enable +more cards than you really need since they just consume memory. Also +some drivers (like MPU401) may conflict with your SCSI controller and +prevent kernel from booting. If you card was in the list of supported +cards (above), please look at the card specific config instructions +(later in this file) before starting to configure. Some cards must be +configured in way which is not obvious. + +So here is the beginning of the config dialog. Answer 'y' or 'n' to these +questions. The default answer is shown so that (y/n) means 'y' by default and +(n/y) means 'n'. To use the default value, just hit ENTER. But be careful +since using the default _doesn't_ guarantee anything. + +Note also that all questions may not be asked. The configuration program +may disable some questions depending on the earlier choices. It may also +select some options automatically as well. + + "ProAudioSpectrum 16 support", + - Answer 'y'_ONLY_ if you have a Pro Audio Spectrum _16_, + Pro Audio Studio 16 or Logitech SoundMan 16 (be sure that + you read the above list correctly). Don't answer 'y' if you + have some other card made by Media Vision or Logitech since they + are not PAS16 compatible. + NOTE! Since 3.5-beta10 you need to enable SB support (next question) + if you want to use the SB emulation of PAS16. It's also possible to + the emulation if you want to use a true SB card together with PAS16 + (there is another question about this that is asked later). + "Sound Blaster support", + - Answer 'y' if you have an original SB card made by Creative Labs + or a full 100% hardware compatible clone (like Thunderboard or + SM Games). If your card was in the list of supported cards (above), + please look at the card specific instructions later in this file + before answering this question. For an unknown card you may answer + 'y' if the card claims to be SB compatible. + Enable this option also with PAS16 (changed since v3.5-beta9). + + Don't enable SB if you have a MAD16 or Mozart compatible card. + + "Generic OPL2/OPL3 FM synthesizer support", + - Answer 'y' if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). + Answering 'y' is usually a safe and recommended choice. However some + cards may have software (TSR) FM emulation. Enabling FM support + with these cards may cause trouble. However I don't currently know + such cards. + "Gravis Ultrasound support", + - Answer 'y' if you have GUS or GUS MAX. Answer 'n' if you don't + have GUS since the GUS driver consumes much memory. + Currently I don't have experiences with the GUS ACE so I don't + know what to answer with it. + "MPU-401 support (NOT for SB16)", + - Be careful with this question. The MPU401 interface is supported + by almost any sound card today. However some natively supported cards + have their own driver for MPU401. Enabling the MPU401 option with + these cards will cause a conflict. Also enabling MPU401 on a system + that doesn't really have a MPU401 could cause some trouble. If your + card was in the list of supported cards (above), please look at + the card specific instructions later in this file. + + In MOST cases this MPU401 driver should only be used with "true" + MIDI-only MPU401 professional cards. In most other cases there + is another way to get the MPU401 compatible interface of a + sound card to work. + Support for the MPU401 compatible MIDI port of SB16, ESS1688 + and MV Jazz16 cards is included in the SB driver. Use it instead + of this separate MPU401 driver with these cards. As well + Soundscape, PSS and Maui drivers include their own MPU401 + options. + + It's safe to answer 'y' if you have a true MPU401 MIDI interface + card. + "6850 UART Midi support", + - It's safe to answer 'n' to this question in all cases. The 6850 + UART interface is so rarely used. + "PSS (ECHO-ADI2111) support", + - Answer 'y' only if you have Orchid SW32, Cardinal DSP16 or some + other card based on the PSS chipset (AD1848 codec + ADSP-2115 + DSP chip + Echo ESC614 ASIC CHIP). + "16 bit sampling option of GUS (_NOT_ GUS MAX)", + - Answer 'y' if you have installed the 16 bit sampling daughtercard + to your GUS. Answer 'n' if you have GUS MAX. Enabling this option + disables GUS MAX support. + "GUS MAX support", + - Answer 'y' only if you have a GUS MAX. + "Microsoft Sound System support", + - Again think carefully before answering 'y' to this question. It's + safe to answer 'y' in case you have the original Windows Sound + System card made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). + Also you may answer 'y' in case your card was not listed earlier + in this file. For cards having native support in the driver, consult + the card specific instructions later in this file. Some drivers + have their own MSS support and enabling this option will cause a + conflict. + Note! The MSS driver permits configuring two DMA channels. This is a + "nonstandard" feature and works only with very few cards (if any). + In most cases the second DMA channel should be disabled or set to + the same channel than the first one. Trying to configure two separate + channels with cards that don't support this feature will prevent + audio (at least recording) from working. + "Ensoniq Soundscape support", + - Answer 'y' if you have a sound card based on the Ensoniq SoundScape + chipset. Such cards are being manufactured at least by Ensoniq, + Spea and Reveal (note that Reveal makes other cards also). The oldest + cards made by Spea don't work properly with Linux. + Soundscape PnP as well as Ensoniq VIVO work only with the commercial + OSS/Linux version. + "MediaTrix AudioTrix Pro support", + - Answer 'y' if you have the AudioTrix Pro. + "Support for MAD16 and/or Mozart based cards", + - Answer y if your card has a Mozart (OAK OTI-601) or MAD16 + (OPTi 82C928, 82C929, 82C924/82C925 or 82C930) audio interface chip. + These chips are + currently quite common so it's possible that many no-name cards + have one of them. In addition the MAD16 chip is used in some + cards made by known manufacturers such as Turtle Beach (Tropez), + Reveal (some models) and Diamond (some recent models). + Note OPTi 82C924 and 82C925 are MAD16 compatible only in non PnP + mode (jumper selectable on many cards). + "Support for TB Maui" + - This enables TB Maui specific initialization. Works with TB Maui + and TB Tropez (may not work with Tropez Plus). + + +Then the configuration program asks some y/n questions about the higher +level services. It's recommended to answer 'y' to each of these questions. +Answer 'n' only if you know you will not need the option. + + "MIDI interface support", + - Answering 'n' disables /dev/midi## devices and access to any + MIDI ports using /dev/sequencer and /dev/music. This option + also affects any MPU401 and/or General MIDI compatible devices. + "FM synthesizer (YM3812/OPL-3) support", + - Answer 'y' here. + "/dev/sequencer support", + - Answering 'n' disables /dev/sequencer and /dev/music. + +Entering the I/O, IRQ and DMA config parameters +----------------------------------------------- + +After the above questions the configuration program prompts for the +card specific configuration information. Usually just a set of +I/O address, IRQ and DMA numbers are asked. With some cards the program +asks for some files to be used during initialization of the card. For example +many cards have a DSP chip or microprocessor which must be initialized by +downloading a program (microcode) file to the card. + +Instructions for answering these questions are given in the next section. + + +Card specific information +========================= + +This section gives additional instructions about configuring some cards. +Please refer manual of your card for valid I/O, IRQ and DMA numbers. Using +the same settings with DOS/Windows and Linux is recommended. Using +different values could cause some problems when switching between +different operating systems. + +Sound Blasters (the original ones by Creative) +--------------------------------------------- + +NOTE! Check if you have a PnP Sound Blaster (cards sold after summer 1995 + are almost certainly PnP ones). With PnP cards you should use isapnptools + to activate them (see above). + +It's possible to configure these cards to use different I/O, IRQ and +DMA settings. Since the possible/default settings have changed between various +models, you have to consult manual of your card for the proper ones. It's +a good idea to use the same values than with DOS/Windows. With SB and SB Pro +it's the only choice. SB16 has software selectable IRQ and DMA channels but +using different values with DOS and Linux is likely to cause troubles. The +DOS driver is not able to reset the card properly after warm boot from Linux +if Linux has used different IRQ or DMA values. + +The original (steam) Sound Blaster (versions 1.x and 2.x) use always +DMA1. There is no way to change it. + +The SB16 needs two DMA channels. A 8 bit one (1 or 3) is required for +8 bit operation and a 16 bit one (5, 6 or 7) for the 16 bit mode. In theory +it's possible to use just one (8 bit) DMA channel by answering the 8 bit +one when the configuration program asks for the 16 bit one. This may work +in some systems but is likely to cause terrible noise on some other systems. + +It's possible to use two SB16/32/64 at the same time. To do this you should +first configure OSS/Free for one card. Then edit local.h manually and define +SB2_BASE, SB2_IRQ, SB2_DMA and SB2_DMA2 for the second one. You can't get +the OPL3, MIDI and EMU8000 devices of the second card to work. If you are +going to use two PnP Sound Blasters, ensure that they are of different model +and have different PnP IDs. There is no way to get two cards with the same +card ID and serial number to work. The easiest way to check this is trying +if isapnptools can see both cards or just one. + +NOTE! Don't enable the SM Games option (asked by the configuration program) + if you are not 101% sure that your card is a Logitech Soundman Games + (not a SM Wave or SM16). + +SB Clones +--------- + +First of all: There are no SB16 clones. There are SB Pro clones with a +16 bit mode which is not SB16 compatible. The most likely alternative is that +the 16 bit mode means MSS/WSS. + +There are just a few fully 100% hardware SB or SB Pro compatible cards. +I know just Thunderboard and SM Games. Other cards require some kind of +hardware initialization before they become SB compatible. Check if your card +was listed in the beginning of this file. In this case you should follow +instructions for your card later in this file. + +For other not fully SB clones you may try initialization using DOS in +the following way: + + - Boot DOS so that the card specific driver gets run. + - Hit ctrl-alt-del (or use loadlin) to boot Linux. Don't + switch off power or press the reset button. + - If you use the same I/O, IRQ and DMA settings in Linux, the + card should work. + +If your card is both SB and MSS compatible, I recommend using the MSS mode. +Most cards of this kind are not able to work in the SB and the MSS mode +simultaneously. Using the MSS mode provides 16 bit recording and playback. + +ProAudioSpectrum 16 and compatibles +----------------------------------- + +PAS16 has a SB emulation chip which can be used together with the native +(16 bit) mode of the card. To enable this emulation you should configure +the driver to have SB support too (this has been changed since version +3.5-beta9 of this driver). + +With current driver versions it's also possible to use PAS16 together with +another SB compatible card. In this case you should configure SB support +for the other card and to disable the SB emulation of PAS16 (there is a +separate questions about this). + +With PAS16 you can use two audio device files at the same time. /dev/dsp (and +/dev/audio) is connected to the 8/16 bit native codec and the /dev/dsp1 (and +/dev/audio1) is connected to the SB emulation (8 bit mono only). + +Gravis Ultrasound +----------------- + +There are many different revisions of the Ultrasound card (GUS). The +earliest ones (pre 3.7) don't have a hardware mixer. With these cards +the driver uses a software emulation for synth and pcm playbacks. It's +also possible to switch some of the inputs (line in, mic) off by setting +mixer volume of the channel level below 10%. For recording you have +to select the channel as a recording source and to use volume above 10%. + +GUS 3.7 has a hardware mixer. + +GUS MAX and the 16 bit sampling daughtercard have a CS4231 codec chip which +also contains a mixer. + +Configuring GUS is simple. Just enable the GUS support and GUS MAX or +the 16 bit daughtercard if you have them. Note that enabling the daughter +card disables GUS MAX driver. + +NOTE for owners of the 16 bit daughtercard: By default the daughtercard +uses /dev/dsp (and /dev/audio). Command "ln -sf /dev/dsp1 /dev/dsp" +selects the daughter card as the default device. + +With just the standard GUS enabled the configuration program prompts +for the I/O, IRQ and DMA numbers for the card. Use the same values than +with DOS. + +With the daughter card option enabled you will be prompted for the I/O, +IRQ and DMA numbers for the daughter card. You have to use different I/O +and DMA values than for the standard GUS. The daughter card permits +simultaneous recording and playback. Use /dev/dsp (the daughtercard) for +recording and /dev/dsp1 (GUS GF1) for playback. + +GUS MAX uses the same I/O address and IRQ settings than the original GUS +(GUS MAX = GUS + a CS4231 codec). In addition an extra DMA channel may be used. +Using two DMA channels permits simultaneous playback using two devices +(dev/dsp0 and /dev/dsp1). The second DMA channel is required for +full duplex audio. +To enable the second DMA channels, give a valid DMA channel when the config +program asks for the GUS MAX DMA (entering -1 disables the second DMA). +Using 16 bit DMA channels (5,6 or 7) is recommended. + +If you have problems in recording with GUS MAX, you could try to use +just one 8 bit DMA channel. Recording will not work with one DMA +channel if it's a 16 bit one. + +Microphone input of GUS MAX is connected to mixer in little bit nonstandard +way. There is actually two microphone volume controls. Normal "mic" controls +only recording level. Mixer control "speaker" is used to control volume of +microphone signal connected directly to line/speaker out. So just decrease +volume of "speaker" if you have problems with microphone feedback. + +GUS ACE works too but any attempt to record or to use the MIDI port +will fail. + +GUS PnP (with RAM) is partially supported but it needs to be initialized using +DOS or isapnptools before starting the driver. + +MPU401 and Windows Sound System +------------------------------- + +Again. Don't enable these options in case your card is listed +somewhere else in this file. + +Configuring these cards is obvious (or it should be). With MSS +you should probably enable the OPL3 synth also since +most MSS compatible cards have it. However check that this is true +before enabling OPL3. + +Sound driver supports more than one MPU401 compatible cards at the same time +but the config program asks config info for just the first of them. +Adding the second or third MPU interfaces must be done manually by +editing sound/local.h (after running the config program). Add defines for +MPU2_BASE & MPU2_IRQ (and MPU3_BASE & MPU3_IRQ) to the file. + +CAUTION! + +The default I/O base of Adaptec AHA-1542 SCSI controller is 0x330 which +is also the default of the MPU401 driver. Don't configure the sound driver to +use 0x330 as the MPU401 base if you have a AHA1542. The kernel will not boot +if you make this mistake. + +PSS +--- + +Even the PSS cards are compatible with SB, MSS and MPU401, you must not +enable these options when configuring the driver. The configuration +program handles these options itself. (You may use the SB, MPU and MSS options +together with PSS if you have another card on the system). + +The PSS driver enables MSS and MPU401 modes of the card. SB is not enabled +since it doesn't work concurrently with MSS. The driver loads also a +DSP algorithm which is used to for the general MIDI emulation. The +algorithm file (.ld) is read by the config program and written to a +file included when the pss.c is compiled. For this reason the config +program asks if you want to download the file. Use the genmidi.ld file +distributed with the DOS/Windows drivers of the card (don't use the mt32.ld). +With some cards the file is called 'synth.ld'. You must have access to +the file when configuring the driver. The easiest way is to mount the DOS +partition containing the file with Linux. + +It's possible to load your own DSP algorithms and run them with the card. +Look at the directory pss_test of snd-util-3.0.tar.gz for more info. + +AudioTrix Pro +------------- + +You have to enable the OPL3 and SB (not SB Pro or SB16) drivers in addition +to the native AudioTrix driver. Don't enable MSS or MPU drivers. + +Configuring ATP is little bit tricky since it uses so many I/O, IRQ and +DMA numbers. Using the same values than with DOS/Win is a good idea. Don't +attempt to use the same IRQ or DMA channels twice. + +The SB mode of ATP is implemented so the ATP driver just enables SB +in the proper address. The SB driver handles the rest. You have to configure +both the SB driver and the SB mode of ATP to use the same IRQ, DMA and I/O +settings. + +Also the ATP has a microcontroller for the General MIDI emulation (OPL4). +For this reason the driver asks for the name of a file containing the +microcode (TRXPRO.HEX). This file is usually located in the directory +where the DOS drivers were installed. You must have access to this file +when configuring the driver. + +If you have the effects daughtercard, it must be initialized by running +the setfx program of snd-util-3.0.tar.gz package. This step is not required +when using the (future) binary distribution version of the driver. + +Ensoniq SoundScape +------------------ + +NOTE! The new PnP SoundScape is not supported yet. Soundscape compatible + cards made by Reveal don't work with Linux. They use older revision + of the Soundscape chipset which is not fully compatible with + newer cards made by Ensoniq. + +The SoundScape driver handles initialization of MSS and MPU supports +itself so you don't need to enable other drivers than SoundScape +(enable also the /dev/dsp, /dev/sequencer and MIDI supports). + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +!!!!! !!!! +!!!!! NOTE! Before version 3.5-beta6 there WERE two sets of audio !!!! +!!!!! device files (/dev/dsp0 and /dev/dsp1). The first one WAS !!!! +!!!!! used only for card initialization and the second for audio !!!! +!!!!! purposes. It WAS required to change /dev/dsp (a symlink) to !!!! +!!!!! point to /dev/dsp1. !!!! +!!!!! !!!! +!!!!! This is not required with OSS versions 3.5-beta6 and later !!!! +!!!!! since there is now just one audio device file. Please !!!! +!!!!! change /dev/dsp to point back to /dev/dsp0 if you are !!!! +!!!!! upgrading from an earlier driver version using !!!! +!!!!! (cd /dev;rm dsp;ln -s dsp0 dsp). !!!! +!!!!! !!!! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +The configuration program asks one DMA channel and two interrupts. One IRQ +and one DMA is used by the MSS codec. The second IRQ is required for the +MPU401 mode (you have to use different IRQs for both purposes). +There were earlier two DMA channels for SoundScape but the current driver +version requires just one. + +The SoundScape card has a Motorola microcontroller which must initialized +_after_ boot (the driver doesn't initialize it during boot). +The initialization is done by running the 'ssinit' program which is +distributed in the snd-util-3.0.tar.gz package. You have to edit two +defines in the ssinit.c and then compile the program. You may run ssinit +manually (after each boot) or add it to /etc/rc.d/rc.local. + +The ssinit program needs the microcode file that comes with the DOS/Windows +driver of the card. You will need to use version 1.30.00 or later +of the microcode file (sndscape.co0 or sndscape.co1 depending on +your card model). THE OLD sndscape.cod WILL NOT WORK. IT WILL HANG YOUR +MACHINE. The only way to get the new microcode file is to download +and install the DOS/Windows driver from ftp://ftp.ensoniq.com/pub. + +Then you have to select the proper microcode file to use: soundscape.co0 +is the right one for most cards and sndscape.co1 is for few (older) cards +made by Reveal and/or Spea. The driver has capability to detect the card +version during boot. Look at the boot log messages in /var/adm/messages +and locate the sound driver initialization message for the SoundScape +card. If the driver displays string , you have +an old card and you will need to use sndscape.co1. For other cards use +soundscape.co0. New Soundscape revisions such as Elite and PnP use +code files with higher numbers (.co2, .co3, etc.). + +NOTE! Ensoniq Soundscape VIVO is not compatible with other Soundscape cards. + Currently it's possible to use it in Linux only with OSS/Linux + drivers. + +Check /var/adm/messages after running ssinit. The driver prints +the board version after downloading the microcode file. That version +number must match the number in the name of the microcode file (extension). + +Running ssinit with a wrong version of the sndscape.co? file is not +dangerous as long as you don't try to use a file called sndscape.cod. +If you have initialized the card using a wrong microcode file (sounds +are terrible), just modify ssinit.c to use another microcode file and try +again. It's possible to use an earlier version of sndscape.co[01] but it +may sound weird. + +MAD16 (Pro) and Mozart +---------------------- + +You need to enable just the MAD16 /Mozart support when configuring +the driver. _Don't_ enable SB, MPU401 or MSS. However you will need the +/dev/audio, /dev/sequencer and MIDI supports. + +Mozart and OPTi 82C928 (the original MAD16) chips don't support +MPU401 mode so enter just 0 when the configuration program asks the +MPU/MIDI I/O base. The MAD16 Pro (OPTi 82C929) and 82C930 chips have MPU401 +mode. + +TB Tropez is based on the 82C929 chip. It has two MIDI ports. +The one connected to the MAD16 chip is the second one (there is a second +MIDI connector/pins somewhere??). If you have not connected the second MIDI +port, just disable the MIDI port of MAD16. The 'Maui' compatible synth of +Tropez is jumper configurable and not connected to the MAD16 chip (the +Maui driver can be used with it). + +Some MAD16 based cards may cause feedback, whistle or terrible noise if the +line3 mixer channel is turned too high. This happens at least with Shuttle +Sound System. Current driver versions set volume of line3 low enough so +this should not be a problem. + +If you have a MAD16 card which have an OPL4 (FM + Wave table) synthesizer +chip (_not_ an OPL3), you have to append a line containing #define MAD16_OPL4 +to the file linux/drivers/sound/local.h (after running make config). + +MAD16 cards having a CS4231 codec support full duplex mode. This mode +can be enabled by configuring the card to use two DMA channels. Possible +DMA channel pairs are: 0&1, 1&0 and 3&0. + +NOTE! Cards having an OPTi 82C924/82C925 chip work with OSS/Free only in +non-PnP mode (usually jumper selectable). The PnP mode is supported only +by OSS/Linux. + +MV Jazz (ProSonic) +------------------ + +The Jazz16 driver is just a hack made to the SB Pro driver. However it works +fairly well. You have to enable SB, SB Pro (_not_ SB16) and MPU401 supports +when configuring the driver. The configuration program asks later if you +want support for MV Jazz16 based cards (after asking SB base address). Answer +'y' here and the driver asks the second (16 bit) DMA channel. + +The Jazz16 driver uses the MPU401 driver in a way which will cause +problems if you have another MPU401 compatible card. In this case you must +give address of the Jazz16 based MPU401 interface when the config +program prompts for the MPU401 information. Then look at the MPU401 +specific section for instructions about configuring more than one MPU401 cards. + +Logitech Soundman Wave +---------------------- + +Read the above MV Jazz specific instructions first. + +The Logitech SoundMan Wave (don't confuse this with the SM16 or SM Games) is +a MV Jazz based card which has an additional OPL4 based wave table +synthesizer. The OPL4 chip is handled by an on board microcontroller +which must be initialized during boot. The config program asks if +you have a SM Wave immediately after asking the second DMA channel of jazz16. +If you answer 'y', the config program will ask name of the file containing +code to be loaded to the microcontroller. The file is usually called +MIDI0001.BIN and it's located in the DOS/Windows driver directory. The file +may also be called as TSUNAMI.BIN or something else (older cards?). + +The OPL4 synth will be inaccessible without loading the microcontroller code. + +Also remember to enable SB MPU401 support if you want to use the OPL4 mode. +(Don't enable the 'normal' MPU401 device as with some earlier driver +versions (pre 3.5-alpha8)). + +NOTE! Don't answer 'y' when the driver asks about SM Games support + (the next question after the MIDI0001.BIN name). However + answering 'y' doesn't cause damage your computer so don't panic. + +Sound Galaxies +-------------- + +There are many different Sound Galaxy cards made by Aztech. The 8 bit +ones are fully SB or SB Pro compatible and there should be no problems +with them. + +The older 16 bit cards (SG Pro16, SG NX Pro16, Nova and Lyra) have +an EEPROM chip for storing the configuration data. There is a microcontroller +which initializes the card to match the EEPROM settings when the machine +is powered on. These cards actually behave just like they have jumpers +for all of the settings. Configure driver for MSS, MPU, SB/SB Pro and OPL3 +supports with these cards. + +There are some new Sound Galaxies in the market. I have no experience with +them so read the card's manual carefully. + +ESS ES1688 and ES688 'AudioDrive' based cards +--------------------------------------------- + +Support for these two ESS chips is embedded in the SB driver. +Configure these cards just like SB. Enable the 'SB MPU401 MIDI port' +if you want to use MIDI features of ES1688. ES688 doesn't have MPU mode +so you don't need to enable it (the driver uses normal SB MIDI automatically +with ES688). + +NOTE! ESS cards are not compatible with MSS/WSS so don't worry if MSS support +of OSS doesn't work with it. + +There are some ES1688/688 based sound cards and (particularly) motherboards +which use software configurable I/O port relocation feature of the chip. +This ESS proprietary feature is supported only by OSS/Linux. + +There are ES1688 based cards which use different interrupt pin assignment than +recommended by ESS (5, 7, 9/2 and 10). In this case all IRQs don't work. +At least a card called (Pearl?) Hypersound 16 supports IRQ 15 but it doesn't +work. + +ES1868 is a PnP chip which is (supposed to be) compatible with ESS1688 +probably works with OSS/Free after initialization using isapnptools. + +Reveal cards +------------ + +There are several different cards made/marketed by Reveal. Some of them +are compatible with SoundScape and some use the MAD16 chip. You may have +to look at the card and try to identify its origin. + +Diamond +------- + +The oldest (Sierra Aria based) sound cards made by Diamond are not supported +(they may work if the card is initialized using DOS). The recent (LX?) +models are based on the MAD16 chip which is supported by the driver. + +Audio Excel DSP16 +----------------- + +Support for this card is currently not functional. A new driver for it +should be available later this year. + +PCMCIA cards +------------ + +Sorry, can't help. Some cards may work and some don't. + +TI TM4000M notebooks +-------------------- + +These computers have a built in sound support based on the Jazz chipset. +Look at the instructions for MV Jazz (above). It's also important to note +that there is something wrong with the mouse port and sound at least on +some TM models. Don't enable the "C&T 82C710 mouse port support" when +configuring Linux. Having it enabled is likely to cause mysterious problems +and kernel failures when sound is used. + +miroSOUND +--------- + +The miroSOUND PCM1-pro, PCM12 and PCM20 radio has been used +successfully. These cards are based on the MAD16, OPL4, and CS4231A chips +and everything said in the section about MAD16 cards applies here, +too. The only major difference between the PCMxx and other MAD16 cards +is that instead of the mixer in the CS4231 codec a separate mixer +controlled by an on-board 80C32 microcontroller is used. Control of +the mixer takes place via the ACI (miro's audio control interface) +protocol that is implemented in a separate lowlevel driver. Make sure +you compile this ACI driver together with the normal MAD16 support +when you use a miroSOUND PCMxx card. The ACI mixer is controlled by +/dev/mixer and the CS4231 mixer by /dev/mixer1 (depends on load +time). Only in special cases you want to change something regularly on +the CS4231 mixer. + +The miroSOUND PCM12 and PCM20 radio is capable of full duplex +operation (simultaneous PCM replay and recording), which allows you to +implement nice real-time signal processing audio effect software and +network telephones. The ACI mixer has to be switched into the "solo" +mode for duplex operation in order to avoid feedback caused by the +mixer (input hears output signal). You can de-/activate this mode +through toggleing the record button for the wave controller with an +OSS-mixer. + +The PCM20 contains a radio tuner, which is also controlled by +ACI. This radio tuner is supported by the ACI driver together with the +miropcm20.o module. Also the 7-band equalizer is integrated +(limited by the OSS-design). Developement has started and maybe +finished for the RDS decoder on this card, too. You will be able to +read RadioText, the Programme Service name, Programme TYpe and +others. Even the v4l radio module benefits from it with a refined +strength value. See aci.[ch] and miropcm20*.[ch] for more details. + +The following configuration parameters have worked fine for the PCM12 +in Markus Kuhn's system, many other configurations might work, too: +CONFIG_MAD16_BASE=0x530, CONFIG_MAD16_IRQ=11, CONFIG_MAD16_DMA=3, +CONFIG_MAD16_DMA2=0, CONFIG_MAD16_MPU_BASE=0x330, CONFIG_MAD16_MPU_IRQ=10, +DSP_BUFFSIZE=65536, SELECTED_SOUND_OPTIONS=0x00281000. + +Bas van der Linden is using his PCM1-pro with a configuration that +differs in: CONFIG_MAD16_IRQ=7, CONFIG_MAD16_DMA=1, CONFIG_MAD16_MPU_IRQ=9 + +Compaq Deskpro XL +----------------- + +The builtin sound hardware of Compaq Deskpro XL is now supported. +You need to configure the driver with MSS and OPL3 supports enabled. +In addition you need to manually edit linux/drivers/sound/local.h and +to add a line containing "#define DESKPROXL" if you used +make menuconfig/xconfig. + +Others? +------- + +Since there are so many different sound cards, it's likely that I have +forgotten to mention many of them. Please inform me if you know yet another +card which works with Linux, please inform me (or is anybody else +willing to maintain a database of supported cards (just like in XF86)?). + +Cards not supported yet +======================= + +Please check the version of sound driver you are using before +complaining that your card is not supported. It's possible you are +using a driver version which was released months before your card was +introduced. + +First of all, there is an easy way to make most sound cards work with Linux. +Just use the DOS based driver to initialize the card to a known state, then use +loadlin.exe to boot Linux. If Linux is configured to use the same I/O, IRQ and +DMA numbers as DOS, the card could work. +(ctrl-alt-del can be used in place of loadlin.exe but it doesn't work with +new motherboards). This method works also with all/most PnP sound cards. + +Don't get fooled with SB compatibility. Most cards are compatible with +SB but that may require a TSR which is not possible with Linux. If +the card is compatible with MSS, it's a better choice. Some cards +don't work in the SB and MSS modes at the same time. + +Then there are cards which are no longer manufactured and/or which +are relatively rarely used (such as the 8 bit ProAudioSpectrum +models). It's extremely unlikely that such cards ever get supported. +Adding support for a new card requires much work and increases time +required in maintaining the driver (some changes need to be done +to all low level drivers and be tested too, maybe with multiple +operating systems). For this reason I have made a decision to not support +obsolete cards. It's possible that someone else makes a separately +distributed driver (diffs) for the card. + +Writing a driver for a new card is not possible if there are no +programming information available about the card. If you don't +find your new card from this file, look from the home page +(http://www.opensound.com/ossfree). Then please contact +manufacturer of the card and ask if they have (or are willing to) +released technical details of the card. Do this before contacting me. I +can only answer 'no' if there are no programming information available. + +I have made decision to not accept code based on reverse engineering +to the driver. There are three main reasons: First I don't want to break +relationships to sound card manufacturers. The second reason is that +maintaining and supporting a driver without any specs will be a pain. +The third reason is that companies have freedom to refuse selling their +products to other than Windows users. + +Some companies don't give low level technical information about their +products to public or at least their require signing a NDA. It's not +possible to implement a freeware driver for them. However it's possible +that support for such cards become available in the commercial version +of this driver (see http://www.4Front-tech.com/oss.html for more info). + +There are some common audio chipsets that are not supported yet. For example +Sierra Aria and IBM Mwave. It's possible that these architectures +get some support in future but I can't make any promises. Just look +at the home page (http://www.opensound.com/ossfree/new_cards.html) +for latest info. + +Information about unsupported sound cards and chipsets is welcome as well +as free copies of sound cards, SDKs and operating systems. + +If you have any corrections and/or comments, please contact me. + +Hannu Savolainen +hannu@opensound.com + +Personal home page: http://www.compusonic.fi/~hannu +home page of OSS/Free: http://www.opensound.com/ossfree + +home page of commercial OSS +(Open Sound System) drivers: http://www.opensound.com/oss.html diff -Nru a/Documentation/sound/oss/README.awe b/Documentation/sound/oss/README.awe --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/README.awe Thu Mar 7 18:17:45 2002 @@ -0,0 +1,218 @@ +================================================================ + AWE32 Sound Driver for Linux / FreeBSD + version 0.4.3; Nov. 1, 1998 + + Takashi Iwai +================================================================ + +* GENERAL NOTES + +This is a sound driver extension for SoundBlaster AWE32 and other +compatible cards (AWE32-PnP, SB32, SB32-PnP, AWE64 & etc) to enable +the wave synth operations. The driver is provided for Linux 1.2.x +and 2.[012].x kernels, as well as FreeBSD, on Intel x86 and DEC +Alpha systems. + +This driver was written by Takashi Iwai , +and provided "as is". The original source (awedrv-0.4.3.tar.gz) and +binary packages are available on the following URL: + http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/ +Note that since the author is apart from this web site, the update is +not frequent now. + + +* NOTE TO LINUX USERS + +To enable this driver on linux-2.[01].x kernels, you need turn on +"AWE32 synth" options in sound menu when configure your linux kernel +and modules. The precise installation procedure is described in the +AWE64-Mini-HOWTO and linux-kernel/Documetation/sound/AWE32. + +If you're using PnP cards, the card must be initialized before loading +the sound driver. There're several options to do this: + - Initialize the card via ISA PnP tools, and load the sound module. + - Initialize the card on DOS, and load linux by loadlin.exe + - Use PnP kernel driver (for Linux-2.x.x) +The detailed instruction for the solution using isapnp tools is found +in many documents like above. A brief instruction is also included in +the installation document of this package. +For PnP driver project, please refer to the following URL: + http://www-jcr.lmh.ox.ac.uk/~pnp/ + + +* USING THE DRIVER + +The awedrv has several different playing modes to realize easy channel +allocation for MIDI songs. To hear the exact sound quality, you need +to obtain the extended sequencer program, drvmidi or playmidi-2.5. + +For playing MIDI files, you *MUST* load the soundfont file on the +driver previously by sfxload utility. Otherwise you'll here no sounds +at all! All the utilities and driver source packages are found in the +above URL. The sfxload program is included in the package +awesfx-0.4.3.tgz. Binary packages are available there, too. See the +instruction in each package for installation. + +Loading a soundfont file is very simple. Just execute the command + + % sfxload synthgm.sbk + +Then, sfxload transfers the file "synthgm.sbk" to the driver. +Both SF1 and SF2 formats are accepted. + +Now you can hear midi musics by a midi player. + + % drvmidi foo.mid + +If you run MIDI player after MOD player, you need to load soundfont +files again, since MOD player programs clear the previous loaded +samples by their own data. + +If you have only 512kb on the sound card, I recommend to use dynamic +sample loading via -L option of drvmidi. 2MB GM/GS soundfont file is +available in most midi files. + + % sfxload synthgm + % drvmidi -L 2mbgmgs foo.mid + +This makes a big difference (believe me)! For more details, please +refer to the FAQ list which is available on the URL above. + +The current chorus, reverb and equalizer status can be changed by +aweset utility program (included in awesfx package). Note that +some awedrv-native programs (like drvmidi and xmp) will change the +current settings by themselves. The aweset program is effective +only for other programs like playmidi. + +Enjoy. + + +* COMPILE FLAGS + +Compile conditions are defined in awe_config.h. + +[Compatibility Conditions] +The following flags are defined automatically when using installation +shell script. + +- AWE_MODULE_SUPPORT + indicates your Linux kernel supports module for each sound card + (in recent 2.1 or 2.2 kernels and unofficial patched 2.0 kernels + as distributed in the RH5.0 package). + This flag is automatically set when you're using 2.1.x kernels. + You can pass the base address and memory size via the following + module options, + io = base I/O port address (eg. 0x620) + memsize = DRAM size in kilobytes (eg. 512) + As default, AWE driver probes these values automatically. + + +[Hardware Conditions] +You DON'T have to define the following two values. +Define them only when the driver couldn't detect the card properly. + +- AWE_DEFAULT_BASE_ADDR (default: not defined) + specifies the base port address of your AWE32 card. + 0 means to autodetect the address. + +- AWE_DEFAULT_MEM_SIZE (default: not defined) + specifies the memory size of your AWE32 card in kilobytes. + -1 means to autodetect its size. + + +[Sample Table Size] +From ver.0.4.0, sample tables are allocated dynamically (except +Linux-1.2.x system), so you need NOT to touch these parameters. +Linux-1.2.x users may need to increase these values to appropriate size +if the sound card is equipped with more DRAM. + +- AWE_MAX_SF_LISTS, AWE_MAX_SAMPLES, AWE_MAX_INFOS + + +[Other Conditions] + +- AWE_ALWAYS_INIT_FM (default: not defined) + indicates the AWE driver always initialize FM passthrough even + without DRAM on board. Emu8000 chip has a restriction for playing + samples on DRAM that at least two channels must be occupied as + passthrough channels. + +- AWE_DEBUG_ON (default: defined) + turns on debugging messages if defined. + +- AWE_HAS_GUS_COMPATIBILITY (default: defined) + Enables GUS compatibility mode if defined, reading GUS patches and + GUS control commands. Define this option to use GMOD or other + GUS module players. + +- CONFIG_AWE32_MIDIEMU (default: defined) + Adds a MIDI emulation device by Emu8000 wavetable. The emulation + device can be accessed as an external MIDI, and sends the MIDI + control codes directly. XG and GS sysex/NRPN are accepted. + No MIDI input is supported. + +- CONFIG_AWE32_MIXER (default: not defined) + Adds a mixer device for AWE32 bass/treble equalizer control. + You can access this device using /dev/mixer?? (usually mixer01). + +- AWE_USE_NEW_VOLUME_CALC (default: defined) + Use the new method to calculate the volume change as compatible + with DOS/Win drivers. This option can be toggled via aweset + program, or drvmidi player. + +- AWE_CHECK_VTARGET (default: defined) + Check the current volume target value when searching for an + empty channel to allocate a new voice. This is experimentally + implemented in this version. (probably, this option doesn't + affect the sound quality severely...) + +- AWE_ALLOW_SAMPLE_SHARING (default: defined) + Allow sample sharing for differently loaded patches. + This function is available only together with awesfx-0.4.3p3. + Note that this is still an experimental option. + +- DEF_FM_CHORUS_DEPTH (default: 0x10) + The default strength to be sent to the chorus effect engine. + From 0 to 0xff. Larger numbers may often cause weird sounds. + +- DEF_FM_REVERB_DEPTH (default: 0x10) + The default strength to be sent to the reverb effect engine. + From 0 to 0xff. Larger numbers may often cause weird sounds. + + +* ACKNOWLEDGMENTS + +Thanks to Witold Jachimczyk (witek@xfactor.wpi.edu) for much advice +on programming of AWE32. Much code is brought from his AWE32-native +MOD player, ALMP. +The port of awedrv to FreeBSD is done by Randall Hopper +(rhh@ct.picker.com). +The new volume calculation routine was derived from Mark Weaver's +ADIP compatible routines. +I also thank linux-awe-ml members for their efforts +to reboot their system many times :-) + + +* TODO'S + +- Complete DOS/Win compatibility +- DSP-like output + + +* COPYRIGHT + +Copyright (C) 1996-1998 Takashi Iwai + +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. diff -Nru a/Documentation/sound/oss/README.modules b/Documentation/sound/oss/README.modules --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/README.modules Thu Mar 7 18:17:42 2002 @@ -0,0 +1,105 @@ +Building a modular sound driver +================================ + + The following information is current as of linux-2.1.85. Check the other +readme files, especially README.OSS, for information not specific to +making sound modular. + + First, configure your kernel. This is an idea of what you should be +setting in the sound section: + + Sound card support + + 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support + + I have SoundBlaster. Select your card from the list. + + Generic OPL2/OPL3 FM synthesizer support + FM synthesizer (YM3812/OPL-3) support + + If you don't set these, you will probably find you can play .wav files +but not .midi. As the help for them says, set them unless you know your +card does not use one of these chips for FM support. + + Once you are configured, make zlilo, modules, modules_install; reboot. +Note that it is no longer necessary or possible to configure sound in the +drivers/sound dir. Now one simply configures and makes one's kernel and +modules in the usual way. + + Then, add to your /etc/modules.conf something like: + +alias char-major-14 sb +post-install sb /sbin/modprobe "-k" "adlib_card" +options sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330 +options adlib_card io=0x388 # FM synthesizer + + Alternatively, if you have compiled in kernel level ISAPnP support: + +alias char-major-14 sb +post-install sb /sbin/modprobe "-k" "adlib_card" +options adlib_card io=0x388 + + The effect of this is that the sound driver and all necessary bits and +pieces autoload on demand, assuming you use kerneld (a sound choice) and +autoclean when not in use. Also, options for the device drivers are +set. They will not work without them. Change as appropriate for your card. +If you are not yet using the very cool kerneld, you will have to "modprobe +-k sb" yourself to get things going. Eventually things may be fixed so +that this kludgery is not necessary; for the time being, it seems to work +well. + + Replace 'sb' with the driver for your card, and give it the right +options. To find the filename of the driver, look in +/lib/modules//misc. Mine looks like: + +adlib_card.o # This is the generic OPLx driver +opl3.o # The OPL3 driver +sb.o # <> +sound.o # The sound driver +uart401.o # Used by sb, maybe other cards + + Whichever card you have, try feeding it the options that would be the +default if you were making the driver wired, not as modules. You can look +at the init_module() code for the card to see what args are expected. + + Note that at present there is no way to configure the io, irq and other +parameters for the modular drivers as one does for the wired drivers.. One +needs to pass the modules the necessary parameters as arguments, either +with /etc/modules.conf or with command-line args to modprobe, e.g. + +modprobe -k sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330 +modprobe -k adlib_card io=0x388 + + recommend using /etc/modules.conf. + +Persistent DMA Buffers: + +The sound modules normally allocate DMA buffers during open() and +deallocate them during close(). Linux can often have problems allocating +DMA buffers for ISA cards on machines with more than 16MB RAM. This is +because ISA DMA buffers must exist below the 16MB boundary and it is quite +possible that we can't find a large enough free block in this region after +the machine has been running for any amount of time. The way to avoid this +problem is to allocate the DMA buffers during module load and deallocate +them when the module is unloaded. For this to be effective we need to load +the sound modules right after the kernel boots, either manually or by an +init script, and keep them around until we shut down. This is a little +wasteful of RAM, but it guarantees that sound always works. + +To make the sound driver use persistent DMA buffers we need to pass the +sound.o module a "dmabuf=1" command-line argument. This is normally done +in /etc/modules.conf like so: + +options sound dmabuf=1 + +If you have 16MB or less RAM or a PCI sound card, this is wasteful and +unnecessary. It is possible that machine with 16MB or less RAM will find +this option useful, but if your machine is so memory-starved that it +cannot find a 64K block free, you will be wasting even more RAM by keeping +the sound modules loaded and the DMA buffers allocated when they are not +needed. The proper solution is to upgrade your RAM. But you do also have +this improper solution as well. Use it wisely. + + I'm afraid I know nothing about anything but my setup, being more of a +text-mode guy anyway. If you have options for other cards or other helpful +hints, send them to me, Jim Bray, jb@as220.org, http://as220.org/jb. diff -Nru a/Documentation/sound/oss/README.ymfsb b/Documentation/sound/oss/README.ymfsb --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/README.ymfsb Thu Mar 7 18:17:43 2002 @@ -0,0 +1,107 @@ +Legacy audio driver for YMF7xx PCI cards. + + +FIRST OF ALL +============ + + This code references YAMAHA's sample codes and data sheets. + I respect and thank for all people they made open the informations + about YMF7xx cards. + + And this codes heavily based on Jeff Garzik 's + old VIA 82Cxxx driver (via82cxxx.c). I also respect him. + + +DISCLIMER +========= + + This driver is currently at early ALPHA stage. It may cause serious + damage to your computer when used. + PLEASE USE IT AT YOUR OWN RISK. + + +ABOUT THIS DRIVER +================= + + This code enables you to use your YMF724[A-F], YMF740[A-C], YMF744, YMF754 + cards. When enabled, your card acts as "SoundBlaster Pro" compatible card. + It can only play 22.05kHz / 8bit / Stereo samples, control external MIDI + port. + If you want to use your card as recent "16-bit" card, you should use + Alsa or OSS/Linux driver. Of course you can write native PCI driver for + your cards :) + + +USAGE +===== + + # modprobe ymfsb (options) + + +OPTIONS FOR MODULE +================== + + io : SB base address (0x220, 0x240, 0x260, 0x280) + synth_io : OPL3 base address (0x388, 0x398, 0x3a0, 0x3a8) + dma : DMA number (0,1,3) + master_volume: AC'97 PCM out Vol (0-100) + spdif_out : SPDIF-out flag (0:disable 1:enable) + + These options will change in future... + + +FREQUENCY +========= + + When playing sounds via this driver, you will hear its pitch is slightly + lower than original sounds. Since this driver recognizes your card acts + with 21.739kHz sample rates rather than 22.050kHz (I think it must be + hardware restriction). So many players become tone deafness. + To prevent this, you should express some options to your sound player + that specify correct sample frequency. For example, to play your MP3 file + correctly with mpg123, specify the frequency like following: + + % mpg123 -r 21739 foo.mp3 + + +SPDIF OUT +========= + + With installing modules with option 'spdif_out=1', you can enjoy your + sounds from SPDIF-out of your card (if it had). + Its Fs is fixed to 48kHz (It never means the sample frequency become + up to 48kHz. All sounds via SPDIF-out also 22kHz samples). So your + digital-in capable components has to be able to handle 48kHz Fs. + + +COPYING +======= + + 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. + + +TODO +==== + * support for multiple cards + (set the different SB_IO,MPU_IO,OPL_IO for each cards) + + * support for OPL (dmfm) : There will be no requirements... :-< + + +AUTHOR +====== + + Daisuke Nagano + diff -Nru a/Documentation/sound/oss/SoundPro b/Documentation/sound/oss/SoundPro --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/SoundPro Thu Mar 7 18:17:44 2002 @@ -0,0 +1,105 @@ +Documentation for the SoundPro CMI8330 extensions in the WSS driver (ad1848.o) +------------------------------------------------------------------------------ + +( Be sure to read Documentation/sound/CMI8330 too ) + +Ion Badulescu, ionut@cs.columbia.edu +February 24, 1999 + +(derived from the OPL3-SA2 documentation by Scott Murray) + +The SoundPro CMI8330 (ISA) is a chip usually found on some Taiwanese +motherboards. The official name in the documentation is CMI8330, SoundPro +is the nickname and the big inscription on the chip itself. + +The chip emulates a WSS as well as a SB16, but it has certain differences +in the mixer section which require separate support. It also emulates an +MPU401 and an OPL3 synthesizer, so you probably want to enable support +for these, too. + +The chip identifies itself as an AD1848, but its mixer is significantly +more advanced than the original AD1848 one. If your system works with +either WSS or SB16 and you are having problems with some mixer controls +(no CD audio, no line-in, etc), you might want to give this driver a try. +Detection should work, but it hasn't been widely tested, so it might still +mis-identify the chip. You can still force soundpro=1 in the modprobe +parameters for ad1848. Please let me know if it happens to you, so I can +adjust the detection routine. + +The chip is capable of doing full-duplex, but since the driver sees it as an +AD1848, it cannot take advantage of this. Moreover, the full-duplex mode is +not achievable through the WSS interface, b/c it needs a dma16 line which is +assigned only to the SB16 subdevice (with isapnp). Windows documentation +says the user must use WSS Playback and SB16 Recording for full-duplex, so +it might be possible to do the same thing under Linux. You can try loading +up both ad1848 and sb then use one for playback and the other for +recording. I don't know if this works, b/c I haven't tested it. Anyway, if +you try it, be very careful: the SB16 mixer *mostly* works, but certain +settings can have unexpected effects. Use the WSS mixer for best results. + +There is also a PCI SoundPro chip. I have not seen this chip, so I have +no idea if the driver will work with it. I suspect it won't. + +As with PnP cards, some configuration is required. There are two ways +of doing this. The most common is to use the isapnptools package to +initialize the card, and use the kernel module form of the sound +subsystem and sound drivers. Alternatively, some BIOS's allow manual +configuration of installed PnP devices in a BIOS menu, which should +allow using the non-modular sound drivers, i.e. built into the kernel. +Since in this latter case you cannot use module parameters, you will +have to enable support for the SoundPro at compile time. + +The IRQ and DMA values can be any that are considered acceptable for a +WSS. Assuming you've got isapnp all happy, then you should be able to +do something like the following (which *must* match the isapnp/BIOS +configuration): + +modprobe ad1848 io=0x530 irq=11 dma=0 soundpro=1 +-and maybe- +modprobe sb io=0x220 irq=5 dma=1 dma16=5 + +-then- +modprobe mpu401 io=0x330 irq=9 +modprobe opl3 io=0x388 + +If all goes well and you see no error messages, you should be able to +start using the sound capabilities of your system. If you get an +error message while trying to insert the module(s), then make +sure that the values of the various arguments match what you specified +in your isapnp configuration file, and that there is no conflict with +another device for an I/O port or interrupt. Checking the contents of +/proc/ioports and /proc/interrupts can be useful to see if you're +butting heads with another device. + +If you do not see the chipset version message, and none of the other +messages present in the system log are helpful, try adding 'debug=1' +to the ad1848 parameters, email me the syslog results and I'll do +my best to help. + +Lastly, if you're using modules and want to set up automatic module +loading with kmod, the kernel module loader, here is the section I +currently use in my conf.modules file: + +# Sound +post-install sound modprobe -k ad1848; modprobe -k mpu401; modprobe -k opl3 +options ad1848 io=0x530 irq=11 dma=0 +options sb io=0x220 irq=5 dma=1 dma16=5 +options mpu401 io=0x330 irq=9 +options opl3 io=0x388 + +The above ensures that ad1848 will be loaded whenever the sound system +is being used. + +Good luck. + +Ion + +NOT REALLY TESTED: +- recording +- recording device selection +- full-duplex + +TODO: +- implement mixer support for surround, loud, digital CD switches. +- come up with a scheme which allows recording volumes for each subdevice. +This is a major OSS API change. diff -Nru a/Documentation/sound/oss/Soundblaster b/Documentation/sound/oss/Soundblaster --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/Soundblaster Thu Mar 7 18:17:46 2002 @@ -0,0 +1,53 @@ +modprobe sound +insmod uart401 +insmod sb ... + +This loads the driver for the Sound Blaster and assorted clones. Cards that +are covered by other drivers should not be using this driver. + +The Sound Blaster module takes the following arguments + +io I/O address of the Sound Blaster chip (0x220,0x240,0x260,0x280) +irq IRQ of the Sound Blaster chip (5,7,9,10) +dma 8-bit DMA channel for the Sound Blaster (0,1,3) +dma16 16-bit DMA channel for SB16 and equivalent cards (5,6,7) +mpu_io I/O for MPU chip if present (0x300,0x330) + +sm_games=1 Set if you have a Logitech soundman games +acer=1 Set this to detect cards in some ACER notebooks +mwave_bug=1 Set if you are trying to use this driver with mwave (see on) +type Use this to specify a specific card type + +The following arguments are taken if ISAPnP support is compiled in + +isapnp=0 Set this to disable ISAPnP detection (use io=0xXXX etc. above) +multiple=0 Set to disable detection of multiple Soundblaster cards. + Consider it a bug if this option is needed, and send in a + report. +pnplegacy=1 Set this to be able to use a PnP card(s) along with a single + non-PnP (legacy) card. Above options for io, irq, etc. are + needed, and will apply only to the legacy card. +reverse=1 Reverses the order of the search in the PnP table. +uart401=1 Set to enable detection of mpu devices on some clones. +isapnpjump=n Jumps to slot n in the driver's PnP table. Use the source, + Luke. + +You may well want to load the opl3 driver for synth music on most SB and +clone SB devices + +insmod opl3 io=0x388 + +Using Mwave + +To make this driver work with Mwave you must set mwave_bug. You also need +to warm boot from DOS/Windows with the required firmware loaded under this +OS. IBM are being difficult about documenting how to load this firmware. + +Avance Logic ALS007 + +This card is supported; see the separate file ALS007 for full details. + +Avance Logic ALS100 + +This card is supported; setup should be as for a standard Sound Blaster 16. +The driver will identify the audio device as a "Sound Blaster 16 (ALS-100)". diff -Nru a/Documentation/sound/oss/Tropez+ b/Documentation/sound/oss/Tropez+ --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/Tropez+ Thu Mar 7 18:17:45 2002 @@ -0,0 +1,26 @@ +From: Paul Barton-Davis + +Here is the configuration I use with a Tropez+ and my modular +driver: + + alias char-major-14 wavefront + alias synth0 wavefront + alias mixer0 cs4232 + alias audio0 cs4232 + pre-install wavefront modprobe "-k" "cs4232" + post-install wavefront modprobe "-k" "opl3" + options wavefront io=0x200 irq=9 + options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 + options opl3 io=0x388 + +Things to note: + + the wavefront options "io" and "irq" ***MUST*** match the "synthio" + and "synthirq" cs4232 options. + + you can do without the opl3 module if you don't + want to use the OPL/[34] synth on the soundcard + + the opl3 io parameter is conventionally not adjustable. + +Please see drivers/sound/README.wavefront for more details. diff -Nru a/Documentation/sound/oss/VIA-chipset b/Documentation/sound/oss/VIA-chipset --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/VIA-chipset Thu Mar 7 18:17:45 2002 @@ -0,0 +1,43 @@ +Running sound cards on VIA chipsets + +o There are problems with VIA chipsets and sound cards that appear to + lock the hardware solidly. Test programs under DOS have verified the + problem exists on at least some (but apparently not all) VIA boards + +o VIA have so far failed to bother to answer support mail on the subject + so if you are a VIA engineer feeling aggrieved as you read this + document go chase your own people. If there is a workaround please + let us know so we can implement it. + + +Certain patterns of ISA DMA access used for most PC sound cards cause the +VIA chipsets to lock up. From the collected reports this appears to cover a +wide range of boards. Some also lock up with sound cards under Win* as well. + +Linux implements a workaround providing your chipset is PCI and you compiled +with PCI Quirks enabled. If so you will see a message + "Activating ISA DMA bug workarounds" + +during booting. If you have a VIA PCI chipset that hangs when you use the +sound and is not generating this message even with PCI quirks enabled +please report the information to the linux-kernel list (see REPORTING-BUGS). + +If you are one of the tiny number of unfortunates with a 486 ISA/VLB VIA +chipset board you need to do the following to build a special kernel for +your board + + edit linux/include/asm-i386/dma.h + +change + +#define isa_dma_bridge_buggy (0) + +to + +#define isa_dma_bridge_buggy (1) + +and rebuild a kernel without PCI quirk support. + + +Other than this particular glitch the VIA [M]VP* chipsets appear to work +perfectly with Linux. diff -Nru a/Documentation/sound/oss/VIBRA16 b/Documentation/sound/oss/VIBRA16 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/VIBRA16 Thu Mar 7 18:17:39 2002 @@ -0,0 +1,80 @@ +Sound Blaster 16X Vibra addendum +-------------------------------- +by Marius Ilioaea + Stefan Laudat + +Sat Mar 6 23:55:27 EET 1999 + + Hello again, + + Playing with a SB Vibra 16x soundcard we found it very difficult +to setup because the kernel reported a lot of DMA errors and wouldn't +simply play any sound. + A good starting point is that the vibra16x chip full-duplex facility +is neither still exploited by the sb driver found in the linux kernel +(tried it with a 2.2.2-ac7), nor in the commercial OSS package (it reports +it as half-duplex soundcard). Oh, I almost forgot, the RedHat sndconfig +failed detecting it ;) + So, the big problem still remains, because the sb module wants a +8-bit and a 16-bit dma, which we could not allocate for vibra... it supports +only two 8-bit dma channels, the second one will be passed to the module +as a 16 bit channel, the kernel will yield about that but everything will +be okay, trust us. + The only inconvenient you may find is that you will have +some sound playing jitters if you have HDD dma support enabled - but this +will happen with almost all soundcards... + + A fully working isapnp.conf is just here: + + + +(READPORT 0x0203) +(ISOLATE PRESERVE) +(IDENTIFY *) +(VERBOSITY 2) +(CONFLICT (IO FATAL)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # or WARNING +# SB 16 and OPL3 devices +(CONFIGURE CTL00f0/-1 (LD 0 +(INT 0 (IRQ 5 (MODE +E))) +(DMA 0 (CHANNEL 1)) +(DMA 1 (CHANNEL 3)) +(IO 0 (SIZE 16) (BASE 0x0220)) +(IO 2 (SIZE 4) (BASE 0x0388)) +(NAME "CTL00f0/-1[0]{Audio }") +(ACT Y) +)) + +# Joystick device - only if you need it :-/ + +(CONFIGURE CTL00f0/-1 (LD 1 +(IO 0 (SIZE 1) (BASE 0x0200)) +(NAME "CTL00f0/-1[1]{Game }") +(ACT Y) +)) +(WAITFORKEY) + + + + So, after a good kernel modules compilation and a 'depmod -a kernel_ver' +you may want to: + +modprobe sb io=0x220 irq=5 dma=1 dma16=3 + + Or, take the hard way: + +insmod souncore +insmod sound +insmod uart401 +insmod sb io=0x220 irq=5 dma=1 dma16=3 +# do you need MIDI? +insmod opl3=0x388 + + Just in case, the kernel sound support should be: + +CONFIG_SOUND=m +CONFIG_SOUND_OSS=m +CONFIG_SOUND_SB=m + + Enjoy your new noisy Linux box! ;) + + diff -Nru a/Documentation/sound/oss/WaveArtist b/Documentation/sound/oss/WaveArtist --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/WaveArtist Thu Mar 7 18:17:42 2002 @@ -0,0 +1,170 @@ + + (the following is from the armlinux CVS) + + WaveArtist mixer and volume levels can be accessed via these commands: + + nn30 read registers nn, where nn = 00 - 09 for mixer settings + 0a - 13 for channel volumes + mm31 write the volume setting in pairs, where mm = (nn - 10) / 2 + rr32 write the mixer settings in pairs, where rr = nn/2 + xx33 reset all settings to default + 0y34 select mono source, y=0 = left, y=1 = right + + bits + nn 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 00 | 0 | 0 0 1 1 | left line mixer gain | left aux1 mixer gain |lmute| +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 01 | 0 | 0 1 0 1 | left aux2 mixer gain | right 2 left mic gain |mmute| +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 02 | 0 | 0 1 1 1 | left mic mixer gain | left mic | left mixer gain |dith | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 03 | 0 | 1 0 0 1 | left mixer input select |lrfg | left ADC gain | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 04 | 0 | 1 0 1 1 | right line mixer gain | right aux1 mixer gain |rmute| +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 05 | 0 | 1 1 0 1 | right aux2 mixer gain | left 2 right mic gain |test | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 06 | 0 | 1 1 1 1 | right mic mixer gain | right mic |right mixer gain |rbyps| +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 07 | 1 | 0 0 0 1 | right mixer select |rrfg | right ADC gain | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 08 | 1 | 0 0 1 1 | mono mixer gain |right ADC mux sel|left ADC mux sel | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 09 | 1 | 0 1 0 1 |loopb|left linout|loop|ADCch|TxFch|OffCD|test |loopb|loopb|osamp| +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 0a | 0 | left PCM channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 0b | 0 | right PCM channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 0c | 0 | left FM channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 0d | 0 | right FM channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 0e | 0 | left wavetable channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 0f | 0 | right wavetable channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 10 | 0 | left PCM expansion channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 11 | 0 | right PCM expansion channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 12 | 0 | left FM expansion channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 13 | 0 | right FM expansion channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + + lmute: left mute + mmute: mono mute + dith: dithds + lrfg: + rmute: right mute + rbyps: right bypass + rrfg: + ADCch: + TxFch: + OffCD: + osamp: + + And the following diagram is derived from the description in the CVS archive: + + MIC L (mouthpiece) + +------+ + -->PreAmp>-\ + +--^---+ | + | | + r2b4-5 | +--------+ + /----*-------------------------------->5 | + | | | + | /----------------------------------->4 | + | | | | + | | /--------------------------------->3 1of5 | +---+ + | | | | mux >-->AMP>--> ADC L + | | | /------------------------------->2 | +-^-+ + | | | | | | | + Line | | | | +----+ +------+ +---+ /---->1 | r3b3-0 + ------------*->mute>--> Gain >--> | | | | + L | | | +----+ +------+ | | | *->0 | + | | | | | | +---^----+ + Aux2 | | | +----+ +------+ | | | | + ----------*--->mute>--> Gain >--> M | | r8b0-2 + L | | +----+ +------+ | | | + | | | | \------\ + Aux1 | | +----+ +------+ | | | + --------*----->mute>--> Gain >--> I | | + L | +----+ +------+ | | | + | | | | + | +----+ +------+ | | +---+ | + *------->mute>--> Gain >--> X >-->AMP>--* + | +----+ +------+ | | +-^-+ | + | | | | | + | +----+ +------+ | | r2b1-3 | + | /----->mute>--> Gain >--> E | | + | | +----+ +------+ | | | + | | | | | + | | +----+ +------+ | | | + | | /--->mute>--> Gain >--> R | | + | | | +----+ +------+ | | | + | | | | | | r9b8-9 + | | | +----+ +------+ | | | | + | | | /->mute>--> Gain >--> | | +---v---+ + | | | | +----+ +------+ +---+ /-*->0 | + DAC | | | | | | | + ------------*----------------------------------->? | +----+ + L | | | | | Mux >-->mute>--> L output + | | | | /->? | +--^-+ + | | | | | | | | + | | | /--------->? | r0b0 + | | | | | | +-------+ + | | | | | | + Mono | | | | | | +-------+ + ----------* | \---> | +----+ + | | | | | | Mix >-->mute>--> Mono output + | | | | *-> | +--^-+ + | | | | | +-------+ | + | | | | | r1b0 + DAC | | | | | +-------+ + ------------*-------------------------*--------->1 | +----+ + R | | | | | | Mux >-->mute>--> R output + | | | | +----+ +------+ +---+ *->0 | +--^-+ + | | | \->mute>--> Gain >--> | | +---^---+ | + | | | +----+ +------+ | | | | r5b0 + | | | | | | r6b0 + | | | +----+ +------+ | | | + | | \--->mute>--> Gain >--> M | | + | | +----+ +------+ | | | + | | | | | + | | +----+ +------+ | | | + | *----->mute>--> Gain >--> I | | + | | +----+ +------+ | | | + | | | | | + | | +----+ +------+ | | +---+ | + \------->mute>--> Gain >--> X >-->AMP>--* + | +----+ +------+ | | +-^-+ | + /--/ | | | | + Aux1 | +----+ +------+ | | r6b1-3 | + -------*------>mute>--> Gain >--> E | | + R | | +----+ +------+ | | | + | | | | | + Aux2 | | +----+ +------+ | | /------/ + ---------*---->mute>--> Gain >--> R | | + R | | | +----+ +------+ | | | + | | | | | | +--------+ + Line | | | +----+ +------+ | | | *->0 | + -----------*-->mute>--> Gain >--> | | | | + R | | | | +----+ +------+ +---+ \---->1 | + | | | | | | + | | | \-------------------------------->2 | +---+ + | | | | Mux >-->AMP>--> ADC R + | | \---------------------------------->3 | +-^-+ + | | | | | + | \------------------------------------>4 | r7b3-0 + | | | + \-----*-------------------------------->5 | + | +---^----+ + r6b4-5 | | + | | r8b3-5 + +--v---+ | + -->PreAmp>-/ + +------+ + MIC R (electret mic) diff -Nru a/Documentation/sound/oss/Wavefront b/Documentation/sound/oss/Wavefront --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/Wavefront Thu Mar 7 18:17:38 2002 @@ -0,0 +1,341 @@ + An OSS/Free Driver for WaveFront soundcards + (Turtle Beach Maui, Tropez, Tropez Plus) + + Paul Barton-Davis, July 1998 + + VERSION 0.2.5 + +Driver Status +------------- + +Requires: Kernel 2.1.106 or later (the driver is included with kernels +2.1.109 and above) + +As of 7/22/1998, this driver is currently in *BETA* state. This means +that it compiles and runs, and that I use it on my system (Linux +2.1.106) with some reasonably demanding applications and uses. I +believe the code is approaching an initial "finished" state that +provides bug-free support for the Tropez Plus. + +Please note that to date, the driver has ONLY been tested on a Tropez +Plus. I would very much like to hear (and help out) people with Tropez +and Maui cards, since I think the driver can support those cards as +well. + +Finally, the driver has not been tested (or even compiled) as a static +(non-modular) part of the kernel. Alan Cox's good work in modularizing +OSS/Free for Linux makes this rather unnecessary. + +Some Questions +-------------- + +********************************************************************** +0) What does this driver do that the maui driver did not ? +********************************************************************** + +* can fully initialize a WaveFront card from cold boot - no DOS + utilities needed +* working patch/sample/program loading and unloading (the maui + driver didn't document how to make this work, and assumed + user-level preparation of the patch data for writing + to the board. ick.) +* full user-level access to all WaveFront commands +* for the Tropez Plus, (primitive) control of the YSS225 FX processor +* Virtual MIDI mode supported - 2 MIDI devices accessible via the + WaveFront's MPU401/UART emulation. One + accesses the WaveFront synth, the other accesses the + external MIDI connector. Full MIDI read/write semantics + for both devices. +* OSS-compliant /dev/sequencer interface for the WaveFront synth, + including native and GUS-format patch downloading. +* semi-intelligent patch management (prototypical at this point) + +********************************************************************** +1) What to do about MIDI interfaces ? +********************************************************************** + +The Tropez Plus (and perhaps other WF cards) can in theory support up +to 2 physical MIDI interfaces. One of these is connected to the +ICS2115 chip (the WaveFront synth itself) and is controlled by +MPU/UART-401 emulation code running as part of the WaveFront OS. The +other is controlled by the CS4232 chip present on the board. However, +physical access to the CS4232 connector is difficult, and it is +unlikely (though not impossible) that you will want to use it. + +An older version of this driver introduced an additional kernel config +variable which controlled whether or not the CS4232 MIDI interface was +configured. Because of Alan Cox's work on modularizing the sound +drivers, and now backporting them to 2.0.34 kernels, there seems to be +little reason to support "static" configuration variables, and so this +has been abandoned in favor of *only* module parameters. Specifying +"mpuio" and "mpuirq" for the cs4232 parameter will result in the +CS4232 MIDI interface being configured; leaving them unspecified will +leave it unconfigured (and thus unusable). + +BTW, I have heard from one Tropez+ user that the CS4232 interface is +more reliable than the ICS2115 one. I have had no problems with the +latter, and I don't have the right cable to test the former one +out. Reports welcome. + +********************************************************************** +2) Why does line XXX of the code look like this .... ? +********************************************************************** + +Either because its not finished yet, or because you're a better coder +than I am, or because you don't understand some aspect of how the card +or the code works. + +I absolutely welcome comments, criticisms and suggestions about the +design and implementation of the driver. + +********************************************************************** +3) What files are included ? +********************************************************************** + + drivers/sound/README.wavefront -- this file + + drivers/sound/wavefront.patch -- patches for the 2.1.106 sound drivers + needed to make the rest of this work + DO NOT USE IF YOU'VE APPLIED THEM + BEFORE, OR HAVE 2.1.109 OR ABOVE + + drivers/sound/wavfront.c -- the driver + drivers/sound/ys225.h -- data declarations for FX config + drivers/sound/ys225.c -- data definitions for FX config + drivers/sound/wf_midi.c -- the "uart401" driver + to support virtual MIDI mode. + include/wavefront.h -- the header file + Documentation/sound/Tropez+ -- short docs on configuration + +********************************************************************** +4) How do I compile/install/use it ? +********************************************************************** + +PART ONE: install the source code into your sound driver directory + + cd + tar -zxvf + +PART TWO: apply the patches + + DO THIS ONLY IF YOU HAVE A KERNEL VERSION BELOW 2.1.109 + AND HAVE NOT ALREADY INSTALLED THE PATCH(ES). + + cd drivers/sound + patch < wavefront.patch + +PART THREE: configure your kernel + + cd + make xconfig (or whichever config option you use) + + - choose YES for Sound Support + - choose MODULE (M) for OSS Sound Modules + - choose MODULE(M) to YM3812/OPL3 support + - choose MODULE(M) for WaveFront support + - choose MODULE(M) for CS4232 support + + - choose "N" for everything else (unless you have other + soundcards you want support for) + + + make dep + make boot + . + . + . + + make modules + . + . + . + make modules_install + +Here's my autoconf.h SOUND section: + +/* + * Sound + */ +#define CONFIG_SOUND 1 +#undef CONFIG_SOUND_OSS +#define CONFIG_SOUND_OSS_MODULE 1 +#undef CONFIG_SOUND_PAS +#undef CONFIG_SOUND_SB +#undef CONFIG_SOUND_ADLIB +#undef CONFIG_SOUND_GUS +#undef CONFIG_SOUND_MPU401 +#undef CONFIG_SOUND_PSS +#undef CONFIG_SOUND_MSS +#undef CONFIG_SOUND_SSCAPE +#undef CONFIG_SOUND_TRIX +#undef CONFIG_SOUND_MAD16 +#undef CONFIG_SOUND_WAVEFRONT +#define CONFIG_SOUND_WAVEFRONT_MODULE 1 +#undef CONFIG_SOUND_CS4232 +#define CONFIG_SOUND_CS4232_MODULE 1 +#undef CONFIG_SOUND_MAUI +#undef CONFIG_SOUND_SGALAXY +#undef CONFIG_SOUND_OPL3SA1 +#undef CONFIG_SOUND_SOFTOSS +#undef CONFIG_SOUND_YM3812 +#define CONFIG_SOUND_YM3812_MODULE 1 +#undef CONFIG_SOUND_VMIDI +#undef CONFIG_SOUND_UART6850 +/* + * Additional low level sound drivers + */ +#undef CONFIG_LOWLEVEL_SOUND + +************************************************************ +6) How do I configure my card ? +************************************************************ + +You need to edit /etc/modules.conf. Here's mine (edited to show the +relevant details): + + # Sound system + alias char-major-14 wavefront + alias synth0 wavefront + alias mixer0 cs4232 + alias audio0 cs4232 + pre-install wavefront modprobe "-k" "cs4232" + post-install wavefront modprobe "-k" "opl3" + options wavefront io=0x200 irq=9 + options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 + options opl3 io=0x388 + +Things to note: + + the wavefront options "io" and "irq" ***MUST*** match the "synthio" + and "synthirq" cs4232 options. + + you can do without the opl3 module if you don't + want to use the OPL/[34] FM synth on the soundcard + + the opl3 io parameter is conventionally not adjustable. + In theory, any not-in-use IO port address would work, but + just use 0x388 and stick with the crowd. + +********************************************************************** +7) What about firmware ? +********************************************************************** + +Turtle Beach have not given me permission to distribute their firmware +for the ICS2115. However, if you have a WaveFront card, then you +almost certainly have the firmware, and if not, its freely available +on their website, at: + + http://www.tbeach.com/tbs/downloads/scardsdown.htm#tropezplus + +The file is called WFOS2001.MOT (for the Tropez+). + +This driver, however, doesn't use the pure firmware as distributed, +but instead relies on a somewhat processed form of it. You can +generate this very easily. Following an idea from Andrew Veliath's +Pinnacle driver, the following flex program will generate the +processed version: + +---- cut here ------------------------- +%option main +%% +^S[28].*\r$ printf ("%c%.*s", yyleng-1,yyleng-1,yytext); +<> { fputc ('\0', stdout); return; } +\n {} +. {} +---- cut here ------------------------- + +To use it, put the above in file (say, ws.l) compile it like this: + + shell> flex -ows.c ws.l + shell> cc -o ws ws.c + +and then use it like this: + + ws < my-copy-of-the-oswf.mot-file > /etc/sound/wavefront.os + +If you put it somewhere else, you'll always have to use the wf_ospath +module parameter (see below) or alter the source code. + +********************************************************************** +7) How do I get it working ? +********************************************************************** + +Optionally, you can reboot with the "new" kernel (even though the only +changes have really been made to a module). + +Then, as root do: + + modprobe wavefront + +You should get something like this in /var/log/messages: + + WaveFront: firmware 1.20 already loaded. + +or + + WaveFront: no response to firmware probe, assume raw. + +then: + + WaveFront: waiting for memory configuration ... + WaveFront: hardware version 1.64 + WaveFront: available DRAM 8191k + WaveFront: 332 samples used (266 real, 13 aliases, 53 multi), 180 empty + WaveFront: 128 programs slots in use + WaveFront: 256 patch slots filled, 142 in use + +The whole process takes about 16 seconds, the longest waits being +after reporting the hardware version (during the firmware download), +and after reporting program status (during patch status inquiry). Its +shorter (about 10 secs) if the firmware is already loaded (i.e. only +warm reboots since the last firmware load). + +The "available DRAM" line will vary depending on how much added RAM +your card has. Mine has 8MB. + +To check basically functionality, use play(1) or splay(1) to send a +.WAV or other audio file through the audio portion. Then use playmidi +to play a General MIDI file. Try the "-D 0" to hear the +difference between sending MIDI to the WaveFront and using the OPL/3, +which is the default (I think ...). If you have an external synth(s) +hooked to the soundcard, you can use "-e" to route to the +external synth(s) (in theory, -D 1 should work as well, but I think +there is a bug in playmidi which prevents this from doing what it +should). + +********************************************************************** +8) What are the module parameters ? +********************************************************************** + +Its best to read wavefront.c for this, but here is a summary: + +integers: + wf_raw - if set, ignore apparent presence of firmware + loaded onto the ICS2115, reset the whole + board, and initialize it from scratch. (default = 0) + + fx_raw - if set, always initialize the YSS225 processor + on the Tropez plus. (default = 1) + + < The next 4 are basically for kernel hackers to allow + tweaking the driver for testing purposes. > + + wait_usecs - loop timer used when waiting for + status conditions on the board. + The default is 150. + + debug_default - debugging flags. See sound/wavefront.h + for WF_DEBUG_* values. Default is zero. + Setting this allows you to debug the + driver during module installation. +strings: + ospath - path to get to the pre-processed OS firmware. + (default: /etc/sound/wavefront.os) + +********************************************************************** +9) Who should I contact if I have problems? +********************************************************************** + +Just me: Paul Barton-Davis + + diff -Nru a/Documentation/sound/oss/btaudio b/Documentation/sound/oss/btaudio --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/btaudio Thu Mar 7 18:17:39 2002 @@ -0,0 +1,92 @@ + +Intro +===== + +people start bugging me about this with questions, looks like I +should write up some documentation for this beast. That way I +don't have to answer that much mails I hope. Yes, I'm lazy... + + +You might have noticed that the bt878 grabber cards have actually +_two_ PCI functions: + +$ lspci +[ ... ] +00:0a.0 Multimedia video controller: Brooktree Corporation Bt878 (rev 02) +00:0a.1 Multimedia controller: Brooktree Corporation Bt878 (rev 02) +[ ... ] + +The first does video, it is backward compatible to the bt848. The second +does audio. btaudio is a driver for the second function. It's a sound +driver which can be used for recording sound (and _only_ recording, no +playback). As most TV cards come with a short cable which can be plugged +into your sound card's line-in you probably don't need this driver if all +you want to do is just watching TV... + + +Driver Status +============= + +Still somewhat experimental. The driver should work stable, i.e. it +should'nt crash your box. It might not work as expected, have bugs, +not being fully OSS API compilant, ... + +Latest versions are available from http://bytesex.org/bttv/, the +driver is in the bttv tarball. Kernel patches might be available too, +have a look at http://bytesex.org/bttv/listing.html. + +The chip knows two different modes. btaudio registers two dsp +devices, one for each mode. They can not be used at the same time. + + +Digital audio mode +================== + +The chip gives you 16 bit stereo sound. The sample rate depends on +the external source which feeds the bt878 with digital sound via I2S +interface. There is a insmod option (rate) to tell the driver which +sample rate the hardware uses (32000 is the default). + +One possible source for digital sound is the msp34xx audio processor +chip which provides digital sound via I2S with 32 kHz sample rate. My +Hauppauge board works this way. + +The Osprey-200 reportly gives you digital sound with 44100 Hz sample +rate. It is also possible that you get no sound at all. + + +analog mode (A/D) +================= + +You can tell the driver to use this mode with the insmod option "analog=1". +The chip has three analog inputs. Consequently you'll get a mixer device +to control these. + +The analog mode supports mono only. Both 8 + 16 bit. Both are _signed_ +int, which is uncommon for the 8 bit case. Sample rate range is 119 kHz +to 448 kHz. Yes, the number of digits is correct. The driver supports +downsampling by powers of two, so you can ask for more usual sample rates +like 44 kHz too. + +With my Hauppauge I get noisy sound on the second input (mapped to line2 +by the mixer device). Others get a useable signal on line1. + + +some examples +============= + +* read audio data from btaudio (dsp2), send to es1730 (dsp,dsp1): + $ sox -w -r 32000 -t ossdsp /dev/dsp2 -t ossdsp /dev/dsp + +* read audio data from btaudio, send to esound daemon (which might be + running on another host): + $ sox -c 2 -w -r 32000 -t ossdsp /dev/dsp2 -t sw - | esdcat -r 32000 + $ sox -c 1 -w -r 32000 -t ossdsp /dev/dsp2 -t sw - | esdcat -m -r 32000 + + +Have fun, + + Gerd + +-- +Gerd Knorr diff -Nru a/Documentation/sound/oss/cs46xx b/Documentation/sound/oss/cs46xx --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/cs46xx Thu Mar 7 18:17:38 2002 @@ -0,0 +1,138 @@ + +Documentation for the Cirrus Logic/Crystal SoundFusion cs46xx/cs4280 audio +controller chips (2001/05/11) + +The cs46xx audio driver supports the DSP line of Cirrus controllers. +Specifically, the cs4610, cs4612, cs4614, cs4622, cs4624, cs4630 and the cs4280 +products. This driver uses the generic ac97_codec driver for AC97 codec +support. + + +Features: + +Full Duplex Playback/Capture supported from 8k-48k. +16Bit Signed LE & 8Bit Unsigned, with Mono or Stereo supported. + +APM/PM - 2.2.x PM is enabled and functional. APM can also +be enabled for 2.4.x by modifying the CS46XX_ACPI_SUPPORT macro +definition. + +DMA playback buffer size is configurable from 16k (defaultorder=2) up to 2Meg +(defaultorder=11). DMA capture buffer size is fixed at a single 4k page as +two 2k fragments. + +MMAP seems to work well with QuakeIII, and test XMMS plugin. + +Myth2 works, but the polling logic is not fully correct, but is functional. + +The 2.4.4-ac6 gameport code in the cs461x joystick driver has been tested +with a Microsoft Sidewinder joystick (cs461x.o and sidewinder.o). This +audio driver must be loaded prior to the joystick driver to enable the +DSP task image supporting the joystick device. + + +Limitations: + +SPDIF is currently not supported. + +Primary codec support only. No secondary codec support is implemented. + + + +NOTES: + +Hercules Game Theatre XP - the EGPIO2 pin controls the external Amp, +and has been tested. +Module parameter hercules_egpio_disable set to 1, will force a 0 to EGPIODR +to disable the external amplifier. + +VTB Santa Cruz - the GPIO7/GPIO8 on the Secondary Codec control +the external amplifier for the "back" speakers, since we do not +support the secondary codec then this external amp is not +turned on. The primary codec external amplifier is supported but +note that the AC97 EAPD bit is inverted logic (amp_voyetra()). + +DMA buffer size - there are issues with many of the Linux applications +concerning the optimal buffer size. Several applications request a +certain fragment size and number and then do not verify that the driver +has the ability to support the requested configuration. +SNDCTL_DSP_SETFRAGMENT ioctl is used to request a fragment size and +number of fragments. Some applications exit if an error is returned +on this particular ioctl. Therefore, in alignment with the other OSS audio +drivers, no error is returned when a SETFRAGs IOCTL is received, but the +values passed from the app are not used in any buffer calculation +(ossfragshift/ossmaxfrags are not used). +Use the "defaultorder=N" module parameter to change the buffer size if +you have an application that requires a specific number of fragments +or a specific buffer size (see below). + +Debug Interface +--------------- +There is an ioctl debug interface to allow runtime modification of the +debug print levels. This debug interface code can be disabled from the +compilation process with commenting the following define: +#define CSDEBUG_INTERFACE 1 +There is also a debug print methodolgy to select printf statements from +different areas of the driver. A debug print level is also used to allow +additional printfs to be active. Comment out the following line in the +driver to disable compilation of the CS_DBGOUT print statements: +#define CSDEBUG 1 + +Please see the defintions for cs_debuglevel and cs_debugmask for additional +information on the debug levels and sections. + +There is also a csdbg executable to allow runtime manipulation of these +parameters. for a copy email: twoller@crystal.cirrus.com + + + +MODULE_PARMS definitions +------------------------ +MODULE_PARM(defaultorder, "i"); +defaultorder=N +where N is a value from 1 to 12 +The buffer order determines the size of the dma buffer for the driver. +under Linux, a smaller buffer allows more responsiveness from many of the +applications (e.g. games). A larger buffer allows some of the apps (esound) +to not underrun the dma buffer as easily. As default, use 32k (order=3) +rather than 64k as some of the games work more responsively. +(2^N) * PAGE_SIZE = allocated buffer size + +MODULE_PARM(cs_debuglevel, "i"); +MODULE_PARM(cs_debugmask, "i"); +cs_debuglevel=N +cs_debugmask=0xMMMMMMMM +where N is a value from 0 (no debug printfs), to 9 (maximum) +0xMMMMMMMM is a debug mask corresponding to the CS_xxx bits (see driver source). + +MODULE_PARM(hercules_egpio_disable, "i"); +hercules_egpio_disable=N +where N is a 0 (enable egpio), or a 1 (disable egpio support) + +MODULE_PARM(initdelay, "i"); +initdelay=N +This value is used to determine the millescond delay during the initialization +code prior to powering up the PLL. On laptops this value can be used to +assist with errors on resume, mostly with IBM laptops. Basically, if the +system is booted under battery power then the mdelay()/udelay() functions fail to +properly delay the required time. Also, if the system is booted under AC power +and then the power removed, the mdelay()/udelay() functions will not delay properly. + +MODULE_PARM(powerdown, "i"); +powerdown=N +where N is 0 (disable any powerdown of the internal blocks) or 1 (enable powerdown) + + +MODULE_PARM(external_amp, "i"); +external_amp=1 +if N is set to 1, then force enabling the EAPD support in the primary AC97 codec. +override the detection logic and force the external amp bit in the AC97 0x26 register +to be reset (0). EAPD should be 0 for powerup, and 1 for powerdown. The VTB Santa Cruz +card has inverted logic, so there is a special function for these cards. + +MODULE_PARM(thinkpad, "i"); +thinkpad=1 +if N is set to 1, then force enabling the clkrun functionality. +Currently, when the part is being used, then clkrun is disabled for the entire system, +but re-enabled when the driver is released or there is no outstanding open count. + diff -Nru a/Documentation/sound/oss/es1370 b/Documentation/sound/oss/es1370 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/es1370 Thu Mar 7 18:17:40 2002 @@ -0,0 +1,70 @@ +/proc/sound, /dev/sndstat +------------------------- + +/proc/sound and /dev/sndstat is not supported by the +driver. To find out whether the driver succeeded loading, +check the kernel log (dmesg). + + +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +This soundcard does not have any hardware MIDI synthesizer; +MIDI synthesis has to be done in software. To allow this +the driver/soundcard supports two PCM (/dev/dsp) interfaces. +The second one goes to the mixer "synth" setting and supports +only a limited set of sampling rates (44100, 22050, 11025, 5512). +By setting lineout to 1 on the driver command line +(eg. insmod es1370 lineout=1) it is even possible on some +cards to convert the LINEIN jack into a second LINEOUT jack, thus +making it possible to output four independent audio channels! + +There is a freely available software package that allows +MIDI file playback on this soundcard called Timidity. +See http://www.cgs.fi/~tt/timidity/. + + + +Thomas Sailer +t.sailer@alumni.ethz.ch diff -Nru a/Documentation/sound/oss/es1371 b/Documentation/sound/oss/es1371 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/es1371 Thu Mar 7 18:17:41 2002 @@ -0,0 +1,64 @@ +/proc/sound, /dev/sndstat +------------------------- + +/proc/sound and /dev/sndstat is not supported by the +driver. To find out whether the driver succeeded loading, +check the kernel log (dmesg). + + +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +This soundcard does not have any hardware MIDI synthesizer; +MIDI synthesis has to be done in software. To allow this +the driver/soundcard supports two PCM (/dev/dsp) interfaces. + +There is a freely available software package that allows +MIDI file playback on this soundcard called Timidity. +See http://www.cgs.fi/~tt/timidity/. + + + +Thomas Sailer +t.sailer@alumni.ethz.ch diff -Nru a/Documentation/sound/oss/mwave b/Documentation/sound/oss/mwave --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/mwave Thu Mar 7 18:17:42 2002 @@ -0,0 +1,185 @@ + How to try to survive an IBM Mwave under Linux SB drivers + + ++ IBM have now released documentation of sorts and Torsten is busy + trying to make the Mwave work. This is not however a trivial task. + +---------------------------------------------------------------------------- + +OK, first thing - the IRQ problem IS a problem, whether the test is bypassed or +not. It is NOT a Linux problem, but an MWAVE problem that is fixed with the +latest MWAVE patches. So, in other words, don't bypass the test for MWAVES! + +I have Windows 95 on /dev/hda1, swap on /dev/hda2, and Red Hat 5 on /dev/hda3. + +The steps, then: + + Boot to Linux. + Mount Windows 95 file system (assume mount point = /dos95). + mkdir /dos95/linux + mkdir /dos95/linux/boot + mkdir /dos95/linux/boot/parms + + Copy the kernel, any initrd image, and loadlin to /dos95/linux/boot/. + + Reboot to Windows 95. + + Edit C:/msdos.sys and add or change the following: + + Logo=0 + BootGUI=0 + + Note that msdos.sys is a text file but it needs to be made 'unhidden', + readable and writable before it can be edited. This can be done with + DOS' "attrib" command. + + Edit config.sys to have multiple config menus. I have one for windows 95 and + five for Linux, like this: +------------ +[menu] +menuitem=W95, Windows 95 +menuitem=LINTP, Linux - ThinkPad +menuitem=LINTP3, Linux - ThinkPad Console +menuitem=LINDOC, Linux - Docked +menuitem=LINDOC3, Linux - Docked Console +menuitem=LIN1, Linux - Single User Mode +REM menudefault=W95,10 + +[W95] + +[LINTP] + +[LINDOC] + +[LINTP3] + +[LINDOC3] + +[LIN1] + +[COMMON] +FILES=30 +REM Please read README.TXT in C:\MWW subdirectory before changing the DOS= statement. +DOS=HIGH,UMB +DEVICE=C:\MWW\MANAGER\MWD50430.EXE +SHELL=c:\command.com /e:2048 +------------------- + +The important things are the SHELL and DEVICE statements. + + Then change autoexec.bat. Basically everything in there originally should be + done ONLY when Windows 95 is booted. Then you add new things specifically + for Linux. Mine is as follows + +--------------- +@ECHO OFF +if "%CONFIG%" == "W95" goto W95 + +REM +REM Linux stuff +REM +SET MWPATH=C:\MWW\DLL;C:\MWW\MWGAMES;C:\MWW\DSP +SET BLASTER=A220 I5 D1 +SET MWROOT=C:\MWW +SET LIBPATH=C:\MWW\DLL +SET PATH=C:\WINDOWS;C:\MWW\DLL; +CALL MWAVE START NOSHOW +c:\linux\boot\loadlin.exe @c:\linux\boot\parms\%CONFIG%.par + +:W95 +REM +REM Windows 95 stuff +REM +c:\toolkit\guard +SET MSINPUT=C:\MSINPUT +SET MWPATH=C:\MWW\DLL;C:\MWW\MWGAMES;C:\MWW\DSP +REM The following is used by DOS games to recognize Sound Blaster hardware. +REM If hardware settings are changed, please change this line as well. +REM See the Mwave README file for instructions. +SET BLASTER=A220 I5 D1 +SET MWROOT=C:\MWW +SET LIBPATH=C:\MWW\DLL +SET PATH=C:\WINDOWS;C:\WINDOWS\COMMAND;E:\ORAWIN95\BIN;f:\msdev\bin;e:\v30\bin.dbg;v:\devt\v30\bin;c:\JavaSDK\Bin;C:\MWW\DLL; +SET INCLUDE=f:\MSDEV\INCLUDE;F:\MSDEV\MFC\INCLUDE +SET LIB=F:\MSDEV\LIB;F:\MSDEV\MFC\LIB +win + +------------------------ + +Now build a file in c:\linux\boot\parms for each Linux config that you have. + +For example, my LINDOC3 config is for a docked Thinkpad at runlevel 3 with no +initrd image, and has a parameter file named LINDOC3.PAR in c:\linux\boot\parms: + +----------------------- +# LOADLIN @param_file image=other_image root=/dev/other +# +# Linux Console in docking station +# +c:\linux\boot\zImage.krn # First value must be filename of Linux kernel. +root=/dev/hda3 # device which gets mounted as root FS +ro # Other kernel arguments go here. +apm=off +doc=yes +3 +----------------------- + +The doc=yes parameter is an environment variable used by my init scripts, not +a kernel argument. + +However, the apm=off parameter IS a kernel argument! APM, at least in my setup, +causes the kernel to crash when loaded via loadlin (but NOT when loaded via +LILO). The APM stuff COULD be forced out of the kernel via the kernel compile +options. Instead, I got an unofficial patch to the APM drivers that allows them +to be dynamically deactivated via kernel arguments. Whatever you chose to +document, APM, it seems, MUST be off for setups like mine. + +Now make sure C:\MWW\MWCONFIG.REF looks like this: + +---------------------- +[NativeDOS] +Default=SB1.5 +SBInputSource=CD +SYNTH=FM +QSound=OFF +Reverb=OFF +Chorus=OFF +ReverbDepth=5 +ChorusDepth=5 +SBInputVolume=5 +SBMainVolume=10 +SBWaveVolume=10 +SBSynthVolume=10 +WaveTableVolume=10 +AudioPowerDriver=ON + +[FastCFG] +Show=No +HideOption=Off +----------------------------- + +OR the Default= line COULD be + +Default=SBPRO + +Reboot to Windows 95 and choose Linux. When booted, use sndconfig to configure +the sound modules and voilà - ThinkPad sound with Linux. + +Now the gotchas - you can either have CD sound OR Mixers but not both. That's a +problem with the SB1.5 (CD sound) or SBPRO (Mixers) settings. No one knows why +this is! + +For some reason MPEG3 files, when played through mpg123, sound like they +are playing at 1/8th speed - not very useful! If you have ANY insight +on why this second thing might be happening, I would be grateful. + +=========================================================== + _/ _/_/_/_/ + _/_/ _/_/ _/ + _/ _/_/ _/_/_/_/ Martin John Bartlett + _/ _/ _/ _/ (martin@nitram.demon.co.uk) +_/ _/_/_/_/ + _/ +_/ _/ + _/_/ +=========================================================== diff -Nru a/Documentation/sound/oss/solo1 b/Documentation/sound/oss/solo1 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/solo1 Thu Mar 7 18:17:46 2002 @@ -0,0 +1,70 @@ +Recording +--------- + +Recording does not work on the author's card, but there +is at least one report of it working on later silicon. +The chip behaves differently than described in the data sheet, +likely due to a chip bug. Working around this would require +the help of ESS (for example by publishing an errata sheet), +but ESS has not done so so far. + +Also, the chip only supports 24 bit addresses for recording, +which means it cannot work on some Alpha mainboards. + + +/proc/sound, /dev/sndstat +------------------------- + +/proc/sound and /dev/sndstat is not supported by the +driver. To find out whether the driver succeeded loading, +check the kernel log (dmesg). + + +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (or later, available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +The card has an OPL compatible FM synthesizer. + +Thomas Sailer +t.sailer@alumni.ethz.ch diff -Nru a/Documentation/sound/oss/sonicvibes b/Documentation/sound/oss/sonicvibes --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/sonicvibes Thu Mar 7 18:17:41 2002 @@ -0,0 +1,81 @@ +/proc/sound, /dev/sndstat +------------------------- + +/proc/sound and /dev/sndstat is not supported by the +driver. To find out whether the driver succeeded loading, +check the kernel log (dmesg). + + +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +The card both has an OPL compatible FM synthesizer as well as +a wavetable synthesizer. + +I haven't managed so far to get the OPL synth running. + +Using the wavetable synthesizer requires allocating +1-4MB of physically contiguous memory, which isn't possible +currently on Linux without ugly hacks like the bigphysarea +patch. Therefore, the driver doesn't support wavetable +synthesis. + + +No support from S3 +------------------ + +I do not get any support from S3. Therefore, the driver +still has many problems. For example, although the manual +states that the chip should be able to access the sample +buffer anywhere in 32bit address space, I haven't managed to +get it working with buffers above 16M. Therefore, the card +has the same disadvantages as ISA soundcards. + +Given that the card is also very noisy, and if you haven't +already bought it, you should strongly opt for one of the +comparatively priced Ensoniq products. + + +Thomas Sailer +t.sailer@alumni.ethz.ch diff -Nru a/Documentation/sound/oss/ultrasound b/Documentation/sound/oss/ultrasound --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/ultrasound Thu Mar 7 18:17:37 2002 @@ -0,0 +1,30 @@ +modprobe sound +insmod ad1848 +insmod gus io=* irq=* dma=* ... + +This loads the driver for the Gravis Ultrasound family of sound cards. + +The gus module takes the following arguments + +io I/O address of the Ultrasound card (eg. io=0x220) +irq IRQ of the Sound Blaster card +dma DMA channel for the Sound Blaster +dma16 2nd DMA channel, only needed for full duplex operation +type 1 for PnP card +gus16 1 for using 16 bit sampling daughter board +no_wave_dma Set to disable DMA usage for wavetable (see note) +db16 ??? + + +no_wave_dma option + +This option defaults to a value of 0, which allows the Ultrasound wavetable +DSP to use DMA for for playback and downloading samples. This is the same +as the old behaviour. If set to 1, no DMA is needed for downloading samples, +and allows owners of a GUS MAX to make use of simultaneous digital audio +(/dev/dsp), MIDI, and wavetable playback. + + +If you have problems in recording with GUS MAX, you could try to use +just one 8 bit DMA channel. Recording will not work with one DMA +channel if it's a 16 bit one. diff -Nru a/Documentation/sound/oss/vwsnd b/Documentation/sound/oss/vwsnd --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/oss/vwsnd Thu Mar 7 18:17:38 2002 @@ -0,0 +1,293 @@ +vwsnd - Sound driver for the Silicon Graphics 320 and 540 Visual +Workstations' onboard audio. + +Copyright 1999 Silicon Graphics, Inc. All rights reserved. + + +At the time of this writing, March 1999, there are two models of +Visual Workstation, the 320 and the 540. This document only describes +those models. Future Visual Workstation models may have different +sound capabilities, and this driver will probably not work on those +boxes. + +The Visual Workstation has an Analog Devices AD1843 "SoundComm" audio +codec chip. The AD1843 is accessed through the Cobalt I/O ASIC, also +known as Lithium. This driver programs both both chips. + +============================================================================== +QUICK CONFIGURATION + + # insmod soundcore + # insmod vwsnd + +============================================================================== +I/O CONNECTIONS + +On the Visual Workstation, only three of the AD1843 inputs are hooked +up. The analog line in jacks are connected to the AD1843's AUX1 +input. The CD audio lines are connected to the AD1843's AUX2 input. +The microphone jack is connected to the AD1843's MIC input. The mic +jack is mono, but the signal is delivered to both the left and right +MIC inputs. You can record in stereo from the mic input, but you will +get the same signal on both channels (within the limits of A/D +accuracy). Full scale on the Line input is +/- 2.0 V. Full scale on +the MIC input is 20 dB less, or +/- 0.2 V. + +The AD1843's LOUT1 outputs are connected to the Line Out jacks. The +AD1843's HPOUT outputs are connected to the speaker/headphone jack. +LOUT2 is not connected. Line out's maximum level is +/- 2.0 V peak to +peak. The speaker/headphone out's maximum is +/- 4.0 V peak to peak. + +The AD1843's PCM input channel and one of its output channels (DAC1) +are connected to Lithium. The other output channel (DAC2) is not +connected. + +============================================================================== +CAPABILITIES + +The AD1843 has PCM input and output (Pulse Code Modulation, also known +as wavetable). PCM input and output can be mono or stereo in any of +four formats. The formats are 16 bit signed and 8 bit unsigned, +u-Law, and A-Law format. Any sample rate from 4 KHz to 49 KHz is +available, in 1 Hz increments. + +The AD1843 includes an analog mixer that can mix all three input +signals (line, mic and CD) into the analog outputs. The mixer has a +separate gain control and mute switch for each input. + +There are two outputs, line out and speaker/headphone out. They +always produce the same signal, and the speaker always has 3 dB more +gain than the line out. The speaker/headphone output can be muted, +but this driver does not export that function. + +The hardware can sync audio to the video clock, but this driver does +not have a way to specify syncing to video. + +============================================================================== +PROGRAMMING + +This section explains the API supported by the driver. Also see the +Open Sound Programming Guide at http://www.opensound.com/pguide/ . +This section assumes familiarity with that document. + +The driver has two interfaces, an I/O interface and a mixer interface. +There is no MIDI or sequencer capability. + +============================================================================== +PROGRAMMING PCM I/O + +The I/O interface is usually accessed as /dev/audio or /dev/dsp. +Using the standard Open Sound System (OSS) ioctl calls, the sample +rate, number of channels, and sample format may be set within the +limitations described above. The driver supports triggering. It also +supports getting the input and output pointers with one-sample +accuracy. + +The SNDCTL_DSP_GETCAP ioctl returns these capabilities. + + DSP_CAP_DUPLEX - driver supports full duplex. + + DSP_CAP_TRIGGER - driver supports triggering. + + DSP_CAP_REALTIME - values returned by SNDCTL_DSP_GETIPTR + and SNDCTL_DSP_GETOPTR are accurate to a few samples. + +Memory mapping (mmap) is not implemented. + +The driver permits subdivided fragment sizes from 64 to 4096 bytes. +The number of fragments can be anything from 3 fragments to however +many fragments fit into 124 kilobytes. It is up to the user to +determine how few/small fragments can be used without introducing +glitches with a given workload. Linux is not realtime, so we can't +promise anything. (sigh...) + +When this driver is switched into or out of mu-Law or A-Law mode on +output, it may produce an audible click. This is unavoidable. To +prevent clicking, use signed 16-bit mode instead, and convert from +mu-Law or A-Law format in software. + +============================================================================== +PROGRAMMING THE MIXER INTERFACE + +The mixer interface is usually accessed as /dev/mixer. It is accessed +through ioctls. The mixer allows the application to control gain or +mute several audio signal paths, and also allows selection of the +recording source. + +Each of the constants described here can be read using the +MIXER_READ(SOUND_MIXER_xxx) ioctl. Those that are not read-only can +also be written using the MIXER_WRITE(SOUND_MIXER_xxx) ioctl. In most +cases, defines constants SOUND_MIXER_READ_xxx and +SOUND_MIXER_WRITE_xxx which work just as well. + +SOUND_MIXER_CAPS Read-only + +This is a mask of optional driver capabilities that are implemented. +This driver's only capability is SOUND_CAP_EXCL_INPUT, which means +that only one recording source can be active at a time. + +SOUND_MIXER_DEVMASK Read-only + +This is a mask of the sound channels. This driver's channels are PCM, +LINE, MIC, CD, and RECLEV. + +SOUND_MIXER_STEREODEVS Read-only + +This is a mask of which sound channels are capable of stereo. All +channels are capable of stereo. (But see caveat on MIC input in I/O +CONNECTIONS section above). + +SOUND_MIXER_OUTMASK Read-only + +This is a mask of channels that route inputs through to outputs. +Those are LINE, MIC, and CD. + +SOUND_MIXER_RECMASK Read-only + +This is a mask of channels that can be recording sources. Those are +PCM, LINE, MIC, CD. + +SOUND_MIXER_PCM Default: 0x5757 (0 dB) + +This is the gain control for PCM output. The left and right channel +gain are controlled independently. This gain control has 64 levels, +which range from -82.5 dB to +12.0 dB in 1.5 dB steps. Those 64 +levels are mapped onto 100 levels at the ioctl, see below. + +SOUND_MIXER_LINE Default: 0x4a4a (0 dB) + +This is the gain control for mixing the Line In source into the +outputs. The left and right channel gain are controlled +independently. This gain control has 32 levels, which range from +-34.5 dB to +12.0 dB in 1.5 dB steps. Those 32 levels are mapped onto +100 levels at the ioctl, see below. + +SOUND_MIXER_MIC Default: 0x4a4a (0 dB) + +This is the gain control for mixing the MIC source into the outputs. +The left and right channel gain are controlled independently. This +gain control has 32 levels, which range from -34.5 dB to +12.0 dB in +1.5 dB steps. Those 32 levels are mapped onto 100 levels at the +ioctl, see below. + +SOUND_MIXER_CD Default: 0x4a4a (0 dB) + +This is the gain control for mixing the CD audio source into the +outputs. The left and right channel gain are controlled +independently. This gain control has 32 levels, which range from +-34.5 dB to +12.0 dB in 1.5 dB steps. Those 32 levels are mapped onto +100 levels at the ioctl, see below. + +SOUND_MIXER_RECLEV Default: 0 (0 dB) + +This is the gain control for PCM input (RECording LEVel). The left +and right channel gain are controlled independently. This gain +control has 16 levels, which range from 0 dB to +22.5 dB in 1.5 dB +steps. Those 16 levels are mapped onto 100 levels at the ioctl, see +below. + +SOUND_MIXER_RECSRC Default: SOUND_MASK_LINE + +This is a mask of currently selected PCM input sources (RECording +SouRCes). Because the AD1843 can only have a single recording source +at a time, only one bit at a time can be set in this mask. The +allowable values are SOUND_MASK_PCM, SOUND_MASK_LINE, SOUND_MASK_MIC, +or SOUND_MASK_CD. Selecting SOUND_MASK_PCM sets up internal +resampling which is useful for loopback testing and for hardware +sample rate conversion. But software sample rate conversion is +probably faster, so I don't know how useful that is. + +SOUND_MIXER_OUTSRC DEFAULT: SOUND_MASK_LINE|SOUND_MASK_MIC|SOUND_MASK_CD + +This is a mask of sources that are currently passed through to the +outputs. Those sources whose bits are not set are muted. + +============================================================================== +GAIN CONTROL + +There are five gain controls listed above. Each has 16, 32, or 64 +steps. Each control has 1.5 dB of gain per step. Each control is +stereo. + +The OSS defines the argument to a channel gain ioctl as having two +components, left and right, each of which ranges from 0 to 100. The +two components are packed into the same word, with the left side gain +in the least significant byte, and the right side gain in the second +least significant byte. In C, we would say this. + + #include + + ... + + assert(leftgain >= 0 && leftgain <= 100); + assert(rightgain >= 0 && rightgain <= 100); + arg = leftgain | rightgain << 8; + +So each OSS gain control has 101 steps. But the hardware has 16, 32, +or 64 steps. The hardware steps are spread across the 101 OSS steps +nearly evenly. The conversion formulas are like this, given N equals +16, 32, or 64. + + int round = N/2 - 1; + OSS_gain_steps = (hw_gain_steps * 100 + round) / (N - 1); + hw_gain_steps = (OSS_gain_steps * (N - 1) + round) / 100; + +Here is a snippet of C code that will return the left and right gain +of any channel in dB. Pass it one of the predefined gain_desc_t +structures to access any of the five channels' gains. + + typedef struct gain_desc { + float min_gain; + float gain_step; + int nbits; + int chan; + } gain_desc_t; + + const gain_desc_t gain_pcm = { -82.5, 1.5, 6, SOUND_MIXER_PCM }; + const gain_desc_t gain_line = { -34.5, 1.5, 5, SOUND_MIXER_LINE }; + const gain_desc_t gain_mic = { -34.5, 1.5, 5, SOUND_MIXER_MIC }; + const gain_desc_t gain_cd = { -34.5, 1.5, 5, SOUND_MIXER_CD }; + const gain_desc_t gain_reclev = { 0.0, 1.5, 4, SOUND_MIXER_RECLEV }; + + int get_gain_dB(int fd, const gain_desc_t *gp, + float *left, float *right) + { + int word; + int lg, rg; + int mask = (1 << gp->nbits) - 1; + + if (ioctl(fd, MIXER_READ(gp->chan), &word) != 0) + return -1; /* fail */ + lg = word & 0xFF; + rg = word >> 8 & 0xFF; + lg = (lg * mask + mask / 2) / 100; + rg = (rg * mask + mask / 2) / 100; + *left = gp->min_gain + gp->gain_step * lg; + *right = gp->min_gain + gp->gain_step * rg; + return 0; + } + +And here is the corresponding routine to set a channel's gain in dB. + + int set_gain_dB(int fd, const gain_desc_t *gp, float left, float right) + { + float max_gain = + gp->min_gain + (1 << gp->nbits) * gp->gain_step; + float round = gp->gain_step / 2; + int mask = (1 << gp->nbits) - 1; + int word; + int lg, rg; + + if (left < gp->min_gain || right < gp->min_gain) + return EINVAL; + lg = (left - gp->min_gain + round) / gp->gain_step; + rg = (right - gp->min_gain + round) / gp->gain_step; + if (lg >= (1 << gp->nbits) || rg >= (1 << gp->nbits)) + return EINVAL; + lg = (100 * lg + mask / 2) / mask; + rg = (100 * rg + mask / 2) / mask; + word = lg | rg << 8; + + return ioctl(fd, MIXER_WRITE(gp->chan), &word); + } + diff -Nru a/Documentation/sound/solo1 b/Documentation/sound/solo1 --- a/Documentation/sound/solo1 Thu Mar 7 18:17:46 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,70 +0,0 @@ -Recording ---------- - -Recording does not work on the author's card, but there -is at least one report of it working on later silicon. -The chip behaves differently than described in the data sheet, -likely due to a chip bug. Working around this would require -the help of ESS (for example by publishing an errata sheet), -but ESS has not done so so far. - -Also, the chip only supports 24 bit addresses for recording, -which means it cannot work on some Alpha mainboards. - - -/proc/sound, /dev/sndstat -------------------------- - -/proc/sound and /dev/sndstat is not supported by the -driver. To find out whether the driver succeeded loading, -check the kernel log (dmesg). - - -ALaw/uLaw sample formats ------------------------- - -This driver does not support the ALaw/uLaw sample formats. -ALaw is the default mode when opening a sound device -using OSS/Free. The reason for the lack of support is -that the hardware does not support these formats, and adding -conversion routines to the kernel would lead to very ugly -code in the presence of the mmap interface to the driver. -And since xquake uses mmap, mmap is considered important :-) -and no sane application uses ALaw/uLaw these days anyway. -In short, playing a Sun .au file as follows: - -cat my_file.au > /dev/dsp - -does not work. Instead, you may use the play script from -Chris Bagwell's sox-12.14 package (or later, available from the URL -below) to play many different audio file formats. -The script automatically determines the audio format -and does do audio conversions if necessary. -http://home.sprynet.com/sprynet/cbagwell/projects.html - - -Blocking vs. nonblocking IO ---------------------------- - -Unlike OSS/Free this driver honours the O_NONBLOCK file flag -not only during open, but also during read and write. -This is an effort to make the sound driver interface more -regular. Timidity has problems with this; a patch -is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. -(Timidity patched will also run on OSS/Free). - - -MIDI UART ---------- - -The driver supports a simple MIDI UART interface, with -no ioctl's supported. - - -MIDI synthesizer ----------------- - -The card has an OPL compatible FM synthesizer. - -Thomas Sailer -t.sailer@alumni.ethz.ch diff -Nru a/Documentation/sound/sonicvibes b/Documentation/sound/sonicvibes --- a/Documentation/sound/sonicvibes Thu Mar 7 18:17:41 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,81 +0,0 @@ -/proc/sound, /dev/sndstat -------------------------- - -/proc/sound and /dev/sndstat is not supported by the -driver. To find out whether the driver succeeded loading, -check the kernel log (dmesg). - - -ALaw/uLaw sample formats ------------------------- - -This driver does not support the ALaw/uLaw sample formats. -ALaw is the default mode when opening a sound device -using OSS/Free. The reason for the lack of support is -that the hardware does not support these formats, and adding -conversion routines to the kernel would lead to very ugly -code in the presence of the mmap interface to the driver. -And since xquake uses mmap, mmap is considered important :-) -and no sane application uses ALaw/uLaw these days anyway. -In short, playing a Sun .au file as follows: - -cat my_file.au > /dev/dsp - -does not work. Instead, you may use the play script from -Chris Bagwell's sox-12.14 package (available from the URL -below) to play many different audio file formats. -The script automatically determines the audio format -and does do audio conversions if necessary. -http://home.sprynet.com/sprynet/cbagwell/projects.html - - -Blocking vs. nonblocking IO ---------------------------- - -Unlike OSS/Free this driver honours the O_NONBLOCK file flag -not only during open, but also during read and write. -This is an effort to make the sound driver interface more -regular. Timidity has problems with this; a patch -is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. -(Timidity patched will also run on OSS/Free). - - -MIDI UART ---------- - -The driver supports a simple MIDI UART interface, with -no ioctl's supported. - - -MIDI synthesizer ----------------- - -The card both has an OPL compatible FM synthesizer as well as -a wavetable synthesizer. - -I haven't managed so far to get the OPL synth running. - -Using the wavetable synthesizer requires allocating -1-4MB of physically contiguous memory, which isn't possible -currently on Linux without ugly hacks like the bigphysarea -patch. Therefore, the driver doesn't support wavetable -synthesis. - - -No support from S3 ------------------- - -I do not get any support from S3. Therefore, the driver -still has many problems. For example, although the manual -states that the chip should be able to access the sample -buffer anywhere in 32bit address space, I haven't managed to -get it working with buffers above 16M. Therefore, the card -has the same disadvantages as ISA soundcards. - -Given that the card is also very noisy, and if you haven't -already bought it, you should strongly opt for one of the -comparatively priced Ensoniq products. - - -Thomas Sailer -t.sailer@alumni.ethz.ch diff -Nru a/Documentation/sound/ultrasound b/Documentation/sound/ultrasound --- a/Documentation/sound/ultrasound Thu Mar 7 18:17:37 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,30 +0,0 @@ -modprobe sound -insmod ad1848 -insmod gus io=* irq=* dma=* ... - -This loads the driver for the Gravis Ultrasound family of sound cards. - -The gus module takes the following arguments - -io I/O address of the Ultrasound card (eg. io=0x220) -irq IRQ of the Sound Blaster card -dma DMA channel for the Sound Blaster -dma16 2nd DMA channel, only needed for full duplex operation -type 1 for PnP card -gus16 1 for using 16 bit sampling daughter board -no_wave_dma Set to disable DMA usage for wavetable (see note) -db16 ??? - - -no_wave_dma option - -This option defaults to a value of 0, which allows the Ultrasound wavetable -DSP to use DMA for for playback and downloading samples. This is the same -as the old behaviour. If set to 1, no DMA is needed for downloading samples, -and allows owners of a GUS MAX to make use of simultaneous digital audio -(/dev/dsp), MIDI, and wavetable playback. - - -If you have problems in recording with GUS MAX, you could try to use -just one 8 bit DMA channel. Recording will not work with one DMA -channel if it's a 16 bit one. diff -Nru a/Documentation/sound/vwsnd b/Documentation/sound/vwsnd --- a/Documentation/sound/vwsnd Thu Mar 7 18:17:38 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,293 +0,0 @@ -vwsnd - Sound driver for the Silicon Graphics 320 and 540 Visual -Workstations' onboard audio. - -Copyright 1999 Silicon Graphics, Inc. All rights reserved. - - -At the time of this writing, March 1999, there are two models of -Visual Workstation, the 320 and the 540. This document only describes -those models. Future Visual Workstation models may have different -sound capabilities, and this driver will probably not work on those -boxes. - -The Visual Workstation has an Analog Devices AD1843 "SoundComm" audio -codec chip. The AD1843 is accessed through the Cobalt I/O ASIC, also -known as Lithium. This driver programs both both chips. - -============================================================================== -QUICK CONFIGURATION - - # insmod soundcore - # insmod vwsnd - -============================================================================== -I/O CONNECTIONS - -On the Visual Workstation, only three of the AD1843 inputs are hooked -up. The analog line in jacks are connected to the AD1843's AUX1 -input. The CD audio lines are connected to the AD1843's AUX2 input. -The microphone jack is connected to the AD1843's MIC input. The mic -jack is mono, but the signal is delivered to both the left and right -MIC inputs. You can record in stereo from the mic input, but you will -get the same signal on both channels (within the limits of A/D -accuracy). Full scale on the Line input is +/- 2.0 V. Full scale on -the MIC input is 20 dB less, or +/- 0.2 V. - -The AD1843's LOUT1 outputs are connected to the Line Out jacks. The -AD1843's HPOUT outputs are connected to the speaker/headphone jack. -LOUT2 is not connected. Line out's maximum level is +/- 2.0 V peak to -peak. The speaker/headphone out's maximum is +/- 4.0 V peak to peak. - -The AD1843's PCM input channel and one of its output channels (DAC1) -are connected to Lithium. The other output channel (DAC2) is not -connected. - -============================================================================== -CAPABILITIES - -The AD1843 has PCM input and output (Pulse Code Modulation, also known -as wavetable). PCM input and output can be mono or stereo in any of -four formats. The formats are 16 bit signed and 8 bit unsigned, -u-Law, and A-Law format. Any sample rate from 4 KHz to 49 KHz is -available, in 1 Hz increments. - -The AD1843 includes an analog mixer that can mix all three input -signals (line, mic and CD) into the analog outputs. The mixer has a -separate gain control and mute switch for each input. - -There are two outputs, line out and speaker/headphone out. They -always produce the same signal, and the speaker always has 3 dB more -gain than the line out. The speaker/headphone output can be muted, -but this driver does not export that function. - -The hardware can sync audio to the video clock, but this driver does -not have a way to specify syncing to video. - -============================================================================== -PROGRAMMING - -This section explains the API supported by the driver. Also see the -Open Sound Programming Guide at http://www.opensound.com/pguide/ . -This section assumes familiarity with that document. - -The driver has two interfaces, an I/O interface and a mixer interface. -There is no MIDI or sequencer capability. - -============================================================================== -PROGRAMMING PCM I/O - -The I/O interface is usually accessed as /dev/audio or /dev/dsp. -Using the standard Open Sound System (OSS) ioctl calls, the sample -rate, number of channels, and sample format may be set within the -limitations described above. The driver supports triggering. It also -supports getting the input and output pointers with one-sample -accuracy. - -The SNDCTL_DSP_GETCAP ioctl returns these capabilities. - - DSP_CAP_DUPLEX - driver supports full duplex. - - DSP_CAP_TRIGGER - driver supports triggering. - - DSP_CAP_REALTIME - values returned by SNDCTL_DSP_GETIPTR - and SNDCTL_DSP_GETOPTR are accurate to a few samples. - -Memory mapping (mmap) is not implemented. - -The driver permits subdivided fragment sizes from 64 to 4096 bytes. -The number of fragments can be anything from 3 fragments to however -many fragments fit into 124 kilobytes. It is up to the user to -determine how few/small fragments can be used without introducing -glitches with a given workload. Linux is not realtime, so we can't -promise anything. (sigh...) - -When this driver is switched into or out of mu-Law or A-Law mode on -output, it may produce an audible click. This is unavoidable. To -prevent clicking, use signed 16-bit mode instead, and convert from -mu-Law or A-Law format in software. - -============================================================================== -PROGRAMMING THE MIXER INTERFACE - -The mixer interface is usually accessed as /dev/mixer. It is accessed -through ioctls. The mixer allows the application to control gain or -mute several audio signal paths, and also allows selection of the -recording source. - -Each of the constants described here can be read using the -MIXER_READ(SOUND_MIXER_xxx) ioctl. Those that are not read-only can -also be written using the MIXER_WRITE(SOUND_MIXER_xxx) ioctl. In most -cases, defines constants SOUND_MIXER_READ_xxx and -SOUND_MIXER_WRITE_xxx which work just as well. - -SOUND_MIXER_CAPS Read-only - -This is a mask of optional driver capabilities that are implemented. -This driver's only capability is SOUND_CAP_EXCL_INPUT, which means -that only one recording source can be active at a time. - -SOUND_MIXER_DEVMASK Read-only - -This is a mask of the sound channels. This driver's channels are PCM, -LINE, MIC, CD, and RECLEV. - -SOUND_MIXER_STEREODEVS Read-only - -This is a mask of which sound channels are capable of stereo. All -channels are capable of stereo. (But see caveat on MIC input in I/O -CONNECTIONS section above). - -SOUND_MIXER_OUTMASK Read-only - -This is a mask of channels that route inputs through to outputs. -Those are LINE, MIC, and CD. - -SOUND_MIXER_RECMASK Read-only - -This is a mask of channels that can be recording sources. Those are -PCM, LINE, MIC, CD. - -SOUND_MIXER_PCM Default: 0x5757 (0 dB) - -This is the gain control for PCM output. The left and right channel -gain are controlled independently. This gain control has 64 levels, -which range from -82.5 dB to +12.0 dB in 1.5 dB steps. Those 64 -levels are mapped onto 100 levels at the ioctl, see below. - -SOUND_MIXER_LINE Default: 0x4a4a (0 dB) - -This is the gain control for mixing the Line In source into the -outputs. The left and right channel gain are controlled -independently. This gain control has 32 levels, which range from --34.5 dB to +12.0 dB in 1.5 dB steps. Those 32 levels are mapped onto -100 levels at the ioctl, see below. - -SOUND_MIXER_MIC Default: 0x4a4a (0 dB) - -This is the gain control for mixing the MIC source into the outputs. -The left and right channel gain are controlled independently. This -gain control has 32 levels, which range from -34.5 dB to +12.0 dB in -1.5 dB steps. Those 32 levels are mapped onto 100 levels at the -ioctl, see below. - -SOUND_MIXER_CD Default: 0x4a4a (0 dB) - -This is the gain control for mixing the CD audio source into the -outputs. The left and right channel gain are controlled -independently. This gain control has 32 levels, which range from --34.5 dB to +12.0 dB in 1.5 dB steps. Those 32 levels are mapped onto -100 levels at the ioctl, see below. - -SOUND_MIXER_RECLEV Default: 0 (0 dB) - -This is the gain control for PCM input (RECording LEVel). The left -and right channel gain are controlled independently. This gain -control has 16 levels, which range from 0 dB to +22.5 dB in 1.5 dB -steps. Those 16 levels are mapped onto 100 levels at the ioctl, see -below. - -SOUND_MIXER_RECSRC Default: SOUND_MASK_LINE - -This is a mask of currently selected PCM input sources (RECording -SouRCes). Because the AD1843 can only have a single recording source -at a time, only one bit at a time can be set in this mask. The -allowable values are SOUND_MASK_PCM, SOUND_MASK_LINE, SOUND_MASK_MIC, -or SOUND_MASK_CD. Selecting SOUND_MASK_PCM sets up internal -resampling which is useful for loopback testing and for hardware -sample rate conversion. But software sample rate conversion is -probably faster, so I don't know how useful that is. - -SOUND_MIXER_OUTSRC DEFAULT: SOUND_MASK_LINE|SOUND_MASK_MIC|SOUND_MASK_CD - -This is a mask of sources that are currently passed through to the -outputs. Those sources whose bits are not set are muted. - -============================================================================== -GAIN CONTROL - -There are five gain controls listed above. Each has 16, 32, or 64 -steps. Each control has 1.5 dB of gain per step. Each control is -stereo. - -The OSS defines the argument to a channel gain ioctl as having two -components, left and right, each of which ranges from 0 to 100. The -two components are packed into the same word, with the left side gain -in the least significant byte, and the right side gain in the second -least significant byte. In C, we would say this. - - #include - - ... - - assert(leftgain >= 0 && leftgain <= 100); - assert(rightgain >= 0 && rightgain <= 100); - arg = leftgain | rightgain << 8; - -So each OSS gain control has 101 steps. But the hardware has 16, 32, -or 64 steps. The hardware steps are spread across the 101 OSS steps -nearly evenly. The conversion formulas are like this, given N equals -16, 32, or 64. - - int round = N/2 - 1; - OSS_gain_steps = (hw_gain_steps * 100 + round) / (N - 1); - hw_gain_steps = (OSS_gain_steps * (N - 1) + round) / 100; - -Here is a snippet of C code that will return the left and right gain -of any channel in dB. Pass it one of the predefined gain_desc_t -structures to access any of the five channels' gains. - - typedef struct gain_desc { - float min_gain; - float gain_step; - int nbits; - int chan; - } gain_desc_t; - - const gain_desc_t gain_pcm = { -82.5, 1.5, 6, SOUND_MIXER_PCM }; - const gain_desc_t gain_line = { -34.5, 1.5, 5, SOUND_MIXER_LINE }; - const gain_desc_t gain_mic = { -34.5, 1.5, 5, SOUND_MIXER_MIC }; - const gain_desc_t gain_cd = { -34.5, 1.5, 5, SOUND_MIXER_CD }; - const gain_desc_t gain_reclev = { 0.0, 1.5, 4, SOUND_MIXER_RECLEV }; - - int get_gain_dB(int fd, const gain_desc_t *gp, - float *left, float *right) - { - int word; - int lg, rg; - int mask = (1 << gp->nbits) - 1; - - if (ioctl(fd, MIXER_READ(gp->chan), &word) != 0) - return -1; /* fail */ - lg = word & 0xFF; - rg = word >> 8 & 0xFF; - lg = (lg * mask + mask / 2) / 100; - rg = (rg * mask + mask / 2) / 100; - *left = gp->min_gain + gp->gain_step * lg; - *right = gp->min_gain + gp->gain_step * rg; - return 0; - } - -And here is the corresponding routine to set a channel's gain in dB. - - int set_gain_dB(int fd, const gain_desc_t *gp, float left, float right) - { - float max_gain = - gp->min_gain + (1 << gp->nbits) * gp->gain_step; - float round = gp->gain_step / 2; - int mask = (1 << gp->nbits) - 1; - int word; - int lg, rg; - - if (left < gp->min_gain || right < gp->min_gain) - return EINVAL; - lg = (left - gp->min_gain + round) / gp->gain_step; - rg = (right - gp->min_gain + round) / gp->gain_step; - if (lg >= (1 << gp->nbits) || rg >= (1 << gp->nbits)) - return EINVAL; - lg = (100 * lg + mask / 2) / mask; - rg = (100 * rg + mask / 2) / mask; - word = lg | rg << 8; - - return ioctl(fd, MIXER_WRITE(gp->chan), &word); - } - diff -Nru a/Documentation/usb/auerswald.txt b/Documentation/usb/auerswald.txt --- a/Documentation/usb/auerswald.txt Thu Mar 7 18:17:45 2002 +++ b/Documentation/usb/auerswald.txt Thu Mar 7 18:17:45 2002 @@ -19,9 +19,9 @@ auerswald and shipped as part of the java software. You may create the devices with: - mknod -m 666 /dev/usb/auer0 c 180 80 + mknod -m 666 /dev/usb/auer0 c 180 112 ... - mknod -m 666 /dev/usb/auer15 c 180 95 + mknod -m 666 /dev/usb/auer15 c 180 127 Future plans ============ diff -Nru a/Documentation/usb/ov511.txt b/Documentation/usb/ov511.txt --- a/Documentation/usb/ov511.txt Thu Mar 7 18:17:38 2002 +++ b/Documentation/usb/ov511.txt Thu Mar 7 18:17:38 2002 @@ -182,9 +182,9 @@ DEFAULT: 1 (Always on) DESC: Controls whether the LED (the little light) on the front of the camera is always off (0), always on (1), or only on when driver is open (2). - This is only supported with the OV511+ chipset, and even then only on - some cameras (ones that actually have the LED wired to the control pin, - and not just hardwired to be on all the time). + This is not supported with the OV511, and might only work with certain + cameras (ones that actually have the LED wired to the control pin, and + not just hard-wired to be on all the time). NAME: dump_bridge TYPE: integer (Boolean) diff -Nru a/Documentation/usb/usb-serial.txt b/Documentation/usb/usb-serial.txt --- a/Documentation/usb/usb-serial.txt Thu Mar 7 18:17:38 2002 +++ b/Documentation/usb/usb-serial.txt Thu Mar 7 18:17:38 2002 @@ -95,12 +95,13 @@ Kroah-Hartman at greg@kroah.com -Compaq iPAQ driver +Compaq iPAQ and HP Jornada driver - This driver can be used to connect to Compaq iPAQ PDAs running - Windows CE 3.0 using a USB autosync cable. It has been tested only on - the Compaq H3135. It should work with the H3600 and later models too. - It may work with other CE based handhelds as well. + This driver can be used to connect to Compaq iPAQ and HP Jornada PDAs + running Windows CE 3.0 or PocketPC 2002 using a USB cable/cradle. It + has been tested only on the Compaq H3135, but is rumoured to work on + with the H3600 and later models as well as the Jornada 548 and 568. + With minor modifications, it may work for other CE based handhelds too. The driver presents a serial interface (usually on /dev/ttyUSB0) over which one may run ppp and establish a TCP/IP link to the iPAQ. Once this @@ -109,11 +110,14 @@ kbytes/sec for download/upload to the iPAQ. The driver works intermittently with the usb-uhci driver but quite - reliably with the uhci driver. Make sure you have the right driver - loaded - usb-uhci is often the default. + reliably with the uhci driver. However, performance is much better + with usb-uhci. It does not seem to work with ohci at all. You must setup hotplug to invoke pppd as soon as the iPAQ is connected. - A ppp script like the one below may be used: + A ppp script like the one below should be kept in the file + /etc/hotplug/usb/ipaq Remember to chmod +x. Make sure there are no + options in /etc/ppp/options or ~/.ppprc which conflict with the ones + given below. #!/bin/bash @@ -133,7 +137,7 @@ On connecting the cable, you should see the usual "Device Connected", "User Authenticated" messages flash by on your iPAQ. Once connected, you can use Win CE programs like ftpView, Pocket Outlook from the iPAQ - and other synce utilities from the Linux side. Remember to enable IP + and xcerdisp, synce utilities from the Linux side. Remember to enable IP forwarding. To use Pocket IE, follow the instructions given at diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Thu Mar 7 18:17:37 2002 +++ b/MAINTAINERS Thu Mar 7 18:17:37 2002 @@ -69,6 +69,14 @@ it has been replaced by a better system and you should be using that. +3C359 NETWORK DRIVER +P: Mike Phillips +M: mikep@linuxtr.net +L: linux-net@vger.rutgers.edu +L: linux-tr@linuxtr.net +W: http://www.linuxtr.net +S: Maintained + 3C501 NETWORK DRIVER P: Alan Cox M: alan@the.3c501.cabal.tm @@ -118,11 +126,11 @@ 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 +P: Enver Haase +M: ehaase@inf.fu-berlin.de +M: A2232@gmx.net +L: linux-m68k@lists.linux-m68k.org +S: Maintained ACENIC DRIVER P: Jes Sorensen @@ -145,6 +153,14 @@ W: http://www.ibm.com/linux/ltc/ S: Supported +AACRAID SCSI RAID DRIVER +P: Adaptec OEM Raid Solutions +M: linux-aacraid-devel@dell.com +L: linux-aacraid-devel@dell.com +L: linux-aacraid-announce@dell.com +W: http://domsch.com/linux +S: Supported + ACPI P: Andy Grover M: andrew.grover@intel.com @@ -256,9 +272,9 @@ BTTV VIDEO4LINUX DRIVER P: Gerd Knorr -M: kraxel@goldbach.in-berlin.de +M: kraxel@bytesex.org L: video4linux-list@redhat.com -W: http://me.in-berlin.de/~kraxel/bttv.html +W: http://bytesex.org/bttv/ S: Maintained BUSLOGIC SCSI DRIVER @@ -290,11 +306,11 @@ S: Maintained COMPAQ FIBRE CHANNEL 64-bit/66MHz PCI non-intelligent HBA -P: Amy Vanzant-Hodge -M: Amy Vanzant-Hodge (fibrechannel@compaq.com) +P: Amy Vanzant-Hodge +M: Amy Vanzant-Hodge (fibrechannel@compaq.com) L: compaqandlinux@cpqlin.van-dijk.net W: ftp.compaq.com/pub/products/drivers/linux -S: Supported +S: Supported COMPAQ SMART2 RAID DRIVER P: Charles White @@ -311,12 +327,12 @@ S: Supported COMPUTONE INTELLIPORT MULTIPORT CARD -P: Michael H. Warfield -M: Michael H. Warfield -W: http://www.computone.com/ -W: http://www.wittsend.com/computone.html -L: linux-computone@lazuli.wittsend.com -S: Supported +P: Michael H. Warfield +M: Michael H. Warfield +W: http://www.computone.com/ +W: http://www.wittsend.com/computone.html +L: linux-computone@lazuli.wittsend.com +S: Supported COMX/MULTIGATE SYNC SERIAL DRIVERS P: Gergely Madarasz @@ -330,15 +346,6 @@ W: http://kbuild.sourceforge.net S: Maintained -CONFIGURE.HELP -P: Steven P. Cole -M: Steven P. Cole -P: Eric S. Raymond -M: Eric S. Raymond -L: kbuild-devel@lists.sourceforge.net -W: http://kbuild.sourceforge.net -S: Maintained - COSA/SRP SYNC SERIAL DRIVER P: Jan "Yenya" Kasprzak M: kas@fi.muni.cz @@ -428,9 +435,9 @@ DIGI INTL. EPCA DRIVER P: Chad Schwartz -M: support@dgii.com -L: digilnux@dgii.com -S: Maintained +M: support@dgii.com +L: digilnux@dgii.com +S: Maintained DIGI RIGHTSWITCH NETWORK DRIVER P: Rick Richardson @@ -452,12 +459,12 @@ S: Supported DISK GEOMETRY AND PARTITION HANDLING -P: Andries Brouwer -M: aeb@cwi.nl -W: http://www.win.tue.nl/~aeb/linux/Large-Disk.html -W: http://www.win.tue.nl/~aeb/linux/zip/zip-1.html -W: http://www.win.tue.nl/~aeb/partitions/partition_types-1.html -S: Maintained +P: Andries Brouwer +M: aeb@cwi.nl +W: http://www.win.tue.nl/~aeb/linux/Large-Disk.html +W: http://www.win.tue.nl/~aeb/linux/zip/zip-1.html +W: http://www.win.tue.nl/~aeb/partitions/partition_types-1.html +S: Maintained DISKQUOTA: P: Marco van Wieringen @@ -532,9 +539,9 @@ S: Maintained ETHERTEAM 16I DRIVER -P: Mika Kuoppala -M: miku@iki.fi -S: Maintained +P: Mika Kuoppala +M: miku@iki.fi +S: Maintained EXT2 FILE SYSTEM P: Remy Card @@ -575,7 +582,7 @@ FREEVXFS FILESYSTEM P: Christoph Hellwig -M: hch@caldera.de +M: hch@infradead.org W: ftp://ftp.openlinux.org/pub/people/hch/vxfs S: Maintained @@ -613,10 +620,10 @@ S: Maintained HFS FILESYSTEM -P: Adrian Sun -M: asun@cobaltnet.com -L: linux-kernel@vger.kernel.org -S: Maintained +P: Adrian Sun +M: asun@cobaltnet.com +L: linux-kernel@vger.kernel.org +S: Maintained HGA FRAMEBUFFER DRIVER P: Ferenc Bakonyi @@ -649,6 +656,11 @@ W: http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi S: Maintained +HPUSBSCSI +P: Oliver Neukum +M: drivers@neukum.org +S: Maintained + I2C DRIVERS P: Simon Vogl M: simon@tk.uni-linz.ac.at @@ -691,10 +703,10 @@ S: Maintained IBM ServeRAID RAID DRIVER -P: Keith Mitchell -M: ipslinux@us.ibm.com -W: http://www.developer.ibm.com/welcome/netfinity/serveraid.html -S: Supported +P: Keith Mitchell +M: ipslinux@us.ibm.com +W: http://www.developer.ibm.com/welcome/netfinity/serveraid.html +S: Supported IDE DRIVER [GENERAL] P: Andre Hedrick @@ -777,12 +789,19 @@ M: tigran@veritas.com S: Maintained +INTEL PRO/1000 GIGABIT ETHERNET SUPPORT +P: Chris Leech +M: christopher.leech@intel.com +P: Scott Feldman +M: scott.feldman@intel.com +S: Supported + INTERMEZZO FILE SYSTEM P: Peter J. Braam -M: braam@clusterfs.com -W: http://www.inter-mezzo.org/ -L: intermezzo-discuss@lists.sourceforge.net -S: Maintained +M: braam@clusterfs.com +W: http://www.inter-mezzo.org/ +L: intermezzo-discuss@lists.sourceforge.net +S: Maintained IP MASQUERADING: P: Juanjo Ciarlante @@ -796,11 +815,11 @@ S: Maintained IRDA SUBSYSTEM -P: Dag Brattli -M: Dag Brattli -L: linux-irda@pasta.cs.uit.no -W: http://irda.sourceforge.net/ -S: Maintained +P: Dag Brattli +M: Dag Brattli +L: linux-irda@pasta.cs.uit.no +W: http://irda.sourceforge.net/ +S: Maintained ISAPNP P: Jaroslav Kysela @@ -837,6 +856,13 @@ W: http://sources.redhat.com/jffs2/ S: Maintained +JFS FILESYSTEM +P: Dave Kleikamp +M: shaggy@austin.ibm.com +L: jfs-discussion@oss.software.ibm.com +W: http://oss.software.ibm.com/jfs/ +S: Supported + JOYSTICK DRIVER P: Vojtech Pavlik M: vojtech@suse.cz @@ -875,10 +901,10 @@ S: Maintained LANMEDIA WAN CARD DRIVER -P: Andrew Stanley-Jones -M: asj@lanmedia.com -W: http://www.lanmedia.com/ -S: Supported +P: Andrew Stanley-Jones +M: asj@lanmedia.com +W: http://www.lanmedia.com/ +S: Supported LAPB module P: Henner Eisen @@ -919,10 +945,10 @@ S: Maintained LOGICAL VOLUME MANAGER -P: Heinz Mauelshagen -L: linux-LVM@sistina.com -W: http://www.sistina.com/lvm -S: Maintained +P: Heinz Mauelshagen +L: linux-LVM@sistina.com +W: http://www.sistina.com/lvm +S: Maintained LSILOGIC/SYMBIOS/NCR 53C8XX and 53C1010 PCI-SCSI drivers P: Gerard Roudier @@ -969,10 +995,9 @@ S: Maintained MICROTEK X6 SCANNER -P: Oliver Neukum -M: drivers@neukum.org -W: http://fachschaft.cup.uni-muenchen.de/~neukum/scanner.html -S: Maintained +P: Oliver Neukum +M: drivers@neukum.org +S: Maintained MIPS P: Ralf Baechle @@ -1015,9 +1040,9 @@ S: Maintained NATSEMI ETHERNET DRIVER (DP8381x) -P: Tim Hockin -M: thockin@hockin.org -S: Maintained +P: Tim Hockin +M: thockin@hockin.org +S: Maintained NCP FILESYSTEM P: Petr Vandrovec @@ -1088,17 +1113,17 @@ S: Maintained NFS CLIENT -P: Trond Myklebust -M: trond.myklebust@fys.uio.no -L: linux-kernel@vger.kernel.org -S: Maintained +P: Trond Myklebust +M: trond.myklebust@fys.uio.no +L: linux-kernel@vger.kernel.org +S: Maintained NI5010 NETWORK DRIVER -P: Jan-Pascal van Best and Andreas Mohr -M: Jan-Pascal van Best -M: Andreas Mohr <100.30936@germany.net> -L: linux-net@vger.kernel.org -S: Maintained +P: Jan-Pascal van Best and Andreas Mohr +M: Jan-Pascal van Best +M: Andreas Mohr <100.30936@germany.net> +L: linux-net@vger.kernel.org +S: Maintained NINJA SCSI-3 / NINJA SCSI-32Bi PCMCIA SCSI HOST ADAPTER DRIVER P: YOKOTA Hiroshi @@ -1127,7 +1152,7 @@ OLYMPIC NETWORK DRIVER P: Peter De Shrijver -M: p2@ace.ulyssis.student.ac.be +M: p2@ace.ulyssis.student.kuleuven.ac.be P: Mike Phillips M: mikep@linuxtr.net L: linux-net@vger.kernel.org @@ -1170,9 +1195,9 @@ PERSONALITY HANDLING P: Christoph Hellwig -M: hch@caldera.de +M: hch@infradead.org L: linux-abi-devel@lists.sourceforge.net -S: Supported +S: Maintained PCI ID DATABASE P: Jens Maurer @@ -1261,6 +1286,12 @@ W: http://www.alarsen.net/linux/qnx4fs/ S: Maintained +RADEON FRAMEBUFFER DISPLAY DRIVER +P: Ani Joshi +M: ajoshi@shell.unixbox.com +L: linux-fbdev-devel@lists.sourceforge.net +S: Maintained + RAGE128 FRAMEBUFFER DISPLAY DRIVER P: Ani Joshi M: ajoshi@shell.unixbox.com @@ -1280,11 +1311,11 @@ S: Maintained REISERFS FILE SYSTEM -P: Hans Reiser -M: reiserfs-dev@namesys.com -L: reiserfs-list@namesys.com -W: http://www.namesys.com -S: Supported +P: Hans Reiser +M: reiserfs-dev@namesys.com +L: reiserfs-list@namesys.com +W: http://www.namesys.com +S: Supported ROSE NETWORK LAYER P: Jean-Paul Roubelat @@ -1470,7 +1501,7 @@ SYSV FILESYSTEM P: Christoph Hellwig -M: hch@caldera.de +M: hch@infradead.org S: Maintained TLAN NETWORK DRIVER @@ -1498,10 +1529,10 @@ S: Maintained TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE -P: Ollie Lho -M: ollie@sis.com.tw +P: Ollie Lho +M: ollie@sis.com.tw L: linux-kernel@vger.kernel.org -S: Supported +S: Supported TMS380 TOKEN-RING NETWORK DRIVER P: Adam Fritzler @@ -1575,6 +1606,12 @@ L: linux-usb-devel@lists.sourceforge.net S: Maintained +USB EHCI DRIVER +P: David Brownell +M: dbrownell@users.sourceforge.net +L: linux-usb-devel@lists.sourceforge.net +S: Maintained + USB HID/HIDBP/INPUT DRIVERS P: Vojtech Pavlik M: vojtech@suse.cz @@ -1613,12 +1650,12 @@ S: Maintained USB OV511 DRIVER -P: Mark McClelland -M: mmcclell@bigfoot.com -L: linux-usb-users@lists.sourceforge.net -L: linux-usb-devel@lists.sourceforge.net -W: http://alpha.dyndns.org/ov511/ -S: Maintained +P: Mark McClelland +M: mmcclell@bigfoot.com +L: linux-usb-users@lists.sourceforge.net +L: linux-usb-devel@lists.sourceforge.net +W: http://alpha.dyndns.org/ov511/ +S: Maintained USB PEGASUS DRIVER P: Petko Manolov @@ -1685,12 +1722,12 @@ S: Maintained USB SERIAL KEYSPAN DRIVER -P: Hugh Blemings -M: hugh@misc.nu +P: Greg Kroah-Hartman +M: greg@kroah.com L: linux-usb-users@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net +W: http://www.kroah.com/linux/ S: Maintained -W: http://misc.nu/hugh/keyspan/ USB SUBSYSTEM P: Greg Kroah-Hartman @@ -1708,6 +1745,12 @@ W: http://usb.in.tum.de S: Maintained +USB "USBNET" DRIVER +P: David Brownell +M: dbrownell@users.sourceforge.net +L: linux-usb-devel@lists.sourceforge.net +S: Maintained + VFAT FILESYSTEM: P: Gordon Chaffee M: chaffee@cs.berkeley.edu @@ -1728,17 +1771,17 @@ S: Maintained VIDEO FOR LINUX -P: Alan Cox -M: Alan.Cox@linux.org +P: Gerd Knorr +M: kraxel@bytesex.org W: http://roadrunner.swansea.linux.org.uk/v4l.shtml -S: Maintained for 2.2 only +S: Maintained WAN ROUTER & SANGOMA WANPIPE DRIVERS & API (X.25, FRAME RELAY, PPP, CISCO HDLC) -P: Nenad Corbic -M: ncorbic@sangoma.com -M: dm@sangoma.com -W: http://www.sangoma.com -S: Supported +P: Nenad Corbic +M: ncorbic@sangoma.com +M: dm@sangoma.com +W: http://www.sangoma.com +S: Supported WAVELAN NETWORK DRIVER & WIRELESS EXTENSIONS P: Jean Tourrilhes diff -Nru a/Makefile b/Makefile --- a/Makefile Thu Mar 7 18:17:38 2002 +++ b/Makefile Thu Mar 7 18:17:38 2002 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 5 -SUBLEVEL = 5 +SUBLEVEL = 6 EXTRAVERSION = KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -162,6 +162,7 @@ DRIVERS-$(CONFIG_PCMCIA) += drivers/pcmcia/pcmcia.o DRIVERS-$(CONFIG_NET_PCMCIA) += drivers/net/pcmcia/pcmcia_net.o DRIVERS-$(CONFIG_NET_WIRELESS) += drivers/net/wireless/wireless_net.o +DRIVERS-$(CONFIG_NET_TULIP) += drivers/net/tulip/tulip_net.o DRIVERS-$(CONFIG_PCMCIA_CHRDEV) += drivers/char/pcmcia/pcmcia_char.o DRIVERS-$(CONFIG_DIO) += drivers/dio/dio.a DRIVERS-$(CONFIG_SBUS) += drivers/sbus/sbus_all.o diff -Nru a/arch/alpha/Makefile b/arch/alpha/Makefile --- a/arch/alpha/Makefile Thu Mar 7 18:17:38 2002 +++ b/arch/alpha/Makefile Thu Mar 7 18:17:38 2002 @@ -25,6 +25,11 @@ have_mcpu_ev67 := $(shell if $(CC) -mcpu=ev67 -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo y; else echo n; fi) +have_msmall_data := $(shell if $(CC) -msmall-data -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo y; else echo n; fi) +ifeq ($(have_msmall_data),y) + CFLAGS := $(CFLAGS) -msmall-data +endif + # Turn on the proper cpu optimizations. ifeq ($(have_mcpu),y) # If GENERIC, make sure to turn off any instruction set extensions that @@ -40,20 +45,20 @@ CFLAGS := $(CFLAGS) -mcpu=pca56 mcpu_done := y endif - ifeq ($(mcpu_done)$(CONFIG_ALPHA_PYXIS),ny) - CFLAGS := $(CFLAGS) -mcpu=ev56 - mcpu_done := y - endif - ifeq ($(mcpu_done)$(CONFIG_ALPHA_POLARIS),ny) - ifeq ($(have_mcpu_pca56),y) - CFLAGS := $(CFLAGS) -mcpu=pca56 - else - CFLAGS := $(CFLAGS) -mcpu=ev56 - endif + ifeq ($(mcpu_done)$(CONFIG_ALPHA_POLARIS)$(have_mcpu_pca56),nyy) + CFLAGS := $(CFLAGS) -mcpu=pca56 mcpu_done := y endif ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV4),ny) CFLAGS := $(CFLAGS) -mcpu=ev4 + mcpu_done := y + endif + ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV56),ny) + CFLAGS := $(CFLAGS) -mcpu=ev56 + mcpu_done := y + endif + ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV5),ny) + CFLAGS := $(CFLAGS) -mcpu=ev5 mcpu_done := y endif ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV67)$(have_mcpu_ev67),nyy) diff -Nru a/arch/alpha/config.in b/arch/alpha/config.in --- a/arch/alpha/config.in Thu Mar 7 18:17:40 2002 +++ b/arch/alpha/config.in Thu Mar 7 18:17:40 2002 @@ -88,19 +88,43 @@ then define_bool CONFIG_ALPHA_EB64P y fi -if [ "$CONFIG_ALPHA_EB164" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \ - -o "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_TAKARA" = "y" ] +if [ "$CONFIG_ALPHA_ALCOR" = "y" ] then define_bool CONFIG_ALPHA_EV5 y define_bool CONFIG_ALPHA_CIA y + bool 'EV56 CPU (speed >= 366MHz)?' CONFIG_ALPHA_EV56 fi -if [ "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ] +if [ "$CONFIG_ALPHA_EB164" = "y" ] +then + define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_CIA y +fi +if [ "$CONFIG_ALPHA_PC164" = "y" -o "$CONFIG_ALPHA_TAKARA" = "y" ] +then + define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_EV56 y + define_bool CONFIG_ALPHA_CIA y +fi +if [ "$CONFIG_ALPHA_MIKASA" = "y" ] +then + bool 'EV5 CPU daughtercard (model 5/xxx)?' CONFIG_ALPHA_PRIMO + if [ "$CONFIG_ALPHA_PRIMO" = "y" ] + then + define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_CIA y + else + define_bool CONFIG_ALPHA_EV4 y + define_bool CONFIG_ALPHA_APECS y + fi +fi +if [ "$CONFIG_ALPHA_NORITAKE" = "y" ] then bool 'EV5 CPU daughtercard (model 5/xxx)?' CONFIG_ALPHA_PRIMO if [ "$CONFIG_ALPHA_PRIMO" = "y" ] then define_bool CONFIG_ALPHA_EV5 y define_bool CONFIG_ALPHA_CIA y + bool 'EV56 CPU (speed >= 333MHz)?' CONFIG_ALPHA_EV56 else define_bool CONFIG_ALPHA_EV4 y define_bool CONFIG_ALPHA_APECS y @@ -121,6 +145,7 @@ -o "$CONFIG_ALPHA_SX164" = "y" -o "$CONFIG_ALPHA_RUFFIAN" = "y" ] then define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_EV56 y define_bool CONFIG_ALPHA_CIA y define_bool CONFIG_ALPHA_PYXIS y fi @@ -145,10 +170,12 @@ then define_bool CONFIG_ALPHA_EV5 y define_bool CONFIG_ALPHA_MCPCIA y + bool 'EV56 CPU (speed >= 400MHz)?' CONFIG_ALPHA_EV56 fi if [ "$CONFIG_ALPHA_RX164" = "y" ] then define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_EV56 y define_bool CONFIG_ALPHA_POLARIS y fi if [ "$CONFIG_ALPHA_JENSEN" = "y" ] diff -Nru a/arch/alpha/defconfig b/arch/alpha/defconfig --- a/arch/alpha/defconfig Thu Mar 7 18:17:44 2002 +++ b/arch/alpha/defconfig Thu Mar 7 18:17:44 2002 @@ -237,7 +237,6 @@ # CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set # CONFIG_BLK_DEV_IDESCSI is not set -# CONFIG_IDE_TASK_IOCTL is not set # # IDE chipset support/bugfixes @@ -250,7 +249,6 @@ # CONFIG_IDEPCI_SHARE_IRQ is not set CONFIG_BLK_DEV_IDEDMA_PCI=y # CONFIG_BLK_DEV_OFFBOARD is not set -# CONFIG_BLK_DEV_IDEDMA_FORCED is not set CONFIG_IDEDMA_PCI_AUTO=y # CONFIG_IDEDMA_ONLYDISK is not set CONFIG_BLK_DEV_IDEDMA=y diff -Nru a/arch/alpha/kernel/core_irongate.c b/arch/alpha/kernel/core_irongate.c --- a/arch/alpha/kernel/core_irongate.c Thu Mar 7 18:17:39 2002 +++ b/arch/alpha/kernel/core_irongate.c Thu Mar 7 18:17:39 2002 @@ -455,7 +455,7 @@ if (address >= end) BUG(); do { - pte_t * pte = pte_alloc(&init_mm, pmd, address); + pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); if (!pte) return -ENOMEM; irongate_remap_area_pte(pte, address, end - address, diff -Nru a/arch/alpha/kernel/core_titan.c b/arch/alpha/kernel/core_titan.c --- a/arch/alpha/kernel/core_titan.c Thu Mar 7 18:17:41 2002 +++ b/arch/alpha/kernel/core_titan.c Thu Mar 7 18:17:41 2002 @@ -279,7 +279,6 @@ titan_init_one_pachip_port(titan_pachip_port *port, int index) { struct pci_controller *hose; - unsigned long sg_size; hose = alloc_pci_controller(); if (index == 0) diff -Nru a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S --- a/arch/alpha/kernel/entry.S Thu Mar 7 18:17:36 2002 +++ b/arch/alpha/kernel/entry.S Thu Mar 7 18:17:36 2002 @@ -489,22 +489,22 @@ .prologue 0 bsr $1,do_switch_stack call_pal PAL_swpctx - unop - bsr $1,undo_switch_stack lda $8,0x3fff - mov $17,$0 + bsr $1,undo_switch_stack bic $30,$8,$8 ret $31,($26),1 .end alpha_switch_to +#if CONFIG_SMP || CONFIG_PREEMPT .globl ret_from_fork .align 3 .ent ret_from_fork ret_from_fork: lda $26,ret_from_sys_call - mov $0,$16 + mov $17,$16 jmp $31,schedule_tail .end ret_from_fork +#endif /* * Oh, well.. Disassembling OSF/1 binaries to find out how the diff -Nru a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c --- a/arch/alpha/kernel/process.c Thu Mar 7 18:17:41 2002 +++ b/arch/alpha/kernel/process.c Thu Mar 7 18:17:41 2002 @@ -64,7 +64,6 @@ while (!need_resched()) barrier(); schedule(); - check_pgt_cache(); } } @@ -283,6 +282,7 @@ unsigned long unused, struct task_struct * p, struct pt_regs * regs) { + extern void ret_from_sys_call(void); extern void ret_from_fork(void); struct thread_info *childti = p->thread_info; @@ -304,7 +304,11 @@ stack = ((struct switch_stack *) regs) - 1; childstack = ((struct switch_stack *) childregs) - 1; *childstack = *stack; +#ifdef CONFIG_SMP childstack->r26 = (unsigned long) ret_from_fork; +#else + childstack->r26 = (unsigned long) ret_from_sys_call; +#endif childti->pcb.usp = usp; childti->pcb.ksp = (unsigned long) childstack; childti->pcb.flags = 1; /* set FEN, clear everything else */ diff -Nru a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h --- a/arch/alpha/kernel/proto.h Thu Mar 7 18:17:40 2002 +++ b/arch/alpha/kernel/proto.h Thu Mar 7 18:17:40 2002 @@ -162,9 +162,9 @@ unsigned char extra; } __mcheck_info; -#define mcheck_expected(cpu) (__mcheck_info.expected) -#define mcheck_taken(cpu) (__mcheck_info.taken) -#define mcheck_extra(cpu) (__mcheck_info.extra) +#define mcheck_expected(cpu) ((void)(cpu), __mcheck_info.expected) +#define mcheck_taken(cpu) ((void)(cpu), __mcheck_info.taken) +#define mcheck_extra(cpu) ((void)(cpu), __mcheck_info.extra) #endif #define DEBUG_MCHECK 0 /* 0 = minimal, 1 = debug, 2 = debug+dump. */ diff -Nru a/arch/alpha/kernel/sys_titan.c b/arch/alpha/kernel/sys_titan.c --- a/arch/alpha/kernel/sys_titan.c Thu Mar 7 18:17:46 2002 +++ b/arch/alpha/kernel/sys_titan.c Thu Mar 7 18:17:46 2002 @@ -84,8 +84,8 @@ *dim3; #else volatile unsigned long *dimB; - if (bcpu == 0) dimB = &cchip->dim0.csr; - else if (bcpu == 1) dimB = &cchip->dim1.csr; + dimB = &cchip->dim0.csr; + if (bcpu == 1) dimB = &cchip->dim1.csr; else if (bcpu == 2) dimB = &cchip->dim2.csr; else if (bcpu == 3) dimB = &cchip->dim3.csr; @@ -190,9 +190,6 @@ static void __init privateer_init_irq(void) { - extern asmlinkage void entInt(void); - int cpu; - outb(0, DMA1_RESET_REG); outb(0, DMA2_RESET_REG); outb(DMA_MODE_CASCADE, DMA2_MODE_REG); diff -Nru a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c --- a/arch/alpha/mm/init.c Thu Mar 7 18:17:42 2002 +++ b/arch/alpha/mm/init.c Thu Mar 7 18:17:42 2002 @@ -42,12 +42,8 @@ static struct pcb_struct original_pcb; -#ifndef CONFIG_SMP -struct pgtable_cache_struct quicklists; -#endif - pgd_t * -get_pgd_slow(void) +pgd_alloc(struct mm_struct *mm) { pgd_t *ret, *init; @@ -69,28 +65,26 @@ return ret; } -int do_check_pgt_cache(int low, int high) +pte_t * +pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) { - 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); + pte_t *pte; + long timeout = 10; + + retry: + pte = (pte_t *) __get_free_page(GFP_KERNEL); + if (pte) + clear_page(pte); + else if (--timeout >= 0) { + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ); + goto retry; } - return freed; + + return pte; } + /* * BAD_PAGE is the page that is used for page faults when linux * is out-of-memory. Older versions of linux just did a @@ -145,7 +139,6 @@ printk("%ld reserved pages\n",reserved); printk("%ld pages shared\n",shared); printk("%ld pages swap cached\n",cached); - printk("%ld pages in page table cache\n",pgtable_cache_size); show_buffers(); } #endif @@ -260,7 +253,7 @@ unsigned long paddr = crb->map[i].pa; crb->map[i].va = vaddr; for (j = 0; j < crb->map[i].count; ++j) { - set_pte(pte_offset(pmd, vaddr), + set_pte(pte_offset_kernel(pmd, vaddr), mk_pte_phys(paddr, PAGE_KERNEL)); paddr += PAGE_SIZE; vaddr += PAGE_SIZE; diff -Nru a/arch/arm/Config.help b/arch/arm/Config.help --- a/arch/arm/Config.help Thu Mar 7 18:17:37 2002 +++ b/arch/arm/Config.help Thu Mar 7 18:17:37 2002 @@ -116,6 +116,16 @@ information about which PCI hardware does work under Linux and which doesn't. +CONFIG_PREEMPT + This option reduces the latency of the kernel when reacting to + real-time or interactive events by allowing a low priority process to + be preempted even if it is in kernel mode executing a system call. + This allows applications to run more reliably even when the system is + under load. + + Say Y here if you are building a kernel for a desktop, embedded + or real-time system. Say N if you are unsure. + CONFIG_MCA MicroChannel Architecture is found in some IBM PS/2 machines and laptops. It is a bus system similar to PCI or ISA. See @@ -521,6 +531,10 @@ Saying N will reduce the size of the Footbridge kernel. +CONFIG_ARCH_IQ80310 + Say Y here if you want to run your kernel on the Intel IQ80310 + evaluation kit for the IOP310 chipset. + CONFIG_ARCH_L7200 Say Y here if you intend to run this kernel on a LinkUp Systems L7200 Software Development Board which uses an ARM720T processor. @@ -558,6 +572,13 @@ If you have any questions or comments about the Compaq Personal Server, send e-mail to skiff@crl.dec.com. +CONFIG_PLD_HOTSWAP + This enables support for the dynamic loading and configuration of + compatible drivers when the contents of the PLD are changed. This + is still experimental and requires configuration tools which are + not yet generally available. Say N here. You must enable the kernel + module loader for this feature to work. + CONFIG_SA1100_ASSABET Say Y here if you are using the Intel(R) StrongARM(R) SA-1110 Microprocessor Development Board (also known as the Assabet). @@ -567,28 +588,14 @@ Microprocessor Development Board (Assabet) with the SA-1111 Development Board (Nepon). -CONFIG_SA1100_H3600 - Say Y here if you intend to run this kernel on the Compaq iPAQ - H3600 handheld computer. Information about this machine and the - Linux port to this machine can be found at: - - - +CONFIG_SA1100_BADGE4 + Say Y here if you want to build a kernel for the HP Laboratories + BadgePAD 4. CONFIG_SA1100_BRUTUS Say Y here if you are using the Intel(R) StrongARM(R) SA-1100 Microprocessor Development Board (also known as the Brutus). -CONFIG_SA1100_LART - Say Y here if you are using the Linux Advanced Radio Terminal - (also known as the LART). See for - information on the LART. - -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 - for information on this system. - CONFIG_SA1100_CERF The Intrinsyc CerfBoard is based on the StrongARM 1110. More information is available at: @@ -602,6 +609,24 @@ handheld instruments. Information about this machine can be found at: . +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 + for information on this system. + +CONFIG_SA1100_H3600 + Say Y here if you intend to run this kernel on the Compaq iPAQ + H3600 handheld computer. Information about this machine and the + Linux port to this machine can be found at: + + + + +CONFIG_SA1100_LART + Say Y here if you are using the Linux Advanced Radio Terminal + (also known as the LART). See for + information on the LART. + CONFIG_SA1100_NANOENGINE The nanoEngine is a StrongARM 1110-based single board computer from Bright Star Engineering. More information is available at: @@ -619,6 +644,23 @@ Say Y if configuring for a Pangolin. Say N otherwise. +CONFIG_SA1100_PFS168 + The Radisys Corp. PFS-168 (aka Tulsa) is an Intel® StrongArm® SA-1110 based + computer which includes the SA-1111 Microprocessor Companion Chip and other + custom I/O designed to add connectivity and multimedia features for vending + and business machine applications. Say Y here if you require support for + this target. + +CONFIG_SA1100_SHANNON + The Shannon (also known as a Tuxscreen, and also as a IS2630) was a + limited edition webphone produced by Philips. The Shannon is a SA1100 + platform with a 640x480 LCD, touchscreen, CIR keyboard, PCMCIA slots, + and a telco interface. + +CONFIG_SA1100_STORK + Say Y here if you intend to run this kernel on the Stork + handheld computer. + 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 @@ -656,6 +698,31 @@ Say Y if you want support for the ARM920T processor. Otherwise, say N. +CONFIG_CPU_ARM922T + The ARM922T is a version of the ARM920T, but with smaller + instruction and data caches. It is used in Altera's + Excalibur XA device family. + + Say Y if you want support for the ARM922T processor. + Otherwise, say N. + +CONFIG_CPU_ARM922_CPU_IDLE + Saying Y here will allow the processor to enter a low power + mode whilst waiting for an interrupt in idle. If you're unsure + say Y. + +CONFIG_CPU_ARM922_I_CACHE_ON + Say Y here to enable the processor instruction cache. Unless + you have a reason not to, say Y. + +CONFIG_CPU_ARM922_D_CACHE_ON + Say Y here to enable the processor data cache. Unless + you have a reason not to, say Y. + +CONFIG_CPU_ARM922_WRITETHROUGH + Say Y here to use the data cache in writethough mode. Unless you + specifically require this, say N. + CONFIG_CPU_ARM1020 The ARM1020 is the cached version of the ARM10 processor, with an addition of a floating-point unit. @@ -688,16 +755,14 @@ CONFIG_FPE_FASTFPE Say Y here to include the FAST floating point emulator in the kernel. - This is an experimental much faster emulator which has only 32 bit + This is an experimental much faster emulator which now also has full precision for the mantissa. It does not support any exceptions. - This makes it very simple, it is approximately 4-8 times faster than - NWFPE. + It is very simple, and approximately 3-6 times faster than NWFPE. - It should be sufficient for most programs. It is definitely not - suitable if you do scientific calculations that need double - precision for iteration formulas that sum up lots of very small - numbers. If you do not feel you need a faster FP emulation you - should better choose NWFPE. + It should be sufficient for most programs. It may be not suitable + for scientific calculations, but you have to check this for yourself. + If you do not feel you need a faster FP emulation you should better + choose NWFPE. It is also possible to say M to build the emulator as a module (fastfpe.o). But keep in mind that you should only load the FP diff -Nru a/arch/arm/Makefile b/arch/arm/Makefile --- a/arch/arm/Makefile Thu Mar 7 18:17:42 2002 +++ b/arch/arm/Makefile Thu Mar 7 18:17:42 2002 @@ -134,6 +134,10 @@ MACHINE = clps711x endif +ifeq ($(CONFIG_ARCH_FORTUNET),y) +TEXTADDR = 0xc0008000 +endif + ifeq ($(CONFIG_ARCH_ANAKIN),y) MACHINE = anakin endif @@ -215,6 +219,7 @@ arch/arm/vmlinux.lds MRPROPER_FILES += \ + arch/arm/tools/constants.h* \ include/asm-arm/arch \ include/asm-arm/proc \ include/asm-arm/constants.h* \ diff -Nru a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile --- a/arch/arm/boot/Makefile Thu Mar 7 18:17:38 2002 +++ b/arch/arm/boot/Makefile Thu Mar 7 18:17:38 2002 @@ -54,6 +54,10 @@ INITRD_VIRT = 0xc0800000 endif +ifeq ($(CONFIG_ARCH_CAMELOT),y) +ZTEXTADDR = 0x00008000 +endif + ifeq ($(CONFIG_ARCH_NEXUSPCI),y) ZTEXTADDR = 0x40008000 endif diff -Nru a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile --- a/arch/arm/boot/compressed/Makefile Thu Mar 7 18:17:39 2002 +++ b/arch/arm/boot/compressed/Makefile Thu Mar 7 18:17:39 2002 @@ -33,6 +33,10 @@ OBJS += head-integrator.o endif +ifeq ($(CONFIG_ARCH_CAMELOT),y) +OBJS += head-epxa10db.o +endif + ifeq ($(CONFIG_ARCH_FTVPCI),y) OBJS += head-ftvpci.o endif diff -Nru a/arch/arm/boot/compressed/head-epxa10db.S b/arch/arm/boot/compressed/head-epxa10db.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/boot/compressed/head-epxa10db.S Thu Mar 7 18:17:47 2002 @@ -0,0 +1,5 @@ +#include +#include + + .section ".start", #alloc, #execinstr + mov r7, #MACH_TYPE_CAMELOT diff -Nru a/arch/arm/boot/compressed/misc.c b/arch/arm/boot/compressed/misc.c --- a/arch/arm/boot/compressed/misc.c Thu Mar 7 18:17:39 2002 +++ b/arch/arm/boot/compressed/misc.c Thu Mar 7 18:17:39 2002 @@ -18,6 +18,8 @@ unsigned int __machine_arch_type; +#include + #include #include #include diff -Nru a/arch/arm/boot/compressed/ofw-shark.c b/arch/arm/boot/compressed/ofw-shark.c --- a/arch/arm/boot/compressed/ofw-shark.c Thu Mar 7 18:17:44 2002 +++ b/arch/arm/boot/compressed/ofw-shark.c Thu Mar 7 18:17:44 2002 @@ -19,7 +19,7 @@ asmlinkage void create_params (unsigned long *buffer) { - /* Is there a better address? Also change in mach-shark/arch.c */ + /* Is there a better address? Also change in mach-shark/core.c */ struct tag *tag = (struct tag *) 0x08003000; int j,i,m,k,nr_banks,size; unsigned char *c; diff -Nru a/arch/arm/config.in b/arch/arm/config.in --- a/arch/arm/config.in Thu Mar 7 18:17:39 2002 +++ b/arch/arm/config.in Thu Mar 7 18:17:39 2002 @@ -91,6 +91,7 @@ dep_bool ' FreeBird-v1.1' CONFIG_SA1100_FREEBIRD $CONFIG_ARCH_SA1100 dep_bool ' GraphicsClient Plus' CONFIG_SA1100_GRAPHICSCLIENT $CONFIG_ARCH_SA1100 dep_bool ' GraphicsMaster' CONFIG_SA1100_GRAPHICSMASTER $CONFIG_ARCH_SA1100 +dep_bool ' HP Labs BadgePAD 4' CONFIG_SA1100_BADGE4 $CONFIG_ARCH_SA1100 dep_bool ' HP Jornada 720' CONFIG_SA1100_JORNADA720 $CONFIG_ARCH_SA1100 dep_bool ' HuW WebPanel' CONFIG_SA1100_HUW_WEBPANEL $CONFIG_ARCH_SA1100 dep_bool ' Itsy' CONFIG_SA1100_ITSY $CONFIG_ARCH_SA1100 @@ -107,6 +108,7 @@ dep_bool ' Victor' CONFIG_SA1100_VICTOR $CONFIG_ARCH_SA1100 dep_bool ' XP860' CONFIG_SA1100_XP860 $CONFIG_ARCH_SA1100 dep_bool ' Yopy' CONFIG_SA1100_YOPY $CONFIG_ARCH_SA1100 +dep_bool ' Stork' CONFIG_SA1100_STORK $CONFIG_ARCH_SA1100 # Determine if SA1111 support is required if [ "$CONFIG_ASSABET_NEPONSET" = "y" -o \ @@ -115,7 +117,8 @@ "$CONFIG_SA1100_XP860" = "y" -o \ "$CONFIG_SA1100_GRAPHICSMASTER" = "y" -o \ "$CONFIG_SA1100_PT_SYSTEM3" = "y" -o \ - "$CONFIG_SA1100_ADSBITSY" = "y" ]; then + "$CONFIG_SA1100_ADSBITSY" = "y" -o \ + "$CONFIG_SA1100_BADGE4" = "y" ]; then define_bool CONFIG_SA1111 y define_int CONFIG_FORCE_MAX_ZONEORDER 9 fi @@ -134,6 +137,7 @@ dep_bool ' CLEP7312' CONFIG_ARCH_CLEP7312 $CONFIG_ARCH_CLPS711X dep_bool ' EDB7211' CONFIG_ARCH_EDB7211 $CONFIG_ARCH_CLPS711X dep_bool ' P720T' CONFIG_ARCH_P720T $CONFIG_ARCH_CLPS711X +dep_bool ' FORTUNET' CONFIG_ARCH_FORTUNET $CONFIG_ARCH_CLPS711X # XXX Maybe these should indicate register compatibility # instead of being mutually exclusive. @@ -464,6 +468,7 @@ tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC bool 'Power Management support' CONFIG_PM +dep_bool 'Preemptible Kernel (experimental)' CONFIG_PREEMPT $CONFIG_CPU_32 $CONFIG_EXPERIMENTAL dep_tristate 'Advanced Power Management Emulation' CONFIG_APM $CONFIG_PM dep_tristate 'RISC OS personality' CONFIG_ARTHUR $CONFIG_CPU_32 @@ -677,7 +682,6 @@ bool 'Compile kernel without frame pointer' CONFIG_NO_FRAME_POINTER bool 'Verbose user fault messages' CONFIG_DEBUG_USER bool 'Include debugging information in kernel binary' CONFIG_DEBUG_INFO -dep_bool 'Disable pgtable cache' CONFIG_NO_PGT_CACHE $CONFIG_CPU_26 bool 'Kernel debugging' CONFIG_DEBUG_KERNEL dep_bool ' Debug memory allocations' CONFIG_DEBUG_SLAB $CONFIG_DEBUG_KERNEL diff -Nru a/arch/arm/def-configs/badge4 b/arch/arm/def-configs/badge4 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/def-configs/badge4 Thu Mar 7 18:17:47 2002 @@ -0,0 +1,923 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_ARM=y +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +# CONFIG_GENERIC_BUST_SPINLOCK is not set +# CONFIG_GENERIC_ISA_DMA is not set + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_OBSOLETE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# System Type +# +# CONFIG_ARCH_ANAKIN is not set +# CONFIG_ARCH_ARCA5K is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_RPC is not set +CONFIG_ARCH_SA1100=y +# CONFIG_ARCH_SHARK is not set + +# +# Archimedes/A5000 Implementations +# + +# +# Archimedes/A5000 Implementations (select only ONE) +# + +# +# Footbridge Implementations +# + +# +# SA11x0 Implementations +# +# CONFIG_SA1100_ASSABET is not set +# CONFIG_SA1100_ADSBITSY is not set +# CONFIG_SA1100_BRUTUS is not set +# CONFIG_SA1100_CERF is not set +# CONFIG_SA1100_H3100 is not set +# CONFIG_SA1100_H3600 is not set +# CONFIG_SA1100_H3800 is not set +# CONFIG_SA1100_H3XXX is not set +# CONFIG_SA1100_EXTENEX1 is not set +# CONFIG_SA1100_FLEXANET is not set +# CONFIG_SA1100_FREEBIRD is not set +# CONFIG_SA1100_GRAPHICSCLIENT is not set +# CONFIG_SA1100_GRAPHICSMASTER is not set +CONFIG_SA1100_BADGE4=y +# CONFIG_SA1100_JORNADA720 is not set +# CONFIG_SA1100_HUW_WEBPANEL is not set +# CONFIG_SA1100_ITSY is not set +# CONFIG_SA1100_LART is not set +# CONFIG_SA1100_NANOENGINE is not set +# CONFIG_SA1100_OMNIMETER is not set +# CONFIG_SA1100_PANGOLIN is not set +# CONFIG_SA1100_PLEB is not set +# CONFIG_SA1100_SHANNON is not set +# CONFIG_SA1100_SHERMAN is not set +# CONFIG_SA1100_SIMPAD is not set +# CONFIG_SA1100_PFS168 is not set +# CONFIG_SA1100_VICTOR is not set +# CONFIG_SA1100_XP860 is not set +# CONFIG_SA1100_YOPY is not set +CONFIG_SA1111=y +CONFIG_FORCE_MAX_ZONEORDER=9 +CONFIG_SA1100_USB=m +CONFIG_SA1100_USB_NETLINK=m +CONFIG_SA1100_USB_CHAR=m + +# +# CLPS711X/EP721X Implementations +# +# CONFIG_ARCH_EP7211 is not set +# CONFIG_ARCH_EP7212 is not set +# CONFIG_ARCH_ACORN is not set +# CONFIG_FOOTBRIDGE is not set +# CONFIG_FOOTBRIDGE_HOST is not set +# CONFIG_FOOTBRIDGE_ADDIN is not set +CONFIG_CPU_32=y +# CONFIG_CPU_26 is not set + +# +# Processor Type +# +# CONFIG_CPU_32v3 is not set +CONFIG_CPU_32v4=y +# CONFIG_CPU_ARM610 is not set +# CONFIG_CPU_ARM710 is not set +# CONFIG_CPU_ARM720T is not set +# CONFIG_CPU_ARM920T is not set +# CONFIG_CPU_ARM922T is not set +# CONFIG_CPU_ARM926T is not set +# CONFIG_CPU_ARM1020 is not set +# CONFIG_CPU_SA110 is not set +CONFIG_CPU_SA1100=y +# CONFIG_ARM_THUMB is not set +CONFIG_DISCONTIGMEM=y + +# +# General setup +# +# CONFIG_PCI is not set +CONFIG_ISA=y +# CONFIG_ISA_DMA is not set +CONFIG_CPU_FREQ=y +CONFIG_HOTPLUG=y + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +CONFIG_PCMCIA_PROBE=y +# CONFIG_I82365 is not set +# CONFIG_TCIC is not set +CONFIG_PCMCIA_SA1100=y +CONFIG_NET=y +# CONFIG_SYSVIPC is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y + +# +# At least one math emulation must be selected +# +CONFIG_FPE_NWFPE=y +CONFIG_FPE_FASTFPE=m +CONFIG_KCORE_ELF=y +# CONFIG_KCORE_AOUT is not set +CONFIG_BINFMT_AOUT=m +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=m +# CONFIG_PM is not set +CONFIG_ARTHUR=m +CONFIG_CMDLINE="init=/linuxrc root=/dev/mtdblock3" +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +CONFIG_MTD_DEBUG=y +CONFIG_MTD_DEBUG_VERBOSE=1 +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_BOOTLDR_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_NOSWAP=y +# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set +# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set +CONFIG_MTD_CFI_GEOMETRY=y +# CONFIG_MTD_CFI_B1 is not set +CONFIG_MTD_CFI_B2=y +# CONFIG_MTD_CFI_B4 is not set +CONFIG_MTD_CFI_I1=y +# CONFIG_MTD_CFI_I2 is not set +# CONFIG_MTD_CFI_I4 is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +CONFIG_MTD_RAM=y +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_NORA is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +CONFIG_MTD_SA1100=y +# CONFIG_MTD_IQ80310 is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +CONFIG_MTD_MTDRAM=m +CONFIG_MTDRAM_TOTAL_SIZE=4096 +CONFIG_MTDRAM_ERASE_SIZE=128 +CONFIG_MTD_BLKMTD=m + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC1000 is not set +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOCPROBE is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV 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_ARPD 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_VLAN_8021Q 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 + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +# CONFIG_NET_ETHERNET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC_OMIT_TIGON_I is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +CONFIG_NET_RADIO=y +# CONFIG_STRIP is not set +# CONFIG_WAVELAN is not set +# CONFIG_ARLAN is not set +# CONFIG_AIRONET4500 is not set +# CONFIG_AIRO is not set +CONFIG_HERMES=y + +# +# Wireless Pcmcia cards support +# +CONFIG_PCMCIA_HERMES=y +CONFIG_AIRO_CS=m +CONFIG_NET_WIRELESS=y + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# PCMCIA network device support +# +CONFIG_NET_PCMCIA=y +CONFIG_PCMCIA_3C589=y +CONFIG_PCMCIA_3C574=m +CONFIG_PCMCIA_FMVJ18X=m +CONFIG_PCMCIA_PCNET=y +CONFIG_PCMCIA_AXNET=m +CONFIG_PCMCIA_NMCLAN=m +CONFIG_PCMCIA_SMC91C92=m +CONFIG_PCMCIA_XIRC2PS=m +CONFIG_NET_PCMCIA_RADIO=y +CONFIG_PCMCIA_RAYCS=m +CONFIG_PCMCIA_NETWAVE=m +CONFIG_PCMCIA_WAVELAN=m + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +CONFIG_IRDA=y + +# +# IrDA protocols +# +CONFIG_IRLAN=y +CONFIG_IRCOMM=y +CONFIG_IRDA_ULTRA=y +# CONFIG_IRDA_OPTIONS is not set + +# +# Infrared-port device drivers +# + +# +# SIR device drivers +# +# CONFIG_IRTTY_SIR is not set +# CONFIG_IRPORT_SIR is not set + +# +# Dongle support +# +# CONFIG_DONGLE is not set + +# +# FIR device drivers +# +# CONFIG_USB_IRDA is not set +# CONFIG_NSC_FIR is not set +# CONFIG_WINBOND_FIR is not set +# CONFIG_TOSHIBA_FIR is not set +# CONFIG_SMC_IRCC_FIR is not set +# CONFIG_ALI_FIR is not set +# CONFIG_VLSI_FIR is not set +CONFIG_SA1100_FIR=y + +# +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=m + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=m + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=m +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_COMMERIAL is not set +# CONFIG_BLK_DEV_IDECS is not set +CONFIG_BLK_DEV_IDECD=m +CONFIG_BLK_DEV_IDETAPE=m +CONFIG_BLK_DEV_IDEFLOPPY=m +CONFIG_BLK_DEV_IDESCSI=m + +# +# IDE chipset support/bugfixes +# +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_DMA_NONPCI is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_ATARAID is not set + +# +# SCSI support +# +CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 +CONFIG_CHR_DEV_ST=m +CONFIG_CHR_DEV_OSST=m +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_SR_EXTRA_DEVS=2 +CONFIG_CHR_DEV_SG=y + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_DEBUG_QUEUES is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_MEGARAID is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx_sync is not set +# CONFIG_SCSI_NCR53C7xx_FAST is not set +# CONFIG_SCSI_NCR53C7xx_DISCONNECT is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_SIM710 is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +# CONFIG_SCSI_PCMCIA is not set + +# +# I2O device support +# +CONFIG_I2O=m +CONFIG_I2O_BLOCK=m +CONFIG_I2O_LAN=m +CONFIG_I2O_SCSI=m +CONFIG_I2O_PROC=m + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input core support +# +CONFIG_INPUT=m +CONFIG_INPUT_KEYBDEV=m +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_SA1100=y +CONFIG_SERIAL_SA1100_CONSOLE=y +CONFIG_SA1100_DEFAULT_BAUDRATE=115200 +# CONFIG_SERIAL_8250 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +CONFIG_I2C=m +CONFIG_I2C_ALGOBIT=m +CONFIG_I2C_ELV=m +CONFIG_I2C_VELLEMAN=m +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ELEKTOR=m +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_PROC=m + +# +# L3 serial bus support +# +CONFIG_L3=m + +# +# Other L3 adapters +# +# CONFIG_BIT_SA1100_GPIO is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_INPUT_SERIO is not set + +# +# Joysticks +# +# CONFIG_INPUT_IFORCE_USB is not set +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set +CONFIG_SOFT_WATCHDOG=m +# CONFIG_WDT is not set +# CONFIG_WDTPCI is not set +# CONFIG_PCWATCHDOG is not set +# CONFIG_ACQUIRE_WDT is not set +# CONFIG_ADVANTECH_WDT is not set +CONFIG_SA1100_WATCHDOG=m +# CONFIG_EUROTECH_WDT is not set +# CONFIG_IB700_WDT is not set +# CONFIG_I810_TCO is not set +# CONFIG_MIXCOMWD is not set +# CONFIG_60XX_WDT is not set +# CONFIG_W83877F_WDT is not set +# CONFIG_MACHZ_WDT is not set +# CONFIG_NVRAM is not set +CONFIG_RTC=m +CONFIG_SA1100_RTC=m +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS 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_EXT3_FS=m +CONFIG_JBD=m +# CONFIG_JBD_DEBUG is not set +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +# CONFIG_UMSDOS_FS is not set +CONFIG_VFAT_FS=m +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_CRAMFS=m +# CONFIG_TMPFS is not set +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +CONFIG_MINIX_FS=m +# CONFIG_VXFS_FS is not set +# 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 is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=m +# 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_INTERMEZZO_FS is not set +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +# CONFIG_NFSD is not set +CONFIG_SUNRPC=m +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_SMB_FS=m +# CONFIG_SMB_NLS_DEFAULT is not set +# CONFIG_NCP_FS is not set +# CONFIG_ZISOFS_FS is not set +CONFIG_ZLIB_FS_INFLATE=m + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_SMB_NLS=y +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_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1251 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_9 is not set +# CONFIG_NLS_ISO8859_13 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_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Sound +# +CONFIG_SOUND=y +# CONFIG_SOUND_BT878 is not set +# CONFIG_SOUND_FUSION is not set +# CONFIG_SOUND_CS4281 is not set +# CONFIG_SOUND_ESSSOLO1 is not set +# CONFIG_SOUND_MAESTRO is not set +# CONFIG_SOUND_SONICVIBES is not set +# CONFIG_SOUND_TRIDENT is not set +# CONFIG_SOUND_MSNDCLAS is not set +# CONFIG_SOUND_MSNDPIN is not set +CONFIG_SOUND_SA1100=y +CONFIG_SOUND_UDA1341=m +CONFIG_SOUND_SA1111_UDA1341=m +CONFIG_SOUND_SA1100SSP=m +# CONFIG_SOUND_OSS is not set +# CONFIG_SOUND_TVMIXER is not set + +# +# Multimedia Capabilities Port drivers +# +# CONFIG_MCP is not set + +# +# USB support +# +CONFIG_USB=y +CONFIG_USB_DEBUG=y + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_LONG_TIMEOUT is not set + +# +# USB Controllers +# +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set +CONFIG_USB_OHCI_SA1111=y + +# +# USB Device Class drivers +# +CONFIG_USB_AUDIO=y +CONFIG_USB_BLUETOOTH=m +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DEBUG=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_DPCM=y +CONFIG_USB_STORAGE_HP8200e=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=m +# CONFIG_USB_HIDDEV is not set +CONFIG_USB_KBD=m +CONFIG_USB_MOUSE=m +CONFIG_USB_WACOM=m + +# +# USB Imaging devices +# +CONFIG_USB_DC2XX=m +CONFIG_USB_MDC800=m +CONFIG_USB_SCANNER=m +CONFIG_USB_MICROTEK=m +CONFIG_USB_HPUSBSCSI=m + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +CONFIG_USB_PEGASUS=m +CONFIG_USB_KAWETH=m +CONFIG_USB_CATC=m +CONFIG_USB_CDCETHER=m +CONFIG_USB_USBNET=m + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=m +# CONFIG_USB_SERIAL_GENERIC is not set +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_OMNINET=m + +# +# USB Miscellaneous drivers +# +CONFIG_USB_RIO500=m + +# +# Bluetooth support +# +CONFIG_BLUEZ=m +CONFIG_BLUEZ_L2CAP=m + +# +# Bluetooth device drivers +# +CONFIG_BLUEZ_HCIUSB=m +CONFIG_BLUEZ_HCIUART=m +CONFIG_BLUEZ_HCIVHCI=m + +# +# Kernel hacking +# +# CONFIG_NO_FRAME_POINTER is not set +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SLAB is not set +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_WAITQ is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_ERRORS=y +CONFIG_DEBUG_LL=y +CONFIG_DEBUG_LL_SER3=y diff -Nru a/arch/arm/def-configs/epxa10db b/arch/arm/def-configs/epxa10db --- a/arch/arm/def-configs/epxa10db Thu Mar 7 18:17:41 2002 +++ b/arch/arm/def-configs/epxa10db Thu Mar 7 18:17:41 2002 @@ -8,6 +8,8 @@ CONFIG_UID16=y CONFIG_RWSEM_GENERIC_SPINLOCK=y # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +# CONFIG_GENERIC_BUST_SPINLOCK is not set +# CONFIG_GENERIC_ISA_DMA is not set # # Code maturity level options @@ -25,6 +27,7 @@ # # System Type # +# CONFIG_ARCH_ADIFCC is not set # CONFIG_ARCH_ANAKIN is not set # CONFIG_ARCH_ARCA5K is not set # CONFIG_ARCH_CLPS7500 is not set @@ -34,6 +37,7 @@ CONFIG_ARCH_CAMELOT=y # CONFIG_ARCH_FOOTBRIDGE is not set # CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP310 is not set # CONFIG_ARCH_L7200 is not set # CONFIG_ARCH_RPC is not set # CONFIG_ARCH_SA1100 is not set @@ -62,12 +66,16 @@ # CONFIG_SA1100_ADSBITSY is not set # CONFIG_SA1100_BRUTUS is not set # CONFIG_SA1100_CERF is not set +# CONFIG_SA1100_H3100 is not set # CONFIG_SA1100_H3600 is not set +# CONFIG_SA1100_H3800 is not set +# CONFIG_SA1100_H3XXX is not set # CONFIG_SA1100_EXTENEX1 is not set # CONFIG_SA1100_FLEXANET is not set # CONFIG_SA1100_FREEBIRD is not set # CONFIG_SA1100_GRAPHICSCLIENT is not set # CONFIG_SA1100_GRAPHICSMASTER is not set +# CONFIG_SA1100_BADGE4 is not set # CONFIG_SA1100_JORNADA720 is not set # CONFIG_SA1100_HUW_WEBPANEL is not set # CONFIG_SA1100_ITSY is not set @@ -76,20 +84,28 @@ # CONFIG_SA1100_OMNIMETER is not set # CONFIG_SA1100_PANGOLIN is not set # CONFIG_SA1100_PLEB is not set +# CONFIG_SA1100_PT_SYSTEM3 is not set +# CONFIG_SA1100_SHANNON is not set # CONFIG_SA1100_SHERMAN is not set # CONFIG_SA1100_SIMPAD is not set # CONFIG_SA1100_PFS168 is not set # CONFIG_SA1100_VICTOR is not set # CONFIG_SA1100_XP860 is not set # CONFIG_SA1100_YOPY is not set +# CONFIG_SA1100_USB is not set +# CONFIG_SA1100_USB_NETLINK is not set +# CONFIG_SA1100_USB_CHAR is not set +# CONFIG_H3600_SLEEVE is not set # # CLPS711X/EP721X Implementations # +# CONFIG_ARCH_AUTCPU12 is not set # CONFIG_ARCH_CDB89712 is not set # CONFIG_ARCH_CLEP7312 is not set # CONFIG_ARCH_EDB7211 is not set # CONFIG_ARCH_P720T is not set +# CONFIG_ARCH_FORTUNET is not set # CONFIG_ARCH_EP7211 is not set # CONFIG_ARCH_EP7212 is not set # CONFIG_ARCH_ACORN is not set @@ -103,14 +119,17 @@ # CONFIG_CPU_ARM610 is not set # CONFIG_CPU_ARM710 is not set # CONFIG_CPU_ARM720T is not set +# CONFIG_CPU_ARM920T is not set CONFIG_CPU_ARM922T=y -CONFIG_CPU_ARM92X_CPU_IDLE=y -CONFIG_CPU_ARM92X_I_CACHE_ON=y -CONFIG_CPU_ARM92X_D_CACHE_ON=y -# CONFIG_CPU_ARM92X_WRITETHROUGH is not set +CONFIG_CPU_ARM922_CPU_IDLE=y +CONFIG_CPU_ARM922_I_CACHE_ON=y +CONFIG_CPU_ARM922_D_CACHE_ON=y +# CONFIG_CPU_ARM922_WRITETHROUGH is not set +# CONFIG_CPU_ARM926T is not set # CONFIG_CPU_ARM1020 is not set # CONFIG_CPU_SA110 is not set # CONFIG_CPU_SA1100 is not set +# CONFIG_XSCALE_PMU is not set # CONFIG_ARM_THUMB is not set # CONFIG_DISCONTIGMEM is not set @@ -120,6 +139,7 @@ # CONFIG_PCI is not set # CONFIG_ISA is not set # CONFIG_ISA_DMA is not set +# CONFIG_FIQ is not set # CONFIG_HOTPLUG is not set # CONFIG_PCMCIA is not set CONFIG_NET=y @@ -134,6 +154,7 @@ CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set # CONFIG_PM is not set +# CONFIG_APM is not set # CONFIG_ARTHUR is not set CONFIG_CMDLINE="console=ttyUA0,38400 root=/dev/mtdblock0 rw" CONFIG_ALIGNMENT_TRAP=y @@ -171,42 +192,28 @@ # CONFIG_MTD_ROM is not set # CONFIG_MTD_ABSENT is not set # CONFIG_MTD_OBSOLETE_CHIPS is not set +# CONFIG_MTD_AMDSTD is not set +# CONFIG_MTD_SHARP is not set +# CONFIG_MTD_JEDEC is not set # # Mapping drivers for chip access # # CONFIG_MTD_PHYSMAP is not set -# CONFIG_MTD_SUN_UFLASH is not set # CONFIG_MTD_NORA is not set -# CONFIG_MTD_PNC2000 is not set -# CONFIG_MTD_RPXLITE is not set -# CONFIG_MTD_TQM8XXL is not set -# CONFIG_MTD_SC520CDP is not set -# CONFIG_MTD_NETSC520 is not set -# CONFIG_MTD_SBC_GXX is not set -# CONFIG_MTD_ELAN_104NC is not set -# CONFIG_MTD_DBOX2 is not set -# CONFIG_MTD_CSTM_MIPS_IXX is not set -CONFIG_MTD_EPXA10DB=y -# CONFIG_MTD_CFI_FLAGADM is not set -# CONFIG_MTD_SOLUTIONENGINE is not set -# CONFIG_MTD_MIXMEM is not set -# CONFIG_MTD_OCTAGON is not set -# CONFIG_MTD_VMAX is not set -# CONFIG_MTD_OCELOT is not set -# CONFIG_MTD_L440GX is not set # CONFIG_MTD_ARM_INTEGRATOR is not set # CONFIG_MTD_CDB89712 is not set # CONFIG_MTD_SA1100 is not set # CONFIG_MTD_DC21285 is not set # CONFIG_MTD_IQ80310 is not set +CONFIG_MTD_EPXA10DB=y +# CONFIG_MTD_PCI is not set # # Self-contained MTD device drivers # # CONFIG_MTD_PMC551 is not set # CONFIG_MTD_SLRAM is not set -# CONFIG_MTD_LART is not set # CONFIG_MTD_MTDRAM is not set # CONFIG_MTD_BLKMTD is not set # CONFIG_MTD_DOC1000 is not set @@ -224,7 +231,6 @@ # # CONFIG_PNP is not set # CONFIG_ISAPNP is not set -# CONFIG_PNPBIOS is not set # # Block devices @@ -234,6 +240,7 @@ # CONFIG_PARIDE is not set # CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_CISS_SCSI_TAPE is not set # CONFIG_BLK_DEV_DAC960 is not set # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set @@ -250,6 +257,7 @@ # CONFIG_MD_RAID0 is not set # CONFIG_MD_RAID1 is not set # CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set # CONFIG_BLK_DEV_LVM is not set # @@ -275,6 +283,7 @@ # CONFIG_IPV6 is not set # CONFIG_KHTTPD is not set # CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set # CONFIG_IPX is not set # CONFIG_ATALK is not set # CONFIG_DECNET is not set @@ -311,7 +320,8 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -CONFIG_ETHER00=y +# CONFIG_ARM_AM79C961A is not set +CONFIG_ETHER00=m # CONFIG_SUNLANCE is not set # CONFIG_SUNBMAC is not set # CONFIG_SUNQE is not set @@ -343,8 +353,8 @@ # CONFIG_PPP_FILTER is not set CONFIG_PPP_ASYNC=y CONFIG_PPP_SYNC_TTY=y -CONFIG_PPP_DEFLATE=y -CONFIG_PPP_BSDCOMP=y +# CONFIG_PPP_DEFLATE is not set +# CONFIG_PPP_BSDCOMP is not set # CONFIG_PPPOE is not set # CONFIG_SLIP is not set @@ -437,12 +447,14 @@ # CONFIG_SERIAL_SA1100_CONSOLE is not set # CONFIG_SERIAL_8250 is not set # CONFIG_SERIAL_8250_CONSOLE is not set +# CONFIG_ATOMWIDE_SERIAL is not set +# CONFIG_DUALSP_SERIAL is not set # CONFIG_SERIAL_8250_EXTENDED is not set # CONFIG_SERIAL_8250_MANY_PORTS is not set # CONFIG_SERIAL_8250_SHARE_IRQ is not set # CONFIG_SERIAL_8250_DETECT_IRQ is not set # CONFIG_SERIAL_8250_MULTIPORT is not set -# CONFIG_SERIAL_8250_HUB6 is not set +# CONFIG_SERIAL_8250_RSA is not set CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y @@ -460,7 +472,6 @@ # CONFIG_L3_ALGOBIT is not set # CONFIG_L3_BIT_SA1100_GPIO is not set # CONFIG_L3_SA1111 is not set -# CONFIG_L3_DRV_UDA1341 is not set # CONFIG_BIT_SA1100_GPIO is not set # @@ -485,7 +496,6 @@ # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set -# CONFIG_SONYPI is not set # # Ftape, the floppy tape device driver @@ -513,7 +523,6 @@ # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set -# CONFIG_CMS_FS is not set # CONFIG_EXT3_FS is not set # CONFIG_JBD is not set # CONFIG_JBD_DEBUG is not set @@ -522,16 +531,17 @@ # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set -CONFIG_JFFS_FS=y -CONFIG_JFFS_FS_VERBOSE=0 -# CONFIG_JFFS2_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 # CONFIG_CRAMFS is not set -# CONFIG_TMPFS is not set -# CONFIG_RAMFS is not set +CONFIG_TMPFS=y +CONFIG_RAMFS=y # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set # CONFIG_MINIX_FS is not set -# CONFIG_FREEVXFS_FS is not set +# CONFIG_VXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -572,6 +582,8 @@ # CONFIG_NCPFS_SMALLDOS is not set # CONFIG_NCPFS_NLS is not set # CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set # # Partition Types @@ -582,9 +594,19 @@ # CONFIG_NLS is not set # +# Multimedia Capabilities Port drivers +# +# CONFIG_MCP is not set +# CONFIG_MCP_SA1100 is not set +# CONFIG_MCP_UCB1200 is not set +# CONFIG_MCP_UCB1200_AUDIO is not set +# CONFIG_MCP_UCB1200_TS is not set + +# # USB support # # CONFIG_USB is not set +# CONFIG_USB_EHCI_HCD is not set # CONFIG_USB_UHCI is not set # CONFIG_USB_UHCI_ALT is not set # CONFIG_USB_OHCI is not set @@ -596,10 +618,10 @@ # CONFIG_USB_STORAGE_DATAFAB is not set # CONFIG_USB_STORAGE_FREECOM is not set # CONFIG_USB_STORAGE_ISD200 is not set -# CONFIG_USB_STORAGE_JUMPSHOT is not set # CONFIG_USB_STORAGE_DPCM is not set # CONFIG_USB_STORAGE_HP8200e is not set # CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set # CONFIG_USB_ACM is not set # CONFIG_USB_PRINTER is not set # CONFIG_USB_DC2XX is not set @@ -607,8 +629,6 @@ # CONFIG_USB_SCANNER is not set # CONFIG_USB_MICROTEK is not set # CONFIG_USB_HPUSBSCSI is not set -# CONFIG_USB_DABUSB is not set -# CONFIG_USB_PLUSB is not set # CONFIG_USB_PEGASUS is not set # CONFIG_USB_KAWETH is not set # CONFIG_USB_CATC is not set @@ -627,21 +647,27 @@ # CONFIG_USB_SERIAL_EMPEG is not set # CONFIG_USB_SERIAL_FTDI_SIO is not set # CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set # CONFIG_USB_SERIAL_EDGEPORT is not set # CONFIG_USB_SERIAL_KEYSPAN_PDA is not set # CONFIG_USB_SERIAL_KEYSPAN is not set # CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set # CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set # CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set # CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set # CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set # CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set # CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set # CONFIG_USB_SERIAL_PL2303 is not set # CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set # CONFIG_USB_SERIAL_OMNINET is not set # CONFIG_USB_RIO500 is not set -# CONFIG_USB_ID75 is not set +# CONFIG_USB_AUERSWALD is not set # # Bluetooth support @@ -652,11 +678,21 @@ # Kernel hacking # # CONFIG_NO_FRAME_POINTER is not set -CONFIG_DEBUG_ERRORS=y -CONFIG_DEBUG_USER=y -CONFIG_DEBUG_INFO=y -CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_INFO is not set # CONFIG_NO_PGT_CACHE is not set -CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_WAITQ is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_LL is not set # CONFIG_DEBUG_DC21285_PORT is not set # CONFIG_DEBUG_CLPS711X_UART2 is not set + +# +# Library routines +# +# CONFIG_CRC32 is not set diff -Nru a/arch/arm/def-configs/fortunet b/arch/arm/def-configs/fortunet --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/def-configs/fortunet Thu Mar 7 18:17:47 2002 @@ -0,0 +1,593 @@ +# +# Automatically generated by make menuconfig: don't edit +# +CONFIG_ARM=y +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +# CONFIG_GENERIC_BUST_SPINLOCK is not set +# CONFIG_GENERIC_ISA_DMA is not set + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_OBSOLETE is not set + +# +# Loadable module support +# +# CONFIG_MODULES is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_KMOD is not set + +# +# System Type +# +# CONFIG_ARCH_ANAKIN is not set +# CONFIG_ARCH_ARCA5K is not set +# CONFIG_ARCH_CLPS7500 is not set +CONFIG_ARCH_CLPS711X=y +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_SHARK is not set + +# +# Archimedes/A5000 Implementations +# +# CONFIG_ARCH_ARC is not set +# CONFIG_ARCH_A5K is not set + +# +# Footbridge Implementations +# +# CONFIG_ARCH_CATS is not set +# CONFIG_ARCH_PERSONAL_SERVER is not set +# CONFIG_ARCH_EBSA285_ADDIN is not set +# CONFIG_ARCH_EBSA285_HOST is not set +# CONFIG_ARCH_NETWINDER is not set + +# +# SA11x0 Implementations +# +# CONFIG_SA1100_ASSABET is not set +# CONFIG_ASSABET_NEPONSET is not set +# CONFIG_SA1100_ADSBITSY is not set +# CONFIG_SA1100_BRUTUS is not set +# CONFIG_SA1100_CERF is not set +# CONFIG_SA1100_H3100 is not set +# CONFIG_SA1100_H3600 is not set +# CONFIG_SA1100_H3800 is not set +# CONFIG_SA1100_H3XXX is not set +# CONFIG_SA1100_EXTENEX1 is not set +# CONFIG_SA1100_FLEXANET is not set +# CONFIG_SA1100_FREEBIRD is not set +# CONFIG_SA1100_GRAPHICSCLIENT is not set +# CONFIG_SA1100_GRAPHICSMASTER is not set +# CONFIG_SA1100_JORNADA720 is not set +# CONFIG_SA1100_HUW_WEBPANEL is not set +# CONFIG_SA1100_ITSY is not set +# CONFIG_SA1100_LART is not set +# CONFIG_SA1100_NANOENGINE is not set +# CONFIG_SA1100_OMNIMETER is not set +# CONFIG_SA1100_PANGOLIN is not set +# CONFIG_SA1100_PLEB is not set +# CONFIG_SA1100_SHANNON is not set +# CONFIG_SA1100_SHERMAN is not set +# CONFIG_SA1100_SIMPAD is not set +# CONFIG_SA1100_PFS168 is not set +# CONFIG_SA1100_VICTOR is not set +# CONFIG_SA1100_XP860 is not set +# CONFIG_SA1100_YOPY is not set +# CONFIG_SA1100_USB is not set +# CONFIG_SA1100_USB_NETLINK is not set +# CONFIG_SA1100_USB_CHAR is not set +# CONFIG_H3600_SLEEVE is not set + +# +# CLPS711X/EP721X Implementations +# +# CONFIG_ARCH_AUTCPU12 is not set +# CONFIG_ARCH_CDB89712 is not set +# CONFIG_ARCH_CLEP7312 is not set +# CONFIG_ARCH_EDB7211 is not set +# CONFIG_ARCH_P720T is not set +CONFIG_ARCH_FORTUNET=y +# CONFIG_ARCH_EP7211 is not set +# CONFIG_ARCH_EP7212 is not set +# CONFIG_ARCH_ACORN is not set +# CONFIG_FOOTBRIDGE is not set +# CONFIG_FOOTBRIDGE_HOST is not set +# CONFIG_FOOTBRIDGE_ADDIN is not set +CONFIG_CPU_32=y +# CONFIG_CPU_26 is not set +# CONFIG_CPU_32v3 is not set +CONFIG_CPU_32v4=y +# CONFIG_CPU_ARM610 is not set +# CONFIG_CPU_ARM710 is not set +CONFIG_CPU_ARM720T=y +# CONFIG_CPU_ARM920T is not set +# CONFIG_CPU_ARM922T is not set +# CONFIG_CPU_ARM926T is not set +# CONFIG_CPU_ARM1020 is not set +# CONFIG_CPU_SA110 is not set +# CONFIG_CPU_SA1100 is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_DISCONTIGMEM is not set + +# +# General setup +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set +# CONFIG_ISA_DMA is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set +CONFIG_NET=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_FPE_NWFPE is not set +CONFIG_FPE_FASTFPE=y +CONFIG_KCORE_ELF=y +# CONFIG_KCORE_AOUT is not set +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PM is not set +# CONFIG_ARTHUR is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_PARTITIONS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_BOOTLDR_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set +# CONFIG_MTD_AMDSTD is not set +# CONFIG_MTD_SHARP is not set +# CONFIG_MTD_JEDEC is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_NORA is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +# CONFIG_MTD_CDB89712 is not set +# CONFIG_MTD_SA1100 is not set +# CONFIG_MTD_DC21285 is not set +# CONFIG_MTD_IQ80310 is not set +CONFIG_MTD_FORTUNET=y +# CONFIG_MTD_PCI is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set +# CONFIG_MTD_DOC1000 is not set +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOCPROBE is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +# CONFIG_INET is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q 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_AUNUDP is not set +# CONFIG_ECONET_NATIVE 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 is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Synchronous Serial Interface +# +# CONFIG_SSI is not set +# CONFIG_SSI_CLPS711X is not set +# CONFIG_SSI_JUNO is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_ANAKIN is not set +# CONFIG_SERIAL_ANAKIN_CONSOLE is not set +# CONFIG_SERIAL_AMBA is not set +# CONFIG_SERIAL_AMBA_CONSOLE is not set +CONFIG_SERIAL_CLPS711X=y +CONFIG_SERIAL_CLPS711X_CONSOLE=y +# CONFIG_SERIAL_21285 is not set +# CONFIG_SERIAL_21285_OLD is not set +# CONFIG_SERIAL_21285_CONSOLE is not set +# CONFIG_SERIAL_UART00 is not set +# CONFIG_SERIAL_UART00_CONSOLE is not set +# CONFIG_SERIAL_SA1100 is not set +# CONFIG_SERIAL_SA1100_CONSOLE is not set +# CONFIG_SERIAL_8250 is not set +# CONFIG_SERIAL_8250_CONSOLE is not set +# CONFIG_SERIAL_8250_EXTENDED is not set +# CONFIG_SERIAL_8250_MANY_PORTS is not set +# CONFIG_SERIAL_8250_SHARE_IRQ is not set +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +# CONFIG_SERIAL_8250_MULTIPORT is not set +# CONFIG_SERIAL_8250_HUB6 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# L3 serial bus support +# +# CONFIG_L3 is not set +# CONFIG_L3_ALGOBIT is not set +# CONFIG_L3_BIT_SA1100_GPIO is not set +# CONFIG_L3_SA1111 is not set +# CONFIG_BIT_SA1100_GPIO is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS_FS=y +CONFIG_JFFS_FS_VERBOSE=0 +# CONFIG_JFFS_PROC_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_TMPFS is not set +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW 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_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFS_V3 is not set +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Multimedia Capabilities Port drivers +# +# CONFIG_MCP is not set +# CONFIG_MCP_SA1100 is not set +# CONFIG_MCP_UCB1200 is not set +# CONFIG_MCP_UCB1200_AUDIO is not set +# CONFIG_MCP_UCB1200_TS is not set + +# +# USB support +# +# CONFIG_USB is not set +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set +# CONFIG_USB_OHCI_SA1111 is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_NO_FRAME_POINTER is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_NO_PGT_CACHE is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_WAITQ is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_DEBUG_DC21285_PORT is not set +# CONFIG_DEBUG_CLPS711X_UART2 is not set +# CONFIG_DEBUG_LL_SER3 is not set diff -Nru a/arch/arm/def-configs/shannon b/arch/arm/def-configs/shannon --- a/arch/arm/def-configs/shannon Thu Mar 7 18:17:38 2002 +++ b/arch/arm/def-configs/shannon Thu Mar 7 18:17:38 2002 @@ -378,7 +378,6 @@ # CONFIG_BLK_DEV_IDECD is not set # CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set -# CONFIG_IDE_TASK_IOCTL is not set # # IDE chipset support/bugfixes diff -Nru a/arch/arm/def-configs/shark b/arch/arm/def-configs/shark --- a/arch/arm/def-configs/shark Thu Mar 7 18:17:38 2002 +++ b/arch/arm/def-configs/shark Thu Mar 7 18:17:38 2002 @@ -9,6 +9,7 @@ CONFIG_RWSEM_GENERIC_SPINLOCK=y # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set # CONFIG_GENERIC_BUST_SPINLOCK is not set +# CONFIG_GENERIC_ISA_DMA is not set # # Code maturity level options @@ -26,14 +27,17 @@ # # System Type # +# CONFIG_ARCH_ADIFCC is not set # CONFIG_ARCH_ANAKIN is not set # CONFIG_ARCH_ARCA5K is not set # CONFIG_ARCH_CLPS7500 is not set # CONFIG_ARCH_CLPS711X is not set # CONFIG_ARCH_CO285 is not set # CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set # CONFIG_ARCH_FOOTBRIDGE is not set # CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP310 is not set # CONFIG_ARCH_L7200 is not set # CONFIG_ARCH_RPC is not set # CONFIG_ARCH_SA1100 is not set @@ -66,12 +70,16 @@ # CONFIG_SA1100_ADSBITSY is not set # CONFIG_SA1100_BRUTUS is not set # CONFIG_SA1100_CERF is not set +# CONFIG_SA1100_H3100 is not set # CONFIG_SA1100_H3600 is not set +# CONFIG_SA1100_H3800 is not set +# CONFIG_SA1100_H3XXX is not set # CONFIG_SA1100_EXTENEX1 is not set # CONFIG_SA1100_FLEXANET is not set # CONFIG_SA1100_FREEBIRD is not set # CONFIG_SA1100_GRAPHICSCLIENT is not set # CONFIG_SA1100_GRAPHICSMASTER is not set +# CONFIG_SA1100_BADGE4 is not set # CONFIG_SA1100_JORNADA720 is not set # CONFIG_SA1100_HUW_WEBPANEL is not set # CONFIG_SA1100_ITSY is not set @@ -80,20 +88,28 @@ # CONFIG_SA1100_OMNIMETER is not set # CONFIG_SA1100_PANGOLIN is not set # CONFIG_SA1100_PLEB is not set +# CONFIG_SA1100_PT_SYSTEM3 is not set +# CONFIG_SA1100_SHANNON is not set # CONFIG_SA1100_SHERMAN is not set # CONFIG_SA1100_SIMPAD is not set # CONFIG_SA1100_PFS168 is not set # CONFIG_SA1100_VICTOR is not set # CONFIG_SA1100_XP860 is not set # CONFIG_SA1100_YOPY is not set +# CONFIG_SA1100_USB is not set +# CONFIG_SA1100_USB_NETLINK is not set +# CONFIG_SA1100_USB_CHAR is not set +# CONFIG_H3600_SLEEVE is not set # # CLPS711X/EP721X Implementations # +# CONFIG_ARCH_AUTCPU12 is not set # CONFIG_ARCH_CDB89712 is not set # CONFIG_ARCH_CLEP7312 is not set # CONFIG_ARCH_EDB7211 is not set # CONFIG_ARCH_P720T is not set +# CONFIG_ARCH_FORTUNET is not set # CONFIG_ARCH_EP7211 is not set # CONFIG_ARCH_EP7212 is not set # CONFIG_ARCH_ACORN is not set @@ -112,9 +128,12 @@ # CONFIG_CPU_ARM710 is not set # CONFIG_CPU_ARM720T is not set # CONFIG_CPU_ARM920T is not set +# CONFIG_CPU_ARM922T is not set +# CONFIG_CPU_ARM926T is not set # CONFIG_CPU_ARM1020 is not set CONFIG_CPU_SA110=y # CONFIG_CPU_SA1100 is not set +# CONFIG_XSCALE_PMU is not set # CONFIG_ARM_THUMB is not set # CONFIG_DISCONTIGMEM is not set @@ -122,8 +141,11 @@ # General setup # CONFIG_PCI=y +# CONFIG_PCI_HOST_PLX90X0 is not set +CONFIG_PCI_HOST_VIA82C505=y CONFIG_ISA=y CONFIG_ISA_DMA=y +# CONFIG_FIQ is not set # CONFIG_PCI_NAMES is not set # CONFIG_HOTPLUG is not set # CONFIG_PCMCIA is not set @@ -143,6 +165,7 @@ CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set # CONFIG_PM is not set +# CONFIG_APM is not set # CONFIG_ARTHUR is not set CONFIG_LEDS=y CONFIG_LEDS_TIMER=y @@ -172,7 +195,6 @@ # # CONFIG_PNP is not set # CONFIG_ISAPNP is not set -# CONFIG_PNPBIOS is not set # # Block devices @@ -182,6 +204,7 @@ # CONFIG_PARIDE is not set # CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_CISS_SCSI_TAPE is not set # CONFIG_BLK_DEV_DAC960 is not set CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_NBD is not set @@ -204,10 +227,11 @@ # # Networking options # -# CONFIG_PACKET is not set +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set # CONFIG_NETLINK is not set # CONFIG_NETFILTER is not set -# CONFIG_FILTER is not set +CONFIG_FILTER=y CONFIG_UNIX=y CONFIG_INET=y # CONFIG_IP_MULTICAST is not set @@ -220,6 +244,7 @@ # CONFIG_IPV6 is not set # CONFIG_KHTTPD is not set # CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set # # @@ -260,6 +285,7 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y +# CONFIG_ARM_AM79C961A is not set # CONFIG_SUNLANCE is not set # CONFIG_HAPPYMEAL is not set # CONFIG_SUNBMAC is not set @@ -280,6 +306,7 @@ # CONFIG_AC3200 is not set # CONFIG_APRICOT is not set CONFIG_CS89x0=y +# CONFIG_DE2104X is not set # CONFIG_TULIP is not set # CONFIG_DE4X5 is not set # CONFIG_DGRS is not set @@ -291,6 +318,7 @@ # CONFIG_NE2K_PCI is not set # CONFIG_NE3210 is not set # CONFIG_ES3210 is not set +# CONFIG_8139CP is not set # CONFIG_8139TOO is not set # CONFIG_8139TOO_PIO is not set # CONFIG_8139TOO_TUNE_TWISTER is not set @@ -300,6 +328,7 @@ # CONFIG_SUNDANCE is not set # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set +# CONFIG_VIA_RHINE_MMIO is not set # CONFIG_WINBOND_840 is not set # CONFIG_NET_POCKET is not set @@ -416,7 +445,6 @@ # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # -# CONFIG_SCSI_DEBUG_QUEUES is not set # CONFIG_SCSI_MULTI_LUN is not set # CONFIG_SCSI_CONSTANTS is not set # CONFIG_SCSI_LOGGING is not set @@ -453,6 +481,7 @@ # CONFIG_SCSI_IMM is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set # CONFIG_SCSI_NCR53C8XX is not set # CONFIG_SCSI_SYM53C8XX is not set # CONFIG_SCSI_PAS16 is not set @@ -521,16 +550,20 @@ # CONFIG_SERIAL_21285 is not set # CONFIG_SERIAL_21285_OLD is not set # CONFIG_SERIAL_21285_CONSOLE is not set +# CONFIG_SERIAL_UART00 is not set +# CONFIG_SERIAL_UART00_CONSOLE is not set # CONFIG_SERIAL_SA1100 is not set # CONFIG_SERIAL_SA1100_CONSOLE is not set # CONFIG_SERIAL_8250 is not set # CONFIG_SERIAL_8250_CONSOLE is not set +# CONFIG_ATOMWIDE_SERIAL is not set +# CONFIG_DUALSP_SERIAL is not set # CONFIG_SERIAL_8250_EXTENDED is not set # CONFIG_SERIAL_8250_MANY_PORTS is not set # CONFIG_SERIAL_8250_SHARE_IRQ is not set # CONFIG_SERIAL_8250_DETECT_IRQ is not set # CONFIG_SERIAL_8250_MULTIPORT is not set -# CONFIG_SERIAL_8250_HUB6 is not set +# CONFIG_SERIAL_8250_RSA is not set CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 CONFIG_PRINTER=m @@ -588,7 +621,6 @@ # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set -# CONFIG_SONYPI is not set # # Ftape, the floppy tape device driver @@ -596,7 +628,6 @@ # CONFIG_FTAPE is not set # CONFIG_AGP is not set # CONFIG_DRM is not set -# CONFIG_MWAVE is not set # # Multimedia devices @@ -617,7 +648,6 @@ # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set -# CONFIG_CMS_FS is not set CONFIG_EXT3_FS=y CONFIG_JBD=y # CONFIG_JBD_DEBUG is not set @@ -630,12 +660,12 @@ # CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set # CONFIG_TMPFS is not set -# CONFIG_RAMFS is not set +CONFIG_RAMFS=y CONFIG_ISO9660_FS=y CONFIG_JOLIET=y # CONFIG_ZISOFS is not set # CONFIG_MINIX_FS is not set -# CONFIG_FREEVXFS_FS is not set +# CONFIG_VXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -807,7 +837,7 @@ # CONFIG_SOUND_DMAP is not set # CONFIG_SOUND_AD1816 is not set # CONFIG_SOUND_SGALAXY is not set -# CONFIG_SOUND_ADLIB is not set +CONFIG_SOUND_ADLIB=m # CONFIG_SOUND_ACI_MIXER is not set # CONFIG_SOUND_CS4232 is not set # CONFIG_SOUND_SSCAPE is not set @@ -850,8 +880,9 @@ # CONFIG_USB is not set # -# USB Controllers +# USB Host Controller Drivers # +# CONFIG_USB_EHCI_HCD is not set # CONFIG_USB_UHCI is not set # CONFIG_USB_UHCI_ALT is not set # CONFIG_USB_OHCI is not set @@ -867,10 +898,10 @@ # CONFIG_USB_STORAGE_DATAFAB is not set # CONFIG_USB_STORAGE_FREECOM is not set # CONFIG_USB_STORAGE_ISD200 is not set -# CONFIG_USB_STORAGE_JUMPSHOT is not set # CONFIG_USB_STORAGE_DPCM is not set # CONFIG_USB_STORAGE_HP8200e is not set # CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set # CONFIG_USB_ACM is not set # CONFIG_USB_PRINTER is not set @@ -898,12 +929,10 @@ # # Video4Linux support is needed for USB Multimedia device support # -# CONFIG_USB_DABUSB is not set # # USB Network adaptors # -# CONFIG_USB_PLUSB is not set # CONFIG_USB_PEGASUS is not set # CONFIG_USB_KAWETH is not set # CONFIG_USB_CATC is not set @@ -926,27 +955,31 @@ # CONFIG_USB_SERIAL_EMPEG is not set # CONFIG_USB_SERIAL_FTDI_SIO is not set # CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set # CONFIG_USB_SERIAL_IR is not set # CONFIG_USB_SERIAL_EDGEPORT is not set # CONFIG_USB_SERIAL_KEYSPAN_PDA is not set # CONFIG_USB_SERIAL_KEYSPAN is not set # CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set # CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set # CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set # CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set # CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set # CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set # CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set # CONFIG_USB_SERIAL_PL2303 is not set # CONFIG_USB_SERIAL_CYBERJACK is not set # CONFIG_USB_SERIAL_XIRCOM is not set # CONFIG_USB_SERIAL_OMNINET is not set # -# Miscellaneous USB drivers +# USB Miscellaneous drivers # # CONFIG_USB_RIO500 is not set -# CONFIG_USB_ID75 is not set +# CONFIG_USB_AUERSWALD is not set # # Bluetooth support @@ -957,11 +990,21 @@ # Kernel hacking # CONFIG_NO_FRAME_POINTER=y -CONFIG_DEBUG_ERRORS=y CONFIG_DEBUG_USER=y # CONFIG_DEBUG_INFO is not set -# CONFIG_MAGIC_SYSRQ is not set # CONFIG_NO_PGT_CACHE is not set -CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_WAITQ is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_LL is not set # CONFIG_DEBUG_DC21285_PORT is not set # CONFIG_DEBUG_CLPS711X_UART2 is not set + +# +# Library routines +# +CONFIG_CRC32=y diff -Nru a/arch/arm/def-configs/stork b/arch/arm/def-configs/stork --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/def-configs/stork Thu Mar 7 18:17:46 2002 @@ -0,0 +1,976 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_ARM=y +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +# CONFIG_GENERIC_BUST_SPINLOCK is not set +# CONFIG_GENERIC_ISA_DMA is not set + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_OBSOLETE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# System Type +# +# CONFIG_ARCH_ADIFCC is not set +# CONFIG_ARCH_ANAKIN is not set +# CONFIG_ARCH_ARCA5K is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP310 is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_RPC is not set +CONFIG_ARCH_SA1100=y +# CONFIG_ARCH_SHARK is not set + +# +# Archimedes/A5000 Implementations +# + +# +# Archimedes/A5000 Implementations (select only ONE) +# +# CONFIG_ARCH_ARC is not set +# CONFIG_ARCH_A5K is not set + +# +# Footbridge Implementations +# +# CONFIG_ARCH_CATS is not set +# CONFIG_ARCH_PERSONAL_SERVER is not set +# CONFIG_ARCH_EBSA285_ADDIN is not set +# CONFIG_ARCH_EBSA285_HOST is not set +# CONFIG_ARCH_NETWINDER is not set + +# +# SA11x0 Implementations +# +# CONFIG_SA1100_ASSABET is not set +# CONFIG_ASSABET_NEPONSET is not set +# CONFIG_SA1100_ADSBITSY is not set +# CONFIG_SA1100_BRUTUS is not set +# CONFIG_SA1100_CERF is not set +# CONFIG_SA1100_H3100 is not set +CONFIG_SA1100_H3600=y +# CONFIG_SA1100_H3800 is not set +CONFIG_SA1100_H3XXX=y +# CONFIG_SA1100_EXTENEX1 is not set +# CONFIG_SA1100_FLEXANET is not set +# CONFIG_SA1100_FREEBIRD is not set +# CONFIG_SA1100_GRAPHICSCLIENT is not set +# CONFIG_SA1100_GRAPHICSMASTER is not set +# CONFIG_SA1100_BADGE4 is not set +# CONFIG_SA1100_JORNADA720 is not set +# CONFIG_SA1100_HUW_WEBPANEL is not set +# CONFIG_SA1100_ITSY is not set +# CONFIG_SA1100_LART is not set +# CONFIG_SA1100_NANOENGINE is not set +# CONFIG_SA1100_OMNIMETER is not set +# CONFIG_SA1100_PANGOLIN is not set +# CONFIG_SA1100_PLEB is not set +# CONFIG_SA1100_PT_SYSTEM3 is not set +# CONFIG_SA1100_SHANNON is not set +# CONFIG_SA1100_SHERMAN is not set +# CONFIG_SA1100_SIMPAD is not set +# CONFIG_SA1100_PFS168 is not set +# CONFIG_SA1100_VICTOR is not set +# CONFIG_SA1100_XP860 is not set +# CONFIG_SA1100_YOPY is not set +CONFIG_SA1100_USB=m +CONFIG_SA1100_USB_NETLINK=m +# CONFIG_SA1100_USB_CHAR is not set +CONFIG_H3600_SLEEVE=m + +# +# CLPS711X/EP721X Implementations +# +# CONFIG_ARCH_AUTCPU12 is not set +# CONFIG_ARCH_CDB89712 is not set +# CONFIG_ARCH_CLEP7312 is not set +# CONFIG_ARCH_EDB7211 is not set +# CONFIG_ARCH_P720T is not set +# CONFIG_ARCH_FORTUNET is not set +# CONFIG_ARCH_EP7211 is not set +# CONFIG_ARCH_EP7212 is not set +# CONFIG_ARCH_ACORN is not set +# CONFIG_FOOTBRIDGE is not set +# CONFIG_FOOTBRIDGE_HOST is not set +# CONFIG_FOOTBRIDGE_ADDIN is not set +CONFIG_CPU_32=y +# CONFIG_CPU_26 is not set + +# +# Processor Type +# +# CONFIG_CPU_32v3 is not set +CONFIG_CPU_32v4=y +# CONFIG_CPU_ARM610 is not set +# CONFIG_CPU_ARM710 is not set +# CONFIG_CPU_ARM720T is not set +# CONFIG_CPU_ARM920T is not set +# CONFIG_CPU_ARM922T is not set +# CONFIG_CPU_ARM926T is not set +# CONFIG_CPU_ARM1020 is not set +# CONFIG_CPU_SA110 is not set +CONFIG_CPU_SA1100=y +# CONFIG_XSCALE_PMU is not set +# CONFIG_ARM_THUMB is not set +CONFIG_DISCONTIGMEM=y + +# +# General setup +# +# CONFIG_PCI is not set +CONFIG_ISA=y +# CONFIG_ISA_DMA is not set +# CONFIG_FIQ is not set +CONFIG_CPU_FREQ=y +CONFIG_HOTPLUG=y + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=m +CONFIG_PCMCIA_PROBE=y +# CONFIG_I82092 is not set +# CONFIG_I82365 is not set +# CONFIG_TCIC is not set +# CONFIG_PCMCIA_CLPS6700 is not set +CONFIG_PCMCIA_SA1100=m +CONFIG_NET=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y + +# +# At least one math emulation must be selected +# +CONFIG_FPE_NWFPE=m +CONFIG_FPE_FASTFPE=y +CONFIG_KCORE_ELF=y +# CONFIG_KCORE_AOUT is not set +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +CONFIG_PM=y +# CONFIG_APM is not set +# CONFIG_ARTHUR is not set +CONFIG_CMDLINE="N" +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_REDBOOT_PARTS=y +CONFIG_MTD_BOOTLDR_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_NOSWAP=y +# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set +# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set +# CONFIG_MTD_CFI_GEOMETRY is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set +# CONFIG_MTD_AMDSTD is not set +# CONFIG_MTD_SHARP is not set +# CONFIG_MTD_JEDEC is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_NORA is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +# CONFIG_MTD_CDB89712 is not set +CONFIG_MTD_SA1100=y +# CONFIG_MTD_DC21285 is not set +# CONFIG_MTD_IQ80310 is not set +# CONFIG_MTD_EPXA10DB is not set +# CONFIG_MTD_PCI is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC1000 is not set +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOCPROBE is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_CISS_SCSI_TAPE is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +CONFIG_RTNETLINK=y +# CONFIG_NETLINK_DEV 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_ARPD 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_VLAN_8021Q 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 + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +# CONFIG_NET_ETHERNET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=m +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=m +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m +# CONFIG_PPPOE is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +CONFIG_NET_RADIO=y +# CONFIG_STRIP is not set +CONFIG_WAVELAN=m +# CONFIG_ARLAN is not set +# CONFIG_AIRONET4500 is not set +# CONFIG_AIRONET4500_NONCS is not set +# CONFIG_AIRONET4500_PROC is not set +# CONFIG_AIRO is not set +CONFIG_HERMES=m + +# +# Wireless Pcmcia cards support +# +CONFIG_PCMCIA_HERMES=m +# CONFIG_AIRO_CS is not set +CONFIG_NET_WIRELESS=y + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# PCMCIA network device support +# +CONFIG_NET_PCMCIA=y +CONFIG_PCMCIA_3C589=m +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_FMVJ18X is not set +CONFIG_PCMCIA_PCNET=m +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_PCMCIA_SMC91C92 is not set +CONFIG_PCMCIA_XIRC2PS=m +# CONFIG_PCMCIA_AXNET is not set +# CONFIG_ARCNET_COM20020_CS is not set +# CONFIG_PCMCIA_IBMTR is not set +# CONFIG_NET_PCMCIA_RADIO is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=m + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=m + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=m +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set +# CONFIG_BLK_DEV_IDEDISK_IBM is not set +# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set +# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set +# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set +# CONFIG_BLK_DEV_IDEDISK_WD is not set +# CONFIG_BLK_DEV_COMMERIAL is not set +# CONFIG_BLK_DEV_TIVO is not set +CONFIG_BLK_DEV_IDECS=m +CONFIG_BLK_DEV_IDECD=m +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set + +# +# IDE chipset support/bugfixes +# +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_DMA_NONPCI is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_ATARAID is not set +# CONFIG_BLK_DEV_ATARAID_PDC is not set +# CONFIG_BLK_DEV_ATARAID_HPT is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Character devices +# +CONFIG_VT=y +# CONFIG_VT_CONSOLE is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_ANAKIN is not set +# CONFIG_SERIAL_ANAKIN_CONSOLE is not set +# CONFIG_SERIAL_AMBA is not set +# CONFIG_SERIAL_AMBA_CONSOLE is not set +# CONFIG_SERIAL_CLPS711X is not set +# CONFIG_SERIAL_CLPS711X_CONSOLE is not set +# CONFIG_SERIAL_21285 is not set +# CONFIG_SERIAL_21285_OLD is not set +# CONFIG_SERIAL_21285_CONSOLE is not set +# CONFIG_SERIAL_UART00 is not set +# CONFIG_SERIAL_UART00_CONSOLE is not set +CONFIG_SERIAL_SA1100=y +CONFIG_SERIAL_SA1100_CONSOLE=y +CONFIG_SA1100_DEFAULT_BAUDRATE=115200 +CONFIG_SERIAL_8250=m +# CONFIG_SERIAL_8250_CONSOLE is not set +# CONFIG_ATOMWIDE_SERIAL is not set +# CONFIG_DUALSP_SERIAL is not set +# CONFIG_SERIAL_8250_EXTENDED is not set +# CONFIG_SERIAL_8250_MANY_PORTS is not set +# CONFIG_SERIAL_8250_SHARE_IRQ is not set +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +# CONFIG_SERIAL_8250_MULTIPORT is not set +# CONFIG_SERIAL_8250_RSA is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=32 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# L3 serial bus support +# +CONFIG_L3=y +CONFIG_L3_ALGOBIT=y +CONFIG_L3_BIT_SA1100_GPIO=y + +# +# Other L3 adapters +# +# CONFIG_L3_SA1111 is not set +CONFIG_BIT_SA1100_GPIO=y + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +CONFIG_MOUSE=m +# CONFIG_PSMOUSE is not set +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +CONFIG_SA1100_RTC=m +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_PCMCIA_SERIAL_CS is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +# CONFIG_UMSDOS_FS is not set +CONFIG_VFAT_FS=m +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_CRAMFS=y +# CONFIG_TMPFS is not set +CONFIG_RAMFS=y +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW 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_QNX4FS_RW 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_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_ROOT_NFS is not set +CONFIG_NFSD=m +CONFIG_NFSD_V3=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_SMB_FS=m +# CONFIG_SMB_NLS_DEFAULT is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +CONFIG_ZLIB_FS_INFLATE=y + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_SMB_NLS=y +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_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1251 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_9 is not set +# CONFIG_NLS_ISO8859_13 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_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Console drivers +# +CONFIG_PC_KEYMAP=y +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +CONFIG_FB=y +CONFIG_DUMMY_CONSOLE=y +# CONFIG_FB_ACORN is not set +# CONFIG_FB_ANAKIN is not set +# CONFIG_FB_CLPS711X is not set +CONFIG_FB_SA1100=y +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_VIRTUAL is not set +CONFIG_FBCON_ADVANCED=y +# CONFIG_FBCON_MFB is not set +# CONFIG_FBCON_CFB2 is not set +# CONFIG_FBCON_CFB4 is not set +# CONFIG_FBCON_CFB8 is not set +CONFIG_FBCON_CFB16=y +# CONFIG_FBCON_CFB24 is not set +# CONFIG_FBCON_CFB32 is not set +# CONFIG_FBCON_AFB is not set +# CONFIG_FBCON_ILBM is not set +# CONFIG_FBCON_IPLAN2P2 is not set +# CONFIG_FBCON_IPLAN2P4 is not set +# CONFIG_FBCON_IPLAN2P8 is not set +# CONFIG_FBCON_MAC is not set +# CONFIG_FBCON_VGA_PLANES is not set +# CONFIG_FBCON_VGA is not set +# CONFIG_FBCON_HGA is not set +CONFIG_FBCON_FONTWIDTH8_ONLY=y +CONFIG_FBCON_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set + +# +# Sound +# +CONFIG_SOUND=y +# CONFIG_SOUND_BT878 is not set +# CONFIG_SOUND_CMPCI is not set +# CONFIG_SOUND_EMU10K1 is not set +# CONFIG_MIDI_EMU10K1 is not set +# CONFIG_SOUND_FUSION is not set +# CONFIG_SOUND_CS4281 is not set +# CONFIG_SOUND_ES1370 is not set +# CONFIG_SOUND_ES1371 is not set +# CONFIG_SOUND_ESSSOLO1 is not set +# CONFIG_SOUND_MAESTRO is not set +# CONFIG_SOUND_MAESTRO3 is not set +# CONFIG_SOUND_ICH is not set +# CONFIG_SOUND_RME96XX is not set +# CONFIG_SOUND_SONICVIBES is not set +# CONFIG_SOUND_TRIDENT is not set +# CONFIG_SOUND_MSNDCLAS is not set +# CONFIG_SOUND_MSNDPIN is not set +# CONFIG_SOUND_VIA82CXXX is not set +# CONFIG_MIDI_VIA82CXXX is not set +CONFIG_SOUND_SA1100=y +CONFIG_SOUND_UDA1341=m +# CONFIG_SOUND_ASSABET_UDA1341 is not set +CONFIG_SOUND_H3600_UDA1341=m +# CONFIG_SOUND_PANGOLIN_UDA1341 is not set +# CONFIG_SOUND_SA1111_UDA1341 is not set +# CONFIG_SOUND_SA1100SSP is not set +# CONFIG_SOUND_OSS is not set +# CONFIG_SOUND_WAVEARTIST is not set +# CONFIG_SOUND_TVMIXER is not set + +# +# Multimedia Capabilities Port drivers +# +# CONFIG_MCP is not set +# CONFIG_MCP_SA1100 is not set +# CONFIG_MCP_UCB1200 is not set +# CONFIG_MCP_UCB1200_AUDIO is not set +# CONFIG_MCP_UCB1200_TS is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_EHCI_HCD is not set +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set +# CONFIG_USB_OHCI_SA1111 is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set + +# +# SCSI support is needed for USB Storage +# +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# USB Imaging devices +# +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_AUERSWALD is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_NO_FRAME_POINTER is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_NO_PGT_CACHE is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_WAITQ is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_DEBUG_DC21285_PORT is not set +# CONFIG_DEBUG_CLPS711X_UART2 is not set + +# +# Library routines +# +# CONFIG_CRC32 is not set diff -Nru a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c --- a/arch/arm/kernel/armksyms.c Thu Mar 7 18:17:42 2002 +++ b/arch/arm/kernel/armksyms.c Thu Mar 7 18:17:42 2002 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -123,6 +124,7 @@ EXPORT_SYMBOL(__readwrite_bug); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(set_irq_type); EXPORT_SYMBOL(pm_idle); EXPORT_SYMBOL(pm_power_off); @@ -167,10 +169,6 @@ EXPORT_SYMBOL(__bus_to_virt); #endif -#ifndef CONFIG_NO_PGT_CACHE -EXPORT_SYMBOL(quicklists); -#endif - /* string / mem functions */ EXPORT_SYMBOL_NOVERS(strcpy); EXPORT_SYMBOL_NOVERS(strncpy); @@ -273,3 +271,7 @@ EXPORT_SYMBOL_NOVERS(__up_wakeup); EXPORT_SYMBOL(get_wchan); + +#ifdef CONFIG_PREEMPT +EXPORT_SYMBOL(kernel_flag); +#endif diff -Nru a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S --- a/arch/arm/kernel/calls.S Thu Mar 7 18:17:45 2002 +++ b/arch/arm/kernel/calls.S Thu Mar 7 18:17:45 2002 @@ -21,13 +21,13 @@ .long SYMBOL_NAME(sys_write) /* 5 */ .long SYMBOL_NAME(sys_open) .long SYMBOL_NAME(sys_close) - .long SYMBOL_NAME(sys_waitpid) + .long SYMBOL_NAME(sys_ni_syscall) /* was sys_waitpid */ .long SYMBOL_NAME(sys_creat) .long SYMBOL_NAME(sys_link) /* 10 */ .long SYMBOL_NAME(sys_unlink) .long SYMBOL_NAME(sys_execve_wrapper) .long SYMBOL_NAME(sys_chdir) - .long SYMBOL_NAME(sys_time) + .long SYMBOL_NAME(sys_time) /* used by libc4 */ .long SYMBOL_NAME(sys_mknod) /* 15 */ .long SYMBOL_NAME(sys_chmod) .long SYMBOL_NAME(sys_lchown16) @@ -36,15 +36,15 @@ .long SYMBOL_NAME(sys_lseek) /* 20 */ .long SYMBOL_NAME(sys_getpid) .long SYMBOL_NAME(sys_mount) - .long SYMBOL_NAME(sys_oldumount) + .long SYMBOL_NAME(sys_oldumount) /* used by libc4 */ .long SYMBOL_NAME(sys_setuid16) .long SYMBOL_NAME(sys_getuid16) /* 25 */ .long SYMBOL_NAME(sys_stime) .long SYMBOL_NAME(sys_ptrace) - .long SYMBOL_NAME(sys_alarm) + .long SYMBOL_NAME(sys_alarm) /* used by libc4 */ .long SYMBOL_NAME(sys_ni_syscall) /* was sys_fstat */ .long SYMBOL_NAME(sys_pause) -/* 30 */ .long SYMBOL_NAME(sys_utime) +/* 30 */ .long SYMBOL_NAME(sys_utime) /* used by libc4 */ .long SYMBOL_NAME(sys_ni_syscall) /* was sys_stty */ .long SYMBOL_NAME(sys_ni_syscall) /* was sys_getty */ .long SYMBOL_NAME(sys_access) @@ -62,7 +62,7 @@ /* 45 */ .long SYMBOL_NAME(sys_brk) .long SYMBOL_NAME(sys_setgid16) .long SYMBOL_NAME(sys_getgid16) - .long SYMBOL_NAME(sys_signal) + .long SYMBOL_NAME(sys_ni_syscall) /* was sys_signal */ .long SYMBOL_NAME(sys_geteuid16) /* 50 */ .long SYMBOL_NAME(sys_getegid16) .long SYMBOL_NAME(sys_acct) @@ -82,29 +82,29 @@ /* 65 */ .long SYMBOL_NAME(sys_getpgrp) .long SYMBOL_NAME(sys_setsid) .long SYMBOL_NAME(sys_sigaction) - .long SYMBOL_NAME(sys_sgetmask) - .long SYMBOL_NAME(sys_ssetmask) + .long SYMBOL_NAME(sys_ni_syscall) /* was sys_sgetmask */ + .long SYMBOL_NAME(sys_ni_syscall) /* was sys_ssetmask */ /* 70 */ .long SYMBOL_NAME(sys_setreuid16) .long SYMBOL_NAME(sys_setregid16) .long SYMBOL_NAME(sys_sigsuspend_wrapper) .long SYMBOL_NAME(sys_sigpending) .long SYMBOL_NAME(sys_sethostname) /* 75 */ .long SYMBOL_NAME(sys_setrlimit) - .long SYMBOL_NAME(sys_old_getrlimit) + .long SYMBOL_NAME(sys_old_getrlimit) /* used by libc4 */ .long SYMBOL_NAME(sys_getrusage) .long SYMBOL_NAME(sys_gettimeofday) .long SYMBOL_NAME(sys_settimeofday) /* 80 */ .long SYMBOL_NAME(sys_getgroups16) .long SYMBOL_NAME(sys_setgroups16) - .long SYMBOL_NAME(old_select) + .long SYMBOL_NAME(old_select) /* used by libc4 */ .long SYMBOL_NAME(sys_symlink) .long SYMBOL_NAME(sys_ni_syscall) /* was sys_lstat */ /* 85 */ .long SYMBOL_NAME(sys_readlink) .long SYMBOL_NAME(sys_uselib) .long SYMBOL_NAME(sys_swapon) .long SYMBOL_NAME(sys_reboot) - .long SYMBOL_NAME(old_readdir) -/* 90 */ .long SYMBOL_NAME(old_mmap) + .long SYMBOL_NAME(old_readdir) /* used by libc4 */ +/* 90 */ .long SYMBOL_NAME(old_mmap) /* used by libc4 */ .long SYMBOL_NAME(sys_munmap) .long SYMBOL_NAME(sys_truncate) .long SYMBOL_NAME(sys_ftruncate) @@ -236,7 +236,22 @@ .long SYMBOL_NAME(sys_mincore) /* 220 */ .long SYMBOL_NAME(sys_madvise) .long SYMBOL_NAME(sys_fcntl64) + .long SYMBOL_NAME(sys_ni_syscall) /* TUX */ + .long SYMBOL_NAME(sys_ni_syscall) /* Security */ .long SYMBOL_NAME(sys_gettid) +/* 225 */ .long SYMBOL_NAME(sys_readahead) + .long SYMBOL_NAME(sys_setxattr) + .long SYMBOL_NAME(sys_lsetxattr) + .long SYMBOL_NAME(sys_fsetxattr) + .long SYMBOL_NAME(sys_getxattr) +/* 230 */ .long SYMBOL_NAME(sys_lgetxattr) + .long SYMBOL_NAME(sys_fgetxattr) + .long SYMBOL_NAME(sys_listxattr) + .long SYMBOL_NAME(sys_llistxattr) + .long SYMBOL_NAME(sys_flistxattr) +/* 235 */ .long SYMBOL_NAME(sys_removexattr) + .long SYMBOL_NAME(sys_lremovexattr) + .long SYMBOL_NAME(sys_fremovexattr) .long SYMBOL_NAME(sys_tkill) __syscall_end: diff -Nru a/arch/arm/kernel/debug.S b/arch/arm/kernel/debug.S --- a/arch/arm/kernel/debug.S Thu Mar 7 18:17:41 2002 +++ b/arch/arm/kernel/debug.S Thu Mar 7 18:17:41 2002 @@ -164,28 +164,49 @@ .endm #elif defined(CONFIG_ARCH_SA1100) + .macro addruart,rx mrc p15, 0, \rx, c1, c0 tst \rx, #1 @ MMU enabled? moveq \rx, #0x80000000 @ physical base address movne \rx, #0xf8000000 @ virtual address - @add \rx, \rx, #0x00050000 @ Ser3 - add \rx, \rx, #0x00010000 @ Ser1 + + @ We probe for the active serial port here, coherently with + @ the comment in include/asm-arm/arch-sa1100/uncompress.h. + @ We assume r1 can be clobbered. + + @ see if Ser3 is active + add \rx, \rx, #0x00050000 + ldr r1, [\rx, #UTCR3] + tst r1, #UTCR3_TXE + + @ if Ser3 is inactive, then try Ser1 + addeq \rx, \rx, #(0x00010000 - 0x00050000) + ldreq r1, [\rx, #UTCR3] + tsteq r1, #UTCR3_TXE + + @ if Ser1 is inactive, then try Ser2 + addeq \rx, \rx, #(0x00030000 - 0x00010000) + ldreq r1, [\rx, #UTCR3] + tsteq r1, #UTCR3_TXE + + @ if all ports are inactive, then there is nothing we can do + moveq pc, lr .endm .macro senduart,rd,rx - str \rd, [\rx, #0x14] @ UTDR + str \rd, [\rx, #UTDR] .endm .macro waituart,rd,rx -1001: ldr \rd, [\rx, #0x20] @ UTSR1 - tst \rd, #1 << 2 @ UTSR1_TNF +1001: ldr \rd, [\rx, #UTSR1] + tst \rd, #UTSR1_TNF beq 1001b .endm .macro busyuart,rd,rx -1001: ldr \rd, [\rx, #0x20] @ UTSR1 - tst \rd, #1 << 0 @ UTSR1_TBY +1001: ldr \rd, [\rx, #UTSR1] + tst \rd, #UTSR1_TBY bne 1001b .endm diff -Nru a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c --- a/arch/arm/kernel/ecard.c Thu Mar 7 18:17:46 2002 +++ b/arch/arm/kernel/ecard.c Thu Mar 7 18:17:46 2002 @@ -91,17 +91,8 @@ ecard_loader_reset(volatile unsigned char *pa, loader_t loader); asmlinkage extern int ecard_loader_read(int off, volatile unsigned char *pa, loader_t loader); -extern int setup_arm_irq(int, struct irqaction *); -extern void do_ecard_IRQ(int, struct pt_regs *); -static void -ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs); - -static struct irqaction irqexpansioncard = { - ecard_irq_noexpmask, SA_INTERRUPT, 0, "expansion cards", NULL, NULL -}; - static inline unsigned short ecard_getu16(unsigned char *v) { @@ -558,7 +549,7 @@ * * They are not meant to be called directly, but via enable/disable_irq. */ -static void ecard_enableirq(unsigned int irqnr) +static void ecard_irq_mask(unsigned int irqnr) { ecard_t *ec = slot_to_ecard(irqnr - 32); @@ -574,7 +565,7 @@ } } -static void ecard_disableirq(unsigned int irqnr) +static void ecard_irq_unmask(unsigned int irqnr) { ecard_t *ec = slot_to_ecard(irqnr - 32); @@ -587,6 +578,12 @@ } } +static struct irqchip ecard_chip = { + ack: ecard_irq_mask, + mask: ecard_irq_mask, + unmask: ecard_irq_unmask, +}; + void ecard_enablefiq(unsigned int fiqnr) { ecard_t *ec = slot_to_ecard(fiqnr); @@ -632,8 +629,7 @@ ec->irqaddr, ec->irqmask, *ec->irqaddr); } -static void -ecard_check_lockup(void) +static void ecard_check_lockup(struct irqdesc *desc) { static int last, lockup; ecard_t *ec; @@ -653,7 +649,7 @@ printk(KERN_ERR "\nInterrupt lockup detected - " "disabling all expansion card interrupts\n"); - disable_irq(IRQ_EXPANSIONCARD); + desc->chip->mask(IRQ_EXPANSIONCARD); printk("Expansion card IRQ state:\n"); @@ -674,11 +670,12 @@ } static void -ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs) +ecard_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) { ecard_t *ec; int called = 0; + desc->chip->mask(irq); for (ec = cards; ec; ec = ec->next) { int pending; @@ -691,14 +688,15 @@ pending = ecard_default_ops.irqpending(ec); if (pending) { - do_ecard_IRQ(ec->irq, regs); + struct irqdesc *d = irq_desc + ec->irq; + d->handle(ec->irq, d, regs); called ++; } } - cli(); + desc->chip->unmask(irq); if (called == 0) - ecard_check_lockup(); + ecard_check_lockup(desc); } #ifdef HAS_EXPMASK @@ -714,20 +712,18 @@ }; static void -ecard_irq_expmask(int intr_no, void *dev_id, struct pt_regs *regs) +ecard_irqexp_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) { const unsigned int statusmask = 15; unsigned int status; status = __raw_readb(EXPMASK_STATUS) & statusmask; if (status) { - unsigned int slot; - ecard_t *ec; -again: - slot = first_set[status]; - ec = slot_to_ecard(slot); + unsigned int slot = first_set[status]; + ecard_t *ec = slot_to_ecard(slot); + if (ec->claimed) { - unsigned int oldexpmask; + struct irqdesc *d = irqdesc + ec->irq; /* * this ugly code is so that we can operate a * prioritorising system: @@ -740,17 +736,7 @@ * Serial cards should go in 0/1, ethernet/scsi in 2/3 * otherwise you will lose serial data at high speeds! */ - oldexpmask = have_expmask; - have_expmask &= priority_masks[slot]; - __raw_writeb(have_expmask, EXPMASK_ENABLE); - sti(); - do_ecard_IRQ(ec->irq, regs); - cli(); - have_expmask = oldexpmask; - __raw_writeb(have_expmask, EXPMASK_ENABLE); - status = __raw_readb(EXPMASK_STATUS) & statusmask; - if (status) - goto again; + d->handle(ec->irq, d, regs); } else { printk(KERN_WARNING "card%d: interrupt from unclaimed " "card???\n", slot); @@ -761,8 +747,7 @@ printk(KERN_WARNING "Wild interrupt from backplane (masks)\n"); } -static void __init -ecard_probeirqhw(void) +static int __init ecard_probeirqhw(void) { ecard_t *ec; int found; @@ -772,24 +757,24 @@ found = (__raw_readb(EXPMASK_STATUS) & 15) == 0; __raw_writeb(0xff, EXPMASK_ENABLE); - if (!found) - return; - - printk(KERN_DEBUG "Expansion card interrupt " - "management hardware found\n"); + if (found) { + printk(KERN_DEBUG "Expansion card interrupt " + "management hardware found\n"); - irqexpansioncard.handler = ecard_irq_expmask; + /* for each card present, set a bit to '1' */ + have_expmask = 0x80000000; - /* for each card present, set a bit to '1' */ - have_expmask = 0x80000000; + for (ec = cards; ec; ec = ec->next) + have_expmask |= 1 << ec->slot_no; - for (ec = cards; ec; ec = ec->next) - have_expmask |= 1 << ec->slot_no; + __raw_writeb(have_expmask, EXPMASK_ENABLE); + } - __raw_writeb(have_expmask, EXPMASK_ENABLE); + return found; } #else -#define ecard_probeirqhw() +#define ecard_irqexp_handler NULL +#define ecard_probeirqhw() (0) #endif #ifndef IO_EC_MEMC8_BASE @@ -977,10 +962,9 @@ * hook the interrupt handlers */ if (ec->irq != 0 && ec->irq >= 32) { - irq_desc[ec->irq].mask_ack = ecard_disableirq; - irq_desc[ec->irq].mask = ecard_disableirq; - irq_desc[ec->irq].unmask = ecard_enableirq; - irq_desc[ec->irq].valid = 1; + set_irq_chip(ec->irq, &ecard_chip); + set_irq_handler(ec->irq, do_level_IRQ); + set_irq_flags(ec->irq, IRQF_VALID); } #ifdef CONFIG_ARCH_RPC @@ -1042,21 +1026,6 @@ return finding_pos; } -static void __init ecard_free_all(void) -{ - ecard_t *ec, *ecn; - - for (ec = cards; ec; ec = ecn) { - ecn = ec->next; - - kfree(ec); - } - - cards = NULL; - - memset(slot_to_expcard, 0, sizeof(slot_to_expcard)); -} - /* * Initialise the expansion card system. * Locate all hardware - interrupt management and @@ -1064,7 +1033,7 @@ */ void __init ecard_init(void) { - int slot; + int slot, irqhw; /* * Register our reboot notifier @@ -1086,13 +1055,10 @@ ecard_probe(8, ECARD_IOC); #endif - ecard_probeirqhw(); + irqhw = ecard_probeirqhw(); - if (setup_arm_irq(IRQ_EXPANSIONCARD, &irqexpansioncard)) { - printk(KERN_ERR "Unable to claim IRQ%d for expansion cards\n", - IRQ_EXPANSIONCARD); - ecard_free_all(); - } + set_irq_chained_handler(IRQ_EXPANSIONCARD, + irqhw ? ecard_irqexp_handler : ecard_irq_handler); ecard_proc_init(); } diff -Nru a/arch/arm/kernel/entry-armo.S b/arch/arm/kernel/entry-armo.S --- a/arch/arm/kernel/entry-armo.S Thu Mar 7 18:17:40 2002 +++ b/arch/arm/kernel/entry-armo.S Thu Mar 7 18:17:40 2002 @@ -354,7 +354,7 @@ @ adr lr, 1b orr lr, lr, #0x08000003 @ Force SVC - bne do_IRQ + bne asm_do_IRQ mov why, #0 get_current_task r5 @@ -377,7 +377,7 @@ @ adr lr, 1b orr lr, lr, #0x08000003 @ Force SVC - bne do_IRQ @ Returns to 1b + bne asm_do_IRQ @ Returns to 1b SVC_RESTORE_ALL __irq_invalid: mov r0, sp diff -Nru a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S --- a/arch/arm/kernel/entry-armv.S Thu Mar 7 18:17:41 2002 +++ b/arch/arm/kernel/entry-armv.S Thu Mar 7 18:17:41 2002 @@ -15,6 +15,7 @@ */ #include #include "entry-header.S" +#include #ifdef IOC_BASE @@ -690,8 +691,7 @@ msr cpsr_c, r9 mov r2, sp bl SYMBOL_NAME(do_DataAbort) - mov r0, #PSR_I_BIT | MODE_SVC - msr cpsr_c, r0 + set_cpsr_c r0, #PSR_I_BIT | MODE_SVC ldr r0, [sp, #S_PSR] msr spsr, r0 ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr @@ -705,17 +705,53 @@ add r4, sp, #S_SP mov r6, lr stmia r4, {r5, r6, r7, r8, r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro +#ifdef CONFIG_PREEMPT + get_thread_info r8 + ldr r9, [r8, #TI_PREEMPT] @ get preempt count + add r7, r9, #1 @ increment it + str r7, [r8, #TI_PREEMPT] +#endif 1: get_irqnr_and_base r0, r6, r5, lr movne r1, sp @ @ routine called with r0 = irq number, r1 = struct pt_regs * @ adrsvc ne, lr, 1b - bne do_IRQ + bne asm_do_IRQ +#ifdef CONFIG_PREEMPT + ldr r0, [r8, #TI_FLAGS] @ get flags + tst r0, #_TIF_NEED_RESCHED + ldrne r6, .LCirq_stat + blne svc_preempt +preempt_return: + ldr r0, [r8, #TI_PREEMPT] @ read preempt value + teq r0, r7 + strne r0, [r0, -r0] @ bug() + str r9, [r8, #TI_PREEMPT] @ restore preempt count +#endif ldr r0, [sp, #S_PSR] @ irqs are already disabled msr spsr, r0 ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr +#ifdef CONFIG_PREEMPT +svc_preempt: teq r9, #0 @ was preempt count = 0 + movne pc, lr @ no + ldr r0, [r6, #4] @ local_irq_count + ldr r1, [r6, #8] @ local_bh_count + adds r0, r0, r1 + movne pc, lr + ldr r1, [r8, #TI_TASK] + set_cpsr_c r2, #MODE_SVC @ enable IRQs + str r0, [r1, #0] @ current->state = TASK_RUNNING +1: bl SYMBOL_NAME(schedule) + set_cpsr_c r0, #PSR_I_BIT | MODE_SVC @ disable IRQs + ldr r0, [r8, #TI_FLAGS] + tst r0, #_TIF_NEED_RESCHED + beq preempt_return + set_cpsr_c r0, #MODE_SVC @ enable IRQs + b 1b +#endif + .align 5 __und_svc: sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ save r0 - r12 @@ -733,8 +769,7 @@ mov r1, sp @ struct pt_regs *regs bl SYMBOL_NAME(do_undefinstr) -1: mov r0, #PSR_I_BIT | MODE_SVC - msr cpsr_c, r0 +1: set_cpsr_c r0, #PSR_I_BIT | MODE_SVC ldr lr, [sp, #S_PSR] @ Get SVC cpsr msr spsr, lr ldmia sp, {r0 - pc}^ @ Restore SVC registers @@ -755,8 +790,7 @@ mov r0, r2 @ address (pc) mov r1, sp @ regs bl SYMBOL_NAME(do_PrefetchAbort) @ call abort handler - mov r0, #PSR_I_BIT | MODE_SVC - msr cpsr_c, r0 + set_cpsr_c r0, #PSR_I_BIT | MODE_SVC ldr r0, [sp, #S_PSR] msr spsr, r0 ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr @@ -769,6 +803,9 @@ .LCprocfns: .word SYMBOL_NAME(processor) #endif .LCfp: .word SYMBOL_NAME(fp_enter) +#ifdef CONFIG_PREEMPT +.LCirq_stat: .word SYMBOL_NAME(irq_stat) +#endif irq_prio_table @@ -793,8 +830,7 @@ #else bl cpu_data_abort #endif - mov r2, #MODE_SVC - msr cpsr_c, r2 @ Enable interrupts + set_cpsr_c r2, #MODE_SVC @ Enable interrupts mov r2, sp adrsvc al, lr, ret_from_exception b SYMBOL_NAME(do_DataAbort) @@ -809,15 +845,29 @@ stmdb r8, {sp, lr}^ alignment_trap r4, r7, __temp_irq zero_fp +#ifdef CONFIG_PREEMPT + get_thread_info r8 + ldr r9, [r8, #TI_PREEMPT] @ get preempt count + add r7, r9, #1 @ increment it + str r7, [r8, #TI_PREEMPT] +#endif 1: get_irqnr_and_base r0, r6, r5, lr movne r1, sp adrsvc ne, lr, 1b @ @ routine called with r0 = irq number, r1 = struct pt_regs * @ - bne do_IRQ + bne asm_do_IRQ +#ifdef CONFIG_PREEMPT + ldr r0, [r8, #TI_PREEMPT] + teq r0, r7 + strne r0, [r0, -r0] + str r9, [r8, #TI_PREEMPT] + mov tsk, r8 +#else + get_thread_info tsk +#endif mov why, #0 - get_current_task tsk b ret_to_user .align 5 @@ -833,15 +883,15 @@ adrsvc al, r9, ret_from_exception @ r9 = normal FP return adrsvc al, lr, fpundefinstr @ lr = undefined instr return -call_fpe: get_current_task r10 +call_fpe: get_thread_info r10 @ get current thread + ldr r4, [r10, #TI_TASK] @ get current task mov r8, #1 - strb r8, [r10, #TSK_USED_MATH] @ set current->used_math + strb r8, [r4, #TSK_USED_MATH] @ set current->used_math ldr r4, .LCfp - add r10, r10, #TSS_FPESAVE @ r10 = workspace + add r10, r10, #TI_FPSTATE @ r10 = workspace ldr pc, [r4] @ Call FP module USR entry point -fpundefinstr: mov r0, #MODE_SVC - msr cpsr_c, r0 @ Enable interrupts +fpundefinstr: set_cpsr_c r0, #MODE_SVC @ Enable interrupts mov r0, lr mov r1, sp adrsvc al, lr, ret_from_exception @@ -857,8 +907,7 @@ stmdb r8, {sp, lr}^ @ Save sp_usr lr_usr alignment_trap r4, r7, __temp_abt zero_fp - mov r0, #MODE_SVC - msr cpsr_c, r0 @ Enable interrupts + set_cpsr_c r0, #MODE_SVC @ Enable interrupts mov r0, r5 @ address (pc) mov r1, sp @ regs bl SYMBOL_NAME(do_PrefetchAbort) @ call abort handler @@ -867,7 +916,7 @@ * This is the return code to user mode for abort handlers */ ENTRY(ret_from_exception) - get_current_task tsk + get_thread_info tsk mov why, #0 b ret_to_user @@ -877,16 +926,16 @@ .text /* * Register switch for ARMv3 and ARMv4 processors - * r0 = previous, r1 = next, return previous. + * r0 = previous thread_info, r1 = next thread_info, return previous. * previous and next are guaranteed not to be the same. */ ENTRY(__switch_to) stmfd sp!, {r4 - sl, fp, lr} @ Store most regs on stack mrs ip, cpsr str ip, [sp, #-4]! @ Save cpsr_SVC - str sp, [r0, #TSS_SAVE] @ Save sp_SVC - ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC - ldr r2, [r1, #TSS_DOMAIN] + str sp, [r0, #TI_CPU_SAVE] @ Save sp_SVC + ldr sp, [r1, #TI_CPU_SAVE] @ Get saved sp_SVC + ldr r2, [r1, #TI_CPU_DOMAIN] ldr ip, [sp], #4 mcr p15, 0, r2, c3, c0 @ Set domain register msr spsr, ip @ Save tasks CPSR into SPSR for this return diff -Nru a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S --- a/arch/arm/kernel/entry-common.S Thu Mar 7 18:17:37 2002 +++ b/arch/arm/kernel/entry-common.S Thu Mar 7 18:17:37 2002 @@ -9,6 +9,7 @@ */ #include #include "entry-header.S" +#include /* * We rely on the fact that R0 is at the bottom of the stack (due to @@ -34,55 +35,55 @@ * stack. */ ret_fast_syscall: -#error ldr r1, [tsk, #TSK_NEED_RESCHED] -#error ldr r2, [tsk, #TSK_SIGPENDING] - teq r1, #0 @ need_resched || sigpending - teqeq r2, #0 - bne slow + set_cpsr_c r1, #PSR_I_BIT | MODE_SVC @ disable interrupts + ldr r1, [tsk, #TI_FLAGS] + tst r1, #_TIF_WORK_MASK + bne ret_fast_work fast_restore_user_regs /* * Ok, we need to do extra processing, enter the slow path. */ -slow: str r0, [sp, #S_R0+S_OFF]! @ returned r0 - b 1f +ret_fast_work: + str r0, [sp, #S_R0+S_OFF]! @ returned r0 + b work_pending +work_resched: + bl SYMBOL_NAME(schedule) /* * "slow" syscall return path. "why" tells us if this was a real syscall. */ -reschedule: - bl SYMBOL_NAME(schedule) ENTRY(ret_to_user) ret_slow_syscall: -#error ldr r1, [tsk, #TSK_NEED_RESCHED] -#error ldr r2, [tsk, #TSK_SIGPENDING] -1: teq r1, #0 @ need_resched => schedule() - bne reschedule - teq r2, #0 @ sigpending => do_signal() - blne __do_signal + set_cpsr_c r1, #PSR_I_BIT | MODE_SVC @ disable interrupts + ldr r1, [tsk, #TI_FLAGS] + tst r1, #_TIF_WORK_MASK + beq no_work_pending +work_pending: + tst r1, #_TIF_NEED_RESCHED + bne work_resched + tst r1, #_TIF_NOTIFY_RESUME | _TIF_SIGPENDING + blne __do_notify_resume +no_work_pending: restore_user_regs -__do_signal: - mov r0, #0 @ NULL 'oldset' - mov r1, sp @ 'regs' +__do_notify_resume: + mov r0, sp @ 'regs' mov r2, why @ 'syscall' -#error b SYMBOL_NAME(do_signal) @ note the bl above sets lr + b SYMBOL_NAME(do_notify_resume) @ note the bl above sets lr /* - * This is how we return from a fork. __switch_to will be calling us - * with r0 pointing at the previous task that was running (ready for - * calling schedule_tail). + * This is how we return from a fork. */ ENTRY(ret_from_fork) - bl SYMBOL_NAME(schedule_tail) - get_current_task tsk - ldr ip, [tsk, #TSK_PTRACE] @ check for syscall tracing + get_thread_info tsk + ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing mov why, #1 - tst ip, #PT_TRACESYS @ are we tracing syscalls? + tst ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls? beq ret_slow_syscall mov r1, sp mov r0, #1 @ trace exit [IP = 1] -#error bl SYMBOL_NAME(syscall_trace) + bl SYMBOL_NAME(syscall_trace) b ret_slow_syscall @@ -134,12 +135,12 @@ str r4, [sp, #-S_OFF]! @ push fifth arg - get_current_task tsk - ldr ip, [tsk, #TSK_PTRACE] @ check for syscall tracing + get_thread_info tsk + ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing bic scno, scno, #0xff000000 @ mask off SWI op-code eor scno, scno, #OS_NUMBER << 20 @ check OS number adr tbl, sys_call_table @ load syscall table pointer - tst ip, #PT_TRACESYS @ are we tracing syscalls? + tst ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls? bne __sys_trace adrsvc al, lr, ret_fast_syscall @ return address @@ -160,7 +161,7 @@ __sys_trace: add r1, sp, #S_OFF mov r0, #0 @ trace entry [IP = 0] -#error bl SYMBOL_NAME(syscall_trace) + bl SYMBOL_NAME(syscall_trace) adrsvc al, lr, __sys_trace_return @ return address add r1, sp, #S_R0 + S_OFF @ pointer to regs @@ -173,7 +174,7 @@ str r0, [sp, #S_R0 + S_OFF]! @ save returned r0 mov r1, sp mov r0, #1 @ trace exit [IP = 1] -#error bl SYMBOL_NAME(syscall_trace) + bl SYMBOL_NAME(syscall_trace) b ret_slow_syscall .align 5 diff -Nru a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S --- a/arch/arm/kernel/entry-header.S Thu Mar 7 18:17:43 2002 +++ b/arch/arm/kernel/entry-header.S Thu Mar 7 18:17:43 2002 @@ -113,7 +113,7 @@ msr cpsr_c, \temp .endm - .macro get_current_task, rd + .macro get_thread_info, rd mov \rd, sp, lsr #13 mov \rd, \rd, lsl #13 .endm @@ -171,7 +171,7 @@ .macro initialise_traps_extra .endm - .macro get_current_task, rd + .macro get_thread_info, rd mov \rd, sp, lsr #13 mov \rd, \rd, lsl #13 .endm @@ -197,10 +197,10 @@ * * We must set at least "tsk" and "why" when calling ret_with_reschedule. */ -scno .req r7 @ syscall number -tbl .req r8 @ syscall table pointer -why .req r8 @ Linux syscall (!= 0) -tsk .req r9 @ current task +scno .req r7 @ syscall number +tbl .req r8 @ syscall table pointer +why .req r8 @ Linux syscall (!= 0) +tsk .req r9 @ current thread_info /* * Get the system call number. @@ -214,6 +214,15 @@ #else mask_pc lr, lr ldr scno, [lr, #-4] @ get SWI instruction +#endif + .endm + + .macro set_cpsr_c, reg, mode +#if 1 + mov \reg, \mode + msr cpsr_c, \reg +#else + msr cpsr_c, \mode #endif .endm diff -Nru a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S --- a/arch/arm/kernel/head.S Thu Mar 7 18:17:36 2002 +++ b/arch/arm/kernel/head.S Thu Mar 7 18:17:36 2002 @@ -14,6 +14,7 @@ #include #include +#include #include #define K(a,b,c) ((a) << 24 | (b) << 12 | (c)) @@ -126,7 +127,7 @@ mov r1, #MACH_TYPE_L7200 #endif - mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode + mov r0, #PSR_F_BIT | PSR_I_BIT | MODE_SVC @ make sure svc mode msr cpsr_c, r0 @ and all irqs disabled bl __lookup_processor_type teq r10, #0 @ invalid processor? @@ -149,7 +150,7 @@ .long SYMBOL_NAME(processor_id) .long SYMBOL_NAME(__machine_arch_type) .long SYMBOL_NAME(cr_alignment) - .long SYMBOL_NAME(init_task_union)+8192 + .long SYMBOL_NAME(init_thread_union)+8192 .type __ret, %function __ret: ldr lr, __switch_data @@ -374,7 +375,7 @@ and r6, r6, r9 @ mask wanted bits teq r5, r6 moveq pc, lr - add r10, r10, #36 @ sizeof(proc_info_list) + add r10, r10, #PROC_INFO_SZ @ sizeof(proc_info_list) cmp r10, r7 blt 1b mov r10, #0 @ unknown processor diff -Nru a/arch/arm/kernel/init_task.c b/arch/arm/kernel/init_task.c --- a/arch/arm/kernel/init_task.c Thu Mar 7 18:17:46 2002 +++ b/arch/arm/kernel/init_task.c Thu Mar 7 18:17:46 2002 @@ -16,7 +16,7 @@ struct mm_struct init_mm = INIT_MM(init_mm); /* - * Initial task structure. + * Initial thread structure. * * We need to make sure that this is 8192-byte aligned due to the * way process stacks are handled. This is done by making sure @@ -25,5 +25,13 @@ * * The things we do for performance.. */ -union task_union init_task_union __attribute__((__section__(".init.task"))) = - { INIT_TASK(init_task_union.task) }; +union thread_union init_thread_union + __attribute__((__section__(".init.task"))) = + { INIT_THREAD_INFO(init_task) }; + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ +struct task_struct init_task = INIT_TASK(init_task); diff -Nru a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c --- a/arch/arm/kernel/irq.c Thu Mar 7 18:17:36 2002 +++ b/arch/arm/kernel/irq.c Thu Mar 7 18:17:36 2002 @@ -34,8 +34,6 @@ #include #include -#include /* pick up fixup_irq definition */ - /* * Maximum IRQ count. Currently, this is arbitary. However, it should * not be set too low to prevent false triggering. Conversely, if it @@ -54,25 +52,44 @@ /* * Dummy mask/unmask handler */ -static void dummy_mask_unmask_irq(unsigned int irq) +void dummy_mask_unmask_irq(unsigned int irq) { } +void do_bad_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) +{ + irq_err_count += 1; + printk(KERN_ERR "IRQ: spurious interrupt %d\n", irq); +} + +static struct irqchip bad_chip = { + ack: dummy_mask_unmask_irq, + mask: dummy_mask_unmask_irq, + unmask: dummy_mask_unmask_irq, +}; + +static struct irqdesc bad_irq_desc = { + chip: &bad_chip, + handle: do_bad_IRQ, + depth: 1, +}; + /** * disable_irq - disable an irq and wait for completion * @irq: Interrupt to disable * - * Disable the selected interrupt line. + * Disable the selected interrupt line. We do this lazily. * - * This function may be called - with care - from IRQ context. + * This function may be called from IRQ context. */ void disable_irq(unsigned int irq) { + struct irqdesc *desc = irq_desc + irq; unsigned long flags; spin_lock_irqsave(&irq_controller_lock, flags); - irq_desc[irq].enabled = 0; - irq_desc[irq].mask(irq); + if (!desc->depth++) + desc->enabled = 0; spin_unlock_irqrestore(&irq_controller_lock, flags); } @@ -80,19 +97,35 @@ * enable_irq - enable interrupt handling on an irq * @irq: Interrupt to enable * - * Re-enables the processing of interrupts on this IRQ line + * Re-enables the processing of interrupts on this IRQ line. + * Note that this may call the interrupt handler, so you may + * get unexpected results if you hold IRQs disabled. * * This function may be called from IRQ context. */ void enable_irq(unsigned int irq) { + struct irqdesc *desc = irq_desc + irq; unsigned long flags; + int pending = 0; spin_lock_irqsave(&irq_controller_lock, flags); - irq_desc[irq].probing = 0; - irq_desc[irq].triggered = 0; - irq_desc[irq].enabled = 1; - irq_desc[irq].unmask(irq); + if (unlikely(!desc->depth)) { + printk("enable_irq(%u) unbalanced from %p\n", irq, + __builtin_return_address(0)); + } else if (!--desc->depth) { + desc->probing = 0; + desc->enabled = 1; + desc->chip->unmask(irq); + pending = desc->pending; + desc->pending = 0; + /* + * If the interrupt was waiting to be processed, + * retrigger it. + */ + if (pending) + desc->chip->rerun(irq); + } spin_unlock_irqrestore(&irq_controller_lock, flags); } @@ -128,7 +161,7 @@ * a large number if IRQs to appear in the same jiffie with the * same instruction pointer (or within 2 instructions). */ -static void check_irq_lock(struct irqdesc *desc, int irq, struct pt_regs *regs) +static int check_irq_lock(struct irqdesc *desc, int irq, struct pt_regs *regs) { unsigned long instr_ptr = instruction_pointer(regs); @@ -138,123 +171,283 @@ if (desc->lck_cnt > MAX_IRQ_CNT) { printk(KERN_ERR "IRQ LOCK: IRQ%d is locking the system, disabled\n", irq); - disable_irq(irq); + return 1; } } else { desc->lck_cnt = 0; desc->lck_pc = instruction_pointer(regs); desc->lck_jif = jiffies; } + return 0; +} + +static void +__do_irq(unsigned int irq, struct irqaction *action, struct pt_regs *regs) +{ + unsigned int status; + + spin_unlock(&irq_controller_lock); + + if (!(action->flags & SA_INTERRUPT)) + __sti(); + + status = 0; + 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(); + spin_lock(&irq_controller_lock); } /* - * do_IRQ handles all normal device IRQ's + * This is for software-decoded IRQs. The caller is expected to + * handle the ack, clear, mask and unmask issues. */ -asmlinkage void do_IRQ(int irq, struct pt_regs * regs) +void +do_simple_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) { - struct irqdesc * desc; - struct irqaction * action; - int cpu; + struct irqaction *action; + const int cpu = smp_processor_id(); + + desc->triggered = 1; + + irq_enter(cpu, irq); + kstat.irqs[cpu][irq]++; + + action = desc->action; + if (action) + __do_irq(irq, desc->action, regs); + + irq_exit(cpu, irq); +} + +/* + * Most edge-triggered IRQ implementations seem to take a broken + * approach to this. Hence the complexity. + */ +void +do_edge_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) +{ + const int cpu = smp_processor_id(); - irq = fixup_irq(irq); + desc->triggered = 1; /* - * Some hardware gives randomly wrong interrupts. Rather - * than crashing, do something sensible. + * If we're currently running this IRQ, or its disabled, + * we shouldn't process the IRQ. Instead, turn on the + * hardware masks. */ - if (irq >= NR_IRQS) - goto bad_irq; + if (unlikely(desc->running || !desc->enabled)) + goto running; - desc = irq_desc + irq; + /* + * Acknowledge and clear the IRQ, but don't mask it. + */ + desc->chip->ack(irq); - spin_lock(&irq_controller_lock); - desc->mask_ack(irq); - spin_unlock(&irq_controller_lock); + /* + * Mark the IRQ currently in progress. + */ + desc->running = 1; - cpu = smp_processor_id(); irq_enter(cpu, irq); kstat.irqs[cpu][irq]++; - desc->triggered = 1; - /* Return with this interrupt masked if no action */ - action = desc->action; + do { + struct irqaction *action; - if (action) { - int status = 0; + action = desc->action; + if (!action) + break; - if (desc->nomask) { - spin_lock(&irq_controller_lock); - desc->unmask(irq); - spin_unlock(&irq_controller_lock); + if (desc->pending && desc->enabled) { + desc->pending = 0; + desc->chip->unmask(irq); } - if (!(action->flags & SA_INTERRUPT)) - __sti(); + __do_irq(irq, action, regs); + } while (desc->pending); - 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(); - - if (!desc->nomask && desc->enabled) { - spin_lock(&irq_controller_lock); - desc->unmask(irq); - spin_unlock(&irq_controller_lock); + irq_exit(cpu, irq); + + desc->running = 0; + + /* + * If we were disabled or freed, shut down the handler. + */ + if (likely(desc->action && !check_irq_lock(desc, irq, regs))) + return; + + running: + /* + * We got another IRQ while this one was masked or + * currently running. Delay it. + */ + desc->pending = 1; + desc->chip->mask(irq); + desc->chip->ack(irq); +} + +/* + * Level-based IRQ handler. Nice and simple. + */ +void +do_level_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) +{ + struct irqaction *action; + const int cpu = smp_processor_id(); + + desc->triggered = 1; + + /* + * Acknowledge, clear _AND_ disable the interrupt. + */ + desc->chip->ack(irq); + + if (likely(desc->enabled)) { + irq_enter(cpu, irq); + kstat.irqs[cpu][irq]++; + + /* + * Return with this interrupt masked if no action + */ + action = desc->action; + if (action) { + __do_irq(irq, desc->action, regs); + + if (likely(desc->enabled && + !check_irq_lock(desc, irq, regs))) + desc->chip->unmask(irq); } } + irq_exit(cpu, irq); +} + +/* + * do_IRQ handles all hardware IRQ's. Decoded IRQs should not + * come via this function. Instead, they should provide their + * own 'handler' + */ +asmlinkage void asm_do_IRQ(int irq, struct pt_regs *regs) +{ + struct irqdesc *desc = irq_desc + irq; + /* - * Debug measure - hopefully we can continue if an - * IRQ lockup problem occurs... + * Some hardware gives randomly wrong interrupts. Rather + * than crashing, do something sensible. */ - check_irq_lock(desc, irq, regs); + if (irq >= NR_IRQS) + desc = &bad_irq_desc; - irq_exit(cpu, irq); + spin_lock(&irq_controller_lock); + desc->handle(irq, desc, regs); + spin_unlock(&irq_controller_lock); - if (softirq_pending(cpu)) + if (softirq_pending(smp_processor_id())) do_softirq(); - return; +} -bad_irq: - irq_err_count += 1; - printk(KERN_ERR "IRQ: spurious interrupt %d\n", irq); - return; +void __set_irq_handler(unsigned int irq, irq_handler_t handle, int is_chained) +{ + struct irqdesc *desc; + unsigned long flags; + + if (irq >= NR_IRQS) { + printk(KERN_ERR "Trying to install handler for IRQ%d\n", irq); + return; + } + + if (handle == NULL) + handle = do_bad_IRQ; + + desc = irq_desc + irq; + + if (is_chained && desc->chip == &bad_chip) + printk(KERN_WARNING "Trying to install chained handler for IRQ%d\n", irq); + + spin_lock_irqsave(&irq_controller_lock, flags); + if (handle == do_bad_IRQ) { + desc->chip->mask(irq); + desc->chip->ack(irq); + desc->depth = 1; + desc->enabled = 0; + } + desc->handle = handle; + if (handle != do_bad_IRQ && is_chained) { + desc->valid = 0; + desc->probe_ok = 0; + desc->depth = 0; + desc->chip->unmask(irq); + } + spin_unlock_irqrestore(&irq_controller_lock, flags); } -#ifdef CONFIG_ARCH_ACORN -void do_ecard_IRQ(int irq, struct pt_regs *regs) +void set_irq_chip(unsigned int irq, struct irqchip *chip) { - struct irqdesc * desc; - struct irqaction * action; - int cpu; + struct irqdesc *desc; + unsigned long flags; + + if (irq >= NR_IRQS) { + printk(KERN_ERR "Trying to install chip for IRQ%d\n", irq); + return; + } + + if (chip == NULL) + chip = &bad_chip; desc = irq_desc + irq; + spin_lock_irqsave(&irq_controller_lock, flags); + desc->chip = chip; + spin_unlock_irqrestore(&irq_controller_lock, flags); +} - cpu = smp_processor_id(); - kstat.irqs[cpu][irq]++; - desc->triggered = 1; +int set_irq_type(unsigned int irq, unsigned int type) +{ + struct irqdesc *desc; + unsigned long flags; + int ret = -ENXIO; - action = desc->action; + if (irq >= NR_IRQS) { + printk(KERN_ERR "Trying to set irq type for IRQ%d\n", irq); + return -ENODEV; + } - if (action) { - do { - action->handler(irq, action->dev_id, regs); - action = action->next; - } while (action); - } else { - spin_lock(&irq_controller_lock); - desc->mask(irq); - spin_unlock(&irq_controller_lock); + desc = irq_desc + irq; + if (desc->chip->type) { + spin_lock_irqsave(&irq_controller_lock, flags); + ret = desc->chip->type(irq, type); + spin_unlock_irqrestore(&irq_controller_lock, flags); } + + return ret; +} + +void set_irq_flags(unsigned int irq, unsigned int iflags) +{ + struct irqdesc *desc; + unsigned long flags; + + if (irq >= NR_IRQS) { + printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq); + return; + } + + desc = irq_desc + irq; + spin_lock_irqsave(&irq_controller_lock, flags); + desc->valid = (iflags & IRQF_VALID) != 0; + desc->probe_ok = (iflags & IRQF_PROBE) != 0; + desc->noautoenable = (iflags & IRQF_NOAUTOEN) != 0; + spin_unlock_irqrestore(&irq_controller_lock, flags); } -#endif -int setup_arm_irq(int irq, struct irqaction * new) +int setup_irq(unsigned int irq, struct irqaction *new) { int shared = 0; struct irqaction *old, **p; @@ -302,11 +495,14 @@ *p = new; if (!shared) { - desc->nomask = (new->flags & SA_IRQNOMASK) ? 1 : 0; - desc->probing = 0; + desc->probing = 0; + desc->running = 0; + desc->pending = 0; + desc->depth = 1; if (!desc->noautoenable) { + desc->depth = 0; desc->enabled = 1; - desc->unmask(irq); + desc->chip->unmask(irq); } } @@ -366,7 +562,7 @@ action->next = NULL; action->dev_id = dev_id; - retval = setup_arm_irq(irq, action); + retval = setup_irq(irq, action); if (retval) kfree(action); @@ -433,14 +629,12 @@ */ spin_lock_irq(&irq_controller_lock); for (i = 0; i < NR_IRQS; i++) { - if (!irq_desc[i].valid || - !irq_desc[i].probe_ok || - irq_desc[i].action) + if (!irq_desc[i].probe_ok || irq_desc[i].action) continue; irq_desc[i].probing = 1; irq_desc[i].triggered = 0; - irq_desc[i].unmask(i); + irq_desc[i].chip->unmask(i); irqs += 1; } spin_unlock_irq(&irq_controller_lock); @@ -456,15 +650,13 @@ */ spin_lock_irq(&irq_controller_lock); for (i = 0; i < NR_IRQS; i++) { - if (irq_desc[i].probing && - irq_desc[i].triggered) { + if (irq_desc[i].probing && irq_desc[i].triggered) { irq_desc[i].probing = 0; irqs -= 1; } } spin_unlock_irq(&irq_controller_lock); - /* now filter out any obviously spurious interrupts */ return irqs; } @@ -508,17 +700,12 @@ void __init init_IRQ(void) { + struct irqdesc *desc; extern void init_dma(void); int irq; - for (irq = 0; irq < NR_IRQS; irq++) { - irq_desc[irq].probe_ok = 0; - irq_desc[irq].valid = 0; - irq_desc[irq].noautoenable = 0; - irq_desc[irq].mask_ack = dummy_mask_unmask_irq; - irq_desc[irq].mask = dummy_mask_unmask_irq; - irq_desc[irq].unmask = dummy_mask_unmask_irq; - } + for (irq = 0, desc = irq_desc; irq < NR_IRQS; irq++, desc++) + *desc = bad_irq_desc; init_arch_irq(); init_dma(); diff -Nru a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c --- a/arch/arm/kernel/process.c Thu Mar 7 18:17:42 2002 +++ b/arch/arm/kernel/process.c Thu Mar 7 18:17:42 2002 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include /* @@ -83,9 +85,7 @@ void cpu_idle(void) { /* endless idle loop with no priority at all */ - init_idle(); - current->nice = 20; - + preempt_disable(); while (1) { void (*idle)(void) = pm_idle; if (!idle) @@ -95,9 +95,6 @@ idle(); leds_event(led_idle_end); schedule(); -#ifndef CONFIG_NO_PGT_CACHE - check_pgt_cache(); -#endif } } @@ -228,51 +225,61 @@ /* * Task structure and kernel stack allocation. */ -static struct task_struct *task_struct_head; -static unsigned int nr_task_struct; +static unsigned long *thread_info_head; +static unsigned int nr_thread_info; #ifdef CONFIG_CPU_32 #define EXTRA_TASK_STRUCT 4 +#define ll_alloc_task_struct() ((struct thread_info *) __get_free_pages(GFP_KERNEL,1)) +#define ll_free_task_struct(p) free_pages((unsigned long)(p),1) #else +extern unsigned long get_page_8k(int priority); +extern void free_page_8k(unsigned long page); + #define EXTRA_TASK_STRUCT 0 +#define ll_alloc_task_struct() ((struct task_struct *)get_page_8k(GFP_KERNEL)) +#define ll_free_task_struct(p) free_page_8k((unsigned long)(p)) #endif -struct task_struct *alloc_task_struct(void) +struct thread_info *alloc_thread_info(void) { - struct task_struct *tsk; + struct thread_info *thread = NULL; - if (EXTRA_TASK_STRUCT) - tsk = task_struct_head; - else - tsk = NULL; - - if (tsk) { - task_struct_head = tsk->next_task; - nr_task_struct -= 1; - } else - tsk = ll_alloc_task_struct(); + if (EXTRA_TASK_STRUCT) { + unsigned long *p = thread_info_head; + + if (p) { + thread_info_head = (unsigned long *)p[0]; + nr_thread_info -= 1; + } + thread = (struct thread_info *)p; + } + + if (!thread) + thread = ll_alloc_task_struct(); #ifdef CONFIG_SYSRQ /* * The stack must be cleared if you want SYSRQ-T to * give sensible stack usage information */ - if (tsk) { - char *p = (char *)tsk; + if (thread) { + char *p = (char *)thread; memzero(p+KERNEL_STACK_SIZE, KERNEL_STACK_SIZE); } #endif - return tsk; + return thread; } -void __free_task_struct(struct task_struct *p) +void free_thread_info(struct thread_info *thread) { - if (EXTRA_TASK_STRUCT && nr_task_struct < EXTRA_TASK_STRUCT) { - p->next_task = task_struct_head; - task_struct_head = p; - nr_task_struct += 1; + if (EXTRA_TASK_STRUCT && nr_thread_info < EXTRA_TASK_STRUCT) { + unsigned long *p = (unsigned long *)thread; + p[0] = (unsigned long)thread_info_head; + thread_info_head = p; + nr_thread_info += 1; } else - ll_free_task_struct(p); + ll_free_task_struct(thread); } /* @@ -284,10 +291,13 @@ void flush_thread(void) { - memset(¤t->thread.debug, 0, sizeof(struct debug_info)); - memset(¤t->thread.fpstate, 0, sizeof(union fp_state)); + struct thread_info *thread = current_thread_info(); + struct task_struct *tsk = current; + + memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); + memset(&thread->fpstate, 0, sizeof(union fp_state)); + current->used_math = 0; - current->flags &= ~PF_USEDFPU; } void release_thread(struct task_struct *dead_task) @@ -300,21 +310,19 @@ unsigned long unused, struct task_struct * p, struct pt_regs * regs) { - struct pt_regs * childregs; - struct context_save_struct * save; - - atomic_set(&p->thread.refcount, 1); + struct pt_regs *childregs; + struct cpu_context_save *save; - childregs = ((struct pt_regs *)((unsigned long)p + 8192)) - 1; + childregs = ((struct pt_regs *)((unsigned long)p->thread_info + THREAD_SIZE)) - 1; *childregs = *regs; childregs->ARM_r0 = 0; childregs->ARM_sp = esp; - save = ((struct context_save_struct *)(childregs)) - 1; + save = ((struct cpu_context_save *)(childregs)) - 1; *save = INIT_CSS; save->pc |= (unsigned long)ret_from_fork; - p->thread.save = save; + p->thread_info->cpu_context = save; return 0; } @@ -324,10 +332,13 @@ */ int dump_fpu (struct pt_regs *regs, struct user_fp *fp) { - if (current->used_math) - memcpy(fp, ¤t->thread.fpstate.soft, sizeof (*fp)); + struct thread_info *thread = current_thread_info(); + int used_math = current->used_math; + + if (used_math) + memcpy(fp, &thread->fpstate.soft, sizeof (*fp)); - return current->used_math; + return used_math; } /* @@ -405,7 +416,7 @@ return 0; stack_page = 4096 + (unsigned long)p; - fp = get_css_fp(&p->thread); + fp = thread_saved_fp(p); do { if (fp < stack_page || fp > 4092+stack_page) return 0; diff -Nru a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c --- a/arch/arm/kernel/ptrace.c Thu Mar 7 18:17:38 2002 +++ b/arch/arm/kernel/ptrace.c Thu Mar 7 18:17:38 2002 @@ -9,6 +9,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include #include #include #include @@ -33,7 +34,7 @@ /* * Breakpoint SWI instruction: SWI &9F0001 */ -#define BREAKINST 0xef9f0001 +#define BREAKINST_ARM 0xef9f0001 /* * Get the address of the live pt_regs for the specified task. @@ -72,7 +73,7 @@ newregs = *regs; newregs.uregs[offset] = data; - + if (valid_user_regs(&newregs)) { regs->uregs[offset] = data; ret = 0; @@ -182,6 +183,20 @@ return val; } +#define OP_MASK 0x01e00000 +#define OP_AND 0x00000000 +#define OP_EOR 0x00200000 +#define OP_SUB 0x00400000 +#define OP_RSB 0x00600000 +#define OP_ADD 0x00800000 +#define OP_ADC 0x00a00000 +#define OP_SBC 0x00c00000 +#define OP_RSC 0x00e00000 +#define OP_ORR 0x01800000 +#define OP_MOV 0x01a00000 +#define OP_BIC 0x01c00000 +#define OP_MVN 0x01e00000 + static unsigned long get_branch_address(struct task_struct *child, unsigned long pc, unsigned long insn) { @@ -200,21 +215,21 @@ aluop1 = ptrace_getrn(child, insn); aluop2 = ptrace_getaluop2(child, insn); - ccbit = get_stack_long(child, REG_PSR) & CC_C_BIT ? 1 : 0; + ccbit = get_stack_long(child, REG_PSR) & PSR_C_BIT ? 1 : 0; - switch (insn & 0x01e00000) { - case 0x00000000: alt = aluop1 & aluop2; break; - case 0x00200000: alt = aluop1 ^ aluop2; break; - case 0x00400000: alt = aluop1 - aluop2; break; - case 0x00600000: alt = aluop2 - aluop1; break; - case 0x00800000: alt = aluop1 + aluop2; break; - case 0x00a00000: alt = aluop1 + aluop2 + ccbit; break; - case 0x00c00000: alt = aluop1 - aluop2 + ccbit; break; - case 0x00e00000: alt = aluop2 - aluop1 + ccbit; break; - case 0x01800000: alt = aluop1 | aluop2; break; - case 0x01a00000: alt = aluop2; break; - case 0x01c00000: alt = aluop1 & ~aluop2; break; - case 0x01e00000: alt = ~aluop2; break; + switch (insn & OP_MASK) { + case OP_AND: alt = aluop1 & aluop2; break; + case OP_EOR: alt = aluop1 ^ aluop2; break; + case OP_SUB: alt = aluop1 - aluop2; break; + case OP_RSB: alt = aluop2 - aluop1; break; + case OP_ADD: alt = aluop1 + aluop2; break; + case OP_ADC: alt = aluop1 + aluop2 + ccbit; break; + case OP_SBC: alt = aluop1 - aluop2 + ccbit; break; + case OP_RSC: alt = aluop2 - aluop1 + ccbit; break; + case OP_ORR: alt = aluop1 | aluop2; break; + case OP_MOV: alt = aluop2; break; + case OP_BIC: alt = aluop1 & ~aluop2; break; + case OP_MVN: alt = ~aluop2; break; } break; } @@ -275,7 +290,7 @@ base = ptrace_getrn(child, insn); if (read_tsk_long(child, base + nr_regs, &alt) == 0) - alt = pc_pointer (alt); + alt = pc_pointer(alt); break; } break; @@ -312,7 +327,7 @@ if (nr < 2) { res = read_tsk_long(child, addr, &dbg->bp[nr].insn); if (res == 0) - res = write_tsk_long(child, addr, BREAKINST); + res = write_tsk_long(child, addr, BREAKINST_ARM); if (res == 0) { dbg->bp[nr].address = addr; @@ -381,7 +396,7 @@ read_tsk_long(child, dbg->bp[i].address, &tmp); write_tsk_long(child, dbg->bp[i].address, dbg->bp[i].insn); - if (tmp != BREAKINST) + if (tmp != BREAKINST_ARM) printk(KERN_ERR "ptrace_cancel_bpt: weirdness\n"); } } @@ -455,9 +470,9 @@ if ((unsigned long) data > _NSIG) break; if (request == PTRACE_SYSCALL) - child->ptrace |= PT_TRACESYS; + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); else - child->ptrace &= ~PT_TRACESYS; + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); child->exit_code = data; /* make sure single-step breakpoint is gone. */ __ptrace_cancel_bpt(child); @@ -490,7 +505,7 @@ if ((unsigned long) data > _NSIG) break; child->thread.debug.nsaved = -1; - child->ptrace &= ~PT_TRACESYS; + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); child->exit_code = data; /* give it a chance to run. */ wake_up_process(child); @@ -547,7 +562,7 @@ break; /* we should check child->used_math here */ - ret = __copy_to_user((void *)data, &child->thread.fpstate, + ret = __copy_to_user((void *)data, &child->thread_info->fpstate, sizeof(struct user_fp)) ? -EFAULT : 0; break; @@ -560,7 +575,7 @@ break; child->used_math = 1; - ret = __copy_from_user(&child->thread.fpstate, (void *)data, + ret = __copy_from_user(&child->thread_info->fpstate, (void *)data, sizeof(struct user_fp)) ? -EFAULT : 0; break; @@ -616,7 +631,7 @@ ret = do_ptrace(request, child, addr, data); out_tsk: - free_task_struct(child); + put_task_struct(child); out: unlock_kernel(); return ret; @@ -626,8 +641,9 @@ { unsigned long ip; - if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) - != (PT_PTRACED|PT_TRACESYS)) + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + if (!(current->ptrace & PT_PTRACED)) return; /* diff -Nru a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c --- a/arch/arm/kernel/setup.c Thu Mar 7 18:17:36 2002 +++ b/arch/arm/kernel/setup.c Thu Mar 7 18:17:36 2002 @@ -38,6 +38,10 @@ #define CONFIG_CMDLINE "" #endif +#ifdef CONFIG_PREEMPT +spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; +#endif + #if defined(CONFIG_FPE_NWFPE) || defined(CONFIG_FPE_FASTFPE) char fpe_type[8]; @@ -69,6 +73,9 @@ #ifdef MULTI_CPU struct processor processor; #endif +#ifdef MULTI_TLB +struct cpu_tlb_fns cpu_tlb; +#endif unsigned char aux_device_present; char elf_platform[ELF_PLATFORM_SIZE]; @@ -176,7 +183,7 @@ static inline void dump_cache(const char *prefix, unsigned int cache) { - unsigned int mult = 2 + CACHE_M(cache) ? 1 : 0; + unsigned int mult = 2 + (CACHE_M(cache) ? 1 : 0); printk("%s size %dK associativity %d line length %d sets %d\n", prefix, @@ -238,6 +245,9 @@ #ifdef MULTI_CPU processor = *list->proc; #endif +#ifdef MULTI_TLB + cpu_tlb = *list->tlb; +#endif printk("Processor: %s %s revision %d\n", proc_info.manufacturer, proc_info.cpu_name, @@ -661,7 +671,7 @@ static void c_show_cache(struct seq_file *m, const char *type, unsigned int cache) { - unsigned int mult = 2 + CACHE_M(cache) ? 1 : 0; + unsigned int mult = 2 + (CACHE_M(cache) ? 1 : 0); seq_printf(m, "%s size\t\t: %d\n" "%s assoc\t\t: %d\n" @@ -734,7 +744,7 @@ cache_types[CACHE_TYPE(cache_info)], cache_clean[CACHE_TYPE(cache_info)], cache_lockdown[CACHE_TYPE(cache_info)], - CACHE_S(cache_info) ? "separate I,D" : "unified"); + CACHE_S(cache_info) ? "Harvard" : "Unified"); if (CACHE_S(cache_info)) { c_show_cache(m, "I", CACHE_ISIZE(cache_info)); diff -Nru a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c --- a/arch/arm/kernel/signal.c Thu Mar 7 18:17:37 2002 +++ b/arch/arm/kernel/signal.c Thu Mar 7 18:17:37 2002 @@ -396,8 +396,12 @@ if (__put_user(retcodes[idx], rc)) return 1; - flush_icache_range((unsigned long)rc, - (unsigned long)(rc + 1)); + /* + * Ensure that the instruction cache sees + * the return code written onto the stack. + */ + cpu_icache_invalidate_range((unsigned long)rc, + (unsigned long)(rc + 1)); retcode = ((unsigned long)rc) + thumb; } @@ -480,6 +484,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) { + struct thread_info *thread = current_thread_info(); struct task_struct *tsk = current; int usig = sig; int ret; @@ -487,8 +492,8 @@ /* * translate the signal */ - if (usig < 32 && tsk->exec_domain && tsk->exec_domain->signal_invmap) - usig = tsk->exec_domain->signal_invmap[usig]; + if (usig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap) + usig = thread->exec_domain->signal_invmap[usig]; /* * Set up the stack frame @@ -532,7 +537,7 @@ * the kernel can handle, and then we build all the user-level signal handling * stack-frames in one go after that. */ -asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) +int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) { struct k_sigaction *ka; siginfo_t info; @@ -677,4 +682,11 @@ if (single_stepping) ptrace_set_bpt(current); return 0; +} + +asmlinkage void +do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) +{ + if (thread_flags & _TIF_SIGPENDING) + do_signal(NULL, regs, syscall); } diff -Nru a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c --- a/arch/arm/kernel/time.c Thu Mar 7 18:17:45 2002 +++ b/arch/arm/kernel/time.c Thu Mar 7 18:17:45 2002 @@ -31,9 +31,15 @@ #include #include -extern int setup_arm_irq(int, struct irqaction *); extern rwlock_t xtime_lock; extern unsigned long wall_jiffies; + +/* this needs a better home */ +spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; + +#ifdef CONFIG_SA1100_RTC_MODULE +EXPORT_SYMBOL(rtc_lock); +#endif /* change this if you have some constant time drift */ #define USECS_PER_JIFFY (1000000/HZ) diff -Nru a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c --- a/arch/arm/kernel/traps.c Thu Mar 7 18:17:43 2002 +++ b/arch/arm/kernel/traps.c Thu Mar 7 18:17:43 2002 @@ -95,7 +95,7 @@ int i; printk("Code: "); - for (i = -2; i < 3; i++) { + for (i = -4; i < 1; i++) { unsigned int val, bad; if (thumb) @@ -115,7 +115,7 @@ static void dump_stack(struct task_struct *tsk, unsigned long sp) { - dump_mem("Stack: ", sp - 16, 8192+(unsigned long)tsk); + dump_mem("Stack: ", sp - 16, 8192+(unsigned long)tsk->thread_info); } static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) @@ -146,7 +146,7 @@ void show_trace_task(struct task_struct *tsk) { if (tsk != current) { - unsigned int fp = tsk->thread.save->fp; + unsigned int fp = thread_saved_fp(tsk); c_backtrace(fp, 0x10); } } @@ -304,16 +304,17 @@ static int bad_syscall(int n, struct pt_regs *regs) { + struct thread_info *thread = current_thread_info(); siginfo_t info; /* You might think just testing `handler' would be enough, but PER_LINUX * points it to no_lcall7 to catch undercover SVr4 binaries. Gutted. */ - if (current->personality != PER_LINUX && current->exec_domain->handler) { + if (current->personality != PER_LINUX && thread->exec_domain->handler) { /* Hand it off to iBCS. The extra parameter and consequent type * forcing is necessary because of the weird ARM calling convention. */ - current->exec_domain->handler(n, regs); + thread->exec_domain->handler(n, regs); return regs->ARM_r0; } diff -Nru a/arch/arm/kernel/via82c505.c b/arch/arm/kernel/via82c505.c --- a/arch/arm/kernel/via82c505.c Thu Mar 7 18:17:36 2002 +++ b/arch/arm/kernel/via82c505.c Thu Mar 7 18:17:36 2002 @@ -74,85 +74,7 @@ via82c505_write_config_dword, }; -#ifdef CONFIG_ARCH_SHARK - -static char size_wanted; - -static int -dummy_read_config_byte(struct pci_dev *dev, int where, u8 *value) -{ - *value=0; - return PCIBIOS_SUCCESSFUL; -} - -static int -dummy_read_config_word(struct pci_dev *dev, int where, u16 *value) -{ - *value=0; - return PCIBIOS_SUCCESSFUL; -} - -static int -dummy_read_config_dword(struct pci_dev *dev, int where, u32 *value) -{ - if (dev->devfn != 0) *value = 0; - else - switch(where) { - case PCI_VENDOR_ID: - *value = PCI_VENDOR_ID_INTERG | PCI_DEVICE_ID_INTERG_2010 << 16; - break; - case PCI_CLASS_REVISION: - *value = PCI_CLASS_DISPLAY_VGA << 16; - break; - case PCI_BASE_ADDRESS_0: - if (size_wanted) { - /* 0x00900000 bytes long (0xff700000) */ - *value = 0xff000000; - size_wanted = 0; - } else { - *value = FB_START; - } - break; - case PCI_INTERRUPT_LINE: - *value = 6; - break; - default: - *value = 0; - } - return PCIBIOS_SUCCESSFUL; -} - -static int -dummy_write_config_byte(struct pci_dev *dev, int where, u8 value) -{ - return PCIBIOS_SUCCESSFUL; -} - -static int -dummy_write_config_word(struct pci_dev *dev, int where, u16 value) -{ - return PCIBIOS_SUCCESSFUL; -} - -static int -dummy_write_config_dword(struct pci_dev *dev, int where, u32 value) -{ - if ((dev->devfn == 0) && (where == PCI_BASE_ADDRESS_0) && (value == 0xffffffff)) - size_wanted = 1; - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops dummy_ops = { - dummy_read_config_byte, - dummy_read_config_word, - dummy_read_config_dword, - dummy_write_config_byte, - dummy_write_config_word, - dummy_write_config_dword, -}; -#endif - -void __init via82c505_init(void *sysdata) +void __init via82c505_preinit(void *sysdata) { struct pci_bus *bus; @@ -166,13 +88,17 @@ outb(0x93,0xA8); outb(0xd0,0xA9); - pci_scan_bus(0, &via82c505_ops, sysdata); +} + +int __init via82c505_setup(int nr, struct pci_sys_data *sys) +{ + return (nr == 0); +} + +struct pci_bus * __init via82c505_scan_bus(int nr, struct pci_sys_data *sysdata) +{ + if (nr == 0) + return pci_scan_bus(0, &via82c505_ops, sysdata); -#ifdef CONFIG_ARCH_SHARK - /* - * Initialize a fake pci-bus number 1 for the CyberPro - * on the vlbus - */ - bus = pci_scan_bus(1, &dummy_ops, sysdata); -#endif + return NULL; } diff -Nru a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile --- a/arch/arm/lib/Makefile Thu Mar 7 18:17:38 2002 +++ b/arch/arm/lib/Makefile Thu Mar 7 18:17:38 2002 @@ -44,8 +44,6 @@ obj-y += io-writesl.o obj-$(CONFIG_CPU_26) += uaccess-armo.o -obj-$(CONFIG_CPU_32) += copy_page-armv3.o copy_page-armv4.o copy_page-armv4mc.o -obj-$(CONFIG_CPU_32v5) += copy_page-armv5te.o include $(TOPDIR)/Rules.make diff -Nru a/arch/arm/lib/copy_page-armv3.S b/arch/arm/lib/copy_page-armv3.S --- a/arch/arm/lib/copy_page-armv3.S Thu Mar 7 18:17:37 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,59 +0,0 @@ -/* - * linux/arch/arm/lib/copypage.S - * - * Copyright (C) 1995-1999 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ASM optimised string functions - */ -#include -#include -#include - - .text - .align 5 -/* - * ARMv3 optimised copy_user_page - * - * FIXME: do we need to handle cache stuff... - */ -ENTRY(armv3_copy_user_page) - stmfd sp!, {r4, lr} @ 2 - mov r2, #PAGE_SZ/64 @ 1 - ldmia r1!, {r3, r4, ip, lr} @ 4+1 -1: stmia r0!, {r3, r4, ip, lr} @ 4 - ldmia r1!, {r3, r4, ip, lr} @ 4+1 - stmia r0!, {r3, r4, ip, lr} @ 4 - ldmia r1!, {r3, r4, ip, lr} @ 4+1 - stmia r0!, {r3, r4, ip, lr} @ 4 - ldmia r1!, {r3, r4, ip, lr} @ 4 - subs r2, r2, #1 @ 1 - stmia r0!, {r3, r4, ip, lr} @ 4 - ldmneia r1!, {r3, r4, ip, lr} @ 4 - bne 1b @ 1 - LOADREGS(fd, sp!, {r4, pc}) @ 3 - - .align 5 -/* - * ARMv3 optimised clear_user_page - * - * FIXME: do we need to handle cache stuff... - */ -ENTRY(armv3_clear_user_page) - str lr, [sp, #-4]! - mov r1, #PAGE_SZ/64 @ 1 - mov r2, #0 @ 1 - mov r3, #0 @ 1 - mov ip, #0 @ 1 - mov lr, #0 @ 1 -1: stmia r0!, {r2, r3, ip, lr} @ 4 - stmia r0!, {r2, r3, ip, lr} @ 4 - stmia r0!, {r2, r3, ip, lr} @ 4 - stmia r0!, {r2, r3, ip, lr} @ 4 - subs r1, r1, #1 @ 1 - bne 1b @ 1 - ldr pc, [sp], #4 - diff -Nru a/arch/arm/lib/copy_page-armv4.S b/arch/arm/lib/copy_page-armv4.S --- a/arch/arm/lib/copy_page-armv4.S Thu Mar 7 18:17:38 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,70 +0,0 @@ -/* - * linux/arch/arm/lib/copypage.S - * - * Copyright (C) 1995-1999 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ASM optimised string functions - */ -#include -#include - - .text - .align 5 -/* - * ARMv4 optimised copy_user_page - * - * We flush the destination cache lines just before we write the data into the - * corresponding address. Since the Dcache is read-allocate, this removes the - * Dcache aliasing issue. The writes will be forwarded to the write buffer, - * and merged as appropriate. - * - * Note: We rely on all ARMv4 processors implementing the "invalidate D line" - * instruction. If your processor does not supply this, you have to write your - * own copy_user_page that does the right thing. - */ -ENTRY(armv4_copy_user_page) - stmfd sp!, {r4, lr} @ 2 - mov r2, #PAGE_SZ/64 @ 1 - ldmia r1!, {r3, r4, ip, lr} @ 4 -1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line - stmia r0!, {r3, r4, ip, lr} @ 4 - ldmia r1!, {r3, r4, ip, lr} @ 4+1 - stmia r0!, {r3, r4, ip, lr} @ 4 - ldmia r1!, {r3, r4, ip, lr} @ 4 - mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line - stmia r0!, {r3, r4, ip, lr} @ 4 - ldmia r1!, {r3, r4, ip, lr} @ 4 - subs r2, r2, #1 @ 1 - stmia r0!, {r3, r4, ip, lr} @ 4 - ldmneia r1!, {r3, r4, ip, lr} @ 4 - bne 1b @ 1 - mcr p15, 0, r1, c7, c10, 4 @ 1 drain WB - ldmfd sp!, {r4, pc} @ 3 - - .align 5 -/* - * ARMv4 optimised clear_user_page - * - * Same story as above. - */ -ENTRY(armv4_clear_user_page) - str lr, [sp, #-4]! - mov r1, #PAGE_SZ/64 @ 1 - mov r2, #0 @ 1 - mov r3, #0 @ 1 - mov ip, #0 @ 1 - mov lr, #0 @ 1 -1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line - stmia r0!, {r2, r3, ip, lr} @ 4 - stmia r0!, {r2, r3, ip, lr} @ 4 - mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line - stmia r0!, {r2, r3, ip, lr} @ 4 - stmia r0!, {r2, r3, ip, lr} @ 4 - subs r1, r1, #1 @ 1 - bne 1b @ 1 - mcr p15, 0, r1, c7, c10, 4 @ 1 drain WB - ldr pc, [sp], #4 diff -Nru a/arch/arm/lib/copy_page-armv4mc.S b/arch/arm/lib/copy_page-armv4mc.S --- a/arch/arm/lib/copy_page-armv4mc.S Thu Mar 7 18:17:44 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,71 +0,0 @@ -/* - * linux/arch/arm/lib/copy_page-armv4mc.S - * - * Copyright (C) 1995-2001 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ASM optimised string functions - */ -#include -#include - - .text - .align 5 -/* - * ARMv4 mini-dcache optimised copy_user_page - * - * We flush the destination cache lines just before we write the data into the - * corresponding address. Since the Dcache is read-allocate, this removes the - * Dcache aliasing issue. The writes will be forwarded to the write buffer, - * and merged as appropriate. - * - * Note: We rely on all ARMv4 processors implementing the "invalidate D line" - * instruction. If your processor does not supply this, you have to write your - * own copy_user_page that does the right thing. - */ -ENTRY(armv4_mc_copy_user_page) - stmfd sp!, {r4, lr} @ 2 - mov r4, r0 - mov r0, r1 - bl map_page_minicache - mov r1, #PAGE_SZ/64 @ 1 - ldmia r0!, {r2, r3, ip, lr} @ 4 -1: mcr p15, 0, r4, c7, c6, 1 @ 1 invalidate D line - stmia r4!, {r2, r3, ip, lr} @ 4 - ldmia r0!, {r2, r3, ip, lr} @ 4+1 - stmia r4!, {r2, r3, ip, lr} @ 4 - ldmia r0!, {r2, r3, ip, lr} @ 4 - mcr p15, 0, r4, c7, c6, 1 @ 1 invalidate D line - stmia r4!, {r2, r3, ip, lr} @ 4 - ldmia r0!, {r2, r3, ip, lr} @ 4 - subs r1, r1, #1 @ 1 - stmia r4!, {r2, r3, ip, lr} @ 4 - ldmneia r0!, {r2, r3, ip, lr} @ 4 - bne 1b @ 1 - ldmfd sp!, {r4, pc} @ 3 - - .align 5 -/* - * ARMv4 optimised clear_user_page - * - * Same story as above. - */ -ENTRY(armv4_mc_clear_user_page) - str lr, [sp, #-4]! - mov r1, #PAGE_SZ/64 @ 1 - mov r2, #0 @ 1 - mov r3, #0 @ 1 - mov ip, #0 @ 1 - mov lr, #0 @ 1 -1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line - stmia r0!, {r2, r3, ip, lr} @ 4 - stmia r0!, {r2, r3, ip, lr} @ 4 - mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line - stmia r0!, {r2, r3, ip, lr} @ 4 - stmia r0!, {r2, r3, ip, lr} @ 4 - subs r1, r1, #1 @ 1 - bne 1b @ 1 - ldr pc, [sp], #4 diff -Nru a/arch/arm/lib/copy_page-armv5te.S b/arch/arm/lib/copy_page-armv5te.S --- a/arch/arm/lib/copy_page-armv5te.S Thu Mar 7 18:17:41 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,79 +0,0 @@ -/* - * linux/arch/arm/lib/copypage-armv5te.S - * - * Copyright (C) 2001 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include - -/* - * General note: - * We don't really want write-allocate cache behaviour for these functions - * since that will just eat through 8K of the cache. - */ - - .text - .align 5 -/* - * ARMv5TE optimised copy_user_page - * r0 = destination - * r1 = source - * r2 = virtual user address of ultimate destination page - * - * The source page may have some clean entries in the cache already, but we - * can safely ignore them - break_cow() will flush them out of the cache - * if we eventually end up using our copied page. - * - * What we could do is use the mini-cache to buffer reads from the source - * page. We rely on the mini-cache being smaller than one page, so we'll - * cycle through the complete cache anyway. - */ -ENTRY(armv5te_copy_user_page) - stmfd sp!, {r4, r5, lr} - mov r5, r0 - mov r0, r1 - bl map_page_minicache - mov r1, r5 - mov lr, #PAGE_SZ/32 - -1: mov ip, r1 - ldrd r2, [r0], #8 - ldrd r4, [r0], #8 - strd r2, [r1], #8 - ldrd r2, [r0], #8 - strd r4, [r1], #8 - ldrd r4, [r0], #8 - strd r2, [r1], #8 - strd r4, [r1], #8 - mcr p15, 0, ip, c7, c10, 1 @ clean D line - mcr p15, 0, ip, c7, c6, 1 @ invalidate D line - subs lr, lr, #1 - bne 1b - - ldmfd sp!, {r4, r5, pc} - - .align 5 -/* - * ARMv5TE optimised clear_user_page - * r0 = destination - * r1 = virtual user address of ultimate destination page - */ -ENTRY(armv5te_clear_user_page) - str lr, [sp, #-4]! - mov r1, #PAGE_SZ/32 - mov r2, #0 - mov r3, #0 -1: mov ip, r0 - strd r2, [r0], #8 - strd r2, [r0], #8 - strd r2, [r0], #8 - strd r2, [r0], #8 - mcr p15, 0, ip, c7, c10, 1 @ clean D line - mcr p15, 0, ip, c7, c6, 1 @ invalidate D line - subs r1, r1, #1 - bne 1b - ldr pc, [sp], #4 diff -Nru a/arch/arm/lib/csumpartial.S b/arch/arm/lib/csumpartial.S --- a/arch/arm/lib/csumpartial.S Thu Mar 7 18:17:44 2002 +++ b/arch/arm/lib/csumpartial.S Thu Mar 7 18:17:44 2002 @@ -41,7 +41,7 @@ tst buf, #1 @ odd address? ldrneb td0, [buf], #1 subne len, len, #1 - adcnes sum, sum, td0, lsl #8 + adcnes sum, sum, td0, lsl #byte(1) .less4: tst len, #6 beq .less8_byte @@ -56,7 +56,11 @@ ldrb td0, [buf], #1 ldrb td3, [buf], #1 sub len, len, #2 +#ifndef __ARMEB__ orr td0, td0, td3, lsl #8 +#else + orr td0, td3, td0, lsl #8 +#endif #endif adcs sum, sum, td0 tst len, #6 @@ -64,7 +68,7 @@ .less8_byte: tst len, #1 @ odd number of bytes ldrneb td0, [buf], #1 @ include last byte - adcnes sum, sum, td0 @ update checksum + adcnes sum, sum, td0, lsl #byte(0) @ update checksum .done: adc r0, sum, #0 @ collect up the last carry ldr td0, [sp], #4 @@ -76,7 +80,7 @@ .not_aligned: tst buf, #1 @ odd address ldrneb td0, [buf], #1 @ make even subne len, len, #1 - adcnes sum, sum, td0, lsl #8 @ update checksum + adcnes sum, sum, td0, lsl #byte(1) @ update checksum tst buf, #2 @ 32-bit aligned? #ifdef __ARM_ARCH_4__ @@ -86,7 +90,11 @@ ldrneb td0, [buf], #1 ldrneb ip, [buf], #1 subne len, len, #2 +#ifndef __ARMEB__ orrne td0, td0, ip, lsl #8 +#else + orrne td0, ip, td0, lsl #8 +#endif #endif adcnes sum, sum, td0 @ update checksum mov pc, lr diff -Nru a/arch/arm/lib/csumpartialcopygeneric.S b/arch/arm/lib/csumpartialcopygeneric.S --- a/arch/arm/lib/csumpartialcopygeneric.S Thu Mar 7 18:17:38 2002 +++ b/arch/arm/lib/csumpartialcopygeneric.S Thu Mar 7 18:17:38 2002 @@ -36,16 +36,16 @@ load1b ip sub len, len, #1 - adcs sum, sum, ip, lsl #8 @ update checksum + adcs sum, sum, ip, lsl #byte(1) @ update checksum strb ip, [dst], #1 tst dst, #2 moveq pc, lr @ dst is now 32bit aligned .dst_16bit: load2b r8, ip sub len, len, #2 - adcs sum, sum, r8 + adcs sum, sum, r8, lsl #byte(0) strb r8, [dst], #1 - adcs sum, sum, ip, lsl #8 + adcs sum, sum, ip, lsl #byte(1) strb ip, [dst], #1 mov pc, lr @ dst is now 32bit aligned @@ -63,16 +63,16 @@ /* Align dst */ load1b ip sub len, len, #1 - adcs sum, sum, ip, lsl #8 @ update checksum + adcs sum, sum, ip, lsl #byte(1) @ update checksum strb ip, [dst], #1 tst len, #6 beq .less8_byteonly 1: load2b r8, ip sub len, len, #2 - adcs sum, sum, r8 + adcs sum, sum, r8, lsl #byte(0) strb r8, [dst], #1 - adcs sum, sum, ip, lsl #8 + adcs sum, sum, ip, lsl #byte(1) strb ip, [dst], #1 .less8_aligned: tst len, #6 bne 1b @@ -80,7 +80,7 @@ tst len, #1 beq .done load1b r8 - adcs sum, sum, r8 @ update checksum + adcs sum, sum, r8, lsl #byte(0) @ update checksum strb r8, [dst], #1 b .done @@ -137,18 +137,19 @@ 4: ands len, len, #3 beq .done - load1l r4 + load1l r5 tst len, #2 + mov r4, r5, lsr #byte(0) beq .exit - adcs sum, sum, r4, lsl #16 + adcs sum, sum, r5, push #16 strb r4, [dst], #1 - mov r4, r4, lsr #8 + mov r4, r5, lsr #byte(1) strb r4, [dst], #1 - mov r4, r4, lsr #8 + mov r4, r5, lsr #byte(2) .exit: tst len, #1 strneb r4, [dst], #1 andne r4, r4, #255 - adcnes sum, sum, r4 + adcnes sum, sum, r4, lsl #byte(0) /* * If the dst pointer was not 16-bit aligned, we @@ -167,27 +168,27 @@ adc sum, sum, #0 @ include C from dst alignment and ip, src, #3 bic src, src, #3 - load1l r4 + load1l r5 cmp ip, #2 beq .src2_aligned bhi .src3_aligned - mov r4, r4, lsr #8 @ C = 0 + mov r4, r5, pull #8 @ C = 0 bics ip, len, #15 beq 2f 1: load4l r5, r6, r7, r8 - orr r4, r4, r5, lsl #24 - mov r5, r5, lsr #8 - orr r5, r5, r6, lsl #24 - mov r6, r6, lsr #8 - orr r6, r6, r7, lsl #24 - mov r7, r7, lsr #8 - orr r7, r7, r8, lsl #24 + orr r4, r4, r5, push #24 + mov r5, r5, pull #8 + orr r5, r5, r6, push #24 + mov r6, r6, pull #8 + orr r6, r6, r7, push #24 + mov r7, r7, pull #8 + orr r7, r7, r8, push #24 stmia dst!, {r4, r5, r6, r7} adcs sum, sum, r4 adcs sum, sum, r5 adcs sum, sum, r6 adcs sum, sum, r7 - mov r4, r8, lsr #8 + mov r4, r8, pull #8 sub ip, ip, #16 teq ip, #0 bne 1b @@ -196,49 +197,50 @@ tst ip, #8 beq 3f load2l r5, r6 - orr r4, r4, r5, lsl #24 - mov r5, r5, lsr #8 - orr r5, r5, r6, lsl #24 + orr r4, r4, r5, push #24 + mov r5, r5, pull #8 + orr r5, r5, r6, push #24 stmia dst!, {r4, r5} adcs sum, sum, r4 adcs sum, sum, r5 - mov r4, r6, lsr #8 + mov r4, r6, pull #8 tst ip, #4 beq 4f 3: load1l r5 - orr r4, r4, r5, lsl #24 + orr r4, r4, r5, push #24 str r4, [dst], #4 adcs sum, sum, r4 - mov r4, r5, lsr #8 4: ands len, len, #3 beq .done + mov r4, r5, lsr #byte(1) tst len, #2 beq .exit - adcs sum, sum, r4, lsl #16 + bic r5, r5, #0xff << byte(0) + adcs sum, sum, r5, push #8 strb r4, [dst], #1 - mov r4, r4, lsr #8 + mov r4, r5, lsr #byte(2) strb r4, [dst], #1 - mov r4, r4, lsr #8 + mov r4, r5, lsr #byte(3) b .exit -.src2_aligned: mov r4, r4, lsr #16 +.src2_aligned: mov r4, r5, pull #16 adds sum, sum, #0 bics ip, len, #15 beq 2f 1: load4l r5, r6, r7, r8 - orr r4, r4, r5, lsl #16 - mov r5, r5, lsr #16 - orr r5, r5, r6, lsl #16 - mov r6, r6, lsr #16 - orr r6, r6, r7, lsl #16 - mov r7, r7, lsr #16 - orr r7, r7, r8, lsl #16 + orr r4, r4, r5, push #16 + mov r5, r5, pull #16 + orr r5, r5, r6, push #16 + mov r6, r6, pull #16 + orr r6, r6, r7, push #16 + mov r7, r7, pull #16 + orr r7, r7, r8, push #16 stmia dst!, {r4, r5, r6, r7} adcs sum, sum, r4 adcs sum, sum, r5 adcs sum, sum, r6 adcs sum, sum, r7 - mov r4, r8, lsr #16 + mov r4, r8, pull #16 sub ip, ip, #16 teq ip, #0 bne 1b @@ -247,51 +249,51 @@ tst ip, #8 beq 3f load2l r5, r6 - orr r4, r4, r5, lsl #16 - mov r5, r5, lsr #16 - orr r5, r5, r6, lsl #16 + orr r4, r4, r5, push #16 + mov r5, r5, pull #16 + orr r5, r5, r6, push #16 stmia dst!, {r4, r5} adcs sum, sum, r4 adcs sum, sum, r5 - mov r4, r6, lsr #16 + mov r4, r6, pull #16 tst ip, #4 beq 4f 3: load1l r5 - orr r4, r4, r5, lsl #16 + orr r4, r4, r5, push #16 str r4, [dst], #4 adcs sum, sum, r4 - mov r4, r5, lsr #16 4: ands len, len, #3 beq .done + mov r4, r5, lsr #byte(2) tst len, #2 beq .exit - adcs sum, sum, r4, lsl #16 + adcs sum, sum, r5, pull #16 strb r4, [dst], #1 - mov r4, r4, lsr #8 + mov r4, r5, lsr #byte(3) strb r4, [dst], #1 tst len, #1 beq .done load1b r4 b .exit -.src3_aligned: mov r4, r4, lsr #24 +.src3_aligned: mov r4, r5, pull #24 adds sum, sum, #0 bics ip, len, #15 beq 2f 1: load4l r5, r6, r7, r8 - orr r4, r4, r5, lsl #8 - mov r5, r5, lsr #24 - orr r5, r5, r6, lsl #8 - mov r6, r6, lsr #24 - orr r6, r6, r7, lsl #8 - mov r7, r7, lsr #24 - orr r7, r7, r8, lsl #8 + orr r4, r4, r5, push #8 + mov r5, r5, pull #24 + orr r5, r5, r6, push #8 + mov r6, r6, pull #24 + orr r6, r6, r7, push #8 + mov r7, r7, pull #24 + orr r7, r7, r8, push #8 stmia dst!, {r4, r5, r6, r7} adcs sum, sum, r4 adcs sum, sum, r5 adcs sum, sum, r6 adcs sum, sum, r7 - mov r4, r8, lsr #24 + mov r4, r8, pull #24 sub ip, ip, #16 teq ip, #0 bne 1b @@ -300,28 +302,29 @@ tst ip, #8 beq 3f load2l r5, r6 - orr r4, r4, r5, lsl #8 - mov r5, r5, lsr #24 - orr r5, r5, r6, lsl #8 + orr r4, r4, r5, push #8 + mov r5, r5, pull #24 + orr r5, r5, r6, push #8 stmia dst!, {r4, r5} adcs sum, sum, r4 adcs sum, sum, r5 - mov r4, r6, lsr #24 + mov r4, r6, pull #24 tst ip, #4 beq 4f 3: load1l r5 - orr r4, r4, r5, lsl #8 + orr r4, r4, r5, push #8 str r4, [dst], #4 adcs sum, sum, r4 - mov r4, r5, lsr #24 4: ands len, len, #3 beq .done + mov r4, r5, lsr #byte(3) tst len, #2 beq .exit - adcs sum, sum, r4, lsl #16 + adcs sum, sum, r5, pull #24 strb r4, [dst], #1 - load1l r4 + load1l r5 + mov r4, r5, lsr #byte(0) strb r4, [dst], #1 - adcs sum, sum, r4, lsl #24 - mov r4, r4, lsr #8 + adcs sum, sum, r4, push #24 + mov r4, r5, lsr #byte(1) b .exit diff -Nru a/arch/arm/lib/ecard.S b/arch/arm/lib/ecard.S --- a/arch/arm/lib/ecard.S Thu Mar 7 18:17:41 2002 +++ b/arch/arm/lib/ecard.S Thu Mar 7 18:17:41 2002 @@ -12,7 +12,7 @@ #include #include -#if defined(CONFIG_CPU_26) +#ifdef CONFIG_CPU_26 #define CPSR2SPSR(rt) #else #define CPSR2SPSR(rt) \ diff -Nru a/arch/arm/lib/getuser.S b/arch/arm/lib/getuser.S --- a/arch/arm/lib/getuser.S Thu Mar 7 18:17:45 2002 +++ b/arch/arm/lib/getuser.S Thu Mar 7 18:17:45 2002 @@ -27,12 +27,13 @@ * Note also that it is intended that __get_user_bad is not global. */ #include +#include .global __get_user_1 __get_user_1: bic r1, sp, #0x1f00 bic r1, r1, #0x00ff - ldr r1, [r1, #TSK_ADDR_LIMIT] + ldr r1, [r1, #TI_ADDR_LIMIT] sub r1, r1, #1 cmp r0, r1 1: ldrlsbt r1, [r0] @@ -44,7 +45,7 @@ __get_user_2: bic r2, sp, #0x1f00 bic r2, r2, #0x00ff - ldr r2, [r2, #TSK_ADDR_LIMIT] + ldr r2, [r2, #TI_ADDR_LIMIT] sub r2, r2, #2 cmp r0, r2 2: ldrlsbt r1, [r0], #1 @@ -62,7 +63,7 @@ __get_user_4: bic r1, sp, #0x1f00 bic r1, r1, #0x00ff - ldr r1, [r1, #TSK_ADDR_LIMIT] + ldr r1, [r1, #TI_ADDR_LIMIT] sub r1, r1, #4 cmp r0, r1 4: ldrlst r1, [r0] @@ -74,7 +75,7 @@ __get_user_8: bic r2, sp, #0x1f00 bic r2, r2, #0x00ff - ldr r2, [r2, #TSK_ADDR_LIMIT] + ldr r2, [r2, #TI_ADDR_LIMIT] sub r2, r2, #8 cmp r0, r2 5: ldrlst r1, [r0], #4 diff -Nru a/arch/arm/lib/io-acorn.S b/arch/arm/lib/io-acorn.S --- a/arch/arm/lib/io-acorn.S Thu Mar 7 18:17:39 2002 +++ b/arch/arm/lib/io-acorn.S Thu Mar 7 18:17:39 2002 @@ -58,7 +58,7 @@ @ Proto : void memc_write(int register, int value); @ Returns: nothing -#if defined(CONFIG_CPU_26) +#ifdef CONFIG_CPU_26 ENTRY(memc_write) cmp r0, #7 RETINSTR(movgt,pc,lr) diff -Nru a/arch/arm/lib/putuser.S b/arch/arm/lib/putuser.S --- a/arch/arm/lib/putuser.S Thu Mar 7 18:17:46 2002 +++ b/arch/arm/lib/putuser.S Thu Mar 7 18:17:46 2002 @@ -27,12 +27,13 @@ * Note also that it is intended that __put_user_bad is not global. */ #include +#include .global __put_user_1 __put_user_1: bic r2, sp, #0x1f00 bic r2, r2, #0x00ff - ldr r2, [r2, #TSK_ADDR_LIMIT] + ldr r2, [r2, #TI_ADDR_LIMIT] sub r2, r2, #1 cmp r0, r2 1: strlsbt r1, [r0] @@ -44,7 +45,7 @@ __put_user_2: bic r2, sp, #0x1f00 bic r2, r2, #0x00ff - ldr r2, [r2, #TSK_ADDR_LIMIT] + ldr r2, [r2, #TI_ADDR_LIMIT] sub r2, r2, #2 cmp r0, r2 movls r2, r1, lsr #8 @@ -63,7 +64,7 @@ __put_user_4: bic r2, sp, #0x1f00 bic r2, r2, #0x00ff - ldr r2, [r2, #TSK_ADDR_LIMIT] + ldr r2, [r2, #TI_ADDR_LIMIT] sub r2, r2, #4 cmp r0, r2 4: strlst r1, [r0] @@ -75,7 +76,7 @@ __put_user_8: bic ip, sp, #0x1f00 bic ip, ip, #0x00ff - ldr ip, [ip, #TSK_ADDR_LIMIT] + ldr ip, [ip, #TI_ADDR_LIMIT] sub ip, ip, #8 cmp r0, ip 5: strlst r1, [r0], #4 diff -Nru a/arch/arm/lib/strrchr.S b/arch/arm/lib/strrchr.S --- a/arch/arm/lib/strrchr.S Thu Mar 7 18:17:45 2002 +++ b/arch/arm/lib/strrchr.S Thu Mar 7 18:17:45 2002 @@ -18,7 +18,7 @@ mov r3, #0 1: ldrb r2, [r0], #1 teq r2, r1 - moveq r3, r0 + subeq r3, r0, #1 teq r2, #0 bne 1b mov r0, r3 diff -Nru a/arch/arm/mach-adifcc/arch.c b/arch/arm/mach-adifcc/arch.c --- a/arch/arm/mach-adifcc/arch.c Thu Mar 7 18:17:46 2002 +++ b/arch/arm/mach-adifcc/arch.c Thu Mar 7 18:17:46 2002 @@ -7,7 +7,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - +#include #include #include #include @@ -33,7 +33,7 @@ setup_ramdisk( 1, 0, 0, 8192 ); setup_initrd( 0xc0800000, 3*1024*1024 ); - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); #endif } diff -Nru a/arch/arm/mach-adifcc/irq.c b/arch/arm/mach-adifcc/irq.c --- a/arch/arm/mach-adifcc/irq.c Thu Mar 7 18:17:42 2002 +++ b/arch/arm/mach-adifcc/irq.c Thu Mar 7 18:17:42 2002 @@ -12,8 +12,6 @@ * 80200 on chip interrupts. That'll change once the hardware adds * support for PCI though. */ - -#include #include #include #include diff -Nru a/arch/arm/mach-adifcc/mm.c b/arch/arm/mach-adifcc/mm.c --- a/arch/arm/mach-adifcc/mm.c Thu Mar 7 18:17:39 2002 +++ b/arch/arm/mach-adifcc/mm.c Thu Mar 7 18:17:39 2002 @@ -14,7 +14,7 @@ static struct map_desc adifcc_io_desc[] __initdata = { /* on-board devices */ - { 0xff400000, 0x00400000, 0x00300000, DOMAIN_IO, 1, 1, 0, 0}, + { 0xff400000, 0x00400000, 0x00300000, DOMAIN_IO, 0, 1, 0, 0}, LAST_DESC }; diff -Nru a/arch/arm/mach-anakin/arch.c b/arch/arm/mach-anakin/arch.c --- a/arch/arm/mach-anakin/arch.c Thu Mar 7 18:17:36 2002 +++ b/arch/arm/mach-anakin/arch.c Thu Mar 7 18:17:36 2002 @@ -30,7 +30,7 @@ fixup_anakin(struct machine_desc *desc, struct param_struct *unused, char **cmdline, struct meminfo *mi) { - ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR, 0); setup_ramdisk(1, 0, 0, CONFIG_BLK_DEV_RAM_SIZE); setup_initrd(0xc0800000, 4 * 1024 * 1024); } diff -Nru a/arch/arm/mach-anakin/mm.c b/arch/arm/mach-anakin/mm.c --- a/arch/arm/mach-anakin/mm.c Thu Mar 7 18:17:46 2002 +++ b/arch/arm/mach-anakin/mm.c Thu Mar 7 18:17:46 2002 @@ -20,7 +20,7 @@ static struct map_desc anakin_io_desc[] __initdata = { { IO_BASE, IO_START, IO_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, - { FLASH_BASE, FLASH_START, FLASH_SIZE, DOMAIN_IO, 1, 1, 0, 0 }, + { FLASH_BASE, FLASH_START, FLASH_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, { VGA_BASE, VGA_START, VGA_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, LAST_DESC }; diff -Nru a/arch/arm/mach-arc/arch.c b/arch/arm/mach-arc/arch.c --- a/arch/arm/mach-arc/arch.c Thu Mar 7 18:17:39 2002 +++ b/arch/arm/mach-arc/arch.c Thu Mar 7 18:17:39 2002 @@ -9,6 +9,7 @@ * * Architecture specific fixups. */ +#include #include #include diff -Nru a/arch/arm/mach-arc/dma.c b/arch/arm/mach-arc/dma.c --- a/arch/arm/mach-arc/dma.c Thu Mar 7 18:17:45 2002 +++ b/arch/arm/mach-arc/dma.c Thu Mar 7 18:17:45 2002 @@ -28,6 +28,10 @@ static void arc_floppy_data_enable_dma(dmach_t channel, dma_t *dma) { DPRINTK("arc_floppy_data_enable_dma\n"); + + if (dma->using_sg) + BUG(); + switch (dma->dma_mode) { case DMA_MODE_READ: { /* read */ extern unsigned char fdc1772_dma_read, fdc1772_dma_read_end; @@ -39,7 +43,7 @@ memcpy ((void *)0x1c, (void *)&fdc1772_dma_read, &fdc1772_dma_read_end - &fdc1772_dma_read); - fdc1772_setupdma(dma->buf.length, dma->buf.address); /* Sets data pointer up */ + fdc1772_setupdma(dma->buf.length, dma->buf.__address); /* Sets data pointer up */ enable_fiq(FIQ_FLOPPYDATA); restore_flags(flags); } @@ -54,7 +58,7 @@ clf(); memcpy ((void *)0x1c, (void *)&fdc1772_dma_write, &fdc1772_dma_write_end - &fdc1772_dma_write); - fdc1772_setupdma(dma->buf.length, dma->buf.address); /* Sets data pointer up */ + fdc1772_setupdma(dma->buf.length, dma->buf.__address); /* Sets data pointer up */ enable_fiq(FIQ_FLOPPYDATA; restore_flags(flags); @@ -140,6 +144,9 @@ extern void floppy_fiqsetup(unsigned long len, unsigned long addr, unsigned long port); + if (dma->using_sg) + BUG(); + if (dma->dma_mode == DMA_MODE_READ) { extern unsigned char floppy_fiqin_start, floppy_fiqin_end; fiqhandler_start = &floppy_fiqin_start; @@ -155,7 +162,7 @@ } memcpy((void *)0x1c, fiqhandler_start, fiqhandler_length); regs.ARM_r9 = dma->buf.length; - regs.ARM_r10 = (unsigned long)dma->buf.address; + regs.ARM_r10 = (unsigned long)dma->buf.__address; regs.ARM_fp = FLOPPYDMA_BASE; set_fiq_regs(®s); enable_fiq(dma->dma_irq); diff -Nru a/arch/arm/mach-arc/mm.c b/arch/arm/mach-arc/mm.c --- a/arch/arm/mach-arc/mm.c Thu Mar 7 18:17:43 2002 +++ b/arch/arm/mach-arc/mm.c Thu Mar 7 18:17:43 2002 @@ -78,15 +78,16 @@ if (!new_pmd) goto no_pmd; - new_pte = pte_alloc(mm, new_pmd, 0); + new_pte = pte_alloc_map(mm, new_pmd, 0); if (!new_pte) goto no_pte; init_pgd = pgd_offset_k(0); init_pmd = pmd_offset(init_pgd, 0); - init_pte = pte_offset(init_pmd, 0); - + init_pte = pte_offset_map_nested(init_pmd, 0); set_pte(new_pte, *init_pte); + pte_unmap_nested(init_pte); + pte_unmap(new_pte); /* * most of the page table entries are zeroed diff -Nru a/arch/arm/mach-clps711x/Makefile b/arch/arm/mach-clps711x/Makefile --- a/arch/arm/mach-clps711x/Makefile Thu Mar 7 18:17:44 2002 +++ b/arch/arm/mach-clps711x/Makefile Thu Mar 7 18:17:44 2002 @@ -21,9 +21,10 @@ obj-$(CONFIG_ARCH_AUTCPU12) += autcpu12.o obj-$(CONFIG_ARCH_CDB89712) += cdb89712.o obj-$(CONFIG_ARCH_CLEP7312) += clep7312.o -obj-$(CONFIG_ARCH_EDB7211) += edb7211-arch.o edb7211-mm.o -obj-$(CONFIG_ARCH_P720T) += p720t.o -leds-$(CONFIG_ARCH_P720T) += p720t-leds.o -obj-$(CONFIG_LEDS) += $(leds-y) +obj-$(CONFIG_ARCH_EDB7211) += edb7211-arch.o edb7211-mm.o +obj-$(CONFIG_ARCH_FORTUNET) += fortunet.o +obj-$(CONFIG_ARCH_P720T) += p720t.o +leds-$(CONFIG_ARCH_P720T) += p720t-leds.o +obj-$(CONFIG_LEDS) += $(leds-y) include $(TOPDIR)/Rules.make diff -Nru a/arch/arm/mach-clps711x/autcpu12.c b/arch/arm/mach-clps711x/autcpu12.c --- a/arch/arm/mach-clps711x/autcpu12.c Thu Mar 7 18:17:40 2002 +++ b/arch/arm/mach-clps711x/autcpu12.c Thu Mar 7 18:17:40 2002 @@ -51,7 +51,7 @@ /* virtual, physical, length, domain, r, w, c, b */ /* memory-mapped extra io and CS8900A Ethernet chip */ /* ethernet chip */ - { AUTCPU12_VIRT_CS8900A, AUTCPU12_PHYS_CS8900A, SZ_1M, DOMAIN_IO, 1, 1, 0, 0 }, + { AUTCPU12_VIRT_CS8900A, AUTCPU12_PHYS_CS8900A, SZ_1M, DOMAIN_IO, 0, 1, 0, 0 }, LAST_DESC }; diff -Nru a/arch/arm/mach-clps711x/cdb89712.c b/arch/arm/mach-clps711x/cdb89712.c --- a/arch/arm/mach-clps711x/cdb89712.c Thu Mar 7 18:17:36 2002 +++ b/arch/arm/mach-clps711x/cdb89712.c Thu Mar 7 18:17:36 2002 @@ -17,7 +17,6 @@ * 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 diff -Nru a/arch/arm/mach-clps711x/edb7211-mm.c b/arch/arm/mach-clps711x/edb7211-mm.c --- a/arch/arm/mach-clps711x/edb7211-mm.c Thu Mar 7 18:17:43 2002 +++ b/arch/arm/mach-clps711x/edb7211-mm.c Thu Mar 7 18:17:43 2002 @@ -56,12 +56,12 @@ /* virtual, physical, length, domain, r, w, c, b */ /* memory-mapped extra keyboard row and CS8900A Ethernet chip */ - { EP7211_VIRT_EXTKBD, EP7211_PHYS_EXTKBD, MB1, DOMAIN_IO, 1, 1, 0, 0 }, - { EP7211_VIRT_CS8900A, EP7211_PHYS_CS8900A, MB1, DOMAIN_IO, 1, 1, 0, 0 }, + { EP7211_VIRT_EXTKBD, EP7211_PHYS_EXTKBD, MB1, DOMAIN_IO, 0, 1, 0, 0 }, + { EP7211_VIRT_CS8900A, EP7211_PHYS_CS8900A, MB1, DOMAIN_IO, 0, 1, 0, 0 }, /* flash banks */ - { EP7211_VIRT_FLASH1, EP7211_PHYS_FLASH1, MB1 * 8, DOMAIN_KERNEL, 1, 1, 0, 0 }, - { EP7211_VIRT_FLASH2, EP7211_PHYS_FLASH2, MB1 * 8, DOMAIN_KERNEL, 1, 1, 0, 0 }, + { EP7211_VIRT_FLASH1, EP7211_PHYS_FLASH1, MB1 * 8, DOMAIN_KERNEL, 0, 1, 0, 0 }, + { EP7211_VIRT_FLASH2, EP7211_PHYS_FLASH2, MB1 * 8, DOMAIN_KERNEL, 0, 1, 0, 0 }, LAST_DESC }; diff -Nru a/arch/arm/mach-clps711x/fortunet.c b/arch/arm/mach-clps711x/fortunet.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mach-clps711x/fortunet.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,81 @@ +/* + * linux/arch/arm/mach-clps711x/fortunet.c + * + * Derived from linux/arch/arm/mach-integrator/arch.c + * + * Copyright (C) 2000 Deep Blue Solutions Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +extern void clps711x_map_io(void); +extern void clps711x_init_irq(void); + +struct meminfo memmap = { 1, 0xC1000000, {{0xC0000000,0x01000000,0}}}; + +typedef struct tag_IMAGE_PARAMS +{ + int ramdisk_ok; + int ramdisk_address; + int ramdisk_size; + int ram_size; + int extra_param_type; + int extra_param_ptr; + int command_line; +} IMAGE_PARAMS; + +#define IMAGE_PARAMS_PHYS 0xC01F0000 + +static void __init +fortunet_fixup(struct machine_desc *desc, struct param_struct *params, + char **cmdline, struct meminfo *mi) +{ + IMAGE_PARAMS *ip; + ip = (IMAGE_PARAMS *)__phys_to_virt(IMAGE_PARAMS_PHYS); + *cmdline = (char *)__phys_to_virt(ip->command_line); +#ifdef CONFIG_BLK_DEV_INITRD + if(ip->ramdisk_ok) + { + initrd_start = __phys_to_virt(ip->ramdisk_address); + initrd_end = initrd_start + ip->ramdisk_size; + } +#endif + memmap.bank[0].size = ip->ram_size; + memmap.end = ip->ram_size+0xC0000000; + *mi = memmap; +} + +MACHINE_START(FORTUNET, "ARM-FortuNet") + MAINTAINER("FortuNet Inc.") + BOOT_MEM(0xc0000000, 0x80000000, 0xf0000000) + BOOT_PARAMS(0x00000000) + FIXUP(fortunet_fixup) + MAPIO(clps711x_map_io) + INITIRQ(clps711x_init_irq) +MACHINE_END diff -Nru a/arch/arm/mach-clps711x/irq.c b/arch/arm/mach-clps711x/irq.c --- a/arch/arm/mach-clps711x/irq.c Thu Mar 7 18:17:40 2002 +++ b/arch/arm/mach-clps711x/irq.c Thu Mar 7 18:17:40 2002 @@ -26,7 +26,7 @@ #include -static void mask_irq_int1(unsigned int irq) +static void int1_mask(unsigned int irq) { u32 intmr1; @@ -35,7 +35,7 @@ clps_writel(intmr1, INTMR1); } -static void mask_ack_irq_int1(unsigned int irq) +static void int1_ack(unsigned int irq) { u32 intmr1; @@ -53,7 +53,7 @@ } } -static void unmask_irq_int1(unsigned int irq) +static void int1_unmask(unsigned int irq) { u32 intmr1; @@ -62,7 +62,13 @@ clps_writel(intmr1, INTMR1); } -static void mask_irq_int2(unsigned int irq) +static struct irqchip int1_chip = { + ack: int1_ack, + mask: int1_mask, + unmask: int1_unmask, +}; + +static void int2_mask(unsigned int irq) { u32 intmr2; @@ -71,7 +77,7 @@ clps_writel(intmr2, INTMR2); } -static void mask_ack_irq_int2(unsigned int irq) +static void int2_ack(unsigned int irq) { u32 intmr2; @@ -84,7 +90,7 @@ } } -static void unmask_irq_int2(unsigned int irq) +static void int2_unmask(unsigned int irq) { u32 intmr2; @@ -93,28 +99,26 @@ clps_writel(intmr2, INTMR2); } +static struct irqchip int2_chip = { + ack: int2_ack, + mask: int2_mask, + unmask: int2_unmask, +}; + void __init clps711x_init_irq(void) { unsigned int i; for (i = 0; i < NR_IRQS; i++) { if (INT1_IRQS & (1 << i)) { - irq_desc[i].valid = 1; - irq_desc[i].probe_ok = 1; - irq_desc[i].mask_ack = (INT1_ACK_IRQS & (1 << i)) ? - mask_ack_irq_int1 : - mask_irq_int1; - irq_desc[i].mask = mask_irq_int1; - irq_desc[i].unmask = unmask_irq_int1; + set_irq_handler(i, do_level_IRQ); + set_irq_chip(i, &int1_chip); + set_irq_flags(i, IRQF_VALID | IRQF_PROBE); } if (INT2_IRQS & (1 << i)) { - irq_desc[i].valid = 1; - irq_desc[i].probe_ok = 1; - irq_desc[i].mask_ack = (INT2_ACK_IRQS & (1 << i)) ? - mask_ack_irq_int2 : - mask_irq_int2; - irq_desc[i].mask = mask_irq_int2; - irq_desc[i].unmask = unmask_irq_int2; + set_irq_handler(i, do_level_IRQ); + set_irq_chip(i, &int2_chip); + set_irq_flags(i, IRQF_VALID | IRQF_PROBE); } } diff -Nru a/arch/arm/mach-ebsa110/core.c b/arch/arm/mach-ebsa110/core.c --- a/arch/arm/mach-ebsa110/core.c Thu Mar 7 18:17:40 2002 +++ b/arch/arm/mach-ebsa110/core.c Thu Mar 7 18:17:40 2002 @@ -39,27 +39,31 @@ { __raw_writeb(1 << irq, IRQ_MSET); } + +static struct irqchip ebsa110_irq_chip = { + ack: ebsa110_mask_irq, + mask: ebsa110_mask_irq, + unmask: ebsa110_unmask_irq, +}; static void __init ebsa110_init_irq(void) { unsigned long flags; - int irq; + unsigned int irq; - save_flags_cli (flags); + local_irq_save(flags); __raw_writeb(0xff, IRQ_MCLR); __raw_writeb(0x55, IRQ_MSET); __raw_writeb(0x00, IRQ_MSET); if (__raw_readb(IRQ_MASK) != 0x55) while (1); __raw_writeb(0xff, IRQ_MCLR); /* clear all interrupt enables */ - restore_flags (flags); + local_irq_restore(flags); for (irq = 0; irq < NR_IRQS; irq++) { - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; - irq_desc[irq].mask_ack = ebsa110_mask_irq; - irq_desc[irq].mask = ebsa110_mask_irq; - irq_desc[irq].unmask = ebsa110_unmask_irq; + set_irq_chip(irq, &ebsa110_irq_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } } diff -Nru a/arch/arm/mach-epxa10db/arch.c b/arch/arm/mach-epxa10db/arch.c --- a/arch/arm/mach-epxa10db/arch.c Thu Mar 7 18:17:45 2002 +++ b/arch/arm/mach-epxa10db/arch.c Thu Mar 7 18:17:45 2002 @@ -18,7 +18,6 @@ * 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 @@ -46,7 +45,7 @@ mi->bank[0].node = 0; /* - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0, 0, 8192 ); setup_initrd(0xc0200000, 6*1024*1024); */ diff -Nru a/arch/arm/mach-epxa10db/irq.c b/arch/arm/mach-epxa10db/irq.c --- a/arch/arm/mach-epxa10db/irq.c Thu Mar 7 18:17:40 2002 +++ b/arch/arm/mach-epxa10db/irq.c Thu Mar 7 18:17:40 2002 @@ -18,28 +18,44 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include +#include #include #include -#include #include #include #include -static void mask_irq(unsigned int irq) +static void epxa_mask_irq(unsigned int irq) { - __raw_writel(1 << irq, INT_MC(IO_ADDRESS(EXC_INT_CTRL00_BASE))); + writel(1 << irq, INT_MC(IO_ADDRESS(EXC_INT_CTRL00_BASE))); } -static void unmask_irq(unsigned int irq) +static void epxa_unmask_irq(unsigned int irq) { - __raw_writel(1 << irq, INT_MS(IO_ADDRESS(EXC_INT_CTRL00_BASE))); + writel(1 << irq, INT_MS(IO_ADDRESS(EXC_INT_CTRL00_BASE))); } + +static struct irqchip epxa_irq_chip = { + ack: epxa_mask_irq, + mask: epxa_mask_irq, + unmask: epxa_unmask_irq, +}; + +static struct resource irq_resource = { + name: "irq_handler", + start: IO_ADDRESS(EXC_INT_CTRL00_BASE), + end: IO_ADDRESS(INT_PRIORITY_FC(EXC_INT_CTRL00_BASE))+4, +}; + void __init epxa10db_init_irq(void) { unsigned int i; + request_resource(&iomem_resource, &irq_resource); + /* * This bit sets up the interrupt controller using * the 6 PLD interrupts mode (the default) each @@ -49,22 +65,15 @@ * on the contents of your PLD */ - __raw_writel(3,INT_MODE(IO_ADDRESS(EXC_INT_CTRL00_BASE))); + writel(3,INT_MODE(IO_ADDRESS(EXC_INT_CTRL00_BASE))); for (i = 0; i < NR_IRQS; i++){ - __raw_writel(i+1, INT_PRIORITY_P0(IO_ADDRESS(EXC_INT_CTRL00_BASE)) + (4*i)); - } - - - for (i = 0; i < NR_IRQS; i++) { - - irq_desc[i].valid = 1; - irq_desc[i].probe_ok = 1; - irq_desc[i].mask_ack = mask_irq; - irq_desc[i].mask = mask_irq; - irq_desc[i].unmask = unmask_irq; + writel(i+1, INT_PRIORITY_P0(IO_ADDRESS(EXC_INT_CTRL00_BASE)) + (4*i)); + set_irq_chip(i,&epxa_irq_chip); + set_irq_handler(i,do_level_IRQ); + set_irq_flags(i, IRQF_VALID | IRQF_PROBE); } - /* Disable all interrupt */ - __raw_writel(-1,INT_MC(IO_ADDRESS(EXC_INT_CTRL00_BASE))); + /* Disable all interrupts */ + writel(-1,INT_MC(IO_ADDRESS(EXC_INT_CTRL00_BASE))); } diff -Nru a/arch/arm/mach-footbridge/Makefile b/arch/arm/mach-footbridge/Makefile --- a/arch/arm/mach-footbridge/Makefile Thu Mar 7 18:17:44 2002 +++ b/arch/arm/mach-footbridge/Makefile Thu Mar 7 18:17:44 2002 @@ -11,7 +11,7 @@ # Object file lists. -obj-y := arch.o dc21285.o dma.o irq.o mm.o +obj-y := arch.o dc21285.o dma.o irq.o isa-irq.o mm.o obj-m := obj-n := obj- := diff -Nru a/arch/arm/mach-footbridge/irq.c b/arch/arm/mach-footbridge/irq.c --- a/arch/arm/mach-footbridge/irq.c Thu Mar 7 18:17:44 2002 +++ b/arch/arm/mach-footbridge/irq.c Thu Mar 7 18:17:44 2002 @@ -27,6 +27,8 @@ #include #include +extern void __init isa_init_irq(unsigned int irq); + /* * Footbridge IRQ translation table * Converts from our IRQ numbers into FootBridge masks @@ -64,9 +66,15 @@ *CSR_IRQ_ENABLE = fb_irq_mask[_DC21285_INR(irq)]; } +static struct irqchip fb_chip = { + ack: fb_mask_irq, + mask: fb_mask_irq, + unmask: fb_unmask_irq, +}; + static void __init __fb_init_irq(void) { - int irq; + unsigned int irq; /* * setup DC21285 IRQs @@ -75,128 +83,9 @@ *CSR_FIQ_DISABLE = -1; for (irq = _DC21285_IRQ(0); irq < _DC21285_IRQ(20); irq++) { - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; - irq_desc[irq].mask_ack = fb_mask_irq; - irq_desc[irq].mask = fb_mask_irq; - irq_desc[irq].unmask = fb_unmask_irq; - } -} - -extern int isa_irq; - -static void isa_mask_pic_lo_irq(unsigned int irq) -{ - unsigned int mask = 1 << (irq & 7); - - outb(inb(PIC_MASK_LO) | mask, PIC_MASK_LO); -} - -static void isa_mask_ack_pic_lo_irq(unsigned int irq) -{ - unsigned int mask = 1 << (irq & 7); - - outb(inb(PIC_MASK_LO) | mask, PIC_MASK_LO); - outb(0x20, PIC_LO); -} - -static void isa_unmask_pic_lo_irq(unsigned int irq) -{ - unsigned int mask = 1 << (irq & 7); - - outb(inb(PIC_MASK_LO) & ~mask, PIC_MASK_LO); -} - -static void isa_mask_pic_hi_irq(unsigned int irq) -{ - unsigned int mask = 1 << (irq & 7); - - outb(inb(PIC_MASK_HI) | mask, PIC_MASK_HI); -} - -static void isa_mask_ack_pic_hi_irq(unsigned int irq) -{ - unsigned int mask = 1 << (irq & 7); - - outb(inb(PIC_MASK_HI) | mask, PIC_MASK_HI); - outb(0x62, PIC_LO); - outb(0x20, PIC_HI); -} - -static void isa_unmask_pic_hi_irq(unsigned int irq) -{ - unsigned int mask = 1 << (irq & 7); - - outb(inb(PIC_MASK_HI) & ~mask, PIC_MASK_HI); -} - -static void no_action(int irq, void *dev_id, struct pt_regs *regs) -{ -} - -static struct irqaction irq_cascade = { handler: no_action, name: "cascade", }; -static struct resource pic1_resource = { "pic1", 0x20, 0x3f }; -static struct resource pic2_resource = { "pic2", 0xa0, 0xbf }; - -static void __init isa_init_irq(int irq) -{ - /* - * Setup, and then probe for an ISA PIC - * If the PIC is not there, then we - * ignore the PIC. - */ - outb(0x11, PIC_LO); - outb(_ISA_IRQ(0), PIC_MASK_LO); /* IRQ number */ - outb(0x04, PIC_MASK_LO); /* Slave on Ch2 */ - outb(0x01, PIC_MASK_LO); /* x86 */ - outb(0xf5, PIC_MASK_LO); /* pattern: 11110101 */ - - outb(0x11, PIC_HI); - outb(_ISA_IRQ(8), PIC_MASK_HI); /* IRQ number */ - outb(0x02, PIC_MASK_HI); /* Slave on Ch1 */ - outb(0x01, PIC_MASK_HI); /* x86 */ - outb(0xfa, PIC_MASK_HI); /* pattern: 11111010 */ - - outb(0x0b, PIC_LO); - outb(0x0b, PIC_HI); - - if (inb(PIC_MASK_LO) == 0xf5 && inb(PIC_MASK_HI) == 0xfa) { - outb(0xff, PIC_MASK_LO);/* mask all IRQs */ - outb(0xff, PIC_MASK_HI);/* mask all IRQs */ - isa_irq = irq; - } else - isa_irq = -1; - - if (isa_irq != -1) { - for (irq = _ISA_IRQ(0); irq < _ISA_IRQ(8); irq++) { - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; - irq_desc[irq].mask_ack = isa_mask_ack_pic_lo_irq; - irq_desc[irq].mask = isa_mask_pic_lo_irq; - irq_desc[irq].unmask = isa_unmask_pic_lo_irq; - } - - for (irq = _ISA_IRQ(8); irq < _ISA_IRQ(16); irq++) { - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; - irq_desc[irq].mask_ack = isa_mask_ack_pic_hi_irq; - irq_desc[irq].mask = isa_mask_pic_hi_irq; - irq_desc[irq].unmask = isa_unmask_pic_hi_irq; - } - - request_resource(&ioport_resource, &pic1_resource); - request_resource(&ioport_resource, &pic2_resource); - setup_arm_irq(IRQ_ISA_CASCADE, &irq_cascade); - setup_arm_irq(isa_irq, &irq_cascade); - - /* - * On the NetWinder, don't automatically - * enable ISA IRQ11 when it is requested. - * There appears to be a missing pull-up - * resistor on this line. - */ - if (machine_is_netwinder()) - irq_desc[_ISA_IRQ(11)].noautoenable = 1; + set_irq_chip(irq, &fb_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } } diff -Nru a/arch/arm/mach-footbridge/isa-irq.c b/arch/arm/mach-footbridge/isa-irq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mach-footbridge/isa-irq.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,170 @@ +/* + * linux/arch/arm/mach-footbridge/irq.c + * + * Copyright (C) 1996-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Changelog: + * 22-Aug-1998 RMK Restructured IRQ routines + * 03-Sep-1998 PJB Merged CATS support + * 20-Jan-1998 RMK Started merge of EBSA286, CATS and NetWinder + * 26-Jan-1999 PJB Don't use IACK on CATS + * 16-Mar-1999 RMK Added autodetect of ISA PICs + */ +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +static void isa_mask_pic_lo_irq(unsigned int irq) +{ + unsigned int mask = 1 << (irq & 7); + + outb(inb(PIC_MASK_LO) | mask, PIC_MASK_LO); +} + +static void isa_ack_pic_lo_irq(unsigned int irq) +{ + unsigned int mask = 1 << (irq & 7); + + outb(inb(PIC_MASK_LO) | mask, PIC_MASK_LO); + outb(0x20, PIC_LO); +} + +static void isa_unmask_pic_lo_irq(unsigned int irq) +{ + unsigned int mask = 1 << (irq & 7); + + outb(inb(PIC_MASK_LO) & ~mask, PIC_MASK_LO); +} + +static struct irqchip isa_lo_chip = { + ack: isa_ack_pic_lo_irq, + mask: isa_mask_pic_lo_irq, + unmask: isa_unmask_pic_lo_irq, +}; + +static void isa_mask_pic_hi_irq(unsigned int irq) +{ + unsigned int mask = 1 << (irq & 7); + + outb(inb(PIC_MASK_HI) | mask, PIC_MASK_HI); +} + +static void isa_ack_pic_hi_irq(unsigned int irq) +{ + unsigned int mask = 1 << (irq & 7); + + outb(inb(PIC_MASK_HI) | mask, PIC_MASK_HI); + outb(0x62, PIC_LO); + outb(0x20, PIC_HI); +} + +static void isa_unmask_pic_hi_irq(unsigned int irq) +{ + unsigned int mask = 1 << (irq & 7); + + outb(inb(PIC_MASK_HI) & ~mask, PIC_MASK_HI); +} + +static struct irqchip isa_hi_chip = { + ack: isa_ack_pic_hi_irq, + mask: isa_mask_pic_hi_irq, + unmask: isa_unmask_pic_hi_irq, +}; + +static void no_action(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +static void +isa_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) +{ + unsigned int isa_irq = *(unsigned char *)PCIIACK_BASE; + + if (isa_irq < _ISA_IRQ(0) || isa_irq >= _ISA_IRQ(16)) { + do_bad_IRQ(isa_irq, desc, regs); + return; + } + + desc = irq_desc + isa_irq; + desc->handle(isa_irq, desc, regs); +} + +static struct irqaction irq_cascade = { handler: no_action, name: "cascade", }; +static struct resource pic1_resource = { "pic1", 0x20, 0x3f }; +static struct resource pic2_resource = { "pic2", 0xa0, 0xbf }; + +void __init isa_init_irq(unsigned int irq) +{ + /* + * Setup, and then probe for an ISA PIC + * If the PIC is not there, then we + * ignore the PIC. + */ + outb(0x11, PIC_LO); + outb(_ISA_IRQ(0), PIC_MASK_LO); /* IRQ number */ + outb(0x04, PIC_MASK_LO); /* Slave on Ch2 */ + outb(0x01, PIC_MASK_LO); /* x86 */ + outb(0xf5, PIC_MASK_LO); /* pattern: 11110101 */ + + outb(0x11, PIC_HI); + outb(_ISA_IRQ(8), PIC_MASK_HI); /* IRQ number */ + outb(0x02, PIC_MASK_HI); /* Slave on Ch1 */ + outb(0x01, PIC_MASK_HI); /* x86 */ + outb(0xfa, PIC_MASK_HI); /* pattern: 11111010 */ + + outb(0x0b, PIC_LO); + outb(0x0b, PIC_HI); + + if (inb(PIC_MASK_LO) == 0xf5 && inb(PIC_MASK_HI) == 0xfa) { + outb(0xff, PIC_MASK_LO);/* mask all IRQs */ + outb(0xff, PIC_MASK_HI);/* mask all IRQs */ + } else { + printk(KERN_INFO "IRQ: ISA PIC not found\n"); + irq = -1; + } + + if (irq != -1) { + for (irq = _ISA_IRQ(0); irq < _ISA_IRQ(8); irq++) { + set_irq_chip(irq, &isa_lo_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + + for (irq = _ISA_IRQ(8); irq < _ISA_IRQ(16); irq++) { + set_irq_chip(irq, &isa_hi_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + + request_resource(&ioport_resource, &pic1_resource); + request_resource(&ioport_resource, &pic2_resource); + setup_irq(IRQ_ISA_CASCADE, &irq_cascade); + + set_irq_chained_handler(irq, isa_irq_handler); + + /* + * On the NetWinder, don't automatically + * enable ISA IRQ11 when it is requested. + * There appears to be a missing pull-up + * resistor on this line. + */ + if (machine_is_netwinder()) + set_irq_flags(_ISA_IRQ(11), IRQF_VALID | + IRQF_PROBE | IRQF_NOAUTOEN); + } +} + + diff -Nru a/arch/arm/mach-footbridge/netwinder-hw.c b/arch/arm/mach-footbridge/netwinder-hw.c --- a/arch/arm/mach-footbridge/netwinder-hw.c Thu Mar 7 18:17:38 2002 +++ b/arch/arm/mach-footbridge/netwinder-hw.c Thu Mar 7 18:17:38 2002 @@ -320,7 +320,7 @@ */ spin_lock_irqsave(&gpio_lock, flags); gpio_modify_op(-1, GPIO_RED_LED | GPIO_FAN); - spin_unlock_irqrestore(&gpio_loc, flags); + spin_unlock_irqrestore(&gpio_lock, flags); } /* diff -Nru a/arch/arm/mach-ftvpci/core.c b/arch/arm/mach-ftvpci/core.c --- a/arch/arm/mach-ftvpci/core.c Thu Mar 7 18:17:41 2002 +++ b/arch/arm/mach-ftvpci/core.c Thu Mar 7 18:17:41 2002 @@ -8,7 +8,6 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff -Nru a/arch/arm/mach-integrator/irq.c b/arch/arm/mach-integrator/irq.c --- a/arch/arm/mach-integrator/irq.c Thu Mar 7 18:17:46 2002 +++ b/arch/arm/mach-integrator/irq.c Thu Mar 7 18:17:46 2002 @@ -46,21 +46,17 @@ { __raw_writel(1 << irq, VA_IC_BASE + IRQ_ENABLE_SET); } + +static struct irqchip sc_chip = { + ack: sc_mask_irq, + mask: sc_mask_irq, + unmask: sc_unmask_irq, +}; void __init integrator_init_irq(void) { unsigned int i; - for (i = 0; i < NR_IRQS; i++) { - if (((1 << i) && INTEGRATOR_SC_VALID_INT) != 0) { - irq_desc[i].valid = 1; - irq_desc[i].probe_ok = 1; - irq_desc[i].mask_ack = sc_mask_irq; - irq_desc[i].mask = sc_mask_irq; - irq_desc[i].unmask = sc_unmask_irq; - } - } - /* Disable all interrupts initially. */ /* Do the core module ones */ __raw_writel(-1, VA_CMIC_BASE + IRQ_ENABLE_CLEAR); @@ -68,4 +64,12 @@ /* do the header card stuff next */ __raw_writel(-1, VA_IC_BASE + IRQ_ENABLE_CLEAR); __raw_writel(-1, VA_IC_BASE + FIQ_ENABLE_CLEAR); + + for (i = 0; i < NR_IRQS; i++) { + if (((1 << i) && INTEGRATOR_SC_VALID_INT) != 0) { + set_irq_chip(i, &sc_chip); + set_irq_handler(i, do_level_IRQ); + set_irq_flags(i, IRQF_VALID | IRQF_PROBE); + } + } } diff -Nru a/arch/arm/mach-integrator/pci.c b/arch/arm/mach-integrator/pci.c --- a/arch/arm/mach-integrator/pci.c Thu Mar 7 18:17:44 2002 +++ b/arch/arm/mach-integrator/pci.c Thu Mar 7 18:17:44 2002 @@ -110,7 +110,6 @@ return irq_tab[intnr]; } -extern void pci_v3_setup_resources(struct resource **res); extern void pci_v3_init(void *); struct hw_pci integrator_pci __initdata = { @@ -118,6 +117,7 @@ swizzle: integrator_swizzle, map_irq: integrator_map_irq, setup: pci_v3_setup, + nr_controllers: 1, scan: pci_v3_scan_bus, preinit: pci_v3_preinit, postinit: pci_v3_postinit, diff -Nru a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c --- a/arch/arm/mach-integrator/pci_v3.c Thu Mar 7 18:17:39 2002 +++ b/arch/arm/mach-integrator/pci_v3.c Thu Mar 7 18:17:39 2002 @@ -20,6 +20,7 @@ * 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 @@ -37,8 +38,6 @@ #include -int setup_arm_irq(int irq, struct irqaction * new); - /* * The V3 PCI interface chip in Integrator provides several windows from * local bus memory into the PCI memory areas. Unfortunately, there @@ -413,12 +412,19 @@ flags: IORESOURCE_MEM | IORESOURCE_PREFETCH, }; -static void __init pci_v3_setup_resources(struct resource **resource) +static int __init pci_v3_setup_resources(struct resource **resource) { - if (request_resource(&iomem_resource, &non_mem)) - printk("PCI: unable to allocate non-prefetchable memory region\n"); - if (request_resource(&iomem_resource, &pre_mem)) - printk("PCI: unable to allocate prefetchable memory region\n"); + if (request_resource(&iomem_resource, &non_mem)) { + printk(KERN_ERR "PCI: unable to allocate non-prefetchable " + "memory region\n"); + return -EBUSY; + } + if (request_resource(&iomem_resource, &pre_mem)) { + release_resource(&non_mem); + printk(KERN_ERR "PCI: unable to allocate prefetchable " + "memory region\n"); + return -EBUSY; + } /* * bus->resource[0] is the IO resource for this bus @@ -428,6 +434,8 @@ resource[0] = &ioport_resource; resource[1] = &non_mem; resource[2] = &pre_mem; + + return 0; } /* @@ -443,13 +451,15 @@ { unsigned long pc = instruction_pointer(regs); unsigned long instr = *(unsigned long *)pc; -// char buf[128]; +#if 0 + char buf[128]; -// sprintf(buf, "V3 fault: address=0x%08lx, pc=0x%08lx [%08lx] LBFADDR=%08x LBFCODE=%02x ISTAT=%02x\n", -// addr, pc, instr, __raw_readl(SC_LBFADDR), __raw_readl(SC_LBFCODE) & 255, -// v3_readb(V3_LB_ISTAT)); -// printk("%s", buf); -// printascii(buf); + sprintf(buf, "V3 fault: address=0x%08lx, pc=0x%08lx [%08lx] LBFADDR=%08x LBFCODE=%02x ISTAT=%02x\n", + addr, pc, instr, __raw_readl(SC_LBFADDR), __raw_readl(SC_LBFCODE) & 255, + v3_readb(V3_LB_ISTAT)); + printk(KERN_DEBUG "%s", buf); + printascii(buf); +#endif v3_writeb(V3_LB_ISTAT, 0); __raw_writel(3, SC_PCI); @@ -513,28 +523,21 @@ #endif } -static struct irqaction v3_int = { - name: "V3", - handler: v3_irq, -}; - -static struct irqaction v3_int2 = { - name: "V3TM", - handler: v3_irq, -}; - extern int (*external_fault)(unsigned long addr, struct pt_regs *regs); -int pci_v3_setup(int nr, struct pci_sys_data *sys) +int __init pci_v3_setup(int nr, struct pci_sys_data *sys) { - if (nr) - pci_v3_setup_resources(sys->resource); - return nr ? 0 : 1; + int ret = 0; + + if (nr == 0) + ret = pci_v3_setup_resources(sys->resource); + + return ret; } struct pci_bus *pci_v3_scan_bus(int nr, struct pci_sys_data *sys) { - return pci_scan_bus(sys->busnr, &pci_v3_ops, sys); + return pci_scan_bus(sys->busnr, &pci_v3_ops, sys); } /* @@ -545,6 +548,7 @@ { unsigned long flags; unsigned int temp; + int ret; /* * Hook in our fault handler for PCI errors @@ -592,7 +596,7 @@ temp |= V3_PCI_CFG_M_IO_REG_DIS | V3_PCI_CFG_M_IO_DIS; v3_writew(V3_PCI_CFG, temp); - printk("FIFO_CFG: %04x FIFO_PRIO: %04x\n", + printk(KERN_DEBUG "FIFO_CFG: %04x FIFO_PRIO: %04x\n", v3_readw(V3_FIFO_CFG), v3_readw(V3_FIFO_PRIORITY)); /* @@ -619,7 +623,10 @@ /* * Grab the PCI error interrupt. */ - setup_arm_irq(IRQ_V3INT, &v3_int); + ret = request_irq(IRQ_V3INT, v3_irq, 0, "V3", NULL); + if (ret) + printk(KERN_ERR "PCI: unable to grab PCI error " + "interrupt: %d\n", ret); spin_unlock_irqrestore(&v3_lock, flags); } @@ -627,6 +634,7 @@ void __init pci_v3_postinit(void) { unsigned int pci_cmd; + int ret; pci_cmd = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE; @@ -636,5 +644,10 @@ v3_writeb(V3_LB_ISTAT, ~0x40); v3_writeb(V3_LB_IMASK, 0x68); -// setup_arm_irq(IRQ_LBUSTIMEOUT, &v3_int2); +#if 0 + ret = request_irq(IRQ_LBUSTIMEOUT, lb_timeout, 0, "bus timeout", NULL); + if (ret) + printk(KERN_ERR "PCI: unable to grab local bus timeout " + "interrupt: %d\n", ret); +#endif } diff -Nru a/arch/arm/mach-iop310/arch.c b/arch/arm/mach-iop310/arch.c --- a/arch/arm/mach-iop310/arch.c Thu Mar 7 18:17:45 2002 +++ b/arch/arm/mach-iop310/arch.c Thu Mar 7 18:17:45 2002 @@ -9,7 +9,7 @@ * published by the Free Software Foundation. * */ - +#include #include #include #include @@ -44,7 +44,7 @@ #elif defined(CONFIG_BLK_DEV_INITRD) setup_ramdisk( 1, 0, 0, CONFIG_BLK_DEV_RAM_SIZE ); setup_initrd( 0xc0800000, 4*1024*1024 ); - ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); /* /dev/ram */ + ROOT_DEV = mk_kdev(RAMDISK_MAJOR, 0); /* /dev/ram */ #endif } diff -Nru a/arch/arm/mach-iop310/iop310-irq.c b/arch/arm/mach-iop310/iop310-irq.c --- a/arch/arm/mach-iop310/iop310-irq.c Thu Mar 7 18:17:46 2002 +++ b/arch/arm/mach-iop310/iop310-irq.c Thu Mar 7 18:17:46 2002 @@ -13,8 +13,6 @@ * Added IOP310 chipset and IQ80310 board demuxing, masking code. - DS * */ - -#include #include #include #include @@ -31,40 +29,46 @@ extern void do_IRQ(int, struct pt_regs *); -u32 iop310_mask = 0; +static u32 iop310_mask /* = 0 */; -static void -iop310_irq_mask (unsigned int irq) +static void iop310_irq_mask (unsigned int irq) { - iop310_mask |= (1 << (irq - IOP310_IRQ_OFS)); + iop310_mask ++; /* * No mask bits on the 80312, so we have to * mask everything from the outside! */ - xs80200_irq_mask(IRQ_XS80200_EXTIRQ); + if (iop310_mask == 1) { + disable_irq(IRQ_XS80200_EXTIRQ); + irq_desc[IRQ_XS80200_EXTIRQ].chip->mask(IRQ_XS80200_EXTIRQ); + } } -static void -iop310_irq_unmask (unsigned int irq) +static void iop310_irq_unmask (unsigned int irq) { - iop310_mask &= ~(1 << (irq - IOP310_IRQ_OFS)); + if (iop310_mask) + iop310_mask --; /* * Check if all 80312 sources are unmasked now */ - if(!iop310_mask) - { - xs80200_irq_unmask(IRQ_XS80200_EXTIRQ); - - } + if (iop310_mask == 0) + enable_irq(IRQ_XS80200_EXTIRQ); } -void iop310_irq_demux(int irq, void *dev_id, - struct pt_regs *regs) +struct irqchip ext_chip = { + ack: iop310_irq_mask, + mask: iop310_irq_mask, + unmask: iop310_irq_unmask, +}; + +void +iop310_irq_demux(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) { u32 fiq1isr = *((volatile u32*)IOP310_FIQ1ISR); u32 fiq2isr = *((volatile u32*)IOP310_FIQ2ISR); + struct irqdesc *d; unsigned int irqno = 0; if(fiq1isr) @@ -88,22 +92,22 @@ irqno = IRQ_IOP310_MU; } - do_IRQ(irqno, regs); + if (irqno) { + d = irq_desc + irqno; + d->handle(irqno, d, regs); + } } void __init iop310_init_irq(void) { - int i; + unsigned int i; for(i = IOP310_IRQ_OFS; i < NR_IOP310_IRQS; i++) { - irq_desc[i].valid = 1; - irq_desc[i].probe_ok = 1; - irq_desc[i].mask_ack = iop310_irq_mask; - irq_desc[i].mask = iop310_irq_mask; - irq_desc[i].unmask = iop310_irq_unmask; + set_irq_chip(i, &ext_chip); + set_irq_handler(i, do_level_IRQ); + set_irq_flags(i, IRQF_VALID | IRQF_PROBE); } xs80200_init_irq(); } - diff -Nru a/arch/arm/mach-iop310/iop310-pci.c b/arch/arm/mach-iop310/iop310-pci.c --- a/arch/arm/mach-iop310/iop310-pci.c Thu Mar 7 18:17:42 2002 +++ b/arch/arm/mach-iop310/iop310-pci.c Thu Mar 7 18:17:42 2002 @@ -52,7 +52,7 @@ #ifdef DEBUG #define DBG(x...) printk(x) #else -#define DBG(x...) +#define DBG(x...) do { } while (0) #endif extern int (*external_fault)(unsigned long, struct pt_regs *); @@ -247,7 +247,8 @@ *IOP310_SATUISR = uisr & 0x0000069f; ret = 1; } -//if (ret) printk("ERROR (%08lx %08lx)", usr, uisr); + if (ret) + DBG("ERROR (%08lx %08lx)", usr, uisr); return ret; } @@ -256,16 +257,19 @@ { int ret; u8 val; -//printk("rdb: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where); + + DBG("rdb: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where); *IOP310_SOCCAR = iop310_cfg_address(dev, where); val = (*IOP310_SOCCDR) >> ((where & 3) * 8); __asm__ __volatile__("nop; nop; nop; nop"); -//printk(">= %08lx ", val); + + DBG(">= %08lx ", val); ret = iop310_sec_pci_status(); if (ret) val = 0xff; -//printk("\n"); + DBG("\n"); *p = val; return PCIBIOS_SUCCESSFUL; @@ -276,16 +280,19 @@ { int ret; u16 val; -//printk("rdw: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where); + + DBG("rdw: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where); *IOP310_SOCCAR = iop310_cfg_address(dev, where); val = (*IOP310_SOCCDR) >> ((where & 3) * 8); __asm__ __volatile__("nop; nop; nop; nop"); -//printk(">= %08lx ", val); + + DBG(">= %08lx ", val); ret = iop310_sec_pci_status(); if (ret) val = 0xffff; -//printk("\n"); + DBG("\n"); *p = val; return PCIBIOS_SUCCESSFUL; @@ -296,16 +303,19 @@ { int ret; u32 val; -//printk("rdl: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where); + + DBG("rdl: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where); *IOP310_SOCCAR = iop310_cfg_address(dev, where); val = *IOP310_SOCCDR; __asm__ __volatile__("nop; nop; nop; nop"); -//printk(">= %08lx ", val); + + DBG(">= %08lx ", val); ret = iop310_sec_pci_status(); if (ret) val = 0xffffffff; -//printk("\n"); + DBG("\n"); *p = val; return PCIBIOS_SUCCESSFUL; @@ -316,12 +326,15 @@ { int ret; u32 val; -//printk("wrb: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where); + + DBG("wrb: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where); *IOP310_SOCCAR = iop310_cfg_address(dev, where); val = *IOP310_SOCCDR; __asm__ __volatile__("nop; nop; nop; nop"); -//printk("<= %08lx", v); + + DBG("<= %08lx", v); ret = iop310_sec_pci_status(); if (ret == 0) { where = (where & 3) * 8; @@ -329,7 +342,7 @@ val |= v << where; *IOP310_SOCCDR = val; } -//printk("\n"); + DBG("\n"); return PCIBIOS_SUCCESSFUL; } @@ -338,12 +351,15 @@ { int ret; u32 val; -//printk("wrw: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where); + + DBG("wrw: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where); *IOP310_SOCCAR = iop310_cfg_address(dev, where); val = *IOP310_SOCCDR; __asm__ __volatile__("nop; nop; nop; nop"); -//printk("<= %08lx", v); + + DBG("<= %08lx", v); ret = iop310_sec_pci_status(); if (ret == 0) { where = (where & 2) * 8; @@ -351,18 +367,20 @@ val |= v << where; *IOP310_SOCCDR = val; } -//printk("\n"); + DBG("\n"); return PCIBIOS_SUCCESSFUL; } static int iop310_sec_wr_cfg_dword(struct pci_dev *dev, int where, u32 v) { -//printk("wrl: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where); + DBG("wrl: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where); *IOP310_SOCCAR = iop310_cfg_address(dev, where); *IOP310_SOCCDR = v; __asm__ __volatile__("nop; nop; nop; nop"); -//printk("<= %08lx\n", v); + + DBG("<= %08lx\n", v); return PCIBIOS_SUCCESSFUL; } @@ -381,8 +399,8 @@ */ int iop310_pci_abort_handler(unsigned long addr, struct pt_regs *regs) { -// printk("PCI abort: address = %08x PC = %08x LR = %08lx\n", -// addr, regs->ARM_pc, regs->ARM_lr); + DBG("PCI abort: address = %08x PC = %08x LR = %08lx\n", + addr, regs->ARM_pc, regs->ARM_lr); return 0; } @@ -443,11 +461,11 @@ case 1: res[0].start = IOP310_PCISEC_LOWER_IO + 0x6e000000; res[0].end = IOP310_PCISEC_LOWER_IO + 0x6e00ffff; - res[0].name = "PCI IO Primary"; + res[0].name = "PCI IO Secondary"; res[1].start = IOP310_PCISEC_LOWER_MEM; res[1].end = IOP310_PCISEC_LOWER_MEM + IOP310_PCI_WINDOW_SIZE; - res[1].name = "PCI Memory Primary"; + res[1].name = "PCI Memory Secondary"; break; } @@ -465,18 +483,17 @@ void iop310_init(void) { DBG("PCI: Intel 80312 PCI-to-PCI init code.\n"); - DBG(" ATU secondary: IOP310_SOMWVR=0x%04x, IOP310_SOIOWVR=0x%04x\n", - *IOP310_SOMWVR, - *IOP310_SOIOWVR); - DBG(" ATU secondary: IOP310_ATUCR=0x%08x\n", *IOP310_ATUCR); - DBG(" ATU secondary: IOP310_SIABAR=0x%08x IOP310_SIALR=0x%08x IOP310_SIATVR=%08x\n", *IOP310_SIABAR, *IOP310_SIALR, *IOP310_SIATVR); - - DBG(" ATU primary: IOP310_POMWVR=0x%04x, IOP310_POIOWVR=0x%04x\n", - *IOP310_POMWVR, - *IOP310_POIOWVR); - DBG(" ATU secondary: IOP310_PIABAR=0x%08x IOP310_PIALR=0x%08x IOP310_PIATVR=%08x\n", *IOP310_PIABAR, *IOP310_PIALR, *IOP310_PIATVR); - - DBG(" P2P: IOP310_PCR=0x%04x IOP310_BCR=0x%04x IOP310_EBCR=0x%04x\n", *IOP310_PCR, *IOP310_BCR, *IOP310_EBCR); + DBG(" ATU secondary: ATUCR =0x%08x\n", *IOP310_ATUCR); + DBG(" ATU secondary: SOMWVR=0x%08x SOIOWVR=0x%08x\n", + *IOP310_SOMWVR, *IOP310_SOIOWVR); + DBG(" ATU secondary: SIABAR=0x%08x SIALR =0x%08x SIATVR=%08x\n", + *IOP310_SIABAR, *IOP310_SIALR, *IOP310_SIATVR); + DBG(" ATU primary: POMWVR=0x%08x POIOWVR=0x%08x\n", + *IOP310_POMWVR, *IOP310_POIOWVR); + DBG(" ATU primary: PIABAR=0x%08x PIALR =0x%08x PIATVR=%08x\n", + *IOP310_PIABAR, *IOP310_PIALR, *IOP310_PIATVR); + DBG(" P2P: PCR=0x%04x BCR=0x%04x EBCR=0x%04x\n", + *IOP310_PCR, *IOP310_BCR, *IOP310_EBCR); /* * Windows have to be carefully opened via a nice set of calls @@ -495,6 +512,5 @@ *IOP310_PCR &= 0xfff8; external_fault = iop310_pci_abort_handler; - } diff -Nru a/arch/arm/mach-iop310/iq80310-irq.c b/arch/arm/mach-iop310/iq80310-irq.c --- a/arch/arm/mach-iop310/iq80310-irq.c Thu Mar 7 18:17:39 2002 +++ b/arch/arm/mach-iop310/iq80310-irq.c Thu Mar 7 18:17:39 2002 @@ -14,8 +14,6 @@ * Moved demux from asm to C - DS * Fixes for various revision boards - DS */ - -#include #include #include #include @@ -29,121 +27,86 @@ #include -extern void xs80200_irq_mask(unsigned int); -extern void xs80200_irq_unmask(unsigned int); -extern void xs80200_init_irq(void); +extern void iop310_init_irq(void); +extern void iop310_irq_demux(unsigned int, struct irqdesc *, struct pt_regs *); -extern void do_IRQ(int, struct pt_regs *); +static void iq80310_irq_mask(unsigned int irq) +{ + *(volatile char *)IQ80310_INT_MASK |= (1 << (irq - IQ80310_IRQ_OFS)); +} -extern u32 iop310_mask; +static void iq80310_irq_unmask(unsigned int irq) +{ + *(volatile char *)IQ80310_INT_MASK &= ~(1 << (irq - IQ80310_IRQ_OFS)); +} -extern void iop310_irq_demux(int, void *, struct pt_regs *); +static struct irqchip iq80310_irq_chip = { + ack: iq80310_irq_mask, + mask: iq80310_irq_mask, + unmask: iq80310_irq_unmask, +}; -extern int iop310_init_irq(void); +extern struct irqchip ext_chip; static void -iq80310_irq_mask (unsigned int irq) +iq80310_cpld_irq_handler(unsigned int irq, struct irqdesc *desc, + struct pt_regs *regs) { - volatile char *mask = (volatile char *)IQ80310_INT_MASK; - *mask |= (1 << (irq - IQ80310_IRQ_OFS)); + unsigned int irq_stat = *(volatile u8*)IQ80310_INT_STAT; + unsigned int irq_mask = *(volatile u8*)IQ80310_INT_MASK; + unsigned int i, handled = 0; + struct irqdesc *d; + + desc->chip->ack(irq); /* - * There's no mask for PCI INT A-C, so we just mask out all - * external interrupts on the CPU. - * - * We set a bit of the iop310 mask so that the iop310_irq_mask - * function does not unmask EXTINT + * Mask out the interrupts which aren't enabled. */ - if (irq > IRQ_IQ80310_INTD) - { - xs80200_irq_mask(IRQ_XS80200_EXTIRQ); - iop310_mask |= (0x80000000 >> (irq - IRQ_IQ80310_INTD)); - } -} - -static void -iq80310_irq_unmask (unsigned int irq) -{ - volatile char *mask = (volatile char *)IQ80310_INT_MASK; - *mask &= ~(1 << (irq - IQ80310_IRQ_OFS)); + irq_stat &= 0x1f & ~irq_mask; /* - * See comment above + * Test each IQ80310 CPLD interrupt */ - if (irq > IRQ_IQ80310_INTD) - { - xs80200_irq_unmask(IRQ_XS80200_EXTIRQ); - iop310_mask &= ~((0x80000000 >> (irq - IRQ_IQ80310_INTD))); - } -} - -static void iq80310_cpld_irq_demux(int irq, void *dev_id, - struct pt_regs *regs) -{ - u8 irq_stat = *((volatile u8*)IQ80310_INT_STAT); - u8 irq_mask = *((volatile u8*)IQ80310_INT_MASK); - unsigned int irqno = 0xffffffff; - - // Needed? If IRQ is masked, it shouldn't get through... - irq_stat &= ~irq_mask; - - - if(irq_stat & 0x01) - irqno = IRQ_IQ80310_TIMER; - else if(irq_stat & 0x02) - irqno = IRQ_IQ80310_I82559; - else if(irq_stat & 0x04) - irqno = IRQ_IQ80310_UART1; - else if(irq_stat & 0x08) - irqno = IRQ_IQ80310_UART2; - else if(irq_stat & 0x10) - irqno = IRQ_IQ80310_INTD; - else if(system_rev) - { - irq_stat = *((volatile u8*)IQ80310_PCI_INT_STAT) & 0xf; - - if(irq_stat & 0x1) - irqno = IRQ_IQ80310_INTA; - else if(irq_stat & 0x2) - irqno = IRQ_IQ80310_INTB; - else if(irq_stat & 0x4) - irqno = IRQ_IQ80310_INTC; - } - else /* Running on a REV D.1 or older, assume PCI INTA */ - irqno = IRQ_IQ80310_INTA; + for (i = IRQ_IQ80310_TIMER, d = irq_desc + IRQ_IQ80310_TIMER; + irq_stat; i++, d++, irq_stat >>= 1) + if (irq_stat & 1) { + d->handle(i, d, regs); + handled++; + } /* - * If we didn't read a CPLD interrupt, we assume it's from - * a device on the chipset itself. + * If running on a board later than REV D.1, we can + * decode the PCI interrupt status. */ - if(irqno == 0xffffffff) - { - iop310_irq_demux(irq, dev_id, regs); - return; + if (system_rev) { + irq_stat = *((volatile u8*)IQ80310_PCI_INT_STAT) & 7; + + for (i = IRQ_IQ80310_INTA, d = irq_desc + IRQ_IQ80310_INTA; + irq_stat; i++, d++, irq_stat >>= 1) + if (irq_stat & 0x1) { + d->handle(i, d, regs); + handled++; + } } /* - * If on a REV D.1 or lower board, we just assumed INTA since - * PCI is not routed, and it may actually be an on-chip interrupt. + * If on a REV D.1 or lower board, we just assumed INTA + * since PCI is not routed, and it may actually be an + * on-chip interrupt. * - * Note that we're giving on-chip interrupts slightly higher - * priority than PCI by handling them first. + * Note that we're giving on-chip interrupts slightly + * higher priority than PCI by handling them first. + * + * On boards later than REV D.1, if we didn't read a + * CPLD interrupt, we assume it's from a device on the + * chipset itself. */ - if(irqno == IRQ_IQ80310_INTA && !system_rev) - iop310_irq_demux(irq, dev_id, regs); + if (system_rev == 0 || handled == 0) + iop310_irq_demux(irq, desc, regs); - do_IRQ(irqno, regs); + desc->chip->unmask(irq); } - -static struct irqaction iq80310_cpld_irq = { - name: "CPLD_IRQ", - handler: iq80310_cpld_irq_demux, - flags: SA_INTERRUPT -}; - -extern int setup_arm_irq(int, struct irqaction *); - void __init iq80310_init_irq(void) { volatile char *mask = (volatile char *)IQ80310_INT_MASK; @@ -156,17 +119,26 @@ */ *IOP310_PIRSR = 0xff; - for (i = IQ80310_IRQ_OFS; i < NR_IRQS; i++) { - irq_desc[i].valid = 1; - irq_desc[i].probe_ok = 1; - irq_desc[i].mask_ack = iq80310_irq_mask; - irq_desc[i].mask = iq80310_irq_mask; - irq_desc[i].unmask = iq80310_irq_unmask; + /* + * Setup the IRQs in the FE820000/FE860000 registers + */ + for (i = IQ80310_IRQ_OFS; i <= IRQ_IQ80310_INTD; i++) { + set_irq_chip(i, &iq80310_irq_chip); + set_irq_handler(i, do_level_IRQ); + set_irq_flags(i, IRQF_VALID | IRQF_PROBE); + } + + /* + * Setup the PCI IRQs + */ + for (i = IRQ_IQ80310_INTA; i < IRQ_IQ80310_INTC; i++) { + set_irq_chip(i, &ext_chip); + set_irq_handler(i, do_level_IRQ); + set_irq_flags(i, IRQF_VALID); } *mask = 0xff; /* mask all sources */ - setup_arm_irq(IRQ_XS80200_EXTIRQ, &iq80310_cpld_irq); - /* enable only external IRQ in the INTCTL for now */ - asm ("mcr p13, 0, %0, c0, c0, 0" : : "r" (1<<1)); + set_irq_chained_handler(IRQ_XS80200_EXTIRQ, + &iq80310_cpld_irq_handler); } diff -Nru a/arch/arm/mach-iop310/iq80310-time.c b/arch/arm/mach-iop310/iq80310-time.c --- a/arch/arm/mach-iop310/iq80310-time.c Thu Mar 7 18:17:39 2002 +++ b/arch/arm/mach-iop310/iq80310-time.c Thu Mar 7 18:17:39 2002 @@ -11,25 +11,21 @@ * published by the Free Software Foundation. * */ - - -#include #include #include #include #include #include #include +#include #include -#include +#include #include #include - -#include -#include - +#include #include +#include static void iq80310_write_timer (u_long val) { @@ -109,14 +105,12 @@ }; -extern int setup_arm_irq(int, struct irqaction*); - void __init time_init(void) { volatile u_char *timer_en = (volatile u_char *)IQ80310_TIMER_EN; gettimeoffset = iq80310_gettimeoffset; - setup_arm_irq(IRQ_IQ80310_TIMER, &timer_irq); + setup_irq(IRQ_IQ80310_TIMER, &timer_irq); *timer_en = 0; iq80310_write_timer(LATCH); *timer_en |= 2; diff -Nru a/arch/arm/mach-iop310/mm.c b/arch/arm/mach-iop310/mm.c --- a/arch/arm/mach-iop310/mm.c Thu Mar 7 18:17:39 2002 +++ b/arch/arm/mach-iop310/mm.c Thu Mar 7 18:17:39 2002 @@ -13,7 +13,7 @@ * option) any later version. * */ - +#include #include #include diff -Nru a/arch/arm/mach-iop310/xs80200-irq.c b/arch/arm/mach-iop310/xs80200-irq.c --- a/arch/arm/mach-iop310/xs80200-irq.c Thu Mar 7 18:17:45 2002 +++ b/arch/arm/mach-iop310/xs80200-irq.c Thu Mar 7 18:17:45 2002 @@ -10,8 +10,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - -#include #include #include #include @@ -22,45 +20,47 @@ #include -void -xs80200_irq_mask (unsigned int irq) +static void xs80200_irq_mask (unsigned int irq) { - long INTCTL; - asm ("mrc p13, 0, %0, c0, c0, 0" : "=r" (INTCTL)); + unsigned long intctl; + asm ("mrc p13, 0, %0, c0, c0, 0" : "=r" (intctl)); switch (irq) { - case IRQ_XS80200_BCU: INTCTL &= ~(1<<3); break; - case IRQ_XS80200_PMU: INTCTL &= ~(1<<2); break; - case IRQ_XS80200_EXTIRQ: INTCTL &= ~(1<<1); break; - case IRQ_XS80200_EXTFIQ: INTCTL &= ~(1<<0); break; + case IRQ_XS80200_BCU: intctl &= ~(1<<3); break; + case IRQ_XS80200_PMU: intctl &= ~(1<<2); break; + case IRQ_XS80200_EXTIRQ: intctl &= ~(1<<1); break; + case IRQ_XS80200_EXTFIQ: intctl &= ~(1<<0); break; } - asm ("mcr p13, 0, %0, c0, c0, 0" : : "r" (INTCTL)); + asm ("mcr p13, 0, %0, c0, c0, 0" : : "r" (intctl)); } -void -xs80200_irq_unmask (unsigned int irq) +static void xs80200_irq_unmask (unsigned int irq) { - long INTCTL; - asm ("mrc p13, 0, %0, c0, c0, 0" : "=r" (INTCTL)); + unsigned long intctl; + asm ("mrc p13, 0, %0, c0, c0, 0" : "=r" (intctl)); switch (irq) { - case IRQ_XS80200_BCU: INTCTL |= (1<<3); break; - case IRQ_XS80200_PMU: INTCTL |= (1<<2); break; - case IRQ_XS80200_EXTIRQ: INTCTL |= (1<<1); break; - case IRQ_XS80200_EXTFIQ: INTCTL |= (1<<0); break; + case IRQ_XS80200_BCU: intctl |= (1<<3); break; + case IRQ_XS80200_PMU: intctl |= (1<<2); break; + case IRQ_XS80200_EXTIRQ: intctl |= (1<<1); break; + case IRQ_XS80200_EXTFIQ: intctl |= (1<<0); break; } - asm ("mcr p13, 0, %0, c0, c0, 0" : : "r" (INTCTL)); + asm ("mcr p13, 0, %0, c0, c0, 0" : : "r" (intctl)); } +static struct irqchip xs80200_chip = { + ack: xs80200_irq_mask, + mask: xs80200_irq_mask, + unmask: xs80200_irq_unmask, +}; + void __init xs80200_init_irq(void) { - int i; + unsigned int i; + + asm("mcr p13, 0, %0, c0, c0, 0" : : "r" (0)); for (i = 0; i < NR_XS80200_IRQS; i++) { - irq_desc[i].valid = 1; - irq_desc[i].probe_ok = 0; - irq_desc[i].mask_ack = xs80200_irq_mask; - irq_desc[i].mask = xs80200_irq_mask; - irq_desc[i].unmask = xs80200_irq_unmask; + set_irq_chip(i, &xs80200_chip); + set_irq_handler(i, do_level_IRQ); + set_irq_flags(i, IRQF_VALID); } } - - diff -Nru a/arch/arm/mach-l7200/core.c b/arch/arm/mach-l7200/core.c --- a/arch/arm/mach-l7200/core.c Thu Mar 7 18:17:39 2002 +++ b/arch/arm/mach-l7200/core.c Thu Mar 7 18:17:39 2002 @@ -5,6 +5,7 @@ * * Extra MM routines for L7200 architecture */ +#include #include #include @@ -90,7 +91,7 @@ mi->bank[0].size = (32*1024*1024); mi->bank[0].node = 0; - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0, 0, CONFIG_BLK_DEV_RAM_SIZE); setup_initrd( __phys_to_virt(0xf1000000), 0x005dac7b); diff -Nru a/arch/arm/mach-rpc/dma.c b/arch/arm/mach-rpc/dma.c --- a/arch/arm/mach-rpc/dma.c Thu Mar 7 18:17:44 2002 +++ b/arch/arm/mach-rpc/dma.c Thu Mar 7 18:17:44 2002 @@ -188,7 +188,7 @@ */ if (!dma->using_sg) { dma->buf.dma_address = pci_map_single(NULL, - dma->buf.address, dma->buf.length, + dma->buf.__address, dma->buf.length, dma->dma_mode == DMA_MODE_READ ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); } @@ -275,6 +275,9 @@ unsigned int fiqhandler_length; struct pt_regs regs; + if (dma->using_sg) + BUG(); + if (dma->dma_mode == DMA_MODE_READ) { extern unsigned char floppy_fiqin_start, floppy_fiqin_end; fiqhandler_start = &floppy_fiqin_start; @@ -286,7 +289,7 @@ } regs.ARM_r9 = dma->buf.length; - regs.ARM_r10 = (unsigned long)dma->buf.address; + regs.ARM_r10 = (unsigned long)dma->buf.__address; regs.ARM_fp = FLOPPYDMA_BASE; if (claim_fiq(&fh)) { diff -Nru a/arch/arm/mach-rpc/irq.c b/arch/arm/mach-rpc/irq.c --- a/arch/arm/mach-rpc/irq.c Thu Mar 7 18:17:38 2002 +++ b/arch/arm/mach-rpc/irq.c Thu Mar 7 18:17:38 2002 @@ -5,7 +5,7 @@ #include #include -static void rpc_mask_irq_ack_a(unsigned int irq) +static void iomd_ack_irq_a(unsigned int irq) { unsigned int val, mask; @@ -15,7 +15,7 @@ iomd_writeb(mask, IOMD_IRQCLRA); } -static void rpc_mask_irq_a(unsigned int irq) +static void iomd_mask_irq_a(unsigned int irq) { unsigned int val, mask; @@ -24,7 +24,7 @@ iomd_writeb(val & ~mask, IOMD_IRQMASKA); } -static void rpc_unmask_irq_a(unsigned int irq) +static void iomd_unmask_irq_a(unsigned int irq) { unsigned int val, mask; @@ -33,7 +33,13 @@ iomd_writeb(val | mask, IOMD_IRQMASKA); } -static void rpc_mask_irq_b(unsigned int irq) +static struct irqchip iomd_a_chip = { + ack: iomd_ack_irq_a, + mask: iomd_mask_irq_a, + unmask: iomd_unmask_irq_a, +}; + +static void iomd_mask_irq_b(unsigned int irq) { unsigned int val, mask; @@ -42,7 +48,7 @@ iomd_writeb(val & ~mask, IOMD_IRQMASKB); } -static void rpc_unmask_irq_b(unsigned int irq) +static void iomd_unmask_irq_b(unsigned int irq) { unsigned int val, mask; @@ -51,7 +57,13 @@ iomd_writeb(val | mask, IOMD_IRQMASKB); } -static void rpc_mask_irq_dma(unsigned int irq) +static struct irqchip iomd_b_chip = { + ack: iomd_mask_irq_b, + mask: iomd_mask_irq_b, + unmask: iomd_unmask_irq_b, +}; + +static void iomd_mask_irq_dma(unsigned int irq) { unsigned int val, mask; @@ -60,7 +72,7 @@ iomd_writeb(val & ~mask, IOMD_DMAMASK); } -static void rpc_unmask_irq_dma(unsigned int irq) +static void iomd_unmask_irq_dma(unsigned int irq) { unsigned int val, mask; @@ -69,7 +81,13 @@ iomd_writeb(val | mask, IOMD_DMAMASK); } -static void rpc_mask_irq_fiq(unsigned int irq) +static struct irqchip iomd_dma_chip = { + ack: iomd_mask_irq_dma, + mask: iomd_mask_irq_dma, + unmask: iomd_unmask_irq_dma, +}; + +static void iomd_mask_irq_fiq(unsigned int irq) { unsigned int val, mask; @@ -78,7 +96,7 @@ iomd_writeb(val & ~mask, IOMD_FIQMASK); } -static void rpc_unmask_irq_fiq(unsigned int irq) +static void iomd_unmask_irq_fiq(unsigned int irq) { unsigned int val, mask; @@ -87,9 +105,15 @@ iomd_writeb(val | mask, IOMD_FIQMASK); } +static struct irqchip iomd_fiq_chip = { + ack: iomd_mask_irq_fiq, + mask: iomd_mask_irq_fiq, + unmask: iomd_unmask_irq_fiq, +}; + void __init rpc_init_irq(void) { - int irq; + unsigned int irq, flags; iomd_writeb(0, IOMD_IRQMASKA); iomd_writeb(0, IOMD_IRQMASKB); @@ -97,45 +121,40 @@ iomd_writeb(0, IOMD_DMAMASK); for (irq = 0; irq < NR_IRQS; irq++) { + flags = IRQF_VALID; + + if (irq <= 6 || (irq >= 9 && irq <= 15)) + flags |= IRQF_PROBE; + + if (irq == 21 || (irq >= 16 && irq <= 19) || + irq == IRQ_KEYBOARDTX) + flags |= IRQF_NOAUTOEN; + switch (irq) { - case 0 ... 6: - irq_desc[irq].probe_ok = 1; - case 7: - irq_desc[irq].valid = 1; - irq_desc[irq].mask_ack = rpc_mask_irq_ack_a; - irq_desc[irq].mask = rpc_mask_irq_a; - irq_desc[irq].unmask = rpc_unmask_irq_a; + case 0 ... 7: + set_irq_chip(irq, &iomd_a_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, flags); break; - case 9 ... 15: - irq_desc[irq].probe_ok = 1; - case 8: - irq_desc[irq].valid = 1; - irq_desc[irq].mask_ack = rpc_mask_irq_b; - irq_desc[irq].mask = rpc_mask_irq_b; - irq_desc[irq].unmask = rpc_unmask_irq_b; + case 8 ... 15: + set_irq_chip(irq, &iomd_b_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, flags); break; - case 16 ... 19: - case 21: - irq_desc[irq].noautoenable = 1; - case 20: - irq_desc[irq].valid = 1; - irq_desc[irq].mask_ack = rpc_mask_irq_dma; - irq_desc[irq].mask = rpc_mask_irq_dma; - irq_desc[irq].unmask = rpc_unmask_irq_dma; + case 16 ... 21: + set_irq_chip(irq, &iomd_dma_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, flags); break; case 64 ... 71: - irq_desc[irq].valid = 1; - irq_desc[irq].mask_ack = rpc_mask_irq_fiq; - irq_desc[irq].mask = rpc_mask_irq_fiq; - irq_desc[irq].unmask = rpc_unmask_irq_fiq; + set_irq_chip(irq, &iomd_fiq_chip); + set_irq_flags(irq, IRQF_VALID); break; } } - - irq_desc[IRQ_KEYBOARDTX].noautoenable = 1; init_FIQ(); } diff -Nru a/arch/arm/mach-sa1100/adsbitsy.c b/arch/arm/mach-sa1100/adsbitsy.c --- a/arch/arm/mach-sa1100/adsbitsy.c Thu Mar 7 18:17:36 2002 +++ b/arch/arm/mach-sa1100/adsbitsy.c Thu Mar 7 18:17:36 2002 @@ -26,8 +26,6 @@ #include #include -#include - #include "generic.h" #include "sa1111.h" @@ -120,14 +118,14 @@ SET_BANK( 0, 0xc0000000, 32*1024*1024 ); mi->nr_banks = 1; - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0, 0, 8192 ); setup_initrd( __phys_to_virt(0xc0800000), 4*1024*1024 ); } static struct map_desc adsbitsy_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xf4000000, 0x18000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* SA1111 */ + { 0xf4000000, 0x18000000, 0x00800000, DOMAIN_IO, 0, 1, 0, 0 }, /* SA1111 */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c --- a/arch/arm/mach-sa1100/assabet.c Thu Mar 7 18:17:36 2002 +++ b/arch/arm/mach-sa1100/assabet.c Thu Mar 7 18:17:36 2002 @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -89,7 +90,7 @@ /* * Set the IRQ edges */ - set_GPIO_IRQ_edge(GPIO_GPIO23, GPIO_RISING_EDGE); /* UCB1300 */ + set_irq_type(IRQ_GPIO23, IRQT_RISING); /* UCB1300 */ sa1100fb_lcd_power = assabet_lcd_power; sa1100fb_backlight_power = assabet_backlight_power; @@ -229,8 +230,8 @@ static struct map_desc assabet_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xf1000000, 0x12000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* Board Control Register */ - { 0xf2800000, 0x4b800000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* MQ200 */ + { 0xf1000000, 0x12000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* Board Control Register */ + { 0xf2800000, 0x4b800000, 0x00800000, DOMAIN_IO, 0, 1, 0, 0 }, /* MQ200 */ /* f3000000 - neponset system registers */ /* f4000000 - neponset SA1111 registers */ LAST_DESC diff -Nru a/arch/arm/mach-sa1100/badge4.c b/arch/arm/mach-sa1100/badge4.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mach-sa1100/badge4.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,211 @@ +/* + * linux/arch/arm/mach-sa1100/badge4.c + * + * BadgePAD 4 specific initialization + * + * Tim Connors + * Christopher Hoover + * + * Copyright (C) 2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "generic.h" +#include "sa1111.h" + +static int __init badge4_sa1111_init(void) +{ + int ret; + + /* + * Ensure that the memory bus request/grant signals are setup, + * and the grant is held in its inactive state + */ + sa1110_mb_disable(); + + /* + * Probe for SA1111. + */ + ret = sa1111_probe(BADGE4_SA1111_BASE); + if (ret < 0) + return ret; + + /* + * We found it. Wake the chip up. + */ + sa1111_wake(); + + /* + * The SDRAM configuration of the SA1110 and the SA1111 must + * match. This is very important to ensure that SA1111 accesses + * don't corrupt the SDRAM. Note that this ungates the SA1111's + * MBGNT signal, so we must have called sa1110_mb_disable() + * beforehand. + */ + sa1111_configure_smc(1, + FExtr(MDCNFG, MDCNFG_SA1110_DRAC0), + FExtr(MDCNFG, MDCNFG_SA1110_TDL0)); + + /* + * We only need to turn on DCLK whenever we want to use the + * DMA. It can otherwise be held firmly in the off position. + */ + SKPCR |= SKPCR_DCLKEN; + + /* + * Enable the SA1110 memory bus request and grant signals. + */ + sa1110_mb_enable(); + + sa1111_init_irq(BADGE4_IRQ_GPIO_SA1111); + + return 0; +} + +static int __init badge4_init(void) +{ + int ret; + + if (!machine_is_badge4()) + return -ENODEV; + + ret = badge4_sa1111_init(); + if (ret < 0) + printk(KERN_ERR __FUNCTION__ + ": SA-1111 initialization failed (%d)\n", ret); + + /* N.B, according to rmk this is the singular place that GPDR + should be set */ + + /* Video expansion */ + GPCR = (BADGE4_GPIO_INT_VID | BADGE4_GPIO_LGP2 | BADGE4_GPIO_LGP3 | + BADGE4_GPIO_LGP4 | BADGE4_GPIO_LGP5 | BADGE4_GPIO_LGP6 | + BADGE4_GPIO_LGP7 | BADGE4_GPIO_LGP8 | BADGE4_GPIO_LGP9 | + BADGE4_GPIO_GPA_VID | BADGE4_GPIO_GPB_VID | + BADGE4_GPIO_GPC_VID); + GPDR |= (BADGE4_GPIO_INT_VID | BADGE4_GPIO_LGP2 | BADGE4_GPIO_LGP3 | + BADGE4_GPIO_LGP4 | BADGE4_GPIO_LGP5 | BADGE4_GPIO_LGP6 | + BADGE4_GPIO_LGP7 | BADGE4_GPIO_LGP8 | BADGE4_GPIO_LGP9 | + BADGE4_GPIO_GPA_VID | BADGE4_GPIO_GPB_VID | + BADGE4_GPIO_GPC_VID); + + /* SDRAM SPD i2c */ + GPCR = (BADGE4_GPIO_SDSDA | BADGE4_GPIO_SDSCL); + GPDR |= (BADGE4_GPIO_SDSDA | BADGE4_GPIO_SDSCL); + + /* uart */ + GPCR = (BADGE4_GPIO_UART_HS1 | BADGE4_GPIO_UART_HS2); + GPDR |= (BADGE4_GPIO_UART_HS1 | BADGE4_GPIO_UART_HS2); + + /* drives CPLD muxsel0 input */ + GPCR = BADGE4_GPIO_MUXSEL0; + GPDR |= BADGE4_GPIO_MUXSEL0; + + /* test points */ + GPCR = (BADGE4_GPIO_TESTPT_J7 | BADGE4_GPIO_TESTPT_J6 | + BADGE4_GPIO_TESTPT_J5); + GPDR |= (BADGE4_GPIO_TESTPT_J7 | BADGE4_GPIO_TESTPT_J6 | + BADGE4_GPIO_TESTPT_J5); + + /* drives CPLD sdram type inputs; this shouldn't be needed; + bootloader left it this way. */ + GPDR |= (BADGE4_GPIO_SDTYP0 | BADGE4_GPIO_SDTYP1); + + /* 5V supply rail. */ + GPCR = BADGE4_GPIO_PCMEN5V; /* initially off */ + GPDR |= BADGE4_GPIO_PCMEN5V; + + /* drives SA1111 reset pin; this shouldn't be needed; + bootloader left it this way. */ + GPSR = BADGE4_GPIO_SA1111_NRST; + GPDR |= BADGE4_GPIO_SA1111_NRST; + + return 0; +} + +__initcall(badge4_init); + + +static unsigned badge4_5V_bitmap = 0; + +void badge4_set_5V(unsigned subsystem, int on) +{ + unsigned long flags; + unsigned old_5V_bitmap; + + local_irq_save(flags); + + old_5V_bitmap = badge4_5V_bitmap; + + if (on) { + badge4_5V_bitmap |= subsystem; + } else { + badge4_5V_bitmap &= ~subsystem; + } + + /* detect on->off and off->on transitions */ + if ((!old_5V_bitmap) && (badge4_5V_bitmap)) { + /* was off, now on */ + printk(KERN_INFO __FUNCTION__ ": enabling 5V supply rail\n"); + GPSR = BADGE4_GPIO_PCMEN5V; + } else if ((old_5V_bitmap) && (!badge4_5V_bitmap)) { + /* was on, now off */ + printk(KERN_INFO __FUNCTION__ ": disabling 5V supply rail\n"); + GPCR = BADGE4_GPIO_PCMEN5V; + } + + local_irq_restore(flags); +} +EXPORT_SYMBOL(badge4_set_5V); + + +static void __init +fixup_badge4(struct machine_desc *desc, struct param_struct *params, + char **cmdline, struct meminfo *mi) +{ + /* nothing needed here */ +} + +static struct map_desc badge4_io_desc[] __initdata = { + /* virtual physical length domain r w c b */ + {0xf1000000, 0x08000000, 0x00100000, DOMAIN_IO, 1,1,0,0},/* SRAM bank 1 */ + {0xf2000000, 0x10000000, 0x00100000, DOMAIN_IO, 1,1,0,0},/* SRAM bank 2 */ + {0xf4000000, 0x48000000, 0x00100000, DOMAIN_IO, 1,1,0,0},/* SA-1111 */ + LAST_DESC +}; + +static void __init badge4_map_io(void) +{ + sa1100_map_io(); + iotable_init(badge4_io_desc); + + sa1100_register_uart(0, 3); + sa1100_register_uart(1, 1); +} + +MACHINE_START(BADGE4, "Hewlett-Packard Laboratories BadgePAD 4") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + BOOT_PARAMS(0xc0000100) + FIXUP(fixup_badge4) + MAPIO(badge4_map_io) + INITIRQ(sa1100_init_irq) +MACHINE_END diff -Nru a/arch/arm/mach-sa1100/brutus.c b/arch/arm/mach-sa1100/brutus.c --- a/arch/arm/mach-sa1100/brutus.c Thu Mar 7 18:17:41 2002 +++ b/arch/arm/mach-sa1100/brutus.c Thu Mar 7 18:17:41 2002 @@ -32,7 +32,7 @@ SET_BANK( 3, 0xd8000000, 4*1024*1024 ); mi->nr_banks = 4; - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0, 0, 8192 ); setup_initrd( __phys_to_virt(0xd8000000), 3*1024*1024 ); } diff -Nru a/arch/arm/mach-sa1100/cerf.c b/arch/arm/mach-sa1100/cerf.c --- a/arch/arm/mach-sa1100/cerf.c Thu Mar 7 18:17:44 2002 +++ b/arch/arm/mach-sa1100/cerf.c Thu Mar 7 18:17:44 2002 @@ -1,11 +1,12 @@ /* * linux/arch/arm/mach-sa1100/cerf.c */ - +#include #include #include #include +#include #include #include @@ -26,12 +27,12 @@ */ #ifdef CONFIG_SA1100_CERF_CPLD /* PDA Full serial port */ - set_GPIO_IRQ_edge(GPIO_GPIO3, GPIO_RISING_EDGE); + set_irq_type(IRQ_GPIO3, IRQT_RISING); /* PDA Bluetooth */ - set_GPIO_IRQ_edge(GPIO_GPIO2, GPIO_RISING_EDGE); + set_irq_type(IRQ_GPIO2, IRQT_RISING); #endif /* CONFIG_SA1100_CERF_CPLD */ - set_GPIO_IRQ_edge(GPIO_UCB1200_IRQ, GPIO_RISING_EDGE); + set_irq_type(IRQ_GPIO_UCB1200_IRQ, IRQT_RISING); } static void __init @@ -55,7 +56,7 @@ #error "Undefined memory size for Cerfboard." #endif -// ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); +// ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); // setup_ramdisk(1, 0, 0, 8192); // // Save 2Meg for RAMDisk // setup_initrd(0xc0500000, 3*1024*1024); @@ -63,11 +64,11 @@ static struct map_desc cerf_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xf0000000, 0x08000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* Crystal Ethernet Chip */ + { 0xf0000000, 0x08000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* Crystal Ethernet Chip */ #ifdef CONFIG_SA1100_CERF_CPLD - { 0xf1000000, 0x40000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* CPLD Chip */ - { 0xf2000000, 0x10000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* CerfPDA Bluetooth */ - { 0xf3000000, 0x18000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* CerfPDA Serial */ + { 0xf1000000, 0x40000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* CPLD Chip */ + { 0xf2000000, 0x10000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* CerfPDA Bluetooth */ + { 0xf3000000, 0x18000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* CerfPDA Serial */ #endif LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/empeg.c b/arch/arm/mach-sa1100/empeg.c --- a/arch/arm/mach-sa1100/empeg.c Thu Mar 7 18:17:45 2002 +++ b/arch/arm/mach-sa1100/empeg.c Thu Mar 7 18:17:45 2002 @@ -24,14 +24,14 @@ SET_BANK( 1, 0xc8000000, 4*1024*1024 ); mi->nr_banks = 2; - ROOT_DEV = MKDEV( 3, 1 ); /* /dev/hda1 */ + ROOT_DEV = mk_kdev( 3, 1 ); /* /dev/hda1 */ setup_ramdisk( 1, 0, 0, 4096 ); setup_initrd( 0xd0000000+((1024-320)*1024), (320*1024) ); } static struct map_desc empeg_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { EMPEG_FLASHBASE, 0x00000000, 0x00200000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash */ + { EMPEG_FLASHBASE, 0x00000000, 0x00200000, DOMAIN_IO, 0, 1, 0, 0 }, /* Flash */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/flexanet.c b/arch/arm/mach-sa1100/flexanet.c --- a/arch/arm/mach-sa1100/flexanet.c Thu Mar 7 18:17:42 2002 +++ b/arch/arm/mach-sa1100/flexanet.c Thu Mar 7 18:17:42 2002 @@ -9,8 +9,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - -#include #include #include #include @@ -113,7 +111,6 @@ * * get_mctrl : set state of modem control lines * set_mctrl : set the modem control lines - * enable_ms : enable modem-status interrupts * pm : power-management. Turn device on/off. * */ @@ -121,7 +118,6 @@ { set_mctrl : flexanet_set_mctrl, get_mctrl : flexanet_get_mctrl, - enable_ms : NULL, pm : NULL, }; @@ -168,7 +164,7 @@ mi->nr_banks = 1; /* setup ramdisk */ - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0, 0, 8192 ); setup_initrd( 0xc0800000, 3*1024*1024 ); } @@ -176,10 +172,10 @@ static struct map_desc flexanet_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xf0000000, 0x10000000, 0x00001000, DOMAIN_IO, 1, 1, 0, 0 }, /* Board Control Register */ - { 0xf1000000, 0x18000000, 0x01000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Ethernet controller */ - { 0xD0000000, 0x40000000, 0x01000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Instrument boards */ - { 0xD8000000, 0x48000000, 0x01000000, DOMAIN_IO, 1, 1, 0, 0 }, /* External peripherals */ + { 0xf0000000, 0x10000000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, /* Board Control Register */ + { 0xf1000000, 0x18000000, 0x01000000, DOMAIN_IO, 0, 1, 0, 0 }, /* Ethernet controller */ + { 0xD0000000, 0x40000000, 0x01000000, DOMAIN_IO, 0, 1, 0, 0 }, /* Instrument boards */ + { 0xD8000000, 0x48000000, 0x01000000, DOMAIN_IO, 0, 1, 0, 0 }, /* External peripherals */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/freebird.c b/arch/arm/mach-sa1100/freebird.c --- a/arch/arm/mach-sa1100/freebird.c Thu Mar 7 18:17:39 2002 +++ b/arch/arm/mach-sa1100/freebird.c Thu Mar 7 18:17:39 2002 @@ -58,7 +58,7 @@ #ifdef CONFIG_SA1100_FREEBIRD_OLD SET_BANK( 0, 0xc0000000, 32*1024*1024 ); mi->nr_banks = 1; - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0 ,0 , 8192 ); setup_initrd( 0xc0800000, 3*1024*1024 ); #endif @@ -66,8 +66,8 @@ static struct map_desc freebird_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xf0000000, 0x12000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* Board Control Register */ - { 0xf2000000, 0x19000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0}, + { 0xf0000000, 0x12000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* Board Control Register */ + { 0xf2000000, 0x19000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0}, LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/graphicsclient.c b/arch/arm/mach-sa1100/graphicsclient.c --- a/arch/arm/mach-sa1100/graphicsclient.c Thu Mar 7 18:17:46 2002 +++ b/arch/arm/mach-sa1100/graphicsclient.c Thu Mar 7 18:17:46 2002 @@ -25,8 +25,6 @@ #include #include -#include - #include "generic.h" @@ -34,60 +32,67 @@ * Handlers for GraphicsClient's external IRQ logic */ -static void ADS_IRQ_demux( int irq, void *dev_id, struct pt_regs *regs ) +static void +gc_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) { - int i; + unsigned int mask; + + while ((mask = ADS_INT_ST1 | (ADS_INT_ST2 << 8))) { + /* clear the parent IRQ */ + GEDR = GPIO_GPIO0; + + irq = ADS_EXT_IRQ(0); + desc = irq_desc + irq; - while( (irq = ADS_INT_ST1 | (ADS_INT_ST2 << 8)) ){ - for( i = 0; i < 16; i++ ) - if( irq & (1<handle(irq, desc, regs); + mask >>= 1; + irq++; + desc++; + } while (mask); } } -static struct irqaction ADS_ext_irq = { - name: "ADS_ext_IRQ", - handler: ADS_IRQ_demux, - flags: SA_INTERRUPT -}; - -static void ADS_mask_and_ack_irq0(unsigned int irq) +static void gc_mask_irq1(unsigned int irq) { int mask = (1 << (irq - ADS_EXT_IRQ(0))); ADS_INT_EN1 &= ~mask; ADS_INT_ST1 = mask; } -static void ADS_mask_irq0(unsigned int irq) -{ - ADS_INT_ST1 = (1 << (irq - ADS_EXT_IRQ(0))); -} - -static void ADS_unmask_irq0(unsigned int irq) +static void gc_unmask_irq1(unsigned int irq) { ADS_INT_EN1 |= (1 << (irq - ADS_EXT_IRQ(0))); } -static void ADS_mask_and_ack_irq1(unsigned int irq) +static struct irqchip gc_irq1_chip = { + ack: gc_mask_irq1, + mask: gc_mask_irq1, + unmask: gc_unmask_irq1, +}; + +static void gc_mask_irq2(unsigned int irq) { int mask = (1 << (irq - ADS_EXT_IRQ(8))); ADS_INT_EN2 &= ~mask; ADS_INT_ST2 = mask; } -static void ADS_mask_irq1(unsigned int irq) -{ - ADS_INT_ST2 = (1 << (irq - ADS_EXT_IRQ(8))); -} - -static void ADS_unmask_irq1(unsigned int irq) +static void gc_unmask_irq2(unsigned int irq) { ADS_INT_EN2 |= (1 << (irq - ADS_EXT_IRQ(8))); } +static struct irqchip gc_irq2_chip = { + ack: gc_mask_irq2, + mask: gc_mask_irq2, + unmask: gc_unmask_irq2, +}; + static void __init graphicsclient_init_irq(void) { - int irq; + unsigned int irq; /* First the standard SA1100 IRQs */ sa1100_init_irq(); @@ -95,26 +100,23 @@ /* disable all IRQs */ ADS_INT_EN1 = 0; ADS_INT_EN2 = 0; + /* clear all IRQs */ ADS_INT_ST1 = 0xff; ADS_INT_ST2 = 0xff; for (irq = ADS_EXT_IRQ(0); irq <= ADS_EXT_IRQ(7); irq++) { - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; - irq_desc[irq].mask_ack = ADS_mask_and_ack_irq0; - irq_desc[irq].mask = ADS_mask_irq0; - irq_desc[irq].unmask = ADS_unmask_irq0; + set_irq_chip(irq, &gc_irq1_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } for (irq = ADS_EXT_IRQ(8); irq <= ADS_EXT_IRQ(15); irq++) { - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; - irq_desc[irq].mask_ack = ADS_mask_and_ack_irq1; - irq_desc[irq].mask = ADS_mask_irq1; - irq_desc[irq].unmask = ADS_unmask_irq1; + set_irq_chip(irq, &gc_irq2_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } - set_GPIO_IRQ_edge(GPIO_GPIO0, GPIO_FALLING_EDGE); - setup_arm_irq( IRQ_GPIO0, &ADS_ext_irq ); + set_irq_type(IRQ_GPIO0, IRQT_FALLING); + set_irq_chained_handler(IRQ_GPIO0, gc_irq_handler); } @@ -130,7 +132,7 @@ SET_BANK( 1, 0xc8000000, 16*1024*1024 ); mi->nr_banks = 2; - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0, 0, 8192 ); setup_initrd( __phys_to_virt(0xc0800000), 4*1024*1024 ); } @@ -142,108 +144,6 @@ LAST_DESC }; -static struct gc_uart_ctrl_data_t gc_uart_ctrl_data[] = { - { GPIO_GC_UART0_CTS, 0, NULL,NULL }, - { GPIO_GC_UART1_CTS, 0, NULL,NULL }, - { GPIO_GC_UART2_CTS, 0, NULL,NULL } -}; - -static void -graphicsclient_cts_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - struct gc_uart_ctrl_data_t * uart_data = (struct gc_uart_ctrl_data_t *)dev_id; - int cts = !(GPLR & uart_data->cts_gpio); - - /* NOTE: I supose that we will no get any interrupt - if the GPIO is not changed, so maybe - the cts_prev_state can be removed ... */ - if (cts != uart_data->cts_prev_state) { - uart_data->cts_prev_state = cts; - - uart_handle_cts_change(uart_data->info, cts); - } -} - -static int -graphicsclient_register_cts_intr(int gpio, int irq, - struct gc_uart_ctrl_data_t *uart_data) -{ - int ret = 0; - - set_GPIO_IRQ_edge(gpio, GPIO_BOTH_EDGES); - - ret = request_irq(irq, graphicsclient_cts_intr, - 0, "GC RS232 CTS", uart_data); - if (ret) { - printk(KERN_ERR "uart_open: failed to register CTS irq (%d)\n", - ret); - free_irq(irq, uart_data); - } - - return ret; -} - -static int -graphicsclient_uart_open(struct uart_port *port, struct uart_info *info) -{ - int ret = 0; - - if (port->mapbase == _Ser1UTCR0) { - Ser1SDCR0 |= SDCR0_UART; - /* Set RTS Output */ - GPSR = GPIO_GC_UART0_RTS; - - gc_uart_ctrl_data[0].cts_prev_state = 0; - gc_uart_ctrl_data[0].info = info; - gc_uart_ctrl_data[0].port = port; - - /* register uart0 CTS irq */ - ret = graphicsclient_register_cts_intr(GPIO_GC_UART0_CTS, - IRQ_GC_UART0_CTS, - &gc_uart_ctrl_data[0]); - } else if (port->mapbase == _Ser2UTCR0) { - Ser2UTCR4 = Ser2HSCR0 = 0; - /* Set RTS Output */ - GPSR = GPIO_GC_UART1_RTS; - - gc_uart_ctrl_data[1].cts_prev_state = 0; - gc_uart_ctrl_data[1].info = info; - gc_uart_ctrl_data[1].port = port; - - /* register uart1 CTS irq */ - ret = graphicsclient_register_cts_intr(GPIO_GC_UART1_CTS, - IRQ_GC_UART1_CTS, - &gc_uart_ctrl_data[1]); - } else if (port->mapbase == _Ser3UTCR0) { - /* Set RTS Output */ - GPSR = GPIO_GC_UART2_RTS; - - gc_uart_ctrl_data[2].cts_prev_state = 0; - gc_uart_ctrl_data[2].info = info; - gc_uart_ctrl_data[2].port = port; - - /* register uart2 CTS irq */ - ret = graphicsclient_register_cts_intr(GPIO_GC_UART2_CTS, - IRQ_GC_UART2_CTS, - &gc_uart_ctrl_data[2]); - } - return ret; -} - -static int -graphicsclient_uart_close(struct uart_port *port, struct uart_info *info) -{ - if (port->mapbase == _Ser1UTCR0) { - free_irq(IRQ_GC_UART0_CTS, &gc_uart_ctrl_data[0]); - } else if (port->mapbase == _Ser2UTCR0) { - free_irq(IRQ_GC_UART1_CTS, &gc_uart_ctrl_data[1]); - } else if (port->mapbase == _Ser3UTCR0) { - free_irq(IRQ_GC_UART2_CTS, &gc_uart_ctrl_data[2]); - } - - return 0; -} - static u_int graphicsclient_get_mctrl(struct uart_port *port) { u_int result = TIOCM_CD | TIOCM_DSR; @@ -296,8 +196,6 @@ } static struct sa1100_port_fns graphicsclient_port_fns __initdata = { - open: graphicsclient_uart_open, - close: graphicsclient_uart_close, get_mctrl: graphicsclient_get_mctrl, set_mctrl: graphicsclient_set_mctrl, pm: graphicsclient_uart_pm, diff -Nru a/arch/arm/mach-sa1100/graphicsmaster.c b/arch/arm/mach-sa1100/graphicsmaster.c --- a/arch/arm/mach-sa1100/graphicsmaster.c Thu Mar 7 18:17:43 2002 +++ b/arch/arm/mach-sa1100/graphicsmaster.c Thu Mar 7 18:17:43 2002 @@ -23,8 +23,6 @@ #include #include -#include - #include "generic.h" #include "sa1111.h" @@ -95,61 +93,67 @@ * Handlers for GraphicsMaster's external IRQ logic */ -static void ADS_IRQ_demux( int irq, void *dev_id, struct pt_regs *regs ) +static void +gm_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) { - int i; + unsigned int mask; - while( (irq = ADS_INT_ST1 | (ADS_INT_ST2 << 8)) ){ - for( i = 0; i < 16; i++ ) - if( irq & (1<handle(irq, desc, regs); + mask >>= 1; + irq++; + desc++; + } while (mask); } } -static struct irqaction ADS_ext_irq = { - name: "ADS_ext_IRQ", - handler: ADS_IRQ_demux, - flags: SA_INTERRUPT -}; - -static void ADS_mask_and_ack_irq0(unsigned int irq) +static void gm_mask_irq1(unsigned int irq) { int mask = (1 << (irq - ADS_EXT_IRQ(0))); ADS_INT_EN1 &= ~mask; ADS_INT_ST1 = mask; } -static void ADS_mask_irq0(unsigned int irq) -{ - ADS_INT_ST1 = (1 << (irq - ADS_EXT_IRQ(0))); -} - -static void ADS_unmask_irq0(unsigned int irq) +static void gm_unmask_irq1(unsigned int irq) { ADS_INT_EN1 |= (1 << (irq - ADS_EXT_IRQ(0))); } -static void ADS_mask_and_ack_irq1(unsigned int irq) +static struct irqchip gm_irq1_chip = { + ack: gm_mask_irq1, + mask: gm_mask_irq1, + unmask: gm_unmask_irq1, +}; + +static void gm_mask_irq2(unsigned int irq) { int mask = (1 << (irq - ADS_EXT_IRQ(8))); ADS_INT_EN2 &= ~mask; ADS_INT_ST2 = mask; } -static void ADS_mask_irq1(unsigned int irq) -{ - ADS_INT_ST2 = (1 << (irq - ADS_EXT_IRQ(8))); -} - -static void ADS_unmask_irq1(unsigned int irq) +static void gm_unmask_irq2(unsigned int irq) { ADS_INT_EN2 |= (1 << (irq - ADS_EXT_IRQ(8))); } +static struct irqchip gm_irq2_chip = { + ack: gm_mask_irq2, + mask: gm_mask_irq2, + unmask: gm_unmask_irq2, +}; + static void __init graphicsmaster_init_irq(void) { - int irq; + unsigned int irq; /* First the standard SA1100 IRQs */ sa1100_init_irq(); @@ -157,26 +161,23 @@ /* disable all IRQs */ ADS_INT_EN1 = 0; ADS_INT_EN2 = 0; + /* clear all IRQs */ ADS_INT_ST1 = 0xff; ADS_INT_ST2 = 0xff; for (irq = ADS_EXT_IRQ(0); irq <= ADS_EXT_IRQ(7); irq++) { - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; - irq_desc[irq].mask_ack = ADS_mask_and_ack_irq0; - irq_desc[irq].mask = ADS_mask_irq0; - irq_desc[irq].unmask = ADS_unmask_irq0; + set_irq_chip(irq, &gm_irq1_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_PROBE | IRQF_VALID); } for (irq = ADS_EXT_IRQ(8); irq <= ADS_EXT_IRQ(15); irq++) { - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; - irq_desc[irq].mask_ack = ADS_mask_and_ack_irq1; - irq_desc[irq].mask = ADS_mask_irq1; - irq_desc[irq].unmask = ADS_unmask_irq1; + set_irq_chip(irq, &gm_irq2_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_PROBE | IRQF_VALID); } - set_GPIO_IRQ_edge(GPIO_GPIO0, GPIO_FALLING_EDGE); - setup_arm_irq( IRQ_GPIO0, &ADS_ext_irq ); + set_irq_type(IRQ_GPIO0, IRQT_FALLING); + set_irq_chained_handler(IRQ_GPIO0, gm_irq_handler); } @@ -193,19 +194,21 @@ SET_BANK( 1, 0xc8000000, 16*1024*1024 ); mi->nr_banks = 2; - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0, 0, 8192 ); setup_initrd( __phys_to_virt(0xc0800000), 4*1024*1024 ); } static struct map_desc graphicsmaster_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xf0000000, 0x10000000, 0x00400000, DOMAIN_IO, 1, 1, 0, 0 }, /* CPLD */ - { 0xf1000000, 0x40000000, 0x00400000, DOMAIN_IO, 1, 1, 0, 0 }, /* CAN */ - { 0xf4000000, 0x18000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* SA-1111 */ + { 0xf0000000, 0x10000000, 0x00400000, DOMAIN_IO, 0, 1, 0, 0 }, /* CPLD */ + { 0xf1000000, 0x40000000, 0x00400000, DOMAIN_IO, 0, 1, 0, 0 }, /* CAN */ + { 0xf4000000, 0x18000000, 0x00800000, DOMAIN_IO, 0, 1, 0, 0 }, /* SA-1111 */ LAST_DESC }; +#error Old code. Someone needs to decide what to do about this. +#if 0 static int graphicsmaster_uart_open(struct uart_port *port, struct uart_info *info) { int ret = 0; @@ -226,6 +229,7 @@ } return ret; } +#endif static u_int graphicsmaster_get_mctrl(struct uart_port *port) { @@ -279,7 +283,6 @@ } static struct sa1100_port_fns graphicsmaster_port_fns __initdata = { - open: graphicsmaster_uart_open, get_mctrl: graphicsmaster_get_mctrl, set_mctrl: graphicsmaster_set_mctrl, pm: graphicsmaster_uart_pm, diff -Nru a/arch/arm/mach-sa1100/h3600.c b/arch/arm/mach-sa1100/h3600.c --- a/arch/arm/mach-sa1100/h3600.c Thu Mar 7 18:17:40 2002 +++ b/arch/arm/mach-sa1100/h3600.c Thu Mar 7 18:17:40 2002 @@ -19,8 +19,6 @@ * and abstracted EGPIO interface. * */ - -#include #include #include #include @@ -61,7 +59,7 @@ | GPIO_H3100_IR_ON \ | GPIO_H3100_IR_FSEL) -void h3100_init_egpio( void ) +static void h3100_init_egpio( void ) { GPDR |= H3100_DIRECT_EGPIO; GPCR = H3100_DIRECT_EGPIO; /* Initially all off */ @@ -74,7 +72,7 @@ H3600_EGPIO = h3600_egpio; } -void h3100_control_egpio( enum ipaq_egpio_type x, int setp ) +static void h3100_control_egpio( enum ipaq_egpio_type x, int setp ) { unsigned int egpio = 0; long gpio = 0; @@ -140,7 +138,7 @@ */ } -unsigned long h3100_read_egpio( void ) +static unsigned long h3100_read_egpio( void ) { return h3600_egpio; } @@ -156,13 +154,13 @@ /************************* H3600 *************************/ -void h3600_init_egpio( void ) +static void h3600_init_egpio( void ) { h3600_egpio = EGPIO_H3600_RS232_ON; H3600_EGPIO = h3600_egpio; } -void h3600_control_egpio( enum ipaq_egpio_type x, int setp ) +static void h3600_control_egpio( enum ipaq_egpio_type x, int setp ) { unsigned int egpio = 0; unsigned long flags; @@ -219,7 +217,7 @@ local_irq_restore(flags); } -unsigned long h3600_read_egpio( void ) +static unsigned long h3600_read_egpio( void ) { return h3600_egpio; } @@ -239,7 +237,7 @@ static unsigned int h3800_asic1_gpio; static unsigned int h3800_asic2_gpio; -void h3800_init_egpio(void) +static void h3800_init_egpio(void) { /* Set up ASIC #1 */ H3800_ASIC1_GPIO_Direction = ASIC1_OUTPUTS; /* All outputs */ @@ -273,7 +271,7 @@ H3800_ASIC1_FlashWP_VPP_ON = 0; } -void h3800_control_egpio( enum ipaq_egpio_type x, int setp ) +static void h3800_control_egpio( enum ipaq_egpio_type x, int setp ) { unsigned int set_asic1_egpio = 0; unsigned int clear_asic1_egpio = 0; @@ -327,7 +325,7 @@ local_irq_restore(flags); } -unsigned long h3800_read_egpio( void ) +static unsigned long h3800_read_egpio( void ) { return h3800_asic1_gpio | (h3800_asic2_gpio << 16); } @@ -491,9 +489,9 @@ static struct map_desc h3600_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { H3600_EGPIO_VIRT, 0x49000000, 0x01000000, DOMAIN_IO, 1, 1, 0, 0 }, /* EGPIO 0 CS#5 */ - { H3600_BANK_2_VIRT, 0x10000000, 0x02800000, DOMAIN_IO, 1, 1, 0, 0 }, /* static memory bank 2 CS#2 */ - { H3600_BANK_4_VIRT, 0x40000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* static memory bank 4 CS#4 */ + { H3600_EGPIO_VIRT, 0x49000000, 0x01000000, DOMAIN_IO, 0, 1, 0, 0 }, /* EGPIO 0 CS#5 */ + { H3600_BANK_2_VIRT, 0x10000000, 0x02800000, DOMAIN_IO, 0, 1, 0, 0 }, /* static memory bank 2 CS#2 */ + { H3600_BANK_4_VIRT, 0x40000000, 0x00800000, DOMAIN_IO, 0, 1, 0, 0 }, /* static memory bank 4 CS#4 */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/huw_webpanel.c b/arch/arm/mach-sa1100/huw_webpanel.c --- a/arch/arm/mach-sa1100/huw_webpanel.c Thu Mar 7 18:17:44 2002 +++ b/arch/arm/mach-sa1100/huw_webpanel.c Thu Mar 7 18:17:44 2002 @@ -2,8 +2,6 @@ * linux/arch/arm/mach-sa1100/huw_webpanel.c * */ - -#include #include #include #include @@ -66,7 +64,7 @@ **/ SET_BANK( 0, 0xc0000000, ((32*1024 - (256 + 32)) * 1024)); mi->nr_banks = 1; - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0, 0, 8192 ); setup_initrd( __phys_to_virt(0xc0800000), 8*1024*1024 ); } @@ -79,7 +77,7 @@ **/ static struct map_desc huw_webpanel_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xf0000000, 0xc1fb8000, 0x00048000, DOMAIN_IO, 1, 1, 0, 0 }, /* Parameter */ + { 0xf0000000, 0xc1fb8000, 0x00048000, DOMAIN_IO, 0, 1, 0, 0 }, /* Parameter */ { 0xf1000000, 0x18000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* Paules CS3, write only */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/irq.c b/arch/arm/mach-sa1100/irq.c --- a/arch/arm/mach-sa1100/irq.c Thu Mar 7 18:17:46 2002 +++ b/arch/arm/mach-sa1100/irq.c Thu Mar 7 18:17:46 2002 @@ -9,8 +9,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - -#include #include #include #include @@ -21,7 +19,6 @@ #include #include #include -#include #include "generic.h" @@ -32,156 +29,170 @@ * This must be called *before* the appropriate IRQ is registered. * Use this instead of directly setting GRER/GFER. */ - static int GPIO_IRQ_rising_edge; static int GPIO_IRQ_falling_edge; +static int GPIO_IRQ_mask = (1 << 11) - 1; -void set_GPIO_IRQ_edge( int gpio_mask, int edge ) +static void sa1100_manual_rerun(unsigned int irq) { - int flags; - - local_irq_save(flags); - if (edge & GPIO_FALLING_EDGE) - GPIO_IRQ_falling_edge |= gpio_mask; - else - GPIO_IRQ_falling_edge &= ~gpio_mask; - if (edge & GPIO_RISING_EDGE) - GPIO_IRQ_rising_edge |= gpio_mask; - else - GPIO_IRQ_rising_edge &= ~gpio_mask; - GPDR &= ~gpio_mask; - GAFR &= ~gpio_mask; - restore_flags(flags); + struct pt_regs regs; + memset(®s, 0, sizeof(regs)); + irq_desc[irq].handle(irq, &irq_desc[irq], ®s); } -EXPORT_SYMBOL(set_GPIO_IRQ_edge); +#define GPIO11_27_MASK(irq) (1 << GPIO_11_27_IRQ(irq)) +static int sa1100_gpio_type(unsigned int irq, unsigned int type) +{ + unsigned int mask; -/* - * We don't need to ACK IRQs on the SA1100 unless they're GPIOs - * this is for internal IRQs i.e. from 11 to 31. - */ + printk(KERN_DEBUG "IRQ%d: ", irq); -static void sa1100_mask_irq(unsigned int irq) -{ - ICMR &= ~(1 << irq); -} + if (irq <= 10) + mask = 1 << irq; + else + mask = GPIO11_27_MASK(irq); -static void sa1100_unmask_irq(unsigned int irq) -{ - ICMR |= (1 << irq); + if (type & __IRQT_RISEDGE) { + printk("rising "); + GPIO_IRQ_rising_edge |= mask; + } else + GPIO_IRQ_rising_edge &= ~mask; + if (type & __IRQT_FALEDGE) { + printk("falling "); + GPIO_IRQ_falling_edge |= mask; + } else + GPIO_IRQ_falling_edge &= ~mask; + + printk("edges\n"); + + GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask; + GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask; + + return 0; } /* * GPIO IRQs must be acknoledged. This is for IRQs from 0 to 10. */ - -static void sa1100_mask_and_ack_GPIO0_10_irq(unsigned int irq) +static void sa1100_low_gpio_ack(unsigned int irq) { - ICMR &= ~(1 << irq); GEDR = (1 << irq); } -static void sa1100_mask_GPIO0_10_irq(unsigned int irq) +static void sa1100_low_gpio_mask(unsigned int irq) { ICMR &= ~(1 << irq); } -static void sa1100_unmask_GPIO0_10_irq(unsigned int irq) +static void sa1100_low_gpio_unmask(unsigned int irq) { - GRER = (GRER & ~(1 << irq)) | (GPIO_IRQ_rising_edge & (1 << irq)); - GFER = (GFER & ~(1 << irq)) | (GPIO_IRQ_falling_edge & (1 << irq)); - ICMR |= (1 << irq); + ICMR |= 1 << irq; } +static struct irqchip sa1100_low_gpio_chip = { + ack: sa1100_low_gpio_ack, + mask: sa1100_low_gpio_mask, + unmask: sa1100_low_gpio_unmask, + rerun: sa1100_manual_rerun, + type: sa1100_gpio_type, +}; + /* - * Install handler for GPIO 11-27 edge detect interrupts + * IRQ11 (GPIO11 through 27) handler. We enter here with the + * irq_controller_lock held, and IRQs disabled. Decode the IRQ + * and call the handler. */ - -static int GPIO_11_27_enabled; /* enabled i.e. unmasked GPIO IRQs */ -static int GPIO_11_27_spurious; /* GPIOs that triggered when masked */ - -static void sa1100_GPIO11_27_demux(int irq, void *dev_id, - struct pt_regs *regs) +static void +sa1100_high_gpio_handler(unsigned int irq, struct irqdesc *desc, + struct pt_regs *regs) { - int i, spurious; + unsigned int mask; - while ((irq = (GEDR & 0xfffff800))) { + mask = GEDR & 0xfffff800; + do { /* - * We don't want to clear GRER/GFER when the corresponding - * IRQ is masked because we could miss a level transition - * i.e. an IRQ which need servicing as soon as it is - * unmasked. However, such situation should happen only - * during the loop below. Thus all IRQs which aren't - * enabled at this point are considered spurious. Those - * are cleared but only de-activated if they happen twice. + * clear down all currently active IRQ sources. + * We will be processing them all. */ - spurious = irq & ~GPIO_11_27_enabled; - if (spurious) { - GEDR = spurious; - GRER &= ~(spurious & GPIO_11_27_spurious); - GFER &= ~(spurious & GPIO_11_27_spurious); - GPIO_11_27_spurious |= spurious; - irq ^= spurious; - if (!irq) continue; - } - - for (i = 11; i <= 27; ++i) { - if (irq & (1<>= 11; + do { + if (mask & 1) + desc->handle(irq, desc, regs); + mask >>= 1; + irq++; + desc++; + } while (mask); + + mask = GEDR & 0xfffff800; + } while (mask); +} -static void sa1100_mask_and_ack_GPIO11_27_irq(unsigned int irq) +/* + * Like GPIO0 to 10, GPIO11-27 IRQs need to be handled specially. + * In addition, the IRQs are all collected up into one bit in the + * interrupt controller registers. + */ +static void sa1100_high_gpio_ack(unsigned int irq) { - int mask = (1 << GPIO_11_27_IRQ(irq)); - GPIO_11_27_spurious &= ~mask; - GPIO_11_27_enabled &= ~mask; + unsigned int mask = GPIO11_27_MASK(irq); + GEDR = mask; } -static void sa1100_mask_GPIO11_27_irq(unsigned int irq) +static void sa1100_high_gpio_mask(unsigned int irq) { - int mask = (1 << GPIO_11_27_IRQ(irq)); - GPIO_11_27_spurious &= ~mask; - GPIO_11_27_enabled &= ~mask; + unsigned int mask = GPIO11_27_MASK(irq); + + GPIO_IRQ_mask &= ~mask; + + GRER &= ~mask; + GFER &= ~mask; } -static void sa1100_unmask_GPIO11_27_irq(unsigned int irq) +static void sa1100_high_gpio_unmask(unsigned int irq) { - int mask = (1 << GPIO_11_27_IRQ(irq)); - if (GPIO_11_27_spurious & mask) { - /* - * We don't want to miss an interrupt that would have occurred - * while it was masked. Simulate it if it is the case. - */ - int state = GPLR; - if (((state & GPIO_IRQ_rising_edge) | - (~state & GPIO_IRQ_falling_edge)) & mask) - { - /* just in case it gets referenced: */ - struct pt_regs dummy; - - memzero(&dummy, sizeof(dummy)); - do_IRQ(irq, &dummy); - - /* we are being called recursively from do_IRQ() */ - return; - } - } - GPIO_11_27_enabled |= mask; - GRER = (GRER & ~mask) | (GPIO_IRQ_rising_edge & mask); - GFER = (GFER & ~mask) | (GPIO_IRQ_falling_edge & mask); + unsigned int mask = GPIO11_27_MASK(irq); + + GPIO_IRQ_mask |= mask; + + GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask; + GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask; +} + +static struct irqchip sa1100_high_gpio_chip = { + ack: sa1100_high_gpio_ack, + mask: sa1100_high_gpio_mask, + unmask: sa1100_high_gpio_unmask, + rerun: sa1100_manual_rerun, + type: sa1100_gpio_type, +}; + +/* + * We don't need to ACK IRQs on the SA1100 unless they're GPIOs + * this is for internal IRQs i.e. from 11 to 31. + */ +static void sa1100_mask_irq(unsigned int irq) +{ + ICMR &= ~(1 << irq); +} + +static void sa1100_unmask_irq(unsigned int irq) +{ + ICMR |= (1 << irq); } +static struct irqchip sa1100_normal_chip = { + ack: sa1100_mask_irq, + mask: sa1100_mask_irq, + unmask: sa1100_unmask_irq, + /* rerun should never be called */ +}; + static struct resource irq_resource = { name: "irqs", start: 0x90050000, @@ -190,7 +201,7 @@ void __init sa1100_init_irq(void) { - int irq; + unsigned int irq; request_resource(&iomem_resource, &irq_resource); @@ -212,33 +223,32 @@ ICCR = 1; for (irq = 0; irq <= 10; irq++) { - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; - irq_desc[irq].mask_ack = sa1100_mask_and_ack_GPIO0_10_irq; - irq_desc[irq].mask = sa1100_mask_GPIO0_10_irq; - irq_desc[irq].unmask = sa1100_unmask_GPIO0_10_irq; + set_irq_chip(irq, &sa1100_low_gpio_chip); + set_irq_handler(irq, do_edge_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } - for (irq = 11; irq <= 31; irq++) { - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 0; - irq_desc[irq].mask_ack = sa1100_mask_irq; - irq_desc[irq].mask = sa1100_mask_irq; - irq_desc[irq].unmask = sa1100_unmask_irq; + for (irq = 12; irq <= 31; irq++) { + set_irq_chip(irq, &sa1100_normal_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID); } for (irq = 32; irq <= 48; irq++) { - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; - irq_desc[irq].mask_ack = sa1100_mask_and_ack_GPIO11_27_irq; - irq_desc[irq].mask = sa1100_mask_GPIO11_27_irq; - irq_desc[irq].unmask = sa1100_unmask_GPIO11_27_irq; + set_irq_chip(irq, &sa1100_high_gpio_chip); + set_irq_handler(irq, do_edge_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } - setup_arm_irq( IRQ_GPIO11_27, &GPIO11_27_irq ); + + /* + * Install handler for GPIO 11-27 edge detect interrupts + */ + set_irq_chip(IRQ_GPIO11_27, &sa1100_normal_chip); + set_irq_chained_handler(IRQ_GPIO11_27, sa1100_high_gpio_handler); /* * We generally don't want the LCD IRQ being * enabled as soon as we request it. */ - irq_desc[IRQ_LCD].noautoenable = 1; + set_irq_flags(IRQ_LCD, IRQF_VALID | IRQF_NOAUTOEN); } diff -Nru a/arch/arm/mach-sa1100/itsy.c b/arch/arm/mach-sa1100/itsy.c --- a/arch/arm/mach-sa1100/itsy.c Thu Mar 7 18:17:41 2002 +++ b/arch/arm/mach-sa1100/itsy.c Thu Mar 7 18:17:41 2002 @@ -31,8 +31,8 @@ likely wrong. */ static struct map_desc itsy_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xe8000000, 0x00000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 0 */ - { 0xf0000000, 0x49000000, 0x01000000, DOMAIN_IO, 1, 1, 0, 0 }, /* EGPIO 0 */ + { 0xe8000000, 0x00000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* Flash bank 0 */ + { 0xf0000000, 0x49000000, 0x01000000, DOMAIN_IO, 0, 1, 0, 0 }, /* EGPIO 0 */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/jornada720.c b/arch/arm/mach-sa1100/jornada720.c --- a/arch/arm/mach-sa1100/jornada720.c Thu Mar 7 18:17:45 2002 +++ b/arch/arm/mach-sa1100/jornada720.c Thu Mar 7 18:17:45 2002 @@ -48,7 +48,7 @@ /* initialize extra IRQs */ set_GPIO_IRQ_edge(GPIO_GPIO1, GPIO_RISING_EDGE); - sa1111_init_irq(IRQ_GPIO1)); /* chained on GPIO 1 */ + sa1111_init_irq(IRQ_GPIO1); /* chained on GPIO 1 */ return 0; } @@ -66,9 +66,9 @@ static struct map_desc jornada720_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xf0000000, 0x48000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* Epson registers */ - { 0xf1000000, 0x48200000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* Epson frame buffer */ - { 0xf4000000, 0x40000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* SA-1111 */ + { 0xf0000000, 0x48000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* Epson registers */ + { 0xf1000000, 0x48200000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* Epson frame buffer */ + { 0xf4000000, 0x40000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* SA-1111 */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/lart.c b/arch/arm/mach-sa1100/lart.c --- a/arch/arm/mach-sa1100/lart.c Thu Mar 7 18:17:44 2002 +++ b/arch/arm/mach-sa1100/lart.c Thu Mar 7 18:17:44 2002 @@ -18,8 +18,8 @@ static struct map_desc lart_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xe8000000, 0x00000000, 0x00400000, DOMAIN_IO, 1, 1, 0, 0 }, /* main flash memory */ - { 0xec000000, 0x08000000, 0x00400000, DOMAIN_IO, 1, 1, 0, 0 }, /* main flash, alternative location */ + { 0xe8000000, 0x00000000, 0x00400000, DOMAIN_IO, 0, 1, 0, 0 }, /* main flash memory */ + { 0xec000000, 0x08000000, 0x00400000, DOMAIN_IO, 0, 1, 0, 0 }, /* main flash, alternative location */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/leds-adsbitsy.c b/arch/arm/mach-sa1100/leds-adsbitsy.c --- a/arch/arm/mach-sa1100/leds-adsbitsy.c Thu Mar 7 18:17:41 2002 +++ b/arch/arm/mach-sa1100/leds-adsbitsy.c Thu Mar 7 18:17:41 2002 @@ -28,7 +28,7 @@ { unsigned long flags; - save_flags_cli(flags); + local_irq_save(flags); switch (evt) { case led_start: @@ -92,5 +92,5 @@ GPCR = hw_led_state ^ LED_MASK; } - restore_flags(flags); + local_irq_restore(flags); } diff -Nru a/arch/arm/mach-sa1100/leds-graphicsclient.c b/arch/arm/mach-sa1100/leds-graphicsclient.c --- a/arch/arm/mach-sa1100/leds-graphicsclient.c Thu Mar 7 18:17:36 2002 +++ b/arch/arm/mach-sa1100/leds-graphicsclient.c Thu Mar 7 18:17:36 2002 @@ -30,7 +30,7 @@ { unsigned long flags; - save_flags_cli(flags); + local_irq_save(flags); switch (evt) { case led_start: @@ -100,5 +100,5 @@ GPCR = hw_led_state ^ LED_MASK; } - restore_flags(flags); + local_irq_restore(flags); } diff -Nru a/arch/arm/mach-sa1100/leds-graphicsmaster.c b/arch/arm/mach-sa1100/leds-graphicsmaster.c --- a/arch/arm/mach-sa1100/leds-graphicsmaster.c Thu Mar 7 18:17:38 2002 +++ b/arch/arm/mach-sa1100/leds-graphicsmaster.c Thu Mar 7 18:17:38 2002 @@ -30,7 +30,7 @@ { unsigned long flags; - save_flags_cli(flags); + local_irq_save(flags); switch (evt) { case led_start: @@ -100,5 +100,5 @@ GPCR = hw_led_state ^ LED_MASK; } - restore_flags(flags); + local_irq_restore(flags); } diff -Nru a/arch/arm/mach-sa1100/leds-system3.c b/arch/arm/mach-sa1100/leds-system3.c --- a/arch/arm/mach-sa1100/leds-system3.c Thu Mar 7 18:17:45 2002 +++ b/arch/arm/mach-sa1100/leds-system3.c Thu Mar 7 18:17:45 2002 @@ -26,7 +26,6 @@ * * */ -#include #include #include diff -Nru a/arch/arm/mach-sa1100/leds.c b/arch/arm/mach-sa1100/leds.c --- a/arch/arm/mach-sa1100/leds.c Thu Mar 7 18:17:39 2002 +++ b/arch/arm/mach-sa1100/leds.c Thu Mar 7 18:17:39 2002 @@ -5,7 +5,6 @@ * * Copyright (C) 2001 Nicolas Pitre */ -#include #include #include diff -Nru a/arch/arm/mach-sa1100/nanoengine.c b/arch/arm/mach-sa1100/nanoengine.c --- a/arch/arm/mach-sa1100/nanoengine.c Thu Mar 7 18:17:36 2002 +++ b/arch/arm/mach-sa1100/nanoengine.c Thu Mar 7 18:17:36 2002 @@ -23,7 +23,7 @@ SET_BANK( 0, 0xc0000000, 32*1024*1024 ); mi->nr_banks = 1; - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0, 0, 8192 ); setup_initrd( __phys_to_virt(0xc0800000), 4*1024*1024 ); @@ -34,9 +34,9 @@ static struct map_desc nanoengine_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xe8000000, 0x00000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 0 */ - { 0xf0000000, 0x10000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* System Registers */ - { 0xf1000000, 0x18A00000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* Internal PCI Config Space */ + { 0xe8000000, 0x00000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* Flash bank 0 */ + { 0xf0000000, 0x10000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* System Registers */ + { 0xf1000000, 0x18A00000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* Internal PCI Config Space */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/neponset.c b/arch/arm/mach-sa1100/neponset.c --- a/arch/arm/mach-sa1100/neponset.c Thu Mar 7 18:17:44 2002 +++ b/arch/arm/mach-sa1100/neponset.c Thu Mar 7 18:17:44 2002 @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -24,53 +23,78 @@ /* - * Install handler for Neponset IRQ. Yes, yes... we are way down the IRQ - * cascade which is not good for IRQ latency, but the hardware has been - * designed that way... + * Install handler for Neponset IRQ. Note that we have to loop here + * since the ETHERNET and USAR IRQs are level based, and we need to + * ensure that the IRQ signal is deasserted before returning. This + * is rather unfortunate. */ - -static void neponset_IRQ_demux( int irq, void *dev_id, struct pt_regs *regs ) +static void +neponset_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) { - int irr; + unsigned int irr; + + while (1) { + struct irqdesc *d; + + /* + * Acknowledge the parent IRQ. + */ + desc->chip->ack(irq); - for(;;){ - irr = IRR & (IRR_ETHERNET | IRR_USAR | IRR_SA1111); - /* Let's have all active IRQ bits high. - * Note: there is a typo in the Neponset user's guide - * for the SA1111 IRR level. + /* + * Read the interrupt reason register. Let's have all + * active IRQ bits high. Note: there is a typo in the + * Neponset user's guide for the SA1111 IRR level. */ - irr ^= (IRR_ETHERNET | IRR_USAR); - if (!irr) break; + irr = IRR ^ (IRR_ETHERNET | IRR_USAR); - if( irr & IRR_ETHERNET ) - do_IRQ(IRQ_NEPONSET_SMC9196, regs); + if ((irr & (IRR_ETHERNET | IRR_USAR | IRR_SA1111)) == 0) + break; - if( irr & IRR_USAR ) - do_IRQ(IRQ_NEPONSET_USAR, regs); + /* + * Since there is no individual mask, we have to + * mask the parent IRQ. This is safe, since we'll + * recheck the register for any pending IRQs. + */ + if (irr & (IRR_ETHERNET | IRR_USAR)) { + desc->chip->mask(irq); - if( irr & IRR_SA1111 ) - sa1111_IRQ_demux(irq, dev_id, regs); + if (irr & IRR_ETHERNET) { + d = irq_desc + IRQ_NEPONSET_SMC9196; + d->handle(IRQ_NEPONSET_SMC9196, d, regs); + } + + if (irr & IRR_USAR) { + d = irq_desc + IRQ_NEPONSET_USAR; + d->handle(IRQ_NEPONSET_USAR, d, regs); + } + + desc->chip->unmask(irq); + } + + if (irr & IRR_SA1111) { + d = irq_desc + IRQ_NEPONSET_SA1111; + d->handle(IRQ_NEPONSET_SA1111, d, regs); + } } } -static struct irqaction neponset_irq = { - name: "Neponset", - handler: neponset_IRQ_demux, - flags: SA_INTERRUPT -}; - static void __init neponset_init_irq(void) { - sa1111_init_irq(-1); /* SA1111 IRQ not routed to a GPIO */ - - /* setup extra Neponset IRQs */ - irq_desc[IRQ_NEPONSET_SMC9196].valid = 1; - irq_desc[IRQ_NEPONSET_SMC9196].probe_ok = 1; - irq_desc[IRQ_NEPONSET_USAR].valid = 1; - irq_desc[IRQ_NEPONSET_USAR].probe_ok = 1; + /* + * Install handler for GPIO25. + */ + set_irq_type(IRQ_GPIO25, IRQT_RISING); + set_irq_chained_handler(IRQ_GPIO25, neponset_irq_handler); - set_GPIO_IRQ_edge(GPIO_GPIO25, GPIO_RISING_EDGE); - setup_arm_irq(IRQ_GPIO25, &neponset_irq); + /* + * Setup other Neponset IRQs. SA1111 will be done by the + * generic SA1111 code. + */ + set_irq_handler(IRQ_NEPONSET_SMC9196, do_simple_IRQ); + set_irq_flags(IRQ_NEPONSET_SMC9196, IRQF_VALID | IRQF_PROBE); + set_irq_handler(IRQ_NEPONSET_USAR, do_simple_IRQ); + set_irq_flags(IRQ_NEPONSET_USAR, IRQF_VALID | IRQF_PROBE); } static int __init neponset_init(void) @@ -101,6 +125,8 @@ return -ENODEV; } + neponset_init_irq(); + /* * Disable GPIO 0/1 drivers so the buttons work on the module. */ @@ -146,7 +172,10 @@ */ sa1110_mb_enable(); - neponset_init_irq(); + /* + * Initialise SA1111 IRQs + */ + sa1111_init_irq(IRQ_NEPONSET_SA1111); return 0; } @@ -155,8 +184,8 @@ static struct map_desc neponset_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xf3000000, 0x10000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* System Registers */ - { 0xf4000000, 0x40000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* SA-1111 */ + { 0xf3000000, 0x10000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* System Registers */ + { 0xf4000000, 0x40000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* SA-1111 */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/omnimeter.c b/arch/arm/mach-sa1100/omnimeter.c --- a/arch/arm/mach-sa1100/omnimeter.c Thu Mar 7 18:17:36 2002 +++ b/arch/arm/mach-sa1100/omnimeter.c Thu Mar 7 18:17:36 2002 @@ -47,14 +47,14 @@ SET_BANK( 0, 0xc0000000, 16*1024*1024 ); mi->nr_banks = 1; - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0, 0, 8192 ); setup_initrd( __phys_to_virt(0xd0000000), 0x00400000 ); } static struct map_desc omnimeter_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xd2000000, 0x10000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* TS */ + { 0xd2000000, 0x10000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* TS */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/pangolin.c b/arch/arm/mach-sa1100/pangolin.c --- a/arch/arm/mach-sa1100/pangolin.c Thu Mar 7 18:17:46 2002 +++ b/arch/arm/mach-sa1100/pangolin.c Thu Mar 7 18:17:46 2002 @@ -1,7 +1,7 @@ /* * linux/arch/arm/mach-sa1100/pangolin.c */ - +#include #include #include #include @@ -23,14 +23,14 @@ SET_BANK( 0, 0xc0000000, 128*1024*1024 ); mi->nr_banks = 1; - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0, 0, 16384 ); setup_initrd( 0xc0800000, 3*1024*1024 ); } static struct map_desc pangolin_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xf2800000, 0x4b800000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* MQ200 */ + { 0xf2800000, 0x4b800000, 0x00800000, DOMAIN_IO, 0, 1, 0, 0 }, /* MQ200 */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/pfs168.c b/arch/arm/mach-sa1100/pfs168.c --- a/arch/arm/mach-sa1100/pfs168.c Thu Mar 7 18:17:36 2002 +++ b/arch/arm/mach-sa1100/pfs168.c Thu Mar 7 18:17:36 2002 @@ -1,7 +1,7 @@ /* * linux/arch/arm/mach-sa1100/pfs168.c */ - +#include #include #include #include @@ -96,27 +96,27 @@ SET_BANK( 0, 0xc0000000, 16*1024*1024 ); mi->nr_banks = 1; - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0, 0, 8192 ); setup_initrd( 0xc0800000, 3*1024*1024 ); } static struct map_desc pfs168_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xe8000000, 0x00000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 0 */ - { 0xf0000000, 0x10000000, 0x00001000, DOMAIN_IO, 1, 1, 0, 0 }, /* 16C752 DUART port A (COM5) */ - { 0xf0001000, 0x10800000, 0x00001000, DOMAIN_IO, 1, 1, 0, 0 }, /* 16C752 DUART port B (COM6) */ - { 0xf0002000, 0x11000000, 0x00001000, DOMAIN_IO, 1, 1, 0, 0 }, /* COM1 RTS control (SYSC1RTS) */ - { 0xf0003000, 0x11400000, 0x00001000, DOMAIN_IO, 1, 1, 0, 0 }, /* Status LED control (SYSLED) */ - { 0xf0004000, 0x11800000, 0x00001000, DOMAIN_IO, 1, 1, 0, 0 }, /* DTMF code read (SYSDTMF) */ - { 0xf0005000, 0x11c00000, 0x00001000, DOMAIN_IO, 1, 1, 0, 0 }, /* LCD configure, enable (SYSLCDDE) */ - { 0xf0006000, 0x12000000, 0x00001000, DOMAIN_IO, 1, 1, 0, 0 }, /* COM1 DSR and motion sense (SYSC1DSR) */ - { 0xf0007000, 0x12800000, 0x00001000, DOMAIN_IO, 1, 1, 0, 0 }, /* COM3 xmit enable (SYSC3TEN) */ - { 0xf0008000, 0x13000000, 0x00001000, DOMAIN_IO, 1, 1, 0, 0 }, /* Control register A (SYSCTLA) */ - { 0xf0009000, 0x13800000, 0x00001000, DOMAIN_IO, 1, 1, 0, 0 }, /* Control register B (SYSCTLB) */ - { 0xf000a000, 0x18000000, 0x00001000, DOMAIN_IO, 1, 1, 0, 0 }, /* SMC91C96 */ - { 0xf2800000, 0x4b800000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* MQ200 */ - { 0xf4000000, 0x40000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* SA-1111 */ + { 0xe8000000, 0x00000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* Flash bank 0 */ + { 0xf0000000, 0x10000000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, /* 16C752 DUART port A (COM5) */ + { 0xf0001000, 0x10800000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, /* 16C752 DUART port B (COM6) */ + { 0xf0002000, 0x11000000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, /* COM1 RTS control (SYSC1RTS) */ + { 0xf0003000, 0x11400000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, /* Status LED control (SYSLED) */ + { 0xf0004000, 0x11800000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, /* DTMF code read (SYSDTMF) */ + { 0xf0005000, 0x11c00000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, /* LCD configure, enable (SYSLCDDE) */ + { 0xf0006000, 0x12000000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, /* COM1 DSR and motion sense (SYSC1DSR) */ + { 0xf0007000, 0x12800000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, /* COM3 xmit enable (SYSC3TEN) */ + { 0xf0008000, 0x13000000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, /* Control register A (SYSCTLA) */ + { 0xf0009000, 0x13800000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, /* Control register B (SYSCTLB) */ + { 0xf000a000, 0x18000000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, /* SMC91C96 */ + { 0xf2800000, 0x4b800000, 0x00800000, DOMAIN_IO, 0, 1, 0, 0 }, /* MQ200 */ + { 0xf4000000, 0x40000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* SA-1111 */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/pleb.c b/arch/arm/mach-sa1100/pleb.c --- a/arch/arm/mach-sa1100/pleb.c Thu Mar 7 18:17:41 2002 +++ b/arch/arm/mach-sa1100/pleb.c Thu Mar 7 18:17:41 2002 @@ -28,15 +28,15 @@ /* make it 1 if a 16MB memory card is used */ mi->nr_banks = 2; /* Default 32MB */ - ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR, 0); setup_ramdisk(1, 0, 0, 8192); setup_initrd(0xc0400000, 4*1024*1024); } static struct map_desc pleb_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xe8000000, 0x00000000, 0x00400000, DOMAIN_IO, 1, 1, 0, 0 }, /* main flash memory */ - { 0xe8400000, 0x08000000, 0x00400000, DOMAIN_IO, 1, 1, 0, 0 }, /* main flash, alternative location */ + { 0xe8000000, 0x00000000, 0x00400000, DOMAIN_IO, 0, 1, 0, 0 }, /* main flash memory */ + { 0xe8400000, 0x08000000, 0x00400000, DOMAIN_IO, 0, 1, 0, 0 }, /* main flash, alternative location */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/pm.c b/arch/arm/mach-sa1100/pm.c --- a/arch/arm/mach-sa1100/pm.c Thu Mar 7 18:17:40 2002 +++ b/arch/arm/mach-sa1100/pm.c Thu Mar 7 18:17:40 2002 @@ -51,8 +51,6 @@ int pm_do_suspend(void) { - int retval; - /* set up pointer to sleep parameters */ sleep_save = kmalloc(SLEEP_SAVE_SIZE*sizeof(long), GFP_ATOMIC); if (!sleep_save) @@ -107,7 +105,7 @@ PSPR = 0; #ifdef DEBUG - printk("*** made it back from resume\n"); + printk(KERN_DEBUG "*** made it back from resume\n"); #endif /* restore registers */ @@ -171,28 +169,22 @@ #define ACPI_S1_SLP_TYP 19 /* - * Send us to sleep. We must not be called from IRQ context. + * Send us to sleep. */ static int sysctl_pm_do_suspend(void) { int retval; - if (in_interrupt()) { - printk(KERN_CRIT "pm_do_suspend() called from IRQ\n"); - return -EINVAL; - } - retval = pm_send_all(PM_SUSPEND, (void *)3); if (retval == 0) { - retval = __pm_do_suspend(); + retval = pm_do_suspend(); pm_send_all(PM_RESUME, (void *)0); } return retval; } - static struct ctl_table pm_table[] = { diff -Nru a/arch/arm/mach-sa1100/sa1111-pcibuf.c b/arch/arm/mach-sa1100/sa1111-pcibuf.c --- a/arch/arm/mach-sa1100/sa1111-pcibuf.c Thu Mar 7 18:17:36 2002 +++ b/arch/arm/mach-sa1100/sa1111-pcibuf.c Thu Mar 7 18:17:36 2002 @@ -13,7 +13,6 @@ * * 06/13/2001 - created. */ -#include #include #include #include diff -Nru a/arch/arm/mach-sa1100/sa1111.c b/arch/arm/mach-sa1100/sa1111.c --- a/arch/arm/mach-sa1100/sa1111.c Thu Mar 7 18:17:42 2002 +++ b/arch/arm/mach-sa1100/sa1111.c Thu Mar 7 18:17:42 2002 @@ -27,7 +27,6 @@ #include #include #include -#include #include @@ -40,70 +39,104 @@ EXPORT_SYMBOL(sa1111_resource); /* - * SA1111 interrupt support + * SA1111 interrupt support. Since clearing an IRQ while there are + * active IRQs causes the interrupt output to pulse, the upper levels + * will call us again if there are more interrupts to process. */ -void sa1111_IRQ_demux(int irq, void *dev_id, struct pt_regs *regs) +static void +sa1111_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) { - unsigned long stat0, stat1; + unsigned int stat0, stat1, i; - while (1) { - int i; + desc->chip->ack(irq); - stat0 = INTSTATCLR0; - stat1 = INTSTATCLR1; + stat0 = INTSTATCLR0; + stat1 = INTSTATCLR1; - if (stat0 == 0 && stat1 == 0) - break; - - for (i = IRQ_SA1111_START; stat0; i++, stat0 >>= 1) - if (stat0 & 1) - do_IRQ(i, regs); - - for (i = IRQ_SA1111_START + 32; stat1; i++, stat1 >>= 1) - if (stat1 & 1) - do_IRQ(i, regs); + if (stat0 == 0 && stat1 == 0) { + do_bad_IRQ(irq, desc, regs); + return; } + + for (i = IRQ_SA1111_START; stat0; i++, stat0 >>= 1) + if (stat0 & 1) + do_edge_IRQ(i, irq_desc + i, regs); + + for (i = IRQ_SA1111_START + 32; stat1; i++, stat1 >>= 1) + if (stat1 & 1) + do_edge_IRQ(i, irq_desc + i, regs); } #define SA1111_IRQMASK_LO(x) (1 << (x - IRQ_SA1111_START)) #define SA1111_IRQMASK_HI(x) (1 << (x - IRQ_SA1111_START - 32)) +static void sa1111_ack_lowirq(unsigned int irq) +{ + INTSTATCLR0 = SA1111_IRQMASK_LO(irq); +} + +static void sa1111_mask_lowirq(unsigned int irq) +{ + INTEN0 &= ~SA1111_IRQMASK_LO(irq); +} + +static void sa1111_unmask_lowirq(unsigned int irq) +{ + INTEN0 |= SA1111_IRQMASK_LO(irq); +} + /* - * A note about masking IRQs: - * - * The GPIO IRQ edge detection only functions while the IRQ itself is - * enabled; edges are not detected while the IRQ is disabled. - * - * This is especially important for the PCMCIA signals, where we must - * pick up every transition. We therefore do not disable the IRQs - * while processing them. - * - * However, since we are changed to a GPIO on the host processor, - * all SA1111 IRQs will be disabled while we're processing any SA1111 - * IRQ. - * - * Note also that changing INTPOL while an IRQ is enabled will itself - * trigger an IRQ. + * Attempt to re-trigger the interrupt. The SA1111 contains a register + * (INTSET) which claims to do this. However, in practice no amount of + * manipulation of INTEN and INTSET guarantees that the interrupt will + * be triggered. In fact, its very difficult, if not impossible to get + * INTSET to re-trigger the interrupt. */ -static void sa1111_mask_and_ack_lowirq(unsigned int irq) +static void sa1111_rerun_lowirq(unsigned int irq) { unsigned int mask = SA1111_IRQMASK_LO(irq); + int i; + + for (i = 0; i < 8; i++) { + INTPOL0 ^= mask; + INTPOL0 ^= mask; + if (INTSTATCLR1 & mask) + break; + } - //INTEN0 &= ~mask; - INTSTATCLR0 = mask; + if (i == 8) + printk(KERN_ERR "Danger Will Robinson: failed to " + "re-trigger IRQ%d\n", irq); } -static void sa1111_mask_and_ack_highirq(unsigned int irq) +static int sa1111_type_lowirq(unsigned int irq, unsigned int flags) { - unsigned int mask = SA1111_IRQMASK_HI(irq); + unsigned int mask = SA1111_IRQMASK_LO(irq); + + if ((!(flags & __IRQT_RISEDGE) ^ !(flags & __IRQT_FALEDGE)) == 0) + return -EINVAL; + + printk("IRQ%d: %s edge\n", irq, flags & __IRQT_RISEDGE ? "rising" : "falling"); - //INTEN1 &= ~mask; - INTSTATCLR1 = mask; + if (flags & __IRQT_RISEDGE) + INTPOL0 &= ~mask; + else + INTPOL0 |= mask; + + return 0; } -static void sa1111_mask_lowirq(unsigned int irq) +static struct irqchip sa1111_low_chip = { + ack: sa1111_ack_lowirq, + mask: sa1111_mask_lowirq, + unmask: sa1111_unmask_lowirq, + rerun: sa1111_rerun_lowirq, + type: sa1111_type_lowirq, +}; + +static void sa1111_ack_highirq(unsigned int irq) { - INTEN0 &= ~SA1111_IRQMASK_LO(irq); + INTSTATCLR1 = SA1111_IRQMASK_HI(irq); } static void sa1111_mask_highirq(unsigned int irq) @@ -111,19 +144,63 @@ INTEN1 &= ~SA1111_IRQMASK_HI(irq); } -static void sa1111_unmask_lowirq(unsigned int irq) +static void sa1111_unmask_highirq(unsigned int irq) { - INTEN0 |= SA1111_IRQMASK_LO(irq); + INTEN1 |= SA1111_IRQMASK_HI(irq); } -static void sa1111_unmask_highirq(unsigned int irq) +/* + * Attempt to re-trigger the interrupt. The SA1111 contains a register + * (INTSET) which claims to do this. However, in practice no amount of + * manipulation of INTEN and INTSET guarantees that the interrupt will + * be triggered. In fact, its very difficult, if not impossible to get + * INTSET to re-trigger the interrupt. + */ +static void sa1111_rerun_highirq(unsigned int irq) { - INTEN1 |= SA1111_IRQMASK_HI(irq); + unsigned int mask = SA1111_IRQMASK_HI(irq); + int i; + + for (i = 0; i < 8; i++) { + INTPOL1 ^= mask; + INTPOL1 ^= mask; + if (INTSTATCLR1 & mask) + break; + } + + if (i == 8) + printk(KERN_ERR "Danger Will Robinson: failed to " + "re-trigger IRQ%d\n", irq); } +static int sa1111_type_highirq(unsigned int irq, unsigned int flags) +{ + unsigned int mask = SA1111_IRQMASK_HI(irq); + + if ((!(flags & __IRQT_RISEDGE) ^ !(flags & __IRQT_FALEDGE)) == 0) + return -EINVAL; + + printk("IRQ%d: %s edge\n", irq, flags & __IRQT_RISEDGE ? "rising" : "falling"); + + if (flags & __IRQT_RISEDGE) + INTPOL1 &= ~mask; + else + INTPOL1 |= mask; + + return 0; +} + +static struct irqchip sa1111_high_chip = { + ack: sa1111_ack_highirq, + mask: sa1111_mask_highirq, + unmask: sa1111_unmask_highirq, + rerun: sa1111_rerun_highirq, + type: sa1111_type_highirq, +}; + void __init sa1111_init_irq(int irq_nr) { - int irq, ret; + unsigned int irq; request_mem_region(_INTTEST0, 512, "irqs"); @@ -140,33 +217,26 @@ SA1111_IRQMASK_HI(S1_READY_NINT); /* clear all IRQs */ - INTSTATCLR0 = -1; - INTSTATCLR1 = -1; + INTSTATCLR0 = ~0; + INTSTATCLR1 = ~0; for (irq = IRQ_GPAIN0; irq <= SSPROR; irq++) { - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 0; - irq_desc[irq].mask_ack = sa1111_mask_and_ack_lowirq; - irq_desc[irq].mask = sa1111_mask_lowirq; - irq_desc[irq].unmask = sa1111_unmask_lowirq; + set_irq_chip(irq, &sa1111_low_chip); + set_irq_handler(irq, do_edge_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } + for (irq = AUDXMTDMADONEA; irq <= S1_BVD1_STSCHG; irq++) { - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 0; - irq_desc[irq].mask_ack = sa1111_mask_and_ack_highirq; - irq_desc[irq].mask = sa1111_mask_highirq; - irq_desc[irq].unmask = sa1111_unmask_highirq; + set_irq_chip(irq, &sa1111_high_chip); + set_irq_handler(irq, do_edge_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } - /* Register SA1111 interrupt */ - if (irq_nr < 0) - return; - - ret = request_irq(irq_nr, sa1111_IRQ_demux, SA_INTERRUPT, - "SA1111", NULL); - if (ret < 0) - printk(KERN_ERR "SA1111: unable to claim IRQ%d: %d\n", - irq_nr, ret); + /* + * Register SA1111 interrupt + */ + set_irq_type(irq_nr, IRQT_RISING); + set_irq_chained_handler(irq_nr, sa1111_irq_handler); } /** @@ -199,12 +269,12 @@ */ id = SBI_SKID; if ((id & SKID_ID_MASK) != SKID_SA1111_ID) { - printk(KERN_DEBUG "SA-1111 not detected: ID = %08lx\n", id); + printk(KERN_DEBUG "SA1111 not detected: ID = %08lx\n", id); ret = -ENODEV; goto release; } - printk(KERN_INFO "SA-1111 Microprocessor Companion Chip: " + printk(KERN_INFO "SA1111 Microprocessor Companion Chip: " "silicon revision %lx, metal revision %lx\n", (id & SKID_SIREV_MASK)>>4, (id & SKID_MTREV_MASK)); diff -Nru a/arch/arm/mach-sa1100/sherman.c b/arch/arm/mach-sa1100/sherman.c --- a/arch/arm/mach-sa1100/sherman.c Thu Mar 7 18:17:36 2002 +++ b/arch/arm/mach-sa1100/sherman.c Thu Mar 7 18:17:36 2002 @@ -24,7 +24,7 @@ SET_BANK( 1, 0xc8000000, 64*1024*1024 ); mi->nr_banks = 2; - ROOT_DEV = MKDEV( 60, 2 ); + ROOT_DEV = mk_kdev( 60, 2 ); setup_ramdisk( 1, 0, 0, 8192 ); // setup_initrd( 0xc0400000, 8*1024*1024 ); } diff -Nru a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c --- a/arch/arm/mach-sa1100/simpad.c Thu Mar 7 18:17:38 2002 +++ b/arch/arm/mach-sa1100/simpad.c Thu Mar 7 18:17:38 2002 @@ -50,7 +50,7 @@ SET_BANK( 0, 0xc0000000, 32*1024*1024 ); #endif mi->nr_banks = 1; - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0, 0, 8192 ); setup_initrd( __phys_to_virt(0xc0800000), 4*1024*1024 ); } @@ -58,7 +58,7 @@ static struct map_desc simpad_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xf2800000, 0x4b800000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* MQ200 */ + { 0xf2800000, 0x4b800000, 0x00800000, DOMAIN_IO, 0, 1, 0, 0 }, /* MQ200 */ { 0xf1000000, 0x18000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* Paules CS3, write only */ LAST_DESC }; @@ -93,7 +93,7 @@ sa1100_register_uart(0, 3); sa1100_register_uart(1, 1); - set_GPIO_IRQ_edge(GPIO_UCB1300_IRQ); + set_irq_type(IRQ_GPIO_UCB1300_IRQ, IRQT_RISING); } #ifdef CONFIG_PROC_FS diff -Nru a/arch/arm/mach-sa1100/stork.c b/arch/arm/mach-sa1100/stork.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mach-sa1100/stork.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,361 @@ +/* + * linux/arch/arm/mach-sa1100/stork.c + * + * Copyright (C) 2001 Ken Gordon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "generic.h" + + +#define STORK_VM_BASE_CS1 0xf0000000 /* where we get mapped (virtual) */ +#define STORK_VM_OFF_CS1 0x08000000 /* where we started mapping (physical) */ +#define STORK_VM_ADJUST_CS1 (STORK_VM_BASE_CS1-STORK_VM_OFF_CS1) /* add to the phys to get virt */ + +#define STORK_VM_BASE_CS2 0xf1000000 /* where we get mapped (virtual) */ +#define STORK_VM_OFF_CS2 0x10000000 /* where we started mapping (physical) */ +#define STORK_VM_ADJUST_CS2 (STORK_VM_BASE_CS2-STORK_VM_OFF_CS2) /* add to the phys to get virt */ + +static int debug = 0; + +static int storkLatchA = 0; +static int storkLatchB = 0; +static int storkLCDCPLD[4] = { 0, 0, 0, 0}; + +int +storkSetLatchA(int bits) +{ + int ret = storkLatchA; + volatile unsigned int *latch = (unsigned int *)(STORK_LATCH_A_ADDR+STORK_VM_ADJUST_CS1); + + storkLatchA |= bits; + *latch = storkLatchA; + return ret; +} + +int +storkClearLatchA(int bits) +{ + int ret = storkLatchA; + volatile unsigned int *latch = (unsigned int *)(STORK_LATCH_A_ADDR+STORK_VM_ADJUST_CS1); + + storkLatchA &= ~bits; + *latch = storkLatchA; + return ret; +} + +int +storkSetLCDCPLD(int which, int bits) +{ + int ret = storkLCDCPLD[which]; + volatile unsigned int *latch = (unsigned int *)(STORK_LCDCPLD_BASE_ADDR+STORK_VM_ADJUST_CS2 + 0x20*which); + + storkLCDCPLD[which] |= bits; + *latch = storkLCDCPLD[which]; + return ret; +} + + +/* NB we don't shadow these 'cos there is no relation between the data written and the data read */ +/* ie the read registers are read only and the write registers write only */ + +int +storkGetLCDCPLD(int which) +{ + volatile unsigned int *latch = (unsigned int *)(STORK_LCDCPLD_BASE_ADDR+STORK_VM_ADJUST_CS2 + 0x20*which); + return *latch; +} + +int +storkClearLCDCPLD(int which, int bits) +{ + int ret = storkLCDCPLD[which]; + volatile unsigned int *latch = (unsigned int *)(STORK_LCDCPLD_BASE_ADDR+STORK_VM_ADJUST_CS2 + 0x20*which); + + storkLCDCPLD[which] &= ~bits; + *latch = storkLCDCPLD[which]; + return ret; +} + +int +storkSetLatchB(int bits) +{ + int ret = storkLatchB; + char buf[100]; + + volatile unsigned int *latch = (unsigned int *)(STORK_LATCH_B_ADDR+STORK_VM_ADJUST_CS1); + sprintf(buf, "%s: bits %04x\n", __FUNCTION__, bits); + if (debug) printk(buf); + + storkLatchB |= bits; + *latch = storkLatchB; + return ret; +} + +int +storkClearLatchB(int bits) +{ + int ret = storkLatchB; + char buf[100]; + + volatile unsigned int *latch = (unsigned int *)(STORK_LATCH_B_ADDR+STORK_VM_ADJUST_CS1); + sprintf(buf, "%s: bits %04x\n", __FUNCTION__, bits); + if (debug) printk(buf); + + storkLatchB &= ~bits; + *latch = storkLatchB; + return ret; +} + +void +storkSetGPIO(int bits) +{ + char buf[100]; + + sprintf(buf, "%s: bits %04x\n", __FUNCTION__, bits); + if (debug) printk(buf); + GPSR = bits; +} + +void +storkClearGPIO(int bits) +{ + char buf[100]; + + sprintf(buf, "%s: bits %04x\n", __FUNCTION__, bits); + if (debug) printk(buf); + GPCR = bits; +} + +int +storkGetGPIO() +{ + char buf[100]; + + int bits = GPLR; + + sprintf(buf, "%s: bits %04x\n", __FUNCTION__, bits); + if (debug) printk(buf); + + return bits; +} + +/* this will return the current state of the hardware ANDED with the given bits + so NE => at least one bit was set, but maybe not all of them! */ + +int +storkTestGPIO(int bits) +{ + int val = storkGetGPIO(); + char buf[100]; + + sprintf(buf, "%s: bits %04x val %04x\n", __FUNCTION__, bits, val); + if (debug) printk(buf); + + return (val & bits); +} + +/* NB the touch screen and the d to a use the same data and clock out pins */ + +static void storkClockTS(void) +{ + storkSetLatchB(STORK_TOUCH_SCREEN_DCLK); + udelay(10); /* hmm wait 200ns (min) - ok this ought to be udelay(1) but that doesn't get */ + /* consistant values so I'm using 10 (urgh) */ + storkClearLatchB(STORK_TOUCH_SCREEN_DCLK); + udelay(10); +} + + +int /* there is always a 12 bit read after the write! */ +storkClockByteToTS(int byte) +{ + int timeout = 10000; /* stuff is meant to happen in 60ns */ + int bit; + int result = 0; + + if (debug) printk("storkClockByteToTS: %02x\n", byte); + + storkClearLatchB(STORK_TOUCH_SCREEN_CS); /* slect touch screen */ + + while (timeout-- > 0) + if (storkTestGPIO(GPIO_STORK_TOUCH_SCREEN_BUSY) == 0) + break; + + if (timeout < 0) { + printk("storkClockBitToTS: GPIO_STORK_TOUCH_SCREEN_BUSY didn't go low!\n\r"); +/* ignore error for now return; */ + } + +/* clock out the given byte */ + + for (bit = 0x80; bit > 0; bit = bit >> 1) { + + if ((bit & byte) == 0) + storkClearLatchB(STORK_TOUCH_SCREEN_DIN); + else + storkSetLatchB(STORK_TOUCH_SCREEN_DIN); + + storkClockTS(); + } + + storkClockTS(); /* will be busy for at a clock (at least) */ + + for (timeout = 10000; timeout >= 0; timeout--) + if (storkTestGPIO(GPIO_STORK_TOUCH_SCREEN_BUSY) == 0) + break; + + if (timeout < 0) { + printk("storkClockBitToTS: 2nd GPIO_STORK_TOUCH_SCREEN_BUSY didn't go low!\n\r"); +/* ignore error for now return; */ + } + +/* clock in the result */ + + for (bit = 0x0800; bit > 0; bit = bit >> 1) { + + if (storkTestGPIO(GPIO_STORK_TOUCH_SCREEN_DATA)) + result |= bit; + + storkClockTS(); + } + + storkSetLatchB(STORK_TOUCH_SCREEN_CS); /* unselect touch screen */ + + return result; +} + +void +storkClockShortToDtoA(int word) +{ + int bit; + + storkClearLatchB(STORK_DA_CS); /* select D to A */ + +/* clock out the given byte */ + + for (bit = 0x8000; bit > 0; bit = bit >> 1) { + + if ((bit & word) == 0) + storkClearLatchB(STORK_TOUCH_SCREEN_DIN); + else + storkSetLatchB(STORK_TOUCH_SCREEN_DIN); + + storkClockTS(); + } + + storkSetLatchB(STORK_DA_CS); /* unselect D to A */ + +/* set DTOA#_LOAD low then high (min 20ns) to transfer value to D to A */ + storkClearLatchB(STORK_DA_LD); + storkSetLatchB(STORK_DA_LD); +} + + + +void +storkInitTSandDtoA(void) +{ + storkClearLatchB(STORK_TOUCH_SCREEN_DCLK | STORK_TOUCH_SCREEN_DIN); + storkSetLatchB(STORK_TOUCH_SCREEN_CS | STORK_DA_CS | STORK_DA_LD); + storkClockByteToTS(0xE2); /* turn on the reference */ + storkClockShortToDtoA(0x8D00); /* turn on the contrast */ + storkClockShortToDtoA(0x0A00); /* turn on the brightness */ +} + +/* see asm-arm/keyboard.h - there are a bunch of basically virtual functions required */ +/* we have to fill in for them or else we can't call handle_scancode when we see a button pressed */ + +static int +stork_kbd_translate(unsigned char scancode, unsigned char *keycode, char rawmode) +{ + if (keycode) + *keycode = scancode; + + return 1; +} + +static char +stork_kbd_unexpected_up(unsigned char code) +{ + return 0; +} + + +struct map_desc stork_io_desc[] __initdata = { + /* virtual physical length domain r w c b */ + { 0xe8000000, 0x00000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* Flash bank 0 */ + { STORK_VM_BASE_CS1, STORK_VM_OFF_CS1, 0x01000000, DOMAIN_IO, 0, 1, 0, 0 }, /* EGPIO 0 */ + { 0xf1000000, 0x10000000, 0x02800000, DOMAIN_IO, 0, 1, 0, 0 }, /* static memory bank 2 */ + { 0xf3800000, 0x40000000, 0x00800000, DOMAIN_IO, 0, 1, 0, 0 }, /* static memory bank 4 */ + LAST_DESC +}; + +int __init +stork_map_io(void) +{ + sa1100_map_io(); + iotable_init(stork_io_desc); + + sa1100_register_uart(0, 1); /* com port */ + sa1100_register_uart(1, 2); + sa1100_register_uart(2, 3); + + printk("Stork driver initing latches\r\n"); + + storkClearLatchB(STORK_RED_LED); /* let's have the red LED on please */ + storkSetLatchB(STORK_YELLOW_LED); + storkSetLatchB(STORK_GREEN_LED); + storkSetLatchA(STORK_BATTERY_CHARGER_ON); + storkSetLatchA(STORK_LCD_5V_POWER_ON); + storkSetLatchA(STORK_LCD_3V3_POWER_ON); + + storkInitTSandDtoA(); + + k_translate = stork_kbd_translate; + k_unexpected_up = stork_kbd_unexpected_up; + + return 0; +} + + +MACHINE_START(STORK, "Stork Technologies prototype") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + BOOT_PARAMS(0xc0000100) + MAPIO(stork_map_io) + INITIRQ(sa1100_init_irq) +MACHINE_END + + +EXPORT_SYMBOL(storkTestGPIO); +EXPORT_SYMBOL(storkSetGPIO); +EXPORT_SYMBOL(storkClearGPIO); +EXPORT_SYMBOL(storkSetLatchA); +EXPORT_SYMBOL(storkClearLatchA); +EXPORT_SYMBOL(storkSetLatchB); +EXPORT_SYMBOL(storkClearLatchB); +EXPORT_SYMBOL(storkClockByteToTS); +EXPORT_SYMBOL(storkClockShortToDtoA); +EXPORT_SYMBOL(storkGetLCDCPLD); +EXPORT_SYMBOL(storkSetLCDCPLD); diff -Nru a/arch/arm/mach-sa1100/system3.c b/arch/arm/mach-sa1100/system3.c --- a/arch/arm/mach-sa1100/system3.c Thu Mar 7 18:17:41 2002 +++ b/arch/arm/mach-sa1100/system3.c Thu Mar 7 18:17:41 2002 @@ -54,12 +54,11 @@ #include #include -#include - #include #include "generic.h" #include "sa1111.h" +#include #define DEBUG 1 @@ -76,19 +75,17 @@ /* init funcs */ static void __init fixup_system3(struct machine_desc *desc, struct param_struct *params, char **cmdline, struct meminfo *mi); -static void __init get_system3_scr(void); static int __init system3_init(void); static void __init system3_init_irq(void); static void __init system3_map_io(void); -static void system3_IRQ_demux( int irq, void *dev_id, struct pt_regs *regs ); -static int system3_get_mctrl(struct uart_port *port); +static u_int system3_get_mctrl(struct uart_port *port); static void system3_set_mctrl(struct uart_port *port, u_int mctrl); static void system3_uart_pm(struct uart_port *port, u_int state, u_int oldstate); static int sdram_notifier(struct notifier_block *nb, unsigned long event, void *data); -static int system3_lcd_power(int on); -static int system3_backlight_power(int on); +static void system3_lcd_power(int on); +static void system3_backlight_power(int on); extern void convert_to_tag_list(struct param_struct *params, int mem_init); @@ -103,9 +100,9 @@ static struct map_desc system3_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xe8000000, 0x00000000, 0x01000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 0 */ - { 0xf3000000, 0x10000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* System Registers */ - { 0xf4000000, 0x40000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* SA-1111 */ + { 0xe8000000, 0x00000000, 0x01000000, DOMAIN_IO, 0, 1, 0, 0 }, /* Flash bank 0 */ + { 0xf3000000, PT_CPLD_BASE, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* System Registers */ + { 0xf4000000, PT_SA1111_BASE, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* SA-1111 */ LAST_DESC }; @@ -115,12 +112,6 @@ pm: system3_uart_pm, }; -static struct irqaction system3_irq = { - name: "PT Digital Board SA1111 IRQ", - handler: system3_IRQ_demux, - flags: SA_INTERRUPT -}; - static struct notifier_block system3_clkchg_block = { notifier_call: sdram_notifier, }; @@ -147,56 +138,82 @@ /********************************************************************* * Install IRQ handler */ -static void system3_IRQ_demux( int irq, void *dev_id, struct pt_regs *regs ) +static void +system3_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) { u_char irr; - for(;;){ - //irr = PTCPLD_REG_IRQSR & (PT_IRQ_LAN | PT_IRQ_USAR | PT_IRQ_SA1111); - irr = PT_IRQSR & (PT_IRQ_LAN | PT_IRQ_SA1111); - - irr ^= (PT_IRQ_LAN); - if (!irr) break; - - if( irr & PT_IRQ_LAN ) - do_IRQ(IRQ_SYSTEM3_SMC9196, regs); - -#if 0 - /* Highspeed Serial Bus not yet used */ - if( irr & PT_IRQ_USAR ) - do_IRQ(PT_USAR_IRQ, regs); -#endif + //DPRINTK( "irq=%d, desc=%p, regs=%p\n", irq, desc, regs ); - if( irr & PT_IRQ_SA1111 ) - sa1111_IRQ_demux(irq, dev_id, regs); - } -} + while (1) { + struct irqdesc *d; + /* + * Acknowledge the parent IRQ. + */ + desc->chip->ack(irq); + + /* + * Read the interrupt reason register. Let's have all + * active IRQ bits high. Note: there is a typo in the + * Neponset user's guide for the SA1111 IRR level. + */ + //irr = PT_IRQSR & (PT_IRR_LAN | PT_IRR_SA1111); + irr = PT_IRQSR & (PT_IRR_SA1111); -static void __init system3_init_irq(void) -{ - int irq; + /* SMC IRQ is low-active, so "switch" bit over */ + //irr ^= (PT_IRR_LAN); - DPRINTK( "%s\n", "START" ); + //DPRINTK( "irr=0x%02x\n", irr ); - /* SA1111 IRQ not routed to a GPIO. */ - sa1111_init_irq(-1); + if ((irr & (PT_IRR_LAN | PT_IRR_SA1111)) == 0) + break; - /* setup extra IRQs */ - irq = IRQ_SYSTEM3_SMC9196; - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; - -#if 0 - /* Highspeed Serial Bus not yet used */ - irq = PT_USAR_IRQ; - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; + /* + * Since there is no individual mask, we have to + * mask the parent IRQ. This is safe, since we'll + * recheck the register for any pending IRQs. + */ + if (irr & (PT_IRR_LAN)) { + desc->chip->mask(irq); + + if (irr & PT_IRR_LAN) { + //DPRINTK( "SMC9196, irq=%d\n", IRQ_SYSTEM3_SMC9196 ); + d = irq_desc + IRQ_SYSTEM3_SMC9196; + d->handle(IRQ_SYSTEM3_SMC9196, d, regs); + } + +#if 0 /* no SSP yet on system 4 */ + if (irr & IRR_USAR) { + d = irq_desc + IRQ_NEPONSET_USAR; + d->handle(IRQ_NEPONSET_USAR, d, regs); + } #endif - /* IRQ by CPLD */ - set_GPIO_IRQ_edge( GPIO_GPIO(25), GPIO_RISING_EDGE ); - setup_arm_irq( IRQ_GPIO25, &system3_irq ); + desc->chip->unmask(irq); + } + + if (irr & PT_IRR_SA1111) { + //DPRINTK( "SA1111, irq=%d\n", IRQ_SYSTEM3_SA1111 ); + d = irq_desc + IRQ_SYSTEM3_SA1111; + d->handle(IRQ_SYSTEM3_SA1111, d, regs); + } + } +} + +static void __init system3_init_irq(void) +{ + /* + * Install handler for GPIO25. + */ + set_irq_type(IRQ_GPIO25, IRQT_RISING); + set_irq_chained_handler(IRQ_GPIO25, system3_irq_handler); + + /* + * install eth irq + */ + set_irq_handler(IRQ_SYSTEM3_SMC9196, do_simple_IRQ); + set_irq_flags(IRQ_SYSTEM3_SMC9196, IRQF_VALID | IRQF_PROBE); } /********************************************************************** @@ -227,7 +244,7 @@ { DPRINTK( "%s\n", "START" ); - ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + ROOT_DEV = mk_kdev(RAMDISK_MAJOR,0); setup_ramdisk( 1, 0, 0, 8192 ); setup_initrd( 0xc0800000, 8*1024*1024 ); } @@ -272,7 +289,7 @@ } } -static int system3_get_mctrl(struct uart_port *port) +static u_int system3_get_mctrl(struct uart_port *port) { u_int ret = 0; u_int irqsr = PT_IRQSR; @@ -360,12 +377,8 @@ static void system3_lcd_power(int on) { -#error why is backlight stuff here??? if (on) { system3_lcd_on(); - system3_lcd_backlight_on(); - system3_lcd_contrast(0x95); - system3_lcd_brightness(240); } else { system3_lcd_off(); } @@ -409,10 +422,12 @@ */ sa1110_mb_disable(); + system3_init_irq(); + /* * Probe for a SA1111. */ - ret = sa1111_probe(0x40000000); + ret = sa1111_probe(PT_SA1111_BASE); if (ret < 0) { printk( KERN_WARNING"PT Digital Board: no SA1111 found!\n" ); goto DONE; @@ -445,7 +460,11 @@ */ sa1110_mb_enable(); - system3_init_irq(); + /* + * Initialise SA1111 IRQs + */ + sa1111_init_irq(IRQ_SYSTEM3_SA1111); + #if defined( CONFIG_CPU_FREQ ) ret = cpufreq_register_notifier(&system3_clkchg_block); @@ -454,6 +473,7 @@ goto DONE; } #endif + ret = 0; DONE: diff -Nru a/arch/arm/mach-sa1100/victor.c b/arch/arm/mach-sa1100/victor.c --- a/arch/arm/mach-sa1100/victor.c Thu Mar 7 18:17:37 2002 +++ b/arch/arm/mach-sa1100/victor.c Thu Mar 7 18:17:37 2002 @@ -48,7 +48,7 @@ SET_BANK( 0, 0xc0000000, 4*1024*1024 ); mi->nr_banks = 1; - ROOT_DEV = MKDEV( 60, 2 ); + ROOT_DEV = mk_kdev( 60, 2 ); /* Get command line parameters passed from the loader (if any) */ if( *((char*)0xc0000000) ) @@ -60,7 +60,7 @@ static struct map_desc victor_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xe8000000, 0x00000000, 0x00200000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash */ + { 0xe8000000, 0x00000000, 0x00200000, DOMAIN_IO, 0, 1, 0, 0 }, /* Flash */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/xp860.c b/arch/arm/mach-sa1100/xp860.c --- a/arch/arm/mach-sa1100/xp860.c Thu Mar 7 18:17:44 2002 +++ b/arch/arm/mach-sa1100/xp860.c Thu Mar 7 18:17:44 2002 @@ -67,9 +67,9 @@ static struct map_desc xp860_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xf0000000, 0x10000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* SCSI */ - { 0xf1000000, 0x18000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* LAN */ - { 0xf4000000, 0x40000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* SA-1111 */ + { 0xf0000000, 0x10000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* SCSI */ + { 0xf1000000, 0x18000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* LAN */ + { 0xf4000000, 0x40000000, 0x00800000, DOMAIN_IO, 0, 1, 0, 0 }, /* SA-1111 */ LAST_DESC }; diff -Nru a/arch/arm/mach-sa1100/yopy.c b/arch/arm/mach-sa1100/yopy.c --- a/arch/arm/mach-sa1100/yopy.c Thu Mar 7 18:17:38 2002 +++ b/arch/arm/mach-sa1100/yopy.c Thu Mar 7 18:17:38 2002 @@ -1,8 +1,6 @@ /* * linux/arch/arm/mach-sa1100/yopy.c */ - -#include #include #include #include @@ -71,9 +69,9 @@ static struct map_desc yopy_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xe8000000, 0x00000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash 0 */ - { 0xec000000, 0x08000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash 1 */ - { 0xf0000000, 0x48000000, 0x00300000, DOMAIN_IO, 1, 1, 0, 0 }, /* LCD */ + { 0xe8000000, 0x00000000, 0x04000000, DOMAIN_IO, 0, 1, 0, 0 }, /* Flash 0 */ + { 0xec000000, 0x08000000, 0x04000000, DOMAIN_IO, 0, 1, 0, 0 }, /* Flash 1 */ + { 0xf0000000, 0x48000000, 0x00300000, DOMAIN_IO, 0, 1, 0, 0 }, /* LCD */ { 0xf1000000, 0x10000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* EGPIO */ LAST_DESC }; diff -Nru a/arch/arm/mach-shark/irq.c b/arch/arm/mach-shark/irq.c --- a/arch/arm/mach-shark/irq.c Thu Mar 7 18:17:45 2002 +++ b/arch/arm/mach-shark/irq.c Thu Mar 7 18:17:45 2002 @@ -7,6 +7,12 @@ * include/asm-arm/arch-ebsa110/irq.h * Copyright (C) 1996-1998 Russell King */ + +#include +#include +#include +#include + #include #include #include @@ -31,12 +37,12 @@ if (irq<8) { mask = 1 << irq; cached_irq_mask[0] |= mask; + outb(cached_irq_mask[1],0xA1); } else { mask = 1 << (irq-8); cached_irq_mask[1] |= mask; + outb(cached_irq_mask[0],0x21); } - outb(cached_irq_mask[1],0xA1); - outb(cached_irq_mask[0],0x21); } static void shark_enable_8259A_irq(unsigned int irq) @@ -45,31 +51,15 @@ if (irq<8) { mask = ~(1 << irq); cached_irq_mask[0] &= mask; + outb(cached_irq_mask[0],0x21); } else { mask = ~(1 << (irq-8)); cached_irq_mask[1] &= mask; + outb(cached_irq_mask[1],0xA1); } - outb(cached_irq_mask[1],0xA1); - outb(cached_irq_mask[0],0x21); } -/* - * Careful! The 8259A is a fragile beast, it pretty - * much _has_ to be done exactly like this (mask it - * first, _then_ send the EOI, and the order of EOI - * to the two 8259s is important! - */ -static void shark_mask_and_ack_8259A_irq(unsigned int irq) -{ - if (irq & 8) { - cached_irq_mask[1] |= 1 << (irq-8); - inb(0xA1); /* DUMMY */ - outb(cached_irq_mask[1],0xA1); - } else { - cached_irq_mask[0] |= 1 << irq; - outb(cached_irq_mask[0],0x21); - } -} +static void shark_ack_8259A_irq(unsigned int irq){} static void bogus_int(int irq, void *dev_id, struct pt_regs *regs) { @@ -78,32 +68,30 @@ static struct irqaction cascade; +static struct irqchip fb_chip = { + ack: shark_ack_8259A_irq, + mask: shark_disable_8259A_irq, + unmask: shark_enable_8259A_irq, +}; + void __init shark_init_irq(void) { int irq; for (irq = 0; irq < NR_IRQS; irq++) { - irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; - irq_desc[irq].mask_ack = shark_mask_and_ack_8259A_irq; - irq_desc[irq].mask = shark_disable_8259A_irq; - irq_desc[irq].unmask = shark_enable_8259A_irq; + set_irq_chip(irq, &fb_chip); + set_irq_handler(irq, do_edge_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } - /* The PICs are initialized to level triggered and auto eoi! - * If they are set to edge triggered they lose some IRQs, - * if they are set to manual eoi they get locked up after - * a short time - */ - /* init master interrupt controller */ - outb(0x19, 0x20); /* Start init sequence, level triggered */ + outb(0x11, 0x20); /* Start init sequence, edge triggered (level: 0x19)*/ outb(0x00, 0x21); /* Vector base */ outb(0x04, 0x21); /* Cascade (slave) on IRQ2 */ outb(0x03, 0x21); /* Select 8086 mode , auto eoi*/ outb(0x0A, 0x20); /* init slave interrupt controller */ - outb(0x19, 0xA0); /* Start init sequence, level triggered */ + outb(0x11, 0xA0); /* Start init sequence, edge triggered */ outb(0x08, 0xA1); /* Vector base */ outb(0x02, 0xA1); /* Cascade (slave) on IRQ2 */ outb(0x03, 0xA1); /* Select 8086 mode, auto eoi */ @@ -119,6 +107,6 @@ cascade.name = "cascade"; cascade.next = NULL; cascade.dev_id = NULL; - setup_arm_irq(2,&cascade); + setup_irq(2,&cascade); } diff -Nru a/arch/arm/mach-shark/pci.c b/arch/arm/mach-shark/pci.c --- a/arch/arm/mach-shark/pci.c Thu Mar 7 18:17:41 2002 +++ b/arch/arm/mach-shark/pci.c Thu Mar 7 18:17:41 2002 @@ -17,11 +17,14 @@ if (dev->bus->number == 0) if (dev->devfn == 0) return 255; else return 11; - else return 6; + else return 255; } struct hw_pci shark_pci __initdata = { - init: via82c505_init, - swizzle: no_swizzle, - map_irq: shark_map_irq + setup: via82c505_setup, + swizzle: pci_std_swizzle, + map_irq: shark_map_irq, + nr_controllers: 1, + scan: via82c505_scan_bus, + preinit: via82c505_preinit }; diff -Nru a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile --- a/arch/arm/mm/Makefile Thu Mar 7 18:17:45 2002 +++ b/arch/arm/mm/Makefile Thu Mar 7 18:17:45 2002 @@ -29,16 +29,16 @@ # Select the processor-specific files p-$(CONFIG_CPU_26) += proc-arm2,3.o -p-$(CONFIG_CPU_ARM610) += proc-arm6,7.o -p-$(CONFIG_CPU_ARM710) += proc-arm6,7.o -p-$(CONFIG_CPU_ARM720T) += proc-arm720.o armv4t-late-abort.o -p-$(CONFIG_CPU_ARM920T) += proc-arm920.o armv4t-early-abort.o -p-$(CONFIG_CPU_ARM922T) += proc-arm922.o armv4t-early-abort.o -p-$(CONFIG_CPU_ARM926T) += proc-arm926.o armv5ej-early-abort.o -p-$(CONFIG_CPU_ARM1020) += proc-arm1020.o armv4t-early-abort.o -p-$(CONFIG_CPU_SA110) += proc-sa110.o armv4-early-abort.o -p-$(CONFIG_CPU_SA1100) += proc-sa110.o armv4-early-abort.o minicache.o -p-$(CONFIG_CPU_XSCALE) += proc-xscale.o armv4t-early-abort.o minicache.o +p-$(CONFIG_CPU_ARM610) += proc-arm6,7.o tlb-v3.o copypage-v3.o +p-$(CONFIG_CPU_ARM710) += proc-arm6,7.o tlb-v3.o copypage-v3.o +p-$(CONFIG_CPU_ARM720T) += proc-arm720.o tlb-v4.o copypage-v4.o abort-lv4t.o +p-$(CONFIG_CPU_ARM920T) += proc-arm920.o tlb-v4wb.o copypage-v4.o abort-ev4t.o +p-$(CONFIG_CPU_ARM922T) += proc-arm922.o tlb-v4wb.o copypage-v4.o abort-ev4t.o +p-$(CONFIG_CPU_ARM926T) += proc-arm926.o tlb-v4wb.o copypage-v4.o abort-ev5ej.o +p-$(CONFIG_CPU_ARM1020) += proc-arm1020.o tlb-v4wb.o copypage-v4.o abort-ev4t.o +p-$(CONFIG_CPU_SA110) += proc-sa110.o tlb-v4wb.o copypage-v4.o copypage-v4mc.o abort-ev4.o minicache.o +p-$(CONFIG_CPU_SA1100) += proc-sa110.o tlb-v4wb.o copypage-v4.o copypage-v4mc.o abort-ev4.o minicache.o +p-$(CONFIG_CPU_XSCALE) += proc-xscale.o tlb-v4wb.o copypage-v5te.o abort-ev4t.o minicache.o obj-y += $(sort $(p-y)) diff -Nru a/arch/arm/mm/abort-ev4.S b/arch/arm/mm/abort-ev4.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mm/abort-ev4.S Thu Mar 7 18:17:40 2002 @@ -0,0 +1,29 @@ +#include +#include +/* + * Function: armv4_early_abort + * + * Params : r2 = address of aborted instruction + * : r3 = saved SPSR + * + * Returns : r0 = address of abort + * : r1 = FSR, bit 8 = write + * : r2-r8 = corrupted + * : r9 = preserved + * : sp = pointer to registers + * + * Purpose : obtain information about current aborted instruction. + * Note: we read user space. This means we might cause a data + * abort here if the I-TLB and D-TLB aren't seeing the same + * picture. Unfortunately, this does happen. We live with it. + */ + .align 5 +ENTRY(armv4_early_abort) + mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR + ldr r3, [r2] @ read aborted ARM instruction + tst r3, #1 << 20 @ L = 1 -> write? + orreq r1, r1, #1 << 8 @ yes. + mov pc, lr + + diff -Nru a/arch/arm/mm/abort-ev4t.S b/arch/arm/mm/abort-ev4t.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mm/abort-ev4t.S Thu Mar 7 18:17:41 2002 @@ -0,0 +1,31 @@ +#include +#include +/* + * Function: armv4t_early_abort + * + * Params : r2 = address of aborted instruction + * : r3 = saved SPSR + * + * Returns : r0 = address of abort + * : r1 = FSR, bit 8 = write + * : r2-r8 = corrupted + * : r9 = preserved + * : sp = pointer to registers + * + * Purpose : obtain information about current aborted instruction. + * Note: we read user space. This means we might cause a data + * abort here if the I-TLB and D-TLB aren't seeing the same + * picture. Unfortunately, this does happen. We live with it. + */ + .align 5 +ENTRY(armv4t_early_abort) + mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR + tst r3, #PSR_T_BIT + ldrneh r3, [r2] @ read aborted thumb instruction + ldreq r3, [r2] @ read aborted ARM instruction + bic r1, r1, #1 << 8 + movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 + tst r3, #1 << 20 @ check write + orreq r1, r1, #1 << 8 + mov pc, lr diff -Nru a/arch/arm/mm/abort-ev5ej.S b/arch/arm/mm/abort-ev5ej.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mm/abort-ev5ej.S Thu Mar 7 18:17:36 2002 @@ -0,0 +1,35 @@ +#include +#include +/* + * Function: armv5ej_early_abort + * + * Params : r2 = address of aborted instruction + * : r3 = saved SPSR + * + * Returns : r0 = address of abort + * : r1 = FSR, bit 8 = write + * : r2-r8 = corrupted + * : r9 = preserved + * : sp = pointer to registers + * + * Purpose : obtain information about current aborted instruction. + * Note: we read user space. This means we might cause a data + * abort here if the I-TLB and D-TLB aren't seeing the same + * picture. Unfortunately, this does happen. We live with it. + */ + .align 5 +ENTRY(armv5ej_early_abort) + mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR + tst r3, #PSR_J_BIT + orrne r1, r1, #1 << 8 @ always assume write + bne 1f + tst r3, #PSR_T_BIT + ldrneh r3, [r2] @ read aborted thumb instruction + ldreq r3, [r2] @ read aborted ARM instruction + movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 + tst r2, #1 << 20 @ L = 1 -> write + orreq r1, r1, #1 << 8 @ yes. +1: mov pc, lr + + diff -Nru a/arch/arm/mm/abort-lv4t.S b/arch/arm/mm/abort-lv4t.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mm/abort-lv4t.S Thu Mar 7 18:17:46 2002 @@ -0,0 +1,224 @@ +#include +#include +/* + * Function: armv4t_late_abort + * + * Params : r2 = address of aborted instruction + * : r3 = saved SPSR + * + * Returns : r0 = address of abort + * : r1 = FSR, bit 8 = writing + * : r2-r8 = corrupted + * : r9 = preserved + * : sp = pointer to registers + * + * Purpose : obtain information about current aborted instruction. + * Note: we read user space. This means we might cause a data + * abort here if the I-TLB and D-TLB aren't seeing the same + * picture. Unfortunately, this does happen. We live with it. + */ +ENTRY(armv4t_late_abort) + tst r3, #PSR_T_BIT @ check for thumb mode + mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR + ldreq r8, [r2] @ read arm instruction + bne .data_thumb_abort + tst r8, #1 << 20 @ L = 1 -> write? + orreq r1, r1, #1 << 8 @ yes. + and r7, r8, #15 << 24 + add pc, pc, r7, lsr #22 @ Now branch to the relevant processing routine + nop + +/* 0 */ b .data_arm_lateldrhpost @ ldrh rd, [rn], #m/rm +/* 1 */ b .data_arm_lateldrhpre @ ldrh rd, [rn, #m/rm] +/* 2 */ b .data_unknown +/* 3 */ b .data_unknown +/* 4 */ b .data_arm_lateldrpostconst @ ldr rd, [rn], #m +/* 5 */ b .data_arm_lateldrpreconst @ ldr rd, [rn, #m] +/* 6 */ b .data_arm_lateldrpostreg @ ldr rd, [rn], rm +/* 7 */ b .data_arm_lateldrprereg @ ldr rd, [rn, rm] +/* 8 */ b .data_arm_ldmstm @ ldm*a rn, +/* 9 */ b .data_arm_ldmstm @ ldm*b rn, +/* a */ b .data_unknown +/* b */ b .data_unknown +/* c */ mov pc, lr @ ldc rd, [rn], #m @ Same as ldr rd, [rn], #m +/* d */ mov pc, lr @ ldc rd, [rn, #m] +/* e */ b .data_unknown +/* f */ +.data_unknown: @ Part of jumptable + mov r0, r2 + mov r1, r8 + mov r2, sp + bl baddataabort + b ret_from_exception + +.data_arm_ldmstm: + tst r8, #1 << 21 @ check writeback bit + moveq pc, lr @ no writeback -> no fixup + mov r7, #0x11 + orr r7, r7, #0x1100 + and r6, r8, r7 + and r2, r8, r7, lsl #1 + add r6, r6, r2, lsr #1 + and r2, r8, r7, lsl #2 + add r6, r6, r2, lsr #2 + and r2, r8, r7, lsl #3 + add r6, r6, r2, lsr #3 + add r6, r6, r6, lsr #8 + add r6, r6, r6, lsr #4 + and r6, r6, #15 @ r7 = no. of registers to transfer. + and r5, r8, #15 << 16 @ Extract 'n' form instruction + ldr r7, [sp, r5, lsr #14] @ Get register 'Rn' + tst r8, #1 << 23 @ Check U bit + subne r7, r7, r6, lsl #2 @ Undo increment + addeq r7, r7, r6, lsl #2 @ Undo decrement + str r7, [sp, r5, lsr #14] @ Put register 'Rn' + mov pc, lr + +.data_arm_lateldrhpre: + tst r8, #1 << 21 @ Check writeback bit + moveq pc, lr @ No writeback -> no fixup +.data_arm_lateldrhpost: + and r5, r8, #0x00f @ get Rm / low nibble of immediate value + tst r8, #1 << 22 @ if (immediate offset) + andne r6, r8, #0xf00 @ { immediate high nibble + orrne r6, r5, r6, lsr #4 @ combine nibbles } else + ldreq r6, [sp, r5, lsl #2] @ { load Rm value } +.data_arm_apply_r6_and_rn: + and r5, r8, #15 << 16 @ Extract 'n' from instruction + ldr r7, [sp, r5, lsr #14] @ Get register 'Rn' + tst r8, #1 << 23 @ Check U bit + subne r7, r7, r6 @ Undo incrmenet + addeq r7, r7, r6 @ Undo decrement + str r7, [sp, r5, lsr #14] @ Put register 'Rn' + mov pc, lr + +.data_arm_lateldrpreconst: + tst r8, #1 << 21 @ check writeback bit + moveq pc, lr @ no writeback -> no fixup +.data_arm_lateldrpostconst: + movs r2, r8, lsl #20 @ Get offset + moveq pc, lr @ zero -> no fixup + and r5, r8, #15 << 16 @ Extract 'n' from instruction + ldr r7, [sp, r5, lsr #14] @ Get register 'Rn' + tst r8, #1 << 23 @ Check U bit + subne r7, r7, r2, lsr #20 @ Undo increment + addeq r7, r7, r2, lsr #20 @ Undo decrement + str r7, [sp, r5, lsr #14] @ Put register 'Rn' + mov pc, lr + +.data_arm_lateldrprereg: + tst r8, #1 << 21 @ check writeback bit + moveq pc, lr @ no writeback -> no fixup +.data_arm_lateldrpostreg: + and r7, r8, #15 @ Extract 'm' from instruction + ldr r6, [sp, r7, lsl #2] @ Get register 'Rm' + mov r5, r8, lsr #7 @ get shift count + ands r5, r5, #31 + and r7, r8, #0x70 @ get shift type + orreq r7, r7, #8 @ shift count = 0 + add pc, pc, r7 + nop + + mov r6, r6, lsl r5 @ 0: LSL #!0 + b .data_arm_apply_r6_and_rn + b .data_arm_apply_r6_and_rn @ 1: LSL #0 + nop + b .data_unknown @ 2: MUL? + nop + b .data_unknown @ 3: MUL? + nop + mov r6, r6, lsr r5 @ 4: LSR #!0 + b .data_arm_apply_r6_and_rn + mov r6, r6, lsr #32 @ 5: LSR #32 + b .data_arm_apply_r6_and_rn + b .data_unknown @ 6: MUL? + nop + b .data_unknown @ 7: MUL? + nop + mov r6, r6, asr r5 @ 8: ASR #!0 + b .data_arm_apply_r6_and_rn + mov r6, r6, asr #32 @ 9: ASR #32 + b .data_arm_apply_r6_and_rn + b .data_unknown @ A: MUL? + nop + b .data_unknown @ B: MUL? + nop + mov r6, r6, ror r5 @ C: ROR #!0 + b .data_arm_apply_r6_and_rn + mov r6, r6, rrx @ D: RRX + b .data_arm_apply_r6_and_rn + b .data_unknown @ E: MUL? + nop + b .data_unknown @ F: MUL? + +.data_thumb_abort: + ldrh r8, [r2] @ read instruction + tst r8, #1 << 11 @ L = 1 -> write? + orreq r1, r1, #1 << 8 @ yes + and r7, r8, #15 << 12 + add pc, pc, r7, lsr #10 @ lookup in table + nop + +/* 0 */ b .data_unknown +/* 1 */ b .data_unknown +/* 2 */ b .data_unknown +/* 3 */ b .data_unknown +/* 4 */ b .data_unknown +/* 5 */ b .data_thumb_reg +/* 6 */ mov pc, lr +/* 7 */ mov pc, lr +/* 8 */ mov pc, lr +/* 9 */ mov pc, lr +/* A */ b .data_unknown +/* B */ b .data_thumb_pushpop +/* C */ b .data_thumb_ldmstm +/* D */ b .data_unknown +/* E */ b .data_unknown +/* F */ b .data_unknown + +.data_thumb_reg: + tst r8, #1 << 9 + moveq pc, lr + tst r8, #1 << 10 @ If 'S' (signed) bit is set + movne r1, #0 @ it must be a load instr + mov pc, lr + +.data_thumb_pushpop: + tst r8, #1 << 10 + beq .data_unknown + mov r7, #0x11 + and r6, r8, r7 + and r2, r8, r7, lsl #1 + add r6, r6, r2, lsr #1 + and r2, r8, r7, lsl #2 + add r6, r6, r2, lsr #2 + and r2, r8, r7, lsl #3 + add r6, r6, r2, lsr #3 + add r6, r6, r6, lsr #4 + and r2, r8, #0x0100 @ catch 'R' bit for push/pop + add r6, r6, r2, lsr #8 + and r6, r6, #15 @ number of regs to transfer + ldr r7, [sp, #13 << 2] + tst r8, #1 << 11 + addne r7, r7, r6, lsl #2 @ increment SP if PUSH + subeq r7, r7, r6, lsr #2 @ decrement SP if POP + str r7, [sp, #13 << 2] + mov pc, lr + +.data_thumb_ldmstm: + mov r7, #0x11 + and r6, r8, r7 + and r2, r8, r7, lsl #1 + add r6, r6, r2, lsr #1 + and r2, r8, r7, lsl #2 + add r6, r6, r2, lsr #2 + and r2, r8, r7, lsl #3 + add r6, r6, r2, lsr #3 + add r6, r6, r6, lsr #4 + and r6, r6, #15 @ number of regs to transfer + and r5, r8, #7 << 8 + ldr r7, [sp, r5, lsr #6] + sub r7, r7, r6, lsr #2 @ always decrement + str r7, [sp, r5, lsr #6] + mov pc, lr diff -Nru a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c --- a/arch/arm/mm/alignment.c Thu Mar 7 18:17:39 2002 +++ b/arch/arm/mm/alignment.c Thu Mar 7 18:17:39 2002 @@ -30,6 +30,10 @@ #include #include +extern void +do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr, + int error_code, struct pt_regs *regs); + /* * 32-bit misaligned trap handler (c) 1998 San Mehat (CCC) -July 1998 * /proc/sys/debug/alignment, modified and integrated into @@ -163,9 +167,9 @@ #define TYPE_LDST 2 #define TYPE_DONE 3 -#define get8_unaligned_check(val,addr,err) \ +#define __get8_unaligned_check(ins,val,addr,err) \ __asm__( \ - "1: ldrb %1, [%2], #1\n" \ + "1: "ins" %1, [%2], #1\n" \ "2:\n" \ " .section .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -179,39 +183,49 @@ : "=r" (err), "=&r" (val), "=r" (addr) \ : "0" (err), "2" (addr)) -#define get8t_unaligned_check(val,addr,err) \ - __asm__( \ - "1: ldrbt %1, [%2], #1\n" \ - "2:\n" \ - " .section .fixup,\"ax\"\n" \ - " .align 2\n" \ - "3: mov %0, #1\n" \ - " b 2b\n" \ - " .previous\n" \ - " .section __ex_table,\"a\"\n" \ - " .align 3\n" \ - " .long 1b, 3b\n" \ - " .previous\n" \ - : "=r" (err), "=&r" (val), "=r" (addr) \ - : "0" (err), "2" (addr)) +#define __get16_unaligned_check(ins,val,addr) \ + do { \ + unsigned int err = 0, v, a = addr; \ + __get8_unaligned_check(ins,val,a,err); \ + __get8_unaligned_check(ins,v,a,err); \ + val |= v << 8; \ + if (err) \ + goto fault; \ + } while (0) + +#define get16_unaligned_check(val,addr) \ + __get16_unaligned_check("ldrb",val,addr) + +#define get16t_unaligned_check(val,addr) \ + __get16_unaligned_check("ldrbt",val,addr) -#define get16_unaligned_check(val,addr) \ +#define __get32_unaligned_check(ins,val,addr) \ do { \ unsigned int err = 0, v, a = addr; \ - get8_unaligned_check(val,a,err); \ - get8_unaligned_check(v,a,err); \ + __get8_unaligned_check(ins,val,a,err); \ + __get8_unaligned_check(ins,v,a,err); \ val |= v << 8; \ + __get8_unaligned_check(ins,v,a,err); \ + val |= v << 16; \ + __get8_unaligned_check(ins,v,a,err); \ + val |= v << 24; \ if (err) \ goto fault; \ } while (0) -#define put16_unaligned_check(val,addr) \ +#define get32_unaligned_check(val,addr) \ + __get32_unaligned_check("ldrb",val,addr) + +#define get32t_unaligned_check(val,addr) \ + __get32_unaligned_check("ldrbt",val,addr) + +#define __put16_unaligned_check(ins,val,addr) \ do { \ unsigned int err = 0, v = val, a = addr; \ __asm__( \ - "1: strb %1, [%2], #1\n" \ + "1: "ins" %1, [%2], #1\n" \ " mov %1, %1, lsr #8\n" \ - "2: strb %1, [%2]\n" \ + "2: "ins" %1, [%2]\n" \ "3:\n" \ " .section .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -229,6 +243,12 @@ goto fault; \ } while (0) +#define put16_unaligned_check(val,addr) \ + __put16_unaligned_check("strb",val,addr) + +#define put16t_unaligned_check(val,addr) \ + __put16_unaligned_check("strbt",val,addr) + #define __put32_unaligned_check(ins,val,addr) \ do { \ unsigned int err = 0, v = val, a = addr; \ @@ -259,37 +279,9 @@ goto fault; \ } while (0) -#define get32_unaligned_check(val,addr) \ - do { \ - unsigned int err = 0, v, a = addr; \ - get8_unaligned_check(val,a,err); \ - get8_unaligned_check(v,a,err); \ - val |= v << 8; \ - get8_unaligned_check(v,a,err); \ - val |= v << 16; \ - get8_unaligned_check(v,a,err); \ - val |= v << 24; \ - if (err) \ - goto fault; \ - } while (0) - #define put32_unaligned_check(val,addr) \ __put32_unaligned_check("strb", val, addr) -#define get32t_unaligned_check(val,addr) \ - do { \ - unsigned int err = 0, v, a = addr; \ - get8t_unaligned_check(val,a,err); \ - get8t_unaligned_check(v,a,err); \ - val |= v << 8; \ - get8t_unaligned_check(v,a,err); \ - val |= v << 16; \ - get8t_unaligned_check(v,a,err); \ - val |= v << 24; \ - if (err) \ - goto fault; \ - } while (0) - #define put32t_unaligned_check(val,addr) \ __put32_unaligned_check("strbt", val, addr) @@ -319,6 +311,9 @@ ai_half += 1; + if (user_mode(regs)) + goto user; + if (LDST_L_BIT(instr)) { unsigned long val; get16_unaligned_check(val, addr); @@ -333,12 +328,27 @@ return TYPE_LDST; -swp: + user: + if (LDST_L_BIT(instr)) { + unsigned long val; + get16t_unaligned_check(val, addr); + + /* signed half-word? */ + if (instr & 0x40) + val = (signed long)((signed short) val); + + regs->uregs[rd] = val; + } else + put16t_unaligned_check(regs->uregs[rd], addr); + + return TYPE_LDST; + + swp: printk(KERN_ERR "Alignment trap: not handling swp instruction\n"); -bad: + bad: return TYPE_ERROR; -fault: + fault: return TYPE_FAULT; } @@ -349,23 +359,27 @@ ai_word += 1; - if (!LDST_P_BIT(instr) && LDST_W_BIT(instr)) + if ((!LDST_P_BIT(instr) && LDST_W_BIT(instr)) || user_mode(regs)) goto trans; - if (LDST_L_BIT(instr)) - get32_unaligned_check(regs->uregs[rd], addr); - else + if (LDST_L_BIT(instr)) { + unsigned int val; + get32_unaligned_check(val, addr); + regs->uregs[rd] = val; + } else put32_unaligned_check(regs->uregs[rd], addr); return TYPE_LDST; -trans: - if (LDST_L_BIT(instr)) - get32t_unaligned_check(regs->uregs[rd], addr); - else + trans: + if (LDST_L_BIT(instr)) { + unsigned int val; + get32t_unaligned_check(val, addr); + regs->uregs[rd] = val; + } else put32t_unaligned_check(regs->uregs[rd], addr); return TYPE_LDST; -fault: + fault: return TYPE_FAULT; } @@ -431,14 +445,31 @@ } #endif - for (regbits = REGMASK_BITS(instr), rd = 0; regbits; regbits >>= 1, rd += 1) - if (regbits & 1) { - if (LDST_L_BIT(instr)) - get32_unaligned_check(regs->uregs[rd], eaddr); - else - put32_unaligned_check(regs->uregs[rd], eaddr); - eaddr += 4; - } + if (user_mode(regs)) { + for (regbits = REGMASK_BITS(instr), rd = 0; regbits; + regbits >>= 1, rd += 1) + if (regbits & 1) { + if (LDST_L_BIT(instr)) { + unsigned int val; + get32t_unaligned_check(val, eaddr); + regs->uregs[rd] = val; + } else + put32t_unaligned_check(regs->uregs[rd], eaddr); + eaddr += 4; + } + } else { + for (regbits = REGMASK_BITS(instr), rd = 0; regbits; + regbits >>= 1, rd += 1) + if (regbits & 1) { + if (LDST_L_BIT(instr)) { + unsigned int val; + get32_unaligned_check(val, eaddr); + regs->uregs[rd] = val; + } else + put32_unaligned_check(regs->uregs[rd], eaddr); + eaddr += 4; + } + } if (LDST_W_BIT(instr)) regs->uregs[rn] = newaddr; @@ -539,7 +570,7 @@ return 0; -bad_or_fault: + bad_or_fault: if (type == TYPE_ERROR) goto bad; regs->ARM_pc -= 4; @@ -549,7 +580,7 @@ do_bad_area(current, current->mm, addr, error_code, regs); return 0; -bad: + bad: /* * Oops, we didn't handle the instruction. */ diff -Nru a/arch/arm/mm/armv4-early-abort.S b/arch/arm/mm/armv4-early-abort.S --- a/arch/arm/mm/armv4-early-abort.S Thu Mar 7 18:17:40 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,29 +0,0 @@ -#include -#include -/* - * Function: armv4_early_abort - * - * Params : r2 = address of aborted instruction - * : r3 = saved SPSR - * - * Returns : r0 = address of abort - * : r1 = FSR, bit 8 = write - * : r2-r8 = corrupted - * : r9 = preserved - * : sp = pointer to registers - * - * Purpose : obtain information about current aborted instruction. - * Note: we read user space. This means we might cause a data - * abort here if the I-TLB and D-TLB aren't seeing the same - * picture. Unfortunately, this does happen. We live with it. - */ - .align 5 -ENTRY(armv4_early_abort) - mrc p15, 0, r1, c5, c0, 0 @ get FSR - mrc p15, 0, r0, c6, c0, 0 @ get FAR - ldr r3, [r2] @ read aborted ARM instruction - tst r3, #1 << 20 @ L = 1 -> write? - orreq r1, r1, #1 << 8 @ yes. - mov pc, lr - - diff -Nru a/arch/arm/mm/armv4t-early-abort.S b/arch/arm/mm/armv4t-early-abort.S --- a/arch/arm/mm/armv4t-early-abort.S Thu Mar 7 18:17:41 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,31 +0,0 @@ -#include -#include -/* - * Function: armv4t_early_abort - * - * Params : r2 = address of aborted instruction - * : r3 = saved SPSR - * - * Returns : r0 = address of abort - * : r1 = FSR, bit 8 = write - * : r2-r8 = corrupted - * : r9 = preserved - * : sp = pointer to registers - * - * Purpose : obtain information about current aborted instruction. - * Note: we read user space. This means we might cause a data - * abort here if the I-TLB and D-TLB aren't seeing the same - * picture. Unfortunately, this does happen. We live with it. - */ - .align 5 -ENTRY(armv4t_early_abort) - mrc p15, 0, r1, c5, c0, 0 @ get FSR - mrc p15, 0, r0, c6, c0, 0 @ get FAR - tst r3, #PSR_T_BIT - ldrneh r3, [r2] @ read aborted thumb instruction - ldreq r3, [r2] @ read aborted ARM instruction - bic r1, r1, #1 << 8 - movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 - tst r3, #1 << 20 @ check write - orreq r1, r1, #1 << 8 - mov pc, lr diff -Nru a/arch/arm/mm/armv4t-late-abort.S b/arch/arm/mm/armv4t-late-abort.S --- a/arch/arm/mm/armv4t-late-abort.S Thu Mar 7 18:17:46 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,224 +0,0 @@ -#include -#include -/* - * Function: armv4t_late_abort - * - * Params : r2 = address of aborted instruction - * : r3 = saved SPSR - * - * Returns : r0 = address of abort - * : r1 = FSR, bit 8 = writing - * : r2-r8 = corrupted - * : r9 = preserved - * : sp = pointer to registers - * - * Purpose : obtain information about current aborted instruction. - * Note: we read user space. This means we might cause a data - * abort here if the I-TLB and D-TLB aren't seeing the same - * picture. Unfortunately, this does happen. We live with it. - */ -ENTRY(armv4t_late_abort) - tst r3, #PSR_T_BIT @ check for thumb mode - mrc p15, 0, r1, c5, c0, 0 @ get FSR - mrc p15, 0, r0, c6, c0, 0 @ get FAR - ldreq r8, [r2] @ read arm instruction - bne .data_thumb_abort - tst r8, #1 << 20 @ L = 1 -> write? - orreq r1, r1, #1 << 8 @ yes. - and r7, r8, #15 << 24 - add pc, pc, r7, lsr #22 @ Now branch to the relevant processing routine - nop - -/* 0 */ b .data_arm_lateldrhpost @ ldrh rd, [rn], #m/rm -/* 1 */ b .data_arm_lateldrhpre @ ldrh rd, [rn, #m/rm] -/* 2 */ b .data_unknown -/* 3 */ b .data_unknown -/* 4 */ b .data_arm_lateldrpostconst @ ldr rd, [rn], #m -/* 5 */ b .data_arm_lateldrpreconst @ ldr rd, [rn, #m] -/* 6 */ b .data_arm_lateldrpostreg @ ldr rd, [rn], rm -/* 7 */ b .data_arm_lateldrprereg @ ldr rd, [rn, rm] -/* 8 */ b .data_arm_ldmstm @ ldm*a rn, -/* 9 */ b .data_arm_ldmstm @ ldm*b rn, -/* a */ b .data_unknown -/* b */ b .data_unknown -/* c */ mov pc, lr @ ldc rd, [rn], #m @ Same as ldr rd, [rn], #m -/* d */ mov pc, lr @ ldc rd, [rn, #m] -/* e */ b .data_unknown -/* f */ -.data_unknown: @ Part of jumptable - mov r0, r2 - mov r1, r8 - mov r2, sp - bl baddataabort - b ret_from_exception - -.data_arm_ldmstm: - tst r8, #1 << 21 @ check writeback bit - moveq pc, lr @ no writeback -> no fixup - mov r7, #0x11 - orr r7, r7, #0x1100 - and r6, r8, r7 - and r2, r8, r7, lsl #1 - add r6, r6, r2, lsr #1 - and r2, r8, r7, lsl #2 - add r6, r6, r2, lsr #2 - and r2, r8, r7, lsl #3 - add r6, r6, r2, lsr #3 - add r6, r6, r6, lsr #8 - add r6, r6, r6, lsr #4 - and r6, r6, #15 @ r7 = no. of registers to transfer. - and r5, r8, #15 << 16 @ Extract 'n' form instruction - ldr r7, [sp, r5, lsr #14] @ Get register 'Rn' - tst r8, #1 << 23 @ Check U bit - subne r7, r7, r6, lsl #2 @ Undo increment - addeq r7, r7, r6, lsl #2 @ Undo decrement - str r7, [sp, r5, lsr #14] @ Put register 'Rn' - mov pc, lr - -.data_arm_lateldrhpre: - tst r8, #1 << 21 @ Check writeback bit - moveq pc, lr @ No writeback -> no fixup -.data_arm_lateldrhpost: - and r5, r8, #0x00f @ get Rm / low nibble of immediate value - tst r8, #1 << 22 @ if (immediate offset) - andne r6, r8, #0xf00 @ { immediate high nibble - orrne r6, r5, r6, lsr #4 @ combine nibbles } else - ldreq r6, [sp, r5, lsl #2] @ { load Rm value } -.data_arm_apply_r6_and_rn: - and r5, r8, #15 << 16 @ Extract 'n' from instruction - ldr r7, [sp, r5, lsr #14] @ Get register 'Rn' - tst r8, #1 << 23 @ Check U bit - subne r7, r7, r6 @ Undo incrmenet - addeq r7, r7, r6 @ Undo decrement - str r7, [sp, r5, lsr #14] @ Put register 'Rn' - mov pc, lr - -.data_arm_lateldrpreconst: - tst r8, #1 << 21 @ check writeback bit - moveq pc, lr @ no writeback -> no fixup -.data_arm_lateldrpostconst: - movs r2, r8, lsl #20 @ Get offset - moveq pc, lr @ zero -> no fixup - and r5, r8, #15 << 16 @ Extract 'n' from instruction - ldr r7, [sp, r5, lsr #14] @ Get register 'Rn' - tst r8, #1 << 23 @ Check U bit - subne r7, r7, r2, lsr #20 @ Undo increment - addeq r7, r7, r2, lsr #20 @ Undo decrement - str r7, [sp, r5, lsr #14] @ Put register 'Rn' - mov pc, lr - -.data_arm_lateldrprereg: - tst r8, #1 << 21 @ check writeback bit - moveq pc, lr @ no writeback -> no fixup -.data_arm_lateldrpostreg: - and r7, r8, #15 @ Extract 'm' from instruction - ldr r6, [sp, r7, lsl #2] @ Get register 'Rm' - mov r5, r8, lsr #7 @ get shift count - ands r5, r5, #31 - and r7, r8, #0x70 @ get shift type - orreq r7, r7, #8 @ shift count = 0 - add pc, pc, r7 - nop - - mov r6, r6, lsl r5 @ 0: LSL #!0 - b .data_arm_apply_r6_and_rn - b .data_arm_apply_r6_and_rn @ 1: LSL #0 - nop - b .data_unknown @ 2: MUL? - nop - b .data_unknown @ 3: MUL? - nop - mov r6, r6, lsr r5 @ 4: LSR #!0 - b .data_arm_apply_r6_and_rn - mov r6, r6, lsr #32 @ 5: LSR #32 - b .data_arm_apply_r6_and_rn - b .data_unknown @ 6: MUL? - nop - b .data_unknown @ 7: MUL? - nop - mov r6, r6, asr r5 @ 8: ASR #!0 - b .data_arm_apply_r6_and_rn - mov r6, r6, asr #32 @ 9: ASR #32 - b .data_arm_apply_r6_and_rn - b .data_unknown @ A: MUL? - nop - b .data_unknown @ B: MUL? - nop - mov r6, r6, ror r5 @ C: ROR #!0 - b .data_arm_apply_r6_and_rn - mov r6, r6, rrx @ D: RRX - b .data_arm_apply_r6_and_rn - b .data_unknown @ E: MUL? - nop - b .data_unknown @ F: MUL? - -.data_thumb_abort: - ldrh r8, [r2] @ read instruction - tst r8, #1 << 11 @ L = 1 -> write? - orreq r1, r1, #1 << 8 @ yes - and r7, r8, #15 << 12 - add pc, pc, r7, lsr #10 @ lookup in table - nop - -/* 0 */ b .data_unknown -/* 1 */ b .data_unknown -/* 2 */ b .data_unknown -/* 3 */ b .data_unknown -/* 4 */ b .data_unknown -/* 5 */ b .data_thumb_reg -/* 6 */ mov pc, lr -/* 7 */ mov pc, lr -/* 8 */ mov pc, lr -/* 9 */ mov pc, lr -/* A */ b .data_unknown -/* B */ b .data_thumb_pushpop -/* C */ b .data_thumb_ldmstm -/* D */ b .data_unknown -/* E */ b .data_unknown -/* F */ b .data_unknown - -.data_thumb_reg: - tst r8, #1 << 9 - moveq pc, lr - tst r8, #1 << 10 @ If 'S' (signed) bit is set - movne r1, #0 @ it must be a load instr - mov pc, lr - -.data_thumb_pushpop: - tst r8, #1 << 10 - beq .data_unknown - mov r7, #0x11 - and r6, r8, r7 - and r2, r8, r7, lsl #1 - add r6, r6, r2, lsr #1 - and r2, r8, r7, lsl #2 - add r6, r6, r2, lsr #2 - and r2, r8, r7, lsl #3 - add r6, r6, r2, lsr #3 - add r6, r6, r6, lsr #4 - and r2, r8, #0x0100 @ catch 'R' bit for push/pop - add r6, r6, r2, lsr #8 - and r6, r6, #15 @ number of regs to transfer - ldr r7, [sp, #13 << 2] - tst r8, #1 << 11 - addne r7, r7, r6, lsl #2 @ increment SP if PUSH - subeq r7, r7, r6, lsr #2 @ decrement SP if POP - str r7, [sp, #13 << 2] - mov pc, lr - -.data_thumb_ldmstm: - mov r7, #0x11 - and r6, r8, r7 - and r2, r8, r7, lsl #1 - add r6, r6, r2, lsr #1 - and r2, r8, r7, lsl #2 - add r6, r6, r2, lsr #2 - and r2, r8, r7, lsl #3 - add r6, r6, r2, lsr #3 - add r6, r6, r6, lsr #4 - and r6, r6, #15 @ number of regs to transfer - and r5, r8, #7 << 8 - ldr r7, [sp, r5, lsr #6] - sub r7, r7, r6, lsr #2 @ always decrement - str r7, [sp, r5, lsr #6] - mov pc, lr diff -Nru a/arch/arm/mm/armv5ej-early-abort.S b/arch/arm/mm/armv5ej-early-abort.S --- a/arch/arm/mm/armv5ej-early-abort.S Thu Mar 7 18:17:36 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,35 +0,0 @@ -#include -#include -/* - * Function: armv5ej_early_abort - * - * Params : r2 = address of aborted instruction - * : r3 = saved SPSR - * - * Returns : r0 = address of abort - * : r1 = FSR, bit 8 = write - * : r2-r8 = corrupted - * : r9 = preserved - * : sp = pointer to registers - * - * Purpose : obtain information about current aborted instruction. - * Note: we read user space. This means we might cause a data - * abort here if the I-TLB and D-TLB aren't seeing the same - * picture. Unfortunately, this does happen. We live with it. - */ - .align 5 -ENTRY(armv5ej_early_abort) - mrc p15, 0, r1, c5, c0, 0 @ get FSR - mrc p15, 0, r0, c6, c0, 0 @ get FAR - tst r3, #PSR_J_BIT - orrne r1, r1, #1 << 8 @ always assume write - bne 1f - tst r3, #PSR_T_BIT - ldrneh r3, [r2] @ read aborted thumb instruction - ldreq r3, [r2] @ read aborted ARM instruction - movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 - tst r2, #1 << 20 @ L = 1 -> write - orreq r1, r1, #1 << 8 @ yes. -1: mov pc, lr - - diff -Nru a/arch/arm/mm/copypage-v3.S b/arch/arm/mm/copypage-v3.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mm/copypage-v3.S Thu Mar 7 18:17:37 2002 @@ -0,0 +1,59 @@ +/* + * linux/arch/arm/lib/copypage.S + * + * Copyright (C) 1995-1999 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ASM optimised string functions + */ +#include +#include +#include + + .text + .align 5 +/* + * ARMv3 optimised copy_user_page + * + * FIXME: do we need to handle cache stuff... + */ +ENTRY(armv3_copy_user_page) + stmfd sp!, {r4, lr} @ 2 + mov r2, #PAGE_SZ/64 @ 1 + ldmia r1!, {r3, r4, ip, lr} @ 4+1 +1: stmia r0!, {r3, r4, ip, lr} @ 4 + ldmia r1!, {r3, r4, ip, lr} @ 4+1 + stmia r0!, {r3, r4, ip, lr} @ 4 + ldmia r1!, {r3, r4, ip, lr} @ 4+1 + stmia r0!, {r3, r4, ip, lr} @ 4 + ldmia r1!, {r3, r4, ip, lr} @ 4 + subs r2, r2, #1 @ 1 + stmia r0!, {r3, r4, ip, lr} @ 4 + ldmneia r1!, {r3, r4, ip, lr} @ 4 + bne 1b @ 1 + LOADREGS(fd, sp!, {r4, pc}) @ 3 + + .align 5 +/* + * ARMv3 optimised clear_user_page + * + * FIXME: do we need to handle cache stuff... + */ +ENTRY(armv3_clear_user_page) + str lr, [sp, #-4]! + mov r1, #PAGE_SZ/64 @ 1 + mov r2, #0 @ 1 + mov r3, #0 @ 1 + mov ip, #0 @ 1 + mov lr, #0 @ 1 +1: stmia r0!, {r2, r3, ip, lr} @ 4 + stmia r0!, {r2, r3, ip, lr} @ 4 + stmia r0!, {r2, r3, ip, lr} @ 4 + stmia r0!, {r2, r3, ip, lr} @ 4 + subs r1, r1, #1 @ 1 + bne 1b @ 1 + ldr pc, [sp], #4 + diff -Nru a/arch/arm/mm/copypage-v4.S b/arch/arm/mm/copypage-v4.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mm/copypage-v4.S Thu Mar 7 18:17:38 2002 @@ -0,0 +1,70 @@ +/* + * linux/arch/arm/lib/copypage.S + * + * Copyright (C) 1995-1999 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ASM optimised string functions + */ +#include +#include + + .text + .align 5 +/* + * ARMv4 optimised copy_user_page + * + * We flush the destination cache lines just before we write the data into the + * corresponding address. Since the Dcache is read-allocate, this removes the + * Dcache aliasing issue. The writes will be forwarded to the write buffer, + * and merged as appropriate. + * + * Note: We rely on all ARMv4 processors implementing the "invalidate D line" + * instruction. If your processor does not supply this, you have to write your + * own copy_user_page that does the right thing. + */ +ENTRY(armv4_copy_user_page) + stmfd sp!, {r4, lr} @ 2 + mov r2, #PAGE_SZ/64 @ 1 + ldmia r1!, {r3, r4, ip, lr} @ 4 +1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line + stmia r0!, {r3, r4, ip, lr} @ 4 + ldmia r1!, {r3, r4, ip, lr} @ 4+1 + stmia r0!, {r3, r4, ip, lr} @ 4 + ldmia r1!, {r3, r4, ip, lr} @ 4 + mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line + stmia r0!, {r3, r4, ip, lr} @ 4 + ldmia r1!, {r3, r4, ip, lr} @ 4 + subs r2, r2, #1 @ 1 + stmia r0!, {r3, r4, ip, lr} @ 4 + ldmneia r1!, {r3, r4, ip, lr} @ 4 + bne 1b @ 1 + mcr p15, 0, r1, c7, c10, 4 @ 1 drain WB + ldmfd sp!, {r4, pc} @ 3 + + .align 5 +/* + * ARMv4 optimised clear_user_page + * + * Same story as above. + */ +ENTRY(armv4_clear_user_page) + str lr, [sp, #-4]! + mov r1, #PAGE_SZ/64 @ 1 + mov r2, #0 @ 1 + mov r3, #0 @ 1 + mov ip, #0 @ 1 + mov lr, #0 @ 1 +1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line + stmia r0!, {r2, r3, ip, lr} @ 4 + stmia r0!, {r2, r3, ip, lr} @ 4 + mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line + stmia r0!, {r2, r3, ip, lr} @ 4 + stmia r0!, {r2, r3, ip, lr} @ 4 + subs r1, r1, #1 @ 1 + bne 1b @ 1 + mcr p15, 0, r1, c7, c10, 4 @ 1 drain WB + ldr pc, [sp], #4 diff -Nru a/arch/arm/mm/copypage-v4mc.S b/arch/arm/mm/copypage-v4mc.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mm/copypage-v4mc.S Thu Mar 7 18:17:44 2002 @@ -0,0 +1,71 @@ +/* + * linux/arch/arm/lib/copy_page-armv4mc.S + * + * Copyright (C) 1995-2001 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ASM optimised string functions + */ +#include +#include + + .text + .align 5 +/* + * ARMv4 mini-dcache optimised copy_user_page + * + * We flush the destination cache lines just before we write the data into the + * corresponding address. Since the Dcache is read-allocate, this removes the + * Dcache aliasing issue. The writes will be forwarded to the write buffer, + * and merged as appropriate. + * + * Note: We rely on all ARMv4 processors implementing the "invalidate D line" + * instruction. If your processor does not supply this, you have to write your + * own copy_user_page that does the right thing. + */ +ENTRY(armv4_mc_copy_user_page) + stmfd sp!, {r4, lr} @ 2 + mov r4, r0 + mov r0, r1 + bl map_page_minicache + mov r1, #PAGE_SZ/64 @ 1 + ldmia r0!, {r2, r3, ip, lr} @ 4 +1: mcr p15, 0, r4, c7, c6, 1 @ 1 invalidate D line + stmia r4!, {r2, r3, ip, lr} @ 4 + ldmia r0!, {r2, r3, ip, lr} @ 4+1 + stmia r4!, {r2, r3, ip, lr} @ 4 + ldmia r0!, {r2, r3, ip, lr} @ 4 + mcr p15, 0, r4, c7, c6, 1 @ 1 invalidate D line + stmia r4!, {r2, r3, ip, lr} @ 4 + ldmia r0!, {r2, r3, ip, lr} @ 4 + subs r1, r1, #1 @ 1 + stmia r4!, {r2, r3, ip, lr} @ 4 + ldmneia r0!, {r2, r3, ip, lr} @ 4 + bne 1b @ 1 + ldmfd sp!, {r4, pc} @ 3 + + .align 5 +/* + * ARMv4 optimised clear_user_page + * + * Same story as above. + */ +ENTRY(armv4_mc_clear_user_page) + str lr, [sp, #-4]! + mov r1, #PAGE_SZ/64 @ 1 + mov r2, #0 @ 1 + mov r3, #0 @ 1 + mov ip, #0 @ 1 + mov lr, #0 @ 1 +1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line + stmia r0!, {r2, r3, ip, lr} @ 4 + stmia r0!, {r2, r3, ip, lr} @ 4 + mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line + stmia r0!, {r2, r3, ip, lr} @ 4 + stmia r0!, {r2, r3, ip, lr} @ 4 + subs r1, r1, #1 @ 1 + bne 1b @ 1 + ldr pc, [sp], #4 diff -Nru a/arch/arm/mm/copypage-v5te.S b/arch/arm/mm/copypage-v5te.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mm/copypage-v5te.S Thu Mar 7 18:17:41 2002 @@ -0,0 +1,79 @@ +/* + * linux/arch/arm/lib/copypage-armv5te.S + * + * Copyright (C) 2001 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include + +/* + * General note: + * We don't really want write-allocate cache behaviour for these functions + * since that will just eat through 8K of the cache. + */ + + .text + .align 5 +/* + * ARMv5TE optimised copy_user_page + * r0 = destination + * r1 = source + * r2 = virtual user address of ultimate destination page + * + * The source page may have some clean entries in the cache already, but we + * can safely ignore them - break_cow() will flush them out of the cache + * if we eventually end up using our copied page. + * + * What we could do is use the mini-cache to buffer reads from the source + * page. We rely on the mini-cache being smaller than one page, so we'll + * cycle through the complete cache anyway. + */ +ENTRY(armv5te_copy_user_page) + stmfd sp!, {r4, r5, lr} + mov r5, r0 + mov r0, r1 + bl map_page_minicache + mov r1, r5 + mov lr, #PAGE_SZ/32 + +1: mov ip, r1 + ldrd r2, [r0], #8 + ldrd r4, [r0], #8 + strd r2, [r1], #8 + ldrd r2, [r0], #8 + strd r4, [r1], #8 + ldrd r4, [r0], #8 + strd r2, [r1], #8 + strd r4, [r1], #8 + mcr p15, 0, ip, c7, c10, 1 @ clean D line + mcr p15, 0, ip, c7, c6, 1 @ invalidate D line + subs lr, lr, #1 + bne 1b + + ldmfd sp!, {r4, r5, pc} + + .align 5 +/* + * ARMv5TE optimised clear_user_page + * r0 = destination + * r1 = virtual user address of ultimate destination page + */ +ENTRY(armv5te_clear_user_page) + str lr, [sp, #-4]! + mov r1, #PAGE_SZ/32 + mov r2, #0 + mov r3, #0 +1: mov ip, r0 + strd r2, [r0], #8 + strd r2, [r0], #8 + strd r2, [r0], #8 + strd r2, [r0], #8 + mcr p15, 0, ip, c7, c10, 1 @ clean D line + mcr p15, 0, ip, c7, c6, 1 @ invalidate D line + subs r1, r1, #1 + bne 1b + ldr pc, [sp], #4 diff -Nru a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c --- a/arch/arm/mm/fault-armv.c Thu Mar 7 18:17:40 2002 +++ b/arch/arm/mm/fault-armv.c Thu Mar 7 18:17:40 2002 @@ -116,7 +116,7 @@ if (!inf->fn(addr, fsr, regs)) return; - printk(KERN_ALERT "Unhandled fault: %s (%X) at 0x%08lx\n", + printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n", inf->name, fsr, addr); force_sig(inf->sig, current); show_pte(current->mm, addr); @@ -151,7 +151,7 @@ if (pmd_bad(*pmd)) goto bad_pmd; - pte = pte_offset(pmd, address); + pte = pte_offset_map(pmd, address); entry = *pte; /* @@ -164,6 +164,7 @@ set_pte(pte, entry); flush_tlb_page(vma, address); } + pte_unmap(pte); return; bad_pgd: @@ -177,38 +178,13 @@ return; } -/* - * Take care of architecture specific things when placing a new PTE into - * a page table, or changing an existing PTE. Basically, there are two - * things that we need to take care of: - * - * 1. If PG_dcache_dirty is set for the page, we need to ensure - * that any cache entries for the kernels virtual memory - * range are written back to the page. - * 2. If we have multiple shared mappings of the same space in - * an object, we need to deal with the cache aliasing issues. - * - * Note that the page_table_lock will be held. - */ -void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte) +static void +make_coherent(struct vm_area_struct *vma, unsigned long addr, struct page *page) { - struct page *page = pte_page(pte); struct vm_area_struct *mpnt; - struct mm_struct *mm; - unsigned long pgoff; - int aliases; - - if (!VALID_PAGE(page) || !page->mapping) - return; - - if (test_and_clear_bit(PG_dcache_dirty, &page->flags)) { - unsigned long kvirt = (unsigned long)page_address(page); - cpu_cache_clean_invalidate_range(kvirt, kvirt + PAGE_SIZE, 0); - } - - mm = vma->vm_mm; - pgoff = (addr - vma->vm_start) >> PAGE_SHIFT; - aliases = 0; + struct mm_struct *mm = vma->vm_mm; + unsigned long pgoff = (addr - vma->vm_start) >> PAGE_SHIFT; + int aliases = 0; /* * If we have any shared mappings that are in the same mm @@ -245,4 +221,31 @@ } if (aliases) adjust_pte(vma, addr); +} + +/* + * Take care of architecture specific things when placing a new PTE into + * a page table, or changing an existing PTE. Basically, there are two + * things that we need to take care of: + * + * 1. If PG_dcache_dirty is set for the page, we need to ensure + * that any cache entries for the kernels virtual memory + * range are written back to the page. + * 2. If we have multiple shared mappings of the same space in + * an object, we need to deal with the cache aliasing issues. + * + * Note that the page_table_lock will be held. + */ +void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte) +{ + struct page *page = pte_page(pte); + + if (VALID_PAGE(page) && page->mapping) { + if (test_and_clear_bit(PG_dcache_dirty, &page->flags)) { + unsigned long kvirt = (unsigned long)page_address(page); + cpu_cache_clean_invalidate_range(kvirt, kvirt + PAGE_SIZE, 0); + } + + make_coherent(vma, addr, page); + } } diff -Nru a/arch/arm/mm/fault-common.c b/arch/arm/mm/fault-common.c --- a/arch/arm/mm/fault-common.c Thu Mar 7 18:17:37 2002 +++ b/arch/arm/mm/fault-common.c Thu Mar 7 18:17:37 2002 @@ -83,11 +83,15 @@ break; } - pte = pte_offset(pmd, addr); +#ifndef CONFIG_HIGHMEM + /* We must not map this if we have highmem enabled */ + pte = pte_offset_map(pmd, addr); printk(", *pte = %08lx", pte_val(*pte)); #ifdef CONFIG_CPU_32 printk(", *ppte = %08lx", pte_val(pte[-PTRS_PER_PTE])); #endif + pte_unmap(pte); +#endif } while(0); printk("\n"); @@ -138,9 +142,10 @@ struct siginfo si; #ifdef CONFIG_DEBUG_USER - printk(KERN_DEBUG "%s: unhandled page fault at pc=0x%08lx, " - "lr=0x%08lx (bad address=0x%08lx, code %d)\n", - tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, error_code); + printk(KERN_DEBUG "%s: unhandled page fault at 0x%08lx, code 0x%03x\n", + tsk->comm, addr, error_code); + show_pte(tsk->mm, addr); + show_regs(regs); #endif tsk->thread.address = addr; @@ -224,8 +229,7 @@ * If we are out of memory for pid1, * sleep for a while and retry */ - tsk->policy |= SCHED_YIELD; - schedule(); + yield(); goto survive; check_stack: diff -Nru a/arch/arm/mm/init.c b/arch/arm/mm/init.c --- a/arch/arm/mm/init.c Thu Mar 7 18:17:38 2002 +++ b/arch/arm/mm/init.c Thu Mar 7 18:17:38 2002 @@ -64,38 +64,6 @@ */ struct page *empty_zero_page; -#ifndef CONFIG_NO_PGT_CACHE -struct pgtable_cache_struct quicklists; - -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; -} -#else -int do_check_pgt_cache(int low, int high) -{ - return 0; -} -#endif - /* This is currently broken * PG_skip is used on sparc/sparc64 architectures to "skip" certain * parts of the address space. @@ -145,9 +113,6 @@ printk("%d slab pages\n", slab); printk("%d pages shared\n", shared); printk("%d pages swap cached\n", cached); -#ifndef CONFIG_NO_PGT_CACHE - printk("%ld page tables cached\n", pgtable_cache_size); -#endif show_buffers(); } diff -Nru a/arch/arm/mm/minicache.c b/arch/arm/mm/minicache.c --- a/arch/arm/mm/minicache.c Thu Mar 7 18:17:39 2002 +++ b/arch/arm/mm/minicache.c Thu Mar 7 18:17:39 2002 @@ -16,9 +16,14 @@ #include #include #include +#include #include -#define minicache_address (0xffff2000) +/* + * 0xffff8000 to 0xffffffff is reserved for any ARM architecture + * specific hacks for copying pages efficiently. + */ +#define minicache_address (0xffff8000) #define minicache_pgprot __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | \ L_PTE_CACHEABLE) @@ -39,7 +44,7 @@ unsigned long map_page_minicache(unsigned long virt) { set_pte(minicache_pte, mk_pte_phys(__pa(virt), minicache_pgprot)); - cpu_tlb_invalidate_page(minicache_address, 0); + flush_kern_tlb_page(minicache_address); return minicache_address; } @@ -53,7 +58,7 @@ pmd = pmd_alloc(&init_mm, pgd, minicache_address); if (!pmd) BUG(); - minicache_pte = pte_alloc(&init_mm, pmd, minicache_address); + minicache_pte = pte_alloc_kernel(&init_mm, pmd, minicache_address); if (!minicache_pte) BUG(); diff -Nru a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c --- a/arch/arm/mm/mm-armv.c Thu Mar 7 18:17:38 2002 +++ b/arch/arm/mm/mm-armv.c Thu Mar 7 18:17:38 2002 @@ -98,11 +98,15 @@ if (!new_pmd) goto no_pmd; - new_pte = pte_alloc(mm, new_pmd, 0); + new_pte = pte_alloc_map(mm, new_pmd, 0); if (!new_pte) goto no_pte; + init_pmd = pmd_offset(init_pgd, 0); + init_pte = pte_offset_map_nested(init_pmd, 0); set_pte(new_pte, *init_pte); + pte_unmap_nested(init_pte); + pte_unmap(new_pte); spin_unlock(&mm->page_table_lock); } @@ -138,7 +142,7 @@ void free_pgd_slow(pgd_t *pgd) { pmd_t *pmd; - pte_t *pte; + struct page *pte; if (!pgd) return; @@ -153,7 +157,7 @@ goto free; } - pte = pte_offset(pmd, 0); + pte = pmd_page(*pmd); pmd_clear(pmd); pte_free(pte); pmd_free(pmd); @@ -198,7 +202,7 @@ set_pmd(pmdp, __mk_pmd(ptep, PMD_TYPE_TABLE | PMD_DOMAIN(domain))); } - ptep = pte_offset(pmdp, virt); + ptep = pte_offset_kernel(pmdp, virt); set_pte(ptep, mk_pte_phys(phys, __pgprot(prot))); } @@ -224,6 +228,20 @@ unsigned long virt, length; int prot_sect, prot_pte; long off; + + if (md->prot_read && md->prot_write && + !md->cacheable && !md->bufferable) { + printk(KERN_WARNING "Security risk: creating user " + "accessible mapping for 0x%08lx at 0x%08lx\n", + md->physical, md->virtual); + } + + if (md->virtual != vectors_base() && md->virtual < PAGE_OFFSET) { + printk(KERN_WARNING "MM: not creating mapping for " + "0x%08lx at 0x%08lx in user region\n", + md->physical, md->virtual); + return; + } prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | (md->prot_read ? L_PTE_USER : 0) | diff -Nru a/arch/arm/mm/proc-arm1020.S b/arch/arm/mm/proc-arm1020.S --- a/arch/arm/mm/proc-arm1020.S Thu Mar 7 18:17:37 2002 +++ b/arch/arm/mm/proc-arm1020.S Thu Mar 7 18:17:37 2002 @@ -420,71 +420,6 @@ #endif mov pc, lr -/* ================================== TLB ================================= */ - -/* - * cpu_arm1020_tlb_invalidate_all() - * - * Invalidate all TLB entries - */ - .align 5 -ENTRY(cpu_arm1020_tlb_invalidate_all) - mov r0, #0 - mcr p15, 0, r0, c7, c10, 4 @ drain WB - mcr p15, 0, r0, c8, c7, 0 @ invalidate I & D tlbs -#ifdef CONFIG_CPU_ARM1020_BRANCH_PREDICTION - mov r0, r0 - mov r0, r0 -#endif - mov pc, lr - -/* - * cpu_arm1020_tlb_invalidate_range(start, end) - * - * invalidate TLB entries covering the specified range - * - * start: range start address - * end: range end address - */ - .align 5 -ENTRY(cpu_arm1020_tlb_invalidate_range) - mov r3, #0 - mcr p15, 0, r3, c7, c10, 4 @ drain WB -1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry - add r0, r0, #PAGESIZE -#ifdef CONFIG_CPU_ARM1020_BRANCH_PREDICTION - mov r0, r0 -#endif - cmp r0, r1 - blt 1b - mov pc, lr - -/* - * cpu_arm1020_tlb_invalidate_page(page, flags) - * - * invalidate the TLB entries for the specified page. - * - * page: page to invalidate - * flags: non-zero if we include the I TLB - */ - .align 5 -ENTRY(cpu_arm1020_tlb_invalidate_page) - mov r3, #0 - mcr p15, 0, r3, c7, c10, 4 @ drain WB -#ifdef CONFIG_CPU_ARM1020_BRANCH_PREDICTION - mov r0, r0 - mov r0, r0 -#endif - teq r1, #0 - mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mcrne p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry -#ifdef CONFIG_CPU_ARM1020_BRANCH_PREDICTION - mov r0, r0 - mov r0, r0 -#endif - mov pc, lr - /* =============================== PageTable ============================== */ /* @@ -695,11 +630,6 @@ .word cpu_arm1020_icache_invalidate_range .word cpu_arm1020_icache_invalidate_page - /* tlb */ - .word cpu_arm1020_tlb_invalidate_all - .word cpu_arm1020_tlb_invalidate_range - .word cpu_arm1020_tlb_invalidate_page - /* pgtable */ .word cpu_arm1020_set_pgd .word cpu_arm1020_set_pmd @@ -741,4 +671,5 @@ .long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT .long cpu_arm1020_info .long arm1020_processor_functions + .long v4wbi_tlb_fns .size __arm1020_proc_info, . - __arm1020_proc_info diff -Nru a/arch/arm/mm/proc-arm2,3.S b/arch/arm/mm/proc-arm2,3.S --- a/arch/arm/mm/proc-arm2,3.S Thu Mar 7 18:17:45 2002 +++ b/arch/arm/mm/proc-arm2,3.S Thu Mar 7 18:17:45 2002 @@ -341,6 +341,7 @@ .long 0 .long cpu_arm2_info .long SYMBOL_NAME(arm2_processor_functions) + .long 0 .long 0x41560250 .long 0xfffffff0 @@ -351,6 +352,7 @@ .long 0 .long cpu_arm250_info .long SYMBOL_NAME(arm250_processor_functions) + .long 0 .long 0x41560300 .long 0xfffffff0 @@ -361,4 +363,4 @@ .long 0 .long cpu_arm3_info .long SYMBOL_NAME(arm3_processor_functions) - + .long 0 diff -Nru a/arch/arm/mm/proc-arm6,7.S b/arch/arm/mm/proc-arm6,7.S --- a/arch/arm/mm/proc-arm6,7.S Thu Mar 7 18:17:46 2002 +++ b/arch/arm/mm/proc-arm6,7.S Thu Mar 7 18:17:46 2002 @@ -48,47 +48,6 @@ mov pc, lr /* - * Function: arm6_7_tlb_invalidate_all (void) - * - * Purpose : flush all TLB entries in all caches - */ -ENTRY(cpu_arm6_tlb_invalidate_all) -ENTRY(cpu_arm7_tlb_invalidate_all) - mov r0, #0 - mcr p15, 0, r0, c5, c0, 0 @ flush TLB - mov pc, lr - -/* - * Function: arm6_7_tlb_invalidate_page (unsigned long address, int end, int flags) - * - * Params : address Area start address - * : end Area end address - * : flags b0 = I cache as well - * - * Purpose : flush a TLB entry - */ -ENTRY(cpu_arm6_tlb_invalidate_range) -ENTRY(cpu_arm7_tlb_invalidate_range) -1: mcr p15, 0, r0, c6, c0, 0 @ flush TLB - add r0, r0, #4096 - cmp r0, r1 - blt 1b - mov pc, lr - -/* - * Function: arm6_7_tlb_invalidate_page (unsigned long address, int flags) - * - * Params : address Address - * : flags b0 = I-TLB as well - * - * Purpose : flush a TLB entry - */ -ENTRY(cpu_arm6_tlb_invalidate_page) -ENTRY(cpu_arm7_tlb_invalidate_page) - mcr p15, 0, r0, c6, c0, 0 @ flush TLB - mov pc, lr - -/* * Function: arm6_7_data_abort () * * Params : r0 = address of aborted instruction @@ -409,11 +368,6 @@ .word cpu_arm6_icache_invalidate_range .word cpu_arm6_icache_invalidate_page - /* tlb */ - .word cpu_arm6_tlb_invalidate_all - .word cpu_arm6_tlb_invalidate_range - .word cpu_arm6_tlb_invalidate_page - /* pgtable */ .word cpu_arm6_set_pgd .word cpu_arm6_set_pmd @@ -453,11 +407,6 @@ .word cpu_arm7_icache_invalidate_range .word cpu_arm7_icache_invalidate_page - /* tlb */ - .word cpu_arm7_tlb_invalidate_all - .word cpu_arm7_tlb_invalidate_range - .word cpu_arm7_tlb_invalidate_page - /* pgtable */ .word cpu_arm7_set_pgd .word cpu_arm7_set_pmd @@ -515,6 +464,7 @@ .long HWCAP_SWP | HWCAP_26BIT .long cpu_arm6_info .long arm6_processor_functions + .long v3_tlb_fns .size __arm6_proc_info, . - __arm6_proc_info .type __arm610_proc_info, #object @@ -528,6 +478,7 @@ .long HWCAP_SWP | HWCAP_26BIT .long cpu_arm610_info .long arm6_processor_functions + .long v3_tlb_fns .size __arm610_proc_info, . - __arm610_proc_info .type __arm7_proc_info, #object @@ -541,6 +492,7 @@ .long HWCAP_SWP | HWCAP_26BIT .long cpu_arm7_info .long arm7_processor_functions + .long v3_tlb_fns .size __arm7_proc_info, . - __arm7_proc_info .type __arm710_proc_info, #object @@ -554,4 +506,5 @@ .long HWCAP_SWP | HWCAP_26BIT .long cpu_arm710_info .long arm7_processor_functions + .long v3_tlb_fns .size __arm710_proc_info, . - __arm710_proc_info diff -Nru a/arch/arm/mm/proc-arm720.S b/arch/arm/mm/proc-arm720.S --- a/arch/arm/mm/proc-arm720.S Thu Mar 7 18:17:38 2002 +++ b/arch/arm/mm/proc-arm720.S Thu Mar 7 18:17:38 2002 @@ -67,44 +67,6 @@ mov pc, lr /* - * Function: arm720_tlb_invalidate_all (void) - * - * Purpose : flush all TLB entries in all caches - */ -ENTRY(cpu_arm720_tlb_invalidate_all) - mov r0, #0 - mcr p15, 0, r0, c8, c7, 0 @ flush TLB (v4) - mov pc, lr - -/* - * Function: arm720_tlb_invalidate_page (unsigned long address, int end, int flags) - * - * Params : address Area start address - * : end Area end address - * : flags b0 = I cache as well - * - * Purpose : flush a TLB entry - */ -ENTRY(cpu_arm720_tlb_invalidate_range) -1: mcr p15, 0, r0, c8, c7, 1 @ flush TLB (v4) - add r0, r0, #4096 - cmp r0, r1 - blt 1b - mov pc, lr - -/* - * Function: arm720_tlb_invalidate_page (unsigned long address, int flags) - * - * Params : address Address - * : flags b0 = I-TLB as well - * - * Purpose : flush a TLB entry - */ -ENTRY(cpu_arm720_tlb_invalidate_page) - mcr p15, 0, r0, c8, c7, 1 @ flush TLB (v4) - mov pc, lr - -/* * Function: arm720_check_bugs (void) * : arm720_proc_init (void) * : arm720_proc_fin (void) @@ -259,11 +221,6 @@ .word cpu_arm720_icache_invalidate_range .word cpu_arm720_icache_invalidate_page - /* tlb */ - .word cpu_arm720_tlb_invalidate_all - .word cpu_arm720_tlb_invalidate_range - .word cpu_arm720_tlb_invalidate_page - /* pgtable */ .word cpu_arm720_set_pgd .word cpu_arm720_set_pmd @@ -307,4 +264,5 @@ .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_26BIT @ elf_hwcap .long cpu_arm720_info @ info .long arm720_processor_functions + .long v4_tlb_fns .size __arm720_proc_info, . - __arm720_proc_info diff -Nru a/arch/arm/mm/proc-arm920.S b/arch/arm/mm/proc-arm920.S --- a/arch/arm/mm/proc-arm920.S Thu Mar 7 18:17:37 2002 +++ b/arch/arm/mm/proc-arm920.S Thu Mar 7 18:17:37 2002 @@ -358,62 +358,6 @@ mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache mov pc, lr -/* ================================== TLB ================================= */ - -/* - * cpu_arm920_tlb_invalidate_all() - * - * Invalidate all TLB entries - */ - .align 5 -ENTRY(cpu_arm920_tlb_invalidate_all) - mov r0, #0 - mcr p15, 0, r0, c7, c10, 4 @ drain WB - mcr p15, 0, r0, c8, c7, 0 @ invalidate I & D TLBs - mov pc, lr - -/* - * cpu_arm920_tlb_invalidate_range(start, end) - * - * invalidate TLB entries covering the specified range - * - * start: range start address - * end: range end address - */ - .align 5 -ENTRY(cpu_arm920_tlb_invalidate_range) - mov r3, #0 - mcr p15, 0, r3, c7, c10, 4 @ drain WB - - mov r3, #PAGESIZE - sub r3, r3, #1 - bic r0, r0, r3 - bic r1, r1, r3 - -1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry - add r0, r0, #PAGESIZE - cmp r0, r1 - blt 1b - mov pc, lr - -/* - * cpu_arm920_tlb_invalidate_page(page, flags) - * - * invalidate the TLB entries for the specified page. - * - * page: page to invalidate - * flags: non-zero if we include the I TLB - */ - .align 5 -ENTRY(cpu_arm920_tlb_invalidate_page) - mov r3, #0 - mcr p15, 0, r3, c7, c10, 4 @ drain WB - teq r1, #0 - mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mcrne p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry - mov pc, lr - /* =============================== PageTable ============================== */ /* @@ -589,11 +533,6 @@ .word cpu_arm920_icache_invalidate_range .word cpu_arm920_icache_invalidate_page - /* tlb */ - .word cpu_arm920_tlb_invalidate_all - .word cpu_arm920_tlb_invalidate_range - .word cpu_arm920_tlb_invalidate_page - /* pgtable */ .word cpu_arm920_set_pgd .word cpu_arm920_set_pmd @@ -635,4 +574,5 @@ .long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT .long cpu_arm920_info .long arm920_processor_functions + .long v4wbi_tlb_fns .size __arm920_proc_info, . - __arm920_proc_info diff -Nru a/arch/arm/mm/proc-arm922.S b/arch/arm/mm/proc-arm922.S --- a/arch/arm/mm/proc-arm922.S Thu Mar 7 18:17:41 2002 +++ b/arch/arm/mm/proc-arm922.S Thu Mar 7 18:17:41 2002 @@ -359,62 +359,6 @@ mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache mov pc, lr -/* ================================== TLB ================================= */ - -/* - * cpu_arm922_tlb_invalidate_all() - * - * Invalidate all TLB entries - */ - .align 5 -ENTRY(cpu_arm922_tlb_invalidate_all) - mov r0, #0 - mcr p15, 0, r0, c7, c10, 4 @ drain WB - mcr p15, 0, r0, c8, c7, 0 @ invalidate I & D TLBs - mov pc, lr - -/* - * cpu_arm922_tlb_invalidate_range(start, end) - * - * invalidate TLB entries covering the specified range - * - * start: range start address - * end: range end address - */ - .align 5 -ENTRY(cpu_arm922_tlb_invalidate_range) - mov r3, #0 - mcr p15, 0, r3, c7, c10, 4 @ drain WB - - mov r3, #PAGESIZE - sub r3, r3, #1 - bic r0, r0, r3 - bic r1, r1, r3 - -1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry - add r0, r0, #PAGESIZE - cmp r0, r1 - blt 1b - mov pc, lr - -/* - * cpu_arm922_tlb_invalidate_page(page, flags) - * - * invalidate the TLB entries for the specified page. - * - * page: page to invalidate - * flags: non-zero if we include the I TLB - */ - .align 5 -ENTRY(cpu_arm922_tlb_invalidate_page) - mov r3, #0 - mcr p15, 0, r3, c7, c10, 4 @ drain WB - teq r1, #0 - mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mcrne p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry - mov pc, lr - /* =============================== PageTable ============================== */ /* @@ -590,11 +534,6 @@ .word cpu_arm922_icache_invalidate_range .word cpu_arm922_icache_invalidate_page - /* tlb */ - .word cpu_arm922_tlb_invalidate_all - .word cpu_arm922_tlb_invalidate_range - .word cpu_arm922_tlb_invalidate_page - /* pgtable */ .word cpu_arm922_set_pgd .word cpu_arm922_set_pmd @@ -636,4 +575,5 @@ .long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT .long cpu_arm922_info .long arm922_processor_functions + .long v4wbi_tlb_fns .size __arm922_proc_info, . - __arm922_proc_info diff -Nru a/arch/arm/mm/proc-arm926.S b/arch/arm/mm/proc-arm926.S --- a/arch/arm/mm/proc-arm926.S Thu Mar 7 18:17:36 2002 +++ b/arch/arm/mm/proc-arm926.S Thu Mar 7 18:17:36 2002 @@ -387,62 +387,6 @@ mov pc, lr -/* ================================== TLB ================================= */ - -/* - * cpu_arm926_tlb_invalidate_all() - * - * Invalidate all TLB entries - */ - .align 5 -ENTRY(cpu_arm926_tlb_invalidate_all) - mov r0, #0 - mcr p15, 0, r0, c7, c10, 4 @ drain WB - mcr p15, 0, r0, c8, c7, 0 @ invalidate I & D TLBs - mov pc, lr - -/* - * cpu_arm926_tlb_invalidate_range(start, end) - * - * invalidate TLB entries covering the specified range - * - * start: range start address - * end: range end address - */ - .align 5 -ENTRY(cpu_arm926_tlb_invalidate_range) - mov r3, #0 - mcr p15, 0, r3, c7, c10, 4 @ drain WB - - mov r3, #PAGESIZE - sub r3, r3, #1 - bic r0, r0, r3 - bic r1, r1, r3 - -1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry - add r0, r0, #PAGESIZE - cmp r0, r1 - blt 1b - mov pc, lr - -/* - * cpu_arm926_tlb_invalidate_page(page, flags) - * - * invalidate the TLB entries for the specified page. - * - * page: page to invalidate - * flags: non-zero if we include the I TLB - */ - .align 5 -ENTRY(cpu_arm926_tlb_invalidate_page) - mov r3, #0 - mcr p15, 0, r3, c7, c10, 4 @ drain WB - teq r1, #0 - mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mcrne p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry - mov pc, lr - /* =============================== PageTable ============================== */ /* @@ -627,11 +571,6 @@ .word cpu_arm926_icache_invalidate_range .word cpu_arm926_icache_invalidate_page - /* tlb */ - .word cpu_arm926_tlb_invalidate_all - .word cpu_arm926_tlb_invalidate_range - .word cpu_arm926_tlb_invalidate_page - /* pgtable */ .word cpu_arm926_set_pgd .word cpu_arm926_set_pmd @@ -673,4 +612,5 @@ .long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT .long cpu_arm926_info .long arm926_processor_functions + .long v4wbi_tlb_fns .size __arm926_proc_info, . - __arm926_proc_info diff -Nru a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mm/proc-macros.S Thu Mar 7 18:17:46 2002 @@ -0,0 +1,25 @@ +#include + +/* + * vma_vm_mm - get mm pointer from vma pointer (vma->vm_mm) + */ + .macro vma_vm_mm, rd, rn + ldr \rd, [\rn, #VMA_VM_MM] + .endm + +/* + * vma_vm_flags - get vma->vm_flags + */ + .macro vma_vm_flags, rd, rn + ldr \rd, [\rn, #VMA_VM_FLAGS] + .endm + +/* + * act_mm - get current->active_mm + */ + .macro act_mm, rd + bic \rd, sp, #8128 + bic \rd, \rd, #63 + ldr \rd, [\rd, #TI_TASK] + ldr \rd, [\rd, #TSK_ACTIVE_MM] + .endm diff -Nru a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S --- a/arch/arm/mm/proc-sa110.S Thu Mar 7 18:17:44 2002 +++ b/arch/arm/mm/proc-sa110.S Thu Mar 7 18:17:44 2002 @@ -406,61 +406,6 @@ mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache mov pc, lr -/* ================================== TLB ================================= */ - -/* - * cpu_sa110_tlb_invalidate_all() - * - * Invalidate all TLB entries - */ - .align 5 -ENTRY(cpu_sa110_tlb_invalidate_all) -ENTRY(cpu_sa1100_tlb_invalidate_all) - mov r0, #0 - mcr p15, 0, r0, c7, c10, 4 @ drain WB - mcr p15, 0, r0, c8, c7, 0 @ invalidate I & D TLBs - mov pc, lr - -/* - * cpu_sa110_tlb_invalidate_range(start, end) - * - * invalidate TLB entries covering the specified range - * - * start: range start address - * end: range end address - */ - .align 5 -ENTRY(cpu_sa110_tlb_invalidate_range) -ENTRY(cpu_sa1100_tlb_invalidate_range) - bic r0, r0, #0x0ff - bic r0, r0, #0xf00 - mov r3, #0 - mcr p15, 0, r3, c7, c10, 4 @ drain WB -1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - add r0, r0, #PAGESIZE - cmp r0, r1 - blo 1b - mcr p15, 0, r3, c8, c5, 0 @ invalidate I TLB - mov pc, lr - -/* - * cpu_sa110_tlb_invalidate_page(page, flags) - * - * invalidate the TLB entries for the specified page. - * - * page: page to invalidate - * flags: non-zero if we include the I TLB - */ - .align 5 -ENTRY(cpu_sa110_tlb_invalidate_page) -ENTRY(cpu_sa1100_tlb_invalidate_page) - mov r3, #0 - mcr p15, 0, r3, c7, c10, 4 @ drain WB - teq r1, #0 - mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mcrne p15, 0, r3, c8, c5, 0 @ invalidate I TLB - mov pc, lr - /* =============================== PageTable ============================== */ /* @@ -615,11 +560,6 @@ .word cpu_sa110_icache_invalidate_range .word cpu_sa110_icache_invalidate_page - /* tlb */ - .word cpu_sa110_tlb_invalidate_all - .word cpu_sa110_tlb_invalidate_range - .word cpu_sa110_tlb_invalidate_page - /* pgtable */ .word cpu_sa110_set_pgd .word cpu_sa110_set_pmd @@ -665,19 +605,14 @@ .word cpu_sa1100_icache_invalidate_range .word cpu_sa1100_icache_invalidate_page - /* tlb */ - .word cpu_sa1100_tlb_invalidate_all - .word cpu_sa1100_tlb_invalidate_range - .word cpu_sa1100_tlb_invalidate_page - /* pgtable */ .word cpu_sa1100_set_pgd .word cpu_sa1100_set_pmd .word cpu_sa1100_set_pte /* misc */ - .word armv4_clear_user_page - .word armv4_copy_user_page + .word armv4_mc_clear_user_page + .word armv4_mc_copy_user_page .size sa1100_processor_functions, . - sa1100_processor_functions @@ -715,6 +650,7 @@ .long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT | HWCAP_FAST_MULT .long cpu_sa110_info .long sa110_processor_functions + .long v4wb_tlb_fns .size __sa110_proc_info, . - __sa110_proc_info .type __sa1100_proc_info,#object @@ -728,6 +664,7 @@ .long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT | HWCAP_FAST_MULT .long cpu_sa1100_info .long sa1100_processor_functions + .long v4wb_tlb_fns .size __sa1100_proc_info, . - __sa1100_proc_info .type __sa1110_proc_info,#object @@ -741,4 +678,5 @@ .long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT | HWCAP_FAST_MULT .long cpu_sa1110_info .long sa1100_processor_functions + .long v4wb_tlb_fns .size __sa1110_proc_info, . - __sa1110_proc_info diff -Nru a/arch/arm/mm/proc-syms.c b/arch/arm/mm/proc-syms.c --- a/arch/arm/mm/proc-syms.c Thu Mar 7 18:17:44 2002 +++ b/arch/arm/mm/proc-syms.c Thu Mar 7 18:17:44 2002 @@ -8,6 +8,9 @@ * published by the Free Software Foundation. */ #include +#include + +#include #include #ifndef MULTI_CPU @@ -20,12 +23,18 @@ EXPORT_SYMBOL(cpu_dcache_invalidate_range); EXPORT_SYMBOL(cpu_icache_invalidate_range); EXPORT_SYMBOL(cpu_icache_invalidate_page); -EXPORT_SYMBOL(cpu_tlb_invalidate_all); -EXPORT_SYMBOL(cpu_tlb_invalidate_range); -EXPORT_SYMBOL(cpu_tlb_invalidate_page); EXPORT_SYMBOL(cpu_set_pgd); EXPORT_SYMBOL(cpu_set_pmd); EXPORT_SYMBOL(cpu_set_pte); #else EXPORT_SYMBOL(processor); +#endif + +#ifndef MULTI_TLB +EXPORT_SYMBOL_NOVERS(__cpu_flush_kern_tlb_all); +EXPORT_SYMBOL_NOVERS(__cpu_flush_user_tlb_mm); +EXPORT_SYMBOL_NOVERS(__cpu_flush_user_tlb_range); +EXPORT_SYMBOL_NOVERS(__cpu_flush_user_tlb_page); +#else +EXPORT_SYMBOL(cpu_tlb); #endif diff -Nru a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S --- a/arch/arm/mm/proc-xscale.S Thu Mar 7 18:17:41 2002 +++ b/arch/arm/mm/proc-xscale.S Thu Mar 7 18:17:41 2002 @@ -494,55 +494,6 @@ ENTRY(xscale_cache_dummy) mov pc, lr -/* ================================== TLB ================================= */ - -/* - * cpu_xscale_tlb_invalidate_all() - * - * Invalidate all TLB entries - */ - .align 5 -ENTRY(cpu_xscale_tlb_invalidate_all) - mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer - mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs - cpwait_ret lr, ip - -/* - * cpu_xscale_tlb_invalidate_range(start, end) - * - * invalidate TLB entries covering the specified range - * - * start: range start address - * end: range end address - */ - .align 5 -ENTRY(cpu_xscale_tlb_invalidate_range) - bic r0, r0, #(PAGESIZE - 1) & 0x00ff - bic r0, r0, #(PAGESIZE - 1) & 0xff00 - mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer -1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry - add r0, r0, #PAGESIZE - cmp r0, r1 - blo 1b - cpwait_ret lr, ip - -/* - * cpu_xscale_tlb_invalidate_page(page, flags) - * - * invalidate the TLB entries for the specified page. - * - * page: page to invalidate - * flags: non-zero if we include the I TLB - */ - .align 5 -ENTRY(cpu_xscale_tlb_invalidate_page) - mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer - teq r1, #0 - mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mcrne p15, 0, r3, c8, c5, 1 @ invalidate I TLB entry - cpwait_ret lr, ip - /* ================================ TLB LOCKING============================== * * The XScale MicroArchitecture implements support for locking entries into @@ -637,6 +588,7 @@ orreq r1, r1, #PMD_SECT_TEX(1) #endif str r1, [r0] + mov ip, #0 mcr p15, 0, r0, c7, c10, 1 @ Clean D cache line mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer mov pc, lr @@ -645,6 +597,8 @@ * cpu_xscale_set_pte(ptep, pte) * * Set a PTE and flush it out + * + * Errata 40: must set memory to write-through for user read-only pages. */ .align 5 ENTRY(cpu_xscale_set_pte) @@ -653,29 +607,49 @@ bic r2, r1, #0xff0 orr r2, r2, #PTE_TYPE_EXT @ extended page - eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY + eor r3, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY - tst r1, #L_PTE_USER | L_PTE_EXEC @ User or Exec? + tst r3, #L_PTE_USER | L_PTE_EXEC @ User or Exec? orrne r2, r2, #PTE_EXT_AP_URO_SRW @ yes -> user r/o, system r/w - tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? + tst r3, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? orreq r2, r2, #PTE_EXT_AP_UNO_SRW @ yes -> user n/a, system r/w @ combined with user -> user r/w + @ + @ Handle the X bit. We want to set this bit for the minicache + @ (U = E = B = W = 0, C = 1) or when write allocate is enabled, + @ and we have a writeable, cacheable region. If we ignore the + @ U and E bits, we can allow user space to use the minicache as + @ well. + @ + @ X = (C & ~W & ~B) | (C & W & B & write_allocate) + @ + eor ip, r1, #L_PTE_CACHEABLE + tst ip, #L_PTE_CACHEABLE | L_PTE_WRITE | L_PTE_BUFFERABLE #if PTE_CACHE_WRITE_ALLOCATE - tst r1, #L_PTE_CACHEABLE @ cacheable? - orrne r2, r2, #PTE_EXT_TEX(1) -#else - eor r1, r1, #L_PTE_CACHEABLE - tst r1, #L_PTE_CACHEABLE | L_PTE_BUFFERABLE @ C = 1 B = 0? - orreq r2, r2, #PTE_EXT_TEX(1) @ yes -> set X (minicache) + eorne ip, r1, #L_PTE_CACHEABLE | L_PTE_WRITE | L_PTE_BUFFERABLE + tstne ip, #L_PTE_CACHEABLE | L_PTE_WRITE | L_PTE_BUFFERABLE #endif + orreq r2, r2, #PTE_EXT_TEX(1) + + @ + @ Erratum 40: The B bit must be cleared for a user read-only + @ cacheable page. + @ + @ B = B & ~((U|E) & C & ~W) + @ + and ip, r1, #L_PTE_USER | L_PTE_EXEC | L_PTE_WRITE | L_PTE_CACHEABLE + teq ip, #L_PTE_USER | L_PTE_CACHEABLE + teqne ip, #L_PTE_EXEC | L_PTE_CACHEABLE + teqne ip, #L_PTE_USER | L_PTE_EXEC | L_PTE_CACHEABLE + biceq r2, r2, #PTE_BUFFERABLE - tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? + tst r3, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? movne r2, #0 @ no -> fault str r2, [r0] @ hardware version - mov r0, r0 + mov ip, #0 mcr p15, 0, r0, c7, c10, 1 @ Clean D cache line mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer mov pc, lr @@ -689,8 +663,8 @@ cpu_80200_name: .asciz "XScale-80200" -cpu_cotulla_name: - .asciz "XScale-Cotulla" +cpu_pxa250_name: + .asciz "XScale-PXA250" .align @@ -743,11 +717,6 @@ .word cpu_xscale_icache_invalidate_range .word cpu_xscale_icache_invalidate_page - /* tlb */ - .word cpu_xscale_tlb_invalidate_all - .word cpu_xscale_tlb_invalidate_range - .word cpu_xscale_tlb_invalidate_page - /* pgtable */ .word cpu_xscale_set_pgd .word cpu_xscale_set_pmd @@ -765,11 +734,11 @@ .long cpu_80200_name .size cpu_80200_info, . - cpu_80200_info - .type cpu_cotulla_info, #object -cpu_cotulla_info: + .type cpu_pxa250_info, #object +cpu_pxa250_info: .long cpu_manu_name - .long cpu_cotulla_name - .size cpu_cotulla_info, . - cpu_cotulla_info + .long cpu_pxa250_name + .size cpu_pxa250_info, . - cpu_pxa250_info .type cpu_arch_name, #object cpu_arch_name: @@ -795,10 +764,11 @@ .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP .long cpu_80200_info .long xscale_processor_functions + .long v4wbi_tlb_fns .size __80200_proc_info, . - __80200_proc_info - .type __cotulla_proc_info,#object -__cotulla_proc_info: + .type __pxa250_proc_info,#object +__pxa250_proc_info: .long 0x69052100 .long 0xfffffff0 .long 0x00000c0e @@ -806,7 +776,9 @@ .long cpu_arch_name .long cpu_elf_name .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP - .long cpu_cotulla_info + .long cpu_pxa250_info .long xscale_processor_functions + .long v4wbi_tlb_fns .size __cotulla_proc_info, . - __cotulla_proc_info + .size __pxa250_proc_info, . - __pxa250_proc_info diff -Nru a/arch/arm/mm/tlb-v3.S b/arch/arm/mm/tlb-v3.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mm/tlb-v3.S Thu Mar 7 18:17:46 2002 @@ -0,0 +1,88 @@ +/* + * linux/arch/arm/mm/tlbv3.S + * + * Copyright (C) 1997-2002 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ARM architecture version 3 TLB handling functions. + * + * Processors: ARM610, ARM710. + */ +#include +#include +#include "proc-macros.S" + + .align 5 +/* + * v3_flush_user_tlb_mm(mm) + * + * Invalidate all TLB entries in a particular address space + * + * - mm - mm_struct describing address space + */ +ENTRY(v3_flush_user_tlb_mm) + act_mm r1 @ get current->active_mm + teq r0, r1 @ == mm ? + movne pc, lr @ no, we dont do anything + +/* + * v3_flush_kern_tlb_all() + * + * Invalidate the entire TLB + */ +ENTRY(v3_flush_kern_tlb_all) + mov r0, #0 + mcr p15, 0, r0, c5, c0, 0 @ invalidate TLB + mov pc, lr + +/* + * v3_flush_user_tlb_range(start, end, mm) + * + * Invalidate a range of TLB entries in the specified address space. + * + * - start - range start address + * - end - range end address + * - mm - mm_struct describing address space + */ + .align 5 +ENTRY(v3_flush_user_tlb_range) + vma_vm_mm r2, r2 + act_mm r3 @ get current->active_mm + teq r2, r3 @ == mm ? + movne pc, lr @ no, we dont do anything + bic r0, r0, #0x0ff + bic r0, r0, #0xf00 +1: mcr p15, 0, r0, c6, c0, 0 @ invalidate TLB entry + add r0, r0, #PAGE_SZ + cmp r0, r1 + blo 1b + mov pc, lr + +/* + * v3_flush_user_tlb_page(vaddr,vma) + * + * Invalidate the specified page in the specified address space. + * + * - vaddr - virtual address (may not be aligned) + * - vma - vma_struct describing address range + */ + .align 5 +ENTRY(v3_flush_user_tlb_page) + vma_vm_mm r2, r1 @ get vma->vm_mm + act_mm r3 @ get current->active_mm + teq r2, r3 @ equal + movne pc, lr @ no +ENTRY(v3_flush_kern_tlb_page) + mcr p15, 0, r0, c6, c0, 0 @ invalidate TLB entry + mov pc, lr + +ENTRY(v3_tlb_fns) + .word v3_flush_kern_tlb_all + .word v3_flush_user_tlb_mm + .word v3_flush_user_tlb_range + .word v3_flush_user_tlb_page + .word v3_flush_kern_tlb_page + diff -Nru a/arch/arm/mm/tlb-v4.S b/arch/arm/mm/tlb-v4.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mm/tlb-v4.S Thu Mar 7 18:17:46 2002 @@ -0,0 +1,106 @@ +/* + * linux/arch/arm/mm/tlbv4.S + * + * Copyright (C) 1997-2002 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ARM architecture version 4 TLB handling functions. + * These assume a split I/D TLBs, and no write buffer. + * + * Processors: ARM720T + */ +#include +#include +#include "proc-macros.S" + + .align 5 +/* + * v4_flush_user_tlb_mm(mm) + * + * Invalidate all TLB entries in a particular address space + * + * - mm - mm_struct describing address space + */ +ENTRY(v4_flush_user_tlb_mm) + act_mm r1 @ get current->active_mm + teq r0, r1 @ == mm ? + movne pc, lr @ no, we dont do anything + +/* + * v4_flush_kern_tlb_all() + * + * Invalidate the entire TLB + */ +ENTRY(v4_flush_kern_tlb_all) + mov r0, #0 + mcr p15, 0, r0, c8, c7, 0 @ invalidate I + D TLBs + mov pc, lr + +/* + * v4_flush_user_tlb_range(start, end, mm) + * + * Invalidate a range of TLB entries in the specified address space. + * + * - start - range start address + * - end - range end address + * - mm - mm_struct describing address space + */ + .align 5 +ENTRY(v4_flush_user_tlb_range) + vma_vm_mm ip, r2 + act_mm r3 @ get current->active_mm + eors r3, ip, r3 @ == mm ? + movne pc, lr @ no, we dont do anything + vma_vm_flags ip, r2 + bic r0, r0, #0x0ff + bic r0, r0, #0xf00 +1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry + add r0, r0, #PAGE_SZ + cmp r0, r1 + blo 1b + tst ip, #VM_EXEC + mcrne p15, 0, r3, c8, c5, 0 @ invalidate I TLB + mov pc, lr + +/* + * v4_flush_user_tlb_page(vaddr,vma) + * + * Invalidate the specified page in the specified address space. + * + * - vaddr - virtual address (may not be aligned) + * - vma - vma_struct describing address range + */ + .align 5 +ENTRY(v4_flush_user_tlb_page) + vma_vm_mm r2, r1 @ get vma->vm_mm + act_mm r3 @ get current->active_mm + teq r2, r3 @ equal + movne pc, lr @ no + vma_vm_flags r2, r1 + mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry + tst r2, #VM_EXEC + mcrne p15, 0, r3, c8, c5, 0 @ invalidate I TLB + mov pc, lr + +/* + * v4_flush_kern_tlb_page(kaddr) + * + * Invalidate the TLB entry for the specified page. The address + * will be in the kernels virtual memory space. Current uses + * only require the D-TLB to be invalidated. + * + * - kaddr - Kernel virtual memory address + */ +ENTRY(v4_flush_kern_tlb_page) + mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry + mov pc, lr + +ENTRY(v4_tlb_fns) + .word v4_flush_kern_tlb_all + .word v4_flush_user_tlb_mm + .word v4_flush_user_tlb_range + .word v4_flush_user_tlb_page + .word v4_flush_kern_tlb_page diff -Nru a/arch/arm/mm/tlb-v4wb.S b/arch/arm/mm/tlb-v4wb.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/arm/mm/tlb-v4wb.S Thu Mar 7 18:17:46 2002 @@ -0,0 +1,160 @@ +/* + * linux/arch/arm/mm/tlbv4wb.S + * + * Copyright (C) 1997-2002 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ARM architecture version 4 and version 5 TLB handling functions. + * These assume a split I/D TLBs, with a write buffer. + * + * Processors: ARM920 ARM922 ARM926 SA110 SA1100 SA1110 XScale + */ +#include +#include +#include "proc-macros.S" + + .align 5 +/* + * v4wb_flush_user_tlb_mm(mm) + * + * Invalidate all TLB entries in a particular address space + * + * - mm - mm_struct describing address space + */ +ENTRY(v4wb_flush_user_tlb_mm) +ENTRY(v4wbi_flush_user_tlb_mm) + act_mm r1 @ get current->active_mm + teq r0, r1 @ == mm ? + movne pc, lr @ no, we dont do anything + +/* + * v4wb_flush_tlb_all() + * + * Invalidate the entire TLB + */ +ENTRY(v4wb_flush_kern_tlb_all) +ENTRY(v4wbi_flush_kern_tlb_all) + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ drain WB + mcr p15, 0, r0, c8, c7, 0 @ invalidate I + D TLBs + mov pc, lr + +/* + * v4wb_flush_user_tlb_range(start, end, mm) + * + * Invalidate a range of TLB entries in the specified address space. + * + * - start - range start address + * - end - range end address + * - mm - mm_struct describing address space + */ + .align 5 +ENTRY(v4wb_flush_user_tlb_range) + vma_vm_mm ip, r2 + act_mm r3 @ get current->active_mm + eors r3, ip, r3 @ == mm ? + movne pc, lr @ no, we dont do anything + vma_vm_flags r2, r2 + mcr p15, 0, r3, c7, c10, 4 @ drain WB + tst r2, #VM_EXEC + mcrne p15, 0, r3, c8, c5, 0 @ invalidate I TLB + bic r0, r0, #0x0ff + bic r0, r0, #0xf00 +1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry + add r0, r0, #PAGE_SZ + cmp r0, r1 + blo 1b + mov pc, lr + +/* + * v4wb_flush_user_tlb_page(vaddr,vma) + * + * Invalidate the specified page in the specified address space. + * + * - vaddr - virtual address (may not be aligned) + * - vma - vma_struct describing address range + */ + .align 5 +ENTRY(v4wb_flush_user_tlb_page) + vma_vm_mm r2, r1 @ get vma->vm_mm + act_mm r3 @ get current->active_mm + teq r2, r3 @ equal + movne pc, lr @ no + vma_vm_flags r2, r1 + mov r3, #0 + mcr p15, 0, r3, c7, c10, 4 @ drain WB + tst r2, #VM_EXEC + mcrne p15, 0, r3, c8, c5, 0 @ invalidate I TLB +ENTRY(v4wb_flush_kern_tlb_page) + mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry + mov pc, lr + +/* + * These two are optimised for ARM920, ARM922, ARM926, Xscale + */ + +/* + * v4wb_flush_user_tlb_range(start, end, mm) + * + * Invalidate a range of TLB entries in the specified address space. + * + * - start - range start address + * - end - range end address + * - mm - mm_struct describing address space + */ + .align 5 +ENTRY(v4wbi_flush_user_tlb_range) + act_mm r3 @ get current->active_mm + teq r2, r3 @ == mm ? + movne pc, lr @ no, we dont do anything + mov r3, #0 + mcr p15, 0, r3, c7, c10, 4 @ drain WB + bic r0, r0, #0x0ff + bic r0, r0, #0xf00 +1: mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry + mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry + add r0, r0, #PAGE_SZ + cmp r0, r1 + blo 1b + mov pc, lr + +/* + * v4wb_flush_tlb_page(vaddr,vma) + * + * Invalidate the specified page in the specified address space. + * + * - vaddr - virtual address (may not be aligned) + * - vma - vma_struct describing address range + */ + .align 5 +ENTRY(v4wbi_flush_user_tlb_page) + vma_vm_mm r2, r1 @ get vma->vm_mm + act_mm r3 @ get current->active_mm + teq r2, r3 @ equal + movne pc, lr @ no + vma_vm_flags r2, r1 + mov r3, #0 + mcr p15, 0, r3, c7, c10, 4 @ drain WB + tst r2, #VM_EXEC + mcrne p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry +ENTRY(v4wbi_flush_kern_tlb_page) + mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry + mov pc, lr + +ENTRY(v4wb_tlb_fns) + .word v4wb_flush_kern_tlb_all + .word v4wb_flush_user_tlb_mm + .word v4wb_flush_user_tlb_range + .word v4wb_flush_user_tlb_page + .word v4wb_flush_kern_tlb_page + +ENTRY(v4wbi_tlb_fns) + .word v4wbi_flush_kern_tlb_all + .word v4wbi_flush_user_tlb_mm + .word v4wbi_flush_user_tlb_range + .word v4wbi_flush_user_tlb_page + .word v4wbi_flush_kern_tlb_page + diff -Nru a/arch/arm/nwfpe/ChangeLog b/arch/arm/nwfpe/ChangeLog --- a/arch/arm/nwfpe/ChangeLog Thu Mar 7 18:17:45 2002 +++ b/arch/arm/nwfpe/ChangeLog Thu Mar 7 18:17:45 2002 @@ -1,3 +1,26 @@ +2002-01-19 Russell King + + * fpa11.h - Add documentation + - remove userRegisters pointer from this structure. + - add new method to obtain integer register values. + * softfloat.c - Remove float128 + * softfloat.h - Remove float128 + * softfloat-specialize - Remove float128 + + * The FPA11 structure is not a kernel-specific data structure. + It is used by users of ptrace to examine the values of the + floating point registers. Therefore, any changes to the + FPA11 structure (size or position of elements contained + within) have to be well thought out. + + * Since 128-bit float requires the FPA11 structure to change + size, it has been removed. 128-bit float is currently unused, + and needs various things to be re-worked so that we won't + overflow the available space in the task structure. + + * The changes are designed to break any patch that goes on top + of this code, so that the authors properly review their changes. + 1999-08-19 Scott Bambrough * fpmodule.c - Changed version number to 0.95 diff -Nru a/arch/arm/nwfpe/double_cpdo.c b/arch/arm/nwfpe/double_cpdo.c --- a/arch/arm/nwfpe/double_cpdo.c Thu Mar 7 18:17:36 2002 +++ b/arch/arm/nwfpe/double_cpdo.c Thu Mar 7 18:17:36 2002 @@ -19,9 +19,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "fpa11.h" #include "softfloat.h" #include "fpopcode.h" -#include "fpa11.h" float64 float64_exp(float64 Fm); float64 float64_ln(float64 Fm); @@ -165,8 +165,7 @@ case RND_CODE: case URD_CODE: - fpa11->fpreg[Fd].fDouble = - int32_to_float64(float64_to_int32(rFm)); + fpa11->fpreg[Fd].fDouble = float64_round_to_int(rFm); break; case SQT_CODE: diff -Nru a/arch/arm/nwfpe/entry.S b/arch/arm/nwfpe/entry.S --- a/arch/arm/nwfpe/entry.S Thu Mar 7 18:17:37 2002 +++ b/arch/arm/nwfpe/entry.S Thu Mar 7 18:17:37 2002 @@ -52,8 +52,6 @@ 1) The kernel has created a struct pt_regs on the stack and saved the user registers into it. See /usr/include/asm/proc/ptrace.h for details. -The emulator code uses userRegisters as the base of an array of words -from which the contents of the registers can be extracted. 2) It calls EmulateAll to emulate a floating point instruction. EmulateAll returns 1 if the emulation was successful, or 0 if not. @@ -72,14 +70,9 @@ .globl nwfpe_enter nwfpe_enter: - /* ?? Could put userRegisters and fpa11 into fixed regs during - emulation. This would reduce load/store overhead at the expense - of stealing two regs from the register allocator. Not sure if - it's worth it. */ - str sp, [r10] @ Store the user registers pointer in the fpa11 structure. mov r4, lr @ save the failure-return addresses + mov sl, sp - mov r0, r10 bl FPA11_CheckInit @ check to see if we are initialised ldr r5, [sp, #60] @ get contents of PC; diff -Nru a/arch/arm/nwfpe/entry26.S b/arch/arm/nwfpe/entry26.S --- a/arch/arm/nwfpe/entry26.S Thu Mar 7 18:17:41 2002 +++ b/arch/arm/nwfpe/entry26.S Thu Mar 7 18:17:41 2002 @@ -65,18 +65,10 @@ .globl nwfpe_enter nwfpe_enter: - ldr r4, =userRegisters - str sp, [r4] @ save pointer to user regs + mov sl, sp + bl FPA11_CheckInit @ check to see if we are initialised - mov r10, sp, lsr #13 @ find workspace - mov r10, r10, lsl #13 - add r10, r10, #TSS_FPESAVE - - ldr r4, =fpa11 - str r10, [r4] @ store pointer to our state - mov r4, sp @ use r4 for local pointer - - ldr r5, [r4, #60] @ get contents of PC + ldr r5, [sp, #60] @ get contents of PC bic r5, r5, #0xfc000003 ldr r0, [r5, #-4] @ get actual instruction into r0 bl EmulateAll @ emulate the instruction @@ -93,10 +85,10 @@ teqne r2, #0x0E000000 bne ret_from_exception @ return ok if not a fp insn - ldr r9, [r4, #60] @ get new condition codes + ldr r9, [sp, #60] @ get new condition codes and r9, r9, #0xfc000003 orr r7, r5, r9 - str r7, [r4, #60] @ update PC copy in regs + str r7, [sp, #60] @ update PC copy in regs mov r0, r6 @ save a copy mov r1, r9 @ fetch the condition codes diff -Nru a/arch/arm/nwfpe/extended_cpdo.c b/arch/arm/nwfpe/extended_cpdo.c --- a/arch/arm/nwfpe/extended_cpdo.c Thu Mar 7 18:17:37 2002 +++ b/arch/arm/nwfpe/extended_cpdo.c Thu Mar 7 18:17:37 2002 @@ -19,9 +19,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "fpa11.h" #include "softfloat.h" #include "fpopcode.h" -#include "fpa11.h" floatx80 floatx80_exp(floatx80 Fm); floatx80 floatx80_ln(floatx80 Fm); @@ -157,8 +157,7 @@ case RND_CODE: case URD_CODE: - fpa11->fpreg[Fd].fExtended = - int32_to_floatx80(floatx80_to_int32(rFm)); + fpa11->fpreg[Fd].fExtended = floatx80_round_to_int(rFm); break; case SQT_CODE: diff -Nru a/arch/arm/nwfpe/fpa11.c b/arch/arm/nwfpe/fpa11.c --- a/arch/arm/nwfpe/fpa11.c Thu Mar 7 18:17:42 2002 +++ b/arch/arm/nwfpe/fpa11.c Thu Mar 7 18:17:42 2002 @@ -18,8 +18,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include -#include #include "fpa11.h" #include "fpopcode.h" @@ -27,6 +25,9 @@ #include "fpmodule.h" #include "fpmodule.inl" +#include +#include + /* forward declarations */ unsigned int EmulateCPDO(const unsigned int); unsigned int EmulateCPDT(const unsigned int); @@ -56,6 +57,7 @@ void SetRoundingMode(const unsigned int opcode) { #if MAINTAIN_FPCR + FPA11 *fpa11 = GET_FPA11(); fpa11->fpcr &= ~MASK_ROUNDING_MODE; #endif switch (opcode & MASK_ROUNDING_MODE) @@ -94,6 +96,7 @@ void SetRoundingPrecision(const unsigned int opcode) { #if MAINTAIN_FPCR + FPA11 *fpa11 = GET_FPA11(); fpa11->fpcr &= ~MASK_ROUNDING_PRECISION; #endif switch (opcode & MASK_ROUNDING_PRECISION) @@ -123,8 +126,9 @@ } } -void FPA11_CheckInit(FPA11 *fpa11) +void FPA11_CheckInit(void) { + FPA11 *fpa11 = GET_FPA11(); if (unlikely(fpa11->initflag == 0)) { resetFPA11(); diff -Nru a/arch/arm/nwfpe/fpa11.h b/arch/arm/nwfpe/fpa11.h --- a/arch/arm/nwfpe/fpa11.h Thu Mar 7 18:17:43 2002 +++ b/arch/arm/nwfpe/fpa11.h Thu Mar 7 18:17:43 2002 @@ -22,45 +22,66 @@ #ifndef __FPA11_H__ #define __FPA11_H__ +#define GET_FPA11() ((FPA11 *)(¤t_thread_info()->fpstate)) + +/* + * The processes registers are always at the very top of the 8K + * stack+task struct. Use the same method as 'current' uses to + * reach them. + */ +register unsigned int *user_registers asm("sl"); + +#define GET_USERREG() (user_registers) + +#include + /* includes */ #include "fpsr.h" /* FP control and status register definitions */ #include "softfloat.h" -/* Need task_struct */ -#include - #define typeNone 0x00 #define typeSingle 0x01 #define typeDouble 0x02 #define typeExtended 0x03 +/* + * This must be no more and no less than 12 bytes. + */ typedef union tagFPREG { - float32 fSingle; - float64 fDouble; floatx80 fExtended; + float64 fDouble; + float32 fSingle; } FPREG; -/* FPA11 device model */ +/* + * FPA11 device model. + * + * This structure is exported to user space. Do not re-order. + * Only add new stuff to the end, and do not change the size of + * any element. Elements of this structure are used by user + * space, and must match struct user_fp in include/asm-arm/user.h. + * We include the byte offsets below for documentation purposes. + * + * The size of this structure and FPREG are checked by fpmodule.c + * on initialisation. If the rules have been broken, NWFPE will + * not initialise. + */ typedef struct tagFPA11 { - unsigned int *userRegisters; - FPREG fpreg[8]; /* 8 floating point registers */ - FPSR fpsr; /* floating point status register */ - FPCR fpcr; /* floating point control register */ - unsigned char fType[8]; /* type of floating point value held in - floating point registers. One of none - single, double or extended. */ - int initflag; /* this is special. The kernel guarantees - to set it to 0 when a thread is launched, - so we can use it to detect whether this - instance of the emulator needs to be - initialised. */ +/* 0 */ FPREG fpreg[8]; /* 8 floating point registers */ +/* 96 */ FPSR fpsr; /* floating point status register */ +/* 100 */ FPCR fpcr; /* floating point control register */ +/* 104 */ unsigned char fType[8]; /* type of floating point value held in + floating point registers. One of none + single, double or extended. */ +/* 112 */ int initflag; /* this is special. The kernel guarantees + to set it to 0 when a thread is launched, + so we can use it to detect whether this + instance of the emulator needs to be + initialised. */ } FPA11; extern void resetFPA11(void); extern void SetRoundingMode(const unsigned int); extern void SetRoundingPrecision(const unsigned int); - -#define GET_FPA11() ((FPA11 *)(¤t->thread.fpstate)) -#define GET_USERREG() (GET_FPA11()->userRegisters) #endif diff -Nru a/arch/arm/nwfpe/fpa11_cpdt.c b/arch/arm/nwfpe/fpa11_cpdt.c --- a/arch/arm/nwfpe/fpa11_cpdt.c Thu Mar 7 18:17:46 2002 +++ b/arch/arm/nwfpe/fpa11_cpdt.c Thu Mar 7 18:17:46 2002 @@ -20,9 +20,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "fpa11.h" #include "softfloat.h" #include "fpopcode.h" -#include "fpa11.h" #include "fpmodule.h" #include "fpmodule.inl" diff -Nru a/arch/arm/nwfpe/fpa11_cprt.c b/arch/arm/nwfpe/fpa11_cprt.c --- a/arch/arm/nwfpe/fpa11_cprt.c Thu Mar 7 18:17:38 2002 +++ b/arch/arm/nwfpe/fpa11_cprt.c Thu Mar 7 18:17:38 2002 @@ -20,10 +20,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "fpa11.h" #include "milieu.h" #include "softfloat.h" #include "fpopcode.h" -#include "fpa11.h" #include "fpa11.inl" #include "fpmodule.h" #include "fpmodule.inl" @@ -82,8 +82,7 @@ unsigned int nRc = 1; SetRoundingMode(opcode); - SetRoundingPrecision(opcode); - + switch (opcode & MASK_ROUNDING_PRECISION) { case ROUND_SINGLE: diff -Nru a/arch/arm/nwfpe/fpmodule.c b/arch/arm/nwfpe/fpmodule.c --- a/arch/arm/nwfpe/fpmodule.c Thu Mar 7 18:17:43 2002 +++ b/arch/arm/nwfpe/fpmodule.c Thu Mar 7 18:17:43 2002 @@ -21,6 +21,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "fpa11.h" + #include #include #include @@ -36,7 +38,6 @@ #include "softfloat.h" #include "fpopcode.h" #include "fpmodule.h" -#include "fpa11.h" #include "fpa11.inl" /* kernel symbols required for signal handling */ @@ -83,6 +84,11 @@ { if (sizeof(FPA11) > sizeof(union fp_state)) { printk(KERN_ERR "nwfpe: bad structure size\n"); + return -EINVAL; + } + + if (sizeof(FPREG) != 12) { + printk(KERN_ERR "nwfpe: bad register size\n"); return -EINVAL; } diff -Nru a/arch/arm/nwfpe/fpopcode.c b/arch/arm/nwfpe/fpopcode.c --- a/arch/arm/nwfpe/fpopcode.c Thu Mar 7 18:17:43 2002 +++ b/arch/arm/nwfpe/fpopcode.c Thu Mar 7 18:17:43 2002 @@ -19,10 +19,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "fpa11.h" #include "softfloat.h" #include "fpopcode.h" #include "fpsr.h" -#include "fpa11.h" #include "fpmodule.h" #include "fpmodule.inl" diff -Nru a/arch/arm/nwfpe/single_cpdo.c b/arch/arm/nwfpe/single_cpdo.c --- a/arch/arm/nwfpe/single_cpdo.c Thu Mar 7 18:17:43 2002 +++ b/arch/arm/nwfpe/single_cpdo.c Thu Mar 7 18:17:43 2002 @@ -19,9 +19,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "fpa11.h" #include "softfloat.h" #include "fpopcode.h" -#include "fpa11.h" float32 float32_exp(float32 Fm); float32 float32_ln(float32 Fm); @@ -139,8 +139,7 @@ case RND_CODE: case URD_CODE: - fpa11->fpreg[Fd].fSingle = - int32_to_float32(float32_to_int32(rFm)); + fpa11->fpreg[Fd].fSingle = float32_round_to_int(rFm); break; case SQT_CODE: diff -Nru a/arch/arm/nwfpe/softfloat-specialize b/arch/arm/nwfpe/softfloat-specialize --- a/arch/arm/nwfpe/softfloat-specialize Thu Mar 7 18:17:46 2002 +++ b/arch/arm/nwfpe/softfloat-specialize Thu Mar 7 18:17:46 2002 @@ -364,108 +364,3 @@ } #endif - -#ifdef FLOAT128 - -/* -------------------------------------------------------------------------------- -The pattern for a default generated quadruple-precision NaN. The `high' and -`low' values hold the most- and least-significant bits, respectively. -------------------------------------------------------------------------------- -*/ -#define float128_default_nan_high LIT64( 0xFFFFFFFFFFFFFFFF ) -#define float128_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF ) - -/* -------------------------------------------------------------------------------- -Returns 1 if the quadruple-precision floating-point value `a' is a NaN; -otherwise returns 0. -------------------------------------------------------------------------------- -*/ -flag float128_is_nan( float128 a ) -{ - - return - ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) ) - && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) ); - -} - -/* -------------------------------------------------------------------------------- -Returns 1 if the quadruple-precision floating-point value `a' is a -signaling NaN; otherwise returns 0. -------------------------------------------------------------------------------- -*/ -flag float128_is_signaling_nan( float128 a ) -{ - - return - ( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE ) - && ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) ); - -} - -/* -------------------------------------------------------------------------------- -Returns the result of converting the quadruple-precision floating-point NaN -`a' to the canonical NaN format. If `a' is a signaling NaN, the invalid -exception is raised. -------------------------------------------------------------------------------- -*/ -static commonNaNT float128ToCommonNaN( float128 a ) -{ - commonNaNT z; - - if ( float128_is_signaling_nan( a ) ) float_raise( float_flag_invalid ); - z.sign = a.high>>63; - shortShift128Left( a.high, a.low, 16, &z.high, &z.low ); - return z; - -} - -/* -------------------------------------------------------------------------------- -Returns the result of converting the canonical NaN `a' to the quadruple- -precision floating-point format. -------------------------------------------------------------------------------- -*/ -static float128 commonNaNToFloat128( commonNaNT a ) -{ - float128 z; - - shift128Right( a.high, a.low, 16, &z.high, &z.low ); - z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF800000000000 ); - return z; - -} - -/* -------------------------------------------------------------------------------- -Takes two quadruple-precision floating-point values `a' and `b', one of -which is a NaN, and returns the appropriate NaN result. If either `a' or -`b' is a signaling NaN, the invalid exception is raised. -------------------------------------------------------------------------------- -*/ -static float128 propagateFloat128NaN( float128 a, float128 b ) -{ - flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; - - aIsNaN = float128_is_nan( a ); - aIsSignalingNaN = float128_is_signaling_nan( a ); - bIsNaN = float128_is_nan( b ); - bIsSignalingNaN = float128_is_signaling_nan( b ); - a.high |= LIT64( 0x0000800000000000 ); - b.high |= LIT64( 0x0000800000000000 ); - if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid ); - if ( aIsNaN ) { - return ( aIsSignalingNaN & bIsNaN ) ? b : a; - } - else { - return b; - } - -} - -#endif - diff -Nru a/arch/arm/nwfpe/softfloat.c b/arch/arm/nwfpe/softfloat.c --- a/arch/arm/nwfpe/softfloat.c Thu Mar 7 18:17:45 2002 +++ b/arch/arm/nwfpe/softfloat.c Thu Mar 7 18:17:45 2002 @@ -28,6 +28,7 @@ =============================================================================== */ +#include "fpa11.h" #include "milieu.h" #include "softfloat.h" @@ -753,277 +754,6 @@ #endif -#ifdef FLOAT128 - -/* -------------------------------------------------------------------------------- -Returns the least-significant 64 fraction bits of the quadruple-precision -floating-point value `a'. -------------------------------------------------------------------------------- -*/ -INLINE bits64 extractFloat128Frac1( float128 a ) -{ - - return a.low; - -} - -/* -------------------------------------------------------------------------------- -Returns the most-significant 48 fraction bits of the quadruple-precision -floating-point value `a'. -------------------------------------------------------------------------------- -*/ -INLINE bits64 extractFloat128Frac0( float128 a ) -{ - - return a.high & LIT64( 0x0000FFFFFFFFFFFF ); - -} - -/* -------------------------------------------------------------------------------- -Returns the exponent bits of the quadruple-precision floating-point value -`a'. -------------------------------------------------------------------------------- -*/ -INLINE int32 extractFloat128Exp( float128 a ) -{ - - return ( a.high>>48 ) & 0x7FFF; - -} - -/* -------------------------------------------------------------------------------- -Returns the sign bit of the quadruple-precision floating-point value `a'. -------------------------------------------------------------------------------- -*/ -INLINE flag extractFloat128Sign( float128 a ) -{ - - return a.high>>63; - -} - -/* -------------------------------------------------------------------------------- -Normalizes the subnormal quadruple-precision floating-point value -represented by the denormalized significand formed by the concatenation of -`aSig0' and `aSig1'. The normalized exponent is stored at the location -pointed to by `zExpPtr'. The most significant 49 bits of the normalized -significand are stored at the location pointed to by `zSig0Ptr', and the -least significant 64 bits of the normalized significand are stored at the -location pointed to by `zSig1Ptr'. -------------------------------------------------------------------------------- -*/ -static void - normalizeFloat128Subnormal( - bits64 aSig0, - bits64 aSig1, - int32 *zExpPtr, - bits64 *zSig0Ptr, - bits64 *zSig1Ptr - ) -{ - int8 shiftCount; - - if ( aSig0 == 0 ) { - shiftCount = countLeadingZeros64( aSig1 ) - 15; - if ( shiftCount < 0 ) { - *zSig0Ptr = aSig1>>( - shiftCount ); - *zSig1Ptr = aSig1<<( shiftCount & 63 ); - } - else { - *zSig0Ptr = aSig1<>= shiftCount; - z = aSig0; - if ( aSign ) z = - z; - if ( ( z < 0 ) ^ aSign ) { - invalid: - float_exception_flags |= float_flag_invalid; - return aSign ? 0x80000000 : 0x7FFFFFFF; - } - if ( ( aSig0<>1, &z.high, &z.low ); - if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask; - } - else { - if ( (sbits64) z.low < 0 ) { - ++z.high; - if ( (bits64) ( z.low<<1 ) == 0 ) z.high &= ~1; - } - } - } - else if ( roundingMode != float_round_to_zero ) { - if ( extractFloat128Sign( z ) - ^ ( roundingMode == float_round_up ) ) { - add128( z.high, z.low, 0, roundBitsMask, &z.high, &z.low ); - } - } - z.low &= ~ roundBitsMask; - } - else { - if ( aExp <= 0x3FFE ) { - if ( ( ( (bits64) ( a.high<<1 ) ) | a.low ) == 0 ) return a; - float_exception_flags |= float_flag_inexact; - aSign = extractFloat128Sign( a ); - switch ( float_rounding_mode ) { - case float_round_nearest_even: - if ( ( aExp == 0x3FFE ) - && ( extractFloat128Frac0( a ) - | extractFloat128Frac1( a ) ) - ) { - return packFloat128( aSign, 0x3FFF, 0, 0 ); - } - break; - case float_round_down: - return - aSign ? packFloat128( 1, 0x3FFF, 0, 0 ) - : packFloat128( 0, 0, 0, 0 ); - case float_round_up: - return - aSign ? packFloat128( 1, 0, 0, 0 ) - : packFloat128( 0, 0x3FFF, 0, 0 ); - } - return packFloat128( aSign, 0, 0, 0 ); - } - lastBitMask = 1; - lastBitMask <<= 0x402F - aExp; - roundBitsMask = lastBitMask - 1; - z.low = 0; - z.high = a.high; - roundingMode = float_rounding_mode; - if ( roundingMode == float_round_nearest_even ) { - z.high += lastBitMask>>1; - if ( ( ( z.high & roundBitsMask ) | a.low ) == 0 ) { - z.high &= ~ lastBitMask; - } - } - else if ( roundingMode != float_round_to_zero ) { - if ( extractFloat128Sign( z ) - ^ ( roundingMode == float_round_up ) ) { - z.high |= ( a.low != 0 ); - z.high += roundBitsMask; - } - } - z.high &= ~ roundBitsMask; - } - if ( ( z.low != a.low ) || ( z.high != a.high ) ) { - float_exception_flags |= float_flag_inexact; - } - return z; - -} - -/* -------------------------------------------------------------------------------- -Returns the result of adding the absolute values of the quadruple-precision -floating-point values `a' and `b'. If `zSign' is true, the sum is negated -before being returned. `zSign' is ignored if the result is a NaN. The -addition is performed according to the IEC/IEEE Standard for Binary -Floating-point Arithmetic. -------------------------------------------------------------------------------- -*/ -static float128 addFloat128Sigs( float128 a, float128 b, flag zSign ) -{ - int32 aExp, bExp, zExp; - bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; - int32 expDiff; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - bSig1 = extractFloat128Frac1( b ); - bSig0 = extractFloat128Frac0( b ); - bExp = extractFloat128Exp( b ); - expDiff = aExp - bExp; - if ( 0 < expDiff ) { - if ( aExp == 0x7FFF ) { - if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b ); - return a; - } - if ( bExp == 0 ) { - --expDiff; - } - else { - bSig0 |= LIT64( 0x0001000000000000 ); - } - shift128ExtraRightJamming( - bSig0, bSig1, 0, expDiff, &bSig0, &bSig1, &zSig2 ); - zExp = aExp; - } - else if ( expDiff < 0 ) { - if ( bExp == 0x7FFF ) { - if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b ); - return packFloat128( zSign, 0x7FFF, 0, 0 ); - } - if ( aExp == 0 ) { - ++expDiff; - } - else { - aSig0 |= LIT64( 0x0001000000000000 ); - } - shift128ExtraRightJamming( - aSig0, aSig1, 0, - expDiff, &aSig0, &aSig1, &zSig2 ); - zExp = bExp; - } - else { - if ( aExp == 0x7FFF ) { - if ( aSig0 | aSig1 | bSig0 | bSig1 ) { - return propagateFloat128NaN( a, b ); - } - return a; - } - add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 ); - if ( aExp == 0 ) return packFloat128( zSign, 0, zSig0, zSig1 ); - zSig2 = 0; - zSig0 |= LIT64( 0x0002000000000000 ); - zExp = aExp; - goto shiftRight1; - } - aSig0 |= LIT64( 0x0001000000000000 ); - add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 ); - --zExp; - if ( zSig0 < LIT64( 0x0002000000000000 ) ) goto roundAndPack; - ++zExp; - shiftRight1: - shift128ExtraRightJamming( - zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 ); - roundAndPack: - return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 ); - -} - -/* -------------------------------------------------------------------------------- -Returns the result of subtracting the absolute values of the quadruple- -precision floating-point values `a' and `b'. If `zSign' is true, the -difference is negated before being returned. `zSign' is ignored if the -result is a NaN. The subtraction is performed according to the IEC/IEEE -Standard for Binary Floating-point Arithmetic. -------------------------------------------------------------------------------- -*/ -static float128 subFloat128Sigs( float128 a, float128 b, flag zSign ) -{ - int32 aExp, bExp, zExp; - bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1; - int32 expDiff; - float128 z; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - bSig1 = extractFloat128Frac1( b ); - bSig0 = extractFloat128Frac0( b ); - bExp = extractFloat128Exp( b ); - expDiff = aExp - bExp; - shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 ); - shortShift128Left( bSig0, bSig1, 14, &bSig0, &bSig1 ); - if ( 0 < expDiff ) goto aExpBigger; - if ( expDiff < 0 ) goto bExpBigger; - if ( aExp == 0x7FFF ) { - if ( aSig0 | aSig1 | bSig0 | bSig1 ) { - return propagateFloat128NaN( a, b ); - } - float_raise( float_flag_invalid ); - z.low = float128_default_nan_low; - z.high = float128_default_nan_high; - return z; - } - if ( aExp == 0 ) { - aExp = 1; - bExp = 1; - } - if ( bSig0 < aSig0 ) goto aBigger; - if ( aSig0 < bSig0 ) goto bBigger; - if ( bSig1 < aSig1 ) goto aBigger; - if ( aSig1 < bSig1 ) goto bBigger; - return packFloat128( float_rounding_mode == float_round_down, 0, 0, 0 ); - bExpBigger: - if ( bExp == 0x7FFF ) { - if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b ); - return packFloat128( zSign ^ 1, 0x7FFF, 0, 0 ); - } - if ( aExp == 0 ) { - ++expDiff; - } - else { - aSig0 |= LIT64( 0x4000000000000000 ); - } - shift128RightJamming( aSig0, aSig1, - expDiff, &aSig0, &aSig1 ); - bSig0 |= LIT64( 0x4000000000000000 ); - bBigger: - sub128( bSig0, bSig1, aSig0, aSig1, &zSig0, &zSig1 ); - zExp = bExp; - zSign ^= 1; - goto normalizeRoundAndPack; - aExpBigger: - if ( aExp == 0x7FFF ) { - if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b ); - return a; - } - if ( bExp == 0 ) { - --expDiff; - } - else { - bSig0 |= LIT64( 0x4000000000000000 ); - } - shift128RightJamming( bSig0, bSig1, expDiff, &bSig0, &bSig1 ); - aSig0 |= LIT64( 0x4000000000000000 ); - aBigger: - sub128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 ); - zExp = aExp; - normalizeRoundAndPack: - --zExp; - return normalizeRoundAndPackFloat128( zSign, zExp - 14, zSig0, zSig1 ); - -} - -/* -------------------------------------------------------------------------------- -Returns the result of adding the quadruple-precision floating-point values -`a' and `b'. The operation is performed according to the IEC/IEEE Standard -for Binary Floating-point Arithmetic. -------------------------------------------------------------------------------- -*/ -float128 float128_add( float128 a, float128 b ) -{ - flag aSign, bSign; - - aSign = extractFloat128Sign( a ); - bSign = extractFloat128Sign( b ); - if ( aSign == bSign ) { - return addFloat128Sigs( a, b, aSign ); - } - else { - return subFloat128Sigs( a, b, aSign ); - } - -} - -/* -------------------------------------------------------------------------------- -Returns the result of subtracting the quadruple-precision floating-point -values `a' and `b'. The operation is performed according to the IEC/IEEE -Standard for Binary Floating-point Arithmetic. -------------------------------------------------------------------------------- -*/ -float128 float128_sub( float128 a, float128 b ) -{ - flag aSign, bSign; - - aSign = extractFloat128Sign( a ); - bSign = extractFloat128Sign( b ); - if ( aSign == bSign ) { - return subFloat128Sigs( a, b, aSign ); - } - else { - return addFloat128Sigs( a, b, aSign ); - } - -} - -/* -------------------------------------------------------------------------------- -Returns the result of multiplying the quadruple-precision floating-point -values `a' and `b'. The operation is performed according to the IEC/IEEE -Standard for Binary Floating-point Arithmetic. -------------------------------------------------------------------------------- -*/ -float128 float128_mul( float128 a, float128 b ) -{ - flag aSign, bSign, zSign; - int32 aExp, bExp, zExp; - bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3; - float128 z; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - aSign = extractFloat128Sign( a ); - bSig1 = extractFloat128Frac1( b ); - bSig0 = extractFloat128Frac0( b ); - bExp = extractFloat128Exp( b ); - bSign = extractFloat128Sign( b ); - zSign = aSign ^ bSign; - if ( aExp == 0x7FFF ) { - if ( ( aSig0 | aSig1 ) - || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) { - return propagateFloat128NaN( a, b ); - } - if ( ( bExp | bSig0 | bSig1 ) == 0 ) goto invalid; - return packFloat128( zSign, 0x7FFF, 0, 0 ); - } - if ( bExp == 0x7FFF ) { - if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b ); - if ( ( aExp | aSig0 | aSig1 ) == 0 ) { - invalid: - float_raise( float_flag_invalid ); - z.low = float128_default_nan_low; - z.high = float128_default_nan_high; - return z; - } - return packFloat128( zSign, 0x7FFF, 0, 0 ); - } - if ( aExp == 0 ) { - if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 ); - normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); - } - if ( bExp == 0 ) { - if ( ( bSig0 | bSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 ); - normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 ); - } - zExp = aExp + bExp - 0x4000; - aSig0 |= LIT64( 0x0001000000000000 ); - shortShift128Left( bSig0, bSig1, 16, &bSig0, &bSig1 ); - mul128To256( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1, &zSig2, &zSig3 ); - add128( zSig0, zSig1, aSig0, aSig1, &zSig0, &zSig1 ); - zSig2 |= ( zSig3 != 0 ); - if ( LIT64( 0x0002000000000000 ) <= zSig0 ) { - shift128ExtraRightJamming( - zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 ); - ++zExp; - } - return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 ); - -} - -/* -------------------------------------------------------------------------------- -Returns the result of dividing the quadruple-precision floating-point value -`a' by the corresponding value `b'. The operation is performed according to -the IEC/IEEE Standard for Binary Floating-point Arithmetic. -------------------------------------------------------------------------------- -*/ -float128 float128_div( float128 a, float128 b ) -{ - flag aSign, bSign, zSign; - int32 aExp, bExp, zExp; - bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; - bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3; - float128 z; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - aSign = extractFloat128Sign( a ); - bSig1 = extractFloat128Frac1( b ); - bSig0 = extractFloat128Frac0( b ); - bExp = extractFloat128Exp( b ); - bSign = extractFloat128Sign( b ); - zSign = aSign ^ bSign; - if ( aExp == 0x7FFF ) { - if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b ); - if ( bExp == 0x7FFF ) { - if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b ); - goto invalid; - } - return packFloat128( zSign, 0x7FFF, 0, 0 ); - } - if ( bExp == 0x7FFF ) { - if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b ); - return packFloat128( zSign, 0, 0, 0 ); - } - if ( bExp == 0 ) { - if ( ( bSig0 | bSig1 ) == 0 ) { - if ( ( aExp | aSig0 | aSig1 ) == 0 ) { - invalid: - float_raise( float_flag_invalid ); - z.low = float128_default_nan_low; - z.high = float128_default_nan_high; - return z; - } - float_raise( float_flag_divbyzero ); - return packFloat128( zSign, 0x7FFF, 0, 0 ); - } - normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 ); - } - if ( aExp == 0 ) { - if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 ); - normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); - } - zExp = aExp - bExp + 0x3FFD; - shortShift128Left( - aSig0 | LIT64( 0x0001000000000000 ), aSig1, 15, &aSig0, &aSig1 ); - shortShift128Left( - bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 ); - if ( le128( bSig0, bSig1, aSig0, aSig1 ) ) { - shift128Right( aSig0, aSig1, 1, &aSig0, &aSig1 ); - ++zExp; - } - zSig0 = estimateDiv128To64( aSig0, aSig1, bSig0 ); - mul128By64To192( bSig0, bSig1, zSig0, &term0, &term1, &term2 ); - sub192( aSig0, aSig1, 0, term0, term1, term2, &rem0, &rem1, &rem2 ); - while ( (sbits64) rem0 < 0 ) { - --zSig0; - add192( rem0, rem1, rem2, 0, bSig0, bSig1, &rem0, &rem1, &rem2 ); - } - zSig1 = estimateDiv128To64( rem1, rem2, bSig0 ); - if ( ( zSig1 & 0x3FFF ) <= 4 ) { - mul128By64To192( bSig0, bSig1, zSig1, &term1, &term2, &term3 ); - sub192( rem1, rem2, 0, term1, term2, term3, &rem1, &rem2, &rem3 ); - while ( (sbits64) rem1 < 0 ) { - --zSig1; - add192( rem1, rem2, rem3, 0, bSig0, bSig1, &rem1, &rem2, &rem3 ); - } - zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 ); - } - shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 ); - return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 ); - -} - -/* -------------------------------------------------------------------------------- -Returns the remainder of the quadruple-precision floating-point value `a' -with respect to the corresponding value `b'. The operation is performed -according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. -------------------------------------------------------------------------------- -*/ -float128 float128_rem( float128 a, float128 b ) -{ - flag aSign, bSign, zSign; - int32 aExp, bExp, expDiff; - bits64 aSig0, aSig1, bSig0, bSig1; - bits64 q, term0, term1, term2, allZero, alternateASig0, alternateASig1; - bits64 sigMean1; - sbits64 sigMean0; - float128 z; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - aSign = extractFloat128Sign( a ); - bSig1 = extractFloat128Frac1( b ); - bSig0 = extractFloat128Frac0( b ); - bExp = extractFloat128Exp( b ); - bSign = extractFloat128Sign( b ); - if ( aExp == 0x7FFF ) { - if ( ( aSig0 | aSig1 ) - || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) { - return propagateFloat128NaN( a, b ); - } - goto invalid; - } - if ( bExp == 0x7FFF ) { - if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b ); - return a; - } - if ( bExp == 0 ) { - if ( ( bSig0 | bSig1 ) == 0 ) { - invalid: - float_raise( float_flag_invalid ); - z.low = float128_default_nan_low; - z.high = float128_default_nan_high; - return z; - } - normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 ); - } - if ( aExp == 0 ) { - if ( ( aSig0 | aSig1 ) == 0 ) return a; - normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); - } - expDiff = aExp - bExp; - if ( expDiff < -1 ) return a; - shortShift128Left( - aSig0 | LIT64( 0x0001000000000000 ), - aSig1, - 15 - ( expDiff < 0 ), - &aSig0, - &aSig1 - ); - shortShift128Left( - bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 ); - q = le128( bSig0, bSig1, aSig0, aSig1 ); - if ( q ) sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 ); - expDiff -= 64; - while ( 0 < expDiff ) { - q = estimateDiv128To64( aSig0, aSig1, bSig0 ); - q = ( 4 < q ) ? q - 4 : 0; - mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 ); - shortShift192Left( term0, term1, term2, 61, &term1, &term2, &allZero ); - shortShift128Left( aSig0, aSig1, 61, &aSig0, &allZero ); - sub128( aSig0, 0, term1, term2, &aSig0, &aSig1 ); - expDiff -= 61; - } - if ( -64 < expDiff ) { - q = estimateDiv128To64( aSig0, aSig1, bSig0 ); - q = ( 4 < q ) ? q - 4 : 0; - q >>= - expDiff; - shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 ); - expDiff += 52; - if ( expDiff < 0 ) { - shift128Right( aSig0, aSig1, - expDiff, &aSig0, &aSig1 ); - } - else { - shortShift128Left( aSig0, aSig1, expDiff, &aSig0, &aSig1 ); - } - mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 ); - sub128( aSig0, aSig1, term1, term2, &aSig0, &aSig1 ); - } - else { - shift128Right( aSig0, aSig1, 12, &aSig0, &aSig1 ); - shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 ); - } - do { - alternateASig0 = aSig0; - alternateASig1 = aSig1; - ++q; - sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 ); - } while ( 0 <= (sbits64) aSig0 ); - add128( - aSig0, aSig1, alternateASig0, alternateASig1, &sigMean0, &sigMean1 ); - if ( ( sigMean0 < 0 ) - || ( ( ( sigMean0 | sigMean1 ) == 0 ) && ( q & 1 ) ) ) { - aSig0 = alternateASig0; - aSig1 = alternateASig1; - } - zSign = ( (sbits64) aSig0 < 0 ); - if ( zSign ) sub128( 0, 0, aSig0, aSig1, &aSig0, &aSig1 ); - return - normalizeRoundAndPackFloat128( aSign ^ zSign, bExp - 4, aSig0, aSig1 ); - -} - -/* -------------------------------------------------------------------------------- -Returns the square root of the quadruple-precision floating-point value `a'. -The operation is performed according to the IEC/IEEE Standard for Binary -Floating-point Arithmetic. -------------------------------------------------------------------------------- -*/ -float128 float128_sqrt( float128 a ) -{ - flag aSign; - int32 aExp, zExp; - bits64 aSig0, aSig1, zSig0, zSig1, zSig2; - bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3; - bits64 shiftedRem0, shiftedRem1; - float128 z; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - aSign = extractFloat128Sign( a ); - if ( aExp == 0x7FFF ) { - if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, a ); - if ( ! aSign ) return a; - goto invalid; - } - if ( aSign ) { - if ( ( aExp | aSig0 | aSig1 ) == 0 ) return a; - invalid: - float_raise( float_flag_invalid ); - z.low = float128_default_nan_low; - z.high = float128_default_nan_high; - return z; - } - if ( aExp == 0 ) { - if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( 0, 0, 0, 0 ); - normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); - } - zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFE; - aSig0 |= LIT64( 0x0001000000000000 ); - zSig0 = estimateSqrt32( aExp, aSig0>>17 ); - zSig0 <<= 31; - shortShift128Left( aSig0, aSig1, 13 - ( aExp & 1 ), &aSig0, &aSig1 ); - zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0 ) + zSig0 + 4; - if ( 0 <= (sbits64) zSig0 ) zSig0 = LIT64( 0xFFFFFFFFFFFFFFFF ); - shortShift128Left( aSig0, aSig1, 2, &aSig0, &aSig1 ); - mul64To128( zSig0, zSig0, &term0, &term1 ); - sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 ); - while ( (sbits64) rem0 < 0 ) { - --zSig0; - shortShift128Left( 0, zSig0, 1, &term0, &term1 ); - term1 |= 1; - add128( rem0, rem1, term0, term1, &rem0, &rem1 ); - } - shortShift128Left( rem0, rem1, 63, &shiftedRem0, &shiftedRem1 ); - zSig1 = estimateDiv128To64( shiftedRem0, shiftedRem1, zSig0 ); - if ( ( zSig1 & 0x3FFF ) <= 5 ) { - if ( zSig1 == 0 ) zSig1 = 1; - mul64To128( zSig0, zSig1, &term1, &term2 ); - shortShift128Left( term1, term2, 1, &term1, &term2 ); - sub128( rem1, 0, term1, term2, &rem1, &rem2 ); - mul64To128( zSig1, zSig1, &term2, &term3 ); - sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 ); - while ( (sbits64) rem1 < 0 ) { - --zSig1; - shortShift192Left( 0, zSig0, zSig1, 1, &term1, &term2, &term3 ); - term3 |= 1; - add192( - rem1, rem2, rem3, term1, term2, term3, &rem1, &rem2, &rem3 ); - } - zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 ); - } - shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 ); - return roundAndPackFloat128( 0, zExp, zSig0, zSig1, zSig2 ); - -} - -/* -------------------------------------------------------------------------------- -Returns 1 if the quadruple-precision floating-point value `a' is equal to -the corresponding value `b', and 0 otherwise. The comparison is performed -according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. -------------------------------------------------------------------------------- -*/ -flag float128_eq( float128 a, float128 b ) -{ - - if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) - && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) - || ( ( extractFloat128Exp( b ) == 0x7FFF ) - && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) - ) { - if ( float128_is_signaling_nan( a ) - || float128_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid ); - } - return 0; - } - return - ( a.low == b.low ) - && ( ( a.high == b.high ) - || ( ( a.low == 0 ) - && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) ) - ); - -} - -/* -------------------------------------------------------------------------------- -Returns 1 if the quadruple-precision floating-point value `a' is less than -or equal to the corresponding value `b', and 0 otherwise. The comparison -is performed according to the IEC/IEEE Standard for Binary Floating-point -Arithmetic. -------------------------------------------------------------------------------- -*/ -flag float128_le( float128 a, float128 b ) -{ - flag aSign, bSign; - - if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) - && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) - || ( ( extractFloat128Exp( b ) == 0x7FFF ) - && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) - ) { - float_raise( float_flag_invalid ); - return 0; - } - aSign = extractFloat128Sign( a ); - bSign = extractFloat128Sign( b ); - if ( aSign != bSign ) { - return - aSign - || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) - == 0 ); - } - return - aSign ? le128( b.high, b.low, a.high, a.low ) - : le128( a.high, a.low, b.high, b.low ); - -} - -/* -------------------------------------------------------------------------------- -Returns 1 if the quadruple-precision floating-point value `a' is less than -the corresponding value `b', and 0 otherwise. The comparison is performed -according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. -------------------------------------------------------------------------------- -*/ -flag float128_lt( float128 a, float128 b ) -{ - flag aSign, bSign; - - if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) - && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) - || ( ( extractFloat128Exp( b ) == 0x7FFF ) - && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) - ) { - float_raise( float_flag_invalid ); - return 0; - } - aSign = extractFloat128Sign( a ); - bSign = extractFloat128Sign( b ); - if ( aSign != bSign ) { - return - aSign - && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) - != 0 ); - } - return - aSign ? lt128( b.high, b.low, a.high, a.low ) - : lt128( a.high, a.low, b.high, b.low ); - -} - -/* -------------------------------------------------------------------------------- -Returns 1 if the quadruple-precision floating-point value `a' is equal to -the corresponding value `b', and 0 otherwise. The invalid exception is -raised if either operand is a NaN. Otherwise, the comparison is performed -according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. -------------------------------------------------------------------------------- -*/ -flag float128_eq_signaling( float128 a, float128 b ) -{ - - if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) - && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) - || ( ( extractFloat128Exp( b ) == 0x7FFF ) - && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) - ) { - float_raise( float_flag_invalid ); - return 0; - } - return - ( a.low == b.low ) - && ( ( a.high == b.high ) - || ( ( a.low == 0 ) - && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) ) - ); - -} - -/* -------------------------------------------------------------------------------- -Returns 1 if the quadruple-precision floating-point value `a' is less than -or equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not -cause an exception. Otherwise, the comparison is performed according to the -IEC/IEEE Standard for Binary Floating-point Arithmetic. -------------------------------------------------------------------------------- -*/ -flag float128_le_quiet( float128 a, float128 b ) -{ - flag aSign, bSign; - - if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) - && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) - || ( ( extractFloat128Exp( b ) == 0x7FFF ) - && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) - ) { - if ( float128_is_signaling_nan( a ) - || float128_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid ); - } - return 0; - } - aSign = extractFloat128Sign( a ); - bSign = extractFloat128Sign( b ); - if ( aSign != bSign ) { - return - aSign - || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) - == 0 ); - } - return - aSign ? le128( b.high, b.low, a.high, a.low ) - : le128( a.high, a.low, b.high, b.low ); - -} - -/* -------------------------------------------------------------------------------- -Returns 1 if the quadruple-precision floating-point value `a' is less than -the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an -exception. Otherwise, the comparison is performed according to the IEC/IEEE -Standard for Binary Floating-point Arithmetic. -------------------------------------------------------------------------------- -*/ -flag float128_lt_quiet( float128 a, float128 b ) -{ - flag aSign, bSign; - - if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) - && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) - || ( ( extractFloat128Exp( b ) == 0x7FFF ) - && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) - ) { - if ( float128_is_signaling_nan( a ) - || float128_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid ); - } - return 0; - } - aSign = extractFloat128Sign( a ); - bSign = extractFloat128Sign( b ); - if ( aSign != bSign ) { - return - aSign - && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) != 0 ); } return diff -Nru a/arch/arm/nwfpe/softfloat.h b/arch/arm/nwfpe/softfloat.h --- a/arch/arm/nwfpe/softfloat.h Thu Mar 7 18:17:38 2002 +++ b/arch/arm/nwfpe/softfloat.h Thu Mar 7 18:17:38 2002 @@ -37,12 +37,10 @@ The macro `FLOATX80' must be defined to enable the extended double-precision floating-point format `floatx80'. If this macro is not defined, the `floatx80' type will not be defined, and none of the functions that either -input or output the `floatx80' type will be defined. The same applies to -the `FLOAT128' macro and the quadruple-precision format `float128'. +input or output the `floatx80' type will be defined. ------------------------------------------------------------------------------- */ #define FLOATX80 -/* #define FLOAT128 */ /* ------------------------------------------------------------------------------- @@ -51,17 +49,10 @@ */ typedef unsigned long int float32; typedef unsigned long long float64; -#ifdef FLOATX80 typedef struct { unsigned short high; unsigned long long low; } floatx80; -#endif -#ifdef FLOAT128 -typedef struct { - unsigned long long high, low; -} float128; -#endif /* ------------------------------------------------------------------------------- @@ -131,9 +122,6 @@ #ifdef FLOATX80 floatx80 int32_to_floatx80( signed int ); #endif -#ifdef FLOAT128 -float128 int32_to_float128( signed int ); -#endif /* ------------------------------------------------------------------------------- @@ -146,9 +134,6 @@ #ifdef FLOATX80 floatx80 float32_to_floatx80( float32 ); #endif -#ifdef FLOAT128 -float128 float32_to_float128( float32 ); -#endif /* ------------------------------------------------------------------------------- @@ -181,9 +166,6 @@ #ifdef FLOATX80 floatx80 float64_to_floatx80( float64 ); #endif -#ifdef FLOAT128 -float128 float64_to_float128( float64 ); -#endif /* ------------------------------------------------------------------------------- @@ -216,9 +198,6 @@ signed int floatx80_to_int32_round_to_zero( floatx80 ); float32 floatx80_to_float32( floatx80 ); float64 floatx80_to_float64( floatx80 ); -#ifdef FLOAT128 -float128 floatx80_to_float128( floatx80 ); -#endif /* ------------------------------------------------------------------------------- @@ -247,43 +226,6 @@ char floatx80_le_quiet( floatx80, floatx80 ); char floatx80_lt_quiet( floatx80, floatx80 ); char floatx80_is_signaling_nan( floatx80 ); - -#endif - -#ifdef FLOAT128 - -/* -------------------------------------------------------------------------------- -Software IEC/IEEE quadruple-precision conversion routines. -------------------------------------------------------------------------------- -*/ -signed int float128_to_int32( float128 ); -signed int float128_to_int32_round_to_zero( float128 ); -float32 float128_to_float32( float128 ); -float64 float128_to_float64( float128 ); -#ifdef FLOATX80 -floatx80 float128_to_floatx80( float128 ); -#endif - -/* -------------------------------------------------------------------------------- -Software IEC/IEEE quadruple-precision operations. -------------------------------------------------------------------------------- -*/ -float128 float128_round_to_int( float128 ); -float128 float128_add( float128, float128 ); -float128 float128_sub( float128, float128 ); -float128 float128_mul( float128, float128 ); -float128 float128_div( float128, float128 ); -float128 float128_rem( float128, float128 ); -float128 float128_sqrt( float128 ); -char float128_eq( float128, float128 ); -char float128_le( float128, float128 ); -char float128_lt( float128, float128 ); -char float128_eq_signaling( float128, float128 ); -char float128_le_quiet( float128, float128 ); -char float128_lt_quiet( float128, float128 ); -char float128_is_signaling_nan( float128 ); #endif diff -Nru a/arch/arm/tools/getconstants.c b/arch/arm/tools/getconstants.c --- a/arch/arm/tools/getconstants.c Thu Mar 7 18:17:41 2002 +++ b/arch/arm/tools/getconstants.c Thu Mar 7 18:17:41 2002 @@ -32,23 +32,21 @@ #endif #define OFF_TSK(n) (unsigned long)&(((struct task_struct *)0)->n) +#define OFF_VMA(n) (unsigned long)&(((struct vm_area_struct *)0)->n) #define DEFN(name,off) asm("\n#define "name" %0" :: "I" (off)) void func(void) { -#error DEFN("TSK_SIGPENDING", OFF_TSK(sigpending)); -DEFN("TSK_ADDR_LIMIT", OFF_TSK(addr_limit)); -#error DEFN("TSK_NEED_RESCHED", OFF_TSK(need_resched)); -#error DEFN("TSK_PTRACE", OFF_TSK(ptrace)); DEFN("TSK_USED_MATH", OFF_TSK(used_math)); +DEFN("TSK_ACTIVE_MM", OFF_TSK(active_mm)); -DEFN("TSS_SAVE", OFF_TSK(thread.save)); -DEFN("TSS_FPESAVE", OFF_TSK(thread.fpstate.soft.save)); +DEFN("VMA_VM_MM", OFF_VMA(vm_mm)); +DEFN("VMA_VM_FLAGS", OFF_VMA(vm_flags)); -#ifdef CONFIG_CPU_32 -DEFN("TSS_DOMAIN", OFF_TSK(thread.domain)); +DEFN("VM_EXEC", VM_EXEC); +#ifdef CONFIG_CPU_32 DEFN("HPTE_TYPE_SMALL", PTE_TYPE_SMALL); DEFN("HPTE_AP_READ", PTE_AP_READ); DEFN("HPTE_AP_WRITE", PTE_AP_WRITE); diff -Nru a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types --- a/arch/arm/tools/mach-types Thu Mar 7 18:17:44 2002 +++ b/arch/arm/tools/mach-types Thu Mar 7 18:17:44 2002 @@ -6,7 +6,7 @@ # To add an entry into this database, please see Documentation/arm/README, # or contact rmk@arm.linux.org.uk # -# Last update: Fri Jan 4 10:27:21 2002 +# Last update: Sun Feb 24 17:43:42 2002 # # machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number # @@ -118,10 +118,10 @@ cdb89712 ARCH_CDB89712 CDB89712 107 graphicsmaster SA1100_GRAPHICSMASTER GRAPHICSMASTER 108 adsbitsy SA1100_ADSBITSY ADSBITSY 109 -cotulla_idp ARCH_COTULLA_IDP COTULLA_IDP 110 +pxa_idp ARCH_PXA_IDP PXA_IDP 110 plce ARCH_PLCE PLCE 111 pt_system3 SA1100_PT_SYSTEM3 PT_SYSTEM3 112 -medalb ARCH_MEDALB MEDALB 113 +murphy ARCH_MEDALB MEDALB 113 eagle ARCH_EAGLE EAGLE 114 dsc21 ARCH_DSC21 DSC21 115 dsc24 ARCH_DSC24 DSC24 116 @@ -162,3 +162,15 @@ cep SA1100_CEP CEP 151 fortunet ARCH_FORTUNET FORTUNET 152 vc547x ARCH_VC547X VC547X 153 +filewalker SA1100_FILEWALKER FILEWALKER 154 +netgateway SA1100_NETGATEWAY NETGATEWAY 155 +symbol2800 SA1100_SYMBOL2800 SYMBOL2800 156 +suns SA1100_SUNS SUNS 157 +frodo SA1100_FRODO FRODO 158 +ms301 SA1100_MACH_TYTE_MS301 MACH_TYTE_MS301 159 +mx1ads ARCH_MX1ADS MX1ADS 160 +h7201 ARCH_H7201 H7201 161 +h7202 ARCH_H7202 H7202 162 +amico ARCH_AMICO AMICO 163 +iam SA1100_IAM IAM 164 +tt530 SA1100_TT530 TT530 165 diff -Nru a/arch/cris/drivers/ide.c b/arch/cris/drivers/ide.c --- a/arch/cris/drivers/ide.c Thu Mar 7 18:17:40 2002 +++ b/arch/cris/drivers/ide.c Thu Mar 7 18:17:40 2002 @@ -94,8 +94,6 @@ * device can't do DMA handshaking for some stupid reason. We don't need to do that. */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #include #include #include diff -Nru a/arch/i386/defconfig b/arch/i386/defconfig --- a/arch/i386/defconfig Thu Mar 7 18:17:38 2002 +++ b/arch/i386/defconfig Thu Mar 7 18:17:38 2002 @@ -98,6 +98,7 @@ # CONFIG_I82092 is not set # CONFIG_I82365 is not set # CONFIG_TCIC is not set +# CONFIG_PCMCIA_SA1100 is not set # # PCI Hotplug Support @@ -105,6 +106,7 @@ # CONFIG_HOTPLUG_PCI is not set # CONFIG_HOTPLUG_PCI_COMPAQ is not set # CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set +# CONFIG_HOTPLUG_PCI_IBM is not set CONFIG_KCORE_ELF=y # CONFIG_KCORE_AOUT is not set CONFIG_BINFMT_AOUT=y @@ -121,7 +123,18 @@ # # Parallel port support # -# CONFIG_PARPORT is not set +CONFIG_PARPORT=y +CONFIG_PARPORT_PC=y +CONFIG_PARPORT_PC_CML1=y +# CONFIG_PARPORT_SERIAL is not set +# CONFIG_PARPORT_PC_PCMCIA is not set +# CONFIG_PARPORT_AMIGA is not set +# CONFIG_PARPORT_MFC3 is not set +# CONFIG_PARPORT_ATARI is not set +# CONFIG_PARPORT_GSC is not set +# CONFIG_PARPORT_SUNBPP is not set +# CONFIG_PARPORT_OTHER is not set +# CONFIG_PARPORT_1284 is not set # # Plug and Play configuration @@ -227,10 +240,9 @@ # CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set # CONFIG_BLK_DEV_IDESCSI is not set -CONFIG_IDE_TASK_IOCTL=y # -# IDE chipset support/bugfixes +# IDE chipset support # CONFIG_BLK_DEV_CMD640=y # CONFIG_BLK_DEV_CMD640_ENHANCED is not set @@ -240,7 +252,6 @@ CONFIG_IDEPCI_SHARE_IRQ=y CONFIG_BLK_DEV_IDEDMA_PCI=y # CONFIG_BLK_DEV_OFFBOARD is not set -# CONFIG_BLK_DEV_IDEDMA_FORCED is not set CONFIG_IDEDMA_PCI_AUTO=y # CONFIG_IDEDMA_ONLYDISK is not set CONFIG_BLK_DEV_IDEDMA=y @@ -333,6 +344,8 @@ # CONFIG_SCSI_IPS is not set # CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_PPA is not set +# CONFIG_SCSI_IMM is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set # CONFIG_SCSI_SYM53C8XX_2 is not set @@ -422,12 +435,9 @@ # CONFIG_AC3200 is not set # CONFIG_APRICOT is not set # CONFIG_CS89x0 is not set -# CONFIG_DE2104X is not set -# CONFIG_TULIP is not set -# CONFIG_DE4X5 is not set # CONFIG_DGRS is not set -# CONFIG_DM9102 is not set -CONFIG_EEPRO100=y +# CONFIG_EEPRO100 is not set +CONFIG_E100=y # CONFIG_LNE390 is not set # CONFIG_FEALNX is not set # CONFIG_NATSEMI is not set @@ -446,7 +456,6 @@ # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set # CONFIG_VIA_RHINE_MMIO is not set -# CONFIG_WINBOND_840 is not set # CONFIG_NET_POCKET is not set # @@ -454,11 +463,13 @@ # # CONFIG_ACENIC is not set # CONFIG_DL2K is not set +# CONFIG_E1000 is not set # CONFIG_MYRI_SBUS is not set # CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set +# CONFIG_TIGON3 is not set # CONFIG_FDDI is not set # CONFIG_PLIP is not set # CONFIG_PPP is not set @@ -481,6 +492,11 @@ # CONFIG_WAN is not set # +# "Tulip" family network device support +# +# CONFIG_NET_TULIP is not set + +# # PCMCIA network device support # CONFIG_NET_PCMCIA=y @@ -494,8 +510,6 @@ # CONFIG_PCMCIA_AXNET is not set # CONFIG_ARCNET_COM20020_CS is not set # CONFIG_PCMCIA_IBMTR is not set -# CONFIG_PCMCIA_XIRCOM is not set -# CONFIG_PCMCIA_XIRTULIP is not set CONFIG_NET_PCMCIA_RADIO=y CONFIG_PCMCIA_RAYCS=y # CONFIG_AIRONET4500_CS is not set @@ -550,6 +564,9 @@ # CONFIG_SERIAL_NONSTANDARD is not set CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 +CONFIG_PRINTER=y +# CONFIG_LP_CONSOLE is not set +# CONFIG_PPDEV is not set # # I2C support @@ -638,6 +655,9 @@ CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_ZISOFS is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set # CONFIG_MINIX_FS is not set # CONFIG_VXFS_FS is not set # CONFIG_NTFS_FS is not set @@ -668,6 +688,7 @@ # CONFIG_ROOT_NFS is not set CONFIG_NFSD=y # CONFIG_NFSD_V3 is not set +# CONFIG_NFSD_TCP is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set @@ -710,19 +731,28 @@ # Advanced Linux Sound Architecture # CONFIG_SND=y -# CONFIG_SND_RTCTIMER is not set CONFIG_SND_SEQUENCER=y # CONFIG_SND_SEQ_DUMMY is not set CONFIG_SND_OSSEMUL=y CONFIG_SND_MIXER_OSS=y CONFIG_SND_PCM_OSS=y CONFIG_SND_SEQUENCER_OSS=y +# CONFIG_SND_RTCTIMER is not set +# CONFIG_SND_VERBOSE_PRINTK is not set # CONFIG_SND_DEBUG is not set + +# +# Generic devices +# # CONFIG_SND_DUMMY is not set # CONFIG_SND_VIRMIDI is not set # CONFIG_SND_MTPAV is not set # CONFIG_SND_SERIAL_U16550 is not set # CONFIG_SND_MPU401 is not set + +# +# ISA devices +# # CONFIG_SND_AD1816A is not set # CONFIG_SND_AD1848 is not set # CONFIG_SND_CS4231 is not set @@ -749,6 +779,10 @@ # CONFIG_SND_DT0197H is not set # CONFIG_SND_OPL3SA2 is not set # CONFIG_SND_SGALAXY is not set + +# +# PCI devices +# # CONFIG_SND_ALI5451 is not set # CONFIG_SND_CS46XX is not set # CONFIG_SND_EMU10K1 is not set diff -Nru a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile --- a/arch/i386/kernel/Makefile Thu Mar 7 18:17:39 2002 +++ b/arch/i386/kernel/Makefile Thu Mar 7 18:17:39 2002 @@ -14,7 +14,7 @@ O_TARGET := kernel.o -export-objs := mca.o mtrr.o msr.o cpuid.o microcode.o i386_ksyms.o +export-objs := mca.o mtrr.o msr.o cpuid.o microcode.o i386_ksyms.o time.o obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \ diff -Nru a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c --- a/arch/i386/kernel/apic.c Thu Mar 7 18:17:44 2002 +++ b/arch/i386/kernel/apic.c Thu Mar 7 18:17:44 2002 @@ -582,12 +582,17 @@ * Detect and enable local APICs on non-SMP boards. * Original code written by Keir Fraser. */ +int dont_enable_local_apic __initdata = 0; static int __init detect_init_APIC (void) { u32 h, l, features; extern void get_cpu_vendor(struct cpuinfo_x86*); + /* Disabled by DMI scan or kernel option? */ + if (dont_enable_local_apic) + return -1; + /* Workaround for us being called before identify_cpu(). */ get_cpu_vendor(&boot_cpu_data); @@ -903,8 +908,14 @@ static unsigned int calibration_result; +int dont_use_local_apic_timer __initdata = 0; + void __init setup_APIC_clocks (void) { + /* Disabled by DMI scan or kernel option? */ + if (dont_use_local_apic_timer) + return; + printk("Using local APIC timer interrupts.\n"); using_apic_timer = 1; diff -Nru a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c --- a/arch/i386/kernel/apm.c Thu Mar 7 18:17:37 2002 +++ b/arch/i386/kernel/apm.c Thu Mar 7 18:17:37 2002 @@ -812,7 +812,7 @@ t1 = IDLE_LEAKY_MAX; - while (need_resched()) { + while (!need_resched()) { if (use_apm_idle) { unsigned int t; diff -Nru a/arch/i386/kernel/dmi_scan.c b/arch/i386/kernel/dmi_scan.c --- a/arch/i386/kernel/dmi_scan.c Thu Mar 7 18:17:42 2002 +++ b/arch/i386/kernel/dmi_scan.c Thu Mar 7 18:17:42 2002 @@ -9,6 +9,7 @@ #include #include #include +#include unsigned long dmi_broken; int is_sony_vaio_laptop; @@ -51,7 +52,7 @@ u8 *data; int i=1; - buf = ioremap(base, len); + buf = bt_ioremap(base, len); if(buf==NULL) return -1; @@ -83,7 +84,7 @@ data+=2; i++; } - iounmap(buf); + bt_iounmap(buf, len); return 0; } @@ -155,7 +156,7 @@ return; if (dmi_ident[slot]) return; - dmi_ident[slot] = kmalloc(strlen(p)+1, GFP_KERNEL); + dmi_ident[slot] = alloc_bootmem(strlen(p)+1); if(dmi_ident[slot]) strcpy(dmi_ident[slot], p); else @@ -416,6 +417,43 @@ return 0; } +/* + * Some machines, usually laptops, can't handle an enabled local APIC. + * The symptoms include hangs or reboots when suspending or resuming, + * attaching or detaching the power cord, or entering BIOS setup screens + * through magic key sequences. + */ +static int __init local_apic_kills_bios(struct dmi_blacklist *d) +{ +#ifdef CONFIG_X86_LOCAL_APIC + extern int dont_enable_local_apic; + if (!dont_enable_local_apic) { + dont_enable_local_apic = 1; + printk(KERN_WARNING "%s with broken BIOS detected. " + "Refusing to enable the local APIC.\n", + d->ident); + } +#endif + return 0; +} + +/* + * The Intel AL440LX mainboard will hang randomly if the local APIC + * timer is running and the APM BIOS hasn't been disabled. + */ +static int __init apm_kills_local_apic_timer(struct dmi_blacklist *d) +{ +#ifdef CONFIG_X86_LOCAL_APIC + extern int dont_use_local_apic_timer; + if (apm_info.bios.version && !dont_use_local_apic_timer) { + dont_use_local_apic_timer = 1; + printk(KERN_WARNING "%s with broken BIOS detected. " + "The local APIC timer will not be used.\n", + d->ident); + } +#endif + return 0; +} /* * Simple "print if true" callback @@ -560,6 +598,25 @@ MATCH(DMI_BIOS_DATE, "09/12/00"), NO_MATCH } }, + /* Machines which have problems handling enabled local APICs */ + + { local_apic_kills_bios, "Dell Inspiron", { + MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + MATCH(DMI_PRODUCT_NAME, "Inspiron"), + NO_MATCH, NO_MATCH + } }, + + { local_apic_kills_bios, "Dell Latitude", { + MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + MATCH(DMI_PRODUCT_NAME, "Latitude"), + NO_MATCH, NO_MATCH + } }, + + { apm_kills_local_apic_timer, "Intel AL440LX", { + MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + MATCH(DMI_BOARD_NAME, "AL440LX"), + NO_MATCH, NO_MATCH } }, + /* Problem Intel 440GX bioses */ { broken_pirq, "SABR1 Bios", { /* Bad $PIR */ @@ -588,7 +645,7 @@ NO_MATCH, NO_MATCH } }, - /* Intel in disgiuse - In this case they can't hide and they don't run + /* Intel in disguise - In this case they can't hide and they don't run too well either... */ { broken_pirq, "Dell PowerEdge 8450", { /* Bad $PIR */ MATCH(DMI_PRODUCT_NAME, "Dell PowerEdge 8450"), @@ -728,12 +785,9 @@ } } -static int __init dmi_scan_machine(void) +void __init dmi_scan_machine(void) { int err = dmi_iterate(dmi_decode); if(err == 0) dmi_check_blacklist(); - return err; } - -module_init(dmi_scan_machine); diff -Nru a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S --- a/arch/i386/kernel/entry.S Thu Mar 7 18:17:40 2002 +++ b/arch/i386/kernel/entry.S Thu Mar 7 18:17:40 2002 @@ -195,7 +195,7 @@ ENTRY(ret_from_fork) -#if CONFIG_SMP +#if CONFIG_SMP || CONFIG_PREEMPT call SYMBOL_NAME(schedule_tail) #endif GET_THREAD_INFO(%ebx) @@ -716,6 +716,7 @@ .long SYMBOL_NAME(sys_lremovexattr) .long SYMBOL_NAME(sys_fremovexattr) .long SYMBOL_NAME(sys_tkill) + .long SYMBOL_NAME(sys_sendfile64) .rept NR_syscalls-(.-sys_call_table)/4 .long SYMBOL_NAME(sys_ni_syscall) diff -Nru a/arch/i386/kernel/i8259.c b/arch/i386/kernel/i8259.c --- a/arch/i386/kernel/i8259.c Thu Mar 7 18:17:46 2002 +++ b/arch/i386/kernel/i8259.c Thu Mar 7 18:17:46 2002 @@ -79,7 +79,6 @@ * through the ICC by us (IPIs) */ #ifdef CONFIG_SMP -BUILD_SMP_INTERRUPT(task_migration_interrupt,TASK_MIGRATION_VECTOR) BUILD_SMP_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR) BUILD_SMP_INTERRUPT(invalidate_interrupt,INVALIDATE_TLB_VECTOR) BUILD_SMP_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR) @@ -473,9 +472,6 @@ * IPI, driven by wakeup. */ set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt); - - /* IPI for task migration */ - set_intr_gate(TASK_MIGRATION_VECTOR, task_migration_interrupt); /* IPI for invalidation */ set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt); diff -Nru a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c --- a/arch/i386/kernel/io_apic.c Thu Mar 7 18:17:41 2002 +++ b/arch/i386/kernel/io_apic.c Thu Mar 7 18:17:41 2002 @@ -67,7 +67,7 @@ * shared ISA-space IRQs, so we have to support them. We are super * fast in the common case, and fast for shared ISA-space IRQs. */ -static void add_pin_to_irq(unsigned int irq, int apic, int pin) +static void __init add_pin_to_irq(unsigned int irq, int apic, int pin) { static int first_free_entry = NR_IRQS; struct irq_pin_list *entry = irq_2_pin + irq; @@ -85,6 +85,26 @@ entry->pin = pin; } +/* + * Reroute an IRQ to a different pin. + */ +static void __init replace_pin_at_irq(unsigned int irq, + int oldapic, int oldpin, + int newapic, int newpin) +{ + struct irq_pin_list *entry = irq_2_pin + irq; + + while (1) { + if (entry->apic == oldapic && entry->pin == oldpin) { + entry->apic = newapic; + entry->pin = newpin; + } + if (!entry->next) + break; + entry = irq_2_pin + entry->next; + } +} + #define __DO_ACTION(R, ACTION, FINAL) \ \ { \ @@ -1533,6 +1553,10 @@ setup_ExtINT_IRQ0_pin(pin2, vector); if (timer_irq_works()) { printk("works.\n"); + if (pin1 != -1) + replace_pin_at_irq(0, 0, pin1, 0, pin2); + else + add_pin_to_irq(0, 0, pin2); if (nmi_watchdog == NMI_IO_APIC) { setup_nmi(); check_nmi_watchdog(); diff -Nru a/arch/i386/kernel/mpparse.c b/arch/i386/kernel/mpparse.c --- a/arch/i386/kernel/mpparse.c Thu Mar 7 18:17:41 2002 +++ b/arch/i386/kernel/mpparse.c Thu Mar 7 18:17:41 2002 @@ -37,6 +37,8 @@ int apic_version [MAX_APICS]; int mp_bus_id_to_type [MAX_MP_BUSSES]; int mp_bus_id_to_node [MAX_MP_BUSSES]; +int mp_bus_id_to_local [MAX_MP_BUSSES]; +int quad_local_to_mp_bus_id [NR_CPUS/4][4]; int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { [0 ... MAX_MP_BUSSES-1] = -1 }; int mp_current_pci_id; @@ -241,13 +243,17 @@ static void __init MP_bus_info (struct mpc_config_bus *m) { char str[7]; + int quad; memcpy(str, m->mpc_bustype, 6); str[6] = 0; if (clustered_apic_mode) { - mp_bus_id_to_node[m->mpc_busid] = translation_table[mpc_record]->trans_quad; - printk("Bus #%d is %s (node %d)\n", m->mpc_busid, str, mp_bus_id_to_node[m->mpc_busid]); + quad = translation_table[mpc_record]->trans_quad; + mp_bus_id_to_node[m->mpc_busid] = quad; + mp_bus_id_to_local[m->mpc_busid] = translation_table[mpc_record]->trans_local; + quad_local_to_mp_bus_id[quad][translation_table[mpc_record]->trans_local] = m->mpc_busid; + printk("Bus #%d is %s (node %d)\n", m->mpc_busid, str, quad); } else { Dprintk("Bus #%d is %s\n", m->mpc_busid, str); } @@ -324,13 +330,14 @@ static void __init MP_translation_info (struct mpc_config_translation *m) { - printk("Translation: record %d, type %d, quad %d, global %d, local %d\n", mpc_record, m->trans_type, - m->trans_quad, m->trans_global, m->trans_local); + printk("Translation: record %d, type %d, quad %d, global %d, local %d\n", mpc_record, m->trans_type, m->trans_quad, m->trans_global, m->trans_local); if (mpc_record >= MAX_MPC_ENTRY) printk("MAX_MPC_ENTRY exceeded!\n"); else translation_table[mpc_record] = m; /* stash this for later */ + if (m->trans_quad+1 > numnodes) + numnodes = m->trans_quad+1; } /* @@ -494,10 +501,6 @@ } } ++mpc_record; - } - if (clustered_apic_mode && nr_ioapics > 2) { - /* don't initialise IO apics on secondary quads */ - nr_ioapics = 2; } if (!num_processors) printk(KERN_ERR "SMP mptable: no processors registered!\n"); diff -Nru a/arch/i386/kernel/pci-pc.c b/arch/i386/kernel/pci-pc.c --- a/arch/i386/kernel/pci-pc.c Thu Mar 7 18:17:43 2002 +++ b/arch/i386/kernel/pci-pc.c Thu Mar 7 18:17:43 2002 @@ -14,6 +14,7 @@ #include #include +#include #include "pci-i386.h" @@ -26,6 +27,16 @@ int (*pci_config_read)(int seg, int bus, int dev, int fn, int reg, int len, u32 *value) = NULL; int (*pci_config_write)(int seg, int bus, int dev, int fn, int reg, int len, u32 value) = NULL; +#ifdef CONFIG_MULTIQUAD +#define BUS2QUAD(global) (mp_bus_id_to_node[global]) +#define BUS2LOCAL(global) (mp_bus_id_to_local[global]) +#define QUADLOCAL2BUS(quad,local) (quad_local_to_mp_bus_id[quad][local]) +#else +#define BUS2QUAD(global) (0) +#define BUS2LOCAL(global) (global) +#define QUADLOCAL2BUS(quad,local) (local) +#endif + /* * This interrupt-safe spinlock protects all accesses to PCI * configuration space. @@ -39,10 +50,71 @@ #ifdef CONFIG_PCI_DIRECT +#ifdef CONFIG_MULTIQUAD +#define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \ + (0x80000000 | (BUS2LOCAL(bus) << 16) | (dev << 11) | (fn << 8) | (reg & ~3)) + +static int pci_conf1_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) /* CONFIG_MULTIQUAD */ +{ + unsigned long flags; + + if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -EINVAL; + + spin_lock_irqsave(&pci_config_lock, flags); + + outl_quad(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8, BUS2QUAD(bus)); + + switch (len) { + case 1: + *value = inb_quad(0xCFC + (reg & 3), BUS2QUAD(bus)); + break; + case 2: + *value = inw_quad(0xCFC + (reg & 2), BUS2QUAD(bus)); + break; + case 4: + *value = inl_quad(0xCFC, BUS2QUAD(bus)); + break; + } + + spin_unlock_irqrestore(&pci_config_lock, flags); + + return 0; +} + +static int pci_conf1_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) /* CONFIG_MULTIQUAD */ +{ + unsigned long flags; + + if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -EINVAL; + + spin_lock_irqsave(&pci_config_lock, flags); + + outl_quad(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8, BUS2QUAD(bus)); + + switch (len) { + case 1: + outb_quad((u8)value, 0xCFC + (reg & 3), BUS2QUAD(bus)); + break; + case 2: + outw_quad((u16)value, 0xCFC + (reg & 2), BUS2QUAD(bus)); + break; + case 4: + outl_quad((u32)value, 0xCFC, BUS2QUAD(bus)); + break; + } + + spin_unlock_irqrestore(&pci_config_lock, flags); + + return 0; +} + +#else /* !CONFIG_MULTIQUAD */ #define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \ (0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3)) -static int pci_conf1_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) +static int pci_conf1_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) /* !CONFIG_MULTIQUAD */ { unsigned long flags; @@ -70,7 +142,7 @@ return 0; } -static int pci_conf1_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) +static int pci_conf1_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) /* !CONFIG_MULTIQUAD */ { unsigned long flags; @@ -98,6 +170,8 @@ return 0; } +#endif /* CONFIG_MULTIQUAD */ + #undef PCI_CONF1_ADDRESS static int pci_conf1_read_config_byte(struct pci_dev *dev, int where, u8 *value) @@ -1017,6 +1091,8 @@ */ int pxb, reg; u8 busno, suba, subb; + int quad = BUS2QUAD(d->bus->number); + printk("PCI: Searching for i450NX host bridges on %s\n", d->slot_name); reg = 0xd0; for(pxb=0; pxb<2; pxb++) { @@ -1025,9 +1101,9 @@ pci_read_config_byte(d, reg++, &subb); DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb); if (busno) - pci_scan_bus(busno, pci_root_ops, NULL); /* Bus A */ + pci_scan_bus(QUADLOCAL2BUS(quad,busno), pci_root_ops, NULL); /* Bus A */ if (suba < subb) - pci_scan_bus(suba+1, pci_root_ops, NULL); /* Bus B */ + pci_scan_bus(QUADLOCAL2BUS(quad,suba+1), pci_root_ops, NULL); /* Bus B */ } pcibios_last_bus = -1; } @@ -1199,6 +1275,8 @@ void __init pcibios_init(void) { + int quad; + if (!pci_root_ops) pcibios_config_init(); if (!pci_root_ops) { @@ -1208,6 +1286,14 @@ printk("PCI: Probing PCI hardware\n"); pci_root_bus = pci_scan_bus(0, pci_root_ops, NULL); + if (clustered_apic_mode && (numnodes > 1)) { + for (quad = 1; quad < numnodes; ++quad) { + printk("Scanning PCI bus %d for quad %d\n", + QUADLOCAL2BUS(quad,0), quad); + pci_scan_bus(QUADLOCAL2BUS(quad,0), + pci_root_ops, NULL); + } + } pcibios_irq_init(); pcibios_fixup_peer_bridges(); diff -Nru a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c --- a/arch/i386/kernel/setup.c Thu Mar 7 18:17:44 2002 +++ b/arch/i386/kernel/setup.c Thu Mar 7 18:17:44 2002 @@ -99,8 +99,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -113,6 +113,7 @@ #include #include #include + /* * Machine setup.. */ @@ -158,6 +159,7 @@ unsigned char aux_device_present; extern void mcheck_init(struct cpuinfo_x86 *c); +extern void dmi_scan_machine(void); extern int root_mountflags; extern char _text, _etext, _edata, _end; extern int blk_nohighio; @@ -907,7 +909,6 @@ */ if (smp_found_config) get_smp_config(); - init_apic_mappings(); #endif @@ -959,6 +960,7 @@ conswitchp = &dummy_con; #endif #endif + dmi_scan_machine(); } static int cachesize_override __initdata = -1; diff -Nru a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c --- a/arch/i386/kernel/smp.c Thu Mar 7 18:17:38 2002 +++ b/arch/i386/kernel/smp.c Thu Mar 7 18:17:38 2002 @@ -485,35 +485,6 @@ do_flush_tlb_all_local(); } -static spinlock_t migration_lock = SPIN_LOCK_UNLOCKED; -static task_t *new_task; - -/* - * This function sends a 'task migration' IPI to another CPU. - * Must be called from syscall contexts, with interrupts *enabled*. - */ -void smp_migrate_task(int cpu, task_t *p) -{ - /* - * The target CPU will unlock the migration spinlock: - */ - _raw_spin_lock(&migration_lock); - new_task = p; - send_IPI_mask(1 << cpu, TASK_MIGRATION_VECTOR); -} - -/* - * Task migration callback. - */ -asmlinkage void smp_task_migration_interrupt(void) -{ - task_t *p; - - ack_APIC_irq(); - p = new_task; - _raw_spin_unlock(&migration_lock); - sched_task_migrated(p); -} /* * this function sends a 'reschedule' IPI to another CPU. * it goes straight through and wastes no time serializing diff -Nru a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c --- a/arch/i386/kernel/smpboot.c Thu Mar 7 18:17:44 2002 +++ b/arch/i386/kernel/smpboot.c Thu Mar 7 18:17:44 2002 @@ -240,7 +240,7 @@ * ^---- (this multiplication can overflow) */ -static unsigned long long div64 (unsigned long long a, unsigned long b0) +static unsigned long long __init div64 (unsigned long long a, unsigned long b0) { unsigned int a1, a2; unsigned long long res; @@ -1012,11 +1012,14 @@ { int apicid, cpu, bit; - if (clustered_apic_mode) { - /* remap the 1st quad's 256k range for cross-quad I/O */ - xquad_portio = ioremap (XQUAD_PORTIO_BASE, XQUAD_PORTIO_LEN); - printk("Cross quad port I/O vaddr 0x%08lx, len %08lx\n", - (u_long) xquad_portio, (u_long) XQUAD_PORTIO_LEN); + if (clustered_apic_mode && (numnodes > 1)) { + printk("Remapping cross-quad port I/O for %d quads\n", + numnodes); + printk("xquad_portio vaddr 0x%08lx, len %08lx\n", + (u_long) xquad_portio, + (u_long) numnodes * XQUAD_PORTIO_LEN); + xquad_portio = ioremap (XQUAD_PORTIO_BASE, + numnodes * XQUAD_PORTIO_LEN); } #ifdef CONFIG_MTRR diff -Nru a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c --- a/arch/i386/kernel/time.c Thu Mar 7 18:17:44 2002 +++ b/arch/i386/kernel/time.c Thu Mar 7 18:17:44 2002 @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -115,6 +116,7 @@ #define TICK_SIZE tick spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED; +EXPORT_SYMBOL(i8253_lock); extern spinlock_t i8259A_lock; diff -Nru a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c --- a/arch/i386/kernel/traps.c Thu Mar 7 18:17:39 2002 +++ b/arch/i386/kernel/traps.c Thu Mar 7 18:17:39 2002 @@ -557,6 +557,8 @@ * allowing programs to debug themselves without the ptrace() * interface. */ + if ((regs->xcs & 3) == 0) + goto clear_TF; if ((tsk->ptrace & (PT_DTRACE|PT_PTRACED)) == PT_DTRACE) goto clear_TF; } @@ -972,6 +974,10 @@ #ifdef CONFIG_EISA if (isa_readl(0x0FFFD9) == 'E'+('I'<<8)+('S'<<16)+('A'<<24)) EISA_bus = 1; +#endif + +#ifdef CONFIG_X86_LOCAL_APIC + init_apic_mappings(); #endif set_trap_gate(0,÷_error); diff -Nru a/arch/i386/mm/init.c b/arch/i386/mm/init.c --- a/arch/i386/mm/init.c Thu Mar 7 18:17:40 2002 +++ b/arch/i386/mm/init.c Thu Mar 7 18:17:40 2002 @@ -105,7 +105,6 @@ static inline void set_pte_phys (unsigned long vaddr, unsigned long phys, pgprot_t flags) { - pgprot_t prot; pgd_t *pgd; pmd_t *pmd; pte_t *pte; @@ -121,10 +120,8 @@ return; } pte = pte_offset_kernel(pmd, vaddr); - if (pte_val(*pte)) - pte_ERROR(*pte); - pgprot_val(prot) = pgprot_val(PAGE_KERNEL) | pgprot_val(flags); - set_pte(pte, mk_pte_phys(phys, prot)); + /* stored as-is, to permit clearing entries */ + set_pte(pte, mk_pte_phys(phys, flags)); /* * It's enough to flush this one mapping. diff -Nru a/arch/i386/mm/ioremap.c b/arch/i386/mm/ioremap.c --- a/arch/i386/mm/ioremap.c Thu Mar 7 18:17:40 2002 +++ b/arch/i386/mm/ioremap.c Thu Mar 7 18:17:40 2002 @@ -161,3 +161,69 @@ if (addr > high_memory) return vfree((void *) (PAGE_MASK & (unsigned long) addr)); } + +#include +void __init *bt_ioremap(unsigned long phys_addr, unsigned long size) +{ + unsigned long offset, last_addr; + unsigned int nrpages; + enum fixed_addresses idx; + + /* Don't allow wraparound or zero size */ + last_addr = phys_addr + size - 1; + if (!size || last_addr < phys_addr) + return NULL; + + /* + * Don't remap the low PCI/ISA area, it's always mapped.. + */ + if (phys_addr >= 0xA0000 && last_addr < 0x100000) + return phys_to_virt(phys_addr); + + /* + * Mappings have to be page-aligned + */ + offset = phys_addr & ~PAGE_MASK; + phys_addr &= PAGE_MASK; + size = PAGE_ALIGN(last_addr) - phys_addr; + + /* + * Mappings have to fit in the FIX_BTMAP area. + */ + nrpages = size >> PAGE_SHIFT; + if (nrpages > NR_FIX_BTMAPS) + return NULL; + + /* + * Ok, go for it.. + */ + idx = FIX_BTMAP_BEGIN; + while (nrpages > 0) { + set_fixmap(idx, phys_addr); + phys_addr += PAGE_SIZE; + --idx; + --nrpages; + } + return (void*) (offset + fix_to_virt(FIX_BTMAP_BEGIN)); +} + +void __init bt_iounmap(void *addr, unsigned long size) +{ + unsigned long virt_addr; + unsigned long offset; + unsigned int nrpages; + enum fixed_addresses idx; + + virt_addr = (unsigned long)addr; + if (virt_addr < fix_to_virt(FIX_BTMAP_BEGIN)) + return; + offset = virt_addr & ~PAGE_MASK; + nrpages = PAGE_ALIGN(offset + size - 1) >> PAGE_SHIFT; + + idx = FIX_BTMAP_BEGIN; + while (nrpages > 0) { + __set_fixmap(idx, 0, __pgprot(0)); + --idx; + --nrpages; + } +} diff -Nru a/arch/i386/vmlinux.lds b/arch/i386/vmlinux.lds --- a/arch/i386/vmlinux.lds Thu Mar 7 18:17:39 2002 +++ b/arch/i386/vmlinux.lds Thu Mar 7 18:17:39 2002 @@ -57,6 +57,10 @@ *(.initcall7.init) } __initcall_end = .; + . = ALIGN(32); + __per_cpu_start = .; + .data.percpu : { *(.data.percpu) } + __per_cpu_end = .; . = ALIGN(4096); __init_end = .; diff -Nru a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c --- a/arch/ia64/kernel/perfmon.c Thu Mar 7 18:17:42 2002 +++ b/arch/ia64/kernel/perfmon.c Thu Mar 7 18:17:42 2002 @@ -332,35 +332,8 @@ return ia64_get_itc(); } -/* 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)); - } - } - } - DBprintk(("uv2kva(%lx-->%lx)\n", 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. + * This is used when initializing the contents of the area. */ static inline unsigned long kvirt_to_pa(unsigned long adr) @@ -374,19 +347,15 @@ rvmalloc(unsigned long size) { void *mem; - unsigned long adr, page; + unsigned long adr; - /* XXX: may have to revisit this part because - * vmalloc() does not necessarily return a page-aligned buffer. - * This maybe a security problem when mapped at user level - */ + size=PAGE_ALIGN(size); mem=vmalloc(size); if (mem) { 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))); + mem_map_reserve(vmalloc_to_page((void *)adr)); adr+=PAGE_SIZE; size-=PAGE_SIZE; } @@ -397,13 +366,12 @@ static void rvfree(void *mem, unsigned long size) { - unsigned long adr, page; + unsigned long adr; if (mem) { adr=(unsigned long) mem; - while (size > 0) { - page = kvirt_to_pa(adr); - mem_map_unreserve(virt_to_page(__va(page))); + while ((long) size > 0) { + mem_map_unreserve(vmalloc_to_page((void *)adr)); adr+=PAGE_SIZE; size-=PAGE_SIZE; } @@ -515,7 +483,6 @@ vma->vm_file = NULL; vma->vm_raend = 0; - /* XXX: see rvmalloc() for page alignment problem */ smpl_buf = rvmalloc(size); if (smpl_buf == NULL) goto no_buffer; diff -Nru a/arch/ia64/sn/io/pci_bus_cvlink.c b/arch/ia64/sn/io/pci_bus_cvlink.c --- a/arch/ia64/sn/io/pci_bus_cvlink.c Thu Mar 7 18:17:40 2002 +++ b/arch/ia64/sn/io/pci_bus_cvlink.c Thu Mar 7 18:17:40 2002 @@ -112,9 +112,9 @@ pci_bus = pci_bus_to_vertex(busnum); if (!pci_bus) { /* - * During probing, the Linux pci code invents non existant + * During probing, the Linux pci code invents non-existent * bus numbers and pci_dev structures and tries to access - * them to determine existance. Don't crib during probing. + * them to determine existence. Don't crib during probing. */ if (done_probing) printk("devfn_to_vertex: Invalid bus number %d given.\n", busnum); diff -Nru a/arch/mips64/sgi-ip27/ip27-rtc.c b/arch/mips64/sgi-ip27/ip27-rtc.c --- a/arch/mips64/sgi-ip27/ip27-rtc.c Thu Mar 7 18:17:44 2002 +++ b/arch/mips64/sgi-ip27/ip27-rtc.c Thu Mar 7 18:17:44 2002 @@ -201,7 +201,8 @@ KL_CONFIG_CH_CONS_INFO(nid)->memory_base + IOC3_BYTEBUS_DEV0; printk(KERN_INFO "Real Time Clock Driver v%s\n", RTC_VERSION); - misc_register(&rtc_dev); + if (misc_register(&rtc_dev)) + return -ENODEV; create_proc_read_entry ("rtc", 0, NULL, rtc_read_proc, NULL); save_flags(flags); diff -Nru a/arch/ppc/boot/ld.script b/arch/ppc/boot/ld.script --- a/arch/ppc/boot/ld.script Thu Mar 7 18:17:38 2002 +++ b/arch/ppc/boot/ld.script Thu Mar 7 18:17:38 2002 @@ -44,6 +44,7 @@ { *(.data) *(.data1) + *(.data.boot) *(.sdata) *(.sdata2) *(.got.plt) *(.got) diff -Nru a/arch/ppc/boot/simple/misc-embedded.c b/arch/ppc/boot/simple/misc-embedded.c --- a/arch/ppc/boot/simple/misc-embedded.c Thu Mar 7 18:17:39 2002 +++ b/arch/ppc/boot/simple/misc-embedded.c Thu Mar 7 18:17:39 2002 @@ -59,7 +59,9 @@ /* Serial port to use. */ unsigned long com_port; -bd_t hold_resid_buf; +/* We need to make sure that this is before the images to ensure + * that it's in a mapped location. - Tom */ +bd_t hold_resid_buf __attribute__ ((__section__ (".data.boot"))); bd_t *hold_residual = &hold_resid_buf; extern unsigned long serial_init(int chan, bd_t *bp); diff -Nru a/arch/ppc/config.in b/arch/ppc/config.in --- a/arch/ppc/config.in Thu Mar 7 18:17:36 2002 +++ b/arch/ppc/config.in Thu Mar 7 18:17:36 2002 @@ -258,8 +258,9 @@ mainmenu_option next_comment comment 'General setup' -bool 'Prompt for advanced kernel configuration options' CONFIG_ADVANCED_OPTIONS bool 'High memory support' CONFIG_HIGHMEM +dep_bool ' Support for PTEs in high memory' CONFIG_HIGHPTE $CONFIG_HIGHMEM +bool 'Prompt for advanced kernel configuration options' CONFIG_ADVANCED_OPTIONS if [ "$CONFIG_ADVANCED_OPTIONS" = "y" ]; then if [ "$CONFIG_HIGHMEM" = "y" ]; then bool " Set high memory pool address" CONFIG_HIGHMEM_START_BOOL diff -Nru a/arch/ppc/iSeries/rtc.c b/arch/ppc/iSeries/rtc.c --- a/arch/ppc/iSeries/rtc.c Thu Mar 7 18:17:44 2002 +++ b/arch/ppc/iSeries/rtc.c Thu Mar 7 18:17:44 2002 @@ -192,7 +192,8 @@ static int __init rtc_init(void) { - misc_register(&rtc_dev); + if (misc_register(&rtc_dev)) + return -ENODEV; create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); printk(KERN_INFO "iSeries Real Time Clock Driver v" RTC_VERSION "\n"); diff -Nru a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S --- a/arch/ppc/kernel/entry.S Thu Mar 7 18:17:41 2002 +++ b/arch/ppc/kernel/entry.S Thu Mar 7 18:17:41 2002 @@ -288,15 +288,12 @@ */ _GLOBAL(_switch) stwu r1,-INT_FRAME_SIZE(r1) - stw r0,GPR0(r1) - lwz r0,0(r1) - stw r0,GPR1(r1) + mflr r0 + stw r0,INT_FRAME_SIZE+4(r1) /* r3-r13 are caller saved -- Cort */ - SAVE_GPR(2, r1) SAVE_8GPRS(14, r1) SAVE_10GPRS(22, r1) - mflr r20 /* Return to switch caller */ - stw r20,INT_FRAME_SIZE+4(r1) + stw r0,_NIP(r1) /* Return to switch caller */ mfmsr r22 li r0,MSR_FP /* Disable floating-point */ #ifdef CONFIG_ALTIVEC @@ -309,17 +306,9 @@ andc r22,r22,r0 mtmsr r22 isync -1: stw r20,_NIP(r1) - stw r22,_MSR(r1) - stw r20,_LINK(r1) +1: stw r22,_MSR(r1) mfcr r20 - mfctr r22 - mfspr r23,XER stw r20,_CCR(r1) - stw r22,_CTR(r1) - stw r23,_XER(r1) - li r0,0x0ff0 - stw r0,TRAP(r1) stw r1,KSP(r3) /* Set old stack pointer */ tophys(r0,r4) @@ -343,7 +332,7 @@ .globl ret_from_fork ret_from_fork: -#ifdef CONFIG_SMP +#if CONFIG_SMP || CONFIG_PREEMPT bl schedule_tail #endif rlwinm r3,r1,0,0,18 diff -Nru a/arch/ppc/kernel/iSeries_head.S b/arch/ppc/kernel/iSeries_head.S --- a/arch/ppc/kernel/iSeries_head.S Thu Mar 7 18:17:41 2002 +++ b/arch/ppc/kernel/iSeries_head.S Thu Mar 7 18:17:41 2002 @@ -839,7 +839,7 @@ /* * We hard enable here (but first soft disable) so that the hash_page - * code can spin on the hash_table_lock without problem on a shared + * code can spin on the mmu_hash_lock without problem on a shared * processor */ li r0,0 diff -Nru a/arch/ppc/kernel/iSeries_misc.S b/arch/ppc/kernel/iSeries_misc.S --- a/arch/ppc/kernel/iSeries_misc.S Thu Mar 7 18:17:37 2002 +++ b/arch/ppc/kernel/iSeries_misc.S Thu Mar 7 18:17:37 2002 @@ -29,7 +29,7 @@ .align 5 #ifdef CONFIG_SMP - .comm hash_table_lock,4 + .comm mmu_hash_lock,4 #endif /* CONFIG_SMP */ _GLOBAL(is_msr_enabled) diff -Nru a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c --- a/arch/ppc/kernel/idle.c Thu Mar 7 18:17:37 2002 +++ b/arch/ppc/kernel/idle.c Thu Mar 7 18:17:37 2002 @@ -47,17 +47,10 @@ #define run_light_on(x) do { } while (0) #endif /* CONFIG_PPC_ISERIES */ -void zero_paged(void); void power_save(void); -unsigned long zero_paged_on = 0; -unsigned long powersave_nap = 0; - -unsigned long *zero_cache; /* head linked list of pre-zero'd pages */ -atomic_t zerototal; /* # pages zero'd over time */ -atomic_t zeropage_hits; /* # zero'd pages request that we've done */ -atomic_t zero_sz; /* # currently pre-zero'd pages */ -atomic_t zeropage_calls; /* # zero'd pages request that've been made */ +unsigned long zero_paged_on; +unsigned long powersave_nap; int idled(void) { @@ -92,7 +85,6 @@ if (need_resched()) { run_light_on(1); schedule(); - check_pgt_cache(); } #ifdef CONFIG_PPC_ISERIES else { @@ -114,141 +106,6 @@ idled(); return 0; } - -#if 0 -/* - * Returns a pre-zero'd page from the list otherwise returns - * NULL. - */ -unsigned long get_zero_page_fast(void) -{ - unsigned long page = 0; - - atomic_inc(&zero_cache_calls); - if ( zero_quicklist ) - { - /* atomically remove this page from the list */ - register unsigned long tmp; - asm ( "101:lwarx %1,0,%3\n" /* reserve zero_cache */ - " lwz %0,0(%1)\n" /* get next -- new zero_cache */ - PPC405_ERR77(0,%3) - " stwcx. %0,0,%3\n" /* update zero_cache */ - " bne- 101b\n" /* if lost reservation try again */ - : "=&r" (tmp), "=&r" (page), "+m" (zero_cache) - : "r" (&zero_quicklist) - : "cc" ); -#ifdef CONFIG_SMP - /* if another cpu beat us above this can happen -- Cort */ - if ( page == 0 ) - return 0; -#endif /* CONFIG_SMP */ - /* we can update zerocount after the fact since it is not - * used for anything but control of a loop which doesn't - * matter since it won't affect anything if it zeros one - * less page -- Cort - */ - atomic_inc((atomic_t *)&zero_cache_hits); - atomic_dec((atomic_t *)&zero_cache_sz); - - /* zero out the pointer to next in the page */ - *(unsigned long *)page = 0; - return page; - } - return 0; -} - -/* - * Experimental stuff to zero out pages in the idle task - * to speed up get_free_pages(). Zero's out pages until - * we've reached the limit of zero'd pages. We handle - * reschedule()'s in here so when we return we know we've - * zero'd all we need to for now. - */ -int zero_cache_water[2] = { 25, 96 }; /* high and low water marks for zero cache */ -void zero_paged(void) -{ - unsigned long pageptr = 0; /* current page being zero'd */ - unsigned long bytecount = 0; - register unsigned long tmp; - pte_t *pte; - - if ( atomic_read(&zero_cache_sz) >= zero_cache_water[0] ) - return; - while ( (atomic_read(&zero_cache_sz) < zero_cache_water[1]) && !need_resched() ) - { - /* - * Mark a page as reserved so we can mess with it - * If we're interrupted we keep this page and our place in it - * since we validly hold it and it's reserved for us. - */ - pageptr = __get_free_pages(GFP_ATOMIC, 0); - if ( !pageptr ) - return; - - cond_resched(); - - /* - * Make the page no cache so we don't blow our cache with 0's - */ - pte = find_pte(&init_mm, pageptr); - if ( !pte ) - { - printk("pte NULL in zero_paged()\n"); - return; - } - - pte_uncache(*pte); - flush_tlb_page(find_vma(&init_mm,pageptr),pageptr); - /* - * Important here to not take time away from real processes. - */ - for ( bytecount = 0; bytecount < PAGE_SIZE ; bytecount += 4 ) - { - cond_resched(); - *(unsigned long *)(bytecount + pageptr) = 0; - } - - /* - * If we finished zero-ing out a page add this page to - * the zero_cache atomically -- we can't use - * down/up since we can't sleep in idle. - * Disabling interrupts is also a bad idea since we would - * steal time away from real processes. - * We can also have several zero_paged's running - * on different processors so we can't interfere with them. - * So we update the list atomically without locking it. - * -- Cort - */ - - /* turn cache on for this page */ - pte_cache(*pte); - flush_tlb_page(find_vma(&init_mm,pageptr),pageptr); - /* atomically add this page to the list */ - asm ( "101:lwarx %0,0,%2\n" /* reserve zero_cache */ - " stw %0,0(%3)\n" /* update *pageptr */ -#ifdef CONFIG_SMP - " sync\n" /* let store settle */ -#endif - PPC405_ERR77(0,%2) - " stwcx. %3,0,%2\n" /* update zero_cache in mem */ - " bne- 101b\n" /* if lost reservation try again */ - : "=&r" (tmp), "+m" (zero_quicklist) - : "r" (&zero_quicklist), "r" (pageptr) - : "cc" ); - /* - * This variable is used in the above loop and nowhere - * else so the worst that could happen is we would - * zero out one more or one less page than we want - * per processor on the machine. This is because - * we could add our page to the list but not have - * zerocount updated yet when another processor - * reads it. -- Cort - */ - atomic_inc((atomic_t *)&zero_cache_sz); - atomic_inc((atomic_t *)&zero_cache_total); - } -} -#endif /* 0 */ void power_save(void) { diff -Nru a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S --- a/arch/ppc/kernel/misc.S Thu Mar 7 18:17:42 2002 +++ b/arch/ppc/kernel/misc.S Thu Mar 7 18:17:42 2002 @@ -369,34 +369,39 @@ isync #else #if defined(CONFIG_SMP) + rlwinm r8,r1,0,0,18 + lwz r8,TI_CPU(r8) + oris r8,r8,10 mfmsr r10 SYNC rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ + rlwinm r0,r0,0,28,26 /* clear DR */ mtmsr r0 - SYNC - lis r9,hash_table_lock@h - ori r9,r9,hash_table_lock@l - rlwinm r8,r1,0,0,18 - lwz r8,TI_CPU(r8) - oris r8,r8,10 + SYNC_601 + isync + lis r9,mmu_hash_lock@h + ori r9,r9,mmu_hash_lock@l + tophys(r9,r9) 10: lwarx r7,0,r9 cmpi 0,r7,0 bne- 10b - /* No 405 Erratum 77 fix needed here, because 4xx can't do SMP */ stwcx. r8,0,r9 bne- 10b -#endif /* CONFIG_SMP */ sync tlbia sync -#ifdef CONFIG_SMP TLBSYNC li r0,0 - stw r0,0(r9) /* clear hash_table_lock */ + stw r0,0(r9) /* clear mmu_hash_lock */ mtmsr r10 - SYNC -#endif -#endif + SYNC_601 + isync +#else /* CONFIG_SMP */ + sync + tlbia + sync +#endif /* CONFIG_SMP */ +#endif /* CONFIG_4xx */ blr /* @@ -415,33 +420,37 @@ 10: #else #if defined(CONFIG_SMP) + rlwinm r8,r1,0,0,18 + lwz r8,TI_CPU(r8) + oris r8,r8,11 mfmsr r10 SYNC rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ + rlwinm r0,r0,0,28,26 /* clear DR */ mtmsr r0 - SYNC - lis r9,hash_table_lock@h - ori r9,r9,hash_table_lock@l - rlwinm r8,r1,0,0,18 - lwz r8,TI_CPU(r8) - oris r8,r8,11 + SYNC_601 + isync + lis r9,mmu_hash_lock@h + ori r9,r9,mmu_hash_lock@l + tophys(r9,r9) 10: lwarx r7,0,r9 cmpi 0,r7,0 bne- 10b - PPC405_ERR77(0,r9) stwcx. r8,0,r9 bne- 10b eieio -#endif /* CONFIG_SMP */ tlbie r3 sync -#ifdef CONFIG_SMP TLBSYNC li r0,0 - stw r0,0(r9) /* clear hash_table_lock */ + stw r0,0(r9) /* clear mmu_hash_lock */ mtmsr r10 - SYNC -#endif + SYNC_601 + isync +#else /* CONFIG_SMP */ + tlbie r3 + sync +#endif /* CONFIG_SMP */ #endif /* CONFIG_4xx */ blr @@ -627,6 +636,40 @@ addi r6,r6,L1_CACHE_LINE_SIZE bdnz 1b sync + isync + blr + +/* + * Flush a particular page from the data cache to RAM, identified + * by its physical address. We turn off the MMU so we can just use + * the physical address (this may be a highmem page without a kernel + * mapping). + * + * void __flush_dcache_icache_phys(unsigned long physaddr) + */ +_GLOBAL(__flush_dcache_icache_phys) + mfspr r5,PVR + rlwinm r5,r5,16,16,31 + cmpi 0,r5,1 + beqlr /* for 601, do nothing */ + mfmsr r10 + rlwinm r0,r10,0,28,26 /* clear DR */ + mtmsr r0 + isync + rlwinm r3,r3,0,0,19 /* Get page base address */ + li r4,4096/L1_CACHE_LINE_SIZE /* Number of lines in a page */ + mtctr r4 + mr r6,r3 +0: dcbst 0,r3 /* Write line to ram */ + addi r3,r3,L1_CACHE_LINE_SIZE + bdnz 0b + sync + mtctr r4 +1: icbi 0,r6 + addi r6,r6,L1_CACHE_LINE_SIZE + bdnz 1b + sync + mtmsr r10 /* restore DR */ isync blr diff -Nru a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c --- a/arch/ppc/kernel/ppc_ksyms.c Thu Mar 7 18:17:46 2002 +++ b/arch/ppc/kernel/ppc_ksyms.c Thu Mar 7 18:17:46 2002 @@ -218,13 +218,13 @@ EXPORT_SYMBOL(__global_save_flags); EXPORT_SYMBOL(__global_restore_flags); #ifdef SPINLOCK_DEBUG -EXPORT_SYMBOL(_spin_lock); -EXPORT_SYMBOL(_spin_unlock); -EXPORT_SYMBOL(spin_trylock); -EXPORT_SYMBOL(_read_lock); -EXPORT_SYMBOL(_read_unlock); -EXPORT_SYMBOL(_write_lock); -EXPORT_SYMBOL(_write_unlock); +EXPORT_SYMBOL(_raw_spin_lock); +EXPORT_SYMBOL(_raw_spin_unlock); +EXPORT_SYMBOL(_raw_spin_trylock); +EXPORT_SYMBOL(_raw_read_lock); +EXPORT_SYMBOL(_raw_read_unlock); +EXPORT_SYMBOL(_raw_write_lock); +EXPORT_SYMBOL(_raw_write_unlock); #endif EXPORT_SYMBOL(smp_call_function); EXPORT_SYMBOL(smp_hw_index); @@ -361,7 +361,7 @@ EXPORT_SYMBOL(handle_mm_fault); /* For MOL */ EXPORT_SYMBOL_NOVERS(disarm_decr); #ifdef CONFIG_PPC_STD_MMU -EXPORT_SYMBOL(flush_hash_page); /* For MOL */ +EXPORT_SYMBOL(flush_hash_pages); /* For MOL */ extern long *intercept_table; EXPORT_SYMBOL(intercept_table); #endif diff -Nru a/arch/ppc/kernel/qspan_pci.c b/arch/ppc/kernel/qspan_pci.c --- a/arch/ppc/kernel/qspan_pci.c Thu Mar 7 18:17:39 2002 +++ b/arch/ppc/kernel/qspan_pci.c Thu Mar 7 18:17:39 2002 @@ -14,7 +14,7 @@ * PCI access and QSpan control register addresses. The selection is * further selected by a bit setting in a board control register. * Although it should happen, we disable interrupts during this operation - * to make sure some driver doesn't accidently access the PCI while + * to make sure some driver doesn't accidentally access the PCI while * we have switched the chip select. */ diff -Nru a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c --- a/arch/ppc/kernel/smp.c Thu Mar 7 18:17:46 2002 +++ b/arch/ppc/kernel/smp.c Thu Mar 7 18:17:46 2002 @@ -291,26 +291,6 @@ atomic_inc(&call_data->finished); } -/* - * Task migration callback. - */ -void smp_task_migration_interrupt(void *new_task) -{ - task_t *p; - - p = new_task; - sched_task_migrated(p); -} - -/* - * This function sends a 'task migration' IPI to another CPU. - * Must be called from syscall contexts, with interrupts *enabled*. - */ -void smp_migrate_task(int cpu, task_t *p) -{ - __smp_call_function(smp_task_migration_interrupt, p, 0, cpu); -} - void __init smp_boot_cpus(void) { int i, cpu_nr; diff -Nru a/arch/ppc/lib/locks.c b/arch/ppc/lib/locks.c --- a/arch/ppc/lib/locks.c Thu Mar 7 18:17:40 2002 +++ b/arch/ppc/lib/locks.c Thu Mar 7 18:17:40 2002 @@ -28,7 +28,7 @@ * since they may inhibit forward progress by other CPUs in getting * a lock. */ -static unsigned long __spin_trylock(volatile unsigned long *lock) +unsigned long __spin_trylock(volatile unsigned long *lock) { unsigned long ret; diff -Nru a/arch/ppc/mm/cachemap.c b/arch/ppc/mm/cachemap.c --- a/arch/ppc/mm/cachemap.c Thu Mar 7 18:17:44 2002 +++ b/arch/ppc/mm/cachemap.c Thu Mar 7 18:17:44 2002 @@ -50,8 +50,6 @@ #include #include -extern int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep); - /* This function will allocate the requested contiguous pages and * map them into the kernel's vmalloc() space. This is done so we * get unique mapping for these pages, outside of the kernel's 1:1 @@ -157,6 +155,6 @@ { unsigned long start; - start = (unsigned long)(page->virtual) + offset; + start = page_address(page) + offset; consistent_sync(start, size, direction); } diff -Nru a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c --- a/arch/ppc/mm/fault.c Thu Mar 7 18:17:41 2002 +++ b/arch/ppc/mm/fault.c Thu Mar 7 18:17:41 2002 @@ -154,6 +154,7 @@ /* Since 4xx supports per-page execute permission, * we lazily flush dcache to icache. */ + ptep = NULL; if (get_pteptr(mm, address, &ptep) && pte_present(*ptep)) { struct page *page = pte_page(*ptep); @@ -164,9 +165,12 @@ } pte_update(ptep, 0, _PAGE_HWEXEC); _tlbie(address); + pte_unmap(ptep); up_read(&mm->mmap_sem); return; } + if (ptep != NULL) + pte_unmap(ptep); #endif /* a read */ } else { @@ -289,27 +293,18 @@ struct mm_struct *mm; if (address < TASK_SIZE) - mm = current->mm; - else - mm = &init_mm; + return NULL; - dir = pgd_offset(mm, address & PAGE_MASK); + dir = pgd_offset(&init_mm, address); if (dir) { pmd = pmd_offset(dir, address & PAGE_MASK); if (pmd && pmd_present(*pmd)) { - pte = pte_offset(pmd, address & PAGE_MASK); - if (pte && pte_present(*pte)) { + pte = pte_offset_kernel(pmd, address & PAGE_MASK); + if (pte && pte_present(*pte)) return(pte); - } - } - else { - return (0); } } - else { - return (0); - } - return (0); + return NULL; } unsigned long va_to_phys(unsigned long address) @@ -334,7 +329,7 @@ if (pgd) { pmd = pmd_offset(pgd, addr & PAGE_MASK); if (pmd && pmd_present(*pmd)) { - pte = pte_offset(pmd, addr & PAGE_MASK); + pte = pte_offset_kernel(pmd, addr & PAGE_MASK); if (pte) { printk(" (0x%08lx)->(0x%08lx)->0x%08lx\n", (long)pgd, (long)pte, (long)pte_val(*pte)); @@ -375,9 +370,9 @@ if (pgd) { pmd = pmd_offset(pgd, addr & PAGE_MASK); if (pmd && pmd_present(*pmd)) { - pte = pte_offset(pmd, addr & PAGE_MASK); + pte = pte_offset_kernel(pmd, addr & PAGE_MASK); if (pte) { - retval = (int)pte_val(*pte); + retval = (int)pte_val(*pte); } } } diff -Nru a/arch/ppc/mm/hashtable.S b/arch/ppc/mm/hashtable.S --- a/arch/ppc/mm/hashtable.S Thu Mar 7 18:17:41 2002 +++ b/arch/ppc/mm/hashtable.S Thu Mar 7 18:17:41 2002 @@ -36,7 +36,7 @@ #include #ifdef CONFIG_SMP - .comm hash_table_lock,4 + .comm mmu_hash_lock,4 #endif /* CONFIG_SMP */ /* @@ -62,8 +62,8 @@ #endif tophys(r7,0) /* gets -KERNELBASE into r7 */ #ifdef CONFIG_SMP - addis r2,r7,hash_table_lock@h - ori r2,r2,hash_table_lock@l + addis r2,r7,mmu_hash_lock@h + ori r2,r2,mmu_hash_lock@l lis r0,0x0fff b 10f 11: lwz r6,0(r2) @@ -88,8 +88,8 @@ rlwimi r3,r23,32-12,29,29 /* MSR_PR -> _PAGE_USER */ 112: add r5,r5,r7 /* convert to phys addr */ rlwimi r5,r4,12,20,29 /* insert top 10 bits of address */ - lwz r5,0(r5) /* get pmd entry */ - rlwinm. r5,r5,0,0,19 /* extract address of pte page */ + lwz r2,0(r5) /* get pmd entry */ + rlwinm. r2,r2,0,0,19 /* extract address of pte page */ #ifdef CONFIG_SMP beq- hash_page_out /* return if no mapping */ #else @@ -99,7 +99,6 @@ to the address following the rfi. */ beqlr- #endif - add r2,r5,r7 /* convert to phys addr */ rlwimi r2,r4,22,20,29 /* insert next 10 bits of address */ rlwinm r0,r3,32-3,24,24 /* _PAGE_RW access -> _PAGE_DIRTY */ ori r0,r0,_PAGE_ACCESSED|_PAGE_HASHPTE @@ -142,9 +141,9 @@ #ifdef CONFIG_SMP eieio - addis r2,r7,hash_table_lock@ha + addis r2,r7,mmu_hash_lock@ha li r0,0 - stw r0,hash_table_lock@l(r2) + stw r0,mmu_hash_lock@l(r2) #endif /* Return from the exception */ @@ -174,16 +173,16 @@ #ifdef CONFIG_SMP hash_page_out: eieio - addis r2,r7,hash_table_lock@ha + addis r2,r7,mmu_hash_lock@ha li r0,0 - stw r0,hash_table_lock@l(r2) + stw r0,mmu_hash_lock@l(r2) blr #endif /* CONFIG_SMP */ /* * Add an entry for a particular page to the hash table. * - * add_hash_page(unsigned context, unsigned long va, pte_t pte) + * add_hash_page(unsigned context, unsigned long va, unsigned long pmdval) * * We assume any necessary modifications to the pte (e.g. setting * the accessed bit) have already been done and that there is actually @@ -199,31 +198,41 @@ mulli r0,r0,0x111 /* multiply by ESID skew */ add r3,r3,r0 /* note create_hpte trims to 24 bits */ +#ifdef CONFIG_SMP + rlwinm r8,r1,0,0,18 /* use cpu number to make tag */ + lwz r8,TI_CPU(r8) /* to go in mmu_hash_lock */ + oris r8,r8,12 +#endif /* CONFIG_SMP */ + /* * We disable interrupts here, even on UP, because we don't * want to race with hash_page, and because we want the * _PAGE_HASHPTE bit to be a reliable indication of whether - * the HPTE exists (or at least whether one did once). -- paulus + * the HPTE exists (or at least whether one did once). + * We also turn off the MMU for data accesses so that we + * we can't take a hash table miss (assuming the code is + * covered by a BAT). -- paulus */ mfmsr r10 SYNC rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ + rlwinm r0,r0,0,28,26 /* clear MSR_DR */ mtmsr r0 - SYNC + SYNC_601 + isync + + tophys(r7,0) #ifdef CONFIG_SMP - lis r9,hash_table_lock@h - ori r9,r9,hash_table_lock@l - rlwinm r8,r1,0,0,18 - lwz r8,TI_CPU(r8) - oris r8,r8,12 -10: lwarx r7,0,r9 - cmpi 0,r7,0 + addis r9,r7,mmu_hash_lock@ha + addi r9,r9,mmu_hash_lock@l +10: lwarx r0,0,r9 /* take the mmu_hash_lock */ + cmpi 0,r0,0 bne- 11f stwcx. r8,0,r9 beq+ 12f -11: lwz r7,0(r9) - cmpi 0,r7,0 +11: lwz r0,0(r9) + cmpi 0,r0,0 beq 10b b 11b 12: isync @@ -234,18 +243,18 @@ * If _PAGE_HASHPTE was already set, we don't replace the existing * HPTE, so we just unlock and return. */ - mr r7,r5 -1: lwarx r6,0,r7 + mr r8,r5 + rlwimi r8,r4,22,20,29 +1: lwarx r6,0,r8 andi. r0,r6,_PAGE_HASHPTE bne 9f /* if HASHPTE already set, done */ - ori r5,r6,_PAGE_ACCESSED|_PAGE_HASHPTE - stwcx. r5,0,r7 + ori r5,r6,_PAGE_HASHPTE + stwcx. r5,0,r8 bne- 1b - li r7,0 /* no address offset needed */ bl create_hpte - lis r8,htab_preloads@ha + addis r8,r7,htab_preloads@ha lwz r3,htab_preloads@l(r8) addi r3,r3,1 stw r3,htab_preloads@l(r8) @@ -254,15 +263,16 @@ #ifdef CONFIG_SMP eieio li r0,0 - stw r0,0(r9) /* clear hash_table_lock */ + stw r0,0(r9) /* clear mmu_hash_lock */ #endif + /* reenable interrupts and DR */ + mtmsr r10 + SYNC_601 + isync + lwz r0,4(r1) mtlr r0 - - /* reenable interrupts */ - mtmsr r10 - SYNC blr /* @@ -273,7 +283,7 @@ * linux PTE (before setting _PAGE_HASHPTE) and r7 contains the * offset to be added to addresses (0 if the MMU is on, * -KERNELBASE if it is off). - * On SMP, the caller should have the hash_table_lock held. + * On SMP, the caller should have the mmu_hash_lock held. * We assume that the caller has (or will) set the _PAGE_HASHPTE * bit in the linux PTE in memory. The value passed in r6 should * be the old linux PTE value; if it doesn't have _PAGE_HASHPTE set @@ -486,41 +496,73 @@ /* * Flush the entry for a particular page from the hash table. * - * flush_hash_page(unsigned context, unsigned long va, pte_t *ptep) + * flush_hash_pages(unsigned context, unsigned long va, unsigned long pmdval, + * int count) * * We assume that there is a hash table in use (Hash != 0). */ -_GLOBAL(flush_hash_page) - /* Convert context and va to VSID */ - mulli r3,r3,897*16 /* multiply context by context skew */ - rlwinm r0,r4,4,28,31 /* get ESID (top 4 bits of va) */ - mulli r0,r0,0x111 /* multiply by ESID skew */ - add r3,r3,r0 /* note code below trims to 24 bits */ +_GLOBAL(flush_hash_pages) + tophys(r7,0) /* * We disable interrupts here, even on UP, because we want * the _PAGE_HASHPTE bit to be a reliable indication of - * whether the HPTE exists. -- paulus + * whether the HPTE exists (or at least whether one did once). + * We also turn off the MMU for data accesses so that we + * we can't take a hash table miss (assuming the code is + * covered by a BAT). -- paulus */ mfmsr r10 - rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ SYNC + rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ + rlwinm r0,r0,0,28,26 /* clear MSR_DR */ mtmsr r0 - SYNC + SYNC_601 + isync + + /* First find a PTE in the range that has _PAGE_HASHPTE set */ + rlwimi r5,r4,22,20,29 +1: lwz r0,0(r5) + cmpwi cr1,r6,1 + andi. r0,r0,_PAGE_HASHPTE + bne 2f + ble cr1,19f + addi r4,r4,0x1000 + addi r5,r5,4 + addi r6,r6,-1 + b 1b + + /* Convert context and va to VSID */ +2: mulli r3,r3,897*16 /* multiply context by context skew */ + rlwinm r0,r4,4,28,31 /* get ESID (top 4 bits of va) */ + mulli r0,r0,0x111 /* multiply by ESID skew */ + add r3,r3,r0 /* note code below trims to 24 bits */ + + /* Construct the high word of the PPC-style PTE (r11) */ +#ifndef CONFIG_PPC64BRIDGE + rlwinm r11,r3,7,1,24 /* put VSID in 0x7fffff80 bits */ + rlwimi r11,r4,10,26,31 /* put in API (abbrev page index) */ +#else /* CONFIG_PPC64BRIDGE */ + clrlwi r3,r3,8 /* reduce vsid to 24 bits */ + sldi r11,r3,12 /* shift vsid into position */ + rlwimi r11,r4,16,20,24 /* put in API (abbrev page index) */ +#endif /* CONFIG_PPC64BRIDGE */ + SET_V(r11) /* set V (valid) bit */ #ifdef CONFIG_SMP - lis r9,hash_table_lock@h - ori r9,r9,hash_table_lock@l + addis r9,r7,mmu_hash_lock@ha + addi r9,r9,mmu_hash_lock@l rlwinm r8,r1,0,0,18 + add r8,r8,r7 lwz r8,TI_CPU(r8) oris r8,r8,9 -10: lwarx r7,0,r9 - cmpi 0,r7,0 +10: lwarx r0,0,r9 + cmpi 0,r0,0 bne- 11f stwcx. r8,0,r9 beq+ 12f -11: lwz r7,0(r9) - cmpi 0,r7,0 +11: lwz r0,0(r9) + cmpi 0,r0,0 beq 10b b 11b 12: isync @@ -528,69 +570,72 @@ /* * Check the _PAGE_HASHPTE bit in the linux PTE. If it is - * already clear, we're done. If not, clear it (atomically) - * and proceed. -- paulus. + * already clear, we're done (for this pte). If not, + * clear it (atomically) and proceed. -- paulus. */ -1: lwarx r6,0,r5 /* fetch the pte */ - andi. r0,r6,_PAGE_HASHPTE - beq 9f /* done if HASHPTE is already clear */ - rlwinm r6,r6,0,31,29 /* clear HASHPTE bit */ - stwcx. r6,0,r5 /* update the pte */ - bne- 1b - - /* Construct the high word of the PPC-style PTE (r5) */ -#ifndef CONFIG_PPC64BRIDGE - rlwinm r5,r3,7,1,24 /* put VSID in 0x7fffff80 bits */ - rlwimi r5,r4,10,26,31 /* put in API (abbrev page index) */ -#else /* CONFIG_PPC64BRIDGE */ - clrlwi r3,r3,8 /* reduce vsid to 24 bits */ - sldi r5,r3,12 /* shift vsid into position */ - rlwimi r5,r4,16,20,24 /* put in API (abbrev page index) */ -#endif /* CONFIG_PPC64BRIDGE */ - SET_V(r5) /* set V (valid) bit */ +33: lwarx r8,0,r5 /* fetch the pte */ + andi. r0,r8,_PAGE_HASHPTE + beq 8f /* done if HASHPTE is already clear */ + rlwinm r8,r8,0,31,29 /* clear HASHPTE bit */ + stwcx. r8,0,r5 /* update the pte */ + bne- 33b /* Get the address of the primary PTE group in the hash table (r3) */ .globl flush_hash_patch_A flush_hash_patch_A: - lis r8,Hash_base@h /* base address of hash table */ + addis r8,r7,Hash_base@h /* base address of hash table */ rlwimi r8,r3,LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT /* VSID -> hash */ - rlwinm r3,r4,20+LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT /* PI -> hash */ - xor r3,r3,r8 /* make primary hash */ - li r8,8 /* PTEs/group */ + rlwinm r0,r4,20+LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT /* PI -> hash */ + xor r8,r0,r8 /* make primary hash */ /* Search the primary PTEG for a PTE whose 1st (d)word matches r5 */ - mtctr r8 - addi r7,r3,-PTE_SIZE -1: LDPTEu r0,PTE_SIZE(r7) /* get next PTE */ - CMPPTE 0,r0,r5 + li r0,8 /* PTEs/group */ + mtctr r0 + addi r12,r8,-PTE_SIZE +1: LDPTEu r0,PTE_SIZE(r12) /* get next PTE */ + CMPPTE 0,r0,r11 bdnzf 2,1b /* loop while ctr != 0 && !cr0.eq */ beq+ 3f /* Search the secondary PTEG for a matching PTE */ - ori r5,r5,PTE_H /* set H (secondary hash) bit */ + ori r11,r11,PTE_H /* set H (secondary hash) bit */ + li r0,8 /* PTEs/group */ .globl flush_hash_patch_B flush_hash_patch_B: - xoris r7,r3,Hash_msk>>16 /* compute secondary hash */ - xori r7,r7,(-PTEG_SIZE & 0xffff) - addi r7,r7,-PTE_SIZE - mtctr r8 -2: LDPTEu r0,PTE_SIZE(r7) - CMPPTE 0,r0,r5 + xoris r12,r8,Hash_msk>>16 /* compute secondary hash */ + xori r12,r12,(-PTEG_SIZE & 0xffff) + addi r12,r12,-PTE_SIZE + mtctr r0 +2: LDPTEu r0,PTE_SIZE(r12) + CMPPTE 0,r0,r11 bdnzf 2,2b - bne- 4f /* should never fail to find it */ + xori r11,r11,PTE_H /* clear H again */ + bne- 4f /* should rarely fail to find it */ 3: li r0,0 - STPTE r0,0(r7) /* invalidate entry */ + STPTE r0,0(r12) /* invalidate entry */ 4: sync tlbie r4 /* in hw tlb too */ sync +8: ble cr1,9f /* if all ptes checked */ +81: addi r6,r6,-1 + addi r5,r5,4 /* advance to next pte */ + addi r4,r4,0x1000 + lwz r0,0(r5) /* check next pte */ + cmpwi cr1,r6,1 + andi. r0,r0,_PAGE_HASHPTE + bne 33b + bgt cr1,81b + +9: #ifdef CONFIG_SMP TLBSYNC -9: li r0,0 - stw r0,0(r9) /* clear hash_table_lock */ + li r0,0 + stw r0,0(r9) /* clear mmu_hash_lock */ #endif -9: mtmsr r10 - SYNC +19: mtmsr r10 + SYNC_601 + isync blr diff -Nru a/arch/ppc/mm/iSeries_hashtable.c b/arch/ppc/mm/iSeries_hashtable.c --- a/arch/ppc/mm/iSeries_hashtable.c Thu Mar 7 18:17:44 2002 +++ b/arch/ppc/mm/iSeries_hashtable.c Thu Mar 7 18:17:44 2002 @@ -39,7 +39,7 @@ int iSeries_hpt_loaded; -static spinlock_t hash_table_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t mmu_hash_lock = SPIN_LOCK_UNLOCKED; extern unsigned long htab_reloads; // Defined in ppc/kernel/ppc_htab.c extern unsigned long htab_evicts; @@ -159,10 +159,10 @@ access |= _PAGE_PRESENT; // _PAGE_PRESENT also needed - spin_lock( &hash_table_lock ); + spin_lock( &mmu_hash_lock ); // check if pte is in the required state if ( ( access & ~(pte_val(*pt)) ) ) { - spin_unlock( &hash_table_lock ); + spin_unlock( &mmu_hash_lock ); return 1; } @@ -177,18 +177,18 @@ va, pte_val(*pt)); - spin_unlock( &hash_table_lock ); + spin_unlock( &mmu_hash_lock ); return 0; } void add_hash_page(unsigned context, unsigned long va, pte_t *ptep) { - spin_lock( &hash_table_lock ); + spin_lock( &mmu_hash_lock ); pte_update(ptep,0,_PAGE_HASHPTE); __create_hpte(CTX_TO_VSID(context, va), va, pte_val(*ptep)); - spin_unlock( &hash_table_lock ); + spin_unlock( &mmu_hash_lock ); } int flush_hash_page(unsigned context, unsigned long va, pte_t *ptep) @@ -208,7 +208,7 @@ hpte1Ptr = hpte0Ptr + 1; *hpte0Ptr = *hpte1Ptr = 0; - spin_lock( &hash_table_lock ); + spin_lock( &mmu_hash_lock ); rtnIndex = HvCallHpt_findValid( &hpte, vpn ); if ( hpte.v ) { @@ -217,7 +217,7 @@ rc = 0; } else rc = 1; - spin_unlock( &hash_table_lock ); + spin_unlock( &mmu_hash_lock ); return rc; } diff -Nru a/arch/ppc/mm/iSeries_mmu.c b/arch/ppc/mm/iSeries_mmu.c --- a/arch/ppc/mm/iSeries_mmu.c Thu Mar 7 18:17:41 2002 +++ b/arch/ppc/mm/iSeries_mmu.c Thu Mar 7 18:17:41 2002 @@ -175,12 +175,13 @@ pte_t *ptep; static int nopreload; - if (nopreload) + if (nopreload || address >= TASK_SIZE) return; - mm = (address < TASK_SIZE)? vma->vm_mm: &init_mm; + mm = vma->vm_mm; pmd = pmd_offset(pgd_offset(mm, address), address); if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, address); + ptep = pte_offset_map(pmd, address); add_hash_page(mm->context, address, ptep); + pte_unmap(ptep); } } diff -Nru a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c --- a/arch/ppc/mm/init.c Thu Mar 7 18:17:40 2002 +++ b/arch/ppc/mm/init.c Thu Mar 7 18:17:40 2002 @@ -113,24 +113,6 @@ /* max amount of low RAM to map in */ unsigned long __max_low_memory = MAX_LOW_MEM; -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 (pte_quicklist) { - pte_free_slow(pte_alloc_one_fast(NULL, 0)); - freed++; - } - } while (pgtable_cache_size > low); - } - return freed; -} - void show_mem(void) { int i,free = 0,total = 0,reserved = 0; @@ -160,7 +142,6 @@ printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); printk("%d pages swap cached\n",cached); - printk("%d pages in page table cache\n",(int)pgtable_cache_size); show_buffers(); } @@ -396,9 +377,11 @@ #ifdef CONFIG_HIGHMEM map_page(PKMAP_BASE, 0, 0); /* XXX gross */ - pkmap_page_table = pte_offset(pmd_offset(pgd_offset_k(PKMAP_BASE), PKMAP_BASE), PKMAP_BASE); + pkmap_page_table = pte_offset_kernel(pmd_offset(pgd_offset_k + (PKMAP_BASE), PKMAP_BASE), PKMAP_BASE); map_page(KMAP_FIX_BEGIN, 0, 0); /* XXX gross */ - kmap_pte = pte_offset(pmd_offset(pgd_offset_k(KMAP_FIX_BEGIN), KMAP_FIX_BEGIN), KMAP_FIX_BEGIN); + kmap_pte = pte_offset_kernel(pmd_offset(pgd_offset_k + (KMAP_FIX_BEGIN), KMAP_FIX_BEGIN), KMAP_FIX_BEGIN); kmap_prot = PAGE_KERNEL; #endif /* CONFIG_HIGHMEM */ @@ -588,10 +571,12 @@ void flush_icache_page(struct vm_area_struct *vma, struct page *page) { + unsigned long phys; + if (page->mapping && !PageReserved(page) && !test_bit(PG_arch_1, &page->flags)) { - __flush_dcache_icache(kmap(page)); - kunmap(page); + phys = ((page - mem_map) << PAGE_SHIFT) + PPC_MEMSTART; + __flush_dcache_icache_phys(phys); set_bit(PG_arch_1, &page->flags); } } diff -Nru a/arch/ppc/mm/mmu_decl.h b/arch/ppc/mm/mmu_decl.h --- a/arch/ppc/mm/mmu_decl.h Thu Mar 7 18:17:45 2002 +++ b/arch/ppc/mm/mmu_decl.h Thu Mar 7 18:17:45 2002 @@ -61,11 +61,12 @@ * which includes all new 82xx processors. We need tlbie/tlbsync here * in that case (I think). -- Dan. */ -static inline void flush_HPTE(unsigned context, unsigned long va, pte_t *pg) +static inline void flush_HPTE(unsigned context, unsigned long va, + unsigned long pdval) { if ((Hash != 0) && (cur_cpu_spec[0]->cpu_features & CPU_FTR_HPTE_TABLE)) - flush_hash_page(0, va, pg); + flush_hash_pages(0, va, pdval, 1); else _tlbie(va); } diff -Nru a/arch/ppc/mm/pgtable.c b/arch/ppc/mm/pgtable.c --- a/arch/ppc/mm/pgtable.c Thu Mar 7 18:17:39 2002 +++ b/arch/ppc/mm/pgtable.c Thu Mar 7 18:17:39 2002 @@ -39,10 +39,6 @@ unsigned long ioremap_bot; int io_bat_index; -#ifndef CONFIG_SMP -struct pgtable_cache_struct quicklists; -#endif - #if defined(CONFIG_6xx) || defined(CONFIG_POWER3) #define HAVE_BATS 1 #endif @@ -173,12 +169,12 @@ /* Use upper 10 bits of VA to index the first level map */ pd = pmd_offset(pgd_offset_k(va), va); /* Use middle 10 bits of VA to index the second-level map */ - pg = pte_alloc(&init_mm, pd, va); + pg = pte_alloc_kernel(&init_mm, pd, va); if (pg != 0) { err = 0; set_pte(pg, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags))); if (mem_init_done) - flush_HPTE(0, va, pg); + flush_HPTE(0, va, pmd_val(*pd)); } spin_unlock(&init_mm.page_table_lock); return err; @@ -272,10 +268,11 @@ if (pgd) { pmd = pmd_offset(pgd, addr & PAGE_MASK); if (pmd_present(*pmd)) { - pte = pte_offset(pmd, addr & PAGE_MASK); + pte = pte_offset_map(pmd, addr & PAGE_MASK); if (pte) { retval = 1; *ptep = pte; + /* XXX caller needs to do pte_unmap, yuck */ } } } @@ -312,8 +309,10 @@ mm = &init_mm; pa = 0; - if (get_pteptr(mm, addr, &pte)) + if (get_pteptr(mm, addr, &pte)) { pa = (pte_val(*pte) & PAGE_MASK) | (addr & ~PAGE_MASK); + pte_unmap(pte); + } return(pa); } diff -Nru a/arch/ppc/mm/ppc_mmu.c b/arch/ppc/mm/ppc_mmu.c --- a/arch/ppc/mm/ppc_mmu.c Thu Mar 7 18:17:39 2002 +++ b/arch/ppc/mm/ppc_mmu.c Thu Mar 7 18:17:39 2002 @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -289,7 +290,6 @@ { struct mm_struct *mm; pmd_t *pmd; - pte_t *ptep; static int nopreload; if (Hash == 0 || nopreload) @@ -299,8 +299,6 @@ return; mm = (address < TASK_SIZE)? vma->vm_mm: &init_mm; pmd = pmd_offset(pgd_offset(mm, address), address); - if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, address); - add_hash_page(mm->context, address, ptep); - } + if (!pmd_none(*pmd)) + add_hash_page(mm->context, address, pmd_val(*pmd)); } diff -Nru a/arch/ppc/mm/tlb.c b/arch/ppc/mm/tlb.c --- a/arch/ppc/mm/tlb.c Thu Mar 7 18:17:45 2002 +++ b/arch/ppc/mm/tlb.c Thu Mar 7 18:17:45 2002 @@ -30,6 +30,7 @@ #include #include #include +#include #include "mmu_decl.h" @@ -104,7 +105,6 @@ { struct mm_struct *mm; pmd_t *pmd; - pte_t *pte; if (Hash == 0) { _tlbie(vmaddr); @@ -112,11 +112,8 @@ } mm = (vmaddr < TASK_SIZE)? vma->vm_mm: &init_mm; pmd = pmd_offset(pgd_offset(mm, vmaddr), vmaddr); - if (!pmd_none(*pmd)) { - pte = pte_offset(pmd, vmaddr); - if (pte_val(*pte) & _PAGE_HASHPTE) - flush_hash_page(mm->context, vmaddr, pte); - } + if (!pmd_none(*pmd)) + flush_hash_pages(mm->context, vmaddr, pmd_val(*pmd), 1); #ifdef CONFIG_SMP smp_send_tlb_invalidate(0); #endif @@ -133,8 +130,8 @@ { struct mm_struct *mm = vma->vm_mm; pmd_t *pmd; - pte_t *pte; unsigned long pmd_end; + int count; unsigned int ctx = mm->context; if (Hash == 0) { @@ -144,24 +141,21 @@ start &= PAGE_MASK; if (start >= end) return; + end = (end - 1) | ~PAGE_MASK; pmd = pmd_offset(pgd_offset(mm, start), start); - do { - pmd_end = (start + PGDIR_SIZE) & PGDIR_MASK; + for (;;) { + pmd_end = ((start + PGDIR_SIZE) & PGDIR_MASK) - 1; + if (pmd_end > end) + pmd_end = end; if (!pmd_none(*pmd)) { - if (!pmd_end || pmd_end > end) - pmd_end = end; - pte = pte_offset(pmd, start); - do { - if ((pte_val(*pte) & _PAGE_HASHPTE) != 0) - flush_hash_page(ctx, start, pte); - start += PAGE_SIZE; - ++pte; - } while (start && start < pmd_end); - } else { - start = pmd_end; + count = ((pmd_end - start) >> PAGE_SHIFT) + 1; + flush_hash_pages(ctx, start, pmd_val(*pmd), count); } + if (pmd_end == end) + break; + start = pmd_end + 1; ++pmd; - } while (start && start < end); + } #ifdef CONFIG_SMP smp_send_tlb_invalidate(0); diff -Nru a/arch/ppc/vmlinux.lds b/arch/ppc/vmlinux.lds --- a/arch/ppc/vmlinux.lds Thu Mar 7 18:17:45 2002 +++ b/arch/ppc/vmlinux.lds Thu Mar 7 18:17:45 2002 @@ -111,6 +111,10 @@ *(.initcall7.init) } __initcall_end = .; + . = ALIGN(32); + __per_cpu_start = .; + .data.percpu : { *(.data.percpu) } + __per_cpu_end = .; . = ALIGN(4096); __init_end = .; diff -Nru a/arch/ppc64/kernel/entry.S b/arch/ppc64/kernel/entry.S --- a/arch/ppc64/kernel/entry.S Thu Mar 7 18:17:37 2002 +++ b/arch/ppc64/kernel/entry.S Thu Mar 7 18:17:37 2002 @@ -311,7 +311,7 @@ blr _GLOBAL(ret_from_fork) -#ifdef CONFIG_SMP +#if CONFIG_SMP || CONFIG_PREEMPT bl .schedule_tail #endif clrrdi r4,r1,THREAD_SHIFT diff -Nru a/arch/ppc64/kernel/sys_ppc32.c b/arch/ppc64/kernel/sys_ppc32.c --- a/arch/ppc64/kernel/sys_ppc32.c Thu Mar 7 18:17:45 2002 +++ b/arch/ppc64/kernel/sys_ppc32.c Thu Mar 7 18:17:45 2002 @@ -924,143 +924,65 @@ -static int cp_new_stat32(struct inode *inode, struct stat32 *statbuf) +static int cp_new_stat32(struct kstat *stat, struct stat32 *statbuf) { - unsigned long ino, blksize, blocks; - kdev_t dev, rdev; - umode_t mode; - nlink_t nlink; - uid_t uid; - gid_t gid; - off_t size; - time_t atime, mtime, ctime; int err; - /* Stream the loads of inode data into the load buffer, - * then we push it all into the store buffer below. This - * should give optimal cache performance. - */ - ino = inode->i_ino; - dev = inode->i_dev; - mode = inode->i_mode; - nlink = inode->i_nlink; - uid = inode->i_uid; - gid = inode->i_gid; - rdev = inode->i_rdev; - size = inode->i_size; - atime = inode->i_atime; - mtime = inode->i_mtime; - ctime = inode->i_ctime; - blksize = inode->i_blksize; - blocks = inode->i_blocks; - - err = put_user(kdev_t_to_nr(dev), &statbuf->st_dev); - err |= put_user(ino, &statbuf->st_ino); - err |= put_user(mode, &statbuf->st_mode); - err |= put_user(nlink, &statbuf->st_nlink); - err |= put_user(uid, &statbuf->st_uid); - err |= put_user(gid, &statbuf->st_gid); - err |= put_user(kdev_t_to_nr(rdev), &statbuf->st_rdev); - err |= put_user(size, &statbuf->st_size); - err |= put_user(atime, &statbuf->st_atime); + err = put_user(stat->dev, &statbuf->st_dev); + err |= put_user(stat->ino, &statbuf->st_ino); + err |= put_user(stat->mode, &statbuf->st_mode); + err |= put_user(stat->nlink, &statbuf->st_nlink); + err |= put_user(high2lowuid(stat->uid), &statbuf->st_uid); + err |= put_user(high2lowgid(stat->gid), &statbuf->st_gid); + err |= put_user(stat->rdev, &statbuf->st_rdev); + if (stat->size > MAX_NON_LFS) + return -EOVERFLOW; + err |= put_user(stat->size, &statbuf->st_size); + err |= put_user(stat->atime, &statbuf->st_atime); err |= put_user(0, &statbuf->__unused1); - err |= put_user(mtime, &statbuf->st_mtime); + err |= put_user(stat->mtime, &statbuf->st_mtime); err |= put_user(0, &statbuf->__unused2); - err |= put_user(ctime, &statbuf->st_ctime); + err |= put_user(stat->ctime, &statbuf->st_ctime); err |= put_user(0, &statbuf->__unused3); - if (blksize) { - err |= put_user(blksize, &statbuf->st_blksize); - err |= put_user(blocks, &statbuf->st_blocks); - } else { - unsigned int tmp_blocks; - -#define D_B 7 -#define I_B (BLOCK_SIZE / sizeof(unsigned short)) - tmp_blocks = (size + BLOCK_SIZE - 1) / BLOCK_SIZE; - if (tmp_blocks > D_B) { - unsigned int indirect; - - indirect = (tmp_blocks - D_B + I_B - 1) / I_B; - tmp_blocks += indirect; - if (indirect > 1) { - indirect = (indirect - 1 + I_B - 1) / I_B; - tmp_blocks += indirect; - if (indirect > 1) - tmp_blocks++; - } - } - err |= put_user(BLOCK_SIZE, &statbuf->st_blksize); - err |= put_user((BLOCK_SIZE / 512) * tmp_blocks, &statbuf->st_blocks); -#undef D_B -#undef I_B - } + err |= put_user(stat->blksize, &statbuf->st_blksize); + err |= put_user(stat->blocks, &statbuf->st_blocks); err |= put_user(0, &statbuf->__unused4[0]); err |= put_user(0, &statbuf->__unused4[1]); return err; } -static __inline__ int -do_revalidate(struct dentry *dentry) -{ - struct inode * inode = dentry->d_inode; - if (inode->i_op && inode->i_op->revalidate) - return inode->i_op->revalidate(dentry); - return 0; -} - asmlinkage long sys32_newstat(char* filename, struct stat32* statbuf) { - struct nameidata nd; - int error; + struct kstat stat; + int error = vfs_stat(filename, &stat); - PPCDBG(PPCDBG_SYS32X, "sys32_newstat - running - filename=%s, statbuf=%p, pid=%ld, comm=%s\n", filename, statbuf, current->pid, current->comm); + if (!error) + error = cp_new_stat32(&stat, statbuf); - error = user_path_walk(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_new_stat32(nd.dentry->d_inode, statbuf); - path_release(&nd); - } return error; } asmlinkage long sys32_newlstat(char * filename, struct stat32 *statbuf) { - struct nameidata nd; - int error; + struct kstat stat; + int error = vfs_lstat(filename, &stat); - PPCDBG(PPCDBG_SYS32X, "sys32_newlstat - running - fn=%s, pid=%ld, comm=%s\n", filename, current->pid, current->comm); + if (!error) + error = cp_new_stat32(&stat, statbuf); - error = user_path_walk_link(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_new_stat32(nd.dentry->d_inode, statbuf); - - path_release(&nd); - } return error; } asmlinkage long sys32_newfstat(unsigned int fd, struct stat32 *statbuf) { - struct file *f; - int err = -EBADF; + struct kstat stat; + int error = vfs_fstat(fd, &stat); - PPCDBG(PPCDBG_SYS32X, "sys32_newfstat - running - fd=%x, pid=%ld, comm=%s\n", fd, current->pid, current->comm); + if (!error) + error = cp_new_stat32(&stat, statbuf); - f = fget(fd); - if (f) { - struct dentry * dentry = f->f_dentry; - - err = do_revalidate(dentry); - if (!err) - err = cp_new_stat32(dentry->d_inode, statbuf); - fput(f); - } - return err; + return error; } static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) @@ -3104,7 +3026,7 @@ /* stat syscall methods. */ extern asmlinkage int sys_stat(char* filename, struct __old_kernel_stat* statbuf); -static int cp_old_stat32(struct inode* inode, struct __old_kernel_stat32* statbuf) +static int cp_old_stat32(struct kstat *stat, struct __old_kernel_stat32* statbuf) { static int warncount = 5; struct __old_kernel_stat32 tmp; @@ -3115,79 +3037,51 @@ current->comm); } - tmp.st_dev = kdev_t_to_nr(inode->i_dev); - tmp.st_ino = inode->i_ino; - tmp.st_mode = inode->i_mode; - tmp.st_nlink = inode->i_nlink; - SET_OLDSTAT_UID(tmp, inode->i_uid); - SET_OLDSTAT_GID(tmp, inode->i_gid); - tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); - tmp.st_size = inode->i_size; - tmp.st_atime = inode->i_atime; - tmp.st_mtime = inode->i_mtime; - tmp.st_ctime = inode->i_ctime; + tmp.st_dev = stat->dev; + tmp.st_ino = stat->ino; + tmp.st_mode = stat->mode; + tmp.st_nlink = stat->nlink; + SET_OLDSTAT_UID(tmp, stat->uid); + SET_OLDSTAT_GID(tmp, stat->gid); + tmp.st_rdev = stat->rdev; + if (stat->size > MAX_NON_LFS) + return -EOVERFLOW; + tmp.st_size = stat->size; + tmp.st_atime = stat->atime; + tmp.st_mtime = stat->mtime; + tmp.st_ctime = stat->ctime; return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } asmlinkage long sys32_stat(char* filename, struct __old_kernel_stat32* statbuf) { - struct nameidata nd; - int error; - - PPCDBG(PPCDBG_SYS32X, "sys32_stat - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - - error = user_path_walk(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_old_stat32(nd.dentry->d_inode, statbuf); - path_release(&nd); - } + struct kstat stat; + int error = vfs_stat(filename, &stat); - PPCDBG(PPCDBG_SYS32X, "sys32_stat - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + if (!error) + error = cp_old_stat32(&stat, statbuf); return error; } asmlinkage long sys32_fstat(unsigned int fd, struct __old_kernel_stat32* statbuf) { - struct file *f; - int err = -EBADF; - - PPCDBG(PPCDBG_SYS32X, "sys32_fstat - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - - f = fget(fd); - if (f) { - struct dentry * dentry = f->f_dentry; - - err = do_revalidate(dentry); - if (!err) - err = cp_old_stat32(dentry->d_inode, statbuf); - fput(f); - } + struct kstat stat; + int error = vfs_fstat(fd, &stat); - PPCDBG(PPCDBG_SYS32X, "sys32_fstat - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + if (!error) + error = cp_old_stat32(&stat, statbuf); - return err; + return error; } asmlinkage long sys32_lstat(char* filename, struct __old_kernel_stat32* statbuf) { - struct nameidata nd; - int error; - - PPCDBG(PPCDBG_SYS32X, "sys32_lstat - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - - error = user_path_walk_link(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_old_stat32(nd.dentry->d_inode, statbuf); - - path_release(&nd); - } + struct kstat stat; + int error = vfs_lstat(filename, &stat); - PPCDBG(PPCDBG_SYS32X, "sys32_lstat - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + if (!error) + error = cp_old_stat32(&stat, statbuf); return error; } diff -Nru a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S --- a/arch/s390/kernel/entry.S Thu Mar 7 18:17:39 2002 +++ b/arch/s390/kernel/entry.S Thu Mar 7 18:17:39 2002 @@ -295,7 +295,7 @@ stosm 24(%r15),0x03 # reenable interrupts sr %r0,%r0 # child returns 0 st %r0,SP_R2(%r15) # store return value (change R2 on stack) -#ifdef CONFIG_SMP +#if CONFIG_SMP || CONFIG_PREEMPT l %r1,BASED(.Lschedtail) la %r14,BASED(sysc_return) br %r1 # call schedule_tail, return to sysc_return @@ -896,7 +896,7 @@ #error .Ltrace: .long syscall_trace .Lvfork: .long sys_vfork -#ifdef CONFIG_SMP +#if CONFIG_SMP || CONFIG_PREEMPT .Lschedtail: .long schedule_tail #endif diff -Nru a/arch/s390x/kernel/entry.S b/arch/s390x/kernel/entry.S --- a/arch/s390x/kernel/entry.S Thu Mar 7 18:17:40 2002 +++ b/arch/s390x/kernel/entry.S Thu Mar 7 18:17:40 2002 @@ -280,7 +280,7 @@ GET_CURRENT # load pointer to task_struct to R9 stosm 48(%r15),0x03 # reenable interrupts xc SP_R2(8,%r15),SP_R2(%r15) # child returns 0 -#ifdef CONFIG_SMP +#if CONFIG_SMP || CONFIG_PREEMPT larl %r14,sysc_return jg schedule_tail # return to sysc_return #else diff -Nru a/arch/sparc64/defconfig b/arch/sparc64/defconfig --- a/arch/sparc64/defconfig Thu Mar 7 18:17:41 2002 +++ b/arch/sparc64/defconfig Thu Mar 7 18:17:41 2002 @@ -273,7 +273,6 @@ # CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set # CONFIG_BLK_DEV_IDESCSI is not set -# CONFIG_IDE_TASK_IOCTL is not set # # IDE chipset support/bugfixes @@ -286,7 +285,6 @@ # CONFIG_IDEPCI_SHARE_IRQ is not set CONFIG_BLK_DEV_IDEDMA_PCI=y # CONFIG_BLK_DEV_OFFBOARD is not set -# CONFIG_BLK_DEV_IDEDMA_FORCED is not set CONFIG_IDEDMA_PCI_AUTO=y CONFIG_IDEDMA_ONLYDISK=y CONFIG_BLK_DEV_IDEDMA=y diff -Nru a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c --- a/arch/sparc64/kernel/sys_sparc32.c Thu Mar 7 18:17:38 2002 +++ b/arch/sparc64/kernel/sys_sparc32.c Thu Mar 7 18:17:38 2002 @@ -1471,6 +1471,8 @@ err |= put_user(high2lowuid(stat->uid), &statbuf->st_uid); err |= put_user(high2lowgid(stat->gid), &statbuf->st_gid); err |= put_user(stat->rdev, &statbuf->st_rdev); + if (stat->size > MAX_NON_LFS) + return -EOVERFLOW; err |= put_user(stat->size, &statbuf->st_size); err |= put_user(stat->atime, &statbuf->st_atime); err |= put_user(0, &statbuf->__unused1); diff -Nru a/arch/x86_64/config.in b/arch/x86_64/config.in --- a/arch/x86_64/config.in Thu Mar 7 18:17:40 2002 +++ b/arch/x86_64/config.in Thu Mar 7 18:17:40 2002 @@ -29,7 +29,7 @@ define_int CONFIG_X86_L1_CACHE_SHIFT 6 define_bool CONFIG_X86_TSC y define_bool CONFIG_X86_GOOD_APIC y -define_bool CONFIG_X86_CMPXCHG +define_bool CONFIG_X86_CMPXCHG y tristate '/dev/cpu/*/msr - Model-specific register support' CONFIG_X86_MSR tristate '/dev/cpu/*/cpuid - CPU information support' CONFIG_X86_CPUID @@ -72,6 +72,7 @@ if [ "$CONFIG_HOTPLUG" = "y" ] ; then source drivers/pcmcia/Config.in + source drivers/hotplug/Config.in else define_bool CONFIG_PCMCIA n fi @@ -80,8 +81,8 @@ define_bool CONFIG_KCORE_ELF y fi # We probably are not going to support a.out, are we? Or should we support a.out in i386 compatibility mode? -#tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT -tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF + #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 'Power Management support' CONFIG_PM diff -Nru a/arch/x86_64/defconfig b/arch/x86_64/defconfig --- a/arch/x86_64/defconfig Thu Mar 7 18:17:45 2002 +++ b/arch/x86_64/defconfig Thu Mar 7 18:17:45 2002 @@ -37,6 +37,7 @@ CONFIG_X86_L1_CACHE_SHIFT=6 CONFIG_X86_TSC=y CONFIG_X86_GOOD_APIC=y +CONFIG_X86_CMPXCHG=y CONFIG_X86_MSR=y CONFIG_X86_CPUID=y # CONFIG_MATH_EMULATION is not set @@ -59,16 +60,7 @@ # CONFIG_BINFMT_MISC is not set CONFIG_PM=y CONFIG_IA32_EMULATION=y -CONFIG_ACPI=y -CONFIG_ACPI_DEBUG=y -CONFIG_ACPI_BUSMGR=y -CONFIG_ACPI_SYS=y -CONFIG_ACPI_CPU=y -CONFIG_ACPI_BUTTON=y -CONFIG_ACPI_AC=y -CONFIG_ACPI_EC=y -CONFIG_ACPI_CMBATT=y -CONFIG_ACPI_THERMAL=y +# CONFIG_ACPI is not set # # Memory Technology Devices (MTD) @@ -99,9 +91,8 @@ # CONFIG_BLK_DEV_DAC960 is not set # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_SIZE=4096 -CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set # # Multi-device support (RAID and LVM) @@ -197,7 +188,6 @@ # CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set # CONFIG_BLK_DEV_IDESCSI is not set -# CONFIG_IDE_TASK_IOCTL is not set # # IDE chipset support/bugfixes @@ -388,7 +378,6 @@ # CONFIG_UDF_RW is not set # CONFIG_UFS_FS is not set # CONFIG_UFS_FS_WRITE is not set -CONFIG_SIMICSFS=y # # Network File Systems diff -Nru a/arch/x86_64/ia32/ia32_binfmt.c b/arch/x86_64/ia32/ia32_binfmt.c --- a/arch/x86_64/ia32/ia32_binfmt.c Thu Mar 7 18:17:37 2002 +++ b/arch/x86_64/ia32/ia32_binfmt.c Thu Mar 7 18:17:37 2002 @@ -12,6 +12,9 @@ #include #include +struct file; +struct elf_phdr; + #define IA32_EMULATOR 1 #define IA32_PAGE_OFFSET 0xE0000000 @@ -77,7 +80,6 @@ __asm__("movl %0,%%fs": :"r" (0)); \ __asm__("movl %0,%%es; movl %0,%%ds": :"r" (__USER32_DS)); \ wrmsrl(MSR_KERNEL_GS_BASE, 0); \ - set_thread_flag(TIF_IA32); \ (regs)->rip = (new_rip); \ (regs)->rsp = (new_rsp); \ (regs)->eflags = 0x200; \ @@ -87,6 +89,8 @@ } while(0) +#define elf_map elf32_map + MODULE_DESCRIPTION("Binary format loader for compatibility with IA32 ELF binaries."); MODULE_AUTHOR("Eric Youngdale, Andi Kleen"); @@ -102,6 +106,7 @@ static void elf32_init(struct pt_regs *regs) { + struct task_struct *me = current; regs->rdi = 0; regs->rsi = 0; regs->rdx = 0; @@ -109,9 +114,13 @@ regs->rax = 0; regs->rbx = 0; regs->rbp = 0; - current->thread.fs = 0; current->thread.gs = 0; - current->thread.fsindex = 0; current->thread.gsindex = 0; - current->thread.ds = __USER_DS; current->thread.es == __USER_DS; + me->thread.fs = 0; + me->thread.gs = 0; + me->thread.fsindex = 0; + me->thread.gsindex = 0; + me->thread.ds = __USER_DS; + me->thread.es = __USER_DS; + set_thread_flag(TIF_IA32); } extern void put_dirty_page(struct task_struct * tsk, struct page *page, unsigned long address); @@ -161,5 +170,18 @@ up_write(¤t->mm->mmap_sem); return 0; +} +static unsigned long +elf32_map (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, int type) +{ + unsigned long map_addr; + struct task_struct *me = current; + + down_write(&me->mm->mmap_sem); + map_addr = do_mmap(filep, ELF_PAGESTART(addr), + eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), prot, type|MAP_32BIT, + eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr)); + up_write(&me->mm->mmap_sem); + return(map_addr); } diff -Nru a/arch/x86_64/ia32/ia32_ioctl.c b/arch/x86_64/ia32/ia32_ioctl.c --- a/arch/x86_64/ia32/ia32_ioctl.c Thu Mar 7 18:17:36 2002 +++ b/arch/x86_64/ia32/ia32_ioctl.c Thu Mar 7 18:17:36 2002 @@ -3083,8 +3083,6 @@ COMPATIBLE_IOCTL(BLKROGET) COMPATIBLE_IOCTL(BLKRRPART) COMPATIBLE_IOCTL(BLKFLSBUF) -COMPATIBLE_IOCTL(BLKRASET) -COMPATIBLE_IOCTL(BLKFRASET) COMPATIBLE_IOCTL(BLKSECTSET) COMPATIBLE_IOCTL(BLKSSZGET) @@ -3596,10 +3594,8 @@ HANDLE_IOCTL(SIOCRTMSG, ret_einval) HANDLE_IOCTL(SIOCGSTAMP, do_siocgstamp) HANDLE_IOCTL(HDIO_GETGEO, hdio_getgeo) -HANDLE_IOCTL(BLKRAGET, w_long) HANDLE_IOCTL(BLKGETSIZE, w_long) HANDLE_IOCTL(0x1260, broken_blkgetsize) -HANDLE_IOCTL(BLKFRAGET, w_long) HANDLE_IOCTL(BLKSECTGET, w_long) HANDLE_IOCTL(BLKPG, blkpg_ioctl_trans) HANDLE_IOCTL(FBIOGETCMAP, fb_ioctl_trans) diff -Nru a/arch/x86_64/ia32/ia32_signal.c b/arch/x86_64/ia32/ia32_signal.c --- a/arch/x86_64/ia32/ia32_signal.c Thu Mar 7 18:17:43 2002 +++ b/arch/x86_64/ia32/ia32_signal.c Thu Mar 7 18:17:43 2002 @@ -82,7 +82,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; siginitset(¤t->blocked, mask); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs.rax = -EINTR; @@ -225,7 +225,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(®s, &frame->sc, &eax)) @@ -252,7 +252,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(®s, &frame->uc.uc_mcontext, &eax)) diff -Nru a/arch/x86_64/kernel/entry.S b/arch/x86_64/kernel/entry.S --- a/arch/x86_64/kernel/entry.S Thu Mar 7 18:17:42 2002 +++ b/arch/x86_64/kernel/entry.S Thu Mar 7 18:17:42 2002 @@ -102,8 +102,6 @@ * A newly forked process directly context switches into this. */ ENTRY(ret_from_fork) - movq %rbx, %rdi - call schedule_tail GET_THREAD_INFO(%rcx) bt $TIF_SYSCALL_TRACE,threadinfo_flags(%rcx) jc rff_trace diff -Nru a/arch/x86_64/kernel/process.c b/arch/x86_64/kernel/process.c --- a/arch/x86_64/kernel/process.c Thu Mar 7 18:17:36 2002 +++ b/arch/x86_64/kernel/process.c Thu Mar 7 18:17:36 2002 @@ -140,7 +140,6 @@ while (!need_resched()) idle(); schedule(); - check_pgt_cache(); } } diff -Nru a/arch/x86_64/kernel/ptrace.c b/arch/x86_64/kernel/ptrace.c --- a/arch/x86_64/kernel/ptrace.c Thu Mar 7 18:17:41 2002 +++ b/arch/x86_64/kernel/ptrace.c Thu Mar 7 18:17:41 2002 @@ -420,9 +420,11 @@ current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0); + preempt_disable(); current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); schedule(); + preempt_enable(); /* * 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 diff -Nru a/arch/x86_64/kernel/signal.c b/arch/x86_64/kernel/signal.c --- a/arch/x86_64/kernel/signal.c Thu Mar 7 18:17:39 2002 +++ b/arch/x86_64/kernel/signal.c Thu Mar 7 18:17:39 2002 @@ -89,7 +89,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); #if DEBUG_SIG printk("rt_sigsuspend savset(%lx) newset(%lx) regs(%p) rip(%lx)\n", @@ -200,7 +200,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(®s, &frame->uc.uc_mcontext, &eax)) @@ -431,7 +431,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } @@ -473,9 +473,11 @@ if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { /* Let the debugger run. */ current->exit_code = signr; + preempt_disable(); current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); schedule(); + preempt_enable(); /* We're back. Did the debugger cancel the sig? */ if (!(signr = current->exit_code)) @@ -530,12 +532,14 @@ case SIGSTOP: { struct signal_struct *sig; + preempt_disable(); current->state = TASK_STOPPED; current->exit_code = signr; sig = current->p_pptr->sig; if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) notify_parent(current, SIGCHLD); schedule(); + preempt_enable(); continue; } diff -Nru a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c --- a/arch/x86_64/kernel/time.c Thu Mar 7 18:17:45 2002 +++ b/arch/x86_64/kernel/time.c Thu Mar 7 18:17:45 2002 @@ -120,6 +120,62 @@ extern spinlock_t i8259A_lock; + +static inline unsigned long do_fast_gettimeoffset(void) +{ + register unsigned long eax, edx; + + /* Read the Time Stamp Counter */ + + rdtsc(eax,edx); + + /* .. relative to previous jiffy (32 bits is enough) */ + eax -= last_tsc_low; /* tsc_low delta */ + + /* + * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient + * = (tsc_low delta) * (usecs_per_clock) + * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy) + * + * Using a mull instead of a divl saves up to 31 clock cycles + * in the critical path. + */ + + edx = (eax*fast_gettimeoffset_quotient) >> 32; + + /* our adjusted time offset in microseconds */ + return delay_at_last_interrupt + edx; +} + +/* + * This version of gettimeofday has microsecond resolution + * and better than microsecond precision on fast x86 machines with TSC. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + unsigned long usec, sec; + + read_lock_irqsave(&xtime_lock, flags); + usec = do_gettimeoffset(); + { + unsigned long lost = jiffies - wall_jiffies; + if (lost) + usec += lost * (1000000 / HZ); + } + sec = xtime.tv_sec; + usec += xtime.tv_usec; + read_unlock_irqrestore(&xtime_lock, flags); + + while (usec >= 1000000) { + usec -= 1000000; + sec++; + } + + tv->tv_sec = sec; + tv->tv_usec = usec; +} + void do_settimeofday(struct timeval *tv) { write_lock_irq(&xtime_lock); @@ -484,7 +540,7 @@ * clock/second. Our precision is about 100 ppm. */ { - cpu_khz = ((1000000*(1UL<<32)) / tsc_quotient); /* FIXME: is it right? */ + cpu_khz = ((1000*(1UL<<32)) / tsc_quotient); printk("Detected %ld Hz processor.\n", cpu_khz); } } diff -Nru a/arch/x86_64/kernel/vsyscall.c b/arch/x86_64/kernel/vsyscall.c --- a/arch/x86_64/kernel/vsyscall.c Thu Mar 7 18:17:40 2002 +++ b/arch/x86_64/kernel/vsyscall.c Thu Mar 7 18:17:40 2002 @@ -60,9 +60,6 @@ long __vxtime_sequence[2] __section_vxtime_sequence; -/* The rest of the kernel knows it as this. */ -extern void do_gettimeofday(struct timeval *tv) __attribute__((alias("do_vgettimeofday"))); - inline void do_vgettimeofday(struct timeval * tv) { long sequence; diff -Nru a/arch/x86_64/kernel/x8664_ksyms.c b/arch/x86_64/kernel/x8664_ksyms.c --- a/arch/x86_64/kernel/x8664_ksyms.c Thu Mar 7 18:17:39 2002 +++ b/arch/x86_64/kernel/x8664_ksyms.c Thu Mar 7 18:17:39 2002 @@ -89,7 +89,6 @@ EXPORT_SYMBOL(strtok); EXPORT_SYMBOL(strpbrk); -EXPORT_SYMBOL(simple_strtol); EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(strncpy_from_user); diff -Nru a/arch/x86_64/mm/fault.c b/arch/x86_64/mm/fault.c --- a/arch/x86_64/mm/fault.c Thu Mar 7 18:17:36 2002 +++ b/arch/x86_64/mm/fault.c Thu Mar 7 18:17:36 2002 @@ -112,7 +112,7 @@ mm = tsk->mm; info.si_code = SEGV_MAPERR; - if (address >= TASK_SIZE) + if (address >= TASK_SIZE && !(error_code & 5)) goto vmalloc_fault; diff -Nru a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c --- a/arch/x86_64/mm/init.c Thu Mar 7 18:17:39 2002 +++ b/arch/x86_64/mm/init.c Thu Mar 7 18:17:39 2002 @@ -1,8 +1,9 @@ /* - * linux/arch/i386/mm/init.c + * linux/arch/x86_64/mm/init.c * * Copyright (C) 1995 Linus Torvalds * Copyright (C) 2000 Pavel Machek + * Copyright (C) 2002 Andi Kleen */ #include @@ -39,28 +40,6 @@ static unsigned long totalram_pages; -int do_check_pgt_cache(int low, int high) -{ - int freed = 0; - if(read_pda(pgtable_cache_sz) > high) { - do { - if (read_pda(pgd_quick)) { - pgd_free_slow(pgd_alloc_one_fast()); - freed++; - } - if (read_pda(pmd_quick)) { - pmd_free_slow(pmd_alloc_one_fast(NULL, 0)); - freed++; - } - if (read_pda(pte_quick)) { - pte_free_slow(pte_alloc_one_fast(NULL, 0)); - freed++; - } - } while(read_pda(pgtable_cache_sz) > low); - } - return freed; -} - /* * NOTE: pagetable_init alloc all the fixmap pagetables contiguous on the * physical space so we can cache the place of the first one and move @@ -89,7 +68,6 @@ printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); printk("%d pages swap cached\n",cached); - printk("%ld pages in page table cache\n",read_pda(pgtable_cache_sz)); show_buffers(); } @@ -138,12 +116,12 @@ if (pmd_none(*pmd)) { pte = (pte_t *) spp_getpage(); set_pmd(pmd, __pmd(__pa(pte) + 0x7)); - if (pte != pte_offset(pmd, 0)) { + if (pte != pte_offset_kernel(pmd, 0)) { printk("PAGETABLE BUG #02!\n"); return; } } - pte = pte_offset(pmd, vaddr); + pte = pte_offset_kernel(pmd, vaddr); if (pte_val(*pte)) pte_ERROR(*pte); set_pte(pte, mk_pte_phys(phys, prot)); diff -Nru a/arch/x86_64/mm/ioremap.c b/arch/x86_64/mm/ioremap.c --- a/arch/x86_64/mm/ioremap.c Thu Mar 7 18:17:42 2002 +++ b/arch/x86_64/mm/ioremap.c Thu Mar 7 18:17:42 2002 @@ -49,7 +49,7 @@ if (address >= end) BUG(); do { - pte_t * pte = pte_alloc(&init_mm, pmd, address); + pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); if (!pte) return -ENOMEM; remap_area_pte(pte, address, end - address, address + phys_addr, flags); diff -Nru a/arch/x86_64/tools/offset.c b/arch/x86_64/tools/offset.c --- a/arch/x86_64/tools/offset.c Thu Mar 7 18:17:42 2002 +++ b/arch/x86_64/tools/offset.c Thu Mar 7 18:17:42 2002 @@ -42,10 +42,6 @@ ENTRY(irqrsp); ENTRY(irqcount); ENTRY(irqstack); - ENTRY(pgd_quick); - ENTRY(pmd_quick); - ENTRY(pte_quick); - ENTRY(pgtable_cache_sz); ENTRY(cpunumber); ENTRY(irqstackptr); ENTRY(me); diff -Nru a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile Thu Mar 7 18:17:40 2002 +++ b/drivers/Makefile Thu Mar 7 18:17:40 2002 @@ -1,7 +1,7 @@ # # Makefile for the Linux kernel device drivers. # -# 15 Sep 2000, Christoph Hellwig +# 15 Sep 2000, Christoph Hellwig # Rewritten to use lists instead of if-statements. # diff -Nru a/drivers/acorn/block/mfmhd.c b/drivers/acorn/block/mfmhd.c --- a/drivers/acorn/block/mfmhd.c Thu Mar 7 18:17:39 2002 +++ b/drivers/acorn/block/mfmhd.c Thu Mar 7 18:17:39 2002 @@ -321,14 +321,14 @@ unsigned long flags; va_list ap; - save_flags_cli(flags); + local_irq_save(flags); va_start(ap, fmt); vsprintf(buffer, fmt, ap); console_print(buffer); va_end(fmt); - restore_flags(flags); + local_irq_restore(flags); }; /* console_printf */ #define DBG(x...) console_printf(x) @@ -746,7 +746,7 @@ /* Yep - a partial access */ /* and issue the remainder */ - issue_request(MINOR(CURRENT->rq_dev), PartFragRead_RestartBlock, PartFragRead_SectorsLeft, CURRENT); + issue_request(minor(CURRENT->rq_dev), PartFragRead_RestartBlock, PartFragRead_SectorsLeft, CURRENT); return; } @@ -929,7 +929,7 @@ DBG("mfm_request: before arg extraction\n"); - dev = MINOR(CURRENT->rq_dev); + dev = minor(CURRENT->rq_dev); block = CURRENT->sector; nsect = CURRENT->nr_sectors; #ifdef DEBUG @@ -1187,10 +1187,10 @@ if (!inode || !(dev = inode->i_rdev)) return -EINVAL; - major = MAJOR(dev); - minor = MINOR(dev); + major = major(dev); + minor = minor(dev); - device = DEVICE_NR(MINOR(inode->i_rdev)), err; + device = DEVICE_NR(minor(inode->i_rdev)), err; if (device >= mfm_drives) return -EINVAL; @@ -1222,7 +1222,7 @@ case BLKROSET: case BLKROGET: case BLKPG: - return blk_ioctl(dev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); default: return -EINVAL; @@ -1231,7 +1231,7 @@ static int mfm_open(struct inode *inode, struct file *file) { - int dev = DEVICE_NR(MINOR(inode->i_rdev)); + int dev = DEVICE_NR(minor(inode->i_rdev)); if (dev >= mfm_drives) return -ENODEV; @@ -1249,7 +1249,7 @@ */ static int mfm_release(struct inode *inode, struct file *file) { - mfm_info[DEVICE_NR(MINOR(inode->i_rdev))].access_count--; + mfm_info[DEVICE_NR(minor(inode->i_rdev))].access_count--; return 0; } @@ -1270,7 +1270,7 @@ void xd_set_geometry(kdev_t dev, unsigned char secsptrack, unsigned char heads, unsigned long discsize, unsigned int secsize) { - int drive = MINOR(dev) >> 6; + int drive = minor(dev) >> 6; if (mfm_info[drive].cylinders == 1) { mfm_info[drive].sectors = secsptrack; @@ -1338,7 +1338,7 @@ for (i = 0; i < mfm_drives; i++) { mfm_geometry (i); - register_disk(&mfm_gendisk, MKDEV(MAJOR_NR,i<<6), 1<<6, + register_disk(&mfm_gendisk, mk_kdev(MAJOR_NR,i<<6), 1<<6, &mfm_fops, mfm_info[i].cylinders * mfm_info[i].heads * mfm_info[i].sectors / 2); @@ -1448,23 +1448,23 @@ */ static int mfm_reread_partitions(kdev_t dev) { - unsigned int start, i, maxp, target = DEVICE_NR(MINOR(dev)); + unsigned int start, i, maxp, target = DEVICE_NR(minor(dev)); unsigned long flags; - save_flags_cli(flags); + local_irq_save(flags); if (mfm_info[target].busy || mfm_info[target].access_count > 1) { - restore_flags (flags); + local_irq_restore (flags); return -EBUSY; } mfm_info[target].busy = 1; - restore_flags (flags); + local_irq_restore (flags); maxp = 1 << mfm_gendisk.minor_shift; start = target << mfm_gendisk.minor_shift; for (i = maxp - 1; i >= 0; i--) { int minor = start + i; - invalidate_device (MKDEV(MAJOR_NR, minor), 1); + invalidate_device (mk_kdev(MAJOR_NR, minor), 1); mfm_gendisk.part[minor].start_sect = 0; mfm_gendisk.part[minor].nr_sects = 0; } diff -Nru a/drivers/acorn/char/mouse_ps2.c b/drivers/acorn/char/mouse_ps2.c --- a/drivers/acorn/char/mouse_ps2.c Thu Mar 7 18:17:40 2002 +++ b/drivers/acorn/char/mouse_ps2.c Thu Mar 7 18:17:40 2002 @@ -1,8 +1,6 @@ /* * Driver for PS/2 mouse on IOMD interface */ - -#include #include #include #include @@ -273,7 +271,8 @@ iomd_writeb(0, IOMD_MSECTL); iomd_writeb(8, IOMD_MSECTL); - misc_register(&psaux_mouse); + if (misc_register(&psaux_mouse)) + return -ENODEV; queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); memset(queue, 0, sizeof(*queue)); queue->head = queue->tail = 0; diff -Nru a/drivers/acorn/net/ether1.c b/drivers/acorn/net/ether1.c --- a/drivers/acorn/net/ether1.c Thu Mar 7 18:17:37 2002 +++ b/drivers/acorn/net/ether1.c Thu Mar 7 18:17:37 2002 @@ -99,13 +99,13 @@ unsigned long flags; unsigned short ret; - if (svflgs) { - save_flags_cli (flags); - } + if (svflgs) + local_irq_save (flags); + outb (addr >> 12, REG_PAGE); ret = inw (ETHER1_RAM + ((addr & 4095) >> 1)); if (svflgs) - restore_flags (flags); + local_irq_restore (flags); return ret; } @@ -114,13 +114,13 @@ { unsigned long flags; - if (svflgs) { - save_flags_cli (flags); - } + if (svflgs) + local_irq_save (flags); + outb (addr >> 12, REG_PAGE); outw (val, ETHER1_RAM + ((addr & 4095) >> 1)); if (svflgs) - restore_flags (flags); + local_irq_restore (flags); } /* @@ -722,7 +722,7 @@ nop.nop_command = CMD_NOP; nop.nop_link = nopaddr; - save_flags_cli(flags); + local_irq_save(flags); ether1_writebuffer (dev, &tx, txaddr, TX_SIZE); ether1_writebuffer (dev, &tbd, tbdaddr, TBD_SIZE); ether1_writebuffer (dev, skb->data, dataddr, len); @@ -733,7 +733,7 @@ /* now reset the previous nop pointer */ ether1_outw (dev, txaddr, tmp, nop_t, nop_link, NORMALIRQS); - restore_flags(flags); + local_irq_restore(flags); /* handle transmit */ dev->trans_start = jiffies; diff -Nru a/drivers/acorn/net/ether3.c b/drivers/acorn/net/ether3.c --- a/drivers/acorn/net/ether3.c Thu Mar 7 18:17:44 2002 +++ b/drivers/acorn/net/ether3.c Thu Mar 7 18:17:44 2002 @@ -491,7 +491,7 @@ del_timer(&priv->timer); - save_flags_cli(flags); + local_irq_save(flags); printk(KERN_ERR "%s: transmit timed out, network cable problem?\n", dev->name); printk(KERN_ERR "%s: state: { status=%04X cfg1=%04X cfg2=%04X }\n", dev->name, ether3_inw(REG_STATUS), ether3_inw(REG_CONFIG1), ether3_inw(REG_CONFIG2)); @@ -501,7 +501,7 @@ priv->tx_head, priv->tx_tail); ether3_setbuffer(dev, buffer_read, priv->tx_tail); printk(KERN_ERR "%s: packet status = %08X\n", dev->name, ether3_readlong(dev)); - restore_flags(flags); + local_irq_restore(flags); priv->regs.config2 |= CFG2_CTRLO; priv->stats.tx_errors += 1; @@ -533,10 +533,10 @@ next_ptr = (priv->tx_head + 1) & 15; - save_flags_cli(flags); + local_irq_save(flags); if (priv->tx_tail == next_ptr) { - restore_flags(flags); + local_irq_restore(flags); return 1; /* unable to queue */ } @@ -565,7 +565,7 @@ } next_ptr = (priv->tx_head + 1) & 15; - restore_flags(flags); + local_irq_restore(flags); dev_kfree_skb(skb); diff -Nru a/drivers/acorn/scsi/ecoscsi.c b/drivers/acorn/scsi/ecoscsi.c --- a/drivers/acorn/scsi/ecoscsi.c Thu Mar 7 18:17:46 2002 +++ b/drivers/acorn/scsi/ecoscsi.c Thu Mar 7 18:17:46 2002 @@ -125,7 +125,11 @@ } NCR5380_init(instance, 0); - request_region (instance->io_port, instance->n_io_port, "ecoscsi"); + if (request_region (instance->io_port, instance->n_io_port, "ecoscsi") == NULL) + { + scsi_unregister(instance); + return 0; + } if (instance->irq != IRQ_NONE) if (request_irq(instance->irq, do_ecoscsi_intr, SA_INTERRUPT, "ecoscsi", NULL)) { diff -Nru a/drivers/acpi/executer/exresnte.c b/drivers/acpi/executer/exresnte.c --- a/drivers/acpi/executer/exresnte.c Thu Mar 7 18:17:40 2002 +++ b/drivers/acpi/executer/exresnte.c Thu Mar 7 18:17:40 2002 @@ -44,7 +44,7 @@ * FUNCTION: Acpi_ex_resolve_node_to_value * * PARAMETERS: Object_ptr - Pointer to a location that contains - * a pointer to a NS node, and will recieve a + * a pointer to a NS node, and will receive a * pointer to the resolved object. * Walk_state - Current state. Valid only if executing AML * code. NULL if simply resolving an object diff -Nru a/drivers/atm/eni.c b/drivers/atm/eni.c --- a/drivers/atm/eni.c Thu Mar 7 18:17:40 2002 +++ b/drivers/atm/eni.c Thu Mar 7 18:17:40 2002 @@ -2310,7 +2310,7 @@ name: DEV_LABEL, id_table: eni_pci_tbl, probe: eni_init_one, - remove: eni_remove_one, + remove: __devexit_p(eni_remove_one), }; diff -Nru a/drivers/atm/firestream.c b/drivers/atm/firestream.c --- a/drivers/atm/firestream.c Thu Mar 7 18:17:38 2002 +++ b/drivers/atm/firestream.c Thu Mar 7 18:17:38 2002 @@ -1530,7 +1530,7 @@ fs_dprintk (FS_DEBUG_QUEUE, "Added %d entries. \n", n); } -static void __exit free_queue (struct fs_dev *dev, struct queue *txq) +static void __devexit free_queue (struct fs_dev *dev, struct queue *txq) { func_enter (); @@ -1546,7 +1546,7 @@ func_exit (); } -static void __exit free_freepool (struct fs_dev *dev, struct freepool *fp) +static void __devexit free_freepool (struct fs_dev *dev, struct freepool *fp) { func_enter (); @@ -2088,7 +2088,7 @@ #endif */ -const static struct pci_device_id firestream_pci_tbl[] __devinitdata = { +static struct pci_device_id firestream_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_FUJITSU_ME, PCI_DEVICE_ID_FUJITSU_FS50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FS_IS50}, { PCI_VENDOR_ID_FUJITSU_ME, PCI_DEVICE_ID_FUJITSU_FS155, @@ -2102,7 +2102,7 @@ name: "firestream", id_table: firestream_pci_tbl, probe: firestream_init_one, - remove: firestream_remove_one, + remove: __devexit_p(firestream_remove_one), }; static int __init firestream_init_module (void) diff -Nru a/drivers/block/DAC960.c b/drivers/block/DAC960.c --- a/drivers/block/DAC960.c Thu Mar 7 18:17:45 2002 +++ b/drivers/block/DAC960.c Thu Mar 7 18:17:46 2002 @@ -5398,7 +5398,7 @@ case BLKFLSBUF: case BLKBSZGET: case BLKBSZSET: - return blk_ioctl(Inode->i_rdev, Request, Argument); + return blk_ioctl(Inode->i_bdev, Request, Argument); case BLKRRPART: /* Re-Read Partition Table. */ diff -Nru a/drivers/block/DAC960.h b/drivers/block/DAC960.h --- a/drivers/block/DAC960.h Thu Mar 7 18:17:46 2002 +++ b/drivers/block/DAC960.h Thu Mar 7 18:17:46 2002 @@ -2074,7 +2074,7 @@ #define DAC960_KernelDevice(ControllerNumber, \ LogicalDriveNumber, \ PartitionNumber) \ - MKDEV(DAC960_MajorNumber(ControllerNumber), \ + mk_kdev(DAC960_MajorNumber(ControllerNumber), \ DAC960_MinorNumber(LogicalDriveNumber, PartitionNumber)) diff -Nru a/drivers/block/Makefile b/drivers/block/Makefile --- a/drivers/block/Makefile Thu Mar 7 18:17:46 2002 +++ b/drivers/block/Makefile Thu Mar 7 18:17:46 2002 @@ -1,7 +1,7 @@ # # Makefile for the kernel block device drivers. # -# 12 June 2000, Christoph Hellwig +# 12 June 2000, Christoph Hellwig # Rewritten to use lists instead of if-statements. # # Note : at this point, these files are compiled on all systems. diff -Nru a/drivers/block/acsi.c b/drivers/block/acsi.c --- a/drivers/block/acsi.c Thu Mar 7 18:17:44 2002 +++ b/drivers/block/acsi.c Thu Mar 7 18:17:44 2002 @@ -784,7 +784,7 @@ status = acsi_getstatus(); if (status != 0) { - int dev = DEVICE_NR(MINOR(CURRENT->rq_dev)); + int dev = DEVICE_NR(minor(CURRENT->rq_dev)); printk( KERN_ERR "ad%c: ", dev+'a' ); if (!acsi_reqsense( acsi_buffer, acsi_info[dev].target, acsi_info[dev].lun)) @@ -815,7 +815,7 @@ status = acsi_getstatus(); if (status != 0) { - int dev = DEVICE_NR(MINOR(CURRENT->rq_dev)); + int dev = DEVICE_NR(minor(CURRENT->rq_dev)); printk( KERN_ERR "ad%c: ", dev+'a' ); if (!acsi_reqsense( acsi_buffer, acsi_info[dev].target, acsi_info[dev].lun)) @@ -993,7 +993,7 @@ panic(DEVICE_NAME ": block not locked"); } - dev = MINOR(CURRENT->rq_dev); + dev = minor(CURRENT->rq_dev); block = CURRENT->sector; if (DEVICE_NR(dev) >= NDevices || block+CURRENT->nr_sectors >= acsi_part[dev].nr_sects) { @@ -1111,7 +1111,7 @@ if (!inode) return -EINVAL; - dev = DEVICE_NR(MINOR(inode->i_rdev)); + dev = DEVICE_NR(minor(inode->i_rdev)); if (dev >= NDevices) return -EINVAL; switch (cmd) { @@ -1141,7 +1141,7 @@ case BLKROGET: case BLKFLSBUF: case BLKPG: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); case BLKRRPART: /* Re-read partition tables */ if (!capable(CAP_SYS_ADMIN)) @@ -1174,7 +1174,7 @@ int device; struct acsi_info_struct *aip; - device = DEVICE_NR(MINOR(inode->i_rdev)); + device = DEVICE_NR(minor(inode->i_rdev)); if (device >= NDevices) return -ENXIO; aip = &acsi_info[device]; @@ -1212,7 +1212,7 @@ static int acsi_release( struct inode * inode, struct file * file ) { - int device = DEVICE_NR(MINOR(inode->i_rdev)); + int device = DEVICE_NR(minor(inode->i_rdev)); if (--access_count[device] == 0 && acsi_info[device].removable) acsi_prevent_removal(device, 0); return( 0 ); @@ -1240,7 +1240,7 @@ static int acsi_media_change (dev_t dev) { - int device = DEVICE_NR(MINOR(dev)); + int device = DEVICE_NR(minor(dev)); struct acsi_info_struct *aip; aip = &acsi_info[device]; @@ -1742,7 +1742,7 @@ acsi_blocksizes[i] = 1024; blksize_size[MAJOR_NR] = acsi_blocksizes; for( i = 0; i < NDevices; ++i ) - register_disk(&acsi_gendisk, MKDEV(MAJOR_NR,i<<4), + register_disk(&acsi_gendisk, mk_kdev(MAJOR_NR,i<<4), (acsi_info[i].type==HARDDISK)?1<<4:1, &acsi_fops, acsi_info[i].size); @@ -1853,7 +1853,7 @@ int res; struct acsi_info_struct *aip; - device = DEVICE_NR(MINOR(dev)); + device = DEVICE_NR(minor(dev)); aip = &acsi_info[device]; gdev = &GENDISK_STRUCT; diff -Nru a/drivers/block/amiflop.c b/drivers/block/amiflop.c --- a/drivers/block/amiflop.c Thu Mar 7 18:17:44 2002 +++ b/drivers/block/amiflop.c Thu Mar 7 18:17:44 2002 @@ -1514,7 +1514,7 @@ rel_fdc(); return -EBUSY; } - fsync_dev(inode->i_rdev); + fsync_bdev(inode->i_bdev); if (fd_motor_on(drive) == 0) { rel_fdc(); return -ENODEV; diff -Nru a/drivers/block/ataflop.c b/drivers/block/ataflop.c --- a/drivers/block/ataflop.c Thu Mar 7 18:17:39 2002 +++ b/drivers/block/ataflop.c Thu Mar 7 18:17:39 2002 @@ -659,7 +659,7 @@ unsigned char *p; int sect, nsect; unsigned long flags; - int type, drive = MINOR(device) & 3; + int type, drive = minor(device) & 3; DPRINT(("do_format( dr=%d tr=%d he=%d offs=%d )\n", drive, desc->track, desc->head, desc->sect_offset )); @@ -672,7 +672,7 @@ atari_turnon_irq( IRQ_MFP_FDC ); /* should be already, just to be sure */ restore_flags(flags); - type = MINOR(device) >> 2; + type = minor(device) >> 2; if (type) { if (--type >= NUM_DISK_MINORS || minor2disktype[type].drive_types > DriveType) { @@ -1367,9 +1367,9 @@ static int check_floppy_change (kdev_t dev) { - unsigned int drive = MINOR(dev) & 0x03; + unsigned int drive = minor(dev) & 0x03; - if (MAJOR(dev) != MAJOR_NR) { + if (major(dev) != MAJOR_NR) { printk(KERN_ERR "floppy_changed: not a floppy\n"); return 0; } @@ -1394,7 +1394,7 @@ static int floppy_revalidate (kdev_t dev) { - int drive = MINOR(dev) & 3; + int drive = minor(dev) & 3; if (test_bit(drive, &changed_floppies) || test_bit(drive, &fake_change) || @@ -1466,13 +1466,13 @@ if (QUEUE_EMPTY) goto the_end; - if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) + if (major(CURRENT->rq_dev) != MAJOR_NR) panic(DEVICE_NAME ": request list destroyed"); if (CURRENT->bh && !buffer_locked(CURRENT->bh)) panic(DEVICE_NAME ": block not locked"); - device = MINOR(CURRENT_DEVICE); + device = minor(CURRENT_DEVICE); drive = device & 3; type = device >> 2; @@ -1553,7 +1553,7 @@ { /* invalidate the buffer track to force a reread */ BufferDrive = -1; - set_bit(MINOR(rdev) & 3, &fake_change); + set_bit(minor(rdev) & 3, &fake_change); check_disk_change(rdev); return 0; } @@ -1574,9 +1574,9 @@ case BLKROSET: case BLKROGET: case BLKFLSBUF: - return blk_ioctl(device, cmd, param); + return blk_ioctl(inode->i_bdev, cmd, param); } - drive = MINOR (device); + drive = minor (device); type = drive >> 2; drive &= 3; switch (cmd) { @@ -1896,15 +1896,15 @@ return -EIO; } - drive = MINOR(inode->i_rdev) & 3; - type = MINOR(inode->i_rdev) >> 2; + drive = minor(inode->i_rdev) & 3; + type = minor(inode->i_rdev) >> 2; DPRINT(("fd_open: type=%d\n",type)); if (drive >= FD_MAX_UNITS || type > NUM_DISK_MINORS) return -ENXIO; old_dev = fd_device[drive]; - if (fd_ref[drive] && old_dev != MINOR(inode->i_rdev)) + if (fd_ref[drive] && old_dev != minor(inode->i_rdev)) return -EBUSY; if (fd_ref[drive] == -1 || (fd_ref[drive] && filp->f_flags & O_EXCL)) @@ -1915,10 +1915,10 @@ else fd_ref[drive]++; - fd_device[drive] = MINOR(inode->i_rdev); + fd_device[drive] = minor(inode->i_rdev); - if (old_dev && old_dev != MINOR(inode->i_rdev)) - invalidate_buffers(MKDEV(FLOPPY_MAJOR, old_dev)); + if (old_dev && old_dev != minor(inode->i_rdev)) + invalidate_buffers(mk_kdev(FLOPPY_MAJOR, old_dev)); if (filp->f_flags & O_NDELAY) return 0; @@ -1939,7 +1939,7 @@ static int floppy_release( struct inode * inode, struct file * filp ) { - int drive = MINOR(inode->i_rdev) & 3; + int drive = minor(inode->i_rdev) & 3; if (fd_ref[drive] < 0) fd_ref[drive] = 0; diff -Nru a/drivers/block/blkpg.c b/drivers/block/blkpg.c --- a/drivers/block/blkpg.c Thu Mar 7 18:17:37 2002 +++ b/drivers/block/blkpg.c Thu Mar 7 18:17:37 2002 @@ -34,7 +34,6 @@ #include /* for set_device_ro() */ #include #include -#include /* for is_swap_partition() */ #include /* for EXPORT_SYMBOL */ #include @@ -63,12 +62,13 @@ * or has the same number as an existing one * 0: all OK. */ -int add_partition(kdev_t dev, struct blkpg_partition *p) +int add_partition(struct block_device *bdev, struct blkpg_partition *p) { struct gendisk *g; long long ppstart, pplength; long pstart, plength; int i, drive, first_minor, end_minor, minor; + kdev_t dev = to_kdev_t(bdev->bd_dev); /* convert bytes to sectors, check for fit in a hd_struct */ ppstart = (p->start >> 9); @@ -126,11 +126,14 @@ * * Note that the dev argument refers to the entire disk, not the partition. */ -int del_partition(kdev_t dev, struct blkpg_partition *p) +int del_partition(struct block_device *bdev, struct blkpg_partition *p) { + kdev_t dev = to_kdev_t(bdev->bd_dev); struct gendisk *g; kdev_t devp; + struct block_device *bdevp; int drive, first_minor, minor; + int holder; /* find the drive major */ g = get_gendisk(dev); @@ -153,22 +156,29 @@ /* partition in use? Incomplete check for now. */ devp = mk_kdev(major(dev), minor); - if (is_mounted(devp) || is_swap_partition(devp)) + bdevp = bdget(kdev_t_to_nr(devp)); + if (!bdevp) + return -ENOMEM; + if (bd_claim(bdevp, &holder) < 0) { + bdput(bdevp); return -EBUSY; + } /* all seems OK */ - fsync_dev(devp); - invalidate_buffers(devp); + fsync_bdev(bdevp); + invalidate_bdev(bdevp, 0); g->part[minor].start_sect = 0; g->part[minor].nr_sects = 0; if (g->sizes) g->sizes[minor] = 0; + bd_release(bdevp); + bdput(bdevp); return 0; } -int blkpg_ioctl(kdev_t dev, struct blkpg_ioctl_arg *arg) +int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg) { struct blkpg_ioctl_arg a; struct blkpg_partition p; @@ -188,9 +198,9 @@ if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (a.op == BLKPG_ADD_PARTITION) - return add_partition(dev, &p); + return add_partition(bdev, &p); else - return del_partition(dev, &p); + return del_partition(bdev, &p); default: return -EINVAL; } @@ -200,16 +210,15 @@ * Common ioctl's for block devices */ extern int block_ioctl(kdev_t dev, unsigned int cmd, unsigned long arg); -int blk_ioctl(kdev_t dev, unsigned int cmd, unsigned long arg) +int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg) { request_queue_t *q; struct gendisk *g; u64 ullval = 0; int intval; unsigned short usval; - - if (kdev_none(dev)) - return -EINVAL; + kdev_t dev = to_kdev_t(bdev->bd_dev); + int holder; intval = block_ioctl(dev, cmd, arg); if (intval != -ENOTTY) @@ -265,7 +274,7 @@ #endif case BLKPG: - return blkpg_ioctl(dev, (struct blkpg_ioctl_arg *) arg); + return blkpg_ioctl(bdev, (struct blkpg_ioctl_arg *) arg); /* * deprecated, use the /proc/iosched interface instead @@ -290,9 +299,10 @@ if (intval > PAGE_SIZE || intval < 512 || (intval & (intval - 1))) return -EINVAL; - if (is_mounted(dev) || is_swap_partition(dev)) + if (bd_claim(bdev, &holder) < 0) return -EBUSY; set_blocksize(dev, intval); + bd_release(bdev); return 0; default: diff -Nru a/drivers/block/cciss.c b/drivers/block/cciss.c --- a/drivers/block/cciss.c Thu Mar 7 18:17:46 2002 +++ b/drivers/block/cciss.c Thu Mar 7 18:17:46 2002 @@ -469,7 +469,7 @@ case BLKROSET: case BLKROGET: case BLKPG: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); case CCISS_GETPCIINFO: { cciss_pci_info_struct pciinfo; @@ -2617,7 +2617,7 @@ static struct pci_driver cciss_pci_driver = { name: "cciss", probe: cciss_init_one, - remove: cciss_remove_one, + remove: __devexit_p(cciss_remove_one), id_table: cciss_pci_device_id, /* id_table */ }; diff -Nru a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c --- a/drivers/block/cpqarray.c Thu Mar 7 18:17:39 2002 +++ b/drivers/block/cpqarray.c Thu Mar 7 18:17:39 2002 @@ -1179,7 +1179,7 @@ case BLKROSET: case BLKROGET: case BLKPG: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); default: return -EINVAL; diff -Nru a/drivers/block/floppy.c b/drivers/block/floppy.c --- a/drivers/block/floppy.c Thu Mar 7 18:17:39 2002 +++ b/drivers/block/floppy.c Thu Mar 7 18:17:39 2002 @@ -3449,7 +3449,7 @@ case BLKROSET: case BLKROGET: case BLKFLSBUF: - return blk_ioctl(device, cmd, param); + return blk_ioctl(inode->i_bdev, cmd, param); } type = TYPE(device); drive = DRIVE(device); diff -Nru a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c --- a/drivers/block/ll_rw_blk.c Thu Mar 7 18:17:38 2002 +++ b/drivers/block/ll_rw_blk.c Thu Mar 7 18:17:38 2002 @@ -1697,9 +1697,6 @@ blk_max_low_pfn = max_low_pfn; blk_max_pfn = max_pfn; -#if defined(CONFIG_IDE) && defined(CONFIG_BLK_DEV_IDE) - ide_init(); /* this MUST precede hd_init */ -#endif #if defined(CONFIG_IDE) && defined(CONFIG_BLK_DEV_HD) hd_init(); #endif diff -Nru a/drivers/block/loop.c b/drivers/block/loop.c --- a/drivers/block/loop.c Thu Mar 7 18:17:37 2002 +++ b/drivers/block/loop.c Thu Mar 7 18:17:37 2002 @@ -852,7 +852,7 @@ break; case BLKBSZGET: case BLKBSZSET: - err = blk_ioctl(inode->i_rdev, cmd, arg); + err = blk_ioctl(inode->i_bdev, cmd, arg); break; default: err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL; diff -Nru a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c --- a/drivers/block/paride/pd.c Thu Mar 7 18:17:40 2002 +++ b/drivers/block/paride/pd.c Thu Mar 7 18:17:40 2002 @@ -481,7 +481,7 @@ case BLKROGET: case BLKFLSBUF: case BLKPG: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); default: return -EINVAL; } diff -Nru a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c --- a/drivers/block/paride/pf.c Thu Mar 7 18:17:38 2002 +++ b/drivers/block/paride/pf.c Thu Mar 7 18:17:38 2002 @@ -433,7 +433,7 @@ case BLKROSET: case BLKROGET: case BLKFLSBUF: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); default: return -EINVAL; } diff -Nru a/drivers/block/ps2esdi.c b/drivers/block/ps2esdi.c --- a/drivers/block/ps2esdi.c Thu Mar 7 18:17:36 2002 +++ b/drivers/block/ps2esdi.c Thu Mar 7 18:17:36 2002 @@ -1111,7 +1111,7 @@ case BLKBSZGET: case BLKBSZSET: case BLKPG: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); } return (-EINVAL); } diff -Nru a/drivers/block/rd.c b/drivers/block/rd.c --- a/drivers/block/rd.c Thu Mar 7 18:17:37 2002 +++ b/drivers/block/rd.c Thu Mar 7 18:17:37 2002 @@ -268,7 +268,7 @@ goto fail; set_bit(BIO_UPTODATE, &sbh->bi_flags); - sbh->bi_end_io(sbh, len >> 9); + sbh->bi_end_io(sbh); return 0; fail: bio_io_error(sbh); @@ -311,7 +311,7 @@ case BLKROSET: case BLKROGET: case BLKSSZGET: - error = blk_ioctl(inode->i_rdev, cmd, arg); + error = blk_ioctl(inode->i_bdev, cmd, arg); }; out: return error; diff -Nru a/drivers/block/xd.c b/drivers/block/xd.c --- a/drivers/block/xd.c Thu Mar 7 18:17:40 2002 +++ b/drivers/block/xd.c Thu Mar 7 18:17:40 2002 @@ -355,7 +355,7 @@ case BLKROSET: case BLKROGET: case BLKPG: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); default: return -EINVAL; diff -Nru a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c --- a/drivers/cdrom/cdrom.c Thu Mar 7 18:17:39 2002 +++ b/drivers/cdrom/cdrom.c Thu Mar 7 18:17:39 2002 @@ -263,6 +263,7 @@ #include #include #include +#include #include #include @@ -1724,6 +1725,11 @@ because they fill up the sys log when CD players poll the drive. */ switch (cmd) { + case BLKROSET: + case BLKROGET: + case BLKFLSBUF: + case BLKSSZGET: + return blk_ioctl(ip->i_bdev, cmd, arg); case CDROMSUBCHNL: { struct cdrom_subchnl q; u_char requested, back; diff -Nru a/drivers/char/Config.help b/drivers/char/Config.help --- a/drivers/char/Config.help Thu Mar 7 18:17:43 2002 +++ b/drivers/char/Config.help Thu Mar 7 18:17:43 2002 @@ -425,6 +425,24 @@ read . The module will be called istallion.o. +CONFIG_SERIAL_TX3912 + The TX3912 is a Toshiba RISC processor based o the MIPS 3900 core; + see . + Say Y here to enable kernel support for the on-board serial port. + +CONFIG_SERIAL_TX3912_CONSOLE + The TX3912 is a Toshiba RISC processor based o the MIPS 3900 core; + see . + Say Y here to direct console I/O to the on-board serial port. + +CONFIG_AU1000_SERIAL_CONSOLE + If you have an Alchemy AU1000 processor (MIPS based) and you want + to use a console on a serial port, say Y. Otherwise, say N. + +CONFIG_AU1000_UART + If you have an Alchemy AU1000 processor (MIPS based) and you want + to use serial ports, say Y. Otherwise, say N. + CONFIG_SYNCLINK Provides support for the SyncLink ISA and PCI multiprotocol serial adapters. These adapters support asynchronous and HDLC bit @@ -941,6 +959,11 @@ The module is called mixcomwd.o. If you want to compile it as a module, say M here and read . Most people will say N. + +CONFIG_EUROTECH_WDT + Enable support for the watchdog timer on the Eurotech CPU-1220 and + CPU-1410 cards. These are PC/104 SBCs. Spec sheets and product + information are at . CONFIG_IB700_WDT This is the driver for the hardware watchdog on the IB700 Single diff -Nru a/drivers/char/Makefile b/drivers/char/Makefile --- a/drivers/char/Makefile Thu Mar 7 18:17:42 2002 +++ b/drivers/char/Makefile Thu Mar 7 18:17:42 2002 @@ -23,7 +23,7 @@ export-objs := busmouse.o console.o keyboard.o sysrq.o \ misc.o pty.o random.o selection.o serial.o \ - sonypi.o tty_io.o tty_ioctl.o generic_serial.o + sonypi.o tty_io.o tty_ioctl.o generic_serial.o rtc.o mod-subdirs := ftape drm pcmcia diff -Nru a/drivers/char/acquirewdt.c b/drivers/char/acquirewdt.c --- a/drivers/char/acquirewdt.c Thu Mar 7 18:17:38 2002 +++ b/drivers/char/acquirewdt.c Thu Mar 7 18:17:38 2002 @@ -207,7 +207,8 @@ printk("WDT driver for Acquire single board computer initialising.\n"); spin_lock_init(&acq_lock); - misc_register(&acq_miscdev); + if (misc_register(&acq_miscdev)) + return -ENODEV; request_region(WDT_STOP, 1, "Acquire WDT"); request_region(WDT_START, 1, "Acquire WDT"); register_reboot_notifier(&acq_notifier); diff -Nru a/drivers/char/agp/agpgart_be.c b/drivers/char/agp/agpgart_be.c --- a/drivers/char/agp/agpgart_be.c Thu Mar 7 18:17:39 2002 +++ b/drivers/char/agp/agpgart_be.c Thu Mar 7 18:17:39 2002 @@ -622,7 +622,7 @@ table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) - set_bit(PG_reserved, &page->flags); + SetPageReserved(page); agp_bridge.gatt_table_real = (unsigned long *) table; CACHE_FLUSH(); @@ -632,7 +632,7 @@ if (agp_bridge.gatt_table == NULL) { for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) - clear_bit(PG_reserved, &page->flags); + ClearPageReserved(page); free_pages((unsigned long) table, page_order); @@ -699,7 +699,7 @@ table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) - clear_bit(PG_reserved, &page->flags); + ClearPageReserved(page); free_pages((unsigned long) agp_bridge.gatt_table_real, page_order); return 0; @@ -812,8 +812,8 @@ if (page == NULL) { return 0; } - atomic_inc(&page->count); - set_bit(PG_locked, &page->flags); + get_page(page); + LockPage(page); atomic_inc(&agp_bridge.current_memory_agp); return (unsigned long)page_address(page); } @@ -828,9 +828,8 @@ } page = virt_to_page(pt); - atomic_dec(&page->count); - clear_bit(PG_locked, &page->flags); - wake_up_page(page); + put_page(page); + UnlockPage(page); free_page((unsigned long) pt); atomic_dec(&agp_bridge.current_memory_agp); } @@ -2278,13 +2277,12 @@ if (page_map->real == NULL) { return -ENOMEM; } - set_bit(PG_reserved, &virt_to_page(page_map->real)->flags); + SetPageReserved(virt_to_page(page_map->real)); CACHE_FLUSH(); page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real), PAGE_SIZE); if (page_map->remapped == NULL) { - clear_bit(PG_reserved, - &virt_to_page(page_map->real)->flags); + ClearPageReserved(virt_to_page(page_map->real)); free_page((unsigned long) page_map->real); page_map->real = NULL; return -ENOMEM; @@ -2301,8 +2299,7 @@ static void amd_free_page_map(amd_page_map *page_map) { iounmap(page_map->remapped); - clear_bit(PG_reserved, - &virt_to_page(page_map->real)->flags); + ClearPageReserved(virt_to_page(page_map->real)); free_page((unsigned long) page_map->real); } @@ -2790,8 +2787,8 @@ if (page == NULL) return 0; - atomic_inc(&page->count); - set_bit(PG_locked, &page->flags); + get_page(page); + LockPage(page); atomic_inc(&agp_bridge.current_memory_agp); global_cache_flush(); @@ -2826,9 +2823,8 @@ } page = virt_to_page(pt); - atomic_dec(&page->count); - clear_bit(PG_locked, &page->flags); - wake_up_page(page); + put_page(page); + UnlockPage(page); free_page((unsigned long) pt); atomic_dec(&agp_bridge.current_memory_agp); } @@ -2910,13 +2906,12 @@ if (page_map->real == NULL) { return -ENOMEM; } - set_bit(PG_reserved, &virt_to_page(page_map->real)->flags); + SetPageReserved(virt_to_page(page_map->real)); CACHE_FLUSH(); page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real), PAGE_SIZE); if (page_map->remapped == NULL) { - clear_bit(PG_reserved, - &virt_to_page(page_map->real)->flags); + ClearPageReserved(virt_to_page(page_map->real)); free_page((unsigned long) page_map->real); page_map->real = NULL; return -ENOMEM; @@ -2933,8 +2928,7 @@ static void serverworks_free_page_map(serverworks_page_map *page_map) { iounmap(page_map->remapped); - clear_bit(PG_reserved, - &virt_to_page(page_map->real)->flags); + ClearPageReserved(virt_to_page(page_map->real)); free_page((unsigned long) page_map->real); } diff -Nru a/drivers/char/drm/drm_scatter.h b/drivers/char/drm/drm_scatter.h --- a/drivers/char/drm/drm_scatter.h Thu Mar 7 18:17:37 2002 +++ b/drivers/char/drm/drm_scatter.h Thu Mar 7 18:17:37 2002 @@ -66,9 +66,6 @@ drm_scatter_gather_t request; drm_sg_mem_t *entry; unsigned long pages, i, j; - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte, pte_entry; DRM_DEBUG( "%s\n", __FUNCTION__ ); @@ -135,25 +132,9 @@ DRM_DEBUG( "sg alloc virtual = %p\n", entry->virtual ); for ( i = entry->handle, j = 0 ; j < pages ; i += PAGE_SIZE, j++ ) { - pgd = pgd_offset_k( i ); - if ( !pgd_present( *pgd ) ) + entry->pagelist[j] = vmalloc_to_page((void *)i); + if (!entry->pagelist[j]) goto failed; - - pmd = pmd_offset( pgd, i ); - if ( !pmd_present( *pmd ) ) - goto failed; - - preempt_disable(); - pte = pte_offset_map(pmd, i); - pte_entry = *pte; - pte_unmap(pte); - preempt_enable(); - - if (!pte_present(pte_entry)) - goto failed; - - entry->pagelist[j] = pte_page(pte_entry); - SetPageReserved(entry->pagelist[j]); } diff -Nru a/drivers/char/drm/drm_vm.h b/drivers/char/drm/drm_vm.h --- a/drivers/char/drm/drm_vm.h Thu Mar 7 18:17:44 2002 +++ b/drivers/char/drm/drm_vm.h Thu Mar 7 18:17:44 2002 @@ -152,9 +152,6 @@ #endif unsigned long offset; unsigned long i; - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte, entry; struct page *page; if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ @@ -162,26 +159,9 @@ offset = address - vma->vm_start; i = (unsigned long)map->handle + offset; - /* We have to walk page tables here because we need large SAREA's, and - * they need to be virtually contiguous in kernel space. - */ - pgd = pgd_offset_k( i ); - if (!pgd_present(*pgd)) - goto oom; - pmd = pmd_offset( pgd, i ); - if (!pmd_present(*pmd)) - goto oom; - - preempt_disable(); - pte = pte_offset_map(pmd, i); - entry = *pte; - pte_unmap(pte); - preempt_enable(); - - if (!pte_present(entry)) - goto oom; - - page = pte_page(entry); + page = vmalloc_to_page((void *)i); + if (!page) + return NOPAGE_OOM; get_page(page); DRM_DEBUG("shm_nopage 0x%lx\n", address); @@ -190,8 +170,6 @@ #else return page; #endif -oom: - return NOPAGE_OOM; } /* Special close routine which deletes map information if we are the last diff -Nru a/drivers/char/drm/i810_dma.c b/drivers/char/drm/i810_dma.c --- a/drivers/char/drm/i810_dma.c Thu Mar 7 18:17:42 2002 +++ b/drivers/char/drm/i810_dma.c Thu Mar 7 18:17:42 2002 @@ -286,8 +286,8 @@ if(address == 0UL) return 0; - atomic_inc(&virt_to_page(address)->count); - set_bit(PG_locked, &virt_to_page(address)->flags); + get_page(virt_to_page(address)); + LockPage(virt_to_page(address)); return address; } @@ -296,9 +296,8 @@ { if (page) { struct page *p = virt_to_page(page); - atomic_dec(p); - clear_bit(PG_locked, &p->flags); - wake_up_page(p); + put_page(p); + UnlockPage(p); free_page(page); } } diff -Nru a/drivers/char/lp.c b/drivers/char/lp.c --- a/drivers/char/lp.c Thu Mar 7 18:17:36 2002 +++ b/drivers/char/lp.c Thu Mar 7 18:17:36 2002 @@ -270,7 +270,7 @@ return error; } -static int lp_wait_ready(int minor) +static int lp_wait_ready(int minor, int nonblock) { int error = 0; @@ -281,7 +281,7 @@ do { error = lp_check_status (minor); - if (error && (LP_F(minor) & LP_ABORT)) + if (error && (nonblock || (LP_F(minor) & LP_ABORT))) break; if (signal_pending (current)) { error = -EINTR; @@ -300,6 +300,8 @@ ssize_t retv = 0; ssize_t written; size_t copy_size = count; + int nonblock = ((file->f_flags & O_NONBLOCK) || + (LP_F(minor) & LP_ABORT)); #ifdef LP_STATS if (jiffies-lp_table[minor].lastcall > LP_TIME(minor)) @@ -326,9 +328,10 @@ lp_table[minor].best_mode); parport_set_timeout (lp_table[minor].dev, - lp_table[minor].timeout); + (nonblock ? PARPORT_INACTIVITY_O_NONBLOCK + : lp_table[minor].timeout)); - if ((retv = lp_wait_ready (minor)) == 0) + if ((retv = lp_wait_ready (minor, nonblock)) == 0) do { /* Write the data. */ written = parport_write (port, kbuf, copy_size); @@ -354,12 +357,16 @@ IEEE1284_MODE_COMPAT); lp_table[minor].current_mode = IEEE1284_MODE_COMPAT; - error = lp_wait_ready (minor); + error = lp_wait_ready (minor, nonblock); if (error) { if (retv == 0) retv = error; break; + } else if (nonblock) { + if (retv == 0) + retv = -EAGAIN; + break; } parport_yield_blocking (lp_table[minor].dev); @@ -407,6 +414,8 @@ struct parport *port = lp_table[minor].dev->port; ssize_t retval = 0; char *kbuf = lp_table[minor].lp_buffer; + int nonblock = ((file->f_flags & O_NONBLOCK) || + (LP_F(minor) & LP_ABORT)); if (count > LP_BUFFER_SIZE) count = LP_BUFFER_SIZE; @@ -415,7 +424,53 @@ return -EINTR; lp_claim_parport_or_block (&lp_table[minor]); - retval = parport_read (port, kbuf, count); + + parport_set_timeout (lp_table[minor].dev, + (nonblock ? PARPORT_INACTIVITY_O_NONBLOCK + : lp_table[minor].timeout)); + + parport_negotiate (lp_table[minor].dev->port, IEEE1284_MODE_COMPAT); + if (parport_negotiate (lp_table[minor].dev->port, + IEEE1284_MODE_NIBBLE)) { + retval = -EIO; + goto out; + } + + while (retval == 0) { + retval = parport_read (port, kbuf, count); + + if (retval > 0) + break; + + if (nonblock) { + retval = -EAGAIN; + break; + } + + /* Wait for data. */ + + if (lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE) { + parport_negotiate (lp_table[minor].dev->port, + IEEE1284_MODE_COMPAT); + lp_error (minor); + if (parport_negotiate (lp_table[minor].dev->port, + IEEE1284_MODE_NIBBLE)) { + retval = -EIO; + goto out; + } + } else + interruptible_sleep_on_timeout (&lp_table[minor].waitq, + LP_TIMEOUT_POLLED); + + if (signal_pending (current)) { + retval = -ERESTARTSYS; + break; + } + + cond_resched (); + } + parport_negotiate (lp_table[minor].dev->port, IEEE1284_MODE_COMPAT); + out: lp_release_parport (&lp_table[minor]); if (retval > 0 && copy_to_user (buf, kbuf, retval)) @@ -476,7 +531,6 @@ printk (KERN_INFO "lp%d: ECP mode\n", minor); lp_table[minor].best_mode = IEEE1284_MODE_ECP; } else { - printk (KERN_INFO "lp%d: compatibility mode\n", minor); lp_table[minor].best_mode = IEEE1284_MODE_COMPAT; } /* Leave peripheral in compatibility mode */ diff -Nru a/drivers/char/mem.c b/drivers/char/mem.c --- a/drivers/char/mem.c Thu Mar 7 18:17:45 2002 +++ b/drivers/char/mem.c Thu Mar 7 18:17:45 2002 @@ -273,6 +273,8 @@ return virtr + read; } +extern long vwrite(char *buf, char *addr, unsigned long count); + /* * This function writes to the *virtual* memory as seen by the kernel. */ @@ -280,12 +282,46 @@ size_t count, loff_t *ppos) { unsigned long p = *ppos; + ssize_t wrote = 0; + ssize_t virtr = 0; + char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */ + + if (p < (unsigned long) high_memory) { + wrote = count; + if (count > (unsigned long) high_memory - p) + wrote = (unsigned long) high_memory - p; + + wrote = do_write_mem(file, (void*)p, p, buf, wrote, ppos); + + p += wrote; + buf += wrote; + count -= wrote; + } + + if (count > 0) { + kbuf = (char *)__get_free_page(GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + while (count > 0) { + int len = count; + + if (len > PAGE_SIZE) + len = PAGE_SIZE; + if (len && copy_from_user(kbuf, buf, len)) { + free_page((unsigned long)kbuf); + return -EFAULT; + } + len = vwrite(kbuf, (char *)p, len); + count -= len; + buf += len; + virtr += len; + p += len; + } + free_page((unsigned long)kbuf); + } - if (p >= (unsigned long) high_memory) - return 0; - if (count > (unsigned long) high_memory - p) - count = (unsigned long) high_memory - p; - return do_write_mem(file, (void*)p, p, buf, count, ppos); + *ppos = p; + return virtr + wrote; } #if !defined(__mc68000__) diff -Nru a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c --- a/drivers/char/mwave/mwavedd.c Thu Mar 7 18:17:41 2002 +++ b/drivers/char/mwave/mwavedd.c Thu Mar 7 18:17:41 2002 @@ -461,7 +461,7 @@ * mwave_exit is called on module unload * mwave_exit is also used to clean up after an aborted mwave_init */ -static void __exit mwave_exit(void) +static void mwave_exit(void) { pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd; diff -Nru a/drivers/char/ppdev.c b/drivers/char/ppdev.c --- a/drivers/char/ppdev.c Thu Mar 7 18:17:43 2002 +++ b/drivers/char/ppdev.c Thu Mar 7 18:17:43 2002 @@ -4,7 +4,7 @@ * This is the code behind /dev/parport* -- it allows a user-space * application to use the parport subsystem. * - * Copyright (C) 1998-2000 Tim Waugh + * Copyright (C) 1998-2000, 2002 Tim Waugh * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -80,6 +80,7 @@ unsigned char irqctl; struct ieee1284_info state; struct ieee1284_info saved_state; + long default_inactivity; }; /* pp_struct.flags bitfields */ @@ -107,7 +108,6 @@ struct pp_struct *pp = file->private_data; char * kbuffer; ssize_t bytes_read = 0; - ssize_t got = 0; struct parport *pport; int mode; @@ -125,8 +125,13 @@ pport = pp->pdev->port; mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR); - while (bytes_read < count) { - ssize_t need = min_t(unsigned long, count - bytes_read, PP_BUFFER_SIZE); + parport_set_timeout (pp->pdev, + (file->f_flags & O_NONBLOCK) ? + PARPORT_INACTIVITY_O_NONBLOCK : + pp->default_inactivity); + + while (bytes_read == 0) { + ssize_t need = min_t(unsigned long, count, PP_BUFFER_SIZE); if (mode == IEEE1284_MODE_EPP) { /* various specials for EPP mode */ @@ -144,35 +149,32 @@ } else { fn = pport->ops->epp_read_data; } - got = (*fn)(pport, kbuffer, need, flags); + bytes_read = (*fn)(pport, kbuffer, need, flags); } else { - got = parport_read (pport, kbuffer, need); + bytes_read = parport_read (pport, kbuffer, need); } - if (got <= 0) { - if (!bytes_read) { - bytes_read = got; - } + if (bytes_read != 0) break; - } - if (copy_to_user (buf + bytes_read, kbuffer, got)) { - bytes_read = -EFAULT; + if (file->f_flags & O_NONBLOCK) { + bytes_read = -EAGAIN; break; } - bytes_read += got; - if (signal_pending (current)) { - if (!bytes_read) { - bytes_read = -EINTR; - } + bytes_read = -ERESTARTSYS; break; } cond_resched(); } + parport_set_timeout (pp->pdev, pp->default_inactivity); + + if (bytes_read > 0 && copy_to_user (buf, kbuffer, bytes_read)) + bytes_read = -EFAULT; + kfree (kbuffer); pp_enable_irq (pp); return bytes_read; @@ -203,6 +205,11 @@ pport = pp->pdev->port; mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR); + parport_set_timeout (pp->pdev, + (file->f_flags & O_NONBLOCK) ? + PARPORT_INACTIVITY_O_NONBLOCK : + pp->default_inactivity); + while (bytes_written < count) { ssize_t n = min_t(unsigned long, count - bytes_written, PP_BUFFER_SIZE); @@ -233,6 +240,12 @@ bytes_written += wrote; + if (file->f_flags & O_NONBLOCK) { + if (!bytes_written) + bytes_written = -EAGAIN; + break; + } + if (signal_pending (current)) { if (!bytes_written) { bytes_written = -EINTR; @@ -243,6 +256,8 @@ cond_resched(); } + parport_set_timeout (pp->pdev, pp->default_inactivity); + kfree (kbuffer); pp_enable_irq (pp); return bytes_written; @@ -352,6 +367,8 @@ pp->saved_state.phase = info->phase; info->mode = pp->state.mode; info->phase = pp->state.phase; + pp->default_inactivity = parport_set_timeout (pp->pdev, 0); + parport_set_timeout (pp->pdev, pp->default_inactivity); return 0; } diff -Nru a/drivers/char/rtc.c b/drivers/char/rtc.c --- a/drivers/char/rtc.c Thu Mar 7 18:17:42 2002 +++ b/drivers/char/rtc.c Thu Mar 7 18:17:42 2002 @@ -41,9 +41,11 @@ * 1.10c Cesar Barros: SMP locking fixes and cleanup * 1.10d Paul Gortmaker: delete paranoia check in rtc_exit * 1.10e Maciej W. Rozycki: Handle DECstation's year weirdness. + * 1.11 Takashi Iwai: Kernel access functions + * rtc_register/rtc_unregister/rtc_control */ -#define RTC_VERSION "1.10e" +#define RTC_VERSION "1.11" #define RTC_IO_EXTENT 0x10 /* Only really two ports, but... */ @@ -139,6 +141,11 @@ static unsigned long rtc_freq = 0; /* Current periodic IRQ rate */ static unsigned long rtc_irq_data = 0; /* our output to the world */ +#if RTC_IRQ +static spinlock_t rtc_task_lock = SPIN_LOCK_UNLOCKED; +static rtc_task_t *rtc_callback = NULL; +#endif + /* * If this driver ever becomes modularised, it will be really nice * to make the epoch retain its value across module reload... @@ -180,6 +187,10 @@ spin_unlock (&rtc_lock); /* Now do the rest of the actions */ + spin_lock(&rtc_task_lock); + if (rtc_callback) + rtc_callback->func(rtc_callback->private_data); + spin_unlock(&rtc_task_lock); wake_up_interruptible(&rtc_wait); kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); @@ -244,8 +255,7 @@ #endif } -static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) { struct rtc_time wtime; @@ -295,7 +305,7 @@ * We don't really want Joe User enabling more * than 64Hz of interrupts on a multi-user machine. */ - if ((rtc_freq > 64) && (!capable(CAP_SYS_RESOURCE))) + if (!kernel && (rtc_freq > 64) && (!capable(CAP_SYS_RESOURCE))) return -EACCES; if (!(rtc_status & RTC_TIMER_ON)) { @@ -493,7 +503,7 @@ * We don't really want Joe User generating more * than 64Hz of interrupts on a multi-user machine. */ - if ((arg > 64) && (!capable(CAP_SYS_RESOURCE))) + if (!kernel && (arg > 64) && (!capable(CAP_SYS_RESOURCE))) return -EACCES; while (arg > (1<func == NULL) + return -EINVAL; + spin_lock_irq(&rtc_lock); + if (rtc_status & RTC_IS_OPEN) { + spin_unlock_irq(&rtc_lock); + return -EBUSY; + } + spin_lock(&rtc_task_lock); + if (rtc_callback) { + spin_unlock(&rtc_task_lock); + spin_unlock_irq(&rtc_lock); + return -EBUSY; + } + rtc_status |= RTC_IS_OPEN; + rtc_callback = task; + spin_unlock(&rtc_task_lock); + spin_unlock_irq(&rtc_lock); + return 0; +#endif +} + +int rtc_unregister(rtc_task_t *task) +{ +#if !RTC_IRQ + return -EIO; +#else + unsigned char tmp; + + spin_lock_irq(&rtc_task_lock); + if (rtc_callback != task) { + spin_unlock_irq(&rtc_task_lock); + return -ENXIO; + } + rtc_callback = NULL; + spin_lock(&rtc_lock); + /* disable controls */ + tmp = CMOS_READ(RTC_CONTROL); + tmp &= ~RTC_PIE; + tmp &= ~RTC_AIE; + tmp &= ~RTC_UIE; + CMOS_WRITE(tmp, RTC_CONTROL); + CMOS_READ(RTC_INTR_FLAGS); + if (rtc_status & RTC_TIMER_ON) { + rtc_status &= ~RTC_TIMER_ON; + del_timer(&rtc_irq_timer); + } + rtc_status &= ~RTC_IS_OPEN; + spin_unlock(&rtc_lock); + spin_unlock_irq(&rtc_task_lock); + return 0; +#endif +} + +int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg) +{ +#if !RTC_IRQ + return -EIO; +#else + spin_lock_irq(&rtc_task_lock); + if (rtc_callback != task) { + spin_unlock_irq(&rtc_task_lock); + return -ENXIO; + } + spin_unlock_irq(&rtc_task_lock); + return rtc_do_ioctl(cmd, arg, 1); +#endif +} + + +/* * The various file operations we support. */ @@ -822,7 +917,6 @@ module_init(rtc_init); module_exit(rtc_exit); -EXPORT_NO_SYMBOLS; #if RTC_IRQ /* diff -Nru a/drivers/char/synclink.c b/drivers/char/synclink.c --- a/drivers/char/synclink.c Thu Mar 7 18:17:42 2002 +++ b/drivers/char/synclink.c Thu Mar 7 18:17:42 2002 @@ -941,7 +941,7 @@ name: "synclink", id_table: synclink_pci_tbl, probe: synclink_init_one, - remove: synclink_remove_one, + remove: __devexit_p(synclink_remove_one), }; static struct tty_driver serial_driver, callout_driver; @@ -1812,7 +1812,7 @@ /* Allocate and claim adapter resources */ retval = mgsl_claim_resources(info); - /* perform existance check and diagnostics */ + /* perform existence check and diagnostics */ if ( !retval ) retval = mgsl_adapter_test(info); @@ -8220,7 +8220,7 @@ return 0; } -static void __exit synclink_remove_one (struct pci_dev *dev) +static void __devexit synclink_remove_one (struct pci_dev *dev) { } diff -Nru a/drivers/char/sysrq.c b/drivers/char/sysrq.c --- a/drivers/char/sysrq.c Thu Mar 7 18:17:41 2002 +++ b/drivers/char/sysrq.c Thu Mar 7 18:17:41 2002 @@ -161,7 +161,7 @@ } file_list_unlock(); DQUOT_OFF(sb); - fsync_dev(sb->s_dev); + fsync_bdev(sb->s_bdev); flags = MS_RDONLY; if (sb->s_op && sb->s_op->remount_fs) { ret = sb->s_op->remount_fs(sb, &flags, NULL); @@ -174,7 +174,7 @@ } else printk("nothing to do\n"); } else { /* Sync only */ - fsync_dev(sb->s_dev); + fsync_bdev(sb->s_bdev); printk("OK\n"); } console_loglevel = orig_loglevel; diff -Nru a/drivers/char/wdt_pci.c b/drivers/char/wdt_pci.c --- a/drivers/char/wdt_pci.c Thu Mar 7 18:17:38 2002 +++ b/drivers/char/wdt_pci.c Thu Mar 7 18:17:38 2002 @@ -558,7 +558,7 @@ } -static void __exit wdtpci_remove_one (struct pci_dev *pdev) +static void __devexit wdtpci_remove_one (struct pci_dev *pdev) { /* here we assume only one device will ever have * been picked up and registered by probe function */ @@ -583,7 +583,7 @@ name: "wdt-pci", id_table: wdtpci_pci_tbl, probe: wdtpci_init_one, - remove: wdtpci_remove_one, + remove: __devexit_p(wdtpci_remove_one), }; diff -Nru a/drivers/hotplug/Config.help b/drivers/hotplug/Config.help --- a/drivers/hotplug/Config.help Thu Mar 7 18:17:36 2002 +++ b/drivers/hotplug/Config.help Thu Mar 7 18:17:36 2002 @@ -29,3 +29,14 @@ When in doubt, say N. +CONFIG_HOTPLUG_PCI_IBM + Say Y here if you have a motherboard with a IBM PCI Hotplug + controller. + + 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 cpqphp.o. If you want to compile it + as a module, say M here and read . + + When in doubt, say N. + diff -Nru a/drivers/hotplug/Config.in b/drivers/hotplug/Config.in --- a/drivers/hotplug/Config.in Thu Mar 7 18:17:39 2002 +++ b/drivers/hotplug/Config.in Thu Mar 7 18:17:39 2002 @@ -4,9 +4,10 @@ mainmenu_option next_comment comment 'PCI Hotplug Support' -dep_tristate 'Support for PCI Hotplug (EXPERIMENTAL)' CONFIG_HOTPLUG_PCI $CONFIG_DDFS $CONFIG_EXPERIMENTAL +dep_tristate 'Support for PCI Hotplug (EXPERIMENTAL)' CONFIG_HOTPLUG_PCI $CONFIG_PCI $CONFIG_EXPERIMENTAL -dep_tristate ' Compaq PCI Hotplug driver' CONFIG_HOTPLUG_PCI_COMPAQ $CONFIG_HOTPLUG_PCI +dep_tristate ' Compaq PCI Hotplug driver' CONFIG_HOTPLUG_PCI_COMPAQ $CONFIG_HOTPLUG_PCI $CONFIG_X86 dep_mbool ' Save configuration into NVRAM on Compaq servers' CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM $CONFIG_HOTPLUG_PCI_COMPAQ +dep_tristate ' IBM PCI Hotplug driver' CONFIG_HOTPLUG_PCI_IBM $CONFIG_HOTPLUG_PCI $CONFIG_X86_IO_APIC $CONFIG_X86 endmenu diff -Nru a/drivers/hotplug/Makefile b/drivers/hotplug/Makefile --- a/drivers/hotplug/Makefile Thu Mar 7 18:17:39 2002 +++ b/drivers/hotplug/Makefile Thu Mar 7 18:17:39 2002 @@ -4,12 +4,13 @@ O_TARGET := vmlinux-obj.o -list-multi := cpqphp.o pci_hotplug.o +list-multi := cpqphp.o pci_hotplug.o ibmphp.o export-objs := pci_hotplug_core.o pci_hotplug_util.o obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o +obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o pci_hotplug-objs := pci_hotplug_core.o \ pci_hotplug_util.o @@ -19,6 +20,12 @@ cpqphp_proc.o \ cpqphp_pci.o +ibmphp-objs := ibmphp_core.o \ + ibmphp_ebda.o \ + ibmphp_pci.o \ + ibmphp_res.o \ + ibmphp_hpc.o + ifeq ($(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM),y) cpqphp-objs += cpqphp_nvram.o endif @@ -31,4 +38,7 @@ cpqphp.o: $(cpqphp-objs) $(LD) -r -o $@ $(cpqphp-objs) + +ibmphp.o: $(ibmphp-objs) + $(LD) -r -o $@ $(ibmphp-objs) diff -Nru a/drivers/hotplug/cpqphp_proc.c b/drivers/hotplug/cpqphp_proc.c --- a/drivers/hotplug/cpqphp_proc.c Thu Mar 7 18:17:46 2002 +++ b/drivers/hotplug/cpqphp_proc.c Thu Mar 7 18:17:46 2002 @@ -177,7 +177,7 @@ int cpqhp_proc_init_ctrl (void) { - ctrl_proc_root = proc_mkdir("driver/hpc", NULL); + ctrl_proc_root = proc_mkdir("hpc", proc_root_driver); if (!ctrl_proc_root) return -ENOMEM; ctrl_proc_root->owner = THIS_MODULE; diff -Nru a/drivers/hotplug/ibmphp.h b/drivers/hotplug/ibmphp.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/ibmphp.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,745 @@ +#ifndef __IBMPHP_H +#define __IBMPHP_H + +/* + * IBM Hot Plug Controller Driver + * + * Written By: Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation + * + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2002 IBM Corp. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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. + * + * Send feedback to + * + */ + +#include "pci_hotplug.h" + +extern int ibmphp_debug; + +#if !defined(CONFIG_HOTPLUG_PCI_IBM_MODULE) + #define MY_NAME "ibmphpd" +#else + #define MY_NAME THIS_MODULE->name +#endif +#define debug(fmt, arg...) do { if (ibmphp_debug) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0) +#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) + + +/* EBDA stuff */ + +/*********************************************************** +* SLOT CAPABILITY * +***********************************************************/ + +#define EBDA_SLOT_133_MAX 0x20 +#define EBDA_SLOT_100_MAX 0x10 +#define EBDA_SLOT_66_MAX 0x02 +#define EBDA_SLOT_PCIX_CAP 0x08 + + +/************************************************************ +* RESOURE TYPE * +************************************************************/ + +#define EBDA_RSRC_TYPE_MASK 0x03 +#define EBDA_IO_RSRC_TYPE 0x00 +#define EBDA_MEM_RSRC_TYPE 0x01 +#define EBDA_PFM_RSRC_TYPE 0x03 +#define EBDA_RES_RSRC_TYPE 0x02 + + +/************************************************************* +* IO RESTRICTION TYPE * +*************************************************************/ + +#define EBDA_IO_RESTRI_MASK 0x0c +#define EBDA_NO_RESTRI 0x00 +#define EBDA_AVO_VGA_ADDR 0x04 +#define EBDA_AVO_VGA_ADDR_AND_ALIA 0x08 +#define EBDA_AVO_ISA_ADDR 0x0c + + +/************************************************************** +* DEVICE TYPE DEF * +**************************************************************/ + +#define EBDA_DEV_TYPE_MASK 0x10 +#define EBDA_PCI_DEV 0x10 +#define EBDA_NON_PCI_DEV 0x00 + + +/*************************************************************** +* PRIMARY DEF DEFINITION * +***************************************************************/ + +#define EBDA_PRI_DEF_MASK 0x20 +#define EBDA_PRI_PCI_BUS_INFO 0x20 +#define EBDA_NORM_DEV_RSRC_INFO 0x00 + + +//-------------------------------------------------------------- +// RIO TABLE DATA STRUCTURE +//-------------------------------------------------------------- + +struct rio_table_hdr { + u8 ver_num; + u8 scal_count; + u8 riodev_count; + u16 offset; +}; + +//------------------------------------------------------------- +// SCALABILITY DETAIL +//------------------------------------------------------------- + +struct scal_detail { + u8 node_id; + u32 cbar; + u8 port0_node_connect; + u8 port0_port_connect; + u8 port1_node_connect; + u8 port1_port_connect; + u8 port2_node_connect; + u8 port2_port_connect; +// struct list_head scal_detail_list; +}; + +//-------------------------------------------------------------- +// RIO DETAIL +//-------------------------------------------------------------- + +struct rio_detail { + u8 rio_node_id; + u32 bbar; + u8 rio_type; + u8 owner_id; + u8 port0_node_connect; + u8 port0_port_connect; + u8 port1_node_connect; + u8 port1_port_connect; + u8 first_slot_num; + u8 status; +// struct list_head rio_detail_list; +}; + + +/**************************************************************** +* HPC DESCRIPTOR NODE * +****************************************************************/ + +struct ebda_hpc_list { + u8 format; + u16 num_ctlrs; + short phys_addr; +// struct list_head ebda_hpc_list; +}; + +/***************************************************************** +* IN HPC DATA STRUCTURE, THE ASSOCIATED SLOT AND BUS * +* STRUCTURE * +*****************************************************************/ + +struct ebda_hpc_slot { + u8 slot_num; + u32 slot_bus_num; + u8 ctl_index; + u8 slot_cap; +}; + +struct ebda_hpc_bus { + u32 bus_num; +/* + u8 slots_at_33_conv; + u8 slots_at_66_conv; + u8 slots_at_66_pcix; + u8 slots_at_100_pcix; + u8 slots_at_133_pcix; +*/ +}; + + +/******************************************************************** +* THREE TYPE OF HOT PLUG CONTROLER * +********************************************************************/ + +struct isa_ctlr_access { + u16 io_start; + u16 io_end; +}; + +struct pci_ctlr_access { + u8 bus; + u8 dev_fun; +}; + +struct wpeg_i2c_ctlr_access { + ulong wpegbbar; + u8 i2c_addr; +}; + +/************************************************************************* +* RSTC DESCRIPTOR NODE * +*************************************************************************/ + +struct ebda_rsrc_list { + u8 format; + u16 num_entries; + u16 phys_addr; + struct ebda_rsrc_list *next; +}; + + +/*************************************************************************** +* PCI RSRC NODE * +***************************************************************************/ + +struct ebda_pci_rsrc { + u8 rsrc_type; + u8 bus_num; + u8 dev_fun; + ulong start_addr; + ulong end_addr; + struct list_head ebda_pci_rsrc_list; +}; + + +/*********************************************************** +* BUS_INFO DATE STRUCTURE * +***********************************************************/ + +struct bus_info { + u8 slot_min; + u8 slot_max; + u8 slot_count; + u8 busno; + u8 current_speed; + u8 supported_speed; + u8 controller_id; + u8 supported_bus_mode; + u8 current_bus_mode; + u8 index; + struct list_head bus_info_list; +}; + + +/*********************************************************** +* GLOBAL VARIABLES * +***********************************************************/ +extern struct list_head ibmphp_ebda_pci_rsrc_head; +extern struct list_head ibmphp_slot_head; + +/*********************************************************** +* FUNCTION PROTOTYPES * +***********************************************************/ + +extern void ibmphp_free_ebda_hpc_queue (void); +extern int ibmphp_access_ebda (void); +extern struct slot *ibmphp_get_slot_from_physical_num (u8); +extern int ibmphp_get_total_hp_slots (void); +extern void ibmphp_free_ibm_slot (struct slot *); +extern void ibmphp_free_bus_info_queue (void); +extern void ibmphp_free_ebda_pci_rsrc_queue (void); +extern struct bus_info *ibmphp_find_same_bus_num (u32); +extern int ibmphp_get_bus_index (u8); +extern u16 ibmphp_get_total_controllers (void); + +/* passed parameters */ +#define MEM 0 +#define IO 1 +#define PFMEM 2 + +/* bit masks */ +#define RESTYPE 0x03 +#define IOMASK 0x00 /* will need to take its complement */ +#define MMASK 0x01 +#define PFMASK 0x03 +#define PCIDEVMASK 0x10 /* we should always have PCI devices */ +#define PRIMARYBUSMASK 0x20 + +/* pci specific defines */ +#define PCI_VENDOR_ID_NOTVALID 0xFFFF +#define PCI_HEADER_TYPE_MULTIDEVICE 0x80 +#define PCI_HEADER_TYPE_MULTIBRIDGE 0x81 + +#define LATENCY 0x64 +#define CACHE 64 +#define DEVICEENABLE 0x015F /* CPQ has 0x0157 */ + +#define IOBRIDGE 0x1000 /* 4k */ +#define MEMBRIDGE 0x100000 /* 1M */ + +/* irqs */ +#define SCSI_IRQ 0x09 +#define LAN_IRQ 0x0A +#define OTHER_IRQ 0x0B + +/* Data Structures */ + +/* type is of the form x x xx xx + * | | | |_ 00 - I/O, 01 - Memory, 11 - PFMemory + * | | - 00 - No Restrictions, 01 - Avoid VGA, 10 - Avoid + * | | VGA and their aliases, 11 - Avoid ISA + * | - 1 - PCI device, 0 - non pci device + * - 1 - Primary PCI Bus Information (0 if Normal device) + * the IO restrictions [2:3] are only for primary buses + */ + + +/* we need this struct because there could be several resource blocks + * allocated per primary bus in the EBDA + */ +struct range_node { + int rangeno; + u32 start; + u32 end; + struct range_node *next; +}; + +struct bus_node { + u8 busno; + int noIORanges; + struct range_node *rangeIO; + int noMemRanges; + struct range_node *rangeMem; + int noPFMemRanges; + struct range_node *rangePFMem; + int needIOUpdate; + int needMemUpdate; + int needPFMemUpdate; + struct resource_node *firstIO; /* first IO resource on the Bus */ + struct resource_node *firstMem; /* first memory resource on the Bus */ + struct resource_node *firstPFMem; /* first prefetchable memory resource on the Bus */ + struct resource_node *firstPFMemFromMem; /* when run out of pfmem available, taking from Mem */ + struct list_head bus_list; +}; + +struct resource_node { + int rangeno; + u8 busno; + u8 devfunc; + u32 start; + u32 end; + u32 len; + int type; /* MEM, IO, PFMEM */ + u8 fromMem; /* this is to indicate that the range is from + * from the Memory bucket rather than from PFMem */ + struct resource_node *next; + struct resource_node *nextRange; /* for the other mem range on bus */ +}; + +struct res_needed { + u32 mem; + u32 pfmem; + u32 io; + u8 not_correct; /* needed for return */ + int devices[32]; /* for device numbers behind this bridge */ +}; + +/* functions */ + +extern int ibmphp_rsrc_init (void); +extern int ibmphp_add_resource (struct resource_node *); +extern int ibmphp_remove_resource (struct resource_node *); +extern int ibmphp_find_resource (struct bus_node *, u32, struct resource_node **, int); +extern int ibmphp_check_resource (struct resource_node *, u8); +extern int ibmphp_remove_bus (struct bus_node *, u8); +extern void ibmphp_free_resources (void); +extern int ibmphp_add_pfmem_from_mem (struct resource_node *); +extern struct bus_node *ibmphp_find_res_bus (u8); +extern void ibmphp_print_test (void); /* for debugging purposes */ + +extern void ibmphp_hpc_initvars (void); +extern int ibmphp_hpc_readslot (struct slot *, u8, u8 *); +extern int ibmphp_hpc_writeslot (struct slot *, u8); +extern void ibmphp_lock_operations (void); +extern void ibmphp_unlock_operations (void); +extern int ibmphp_hpc_fillhpslotinfo (struct hotplug_slot *); +extern int ibmphp_hpc_start_poll_thread (void); +extern void ibmphp_hpc_stop_poll_thread (void); + +//---------------------------------------------------------------------------- + + +//---------------------------------------------------------------------------- +// HPC return codes +//---------------------------------------------------------------------------- +#define FALSE 0x00 +#define TRUE 0x01 +#define HPC_ERROR 0xFF + +//----------------------------------------------------------------------------- +// BUS INFO +//----------------------------------------------------------------------------- +#define BUS_SPEED 0x30 +#define BUS_MODE 0x40 +#define BUS_MODE_PCIX 0x01 +#define BUS_MODE_PCI 0x00 +#define BUS_SPEED_2 0x20 +#define BUS_SPEED_1 0x10 +#define BUS_SPEED_33 0x00 +#define BUS_SPEED_66 0x01 +#define BUS_SPEED_100 0x02 +#define BUS_SPEED_133 0x03 +#define BUS_SPEED_66PCIX 0x04 +#define BUS_SPEED_66UNKNOWN 0x05 +#define BUS_STATUS_AVAILABLE 0x01 +#define BUS_CONTROL_AVAILABLE 0x02 +#define SLOT_LATCH_REGS_SUPPORTED 0x10 + +#define PRGM_MODEL_REV_LEVEL 0xF0 +#define MAX_ADAPTER_NONE 0x09 + +//---------------------------------------------------------------------------- +// HPC 'write' operations/commands +//---------------------------------------------------------------------------- +// Command Code State Write to reg +// Machine at index +//------------------------- ---- ------- ------------ +#define HPC_CTLR_ENABLEIRQ 0x00 // N 15 +#define HPC_CTLR_DISABLEIRQ 0x01 // N 15 +#define HPC_SLOT_OFF 0x02 // Y 0-14 +#define HPC_SLOT_ON 0x03 // Y 0-14 +#define HPC_SLOT_ATTNOFF 0x04 // N 0-14 +#define HPC_SLOT_ATTNON 0x05 // N 0-14 +#define HPC_CTLR_CLEARIRQ 0x06 // N 15 +#define HPC_CTLR_RESET 0x07 // Y 15 +#define HPC_CTLR_IRQSTEER 0x08 // N 15 +#define HPC_BUS_33CONVMODE 0x09 // Y 31-34 +#define HPC_BUS_66CONVMODE 0x0A // Y 31-34 +#define HPC_BUS_66PCIXMODE 0x0B // Y 31-34 +#define HPC_BUS_100PCIXMODE 0x0C // Y 31-34 +#define HPC_BUS_133PCIXMODE 0x0D // Y 31-34 +#define HPC_ALLSLOT_OFF 0x11 // Y 15 +#define HPC_ALLSLOT_ON 0x12 // Y 15 +#define HPC_SLOT_BLINKLED 0x13 // N 0-14 + +//---------------------------------------------------------------------------- +// read commands +//---------------------------------------------------------------------------- +#define READ_SLOTSTATUS 0x01 +#define READ_EXTSLOTSTATUS 0x02 +#define READ_BUSSTATUS 0x03 +#define READ_CTLRSTATUS 0x04 +#define READ_ALLSTAT 0x05 +#define READ_ALLSLOT 0x06 +#define READ_SLOTLATCHLOWREG 0x07 +#define READ_REVLEVEL 0x08 +#define READ_HPCOPTIONS 0x09 +//---------------------------------------------------------------------------- +// slot status +//---------------------------------------------------------------------------- +#define HPC_SLOT_POWER 0x01 +#define HPC_SLOT_CONNECT 0x02 +#define HPC_SLOT_ATTN 0x04 +#define HPC_SLOT_PRSNT2 0x08 +#define HPC_SLOT_PRSNT1 0x10 +#define HPC_SLOT_PWRGD 0x20 +#define HPC_SLOT_BUS_SPEED 0x40 +#define HPC_SLOT_LATCH 0x80 + +//---------------------------------------------------------------------------- +// HPC_SLOT_POWER status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_POWER_OFF 0x00 +#define HPC_SLOT_POWER_ON 0x01 + +//---------------------------------------------------------------------------- +// HPC_SLOT_CONNECT status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_CONNECTED 0x00 +#define HPC_SLOT_DISCONNECTED 0x01 + +//---------------------------------------------------------------------------- +// HPC_SLOT_ATTN status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_ATTN_OFF 0x00 +#define HPC_SLOT_ATTN_ON 0x01 +#define HPC_SLOT_ATTN_BLINK 0x02 + +//---------------------------------------------------------------------------- +// HPC_SLOT_PRSNT status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_EMPTY 0x00 +#define HPC_SLOT_PRSNT_7 0x01 +#define HPC_SLOT_PRSNT_15 0x02 +#define HPC_SLOT_PRSNT_25 0x03 + +//---------------------------------------------------------------------------- +// HPC_SLOT_PWRGD status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_PWRGD_FAULT_NONE 0x00 +#define HPC_SLOT_PWRGD_GOOD 0x01 + +//---------------------------------------------------------------------------- +// HPC_SLOT_BUS_SPEED status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_BUS_SPEED_OK 0x00 +#define HPC_SLOT_BUS_SPEED_MISM 0x01 + +//---------------------------------------------------------------------------- +// HPC_SLOT_LATCH status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_LATCH_OPEN 0x01 // NOTE : in PCI spec bit off = open +#define HPC_SLOT_LATCH_CLOSED 0x00 // NOTE : in PCI spec bit on = closed + + +//---------------------------------------------------------------------------- +// extended slot status +//---------------------------------------------------------------------------- +#define HPC_SLOT_PCIX 0x01 +#define HPC_SLOT_SPEED1 0x02 +#define HPC_SLOT_SPEED2 0x04 +#define HPC_SLOT_BLINK_ATTN 0x08 +#define HPC_SLOT_RSRVD1 0x10 +#define HPC_SLOT_RSRVD2 0x20 +#define HPC_SLOT_BUS_MODE 0x40 +#define HPC_SLOT_RSRVD3 0x80 + +//---------------------------------------------------------------------------- +// HPC_XSLOT_PCIX_CAP status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_PCIX_NO 0x00 +#define HPC_SLOT_PCIX_YES 0x01 + +//---------------------------------------------------------------------------- +// HPC_XSLOT_SPEED status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_SPEED_33 0x00 +#define HPC_SLOT_SPEED_66 0x01 +#define HPC_SLOT_SPEED_133 0x02 + +//---------------------------------------------------------------------------- +// HPC_XSLOT_ATTN_BLINK status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_ATTN_BLINK_OFF 0x00 +#define HPC_SLOT_ATTN_BLINK_ON 0x01 + +//---------------------------------------------------------------------------- +// HPC_XSLOT_BUS_MODE status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_BUS_MODE_OK 0x00 +#define HPC_SLOT_BUS_MODE_MISM 0x01 + +//---------------------------------------------------------------------------- +// Controller status +//---------------------------------------------------------------------------- +#define HPC_CTLR_WORKING 0x01 +#define HPC_CTLR_FINISHED 0x02 +#define HPC_CTLR_RESULT0 0x04 +#define HPC_CTLR_RESULT1 0x08 +#define HPC_CTLR_RESULE2 0x10 +#define HPC_CTLR_RESULT3 0x20 +#define HPC_CTLR_IRQ_ROUTG 0x40 +#define HPC_CTLR_IRQ_PENDG 0x80 + +//---------------------------------------------------------------------------- +// HPC_CTLR_WROKING status return codes +//---------------------------------------------------------------------------- +#define HPC_CTLR_WORKING_NO 0x00 +#define HPC_CTLR_WORKING_YES 0x01 + +//---------------------------------------------------------------------------- +// HPC_CTLR_FINISHED status return codes +//---------------------------------------------------------------------------- +#define HPC_CTLR_FINISHED_NO 0x00 +#define HPC_CTLR_FINISHED_YES 0x01 + +//---------------------------------------------------------------------------- +// HPC_CTLR_RESULT status return codes +//---------------------------------------------------------------------------- +#define HPC_CTLR_RESULT_SUCCESS 0x00 +#define HPC_CTLR_RESULT_FAILED 0x01 +#define HPC_CTLR_RESULT_RSVD 0x02 +#define HPC_CTLR_RESULT_NORESP 0x03 + + +//---------------------------------------------------------------------------- +// macro for slot info +//---------------------------------------------------------------------------- +#define SLOT_POWER(s) ((u8) ((s & HPC_SLOT_POWER) \ + ? HPC_SLOT_POWER_ON : HPC_SLOT_POWER_OFF)) + +#define SLOT_CONNECT(s) ((u8) ((s & HPC_SLOT_CONNECT) \ + ? HPC_SLOT_DISCONNECTED : HPC_SLOT_CONNECTED)) + +#define SLOT_ATTN(s,es) ((u8) ((es & HPC_SLOT_BLINK_ATTN) \ + ? HPC_SLOT_ATTN_BLINK \ + : ((s & HPC_SLOT_ATTN) ? HPC_SLOT_ATTN_ON : HPC_SLOT_ATTN_OFF))) + +#define SLOT_PRESENT(s) ((u8) ((s & HPC_SLOT_PRSNT1) \ + ? ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_EMPTY : HPC_SLOT_PRSNT_15) \ + : ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_PRSNT_25 : HPC_SLOT_PRSNT_7))) + +#define SLOT_PWRGD(s) ((u8) ((s & HPC_SLOT_PWRGD) \ + ? HPC_SLOT_PWRGD_GOOD : HPC_SLOT_PWRGD_FAULT_NONE)) + +#define SLOT_BUS_SPEED(s) ((u8) ((s & HPC_SLOT_BUS_SPEED) \ + ? HPC_SLOT_BUS_SPEED_MISM : HPC_SLOT_BUS_SPEED_OK)) + +#define SLOT_LATCH(s) ((u8) ((s & HPC_SLOT_LATCH) \ + ? HPC_SLOT_LATCH_CLOSED : HPC_SLOT_LATCH_OPEN)) + +#define SLOT_PCIX(es) ((u8) ((es & HPC_SLOT_PCIX) \ + ? HPC_SLOT_PCIX_YES : HPC_SLOT_PCIX_NO)) + +#define SLOT_SPEED(es) ((u8) ((es & HPC_SLOT_SPEED2) \ + ? ((es & HPC_SLOT_SPEED1) ? HPC_SLOT_SPEED_133 \ + : HPC_SLOT_SPEED_66) \ + : HPC_SLOT_SPEED_33)) + +#define SLOT_BUS_MODE(es) ((u8) ((es & HPC_SLOT_BUS_MODE) \ + ? HPC_SLOT_BUS_MODE_MISM : HPC_SLOT_BUS_MODE_OK)) + +//-------------------------------------------------------------------------- +// macro for bus info +//--------------------------------------------------------------------------- +#define CURRENT_BUS_SPEED(s) ((u8) (s & BUS_SPEED_2) \ + ? ((s & BUS_SPEED_1) ? BUS_SPEED_133 : BUS_SPEED_100) \ + : ((s & BUS_SPEED_1) ? BUS_SPEED_66 : BUS_SPEED_33)) + +#define CURRENT_BUS_MODE(s) ((u8) (s & BUS_MODE) ? BUS_MODE_PCIX : BUS_MODE_PCI) + +#define READ_BUS_STATUS(s) ((u8) (s->options & BUS_STATUS_AVAILABLE)) + +#define READ_BUS_MODE(s) ((s->revision & PRGM_MODEL_REV_LEVEL) >= 0x20) + +#define SET_BUS_STATUS(s) ((u8) (s->options & BUS_CONTROL_AVAILABLE)) + +#define READ_SLOT_LATCH(s) ((u8) (s->options & SLOT_LATCH_REGS_SUPPORTED)) + +//---------------------------------------------------------------------------- +// macro for controller info +//---------------------------------------------------------------------------- +#define CTLR_WORKING(c) ((u8) ((c & HPC_CTLR_WORKING) \ + ? HPC_CTLR_WORKING_YES : HPC_CTLR_WORKING_NO)) +#define CTLR_FINISHED(c) ((u8) ((c & HPC_CTLR_FINISHED) \ + ? HPC_CTLR_FINISHED_YES : HPC_CTLR_FINISHED_NO)) +#define CTLR_RESULT(c) ((u8) ((c & HPC_CTLR_RESULT1) \ + ? ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_NORESP \ + : HPC_CTLR_RESULT_RSVD) \ + : ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_FAILED \ + : HPC_CTLR_RESULT_SUCCESS))) + +// command that affect the state machine of HPC +#define NEEDTOCHECK_CMDSTATUS(c) ((c == HPC_SLOT_OFF) || \ + (c == HPC_SLOT_ON) || \ + (c == HPC_CTLR_RESET) || \ + (c == HPC_BUS_33CONVMODE) || \ + (c == HPC_BUS_66CONVMODE) || \ + (c == HPC_BUS_66PCIXMODE) || \ + (c == HPC_BUS_100PCIXMODE) || \ + (c == HPC_BUS_133PCIXMODE) || \ + (c == HPC_ALLSLOT_OFF) || \ + (c == HPC_ALLSLOT_ON)) + + +/* Core part of the driver */ + +#define ENABLE 1 +#define DISABLE 0 + +#define ADD 0 +#define REMOVE 1 +#define DETAIL 2 + +#define MAX_OPS 3 +#define CARD_INFO 0x07 +#define PCIX133 0x07 +#define PCIX66 0x05 +#define PCI66 0x04 + +extern struct pci_ops *ibmphp_pci_root_ops; + +/* Variables */ + +struct pci_func { + struct pci_dev *dev; /* from the OS */ + u8 busno; + u8 device; + u8 function; + struct resource_node *io[6]; + struct resource_node *mem[6]; + struct resource_node *pfmem[6]; + struct pci_func *next; + int devices[32]; /* for bridge config */ + u8 irq[4]; /* for interrupt config */ + u8 bus; /* flag for unconfiguring, to say if PPB */ +}; + +struct slot { + u8 bus; + u8 device; + u8 number; + char name[100]; + u32 capabilities; + struct hotplug_slot *hotplug_slot; + struct controller *ctrl; + struct pci_func *func; + u8 irq[4]; + u8 flag; /* this is for disable slot and polling */ + int bit_mode; /* 0 = 32, 1 = 64 */ + u8 ctlr_index; + struct bus_info *bus_on; + struct list_head ibm_slot_list; + u8 status; + u8 ext_status; + u8 busstatus; +}; + +struct controller { + struct ebda_hpc_slot *slots; + struct ebda_hpc_bus *buses; + u8 revision; + u8 options; /* which options HPC supports */ + u8 status; + u8 ctlr_id; /* TONI */ + u8 slot_count; + u8 bus_count; + u8 ctlr_relative_id; + u32 irq; + union { + struct isa_ctlr_access isa_ctlr; + struct pci_ctlr_access pci_ctlr; + struct wpeg_i2c_ctlr_access wpeg_ctlr; + } u; + u8 ctlr_type; + struct list_head ebda_hpc_list; +}; + +/* Functions */ + +extern int ibmphp_init_devno (struct slot **); /* This function is called from EBDA, so we need it not be static */ +extern int ibmphp_disable_slot (struct hotplug_slot *); /* This function is called from HPC, so we need it to not be static */ +extern int ibmphp_update_slot_info (struct slot *); /* This function is called from HPC, so we need it to not be be static */ +extern int ibmphp_configure_card (struct pci_func *, u8); +extern int ibmphp_unconfigure_card (struct slot **, int); +extern struct hotplug_slot_ops ibmphp_hotplug_slot_ops; + +static inline void long_delay (int delay) +{ + set_current_state (TASK_INTERRUPTIBLE); + schedule_timeout (delay); +} + +#endif //__IBMPHP_H + diff -Nru a/drivers/hotplug/ibmphp_core.c b/drivers/hotplug/ibmphp_core.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/ibmphp_core.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,1480 @@ +/* + * IBM Hot Plug Controller Driver + * + * Written By: Chuck Cole, Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation + * + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2002 IBM Corp. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../arch/i386/kernel/pci-i386.h" /* for struct irq_routing_table */ +#include "ibmphp.h" + +#define attn_on(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNON) +#define attn_off(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNOFF) +#define attn_LED_blink(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_BLINKLED) +#define get_ctrl_revision(sl, rev) ibmphp_hpc_readslot (sl, READ_REVLEVEL, rev) +#define get_hpc_options(sl, opt) ibmphp_hpc_readslot (sl, READ_HPCOPTIONS, opt) + +#define DRIVER_VERSION "0.1" +#define DRIVER_DESC "IBM Hot Plug PCI Controller Driver" + +int ibmphp_debug; + +static int debug; +MODULE_PARM (debug, "i"); +MODULE_PARM_DESC (debug, "Debugging mode enabled or not"); +MODULE_LICENSE ("GPL"); +MODULE_DESCRIPTION (DRIVER_DESC); + +static int *ops[MAX_OPS + 1]; +static struct pci_ops *ibmphp_pci_root_ops; +static int max_slots; + +static int irqs[16]; /* PIC mode IRQ's we're using so far (in case MPS tables don't provide default info for empty slots */ + +static int init_flag; + +/* +static int get_max_adapter_speed_1 (struct hotplug_slot *, u8 *, u8); + +static inline int get_max_adapter_speed (struct hotplug_slot *hs, u8 *value) +{ + return get_max_adapter_speed_1 (hs, value, 1); +} +*/ +static inline int get_cur_bus_info (struct slot **sl) +{ + int rc = 1; + struct slot * slot_cur = *sl; + + debug ("options = %x\n", slot_cur->ctrl->options); + debug ("revision = %x\n", slot_cur->ctrl->revision); + + if (READ_BUS_STATUS (slot_cur->ctrl)) + rc = ibmphp_hpc_readslot (slot_cur, READ_BUSSTATUS, NULL); + + if (rc) + return rc; + + slot_cur->bus_on->current_speed = CURRENT_BUS_SPEED (slot_cur->busstatus); + if (READ_BUS_MODE (slot_cur->ctrl)) + slot_cur->bus_on->current_bus_mode = CURRENT_BUS_MODE (slot_cur->busstatus); + + debug ("busstatus = %x, bus_speed = %x, bus_mode = %x\n", slot_cur->busstatus, slot_cur->bus_on->current_speed, slot_cur->bus_on->current_bus_mode); + + *sl = slot_cur; + return 0; +} + +static inline int slot_update (struct slot **sl) +{ + int rc; + rc = ibmphp_hpc_readslot (*sl, READ_ALLSTAT, NULL); + if (rc) + return rc; + if (!init_flag) + return get_cur_bus_info (sl); + return rc; +} + +static int get_max_slots (void) +{ + struct list_head * tmp; + int slot_count = 0; + + list_for_each (tmp, &ibmphp_slot_head) + ++slot_count; + return slot_count; +} + +/* This routine will put the correct slot->device information per slot. It's + * called from initialization of the slot structures. It will also assign + * interrupt numbers per each slot. + * Parameters: struct slot + * Returns 0 or errors + */ +int ibmphp_init_devno (struct slot **cur_slot) +{ + struct irq_routing_table *rtable; + int len; + int loop; + int i; + + rtable = pcibios_get_irq_routing_table (); + if (!rtable) { + err ("no BIOS routing table...\n"); + return -ENOMEM; + } + + len = (rtable->size - sizeof (struct irq_routing_table)) / sizeof (struct irq_info); + + if (!len) + return -1; + for (loop = 0; loop < len; loop++) { + if ((*cur_slot)->number == rtable->slots[loop].slot) { + if ((*cur_slot)->bus == rtable->slots[loop].bus) { + (*cur_slot)->device = PCI_SLOT (rtable->slots[loop].devfn); + for (i = 0; i < 4; i++) + (*cur_slot)->irq[i] = IO_APIC_get_PCI_irq_vector ((int) (*cur_slot)->bus, (int) (*cur_slot)->device, i); + + debug ("(*cur_slot)->irq[0] = %x\n", (*cur_slot)->irq[0]); + debug ("(*cur_slot)->irq[1] = %x\n", (*cur_slot)->irq[1]); + debug ("(*cur_slot)->irq[2] = %x\n", (*cur_slot)->irq[2]); + debug ("(*cur_slot)->irq[3] = %x\n", (*cur_slot)->irq[3]); + + debug ("rtable->exlusive_irqs = %x\n", rtable->exclusive_irqs); + debug ("rtable->slots[loop].irq[0].bitmap = %x\n", rtable->slots[loop].irq[0].bitmap); + debug ("rtable->slots[loop].irq[1].bitmap = %x\n", rtable->slots[loop].irq[1].bitmap); + debug ("rtable->slots[loop].irq[2].bitmap = %x\n", rtable->slots[loop].irq[2].bitmap); + debug ("rtable->slots[loop].irq[3].bitmap = %x\n", rtable->slots[loop].irq[3].bitmap); + + debug ("rtable->slots[loop].irq[0].link= %x\n", rtable->slots[loop].irq[0].link); + debug ("rtable->slots[loop].irq[1].link = %x\n", rtable->slots[loop].irq[1].link); + debug ("rtable->slots[loop].irq[2].link = %x\n", rtable->slots[loop].irq[2].link); + debug ("rtable->slots[loop].irq[3].link = %x\n", rtable->slots[loop].irq[3].link); + debug ("end of init_devno\n"); + return 0; + } + } + } + + return -1; +} + +static inline int power_on (struct slot *slot_cur) +{ + u8 cmd = HPC_SLOT_ON; + int retval; + + retval = ibmphp_hpc_writeslot (slot_cur, cmd); + if (retval) { + err ("power on failed\n"); + return retval; + } + if (CTLR_RESULT (slot_cur->ctrl->status)) { + err ("command not completed successfully in power_on \n"); + return -EIO; + } + long_delay (3 * HZ); /* For ServeRAID cards, and some 66 PCI */ + return 0; +} + +static inline int power_off (struct slot *slot_cur) +{ + u8 cmd = HPC_SLOT_OFF; + int retval; + + retval = ibmphp_hpc_writeslot (slot_cur, cmd); + if (retval) { + err ("power off failed \n"); + return retval; + } + if (CTLR_RESULT (slot_cur->ctrl->status)) { + err ("command not completed successfully in power_off \n"); + return -EIO; + } + return 0; +} + +static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 value) +{ + int rc = 0; + struct slot *pslot; + u8 cmd; + int hpcrc = 0; + + debug ("set_attention_status - Entry hotplug_slot[%lx] value[%x]\n", (ulong) hotplug_slot, value); + ibmphp_lock_operations (); + cmd = 0x00; // avoid compiler warning + + if (hotplug_slot) { + switch (value) { + case HPC_SLOT_ATTN_OFF: + cmd = HPC_SLOT_ATTNOFF; + break; + case HPC_SLOT_ATTN_ON: + cmd = HPC_SLOT_ATTNON; + break; + case HPC_SLOT_ATTN_BLINK: + cmd = HPC_SLOT_BLINKLED; + break; + default: + rc = -ENODEV; + err ("set_attention_status - Error : invalid input [%x]\n", value); + break; + } + if (rc == 0) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) + hpcrc = ibmphp_hpc_writeslot (pslot, cmd); + else + rc = -ENODEV; + } + } else + rc = -ENODEV; + + if (hpcrc) + rc = hpcrc; + + ibmphp_unlock_operations (); + + debug ("set_attention_status - Exit rc[%d]\n", rc); + return rc; +} + +static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 * value) +{ + int rc = -ENODEV; + struct slot *pslot; + int hpcrc = 0; + struct slot myslot; + + debug ("get_attention_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); + + ibmphp_lock_operations (); + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + if (!hpcrc) + hpcrc = ibmphp_hpc_readslot (pslot, READ_EXTSLOTSTATUS, &(myslot.ext_status)); + if (!hpcrc) { + *value = SLOT_ATTN (myslot.status, myslot.ext_status); + rc = 0; + } + } + } else + rc = -ENODEV; + + if (hpcrc) + rc = hpcrc; + + ibmphp_unlock_operations (); + debug ("get_attention_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); + return rc; +} + +static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 * value) +{ + int rc = -ENODEV; + struct slot *pslot; + int hpcrc = 0; + struct slot myslot; + + debug ("get_latch_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); + ibmphp_lock_operations (); + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + if (!hpcrc) { + *value = SLOT_LATCH (myslot.status); + rc = 0; + } + } + } else + rc = -ENODEV; + + if (hpcrc) + rc = hpcrc; + + ibmphp_unlock_operations (); + debug ("get_latch_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); + return rc; +} + + +static int get_power_status (struct hotplug_slot *hotplug_slot, u8 * value) +{ + int rc = -ENODEV; + struct slot *pslot; + int hpcrc = 0; + struct slot myslot; + + debug ("get_power_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); + ibmphp_lock_operations (); + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + if (!hpcrc) { + *value = SLOT_POWER (myslot.status); + rc = 0; + } + } + } else + rc = -ENODEV; + + if (hpcrc) + rc = hpcrc; + + ibmphp_unlock_operations (); + debug ("get_power_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); + return rc; +} + +static int get_adapter_present (struct hotplug_slot *hotplug_slot, u8 * value) +{ + int rc = -ENODEV; + struct slot *pslot; + u8 present; + int hpcrc = 0; + struct slot myslot; + + debug ("get_adapter_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); + ibmphp_lock_operations (); + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + if (!hpcrc) { + present = SLOT_PRESENT (myslot.status); + if (present == HPC_SLOT_EMPTY) + *value = 0; + else + *value = 1; + rc = 0; + } + } + } else + rc = -ENODEV; + if (hpcrc) + rc = hpcrc; + + ibmphp_unlock_operations (); + debug ("get_adapter_present - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); + return rc; +} +/* +static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, u8 * value) +{ + int rc = -ENODEV; + struct slot *pslot; + u8 mode = 0; + + debug ("get_max_bus_speed - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong)hotplug_slot, (ulong) value); + + ibmphp_lock_operations (); + + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + rc = 0; + mode = pslot->bus_on->supported_bus_mode; + *value = pslot->bus_on->supported_speed; + *value &= 0x0f; + + if (mode == BUS_MODE_PCIX) + *value |= 0x80; + else if (mode == BUS_MODE_PCI) + *value |= 0x40; + else + *value |= 0x20; + } + } else + rc = -ENODEV; + + ibmphp_unlock_operations (); + debug ("get_max_bus_speed - Exit rc[%d] value[%x]\n", rc, *value); + return rc; +} + +static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, u8 * value) +{ + int rc = -ENODEV; + struct slot *pslot; + u8 mode = 0; + + debug ("get_cur_bus_speed - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong)hotplug_slot, (ulong) value); + + ibmphp_lock_operations (); + + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + rc = get_cur_bus_info (&pslot); + if (!rc) { + mode = pslot->bus_on->current_bus_mode; + *value = pslot->bus_on->current_speed; + *value &= 0x0f; + + if (mode == BUS_MODE_PCIX) + *value |= 0x80; + else if (mode == BUS_MODE_PCI) + *value |= 0x40; + else + *value |= 0x20; + } + } + } else + rc = -ENODEV; + + ibmphp_unlock_operations (); + debug ("get_cur_bus_speed - Exit rc[%d] value[%x]\n", rc, *value); + return rc; +} + +static int get_max_adapter_speed_1 (struct hotplug_slot *hotplug_slot, u8 * value, u8 flag) +{ + int rc = -ENODEV; + struct slot *pslot; + int hpcrc = 0; + struct slot myslot; + + debug ("get_max_adapter_speed - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong)hotplug_slot, (ulong) value); + + if (flag) + ibmphp_lock_operations (); + + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + + if (!(SLOT_LATCH (myslot.status)) && (SLOT_PRESENT (myslot.status))) { + hpcrc = ibmphp_hpc_readslot (pslot, READ_EXTSLOTSTATUS, &(myslot.ext_status)); + if (!hpcrc) { + *value = SLOT_SPEED (myslot.ext_status); + rc = 0; + } + } else { + *value = MAX_ADAPTER_NONE; + rc = 0; + } + } + } else + rc = -ENODEV; + + if (hpcrc) + rc = hpcrc; + + if (flag) + ibmphp_unlock_operations (); + + debug ("get_adapter_present - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); + return rc; +} + +static int get_card_bus_names (struct hotplug_slot *hotplug_slot, char * value) +{ + int rc = -ENODEV; + struct slot *pslot = NULL; + struct pci_dev * dev = NULL; + + debug ("get_card_bus_names - Entry hotplug_slot[%lx] \n", (ulong)hotplug_slot); + + ibmphp_lock_operations (); + + if (hotplug_slot) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + rc = 0; + if (pslot->func) + dev = pslot->func->dev; + else + dev = pci_find_slot (pslot->bus, (pslot->device << 3) | (0x00 & 0x7)); + if (dev) + snprintf (value, 100, "Bus %d : %s", pslot->bus,dev->name); + else + snprintf (value, 100, "Bus %d", pslot->bus); + + + } + } else + rc = -ENODEV; + + ibmphp_unlock_operations (); + debug ("get_card_bus_names - Exit rc[%d] value[%x]\n", rc, *value); + return rc; +} + +*/ +/******************************************************************************* + * This routine will initialize the ops data structure used in the validate + * function. It will also power off empty slots that are powered on since BIOS + * leaves those on, albeit disconnected + ******************************************************************************/ +static int init_ops (void) +{ + struct slot *slot_cur; + int retval; + int j; + int rc; + + for (j = 0; j < MAX_OPS; j++) { + ops[j] = (int *) kmalloc ((max_slots + 1) * sizeof (int), GFP_KERNEL); + if (!ops[j]) { + err ("out of system memory \n"); + return -ENOMEM; + } + } + + ops[ADD][0] = 0; + ops[REMOVE][0] = 0; + ops[DETAIL][0] = 0; + + for (j = 1; j <= max_slots; j++) { + + slot_cur = ibmphp_get_slot_from_physical_num (j); + + debug ("BEFORE GETTING SLOT STATUS, slot # %x\n", slot_cur->number); + + if (slot_cur->ctrl->revision == 0xFF) + if (get_ctrl_revision (slot_cur, &slot_cur->ctrl->revision)) + return -1; + + if (slot_cur->bus_on->current_speed == 0xFF) + if (get_cur_bus_info (&slot_cur)) + return -1; + + if (slot_cur->ctrl->options == 0xFF) + if (get_hpc_options (slot_cur, &slot_cur->ctrl->options)) + return -1; + + retval = slot_update (&slot_cur); + if (retval) + return retval; + + debug ("status = %x, ext_status = %x\n", slot_cur->status, slot_cur->ext_status); + debug ("SLOT_POWER = %x, SLOT_PRESENT = %x, SLOT_LATCH = %x\n", SLOT_POWER (slot_cur->status), SLOT_PRESENT (slot_cur->status), SLOT_LATCH (slot_cur->status)); + + if (!(SLOT_POWER (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status)) && !(SLOT_LATCH (slot_cur->status))) + /* No power, adapter, and latch closed */ + ops[ADD][j] = 1; + else + ops[ADD][j] = 0; + + ops[DETAIL][j] = 1; + + if ((SLOT_POWER (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status)) && !(SLOT_LATCH (slot_cur->status))) + /*Power,adapter,latch closed */ + ops[REMOVE][j] = 1; + else + ops[REMOVE][j] = 0; + + if ((SLOT_POWER (slot_cur->status)) && !(SLOT_PRESENT (slot_cur->status)) && !(SLOT_LATCH (slot_cur->status))) { + debug ("BEFORE POWER OFF COMMAND\n"); + rc = power_off (slot_cur); + if (rc) + return rc; + + /* retval = slot_update (&slot_cur); + * if (retval) + * return retval; + * ibmphp_update_slot_info (slot_cur); + */ + } + } + init_flag = 0; + return 0; +} + +/* This operation will check whether the slot is within the bounds and + * the operation is valid to perform on that slot + * Parameters: slot, operation + * Returns: 0 or error codes + */ +static int validate (struct slot *slot_cur, int opn) +{ + int number; + int retval; + + if (!slot_cur) + return -ENODEV; + number = slot_cur->number; + if ((number > max_slots) || (number < 0)) + return -EBADSLT; + debug ("slot_number in validate is %d\n", slot_cur->number); + + retval = slot_update (&slot_cur); + if (retval) + return retval; + + if (!(SLOT_POWER (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status)) + && !(SLOT_LATCH (slot_cur->status))) + ops[ADD][number] = 1; + else + ops[ADD][number] = 0; + + ops[DETAIL][number] = 1; + + if ((SLOT_POWER (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status)) + && !(SLOT_LATCH (slot_cur->status))) + ops[REMOVE][number] = 1; + else + ops[REMOVE][number] = 0; + + switch (opn) { + case ENABLE: + if (ops[ADD][number]) + return 0; + break; + case DISABLE: + if (ops[REMOVE][number]) + return 0; + break; + case DETAIL: + if (ops[DETAIL][number]) + return 0; + break; + default: + return -EINVAL; + break; + } + err ("validate failed....\n"); + return -EINVAL; +} + +/******************************************************************************** + * This routine is for updating the data structures in the hotplug core + * Parameters: struct slot + * Returns: 0 or error + *******************************************************************************/ +int ibmphp_update_slot_info (struct slot *slot_cur) +{ + struct hotplug_slot_info *info; + char buffer[10]; + int rc; +// u8 bus_speed; + + info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); + if (!info) { + err ("out of system memory \n"); + return -ENOMEM; + } + + snprintf (buffer, 10, "%d", slot_cur->number); + info->power_status = SLOT_POWER (slot_cur->status); + info->attention_status = SLOT_ATTN (slot_cur->status, slot_cur->ext_status); + info->latch_status = SLOT_LATCH (slot_cur->status); + if (!SLOT_PRESENT (slot_cur->status)) { + info->adapter_status = 0; +// info->max_adapter_speed_status = MAX_ADAPTER_NONE; + } else { + info->adapter_status = 1; +// get_max_adapter_speed_1 (slot_cur->hotplug_slot, &info->max_adapter_speed_status, 0); + } +/* + bus_speed = slot_cur->bus_on->current_speed; + bus_speed &= 0x0f; + + if (slot_cur->bus_on->current_bus_mode == BUS_MODE_PCIX) + bus_speed |= 0x80; + else if (slot_cur->bus_on->current_bus_mode == BUS_MODE_PCI) + bus_speed |= 0x40; + else + bus_speed |= 0x20; + + info->cur_bus_speed_status = bus_speed; + info->max_bus_speed_status = slot_cur->hotplug_slot->info->max_bus_speed_status; + // To do: card_bus_names +*/ + rc = pci_hp_change_slot_info (buffer, info); + kfree (info); + return rc; +} + + +/****************************************************************************** + * This function will return the pci_func, given bus and devfunc, or NULL. It + * is called from visit routines + ******************************************************************************/ + +static struct pci_func *ibm_slot_find (u8 busno, u8 device, u8 function) +{ + struct pci_func *func_cur; + struct slot *slot_cur; + struct list_head * tmp; + list_for_each (tmp, &ibmphp_slot_head) { + slot_cur = list_entry (tmp, struct slot, ibm_slot_list); + if (slot_cur->func) { + func_cur = slot_cur->func; + while (func_cur) { + if ((func_cur->busno == busno) && (func_cur->device == device) && (func_cur->function == function)) + return func_cur; + func_cur = func_cur->next; + } + } + } + return NULL; +} + +/* This routine is to find the pci_bus from kernel structures. + * Parameters: bus number + * Returns : pci_bus * or NULL if not found + */ +static struct pci_bus *find_bus (u8 busno) +{ + const struct list_head *tmp; + struct pci_bus *bus; + debug ("inside find_bus, busno = %x \n", busno); + + list_for_each (tmp, &pci_root_buses) { + bus = (struct pci_bus *) pci_bus_b (tmp); + if (bus) + if (bus->number == busno) + return bus; + } + return NULL; +} + +/****************************************************************** + * This function is here because we can no longer use pci_root_ops + ******************************************************************/ +static struct pci_ops *get_root_pci_ops (void) +{ + struct pci_bus * bus; + + if ((bus = find_bus (0))) + return bus->ops; + return NULL; +} + +/************************************************************* + * This routine frees up memory used by struct slot, including + * the pointers to pci_func, bus, hotplug_slot, controller, + * and deregistering from the hotplug core + *************************************************************/ +static void free_slots (void) +{ + struct slot *slot_cur; + struct list_head * tmp; + struct list_head * next; + + list_for_each_safe (tmp, next, &ibmphp_slot_head) { + + slot_cur = list_entry (tmp, struct slot, ibm_slot_list); + + pci_hp_deregister (slot_cur->hotplug_slot); + + if (slot_cur->hotplug_slot) { + kfree (slot_cur->hotplug_slot); + slot_cur->hotplug_slot = NULL; + } + + if (slot_cur->ctrl) + slot_cur->ctrl = NULL; + + if (slot_cur->bus_on) + slot_cur->bus_on = NULL; + + ibmphp_unconfigure_card (&slot_cur, -1); /* we don't want to actually remove the resources, since free_resources will do just that */ + + kfree (slot_cur); + } +} + +static int ibm_is_pci_dev_in_use (struct pci_dev *dev) +{ + int i = 0; + int inuse = 0; + + if (dev->driver) + return 1; + + for (i = 0; !dev->driver && !inuse && (i < 6); i++) { + + if (!pci_resource_start (dev, i)) + continue; + + if (pci_resource_flags (dev, i) & IORESOURCE_IO) + inuse = check_region (pci_resource_start (dev, i), pci_resource_len (dev, i)); + + else if (pci_resource_flags (dev, i) & IORESOURCE_MEM) + inuse = check_mem_region (pci_resource_start (dev, i), pci_resource_len (dev, i)); + } + + return inuse; +} + +static int ibm_pci_hp_remove_device (struct pci_dev *dev) +{ + if (ibm_is_pci_dev_in_use (dev)) { + err ("***Cannot safely power down device -- it appears to be in use***\n"); + return -EBUSY; + } + pci_remove_device (dev); + return 0; +} + +static int ibm_unconfigure_visit_pci_dev_phase2 (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) +{ + struct pci_dev *dev = wrapped_dev->dev; + struct pci_func *temp_func; + int i = 0; + + do { + temp_func = ibm_slot_find (dev->bus->number, dev->devfn >> 3, i++); + } while (temp_func && (temp_func->function != (dev->devfn & 0x07))); + + if (dev) { + if (ibm_pci_hp_remove_device (dev) == 0) + kfree (dev); /* Now, remove */ + else + return -1; + } + + if (temp_func) + temp_func->dev = NULL; + else + err ("No pci_func representation for bus, devfn = %d, %x\n", dev->bus->number, dev->devfn); + + return 0; +} + +static int ibm_unconfigure_visit_pci_bus_phase2 (struct pci_bus_wrapped *wrapped_bus, struct pci_dev_wrapped *wrapped_dev) +{ + struct pci_bus *bus = wrapped_bus->bus; + + pci_proc_detach_bus (bus); + /* The cleanup code should live in the kernel... */ + bus->self->subordinate = NULL; + /* unlink from parent bus */ + list_del (&bus->node); + + /* Now, remove */ + if (bus) + kfree (bus); + + return 0; +} + +static int ibm_unconfigure_visit_pci_dev_phase1 (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) +{ + struct pci_dev *dev = wrapped_dev->dev; + + debug ("attempting removal of driver for device (%x, %x, %x)\n", dev->bus->number, PCI_SLOT (dev->devfn), PCI_FUNC (dev->devfn)); + + /* Now, remove the Linux Driver Representation */ + if (dev->driver) { + debug ("is there a driver?\n"); + if (dev->driver->remove) { + dev->driver->remove (dev); + debug ("driver was properly removed\n"); + } + dev->driver = NULL; + } + + return ibm_is_pci_dev_in_use (dev); +} + +static struct pci_visit ibm_unconfigure_functions_phase1 = { + post_visit_pci_dev: ibm_unconfigure_visit_pci_dev_phase1, +}; + +static struct pci_visit ibm_unconfigure_functions_phase2 = { + post_visit_pci_bus: ibm_unconfigure_visit_pci_bus_phase2, + post_visit_pci_dev: ibm_unconfigure_visit_pci_dev_phase2, +}; + +static int ibm_unconfigure_device (struct pci_func *func) +{ + int rc = 0; + struct pci_dev_wrapped wrapped_dev; + struct pci_bus_wrapped wrapped_bus; + struct pci_dev *temp; + u8 j; + + memset (&wrapped_dev, 0, sizeof (struct pci_dev_wrapped)); + memset (&wrapped_bus, 0, sizeof (struct pci_bus_wrapped)); + + debug ("inside ibm_unconfigure_device\n"); + debug ("func->device = %x, func->function = %x\n", func->device, func->function); + debug ("func->device << 3 | 0x0 = %x\n", func->device << 3 | 0x0); + + for (j = 0; j < 0x08; j++) { + temp = pci_find_slot (func->busno, (func->device << 3) | j); + if (temp) { + wrapped_dev.dev = temp; + wrapped_bus.bus = temp->bus; + rc = pci_visit_dev (&ibm_unconfigure_functions_phase1, &wrapped_dev, &wrapped_bus); + if (rc) + break; + + rc = pci_visit_dev (&ibm_unconfigure_functions_phase2, &wrapped_dev, &wrapped_bus); + if (rc) + break; + } + } + debug ("rc in ibm_unconfigure_device b4 returning is %d \n", rc); + return rc; +} + +static int configure_visit_pci_dev (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) +{ + // struct pci_bus *bus = wrapped_bus->bus; /* We don't need this, since we don't create in the else statement */ + struct pci_dev *dev = wrapped_dev->dev; + struct pci_func *temp_func; + int i = 0; + + do { + temp_func = ibm_slot_find (dev->bus->number, dev->devfn >> 3, i++); + } while (temp_func && (temp_func->function != (dev->devfn & 0x07))); + + if (temp_func) + temp_func->dev = dev; + else { + /* This should not really happen, since we create functions + first and then call to configure */ + debug (" We shouldn't come here \n"); + } + + if (temp_func->dev) { + pci_proc_attach_device (temp_func->dev); + pci_announce_device_to_drivers (temp_func->dev); + } + + return 0; +} + +static struct pci_visit configure_functions = { + visit_pci_dev: configure_visit_pci_dev, +}; + +static int ibm_configure_device (struct pci_func *func) +{ + unsigned char bus; + struct pci_dev dev0; + struct pci_bus *child; + struct pci_dev *temp; + int rc = 0; + + struct pci_dev_wrapped wrapped_dev; + struct pci_bus_wrapped wrapped_bus; + + memset (&wrapped_dev, 0, sizeof (struct pci_dev_wrapped)); + memset (&wrapped_bus, 0, sizeof (struct pci_bus_wrapped)); + memset (&dev0, 0, sizeof (struct pci_dev)); + + if (func->dev == NULL) + func->dev = pci_find_slot (func->busno, (func->device << 3) | (func->function & 0x7)); + + if (func->dev == NULL) { + dev0.bus = find_bus (func->busno); + dev0.devfn = ((func->device << 3) + (func->function & 0x7)); + dev0.sysdata = dev0.bus->sysdata; + + func->dev = pci_scan_slot (&dev0); + + if (func->dev == NULL) { + err ("ERROR... : pci_dev still NULL \n"); + return 0; + } + } + if (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { + pci_read_config_byte (func->dev, PCI_SECONDARY_BUS, &bus); + child = (struct pci_bus *) pci_add_new_bus (func->dev->bus, (func->dev), bus); + pci_do_scan_bus (child); + } + + temp = func->dev; + if (temp) { + wrapped_dev.dev = temp; + wrapped_bus.bus = temp->bus; + rc = pci_visit_dev (&configure_functions, &wrapped_dev, &wrapped_bus); + } + return rc; +} +/******************************************************* + * Returns whether the bus is empty or not + *******************************************************/ +static int is_bus_empty (struct slot * slot_cur) +{ + int rc; + struct slot * tmp_slot; + u8 i = slot_cur->bus_on->slot_min; + + while (i <= slot_cur->bus_on->slot_max) { + if (i == slot_cur->number) { + i++; + continue; + } + tmp_slot = ibmphp_get_slot_from_physical_num (i); + rc = slot_update (&tmp_slot); + if (rc) + return 0; + if (SLOT_PRESENT (tmp_slot->status) && SLOT_POWER (tmp_slot->status)) + return 0; + i++; + } + return 1; +} + +/*********************************************************** + * If the HPC permits and the bus currently empty, tries to set the + * bus speed and mode at the maximum card capability + ***********************************************************/ +static int set_bus (struct slot * slot_cur) +{ + int rc; + u8 speed; + u8 cmd = 0x0; + + debug ("%s - entry slot # %d \n", __FUNCTION__, slot_cur->number); + if (SET_BUS_STATUS (slot_cur->ctrl) && is_bus_empty (slot_cur)) { + rc = slot_update (&slot_cur); + if (rc) + return rc; + speed = SLOT_SPEED (slot_cur->ext_status); + debug ("ext_status = %x, speed = %x\n", slot_cur->ext_status, speed); + switch (speed) { + case HPC_SLOT_SPEED_33: + cmd = HPC_BUS_33CONVMODE; + break; + case HPC_SLOT_SPEED_66: + if (SLOT_PCIX (slot_cur->ext_status)) + cmd = HPC_BUS_66PCIXMODE; + else + cmd = HPC_BUS_66CONVMODE; + break; + case HPC_SLOT_SPEED_133: + if (slot_cur->bus_on->slot_count > 1) + cmd = HPC_BUS_100PCIXMODE; + else + cmd = HPC_BUS_133PCIXMODE; + break; + default: + err ("wrong slot speed \n"); + return -ENODEV; + } + debug ("setting bus speed for slot %d, cmd %x\n", slot_cur->number, cmd); + rc = ibmphp_hpc_writeslot (slot_cur, cmd); + if (rc) + return rc; + } + debug ("%s -Exit \n", __FUNCTION__); + return 0; +} + +static inline void print_card_capability (struct slot *slot_cur) +{ + info ("capability of the card is "); + if ((slot_cur->ext_status & CARD_INFO) == PCIX133) + info (" 133 MHz PCI-X \n"); + else if ((slot_cur->ext_status & CARD_INFO) == PCIX66) + info (" 66 MHz PCI-X \n"); + else if ((slot_cur->ext_status & CARD_INFO) == PCI66) + info (" 66 MHz PCI \n"); + else + info (" 33 MHz PCI \n"); + +} + +/* This routine will power on the slot, configure the device(s) and find the + * drivers for them. + * Parameters: hotplug_slot + * Returns: 0 or failure codes + */ +static int enable_slot (struct hotplug_slot *hs) +{ + int rc, i, rcpr; + struct slot *slot_cur; + u8 function; + u8 faulted = 0; + struct pci_func *tmp_func; + + ibmphp_lock_operations (); + + debug ("ENABLING SLOT........ \n"); + slot_cur = (struct slot *) hs->private; + + if ((rc = validate (slot_cur, ENABLE))) { + err ("validate function failed \n"); + attn_off (slot_cur); /* need to turn off if was blinking b4 */ + attn_on (slot_cur); + rc = slot_update (&slot_cur); + if (rc) { + ibmphp_unlock_operations(); + return rc; + } + ibmphp_update_slot_info (slot_cur); + ibmphp_unlock_operations (); + return rc; + } + + attn_LED_blink (slot_cur); + + rc = set_bus (slot_cur); + if (rc) { + err ("was not able to set the bus \n"); + attn_off (slot_cur); + attn_on (slot_cur); + ibmphp_unlock_operations (); + return -ENODEV; + } + + rc = power_on (slot_cur); + + if (rc) { + err ("something wrong when powering up... please see below for details\n"); + /* need to turn off before on, otherwise, blinking overwrites */ + attn_off(slot_cur); + attn_on (slot_cur); + if (slot_update (&slot_cur)) { + attn_off (slot_cur); + attn_on (slot_cur); + ibmphp_unlock_operations (); + return -ENODEV; + } + /* Check to see the error of why it failed */ + if (!(SLOT_PWRGD (slot_cur->status))) + err ("power fault occured trying to power up \n"); + else if (SLOT_BUS_SPEED (slot_cur->status)) { + err ("bus speed mismatch occured. please check current bus speed and card capability \n"); + print_card_capability (slot_cur); + } else if (SLOT_BUS_MODE (slot_cur->ext_status)) + err ("bus mode mismatch occured. please check current bus mode and card capability \n"); + + ibmphp_update_slot_info (slot_cur); + ibmphp_unlock_operations (); + return rc; + } + debug ("after power_on\n"); + + rc = slot_update (&slot_cur); + if (rc) { + attn_off (slot_cur); + attn_on (slot_cur); + rcpr = power_off (slot_cur); + if (rcpr) { + ibmphp_unlock_operations (); + return rcpr; + } + ibmphp_unlock_operations (); + return rc; + } + + if (SLOT_POWER (slot_cur->status) && !(SLOT_PWRGD (slot_cur->status))) { + faulted = 1; + err ("power fault occured trying to power up...\n"); + } else if (SLOT_POWER (slot_cur->status) && (SLOT_BUS_SPEED (slot_cur->status))) { + faulted = 1; + err ("bus speed mismatch occured. please check current bus speed and card capability \n"); + print_card_capability (slot_cur); + } + /* Don't think this case will happen after above checks... but just in case, for paranoia sake */ + else if (!(SLOT_POWER (slot_cur->status))) { + err ("power on failed... \n"); + faulted = 1; + } + if (faulted) { + attn_off (slot_cur); /* need to turn off b4 on */ + attn_on (slot_cur); + rcpr = power_off (slot_cur); + if (rcpr) { + ibmphp_unlock_operations (); + return rcpr; + } + + if (slot_update (&slot_cur)) { + ibmphp_unlock_operations (); + return -ENODEV; + } + ibmphp_update_slot_info (slot_cur); + ibmphp_unlock_operations (); + return -EINVAL; + } + + slot_cur->func = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); + if (!slot_cur->func) { /* We cannot do update_slot_info here, since no memory for kmalloc n.e.ways, and update_slot_info allocates some */ + err ("out of system memory \n"); + attn_off (slot_cur); + attn_on (slot_cur); + rcpr = power_off (slot_cur); + if (rcpr) { + ibmphp_unlock_operations (); + return rcpr; + } + ibmphp_unlock_operations (); + return -ENOMEM; + } + memset (slot_cur->func, 0, sizeof (struct pci_func)); + slot_cur->func->busno = slot_cur->bus; + slot_cur->func->device = slot_cur->device; + for (i = 0; i < 4; i++) + slot_cur->func->irq[i] = slot_cur->irq[i]; + + debug ("b4 configure_card, slot_cur->bus = %x, slot_cur->device = %x\n", slot_cur->bus, slot_cur->device); + + if (ibmphp_configure_card (slot_cur->func, slot_cur->number)) { + err ("configure_card was unsuccessful... \n"); + ibmphp_unconfigure_card (&slot_cur, 1); /* true because don't need to actually deallocate resources, just remove references */ + debug ("after unconfigure_card\n"); + slot_cur->func = NULL; + attn_off (slot_cur); /* need to turn off in case was blinking */ + attn_on (slot_cur); + rcpr = power_off (slot_cur); + if (rcpr) { + ibmphp_unlock_operations (); + return rcpr; + } + if (slot_update (&slot_cur)) { + ibmphp_unlock_operations(); + return -ENODEV; + } + ibmphp_update_slot_info (slot_cur); + ibmphp_unlock_operations (); + return -ENOMEM; + } + function = 0x00; + do { + tmp_func = ibm_slot_find (slot_cur->bus, slot_cur->func->device, function++); + if (tmp_func && !(tmp_func->dev)) + ibm_configure_device (tmp_func); + } while (tmp_func); + + attn_off (slot_cur); + if (slot_update (&slot_cur)) { + ibmphp_unlock_operations (); + return -EFAULT; + } + ibmphp_print_test (); + rc = ibmphp_update_slot_info (slot_cur); + ibmphp_unlock_operations(); + return rc; +} + +/************************************************************** +* HOT REMOVING ADAPTER CARD * +* INPUT: POINTER TO THE HOTPLUG SLOT STRUCTURE * +* OUTPUT: SUCCESS 0 ; FAILURE: UNCONFIGURE , VALIDATE * + DISABLE POWER , * +**************************************************************/ +int ibmphp_disable_slot (struct hotplug_slot *hotplug_slot) +{ + int rc; + struct slot *slot_cur = (struct slot *) hotplug_slot->private; + u8 flag = slot_cur->flag; + + slot_cur->flag = TRUE; + debug ("DISABLING SLOT... \n"); + + ibmphp_lock_operations (); + if (slot_cur == NULL) { + ibmphp_unlock_operations (); + return -ENODEV; + } + if (slot_cur->ctrl == NULL) { + ibmphp_unlock_operations (); + return -ENODEV; + } + if (flag == TRUE) { + rc = validate (slot_cur, DISABLE); /* checking if powered off already & valid slot # */ + if (rc) { + /* Need to turn off if was blinking b4 */ + attn_off (slot_cur); + attn_on (slot_cur); + if (slot_update (&slot_cur)) { + ibmphp_unlock_operations (); + return -EFAULT; + } + + ibmphp_update_slot_info (slot_cur); + ibmphp_unlock_operations (); + return rc; + } + } + attn_LED_blink (slot_cur); + + if (slot_cur->func == NULL) { + /* We need this for fncs's that were there on bootup */ + slot_cur->func = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); + if (!slot_cur->func) { + err ("out of system memory \n"); + attn_off (slot_cur); + attn_on (slot_cur); + ibmphp_unlock_operations (); + return -ENOMEM; + } + memset (slot_cur->func, 0, sizeof (struct pci_func)); + slot_cur->func->busno = slot_cur->bus; + slot_cur->func->device = slot_cur->device; + } + + if ((rc = ibm_unconfigure_device (slot_cur->func))) { + err ("removing from kernel failed... \n"); + err ("Please check to see if it was statically linked or is in use otherwise. (perhaps the driver is not 'hot-removable')\n"); + attn_off (slot_cur); + attn_on (slot_cur); + ibmphp_unlock_operations (); + return rc; + } + + rc = ibmphp_unconfigure_card (&slot_cur, 0); + slot_cur->func = NULL; + debug ("in disable_slot. after unconfigure_card \n"); + if (rc) { + err ("could not unconfigure card.\n"); + attn_off (slot_cur); /* need to turn off if was blinking b4 */ + attn_on (slot_cur); + + if (slot_update (&slot_cur)) { + ibmphp_unlock_operations (); + return -EFAULT; + } + + if (flag) + ibmphp_update_slot_info (slot_cur); + + ibmphp_unlock_operations (); + return -EFAULT; + } + + rc = ibmphp_hpc_writeslot (hotplug_slot->private, HPC_SLOT_OFF); + if (rc) { + attn_off (slot_cur); + attn_on (slot_cur); + if (slot_update (&slot_cur)) { + ibmphp_unlock_operations (); + return -EFAULT; + } + + if (flag) + ibmphp_update_slot_info (slot_cur); + + ibmphp_unlock_operations (); + return rc; + } + + attn_off (slot_cur); + if (slot_update (&slot_cur)) { + ibmphp_unlock_operations (); + return -EFAULT; + } + if (flag) + rc = ibmphp_update_slot_info (slot_cur); + else + rc = 0; + + ibmphp_print_test (); + ibmphp_unlock_operations(); + return rc; +} + +struct hotplug_slot_ops ibmphp_hotplug_slot_ops = { + owner: THIS_MODULE, + set_attention_status: set_attention_status, + enable_slot: enable_slot, + disable_slot: ibmphp_disable_slot, + hardware_test: NULL, + get_power_status: get_power_status, + get_attention_status: get_attention_status, + get_latch_status: get_latch_status, + get_adapter_status: get_adapter_present, +/* get_max_bus_speed_status: get_max_bus_speed, + get_max_adapter_speed_status: get_max_adapter_speed, + get_cur_bus_speed_status: get_cur_bus_speed, + get_card_bus_names_status: get_card_bus_names, +*/ +}; + +static void ibmphp_unload (void) +{ + free_slots (); + debug ("after slots \n"); + ibmphp_free_resources (); + debug ("after resources \n"); + ibmphp_free_bus_info_queue (); + debug ("after bus info \n"); + ibmphp_free_ebda_hpc_queue (); + debug ("after ebda hpc \n"); + ibmphp_free_ebda_pci_rsrc_queue (); + debug ("after ebda pci rsrc \n"); +} + +static int __init ibmphp_init (void) +{ + int i = 0; + int rc = 0; + + init_flag = 1; + ibmphp_pci_root_ops = get_root_pci_ops (); + if (ibmphp_pci_root_ops == NULL) { + err ("cannot read bus operations... will not be able to read the cards. Please check your system \n"); + return -ENODEV; + } + + ibmphp_debug = debug; + + ibmphp_hpc_initvars (); + + for (i = 0; i < 16; i++) + irqs[i] = 0; + + if ((rc = ibmphp_access_ebda ())) { + ibmphp_unload (); + return rc; + } + debug ("after ibmphp_access_ebda () \n"); + + if ((rc = ibmphp_rsrc_init ())) { + ibmphp_unload (); + return rc; + } + debug ("AFTER Resource & EBDA INITIALIZATIONS \n"); + + max_slots = get_max_slots (); + + if (init_ops ()) { + ibmphp_unload (); + return -ENODEV; + } + ibmphp_print_test (); + if ((rc = ibmphp_hpc_start_poll_thread ())) { + ibmphp_unload (); + return -ENODEV; + } + + /* lock ourselves into memory with a module count of -1 + * so that no one can unload us. */ + MOD_DEC_USE_COUNT; + + info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); + + return 0; +} + +static void __exit ibmphp_exit (void) +{ + ibmphp_hpc_stop_poll_thread (); + debug ("after polling \n"); + ibmphp_unload (); + debug ("done \n"); +} + +module_init (ibmphp_init); +module_exit (ibmphp_exit); diff -Nru a/drivers/hotplug/ibmphp_ebda.c b/drivers/hotplug/ibmphp_ebda.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/ibmphp_ebda.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,851 @@ +/* + * IBM Hot Plug Controller Driver + * + * Written By: Tong Yu, IBM Corporation + * + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2002 IBM Corp. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ibmphp.h" + +/* + * POST builds data blocks(in this data block definition, a char-1 + * byte, short(or word)-2 byte, long(dword)-4 byte) in the Extended + * BIOS Data Area which describe the configuration of the hot-plug + * controllers and resources used by the PCI Hot-Plug devices. + * + * This file walks EBDA, maps data block from physical addr, + * reconstruct linked lists about all system resource(MEM, PFM, IO) + * already assigned by POST, as well as linked lists about hot plug + * controllers (ctlr#, slot#, bus&slot features...) + */ + +/* Global lists */ +LIST_HEAD (ibmphp_ebda_pci_rsrc_head); +LIST_HEAD (ibmphp_slot_head); + +/* Local variables */ +static struct ebda_hpc_list *hpc_list_ptr; +static struct ebda_rsrc_list *rsrc_list_ptr; +static struct rio_table_hdr *rio_table_ptr; +static LIST_HEAD (ebda_hpc_head); +static LIST_HEAD (bus_info_head); +static void *io_mem; + +/* Local functions */ +static int ebda_rsrc_controller (void); +static int ebda_rsrc_rsrc (void); +static int ebda_rio_table (void); + +static struct slot *alloc_ibm_slot (void) +{ + struct slot *slot; + + slot = kmalloc (sizeof (struct slot), GFP_KERNEL); + if (!slot) + return NULL; + memset (slot, 0, sizeof (*slot)); + return slot; +} + +static struct ebda_hpc_list *alloc_ebda_hpc_list (void) +{ + struct ebda_hpc_list *list; + + list = kmalloc (sizeof (struct ebda_hpc_list), GFP_KERNEL); + if (!list) + return NULL; + memset (list, 0, sizeof (*list)); + return list; +} + +static struct controller *alloc_ebda_hpc (u32 slot_count, u32 bus_count) +{ + struct controller *controller; + struct ebda_hpc_slot *slots; + struct ebda_hpc_bus *buses; + + controller = kmalloc (sizeof (struct controller), GFP_KERNEL); + if (!controller) + return NULL; + memset (controller, 0, sizeof (*controller)); + + slots = kmalloc (sizeof (struct ebda_hpc_slot) * slot_count, GFP_KERNEL); + if (!slots) { + kfree (controller); + return NULL; + } + memset (slots, 0, sizeof (*slots) * slot_count); + controller->slots = slots; + + buses = kmalloc (sizeof (struct ebda_hpc_bus) * bus_count, GFP_KERNEL); + if (!buses) { + kfree (controller->slots); + kfree (controller); + return NULL; + } + memset (buses, 0, sizeof (*buses) * bus_count); + controller->buses = buses; + + return controller; +} + +static void free_ebda_hpc (struct controller *controller) +{ + kfree (controller->slots); + controller->slots = NULL; + kfree (controller->buses); + controller->buses = NULL; + kfree (controller); +} + +static struct ebda_rsrc_list *alloc_ebda_rsrc_list (void) +{ + struct ebda_rsrc_list *list; + + list = kmalloc (sizeof (struct ebda_rsrc_list), GFP_KERNEL); + if (!list) + return NULL; + memset (list, 0, sizeof (*list)); + return list; +} + +static struct ebda_pci_rsrc *alloc_ebda_pci_rsrc (void) +{ + struct ebda_pci_rsrc *resource; + + resource = kmalloc (sizeof (struct ebda_pci_rsrc), GFP_KERNEL); + if (!resource) + return NULL; + memset (resource, 0, sizeof (*resource)); + return resource; +} + +static void print_bus_info (void) +{ + struct bus_info *ptr; + struct list_head *ptr1; + + list_for_each (ptr1, &bus_info_head) { + ptr = list_entry (ptr1, struct bus_info, bus_info_list); + debug ("%s - slot_min = %x\n", __FUNCTION__, ptr->slot_min); + debug ("%s - slot_max = %x\n", __FUNCTION__, ptr->slot_max); + debug ("%s - slot_count = %x\n", __FUNCTION__, ptr->slot_count); + debug ("%s - bus# = %x\n", __FUNCTION__, ptr->busno); + debug ("%s - current_speed = %x\n", __FUNCTION__, ptr->current_speed); + debug ("%s - supported_speed = %x\n", __FUNCTION__, ptr->supported_speed); + debug ("%s - controller_id = %x\n", __FUNCTION__, ptr->controller_id); + debug ("%s - bus_mode = %x\n", __FUNCTION__, ptr->supported_bus_mode); + } +} + +static void print_ebda_pci_rsrc (void) +{ + struct ebda_pci_rsrc *ptr; + struct list_head *ptr1; + + list_for_each (ptr1, &ibmphp_ebda_pci_rsrc_head) { + ptr = list_entry (ptr1, struct ebda_pci_rsrc, ebda_pci_rsrc_list); + debug ("%s - rsrc type: %x bus#: %x dev_func: %x start addr: %lx end addr: %lx\n", + __FUNCTION__, ptr->rsrc_type ,ptr->bus_num, ptr->dev_fun,ptr->start_addr, ptr->end_addr); + } +} + +static void print_ebda_hpc (void) +{ + struct controller *hpc_ptr; + struct list_head *ptr1; + u16 index; + + list_for_each (ptr1, &ebda_hpc_head) { + + hpc_ptr = list_entry (ptr1, struct controller, ebda_hpc_list); + + for (index = 0; index < hpc_ptr->slot_count; index++) { + debug ("%s - physical slot#: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_num); + debug ("%s - pci bus# of the slot: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_bus_num); + debug ("%s - index into ctlr addr: %x\n", __FUNCTION__, hpc_ptr->slots[index].ctl_index); + debug ("%s - cap of the slot: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_cap); + } + + for (index = 0; index < hpc_ptr->bus_count; index++) { + debug ("%s - bus# of each bus controlled by this ctlr: %x\n", __FUNCTION__, hpc_ptr->buses[index].bus_num); + } + + debug ("%s - type of hpc: %x\n", __FUNCTION__, hpc_ptr->ctlr_type); + switch (hpc_ptr->ctlr_type) { + case 1: + debug ("%s - bus: %x\n", __FUNCTION__, hpc_ptr->u.pci_ctlr.bus); + debug ("%s - dev_fun: %x\n", __FUNCTION__, hpc_ptr->u.pci_ctlr.dev_fun); + debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq); + break; + + case 0: + debug ("%s - io_start: %x\n", __FUNCTION__, hpc_ptr->u.isa_ctlr.io_start); + debug ("%s - io_end: %x\n", __FUNCTION__, hpc_ptr->u.isa_ctlr.io_end); + debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq); + break; + + case 2: + debug ("%s - wpegbbar: %lx\n", __FUNCTION__, hpc_ptr->u.wpeg_ctlr.wpegbbar); + debug ("%s - i2c_addr: %x\n", __FUNCTION__, hpc_ptr->u.wpeg_ctlr.i2c_addr); + debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq); + break; + } + } +} + +int ibmphp_access_ebda (void) +{ + u8 format, num_ctlrs, rio_complete, hs_complete; + u16 ebda_seg, num_entries, next_offset, offset, blk_id, sub_addr, rc, re, rc_id, re_id, base; + + + rio_complete = 0; + hs_complete = 0; + + io_mem = ioremap ((0x40 << 4) + 0x0e, 2); + if (!io_mem ) + return -ENOMEM; + ebda_seg = readw (io_mem); + iounmap (io_mem); + debug ("returned ebda segment: %x\n", ebda_seg); + + io_mem = ioremap (ebda_seg<<4, 65000); + if (!io_mem ) + return -ENOMEM; + next_offset = 0x180; + + for (;;) { + offset = next_offset; + next_offset = readw (io_mem + offset); /* offset of next blk */ + + offset += 2; + if (next_offset == 0) /* 0 indicate it's last blk */ + break; + blk_id = readw (io_mem + offset); /* this blk id */ + + offset += 2; + /* check if it is hot swap block or rio block */ + if (blk_id != 0x4853 && blk_id != 0x4752) + continue; + /* found hs table */ + if (blk_id == 0x4853) { + debug ("now enter hot swap block---\n"); + debug ("hot blk id: %x\n", blk_id); + format = readb (io_mem + offset); + + offset += 1; + if (format != 4) { + iounmap (io_mem); + return -ENODEV; + } + debug ("hot blk format: %x\n", format); + /* hot swap sub blk */ + base = offset; + + sub_addr = base; + re = readw (io_mem + sub_addr); /* next sub blk */ + + sub_addr += 2; + rc_id = readw (io_mem + sub_addr); /* sub blk id */ + + sub_addr += 2; + if (rc_id != 0x5243) { + iounmap (io_mem); + return -ENODEV; + } + /* rc sub blk signature */ + num_ctlrs = readb (io_mem + sub_addr); + + sub_addr += 1; + hpc_list_ptr = alloc_ebda_hpc_list (); + if (!hpc_list_ptr) { + iounmap (io_mem); + return -ENOMEM; + } + hpc_list_ptr->format = format; + hpc_list_ptr->num_ctlrs = num_ctlrs; + hpc_list_ptr->phys_addr = sub_addr; /* offset of RSRC_CONTROLLER blk */ + debug ("info about hpc descriptor---\n"); + debug ("hot blk format: %x\n", format); + debug ("num of controller: %x\n", num_ctlrs); + debug ("offset of hpc data structure enteries: %x\n ", sub_addr); + + sub_addr = base + re; /* re sub blk */ + rc = readw (io_mem + sub_addr); /* next sub blk */ + + sub_addr += 2; + re_id = readw (io_mem + sub_addr); /* sub blk id */ + + sub_addr += 2; + if (re_id != 0x5245) { + iounmap (io_mem); + return -ENODEV; + } + + /* signature of re */ + num_entries = readw (io_mem + sub_addr); + + sub_addr += 2; /* offset of RSRC_ENTRIES blk */ + rsrc_list_ptr = alloc_ebda_rsrc_list (); + if (!rsrc_list_ptr ) { + iounmap (io_mem); + return -ENOMEM; + } + rsrc_list_ptr->format = format; + rsrc_list_ptr->num_entries = num_entries; + rsrc_list_ptr->phys_addr = sub_addr; + + debug ("info about rsrc descriptor---\n"); + debug ("format: %x\n", format); + debug ("num of rsrc: %x\n", num_entries); + debug ("offset of rsrc data structure enteries: %x\n ", sub_addr); + + hs_complete = 1; + } + /* found rio table */ + else if (blk_id == 0x4752) { + debug ("now enter io table ---\n"); + debug ("rio blk id: %x\n", blk_id); + + rio_table_ptr = kmalloc (sizeof (struct rio_table_hdr), GFP_KERNEL); + if (!rio_table_ptr) + return -ENOMEM; + memset (rio_table_ptr, 0, sizeof (struct rio_table_hdr) ); + rio_table_ptr->ver_num = readb (io_mem + offset); + rio_table_ptr->scal_count = readb (io_mem + offset + 1); + rio_table_ptr->riodev_count = readb (io_mem + offset + 2); + rio_table_ptr->offset = offset +3 ; + + debug ("info about rio table hdr ---\n"); + debug ("ver_num: %x\nscal_count: %x\nriodev_count: %x\noffset of rio table: %x\n ", rio_table_ptr->ver_num, rio_table_ptr->scal_count, rio_table_ptr->riodev_count, rio_table_ptr->offset); + + rio_complete = 1; + } + + if (hs_complete && rio_complete) { + rc = ebda_rsrc_controller (); + if (rc) { + iounmap(io_mem); + return rc; + } + rc = ebda_rsrc_rsrc (); + if (rc) { + iounmap(io_mem); + return rc; + } + rc = ebda_rio_table (); + if (rc) { + iounmap(io_mem); + return rc; + } + iounmap (io_mem); + return 0; + } + } + iounmap (io_mem); + return -ENODEV; +} + + +/* + * map info (ctlr-id, slot count, slot#.. bus count, bus#, ctlr type...) of + * each hpc from physical address to a list of hot plug controllers based on + * hpc descriptors. + */ +static int ebda_rsrc_controller (void) +{ + u16 addr, addr_slot, addr_bus; + u8 ctlr_id, temp, bus_index; + u16 ctlr, slot, bus; + u16 slot_num, bus_num, index; + struct hotplug_slot *hp_slot_ptr; + struct controller *hpc_ptr; + struct ebda_hpc_bus *bus_ptr; + struct ebda_hpc_slot *slot_ptr; + struct bus_info *bus_info_ptr1, *bus_info_ptr2; + int rc; + + addr = hpc_list_ptr->phys_addr; + for (ctlr = 0; ctlr < hpc_list_ptr->num_ctlrs; ctlr++) { + bus_index = 1; + ctlr_id = readb (io_mem + addr); + addr += 1; + slot_num = readb (io_mem + addr); + + addr += 1; + addr_slot = addr; /* offset of slot structure */ + addr += (slot_num * 4); + + bus_num = readb (io_mem + addr); + + addr += 1; + addr_bus = addr; /* offset of bus */ + addr += (bus_num * 9); /* offset of ctlr_type */ + temp = readb (io_mem + addr); + + addr += 1; + /* init hpc structure */ + hpc_ptr = alloc_ebda_hpc (slot_num, bus_num); + if (!hpc_ptr ) { + iounmap (io_mem); + return -ENOMEM; + } + hpc_ptr->ctlr_id = ctlr_id; + hpc_ptr->ctlr_relative_id = ctlr; + hpc_ptr->slot_count = slot_num; + hpc_ptr->bus_count = bus_num; + debug ("now enter ctlr data struture ---\n"); + debug ("ctlr id: %x\n", ctlr_id); + debug ("ctlr_relative_id: %x\n", hpc_ptr->ctlr_relative_id); + debug ("count of slots controlled by this ctlr: %x\n", slot_num); + debug ("count of buses controlled by this ctlr: %x\n", bus_num); + + /* init slot structure, fetch slot, bus, cap... */ + slot_ptr = hpc_ptr->slots; + for (slot = 0; slot < slot_num; slot++) { + slot_ptr->slot_num = readb (io_mem + addr_slot); + slot_ptr->slot_bus_num = readb (io_mem + addr_slot + slot_num); + slot_ptr->ctl_index = readb (io_mem + addr_slot + 2*slot_num); + slot_ptr->slot_cap = readb (io_mem + addr_slot + 3*slot_num); + + // create bus_info lined list --- if only one slot per bus: slot_min = slot_max + + bus_info_ptr2 = ibmphp_find_same_bus_num (slot_ptr->slot_bus_num); + if (!bus_info_ptr2) { + bus_info_ptr1 = (struct bus_info *) kmalloc (sizeof (struct bus_info), GFP_KERNEL); + if (!bus_info_ptr1) { + iounmap (io_mem); + return -ENOMEM; + } + memset (bus_info_ptr1, 0, sizeof (struct bus_info)); + bus_info_ptr1->slot_min = slot_ptr->slot_num; + bus_info_ptr1->slot_max = slot_ptr->slot_num; + bus_info_ptr1->slot_count += 1; + bus_info_ptr1->busno = slot_ptr->slot_bus_num; + bus_info_ptr1->index = bus_index++; + bus_info_ptr1->current_speed = 0xff; + bus_info_ptr1->current_bus_mode = 0xff; + if ( ((slot_ptr->slot_cap) & EBDA_SLOT_133_MAX) == EBDA_SLOT_133_MAX ) + bus_info_ptr1->supported_speed = 3; + else if ( ((slot_ptr->slot_cap) & EBDA_SLOT_100_MAX) == EBDA_SLOT_100_MAX ) + bus_info_ptr1->supported_speed = 2; + else if ( ((slot_ptr->slot_cap) & EBDA_SLOT_66_MAX) == EBDA_SLOT_66_MAX ) + bus_info_ptr1->supported_speed = 1; + bus_info_ptr1->controller_id = hpc_ptr->ctlr_id; + if ( ((slot_ptr->slot_cap) & EBDA_SLOT_PCIX_CAP) == EBDA_SLOT_PCIX_CAP ) + bus_info_ptr1->supported_bus_mode = 1; + else + bus_info_ptr1->supported_bus_mode =0; + + + list_add_tail (&bus_info_ptr1->bus_info_list, &bus_info_head); + + } else { + bus_info_ptr2->slot_min = min (bus_info_ptr2->slot_min, slot_ptr->slot_num); + bus_info_ptr2->slot_max = max (bus_info_ptr2->slot_max, slot_ptr->slot_num); + bus_info_ptr2->slot_count += 1; + + } + + // end of creating the bus_info linked list + + slot_ptr++; + addr_slot += 1; + } + + /* init bus structure */ + bus_ptr = hpc_ptr->buses; + for (bus = 0; bus < bus_num; bus++) { + bus_ptr->bus_num = readb (io_mem + addr_bus); + bus_ptr++; + addr_bus += 1; + } + + hpc_ptr->ctlr_type = temp; + + switch (hpc_ptr->ctlr_type) { + case 1: + hpc_ptr->u.pci_ctlr.bus = readb (io_mem + addr); + hpc_ptr->u.pci_ctlr.dev_fun = readb (io_mem + addr + 1); + hpc_ptr->irq = readb (io_mem + addr + 2); + addr += 3; + break; + + case 0: + hpc_ptr->u.isa_ctlr.io_start = readw (io_mem + addr); + hpc_ptr->u.isa_ctlr.io_end = readw (io_mem + addr + 2); + hpc_ptr->irq = readb (io_mem + addr + 4); + addr += 5; + break; + + case 2: + hpc_ptr->u.wpeg_ctlr.wpegbbar = readl (io_mem + addr); + hpc_ptr->u.wpeg_ctlr.i2c_addr = readb (io_mem + addr + 4); + /* following 2 lines for testing purpose */ + if (hpc_ptr->u.wpeg_ctlr.i2c_addr == 0) + hpc_ptr->ctlr_type = 4; + + + hpc_ptr->irq = readb (io_mem + addr + 5); + addr += 6; + break; + case 4: + hpc_ptr->u.wpeg_ctlr.wpegbbar = readl (io_mem + addr); + hpc_ptr->u.wpeg_ctlr.i2c_addr = readb (io_mem + addr + 4); + hpc_ptr->irq = readb (io_mem + addr + 5); + addr += 6; + break; + default: + iounmap (io_mem); + return -ENODEV; + } + /* following 3 line: Now our driver only supports I2c ctlrType */ + if ((hpc_ptr->ctlr_type != 2) && (hpc_ptr->ctlr_type != 4)) { + err ("Please run this driver on ibm xseries440\n "); + return -ENODEV; + } + + hpc_ptr->revision = 0xff; + hpc_ptr->options = 0xff; + + // register slots with hpc core as well as create linked list of ibm slot + for (index = 0; index < hpc_ptr->slot_count; index++) { + + hp_slot_ptr = (struct hotplug_slot *) kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL); + if (!hp_slot_ptr) { + iounmap (io_mem); + return -ENOMEM; + } + memset (hp_slot_ptr, 0, sizeof (struct hotplug_slot)); + + hp_slot_ptr->info = (struct hotplug_slot_info *) kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); + if (!hp_slot_ptr->info) { + iounmap (io_mem); + kfree (hp_slot_ptr); + return -ENOMEM; + } + memset (hp_slot_ptr->info, 0, sizeof (struct hotplug_slot_info)); + + hp_slot_ptr->name = (char *) kmalloc (10, GFP_KERNEL); + if (!hp_slot_ptr->name) { + iounmap (io_mem); + kfree (hp_slot_ptr->info); + kfree (hp_slot_ptr); + return -ENOMEM; + } + + hp_slot_ptr->private = alloc_ibm_slot (); + if (!hp_slot_ptr->private) { + iounmap (io_mem); + kfree (hp_slot_ptr->name); + kfree (hp_slot_ptr->info); + kfree (hp_slot_ptr); + return -ENOMEM; + } + + ((struct slot *)hp_slot_ptr->private)->flag = TRUE; + snprintf (hp_slot_ptr->name, 10, "%d", hpc_ptr->slots[index].slot_num); + + ((struct slot *) hp_slot_ptr->private)->capabilities = hpc_ptr->slots[index].slot_cap; + ((struct slot *) hp_slot_ptr->private)->bus = hpc_ptr->slots[index].slot_bus_num; + + bus_info_ptr1 = ibmphp_find_same_bus_num (hpc_ptr->slots[index].slot_bus_num); + if (!bus_info_ptr1) { + iounmap (io_mem); + return -ENODEV; + } + ((struct slot *) hp_slot_ptr->private)->bus_on = bus_info_ptr1; + bus_info_ptr1 = NULL; + ((struct slot *) hp_slot_ptr->private)->ctrl = hpc_ptr; + + + ((struct slot *) hp_slot_ptr->private)->ctlr_index = hpc_ptr->slots[index].ctl_index; + ((struct slot *) hp_slot_ptr->private)->number = hpc_ptr->slots[index].slot_num; + + ((struct slot *) hp_slot_ptr->private)->hotplug_slot = hp_slot_ptr; + + rc = ibmphp_hpc_fillhpslotinfo (hp_slot_ptr); + if (rc) { + iounmap (io_mem); + return rc; + } + + rc = ibmphp_init_devno ((struct slot **) &hp_slot_ptr->private); + if (rc) { + iounmap (io_mem); + return rc; + } + hp_slot_ptr->ops = &ibmphp_hotplug_slot_ops; + + pci_hp_register (hp_slot_ptr); + + // end of registering ibm slot with hotplug core + + list_add (& ((struct slot *)(hp_slot_ptr->private))->ibm_slot_list, &ibmphp_slot_head); + } + + print_bus_info (); + list_add (&hpc_ptr->ebda_hpc_list, &ebda_hpc_head ); + + } /* each hpc */ + print_ebda_hpc (); + return 0; +} + +/* + * map info (bus, devfun, start addr, end addr..) of i/o, memory, + * pfm from the physical addr to a list of resource. + */ +static int ebda_rsrc_rsrc (void) +{ + u16 addr; + short rsrc; + u8 type, rsrc_type; + struct ebda_pci_rsrc *rsrc_ptr; + + addr = rsrc_list_ptr->phys_addr; + debug ("now entering rsrc land\n"); + debug ("offset of rsrc: %x\n", rsrc_list_ptr->phys_addr); + + for (rsrc = 0; rsrc < rsrc_list_ptr->num_entries; rsrc++) { + type = readb (io_mem + addr); + + addr += 1; + rsrc_type = type & EBDA_RSRC_TYPE_MASK; + + if (rsrc_type == EBDA_IO_RSRC_TYPE) { + rsrc_ptr = alloc_ebda_pci_rsrc (); + if (!rsrc_ptr) { + iounmap (io_mem); + return -ENOMEM; + } + rsrc_ptr->rsrc_type = type; + + rsrc_ptr->bus_num = readb (io_mem + addr); + rsrc_ptr->dev_fun = readb (io_mem + addr + 1); + rsrc_ptr->start_addr = readw (io_mem + addr + 2); + rsrc_ptr->end_addr = readw (io_mem + addr + 4); + addr += 6; + + debug ("rsrc from io type ----\n"); + debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %lx end addr: %lx\n", + rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr); + + list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head); + } + + if (rsrc_type == EBDA_MEM_RSRC_TYPE || rsrc_type == EBDA_PFM_RSRC_TYPE) { + rsrc_ptr = alloc_ebda_pci_rsrc (); + if (!rsrc_ptr ) { + iounmap (io_mem); + return -ENOMEM; + } + rsrc_ptr->rsrc_type = type; + + rsrc_ptr->bus_num = readb (io_mem + addr); + rsrc_ptr->dev_fun = readb (io_mem + addr + 1); + rsrc_ptr->start_addr = readl (io_mem + addr + 2); + rsrc_ptr->end_addr = readl (io_mem + addr + 6); + addr += 10; + + debug ("rsrc from mem or pfm ---\n"); + debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %lx end addr: %lx\n", + rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr); + + list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head); + } + } + kfree (rsrc_list_ptr); + rsrc_list_ptr = NULL; + print_ebda_pci_rsrc (); + return 0; +} + +/* + * map info of scalability details and rio details from physical address + */ +static int ebda_rio_table(void) +{ + u16 offset; + u8 i; + struct scal_detail *scal_detail_ptr; + struct rio_detail *rio_detail_ptr; + + offset = rio_table_ptr->offset; + for (i = 0; i < rio_table_ptr->scal_count; i++) { + + scal_detail_ptr = kmalloc (sizeof (struct scal_detail), GFP_KERNEL ); + if (!scal_detail_ptr ) + return -ENOMEM; + memset (scal_detail_ptr, 0, sizeof (struct scal_detail) ); + scal_detail_ptr->node_id = readb (io_mem + offset); + scal_detail_ptr->cbar = readl (io_mem+ offset + 1); + scal_detail_ptr->port0_node_connect = readb (io_mem + 5); + scal_detail_ptr->port0_port_connect = readb (io_mem + 6); + scal_detail_ptr->port1_node_connect = readb (io_mem + 7); + scal_detail_ptr->port1_port_connect = readb (io_mem + 8); + scal_detail_ptr->port2_node_connect = readb (io_mem + 9); + scal_detail_ptr->port2_port_connect = readb (io_mem + 10); + debug ("node_id: %x\ncbar: %x\nport0_node: %x\nport0_port: %x\nport1_node: %x\nport1_port: %x\nport2_node: %x\nport2_port: %x\n", scal_detail_ptr->node_id, scal_detail_ptr->cbar, scal_detail_ptr->port0_node_connect, scal_detail_ptr->port0_port_connect, scal_detail_ptr->port1_node_connect, scal_detail_ptr->port1_port_connect, scal_detail_ptr->port2_node_connect, scal_detail_ptr->port2_port_connect); +// list_add (&scal_detail_ptr->scal_detail_list, &scal_detail_head); + offset += 11; + } + for (i=0; i < rio_table_ptr->riodev_count; i++) { + rio_detail_ptr = kmalloc (sizeof (struct rio_detail), GFP_KERNEL ); + if (!rio_detail_ptr ) + return -ENOMEM; + memset (rio_detail_ptr, 0, sizeof (struct rio_detail) ); + rio_detail_ptr->rio_node_id = readb (io_mem + offset ); + rio_detail_ptr->bbar = readl (io_mem + offset + 1); + rio_detail_ptr->rio_type = readb (io_mem + offset + 5); + rio_detail_ptr->owner_id = readb (io_mem + offset + 6); + rio_detail_ptr->port0_node_connect = readb (io_mem + offset + 7); + rio_detail_ptr->port0_port_connect = readb (io_mem + offset + 8); + rio_detail_ptr->port1_node_connect = readb (io_mem + offset + 9); + rio_detail_ptr->port1_port_connect = readb (io_mem + offset + 10); + rio_detail_ptr->first_slot_num = readb (io_mem + offset + 11); + rio_detail_ptr->status = readb (io_mem + offset + 12); + debug ("rio_node_id: %x\nbbar: %x\nrio_type: %x\nowner_id: %x\nport0_node: %x\nport0_port: %x\nport1_node: %x\nport1_port: %x\nfirst_slot_num: %x\nstatus: %x\n", rio_detail_ptr->rio_node_id, rio_detail_ptr->bbar, rio_detail_ptr->rio_type, rio_detail_ptr->owner_id, rio_detail_ptr->port0_node_connect, rio_detail_ptr->port0_port_connect, rio_detail_ptr->port1_node_connect, rio_detail_ptr->port1_port_connect, rio_detail_ptr->first_slot_num, rio_detail_ptr->status); + offset += 13; + } + return 0; +} + +u16 ibmphp_get_total_controllers (void) +{ + return hpc_list_ptr->num_ctlrs; +} + +struct slot *ibmphp_get_slot_from_physical_num (u8 physical_num) +{ + struct slot *slot; + struct list_head *list; + + list_for_each (list, &ibmphp_slot_head) { + slot = list_entry (list, struct slot, ibm_slot_list); + if (slot->number == physical_num) + return slot; + } + return NULL; +} + +/* To find: + * - the smallest slot number + * - the largest slot number + * - the total number of the slots based on each bus + * (if only one slot per bus slot_min = slot_max ) + */ +struct bus_info *ibmphp_find_same_bus_num (u32 num) +{ + struct bus_info *ptr; + struct list_head *ptr1; + + list_for_each (ptr1, &bus_info_head) { + ptr = list_entry (ptr1, struct bus_info, bus_info_list); + if (ptr->busno == num) + return ptr; + } + return NULL; +} + +/* Finding relative bus number, in order to map corresponding + * bus register + */ +int ibmphp_get_bus_index (u8 num) +{ + struct bus_info *ptr; + struct list_head *ptr1; + + list_for_each (ptr1, &bus_info_head) { + ptr = list_entry (ptr1, struct bus_info, bus_info_list); + if (ptr->busno == num) + return ptr->index; + } + return -ENODEV; +} + +void ibmphp_free_bus_info_queue (void) +{ + struct bus_info *bus_info; + struct list_head *list; + struct list_head *next; + + list_for_each_safe (list, next, &bus_info_head ) { + bus_info = list_entry (list, struct bus_info, bus_info_list); + kfree (bus_info); + } +} + +/* + * Calculate the total hot pluggable slots controlled by total hpcs + */ +/* +int ibmphp_get_total_hp_slots (void) +{ + struct ebda_hpc *ptr; + int slot_num = 0; + + ptr = ebda_hpc_head; + while (ptr != NULL) { + slot_num += ptr->slot_count; + ptr = ptr->next; + } + return slot_num; +} +*/ + +void ibmphp_free_ebda_hpc_queue (void) +{ + struct controller *controller; + struct list_head *list; + struct list_head *next; + + list_for_each_safe (list, next, &ebda_hpc_head) { + controller = list_entry (list, struct controller, ebda_hpc_list); + free_ebda_hpc (controller); + } +} + +void ibmphp_free_ebda_pci_rsrc_queue (void) +{ + struct ebda_pci_rsrc *resource; + struct list_head *list; + struct list_head *next; + + list_for_each_safe (list, next, &ibmphp_ebda_pci_rsrc_head) { + resource = list_entry (list, struct ebda_pci_rsrc, ebda_pci_rsrc_list); + kfree (resource); + resource = NULL; + } +} + diff -Nru a/drivers/hotplug/ibmphp_hpc.c b/drivers/hotplug/ibmphp_hpc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/ibmphp_hpc.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,1135 @@ +/* + * IBM Hot Plug Controller Driver + * + * Written By: Jyoti Shah, IBM Corporation + * + * Copyright (c) 2001,2001 IBM Corp. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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. + * + * Send feedback to + * + * + */ + +//#include +#include +#include +#include +#include +#include +#include "ibmphp.h" + +#define POLL_NO 0x01 +#define POLL_YES 0x00 + +static int to_debug = FALSE; +#define debug_polling(fmt, arg...) do { if (to_debug) debug (fmt, arg); } while (0) + +//---------------------------------------------------------------------------- +// timeout values +//---------------------------------------------------------------------------- +#define CMD_COMPLETE_TOUT_SEC 60 // give HPC 60 sec to finish cmd +#define HPC_CTLR_WORKING_TOUT 60 // give HPC 60 sec to finish cmd +#define HPC_GETACCESS_TIMEOUT 60 // seconds +#define POLL_INTERVAL_SEC 2 // poll HPC every 2 seconds +#define POLL_LATCH_CNT 5 // poll latch 5 times, then poll slots + +//---------------------------------------------------------------------------- +// Winnipeg Architected Register Offsets +//---------------------------------------------------------------------------- +#define WPG_I2CMBUFL_OFFSET 0x08 // I2C Message Buffer Low +#define WPG_I2CMOSUP_OFFSET 0x10 // I2C Master Operation Setup Reg +#define WPG_I2CMCNTL_OFFSET 0x20 // I2C Master Control Register +#define WPG_I2CPARM_OFFSET 0x40 // I2C Parameter Register +#define WPG_I2CSTAT_OFFSET 0x70 // I2C Status Register + +//---------------------------------------------------------------------------- +// Winnipeg Store Type commands (Add this commands to the register offset) +//---------------------------------------------------------------------------- +#define WPG_I2C_AND 0x1000 // I2C AND operation +#define WPG_I2C_OR 0x2000 // I2C OR operation + +//---------------------------------------------------------------------------- +// Command set for I2C Master Operation Setup Regisetr +//---------------------------------------------------------------------------- +#define WPG_READATADDR_MASK 0x00010000 // read,bytes,I2C shifted,index +#define WPG_WRITEATADDR_MASK 0x40010000 // write,bytes,I2C shifted,index +#define WPG_READDIRECT_MASK 0x10010000 +#define WPG_WRITEDIRECT_MASK 0x60010000 + + +//---------------------------------------------------------------------------- +// bit masks for I2C Master Control Register +//---------------------------------------------------------------------------- +#define WPG_I2CMCNTL_STARTOP_MASK 0x00000002 // Start the Operation + +//---------------------------------------------------------------------------- +// +//---------------------------------------------------------------------------- +#define WPG_I2C_IOREMAP_SIZE 0x2044 // size of linear address interval +#define WPG_CTLR_MAX 0x01 // max controllers +#define WPG_SLOT_MAX 0x06 // max slots +#define WPG_CTLR_SLOT_MAX 0x06 // max slots per controller +#define WPG_FIRST_CTLR 0x00 // index of the controller + +//---------------------------------------------------------------------------- +// command index +//---------------------------------------------------------------------------- +#define WPG_1ST_SLOT_INDEX 0x01 // index - 1st slot for ctlr +#define WPG_CTLR_INDEX 0x0F // index - ctlr +#define WPG_1ST_EXTSLOT_INDEX 0x10 // index - 1st ext slot for ctlr +#define WPG_1ST_BUS_INDEX 0x1F // index - 1st bus for ctlr + +//---------------------------------------------------------------------------- +// macro utilities +//---------------------------------------------------------------------------- +// if bits 20,22,25,26,27,29,30 are OFF return TRUE +#define HPC_I2CSTATUS_CHECK(s) ((u8)((s & 0x00000A76) ? FALSE : TRUE)) + +// return code 0:poll slots, 1-POLL_LATCH_CNT:poll latch register +#define INCREMENT_POLLCNT(i) ((i < POLL_LATCH_CNT) ? i++ : (i=0)) +//---------------------------------------------------------------------------- +// global variables +//---------------------------------------------------------------------------- +static int ibmphp_shutdown; +static int tid_poll; +static int stop_polling; // 2 values: poll, don't poll +static struct semaphore sem_hpcaccess; // lock access to HPC +static struct semaphore semOperations; // lock all operations and + // access to data structures +static struct semaphore sem_exit; // make sure polling thread goes away +static struct semaphore sem_poll; // make sure poll is idle +//---------------------------------------------------------------------------- +// local function prototypes +//---------------------------------------------------------------------------- +static u8 ctrl_read (struct controller *, void *, u8); +static u8 ctrl_write (struct controller *, void *, u8, u8); +static u8 hpc_writecmdtoindex (u8, u8); +static u8 hpc_readcmdtoindex (u8, u8); +static void get_hpc_access (void); +static void free_hpc_access (void); +static void poll_hpc (void); +static int update_slot (struct slot *, u8); +static int process_changeinstatus (struct slot *, struct slot *); +static int process_changeinlatch (u8, u8); +static int hpc_poll_thread (void *); +static int hpc_wait_ctlr_notworking (int, struct controller *, void *, u8 *); +//---------------------------------------------------------------------------- + + +/*---------------------------------------------------------------------- +* Name: ibmphp_hpc_initvars +* +* Action: initialize semaphores and variables +*---------------------------------------------------------------------*/ +void ibmphp_hpc_initvars (void) +{ + debug ("%s - Entry\n", __FUNCTION__); + + init_MUTEX (&sem_hpcaccess); + init_MUTEX (&semOperations); + init_MUTEX_LOCKED (&sem_exit); + init_MUTEX_LOCKED (&sem_poll); + stop_polling = POLL_YES; + to_debug = FALSE; + ibmphp_shutdown = FALSE; + tid_poll = 0; + + debug ("%s - Exit\n", __FUNCTION__); +} + +/*---------------------------------------------------------------------- +* Name: ctrl_read +* +* Action: read from HPC over I2C +* +*---------------------------------------------------------------------*/ +static u8 ctrl_read (struct controller *ctlr_ptr, void *WPGBbar, u8 index) +{ + u8 status; + int i; + void *wpg_addr; // base addr + offset + ulong wpg_data, // data to/from WPG LOHI format + ultemp, data; // actual data HILO format + + + debug_polling ("%s - Entry WPGBbar[%lx] index[%x] \n", __FUNCTION__, (ulong) WPGBbar, index); + + //-------------------------------------------------------------------- + // READ - step 1 + // read at address, byte length, I2C address (shifted), index + // or read direct, byte length, index + if (ctlr_ptr->ctlr_type == 0x02) { + data = WPG_READATADDR_MASK; + // fill in I2C address + ultemp = (ulong) ctlr_ptr->u.wpeg_ctlr.i2c_addr; + ultemp = ultemp >> 1; + data |= (ultemp << 8); + + // fill in index + data |= (ulong) index; + } else if (ctlr_ptr->ctlr_type == 0x04) { + data = WPG_READDIRECT_MASK; + + // fill in index + ultemp = (ulong) index; + ultemp = ultemp << 8; + data |= ultemp; + } else { + err ("this controller type is not supported \n"); + return HPC_ERROR; + } + + wpg_data = swab32 (data); // swap data before writing + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMOSUP_OFFSET; + writel (wpg_data, wpg_addr); + + //-------------------------------------------------------------------- + // READ - step 2 : clear the message buffer + data = 0x00000000; + wpg_data = swab32 (data); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET; + writel (wpg_data, wpg_addr); + + //-------------------------------------------------------------------- + // READ - step 3 : issue start operation, I2C master control bit 30:ON + // 2020 : [20] OR operation at [20] offset 0x20 + data = WPG_I2CMCNTL_STARTOP_MASK; + wpg_data = swab32 (data); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET + (ulong) WPG_I2C_OR; + writel (wpg_data, wpg_addr); + + //-------------------------------------------------------------------- + // READ - step 4 : wait until start operation bit clears + i = CMD_COMPLETE_TOUT_SEC; + while (i) { + long_delay (1 * HZ / 100); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET; + wpg_data = readl (wpg_addr); + data = swab32 (wpg_data); + if (!(data & WPG_I2CMCNTL_STARTOP_MASK)) + break; + i--; + } + if (i == 0) { + debug ("%s - Error : WPG timeout\n", __FUNCTION__); + return HPC_ERROR; + } + //-------------------------------------------------------------------- + // READ - step 5 : read I2C status register + i = CMD_COMPLETE_TOUT_SEC; + while (i) { + long_delay (1 * HZ / 100); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CSTAT_OFFSET; + wpg_data = readl (wpg_addr); + data = swab32 (wpg_data); + if (HPC_I2CSTATUS_CHECK (data)) + break; + i--; + } + if (i == 0) { + debug ("ctrl_read - Exit Error:I2C timeout\n"); + return HPC_ERROR; + } + + //-------------------------------------------------------------------- + // READ - step 6 : get DATA + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET; + wpg_data = readl (wpg_addr); + data = swab32 (wpg_data); + + status = (u8) data; + + debug_polling ("%s - Exit index[%x] status[%x]\n", __FUNCTION__, index, status); + + return (status); +} + +/*---------------------------------------------------------------------- +* Name: ctrl_write +* +* Action: write to HPC over I2C +* +* Return 0 or error codes +*---------------------------------------------------------------------*/ +static u8 ctrl_write (struct controller *ctlr_ptr, void *WPGBbar, u8 index, u8 cmd) +{ + u8 rc; + void *wpg_addr; // base addr + offset + ulong wpg_data, // data to/from WPG LOHI format + ultemp, data; // actual data HILO format + int i; + + + debug_polling ("%s - Entry WPGBbar[%lx] index[%x] cmd[%x]\n", + __FUNCTION__, (ulong) WPGBbar, index, cmd); + + rc = 0; + //-------------------------------------------------------------------- + // WRITE - step 1 + // write at address, byte length, I2C address (shifted), index + // or write direct, byte length, index + data = 0x00000000; + + if (ctlr_ptr->ctlr_type == 0x02) { + data = WPG_WRITEATADDR_MASK; + // fill in I2C address + ultemp = (ulong) ctlr_ptr->u.wpeg_ctlr.i2c_addr; + ultemp = ultemp >> 1; + data |= (ultemp << 8); + + // fill in index + data |= (ulong) index; + } else if (ctlr_ptr->ctlr_type == 0x04) { + data = WPG_WRITEDIRECT_MASK; + + // fill in index + ultemp = (ulong) index; + ultemp = ultemp << 8; + data |= ultemp; + } else { + err ("this controller type is not supported \n"); + return HPC_ERROR; + } + + wpg_data = swab32 (data); // swap data before writing + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMOSUP_OFFSET; + writel (wpg_data, wpg_addr); + + //-------------------------------------------------------------------- + // WRITE - step 2 : clear the message buffer + data = 0x00000000 | (ulong) cmd; + wpg_data = swab32 (data); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET; + writel (wpg_data, wpg_addr); + + //-------------------------------------------------------------------- + // WRITE - step 3 : issue start operation,I2C master control bit 30:ON + // 2020 : [20] OR operation at [20] offset 0x20 + data = WPG_I2CMCNTL_STARTOP_MASK; + wpg_data = swab32 (data); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET + (ulong) WPG_I2C_OR; + writel (wpg_data, wpg_addr); + + //-------------------------------------------------------------------- + // WRITE - step 4 : wait until start operation bit clears + i = CMD_COMPLETE_TOUT_SEC; + while (i) { + long_delay (1 * HZ / 100); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET; + wpg_data = readl (wpg_addr); + data = swab32 (wpg_data); + if (!(data & WPG_I2CMCNTL_STARTOP_MASK)) + break; + i--; + } + if (i == 0) { + debug ("%s - Exit Error:WPG timeout\n", __FUNCTION__); + rc = HPC_ERROR; + } + + //-------------------------------------------------------------------- + // WRITE - step 5 : read I2C status register + i = CMD_COMPLETE_TOUT_SEC; + while (i) { + long_delay (1 * HZ / 100); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CSTAT_OFFSET; + wpg_data = readl (wpg_addr); + data = swab32 (wpg_data); + if (HPC_I2CSTATUS_CHECK (data)) + break; + i--; + } + if (i == 0) { + debug ("ctrl_read - Error : I2C timeout\n"); + rc = HPC_ERROR; + } + + debug_polling ("%s Exit rc[%x]\n", __FUNCTION__, rc); + return (rc); +} + +/*---------------------------------------------------------------------- +* Name: hpc_writecmdtoindex() +* +* Action: convert a write command to proper index within a controller +* +* Return index, HPC_ERROR +*---------------------------------------------------------------------*/ +static u8 hpc_writecmdtoindex (u8 cmd, u8 index) +{ + u8 rc; + + switch (cmd) { + case HPC_CTLR_ENABLEIRQ: // 0x00.N.15 + case HPC_CTLR_CLEARIRQ: // 0x06.N.15 + case HPC_CTLR_RESET: // 0x07.N.15 + case HPC_CTLR_IRQSTEER: // 0x08.N.15 + case HPC_CTLR_DISABLEIRQ: // 0x01.N.15 + case HPC_ALLSLOT_ON: // 0x11.N.15 + case HPC_ALLSLOT_OFF: // 0x12.N.15 + rc = 0x0F; + break; + + case HPC_SLOT_OFF: // 0x02.Y.0-14 + case HPC_SLOT_ON: // 0x03.Y.0-14 + case HPC_SLOT_ATTNOFF: // 0x04.N.0-14 + case HPC_SLOT_ATTNON: // 0x05.N.0-14 + case HPC_SLOT_BLINKLED: // 0x13.N.0-14 + rc = index; + break; + + case HPC_BUS_33CONVMODE: + case HPC_BUS_66CONVMODE: + case HPC_BUS_66PCIXMODE: + case HPC_BUS_100PCIXMODE: + case HPC_BUS_133PCIXMODE: + rc = index + WPG_1ST_BUS_INDEX - 1; + break; + + default: + err ("hpc_writecmdtoindex - Error invalid cmd[%x]\n", cmd); + rc = HPC_ERROR; + } + + return rc; +} + +/*---------------------------------------------------------------------- +* Name: hpc_readcmdtoindex() +* +* Action: convert a read command to proper index within a controller +* +* Return index, HPC_ERROR +*---------------------------------------------------------------------*/ +static u8 hpc_readcmdtoindex (u8 cmd, u8 index) +{ + u8 rc; + + switch (cmd) { + case READ_CTLRSTATUS: + rc = 0x0F; + break; + case READ_SLOTSTATUS: + case READ_ALLSTAT: + rc = index; + break; + case READ_EXTSLOTSTATUS: + rc = index + WPG_1ST_EXTSLOT_INDEX; + break; + case READ_BUSSTATUS: + rc = index + WPG_1ST_BUS_INDEX - 1; + break; + case READ_SLOTLATCHLOWREG: + rc = 0x28; + break; + case READ_REVLEVEL: + rc = 0x25; + break; + case READ_HPCOPTIONS: + rc = 0x27; + break; + default: + rc = HPC_ERROR; + } + return rc; +} + +/*---------------------------------------------------------------------- +* Name: HPCreadslot() +* +* Action: issue a READ command to HPC +* +* Input: pslot - can not be NULL for READ_ALLSTAT +* pstatus - can be NULL for READ_ALLSTAT +* +* Return 0 or error codes +*---------------------------------------------------------------------*/ +int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus) +{ + void *wpg_bbar; + struct controller *ctlr_ptr; + struct list_head *pslotlist; + u8 index, status; + int rc = 0; + int busindex; + + debug_polling ("%s - Entry pslot[%lx] cmd[%x] pstatus[%lx]\n", + __FUNCTION__, (ulong) pslot, cmd, (ulong) pstatus); + + if ((pslot == NULL) + || ((pstatus == NULL) && (cmd != READ_ALLSTAT) && (cmd != READ_BUSSTATUS))) { + rc = -EINVAL; + err ("%s - Error invalid pointer, rc[%d]\n", __FUNCTION__, rc); + return rc; + } + + if (cmd == READ_BUSSTATUS) { + busindex = ibmphp_get_bus_index (pslot->bus); + if (busindex < 0) { + rc = -EINVAL; + err ("%s - Exit Error:invalid bus, rc[%d]\n", __FUNCTION__, rc); + return rc; + } else + index = (u8) busindex; + } else + index = pslot->ctlr_index; + + index = hpc_readcmdtoindex (cmd, index); + + if (index == HPC_ERROR) { + rc = -EINVAL; + err ("%s - Exit Error:invalid index, rc[%d]\n", __FUNCTION__, rc); + return rc; + } + + ctlr_ptr = pslot->ctrl; + + get_hpc_access (); + + //-------------------------------------------------------------------- + // map physical address to logical address + //-------------------------------------------------------------------- + wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE); + + //-------------------------------------------------------------------- + // check controller status before reading + //-------------------------------------------------------------------- + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status); + if (!rc) { + switch (cmd) { + case READ_ALLSTAT: + // update the slot structure + pslot->ctrl->status = status; + pslot->status = ctrl_read (ctlr_ptr, wpg_bbar, index); + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, + &status); + if (!rc) + pslot->ext_status = ctrl_read (ctlr_ptr, wpg_bbar, index + WPG_1ST_EXTSLOT_INDEX); + + break; + + case READ_SLOTSTATUS: + // DO NOT update the slot structure + *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); + break; + + case READ_EXTSLOTSTATUS: + // DO NOT update the slot structure + *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); + break; + + case READ_CTLRSTATUS: + // DO NOT update the slot structure + *pstatus = status; + break; + + case READ_BUSSTATUS: + pslot->busstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); + break; + case READ_REVLEVEL: + *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); + break; + case READ_HPCOPTIONS: + *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); + break; + case READ_SLOTLATCHLOWREG: + // DO NOT update the slot structure + *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); + break; + + // Not used + case READ_ALLSLOT: + list_for_each (pslotlist, &ibmphp_slot_head) { + pslot = list_entry (pslotlist, struct slot, ibm_slot_list); + index = pslot->ctlr_index; + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, + wpg_bbar, &status); + if (!rc) { + pslot->status = ctrl_read (ctlr_ptr, wpg_bbar, index); + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, + ctlr_ptr, wpg_bbar, &status); + if (!rc) + pslot->ext_status = + ctrl_read (ctlr_ptr, wpg_bbar, + index + WPG_1ST_EXTSLOT_INDEX); + } else { + err ("%s - Error ctrl_read failed\n", __FUNCTION__); + rc = -EINVAL; + break; + } + } + break; + default: + rc = -EINVAL; + break; + } + } + //-------------------------------------------------------------------- + // cleanup + //-------------------------------------------------------------------- + iounmap (wpg_bbar); // remove physical to logical address mapping + free_hpc_access (); + + debug_polling ("%s - Exit rc[%d]\n", __FUNCTION__, rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: ibmphp_hpc_writeslot() +* +* Action: issue a WRITE command to HPC +*---------------------------------------------------------------------*/ +int ibmphp_hpc_writeslot (struct slot * pslot, u8 cmd) +{ + void *wpg_bbar; + struct controller *ctlr_ptr; + u8 index, status; + int busindex; + u8 done; + int rc = 0; + int timeout; + + debug_polling ("%s - Entry pslot[%lx] cmd[%x]\n", __FUNCTION__, (ulong) pslot, cmd); + if (pslot == NULL) { + rc = -EINVAL; + err ("%s - Error Exit rc[%d]\n", __FUNCTION__, rc); + return rc; + } + + if ((cmd == HPC_BUS_33CONVMODE) || (cmd == HPC_BUS_66CONVMODE) || + (cmd == HPC_BUS_66PCIXMODE) || (cmd == HPC_BUS_100PCIXMODE) || + (cmd == HPC_BUS_133PCIXMODE)) { + busindex = ibmphp_get_bus_index (pslot->bus); + if (busindex < 0) { + rc = -EINVAL; + err ("%s - Exit Error:invalid bus, rc[%d]\n", __FUNCTION__, rc); + return rc; + } else + index = (u8) busindex; + } else + index = pslot->ctlr_index; + + index = hpc_writecmdtoindex (cmd, index); + + if (index == HPC_ERROR) { + rc = -EINVAL; + err ("%s - Error Exit rc[%d]\n", __FUNCTION__, rc); + return rc; + } + + ctlr_ptr = pslot->ctrl; + + get_hpc_access (); + + //-------------------------------------------------------------------- + // map physical address to logical address + //-------------------------------------------------------------------- + wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE); + + debug ("%s - ctlr id[%x] physical[%lx] logical[%lx] i2c[%x]\n", __FUNCTION__, + ctlr_ptr->ctlr_id, (ulong) (ctlr_ptr->u.wpeg_ctlr.wpegbbar), (ulong) wpg_bbar, + ctlr_ptr->u.wpeg_ctlr.i2c_addr); + + //-------------------------------------------------------------------- + // check controller status before writing + //-------------------------------------------------------------------- + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status); + if (!rc) { + + ctrl_write (ctlr_ptr, wpg_bbar, index, cmd); + + //-------------------------------------------------------------------- + // check controller is still not working on the command + //-------------------------------------------------------------------- + timeout = CMD_COMPLETE_TOUT_SEC; + done = FALSE; + while (!done) { + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, + &status); + if (!rc) { + if (NEEDTOCHECK_CMDSTATUS (cmd)) { + if (CTLR_FINISHED (status) == HPC_CTLR_FINISHED_YES) + done = TRUE; + } else + done = TRUE; + } + if (!done) { + long_delay (1 * HZ); + if (timeout < 1) { + done = TRUE; + err ("%s - Error command complete timeout\n", __FUNCTION__); + rc = -EFAULT; + } else + timeout--; + } + } + ctlr_ptr->status = status; + } + // cleanup + iounmap (wpg_bbar); // remove physical to logical address mapping + free_hpc_access (); + + debug_polling ("%s - Exit rc[%d]\n", __FUNCTION__, rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: get_hpc_access() +* +* Action: make sure only one process can access HPC at one time +*---------------------------------------------------------------------*/ +static void get_hpc_access (void) +{ + down (&sem_hpcaccess); +} + +/*---------------------------------------------------------------------- +* Name: free_hpc_access() +*---------------------------------------------------------------------*/ +void free_hpc_access (void) +{ + up (&sem_hpcaccess); +} + +/*---------------------------------------------------------------------- +* Name: ibmphp_lock_operations() +* +* Action: make sure only one process can change the data structure +*---------------------------------------------------------------------*/ +void ibmphp_lock_operations (void) +{ + down (&semOperations); + stop_polling = POLL_NO; + to_debug = TRUE; + + /* waiting for polling to actually stop */ + down (&sem_poll); +} + +/*---------------------------------------------------------------------- +* Name: ibmphp_unlock_operations() +*---------------------------------------------------------------------*/ +void ibmphp_unlock_operations (void) +{ + debug ("%s - Entry\n", __FUNCTION__); + stop_polling = POLL_YES; + to_debug = FALSE; + up (&semOperations); + debug ("%s - Exit\n", __FUNCTION__); +} + +/*---------------------------------------------------------------------- +* Name: poll_hpc() +*---------------------------------------------------------------------*/ +static void poll_hpc (void) +{ + struct slot myslot, *pslot = NULL; + struct list_head *pslotlist; + int rc; + u8 oldlatchlow = 0x00; + u8 curlatchlow = 0x00; + int pollcnt = 0; + u8 ctrl_count = 0x00; + + debug ("poll_hpc - Entry\n"); + + while (!ibmphp_shutdown) { + if (stop_polling) { + debug ("poll_hpc - stop_polling\n"); + up (&sem_poll); + /* to prevent deadlock */ + if (ibmphp_shutdown) + break; + /* to make the thread sleep */ + down (&semOperations); + up (&semOperations); + debug ("poll_hpc - after stop_polling sleep\n"); + } else { + if (pollcnt) { + // only poll the latch register + oldlatchlow = curlatchlow; + + ctrl_count = 0x00; + list_for_each (pslotlist, &ibmphp_slot_head) { + if (ctrl_count >= ibmphp_get_total_controllers()) + break; + pslot = list_entry (pslotlist, struct slot, ibm_slot_list); + if (pslot->ctrl->ctlr_relative_id == ctrl_count) { + ctrl_count++; + if (READ_SLOT_LATCH (pslot->ctrl)) { + rc = ibmphp_hpc_readslot (pslot, + READ_SLOTLATCHLOWREG, + &curlatchlow); + if (oldlatchlow != curlatchlow) + process_changeinlatch (oldlatchlow, + curlatchlow); + } + } + } + } else { + list_for_each (pslotlist, &ibmphp_slot_head) { + if (stop_polling) + break; + pslot = list_entry (pslotlist, struct slot, ibm_slot_list); + // make a copy of the old status + memcpy ((void *) &myslot, (void *) pslot, + sizeof (struct slot)); + rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL); + if ((myslot.status != pslot->status) + || (myslot.ext_status != pslot->ext_status)) + process_changeinstatus (pslot, &myslot); + } + + if (!stop_polling) { + ctrl_count = 0x00; + list_for_each (pslotlist, &ibmphp_slot_head) { + if (ctrl_count >= ibmphp_get_total_controllers()) + break; + pslot = + list_entry (pslotlist, struct slot, + ibm_slot_list); + if (pslot->ctrl->ctlr_relative_id == ctrl_count) { + ctrl_count++; + if (READ_SLOT_LATCH (pslot->ctrl)) + rc = ibmphp_hpc_readslot (pslot, + READ_SLOTLATCHLOWREG, + &curlatchlow); + } + } + } + } + INCREMENT_POLLCNT (pollcnt); + long_delay (POLL_INTERVAL_SEC * HZ); // snooze + } + } + up (&sem_poll); + up (&sem_exit); + debug ("poll_hpc - Exit\n"); +} + + +/* ---------------------------------------------------------------------- + * Name: ibmphp_hpc_fillhpslotinfo(hotplug_slot * phpslot) + * + * Action: fill out the hotplug_slot info + * + * Input: pointer to hotplug_slot + * + * Return + * Value: 0 or error codes + *-----------------------------------------------------------------------*/ +int ibmphp_hpc_fillhpslotinfo (struct hotplug_slot *phpslot) +{ + int rc = 0; + struct slot *pslot; + + if (phpslot && phpslot->private) { + pslot = (struct slot *) phpslot->private; + rc = update_slot (pslot, (u8) TRUE); + if (!rc) { + + // power - enabled:1 not:0 + phpslot->info->power_status = SLOT_POWER (pslot->status); + + // attention - off:0, on:1, blinking:2 + phpslot->info->attention_status = SLOT_ATTN (pslot->status, pslot->ext_status); + + // latch - open:1 closed:0 + phpslot->info->latch_status = SLOT_LATCH (pslot->status); + + // pci board - present:1 not:0 + if (SLOT_PRESENT (pslot->status)) + phpslot->info->adapter_status = 1; + else + phpslot->info->adapter_status = 0; +/* + if (pslot->bus_on->supported_bus_mode + && (pslot->bus_on->supported_speed == BUS_SPEED_66)) + phpslot->info->max_bus_speed_status = BUS_SPEED_66PCIX; + else + phpslot->info->max_bus_speed_status = pslot->bus_on->supported_speed; +*/ } else + rc = -EINVAL; + } else + rc = -EINVAL; + + return rc; +} + +/*---------------------------------------------------------------------- +* Name: update_slot +* +* Action: fill out slot status and extended status, controller status +* +* Input: pointer to slot struct +*---------------------------------------------------------------------*/ +static int update_slot (struct slot *pslot, u8 update) +{ + int rc = 0; + + debug ("%s - Entry pslot[%lx]\n", __FUNCTION__, (ulong) pslot); + rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL); + debug ("%s - Exit rc[%d]\n", __FUNCTION__, rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: process_changeinstatus +* +* Action: compare old and new slot status, process the change in status +* +* Input: pointer to slot struct, old slot struct +* +* Return 0 or error codes +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +static int process_changeinstatus (struct slot *pslot, struct slot *poldslot) +{ + u8 status; + int rc = 0; + u8 disable = FALSE; + u8 update = FALSE; + + debug ("process_changeinstatus - Entry pslot[%lx], poldslot[%lx]\n", (ulong) pslot, + (ulong) poldslot); + + // bit 0 - HPC_SLOT_POWER + if ((pslot->status & 0x01) != (poldslot->status & 0x01)) + /* ????????? DO WE NEED TO UPDATE BUS SPEED INFO HERE ??? */ + update = TRUE; + + // bit 1 - HPC_SLOT_CONNECT + // ignore + + // bit 2 - HPC_SLOT_ATTN + if ((pslot->status & 0x04) != (poldslot->status & 0x04)) + update = TRUE; + + // bit 3 - HPC_SLOT_PRSNT2 + // bit 4 - HPC_SLOT_PRSNT1 + if (((pslot->status & 0x08) != (poldslot->status & 0x08)) + || ((pslot->status & 0x10) != (poldslot->status & 0x10))) + update = TRUE; + + // bit 5 - HPC_SLOT_PWRGD + if ((pslot->status & 0x20) != (poldslot->status & 0x20)) + // OFF -> ON: ignore, ON -> OFF: disable slot + if (poldslot->status & 0x20) + disable = TRUE; + + // bit 6 - HPC_SLOT_BUS_SPEED + // ignore + + // bit 7 - HPC_SLOT_LATCH + if ((pslot->status & 0x80) != (poldslot->status & 0x80)) { + update = TRUE; + // OPEN -> CLOSE + if (pslot->status & 0x80) { + if (SLOT_POWER (pslot->status)) { + // power goes on and off after closing latch + // check again to make sure power is still ON + long_delay (1 * HZ); + rc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &status); + if (SLOT_POWER (status)) + update = TRUE; + else // overwrite power in pslot to OFF + pslot->status &= ~HPC_SLOT_POWER; + } + } + // CLOSE -> OPEN + else if ((SLOT_POWER (poldslot->status) == HPC_SLOT_POWER_ON) + || (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED)) { + disable = TRUE; + } + // else - ignore + } + // bit 4 - HPC_SLOT_BLINK_ATTN + if ((pslot->ext_status & 0x08) != (poldslot->ext_status & 0x08)) + update = TRUE; + + if (disable) { + debug ("process_changeinstatus - disable slot\n"); + pslot->flag = FALSE; + rc = ibmphp_disable_slot (pslot->hotplug_slot); + } + + if (update || disable) { + ibmphp_update_slot_info (pslot); + } + + debug ("%s - Exit rc[%d] disable[%x] update[%x]\n", __FUNCTION__, rc, disable, update); + + return rc; +} + +/*---------------------------------------------------------------------- +* Name: process_changeinlatch +* +* Action: compare old and new latch reg status, process the change +* +* Input: old and current latch register status +* +* Return 0 or error codes +* Value: +*---------------------------------------------------------------------*/ +static int process_changeinlatch (u8 old, u8 new) +{ + struct slot myslot, *pslot; + u8 i; + u8 mask; + int rc = 0; + + debug ("%s - Entry old[%x], new[%x]\n", __FUNCTION__, old, new); + // bit 0 reserved, 0 is LSB, check bit 1-6 for 6 slots + + for (i = 1; i <= 6; i++) { + mask = 0x01 << i; + if ((mask & old) != (mask & new)) { + pslot = ibmphp_get_slot_from_physical_num (i); + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL); + debug ("%s - call process_changeinstatus for slot[%d]\n", __FUNCTION__, i); + process_changeinstatus (pslot, &myslot); + } else { + rc = -EINVAL; + err ("%s - Error bad pointer for slot[%d]\n", __FUNCTION__, i); + } + } + } + debug ("%s - Exit rc[%d]\n", __FUNCTION__, rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: hpc_poll_thread +* +* Action: polling +* +* Return 0 +* Value: +*---------------------------------------------------------------------*/ +static int hpc_poll_thread (void *data) +{ + debug ("%s - Entry\n", __FUNCTION__); + lock_kernel (); + daemonize (); + reparent_to_init (); + + // New name + strcpy (current->comm, "hpc_poll"); + + unlock_kernel (); + + poll_hpc (); + + tid_poll = 0; + debug ("%s - Exit\n", __FUNCTION__); + return 0; +} + + +/*---------------------------------------------------------------------- +* Name: ibmphp_hpc_start_poll_thread +* +* Action: start polling thread +*---------------------------------------------------------------------*/ +int ibmphp_hpc_start_poll_thread (void) +{ + int rc = 0; + + debug ("ibmphp_hpc_start_poll_thread - Entry\n"); + + tid_poll = kernel_thread (hpc_poll_thread, 0, 0); + if (tid_poll < 0) { + err ("ibmphp_hpc_start_poll_thread - Error, thread not started\n"); + rc = -1; + } + + debug ("ibmphp_hpc_start_poll_thread - Exit tid_poll[%d] rc[%d]\n", tid_poll, rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: ibmphp_hpc_stop_poll_thread +* +* Action: stop polling thread and cleanup +*---------------------------------------------------------------------*/ +void ibmphp_hpc_stop_poll_thread (void) +{ + debug ("ibmphp_hpc_stop_poll_thread - Entry\n"); + + ibmphp_shutdown = TRUE; + ibmphp_lock_operations (); + + // wait for poll thread to exit + down (&sem_exit); + + // cleanup + free_hpc_access (); + ibmphp_unlock_operations (); + up (&sem_poll); + up (&sem_exit); + + debug ("ibmphp_hpc_stop_poll_thread - Exit\n"); +} + +/*---------------------------------------------------------------------- +* Name: hpc_wait_ctlr_notworking +* +* Action: wait until the controller is in a not working state +* +* Return 0, HPC_ERROR +* Value: +*---------------------------------------------------------------------*/ +static int hpc_wait_ctlr_notworking (int timeout, struct controller *ctlr_ptr, void *wpg_bbar, + u8 * pstatus) +{ + int rc = 0; + u8 done = FALSE; + + debug_polling ("hpc_wait_ctlr_notworking - Entry timeout[%d]\n", timeout); + + while (!done) { + *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, WPG_CTLR_INDEX); + if (*pstatus == HPC_ERROR) { + rc = HPC_ERROR; + done = TRUE; + } + if (CTLR_WORKING (*pstatus) == HPC_CTLR_WORKING_NO) + done = TRUE; + if (!done) { + long_delay (1 * HZ); + if (timeout < 1) { + done = TRUE; + err ("HPCreadslot - Error ctlr timeout\n"); + rc = HPC_ERROR; + } else + timeout--; + } + } + debug_polling ("hpc_wait_ctlr_notworking - Exit rc[%x] status[%x]\n", rc, *pstatus); + return rc; +} diff -Nru a/drivers/hotplug/ibmphp_pci.c b/drivers/hotplug/ibmphp_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/ibmphp_pci.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,1719 @@ +/* + * IBM Hot Plug Controller Driver + * + * Written By: Irene Zubarev, IBM Corporation + * + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2002 IBM Corp. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include "ibmphp.h" + + +static int configure_device(struct pci_func *); +static int configure_bridge(struct pci_func **, u8); +static struct res_needed *scan_behind_bridge(struct pci_func *, u8); +static int add_new_bus (struct bus_node *, struct resource_node *, struct resource_node *, struct resource_node *, u8); +static u8 find_sec_number (u8 primary_busno, u8 slotno); + +/* + * NOTE..... If BIOS doesn't provide default routing, we assign: + * 9 for SCSI, 10 for LAN adapters, and 11 for everything else. + * If adapter is bridged, then we assign 11 to it and devices behind it. + * We also assign the same irq numbers for multi function devices. + * These are PIC mode, so shouldn't matter n.e.ways (hopefully) + */ +static void assign_alt_irq (struct pci_func * cur_func, u8 class_code) +{ + int j = 0; + for (j = 0; j < 4; j++) { + if (cur_func->irq[j] == 0xff) { + switch (class_code) { + case PCI_BASE_CLASS_STORAGE: + cur_func->irq[j] = SCSI_IRQ; + break; + case PCI_BASE_CLASS_NETWORK: + cur_func->irq[j] = LAN_IRQ; + break; + default: + cur_func->irq[j] = OTHER_IRQ; + break; + } + } + } +} + +/* + * Configures the device to be added (will allocate needed resources if it + * can), the device can be a bridge or a regular pci device, can also be + * multi-functional + * + * Input: function to be added + * + * TO DO: The error case with Multifunction device or multi function bridge, + * if there is an error, will need to go through all previous functions and + * unconfigure....or can add some code into unconfigure_card.... + */ +int ibmphp_configure_card (struct pci_func *func, u8 slotno) +{ + u16 vendor_id; + u32 class; + u8 class_code; + u8 hdr_type, device, sec_number; + u8 function; + struct pci_func *newfunc; /* for multi devices */ + struct pci_func *cur_func, *prev_func; + int rc, i, j; + int cleanup_count; + u8 flag; + u8 valid_device = 0x00; /* to see if we are able to read from card any device info at all */ + + debug ("inside configure_card, func->busno = %x \n", func->busno); + + device = func->device; + cur_func = func; + + /* We only get bus and device from IRQ routing table. So at this point, + * func->busno is correct, and func->device contains only device (at the 5 + * highest bits) + */ + + /* For every function on the card */ + for (function = 0x00; function < 0x08; function++) { + cur_func->function = function; + + debug ("inside the loop, cur_func->busno = %x, cur_func->device = %x, cur_func->funcion = %x\n", cur_func->busno, device, function); + + pci_read_config_word_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_VENDOR_ID, &vendor_id); + + debug ("vendor_id is %x\n", vendor_id); + if (vendor_id != PCI_VENDOR_ID_NOTVALID) { + /* found correct device!!! */ + debug ("found valid device, vendor_id = %x\n", vendor_id); + + ++valid_device; + + /* header: x x x x x x x x + * | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge + * |_=> 0 = single function device, 1 = multi-function device + */ + + pci_read_config_byte_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_HEADER_TYPE, &hdr_type); + pci_read_config_dword_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_CLASS_REVISION, &class); + + class_code = class >> 24; + debug ("hrd_type = %x, class = %x, class_code %x \n", hdr_type, class, class_code); + class >>= 8; /* to take revision out, class = class.subclass.prog i/f */ + if (class == PCI_CLASS_NOT_DEFINED_VGA) { + err ("The device %x is VGA compatible and as is not supported for hot plugging. " + "Please choose another device.\n", cur_func->device); + return -ENODEV; + } else if (class == PCI_CLASS_DISPLAY_VGA) { + err ("The device %x is not supported for hot plugging. " + "Please choose another device.\n", cur_func->device); + return -ENODEV; + } + switch (hdr_type) { + case PCI_HEADER_TYPE_NORMAL: + debug ("single device case.... vendor id = %x, hdr_type = %x, class = %x\n", vendor_id, hdr_type, class); + assign_alt_irq (cur_func, class_code); + if ((rc = configure_device (cur_func)) < 0) { + /* We need to do this in case some other BARs were properly inserted */ + err ("was not able to configure devfunc %x on bus %x. \n", + cur_func->device, cur_func->busno); + cleanup_count = 6; + goto error; + } + cur_func->next = NULL; + function = 0x8; + break; + case PCI_HEADER_TYPE_MULTIDEVICE: + assign_alt_irq (cur_func, class_code); + if ((rc = configure_device (cur_func)) < 0) { + /* We need to do this in case some other BARs were properly inserted */ + err ("was not able to configure devfunc %x on bus %x...bailing out\n", + cur_func->device, cur_func->busno); + cleanup_count = 6; + goto error; + } + newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); + if (!newfunc) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (newfunc, 0, sizeof (struct pci_func)); + newfunc->busno = cur_func->busno; + newfunc->device = device; + cur_func->next = newfunc; + cur_func = newfunc; + for (j = 0; j < 4; j++) + newfunc->irq[j] = cur_func->irq[j]; + break; + case PCI_HEADER_TYPE_MULTIBRIDGE: + class >>= 8; + if (class != PCI_CLASS_BRIDGE_PCI) { + err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. " + "Please insert another card.\n", cur_func->device); + return -ENODEV; + } + assign_alt_irq (cur_func, class_code); + rc = configure_bridge (&cur_func, slotno); + if (rc == -ENODEV) { + err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n"); + err ("Bus %x, devfunc %x \n", cur_func->busno, cur_func->device); + return rc; + } + if (rc) { + /* We need to do this in case some other BARs were properly inserted */ + err ("was not able to hot-add PPB properly.\n"); + func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ + cleanup_count = 2; + goto error; + } + + pci_read_config_byte_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_SECONDARY_BUS, &sec_number); + flag = FALSE; + for (i = 0; i < 32; i++) { + if (func->devices[i]) { + newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); + if (!newfunc) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (newfunc, 0, sizeof (struct pci_func)); + newfunc->busno = sec_number; + newfunc->device = (u8) i; + for (j = 0; j < 4; j++) + newfunc->irq[j] = cur_func->irq[j]; + + if (flag) { + for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; + prev_func->next = newfunc; + } else + cur_func->next = newfunc; + + rc = ibmphp_configure_card (newfunc, slotno); + /* This could only happen if kmalloc failed */ + if (rc) { + /* We need to do this in case bridge itself got configured properly, but devices behind it failed */ + func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ + cleanup_count = 2; + goto error; + } + flag = TRUE; + } + } + + newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); + if (!newfunc) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (newfunc, 0, sizeof (struct pci_func)); + newfunc->busno = cur_func->busno; + newfunc->device = device; + for (j = 0; j < 4; j++) + newfunc->irq[j] = cur_func->irq[j]; + for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; + prev_func->next = newfunc; + cur_func = newfunc; + break; + case PCI_HEADER_TYPE_BRIDGE: + class >>= 8; + debug ("class now is %x\n", class); + if (class != PCI_CLASS_BRIDGE_PCI) { + err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. " + "Please insert another card.\n", cur_func->device); + return -ENODEV; + } + + assign_alt_irq (cur_func, class_code); + + debug ("cur_func->busno b4 configure_bridge is %x\n", cur_func->busno); + rc = configure_bridge (&cur_func, slotno); + if (rc == -ENODEV) { + err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n"); + err ("Bus %x, devfunc %x \n", cur_func->busno, cur_func->device); + return rc; + } + if (rc) { + /* We need to do this in case some other BARs were properly inserted */ + func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ + err ("was not able to hot-add PPB properly.\n"); + cleanup_count = 2; + goto error; + } + debug ("cur_func->busno = %x, device = %x, function = %x\n", cur_func->busno, device, function); + pci_read_config_byte_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_SECONDARY_BUS, &sec_number); + debug ("after configuring bridge..., sec_number = %x\n", sec_number); + flag = FALSE; + for (i = 0; i < 32; i++) { + if (func->devices[i]) { + debug ("inside for loop, device is %x\n", i); + newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); + if (!newfunc) { + err (" out of system memory \n"); + return -ENOMEM; + } + memset (newfunc, 0, sizeof (struct pci_func)); + newfunc->busno = sec_number; + newfunc->device = (u8) i; + for (j = 0; j < 4; j++) + newfunc->irq[j] = cur_func->irq[j]; + + if (flag) { + for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; + prev_func->next = newfunc; + } else + cur_func->next = newfunc; + + rc = ibmphp_configure_card (newfunc, slotno); + + /* Again, this case should not happen... For complete paranoia, will need to call remove_bus */ + if (rc) { + /* We need to do this in case some other BARs were properly inserted */ + func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ + cleanup_count = 2; + goto error; + } + flag = TRUE; + } + } + + function = 0x8; + break; + default: + err ("MAJOR PROBLEM!!!!, header type not supported? %x\n", hdr_type); + return -ENXIO; + break; + } /* end of switch */ + } /* end of valid device */ + } /* end of for */ + + if (!valid_device) { + err ("Cannot find any valid devices on the card. Or unable to read from card.\n"); + return -ENODEV; + } + + return 0; + +error: + for (i = 0; i < cleanup_count; i++) { + if (cur_func->io[i]) { + ibmphp_remove_resource (cur_func->io[i]); + cur_func->io[i] = NULL; + } else if (cur_func->pfmem[i]) { + ibmphp_remove_resource (cur_func->pfmem[i]); + cur_func->pfmem[i] = NULL; + } else if (cur_func->mem[i]) { + ibmphp_remove_resource (cur_func->mem[i]); + cur_func->mem[i] = NULL; + } + } + return rc; +} + +/* + * This function configures the pci BARs of a single device. + * Input: pointer to the pci_func + * Output: configured PCI, 0, or error + */ +static int configure_device (struct pci_func *func) +{ + u32 bar[6]; + u32 address[] = { + PCI_BASE_ADDRESS_0, + PCI_BASE_ADDRESS_1, + PCI_BASE_ADDRESS_2, + PCI_BASE_ADDRESS_3, + PCI_BASE_ADDRESS_4, + PCI_BASE_ADDRESS_5, + 0 + }; + u8 irq; + int count; + int len[6]; + struct resource_node *io[6]; + struct resource_node *mem[6]; + struct resource_node *mem_tmp; + struct resource_node *pfmem[6]; + u8 device; + u8 function; + + debug ("%s - inside\n", __FUNCTION__); + + device = func->device; + function = func->function; + + for (count = 0; address[count]; count++) { /* for 6 BARs */ + + /* not sure if i need this. per scott, said maybe need smth like this + if devices don't adhere 100% to the spec, so don't want to write + to the reserved bits + + pcibios_read_config_byte(cur_func->busno, cur_func->device, + PCI_BASE_ADDRESS_0 + 4 * count, &tmp); + if (tmp & 0x01) // IO + pcibios_write_config_dword(cur_func->busno, cur_func->device, + PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFD); + else // Memory + pcibios_write_config_dword(cur_func->busno, cur_func->device, + PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFF); + */ + pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0xFFFFFFFF); + pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]); + + if (!bar[count]) /* This BAR is not implemented */ + continue; + + debug ("Device %x BAR %d wants %x\n", func->device, count, bar[count]); + + if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) { + /* This is IO */ + debug ("inside IO SPACE\n"); + + len[count] = bar[count] & 0xFFFFFFFC; + len[count] = ~len[count] + 1; + + debug ("len[count] in IO %x, count %d\n", len[count], count); + + io[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + + if (!io[count]) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (io[count], 0, sizeof (struct resource_node)); + io[count]->type = IO; + io[count]->busno = func->busno; + io[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); + io[count]->len = len[count]; + if (ibmphp_check_resource(io[count], 0) == 0) { + ibmphp_add_resource (io[count]); + func->io[count] = io[count]; + } else { + err ("cannot allocate requested io for bus %x device %x function %x len %x\n", + func->busno, func->device, func->function, len[count]); + kfree (io[count]); + return -EIO; + } + pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->io[count]->start); + + /* _______________This is for debugging purposes only_____________________ */ + debug ("b4 writing, the IO address is %x\n", func->io[count]->start); + pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]); + debug ("after writing.... the start address is %x\n", bar[count]); + /* _________________________________________________________________________*/ + + } else { + /* This is Memory */ + if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) { + /* pfmem */ + debug ("PFMEM SPACE\n"); + + len[count] = bar[count] & 0xFFFFFFF0; + len[count] = ~len[count] + 1; + + debug ("len[count] in PFMEM %x, count %d\n", len[count], count); + + pfmem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!pfmem[count]) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (pfmem[count], 0, sizeof (struct resource_node)); + pfmem[count]->type = PFMEM; + pfmem[count]->busno = func->busno; + pfmem[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); + pfmem[count]->len = len[count]; + pfmem[count]->fromMem = FALSE; + if (ibmphp_check_resource (pfmem[count], 0) == 0) { + ibmphp_add_resource (pfmem[count]); + func->pfmem[count] = pfmem[count]; + } else { + mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!mem_tmp) { + err ("out of system memory \n"); + kfree (pfmem[count]); + return -ENOMEM; + } + memset (mem_tmp, 0, sizeof (struct resource_node)); + mem_tmp->type = MEM; + mem_tmp->busno = pfmem[count]->busno; + mem_tmp->devfunc = pfmem[count]->devfunc; + mem_tmp->len = pfmem[count]->len; + debug ("there's no pfmem... going into mem.\n"); + if (ibmphp_check_resource (mem_tmp, 0) == 0) { + ibmphp_add_resource (mem_tmp); + pfmem[count]->fromMem = TRUE; + pfmem[count]->rangeno = mem_tmp->rangeno; + pfmem[count]->start = mem_tmp->start; + pfmem[count]->end = mem_tmp->end; + ibmphp_add_pfmem_from_mem (pfmem[count]); + func->pfmem[count] = pfmem[count]; + } else { + err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n", + func->busno, func->device, len[count]); + kfree (mem_tmp); + kfree (pfmem[count]); + return -EIO; + } + } + + pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->pfmem[count]->start); + + /*_______________This if for debugging purposes only______________________________*/ + debug ("b4 writing, start addres is %x\n", func->pfmem[count]->start); + pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]); + debug ("after writing, start address is %x\n", bar[count]); + /*_________________________________________________________________________________*/ + + if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */ + debug ("inside the mem 64 case, count %d\n", count); + count += 1; + /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ + pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0x00000000); + } + } else { + /* regular memory */ + debug ("REGULAR MEM SPACE\n"); + + len[count] = bar[count] & 0xFFFFFFF0; + len[count] = ~len[count] + 1; + + debug ("len[count] in Mem %x, count %d\n", len[count], count); + + mem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!mem[count]) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (mem[count], 0, sizeof (struct resource_node)); + mem[count]->type = MEM; + mem[count]->busno = func->busno; + mem[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); + mem[count]->len = len[count]; + if (ibmphp_check_resource (mem[count], 0) == 0) { + ibmphp_add_resource (mem[count]); + func->mem[count] = mem[count]; + } else { + err ("cannot allocate requested mem for bus %x, device %x, len %x\n", + func->busno, func->device, len[count]); + kfree (mem[count]); + return -EIO; + } + pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->mem[count]->start); + /* _______________________This is for debugging purposes only _______________________*/ + debug ("b4 writing, start address is %x\n", func->mem[count]->start); + pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]); + debug ("after writing, the address is %x\n", bar[count]); + /* __________________________________________________________________________________*/ + + if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + debug ("inside mem 64 case, reg. mem, count %d\n", count); + count += 1; + /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ + pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0x00000000); + } + } + } /* end of mem */ + } /* end of for */ + + func->bus = 0; /* To indicate that this is not a PPB */ + pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_INTERRUPT_PIN, &irq); + if ((irq > 0x00) && (irq < 0x05)) + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_INTERRUPT_LINE, func->irq[irq - 1]); + + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_CACHE_LINE_SIZE, CACHE); + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_LATENCY_TIMER, LATENCY); + + pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_ROM_ADDRESS, 0x00L); + pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_COMMAND, DEVICEENABLE); + + return 0; +} + +/****************************************************************************** + * This routine configures a PCI-2-PCI bridge and the functions behind it + * Parameters: pci_func + * Returns: + ******************************************************************************/ +static int configure_bridge (struct pci_func **func_passed, u8 slotno) +{ + int count; + int i; + int rc; + u8 sec_number; + u8 io_base; + u16 pfmem_base; + u32 bar[2]; + u32 len[2]; + u8 flag_io = FALSE; + u8 flag_mem = FALSE; + u8 flag_pfmem = FALSE; + u8 need_io_upper = FALSE; + u8 need_pfmem_upper = FALSE; + struct res_needed *amount_needed = NULL; + struct resource_node *io = NULL; + struct resource_node *bus_io[2] = {NULL, NULL}; + struct resource_node *mem = NULL; + struct resource_node *bus_mem[2] = {NULL, NULL}; + struct resource_node *mem_tmp = NULL; + struct resource_node *pfmem = NULL; + struct resource_node *bus_pfmem[2] = {NULL, NULL}; + struct bus_node *bus; + u32 address[] = { + PCI_BASE_ADDRESS_0, + PCI_BASE_ADDRESS_1, + 0 + }; + struct pci_func *func = *func_passed; + u8 function; + u8 device; + u8 irq; + int retval; + + debug ("%s - enter\n", __FUNCTION__); + + function = func->function; + device = func->device; + + /* Configuring necessary info for the bridge so that we could see the devices + * behind it + */ + + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PRIMARY_BUS, func->busno); + + /* _____________________For debugging purposes only __________________________ + pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PRIMARY_BUS, &pri_number); + debug ("primary # written into the bridge is %x\n", pri_number); + ___________________________________________________________________________*/ + + /* in EBDA, only get allocated 1 additional bus # per slot */ + sec_number = find_sec_number (func->busno, slotno); + if (sec_number == 0xff) { + err ("cannot allocate secondary bus number for the bridged device \n"); + return -EINVAL; + } + + debug ("after find_sec_number, the number we got is %x\n", sec_number); + debug ("AFTER FIND_SEC_NUMBER, func->busno IS %x\n", func->busno); + + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SECONDARY_BUS, sec_number); + + /* __________________For debugging purposes only __________________________________ + pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SECONDARY_BUS, &sec_number); + debug ("sec_number after write/read is %x\n", sec_number); + ________________________________________________________________________________*/ + + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SUBORDINATE_BUS, sec_number); + + /* __________________For debugging purposes only ____________________________________ + pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SUBORDINATE_BUS, &sec_number); + debug ("subordinate number after write/read is %x\n", sec_number); + __________________________________________________________________________________*/ + + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_CACHE_LINE_SIZE, CACHE); + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_LATENCY_TIMER, LATENCY); + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SEC_LATENCY_TIMER, LATENCY); + + debug ("func->busno is %x\n", func->busno); + debug ("sec_number after writing is %x\n", sec_number); + + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !!!!!!!!!!!!!!!NEED TO ADD!!! FAST BACK-TO-BACK ENABLE!!!!!!!!!!!!!!!!!!!! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + + + /* First we need to allocate mem/io for the bridge itself in case it needs it */ + for (count = 0; address[count]; count++) { /* for 2 BARs */ + pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0xFFFFFFFF); + pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]); + + if (!bar[count]) { + /* This BAR is not implemented */ + debug ("so we come here then, eh?, count = %d\n", count); + continue; + } + // tmp_bar = bar[count]; + + debug ("Bar %d wants %x\n", count, bar[count]); + + if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) { + /* This is IO */ + len[count] = bar[count] & 0xFFFFFFFC; + len[count] = ~len[count] + 1; + + debug ("len[count] in IO = %x\n", len[count]); + + bus_io[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + + if (!bus_io[count]) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (bus_io[count], 0, sizeof (struct resource_node)); + bus_io[count]->type = IO; + bus_io[count]->busno = func->busno; + bus_io[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); + bus_io[count]->len = len[count]; + if (ibmphp_check_resource (bus_io[count], 0) == 0) { + ibmphp_add_resource (bus_io[count]); + func->io[count] = bus_io[count]; + } else { + err ("cannot allocate requested io for bus %x, device %x, len %x\n", + func->busno, func->device, len[count]); + kfree (bus_io[count]); + return -EIO; + } + + pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->io[count]->start); + + } else { + /* This is Memory */ + if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) { + /* pfmem */ + len[count] = bar[count] & 0xFFFFFFF0; + len[count] = ~len[count] + 1; + + debug ("len[count] in PFMEM = %x\n", len[count]); + + bus_pfmem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!bus_pfmem[count]) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (bus_pfmem[count], 0, sizeof (struct resource_node)); + bus_pfmem[count]->type = PFMEM; + bus_pfmem[count]->busno = func->busno; + bus_pfmem[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); + bus_pfmem[count]->len = len[count]; + bus_pfmem[count]->fromMem = FALSE; + if (ibmphp_check_resource (bus_pfmem[count], 0) == 0) { + ibmphp_add_resource (bus_pfmem[count]); + func->pfmem[count] = bus_pfmem[count]; + } else { + mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!mem_tmp) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (mem_tmp, 0, sizeof (struct resource_node)); + mem_tmp->type = MEM; + mem_tmp->busno = bus_pfmem[count]->busno; + mem_tmp->devfunc = bus_pfmem[count]->devfunc; + mem_tmp->len = bus_pfmem[count]->len; + if (ibmphp_check_resource (mem_tmp, 0) == 0) { + ibmphp_add_resource (mem_tmp); + bus_pfmem[count]->fromMem = TRUE; + bus_pfmem[count]->rangeno = mem_tmp->rangeno; + ibmphp_add_pfmem_from_mem (bus_pfmem[count]); + func->pfmem[count] = bus_pfmem[count]; + } else { + err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n", + func->busno, func->device, len[count]); + kfree (mem_tmp); + kfree (bus_pfmem[count]); + return -EIO; + } + } + + pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->pfmem[count]->start); + + if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + count += 1; + /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ + pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0x00000000); + + } + } else { + /* regular memory */ + len[count] = bar[count] & 0xFFFFFFF0; + len[count] = ~len[count] + 1; + + debug ("len[count] in Memory is %x\n", len[count]); + + bus_mem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!bus_mem[count]) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (bus_mem[count], 0, sizeof (struct resource_node)); + bus_mem[count]->type = MEM; + bus_mem[count]->busno = func->busno; + bus_mem[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); + bus_mem[count]->len = len[count]; + if (ibmphp_check_resource (bus_mem[count], 0) == 0) { + ibmphp_add_resource (bus_mem[count]); + func->mem[count] = bus_mem[count]; + } else { + err ("cannot allocate requested mem for bus %x, device %x, len %x\n", + func->busno, func->device, len[count]); + kfree (bus_mem[count]); + return -EIO; + } + + pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->mem[count]->start); + + if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + count += 1; + /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ + pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0x00000000); + + } + } + } /* end of mem */ + } /* end of for */ + + /* Now need to see how much space the devices behind the bridge needed */ + amount_needed = scan_behind_bridge (func, sec_number); + if (amount_needed == NULL) + return -ENOMEM; + + debug ("after coming back from scan_behind_bridge\n"); + debug ("amount_needed->not_correct = %x\n", amount_needed->not_correct); + debug ("amount_needed->io = %x\n", amount_needed->io); + debug ("amount_needed->mem = %x\n", amount_needed->mem); + debug ("amount_needed->pfmem = %x\n", amount_needed->pfmem); + + if (amount_needed->not_correct) { + debug ("amount_needed is not correct \n"); + for (count = 0; address[count]; count++) { + /* for 2 BARs */ + if (bus_io[count]) { + ibmphp_remove_resource (bus_io[count]); + func->io[count] = NULL; + } else if (bus_pfmem[count]) { + ibmphp_remove_resource (bus_pfmem[count]); + func->pfmem[count] = NULL; + } else if (bus_mem[count]) { + ibmphp_remove_resource (bus_mem[count]); + func->mem[count] = NULL; + } + } + kfree (amount_needed); + return -ENODEV; + } + + if (!amount_needed->io) { + debug ("it doesn't want IO?\n"); + flag_io = TRUE; + } else { + debug ("it wants %x IO behind the bridge \n", amount_needed->io); + io = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + + if (!io) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (io, 0, sizeof (struct resource_node)); + io->type = IO; + io->busno = func->busno; + io->devfunc = ((func->device << 3) | (func->function & 0x7)); + io->len = amount_needed->io; + if (ibmphp_check_resource (io, 1) == 0) { + debug ("were we able to add io\n"); + ibmphp_add_resource (io); + flag_io = TRUE; + } + } + + if (!amount_needed->mem) { + debug ("it doesn't want n.e.memory?\n"); + flag_mem = TRUE; + } else { + debug ("it wants %x memory behind the bridge\n", amount_needed->mem); + mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!mem) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (mem, 0, sizeof (struct resource_node)); + mem->type = MEM; + mem->busno = func->busno; + mem->devfunc = ((func->device << 3) | (func->function & 0x7)); + mem->len = amount_needed->mem; + if (ibmphp_check_resource (mem, 1) == 0) { + ibmphp_add_resource (mem); + flag_mem = TRUE; + debug ("were we able to add mem\n"); + } + } + + if (!amount_needed->pfmem) { + debug ("it doesn't want n.e.pfmem mem?\n"); + flag_pfmem = TRUE; + } else { + debug ("it wants %x pfmemory behind the bridge\n", amount_needed->pfmem); + pfmem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!pfmem) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (pfmem, 0, sizeof (struct resource_node)); + pfmem->type = PFMEM; + pfmem->busno = func->busno; + pfmem->devfunc = ((func->device << 3) | (func->function & 0x7)); + pfmem->len = amount_needed->pfmem; + pfmem->fromMem = FALSE; + if (ibmphp_check_resource (pfmem, 1) == 0) { + ibmphp_add_resource (pfmem); + flag_pfmem = TRUE; + } else { + mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!mem_tmp) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (mem_tmp, 0, sizeof (struct resource_node)); + mem_tmp->type = MEM; + mem_tmp->busno = pfmem->busno; + mem_tmp->devfunc = pfmem->devfunc; + mem_tmp->len = pfmem->len; + if (ibmphp_check_resource (mem_tmp, 1) == 0) { + ibmphp_add_resource (mem_tmp); + pfmem->fromMem = TRUE; + pfmem->rangeno = mem_tmp->rangeno; + ibmphp_add_pfmem_from_mem (pfmem); + flag_pfmem = TRUE; + } + } + } + + debug ("b4 if (flag_io && flag_mem && flag_pfmem)\n"); + debug ("flag_io = %x, flag_mem = %x, flag_pfmem = %x\n", flag_io, flag_mem, flag_pfmem); + + if (flag_io && flag_mem && flag_pfmem) { + bus = kmalloc (sizeof (struct bus_node), GFP_KERNEL); + if (!bus) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (bus, 0, sizeof (struct bus_node)); + bus->busno = sec_number; + debug ("b4 adding new bus\n"); + rc = add_new_bus (bus, io, mem, pfmem, func->busno); + if (rc) { + if (rc == -ENOMEM) { + ibmphp_remove_bus (bus, func->busno); + return rc; + } + retval = rc; + goto error; + } + pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE, &io_base); + pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_BASE, &pfmem_base); + + if ((io_base & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { + debug ("io 32\n"); + need_io_upper = TRUE; + } + if ((io_base & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { + debug ("pfmem 64\n"); + need_pfmem_upper = TRUE; + } + + if (bus->noIORanges) { + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE, 0x00 | bus->rangeIO->start >> 8); + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_LIMIT, 0x00 | bus->rangeIO->end >> 8); + + /* _______________This is for debugging purposes only ____________________ + pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE, &temp); + debug ("io_base = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8); + pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_LIMIT, &temp); + debug ("io_limit = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8); + ________________________________________________________________________*/ + + if (need_io_upper) { /* since can't support n.e.ways */ + pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE_UPPER16, 0x0000); + pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_LIMIT_UPPER16, 0x0000); + } + } else { + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE, 0x00); + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_LIMIT, 0x00); + } + + if (bus->noMemRanges) { + pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_BASE, 0x0000 | bus->rangeMem->start >> 16); + pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_LIMIT, 0x0000 | bus->rangeMem->end >> 16); + + /* ____________________This is for debugging purposes only ________________________ + pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_BASE, &temp); + debug ("mem_base = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); + pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_LIMIT, &temp); + debug ("mem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); + __________________________________________________________________________________*/ + + } else { + pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_BASE, 0xffff); + pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_LIMIT, 0x0000); + } + if (bus->noPFMemRanges) { + pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_BASE, 0x0000 | bus->rangePFMem->start >> 16); + pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_LIMIT, 0x0000 | bus->rangePFMem->end >> 16); + + /* __________________________This is for debugging purposes only _______________________ + pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_BASE, &temp); + debug ("pfmem_base = %x", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); + pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_LIMIT, &temp); + debug ("pfmem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); + ______________________________________________________________________________________*/ + + if (need_pfmem_upper) { /* since can't support n.e.ways */ + pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_BASE_UPPER32, 0x00000000); + pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_LIMIT_UPPER32, 0x00000000); + } + } else { + pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_BASE, 0xffff); + pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_LIMIT, 0x0000); + } + + debug ("b4 writing control information\n"); + + pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_INTERRUPT_PIN, &irq); + if ((irq > 0x00) && (irq < 0x05)) + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_INTERRUPT_LINE, func->irq[irq - 1]); + /* + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_BRIDGE_CONTROL, ctrl); + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_PARITY); + pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_SERR); + */ + + pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_COMMAND, DEVICEENABLE); + pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_BRIDGE_CONTROL, 0x07); + for (i = 0; i < 32; i++) { + if (amount_needed->devices[i]) { + debug ("device where devices[i] is 1 = %x\n", i); + func->devices[i] = 1; + } + } + func->bus = 1; /* For unconfiguring, to indicate it's PPB */ + func_passed = &func; + debug ("func->busno b4 returning is %x\n", func->busno); + debug ("func->busno b4 returning in the other structure is %x\n", (*func_passed)->busno); + kfree (amount_needed); + return 0; + } else { + err ("Configuring bridge was unsuccessful... \n"); + mem_tmp = NULL; + retval = -EIO; + goto error; + } + +error: + if (amount_needed) + kfree (amount_needed); + if (pfmem) + ibmphp_remove_resource (pfmem); + if (io) + ibmphp_remove_resource (io); + if (mem) + ibmphp_remove_resource (mem); + for (i = 0; i < 2; i++) { /* for 2 BARs */ + if (bus_io[i]) { + ibmphp_remove_resource (bus_io[i]); + func->io[i] = NULL; + } else if (bus_pfmem[i]) { + ibmphp_remove_resource (bus_pfmem[i]); + func->pfmem[i] = NULL; + } else if (bus_mem[i]) { + ibmphp_remove_resource (bus_mem[i]); + func->mem[i] = NULL; + } + } + return retval; +} + +/***************************************************************************** + * This function adds up the amount of resources needed behind the PPB bridge + * and passes it to the configure_bridge function + * Input: bridge function + * Ouput: amount of resources needed + *****************************************************************************/ +static struct res_needed *scan_behind_bridge (struct pci_func * func, u8 busno) +{ + int count, len[6]; + u16 vendor_id; + u8 hdr_type; + u8 device, function; + int howmany = 0; /*this is to see if there are any devices behind the bridge */ + + u32 bar[6], class; + u32 address[] = { + PCI_BASE_ADDRESS_0, + PCI_BASE_ADDRESS_1, + PCI_BASE_ADDRESS_2, + PCI_BASE_ADDRESS_3, + PCI_BASE_ADDRESS_4, + PCI_BASE_ADDRESS_5, + 0 + }; + struct res_needed *amount; + + amount = kmalloc (sizeof (struct res_needed), GFP_KERNEL); + if (amount == NULL) + return NULL; + memset (amount, 0, sizeof (struct res_needed)); + + debug ("the bus_no behind the bridge is %x\n", busno); + debug ("scanning devices behind the bridge...\n"); + for (device = 0; device < 32; device++) { + amount->devices[device] = 0; + for (function = 0; function < 8; function++) { + + pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_VENDOR_ID, &vendor_id); + + if (vendor_id != PCI_VENDOR_ID_NOTVALID) { + /* found correct device!!! */ + howmany++; + + pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_HEADER_TYPE, &hdr_type); + pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_CLASS_REVISION, &class); + + debug ("hdr_type behind the bridge is %x\n", hdr_type); + if (hdr_type & PCI_HEADER_TYPE_BRIDGE) { + err ("embedded bridges not supported for hot-plugging.\n"); + amount->not_correct = TRUE; + return amount; + } + + class >>= 8; /* to take revision out, class = class.subclass.prog i/f */ + if (class == PCI_CLASS_NOT_DEFINED_VGA) { + err ("The device %x is VGA compatible and as is not supported for hot plugging. " + "Please choose another device.\n", device); + amount->not_correct = TRUE; + return amount; + } else if (class == PCI_CLASS_DISPLAY_VGA) { + err ("The device %x is not supported for hot plugging. " + "Please choose another device.\n", device); + amount->not_correct = TRUE; + return amount; + } + + amount->devices[device] = 1; + + for (count = 0; address[count]; count++) { + /* for 6 BARs */ + /* + pci_read_config_byte_nodev(ibmphp_pci_root_ops, busno, device, function, address[count], &tmp); + if (tmp & 0x01) // IO + pci_write_config_dword_nodev(ibmphp_pci_root_ops, busno, device, function, address[count], 0xFFFFFFFD); + else // MEMORY + pci_write_config_dword_nodev(ibmphp_pci_root_ops, busno, device, function, address[count], 0xFFFFFFFF); + */ + pci_write_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], 0xFFFFFFFF); + pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], &bar[count]); + + debug ("what is bar[count]? %x, count = %d\n", bar[count], count); + + if (!bar[count]) /* This BAR is not implemented */ + continue; + + //tmp_bar = bar[count]; + + debug ("count %d device %x function %x wants %x resources \n", count, device, function, bar[count]); + + if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) { + /* This is IO */ + len[count] = bar[count] & 0xFFFFFFFC; + len[count] = ~len[count] + 1; + amount->io += len[count]; + } else { + /* This is Memory */ + if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) { + /* pfmem */ + len[count] = bar[count] & 0xFFFFFFF0; + len[count] = ~len[count] + 1; + amount->pfmem += len[count]; + if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) + /* takes up another dword */ + count += 1; + + } else { + /* regular memory */ + len[count] = bar[count] & 0xFFFFFFF0; + len[count] = ~len[count] + 1; + amount->mem += len[count]; + if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + count += 1; + } + } + } + } /* end for */ + } /* end if (valid) */ + } /* end for */ + } /* end for */ + + if (!howmany) + amount->not_correct = TRUE; + else + amount->not_correct = FALSE; + if ((amount->io) && (amount->io < IOBRIDGE)) + amount->io = IOBRIDGE; + if ((amount->mem) && (amount->mem < MEMBRIDGE)) + amount->mem = MEMBRIDGE; + if ((amount->pfmem) && (amount->pfmem < MEMBRIDGE)) + amount->pfmem = MEMBRIDGE; + return amount; +} + +/* The following 3 unconfigure_boot_ routines deal with the case when we had the card + * upon bootup in the system, since we don't allocate func to such case, we need to read + * the start addresses from pci config space and then find the corresponding entries in + * our resource lists. The functions return either 0, -ENODEV, or -1 (general failure) + * Change: we also call these functions even if we configured the card ourselves (i.e., not + * the bootup case), since it should work same way + */ +static int unconfigure_boot_device (u8 busno, u8 device, u8 function) +{ + u32 start_address; + u32 address[] = { + PCI_BASE_ADDRESS_0, + PCI_BASE_ADDRESS_1, + PCI_BASE_ADDRESS_2, + PCI_BASE_ADDRESS_3, + PCI_BASE_ADDRESS_4, + PCI_BASE_ADDRESS_5, + 0 + }; + int count; + struct resource_node *io; + struct resource_node *mem; + struct resource_node *pfmem; + struct bus_node *bus; + u32 end_address; + u32 temp_end; + u32 size; + u32 tmp_address; + + debug ("%s - enter\n", __FUNCTION__); + + bus = ibmphp_find_res_bus (busno); + if (!bus) { + debug ("cannot find corresponding bus.\n"); + return -EINVAL; + } + + for (count = 0; address[count]; count++) { /* for 6 BARs */ + pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], &start_address); + + /* We can do this here, b/c by that time the device driver of the card has been stopped */ + + pci_write_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], 0xFFFFFFFF); + pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], &size); + pci_write_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], start_address); + + debug ("start_address is %x\n", start_address); + debug ("busno, device, function %x %x %x\n", busno, device, function); + if (!size) { + /* This BAR is not implemented */ + debug ("is this bar no implemented?, count = %d\n", count); + continue; + } + tmp_address = start_address; + if (start_address & PCI_BASE_ADDRESS_SPACE_IO) { + /* This is IO */ + start_address &= PCI_BASE_ADDRESS_IO_MASK; + size = size & 0xFFFFFFFC; + size = ~size + 1; + end_address = start_address + size - 1; + if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) { + err ("cannot find corresponding IO resource to remove\n"); + return -EIO; + } + debug ("io->start = %x\n", io->start); + temp_end = io->end; + start_address = io->end + 1; + ibmphp_remove_resource (io); + /* This is needed b/c of the old I/O restrictions in the BIOS */ + while (temp_end < end_address) { + if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) { + err ("cannot find corresponding IO resource to remove\n"); + return -EIO; + } + debug ("io->start = %x\n", io->start); + temp_end = io->end; + start_address = io->end + 1; + ibmphp_remove_resource (io); + } + + /* ????????? DO WE NEED TO WRITE ANYTHING INTO THE PCI CONFIG SPACE BACK ?????????? */ + } else { + /* This is Memory */ + if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) { + /* pfmem */ + start_address &= PCI_BASE_ADDRESS_MEM_MASK; + debug ("start address of pfmem is %x\n", start_address); + + if (ibmphp_find_resource (bus, start_address, &pfmem, PFMEM) < 0) { + err ("cannot find corresponding PFMEM resource to remove\n"); + return -EIO; + } + if (pfmem) + debug ("pfmem->start = %x\n", pfmem->start); + + ibmphp_remove_resource (pfmem); + + if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + count += 1; + } + + } else { + /* regular memory */ + start_address &= PCI_BASE_ADDRESS_MEM_MASK; + debug ("start address of mem is %x\n", start_address); + if (ibmphp_find_resource (bus, start_address, &mem, MEM) < 0) { + err ("cannot find corresponding MEM resource to remove\n"); + return -EIO; + } + if (mem) + debug ("mem->start = %x\n", mem->start); + + ibmphp_remove_resource (mem); + + if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + count += 1; + } + } + } /* end of mem */ + } /* end of for */ + + return 0; +} + +static int unconfigure_boot_bridge (u8 busno, u8 device, u8 function) +{ + int count; + int bus_no, pri_no, sub_no, sec_no = 0; + u32 start_address, tmp_address; + u8 sec_number, sub_number, pri_number; + struct resource_node *io = NULL; + struct resource_node *mem = NULL; + struct resource_node *pfmem = NULL; + struct bus_node *bus; + u32 address[] = { + PCI_BASE_ADDRESS_0, + PCI_BASE_ADDRESS_1, + 0 + }; + + bus_no = (int) busno; + debug ("busno is %x\n", busno); + pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PRIMARY_BUS, &pri_number); + debug ("%s - busno = %x, primary_number = %x\n", __FUNCTION__, busno, pri_number); + + pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_SECONDARY_BUS, &sec_number); + debug ("sec_number is %x\n", sec_number); + sec_no = (int) sec_number; + pri_no = (int) pri_number; + if (pri_no != bus_no) { + err ("primary numbers in our structures and pci config space don't match.\n"); + return -EINVAL; + } + + pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_SECONDARY_BUS, &sec_number); + sec_no = (int) sec_no; + + pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_SUBORDINATE_BUS, &sub_number); + sub_no = (int) sub_number; + debug ("sub_no is %d, sec_no is %d\n", sub_no, sec_no); + if (sec_no != sub_number) { + err ("there're more buses behind this bridge. Hot removal is not supported. Please choose another card\n"); + return -ENODEV; + } + + bus = ibmphp_find_res_bus (sec_number); + debug ("bus->busno is %x\n", bus->busno); + debug ("sec_number is %x\n", sec_number); + if (!bus) { + err ("cannot find Bus structure for the bridged device\n"); + return -EINVAL; + } + + ibmphp_remove_bus (bus, busno); + + for (count = 0; address[count]; count++) { + /* for 2 BARs */ + pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], &start_address); + + if (!start_address) { + /* This BAR is not implemented */ + continue; + } + + tmp_address = start_address; + + if (start_address & PCI_BASE_ADDRESS_SPACE_IO) { + /* This is IO */ + start_address &= PCI_BASE_ADDRESS_IO_MASK; + if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) { + err ("cannot find corresponding IO resource to remove\n"); + return -EIO; + } + if (io) + debug ("io->start = %x\n", io->start); + + ibmphp_remove_resource (io); + + /* ????????? DO WE NEED TO WRITE ANYTHING INTO THE PCI CONFIG SPACE BACK ?????????? */ + } else { + /* This is Memory */ + if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) { + /* pfmem */ + start_address &= PCI_BASE_ADDRESS_MEM_MASK; + if (ibmphp_find_resource (bus, start_address, &pfmem, PFMEM) < 0) { + err ("cannot find corresponding PFMEM resource to remove\n"); + return -EINVAL; + } + if (pfmem) + debug ("pfmem->start = %x\n", pfmem->start); + + ibmphp_remove_resource (pfmem); + + if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + count += 1; + } + + } else { + /* regular memory */ + start_address &= PCI_BASE_ADDRESS_MEM_MASK; + if (ibmphp_find_resource (bus, start_address, &mem, MEM) < 0) { + err ("cannot find corresponding MEM resource to remove\n"); + return -EINVAL; + } + if (mem) + debug ("mem->start = %x\n", mem->start); + + ibmphp_remove_resource (mem); + + if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + count += 1; + } + } + } /* end of mem */ + } /* end of for */ + debug ("%s - exiting, returning success\n", __FUNCTION__); + return 0; +} + +static int unconfigure_boot_card (struct slot *slot_cur) +{ + u16 vendor_id; + u32 class; + u8 hdr_type; + u8 device; + u8 busno; + u8 function; + int rc; + u8 valid_device = 0x00; /* To see if we are ever able to find valid device and read it */ + + debug ("%s - enter\n", __FUNCTION__); + + device = slot_cur->device; + busno = slot_cur->bus; + + debug ("b4 for loop, device is %x\n", device); + /* For every function on the card */ + for (function = 0x0; function < 0x08; function++) { + + pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_VENDOR_ID, &vendor_id); + + if (vendor_id != PCI_VENDOR_ID_NOTVALID) { + /* found correct device!!! */ + ++valid_device; + + debug ("%s - found correct device\n", __FUNCTION__); + + /* header: x x x x x x x x + * | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge + * |_=> 0 = single function device, 1 = multi-function device + */ + + pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_HEADER_TYPE, &hdr_type); + pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_CLASS_REVISION, &class); + + debug ("hdr_type %x, class %x\n", hdr_type, class); + class >>= 8; /* to take revision out, class = class.subclass.prog i/f */ + if (class == PCI_CLASS_NOT_DEFINED_VGA) { + err ("The device %x function %x is VGA compatible and is not supported for hot removing. " + "Please choose another device.\n", device, function); + return -ENODEV; + } else if (class == PCI_CLASS_DISPLAY_VGA) { + err ("The device %x function %x is not supported for hot removing. " + "Please choose another device.\n", device, function); + return -ENODEV; + } + + switch (hdr_type) { + case PCI_HEADER_TYPE_NORMAL: + rc = unconfigure_boot_device (busno, device, function); + if (rc) { + err ("was not able to unconfigure device %x func %x on bus %x. bailing out... \n", + device, function, busno); + return rc; + } + function = 0x8; + break; + case PCI_HEADER_TYPE_MULTIDEVICE: + rc = unconfigure_boot_device (busno, device, function); + if (rc) { + err ("was not able to unconfigure device %x func %x on bus %x. bailing out... \n", + device, function, busno); + return rc; + } + break; + case PCI_HEADER_TYPE_BRIDGE: + class >>= 8; + if (class != PCI_CLASS_BRIDGE_PCI) { + err ("This device %x function %x is not PCI-to-PCI bridge, " + "and is not supported for hot-removing. " + "Please try another card.\n", device, function); + return -ENODEV; + } + rc = unconfigure_boot_bridge (busno, device, function); + if (rc != 0) { + err ("was not able to hot-remove PPB properly.\n"); + return rc; + } + + function = 0x8; + break; + case PCI_HEADER_TYPE_MULTIBRIDGE: + class >>= 8; + if (class != PCI_CLASS_BRIDGE_PCI) { + err ("This device %x function %x is not PCI-to-PCI bridge, " + "and is not supported for hot-removing. " + "Please try another card.\n", device, function); + return -ENODEV; + } + rc = unconfigure_boot_bridge (busno, device, function); + if (rc != 0) { + err ("was not able to hot-remove PPB properly.\n"); + return rc; + } + break; + default: + err ("MAJOR PROBLEM!!!! Cannot read device's header \n"); + return -1; + break; + } /* end of switch */ + } /* end of valid device */ + } /* end of for */ + + if (!valid_device) { + err ("Could not find device to unconfigure. Or could not read the card. \n"); + return -1; + } + return 0; +} + +/* + * free the resources of the card (multi, single, or bridged) + * Parameters: slot, flag to say if this is for removing entire module or just + * unconfiguring the device + * TO DO: will probably need to add some code in case there was some resource, + * to remove it... this is from when we have errors in the configure_card... + * !!!!!!!!!!!!!!!!!!!!!!!!!FOR BUSES!!!!!!!!!!!! + * Returns: 0, -1, -ENODEV + */ +int ibmphp_unconfigure_card (struct slot **slot_cur, int the_end) +{ + int i; + int count; + int rc; + struct slot *sl = *slot_cur; + struct pci_func *cur_func = NULL; + struct pci_func *temp_func; + + debug ("%s - enter\n", __FUNCTION__); + + if (!the_end) { + /* Need to unconfigure the card */ + rc = unconfigure_boot_card (sl); + if ((rc == -ENODEV) || (rc == -EIO) || (rc == -EINVAL)) { + /* In all other cases, will still need to get rid of func structure if it exists */ + return rc; + } + } + + if (sl->func) { + debug ("do we come in here? \n"); + cur_func = sl->func; + while (cur_func) { + /* TO DO: WILL MOST LIKELY NEED TO GET RID OF THE BUS STRUCTURE FROM RESOURCES AS WELL */ + if (cur_func->bus) { + /* in other words, it's a PPB */ + count = 2; + } else { + count = 6; + } + + for (i = 0; i < count; i++) { + if (cur_func->io[count]) { + debug ("io[%d] exists \n", count); + if (the_end > 0) + ibmphp_remove_resource (cur_func->io[count]); + cur_func->io[count] = NULL; + } + if (cur_func->mem[count]) { + debug ("mem[%d] exists \n", count); + if (the_end > 0) + ibmphp_remove_resource (cur_func->mem[count]); + cur_func->mem[count] = NULL; + } + if (cur_func->pfmem[count]) { + debug ("pfmem[%d] exists \n", count); + if (the_end > 0) + ibmphp_remove_resource (cur_func->pfmem[count]); + cur_func->pfmem[count] = NULL; + } + } + + temp_func = cur_func->next; + kfree (cur_func); + cur_func = temp_func; + } + } + + sl->func = NULL; + *slot_cur = sl; + return 0; +} + +/* + * add a new bus resulting from hot-plugging a PPB bridge with devices + * + * Input: bus and the amount of resources needed (we know we can assign those, + * since they've been checked already + * Output: bus added to the correct spot + * 0, -1, error + */ +static int add_new_bus (struct bus_node *bus, struct resource_node *io, struct resource_node *mem, struct resource_node *pfmem, u8 parent_busno) +{ + struct range_node *io_range = NULL; + struct range_node *mem_range = NULL; + struct range_node *pfmem_range = NULL; + struct bus_node *cur_bus = NULL; + + /* Trying to find the parent bus number */ + cur_bus = ibmphp_find_res_bus (parent_busno); + if (!cur_bus) { + err ("strange, cannot find bus which is supposed to be at the system... something is terribly wrong...\n"); + return -ENODEV; + } + + list_add (&bus->bus_list, &cur_bus->bus_list); + + if (io) { + io_range = kmalloc (sizeof (struct range_node), GFP_KERNEL); + if (!io_range) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (io_range, 0, sizeof (struct range_node)); + io_range->start = io->start; + io_range->end = io->end; + io_range->rangeno = 1; + bus->noIORanges = 1; + bus->rangeIO = io_range; + } + if (mem) { + mem_range = kmalloc (sizeof (struct range_node), GFP_KERNEL); + if (!mem_range) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (mem_range, 0, sizeof (struct range_node)); + mem_range->start = mem->start; + mem_range->end = mem->end; + mem_range->rangeno = 1; + bus->noMemRanges = 1; + bus->rangeMem = mem_range; + } + if (pfmem) { + pfmem_range = kmalloc (sizeof (struct range_node), GFP_KERNEL); + if (!pfmem_range) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (pfmem_range, 0, sizeof (struct range_node)); + pfmem_range->start = pfmem->start; + pfmem_range->end = pfmem->end; + pfmem_range->rangeno = 1; + bus->noPFMemRanges = 1; + bus->rangePFMem = pfmem_range; + } + return 0; +} + +/* + * find the 1st available bus number for PPB to set as its secondary bus + * Parameters: bus_number of the primary bus + * Returns: bus_number of the secondary bus or 0xff in case of failure + */ +static u8 find_sec_number (u8 primary_busno, u8 slotno) +{ + int min, max; + u8 busno; + struct bus_info *bus; + + bus = ibmphp_find_same_bus_num (primary_busno); + if (!bus) { + err ("cannot get slot range of the bus from the BIOS\n"); + return 0xff; + } + max = bus->slot_max; + min = bus->slot_min; + if ((slotno > max) || (slotno < min)) { + err ("got the wrong range\n"); + return 0xff; + } + busno = (u8) (slotno - (u8) min); + busno += primary_busno + 0x01; + if (!ibmphp_find_res_bus (busno)) + return busno; + return 0xff; +} + diff -Nru a/drivers/hotplug/ibmphp_res.c b/drivers/hotplug/ibmphp_res.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/ibmphp_res.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,2067 @@ +/* + * IBM Hot Plug Controller Driver + * + * Written By: Irene Zubarev, IBM Corporation + * + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2002 IBM Corp. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include "ibmphp.h" + +static int flags = 0; /* for testing */ + +static void update_resources (struct bus_node *bus_cur, int type, int rangeno); +static int once_over (void); +static int remove_ranges (struct bus_node *, struct bus_node *); +static int update_bridge_ranges (struct bus_node **); +static int add_range (int type, struct range_node *, struct bus_node *); +static void fix_resources (struct bus_node *); +static inline struct bus_node *find_bus_wprev (u8, struct bus_node **, u8); + +static LIST_HEAD(gbuses); + +static struct bus_node * alloc_error_bus (struct ebda_pci_rsrc * curr) +{ + struct bus_node * newbus; + + newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL); + if (!newbus) { + err ("out of system memory \n"); + return NULL; + } + + memset (newbus, 0, sizeof (struct bus_node)); + newbus->busno = curr->bus_num; + list_add_tail (&newbus->bus_list, &gbuses); + return newbus; +} + +static struct resource_node * alloc_resources (struct ebda_pci_rsrc * curr) +{ + struct resource_node *rs = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!rs) { + err ("out of system memory \n"); + return NULL; + } + memset (rs, 0, sizeof (struct resource_node)); + rs->busno = curr->bus_num; + rs->devfunc = curr->dev_fun; + rs->start = curr->start_addr; + rs->end = curr->end_addr; + rs->len = curr->end_addr - curr->start_addr + 1; + return rs; +} + +static int alloc_bus_range (struct bus_node **new_bus, struct range_node **new_range, struct ebda_pci_rsrc *curr, int flag, u8 first_bus) +{ + struct bus_node * newbus; + struct range_node *newrange; + u8 num_ranges = 0; + + if (first_bus) { + newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL); + if (!newbus) { + err ("out of system memory. \n"); + return -ENOMEM; + } + memset (newbus, 0, sizeof (struct bus_node)); + newbus->busno = curr->bus_num; + } else { + newbus = *new_bus; + switch (flag) { + case MEM: + num_ranges = newbus->noMemRanges; + break; + case PFMEM: + num_ranges = newbus->noPFMemRanges; + break; + case IO: + num_ranges = newbus->noIORanges; + break; + } + } + + newrange = kmalloc (sizeof (struct range_node), GFP_KERNEL); + if (!newrange) { + if (first_bus) + kfree (newbus); + err ("out of system memory \n"); + return -ENOMEM; + } + memset (newrange, 0, sizeof (struct range_node)); + newrange->start = curr->start_addr; + newrange->end = curr->end_addr; + + if (first_bus || (!num_ranges)) + newrange->rangeno = 1; + else { + /* need to insert our range */ + add_range (flag, newrange, newbus); + debug ("%d resource Primary Bus inserted on bus %x [%x - %x]\n", flag, newbus->busno, newrange->start, newrange->end); + } + + switch (flag) { + case MEM: + newbus->rangeMem = newrange; + if (first_bus) + newbus->noMemRanges = 1; + else { + debug ("First Memory Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + ++newbus->noMemRanges; + fix_resources (newbus); + } + break; + case IO: + newbus->rangeIO = newrange; + if (first_bus) + newbus->noIORanges = 1; + else { + debug ("First IO Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + ++newbus->noIORanges; + fix_resources (newbus); + } + break; + case PFMEM: + newbus->rangePFMem = newrange; + if (first_bus) + newbus->noPFMemRanges = 1; + else { + debug ("1st PFMemory Primary on Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + ++newbus->noPFMemRanges; + fix_resources (newbus); + } + + break; + } + + *new_bus = newbus; + *new_range = newrange; + return 0; +} + + +/* Notes: + * 1. The ranges are ordered. The buses are not ordered. (First come) + * + * 2. If cannot allocate out of PFMem range, allocate from Mem ranges. PFmemFromMem + * are not sorted. (no need since use mem node). To not change the entire code, we + * also add mem node whenever this case happens so as not to change + * ibmphp_check_mem_resource etc (and since it really is taking Mem resource) + */ + +/***************************************************************************** + * This is the Resource Management initialization function. It will go through + * the Resource list taken from EBDA and fill in this module's data structures + * + * THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES, + * SINCE WE'RE GOING TO ASSUME FOR NOW WE DON'T HAVE THOSE ON OUR BUSES FOR NOW + * + * Input: ptr to the head of the resource list from EBDA + * Output: 0, -1 or error codes + ***************************************************************************/ +int ibmphp_rsrc_init (void) +{ + struct ebda_pci_rsrc *curr; + struct range_node *newrange = NULL; + struct bus_node *newbus = NULL; + struct bus_node *bus_cur; + struct bus_node *bus_prev; + struct list_head *tmp; + struct resource_node *new_io = NULL; + struct resource_node *new_mem = NULL; + struct resource_node *new_pfmem = NULL; + int rc; + struct list_head *tmp_ebda; + + list_for_each (tmp_ebda, &ibmphp_ebda_pci_rsrc_head) { + curr = list_entry (tmp_ebda, struct ebda_pci_rsrc, ebda_pci_rsrc_list); + if (!(curr->rsrc_type & PCIDEVMASK)) { + /* EBDA still lists non PCI devices, so ignore... */ + debug ("this is not a PCI DEVICE in rsrc_init, please take care\n"); + // continue; + } + + /* this is a primary bus resource */ + if (curr->rsrc_type & PRIMARYBUSMASK) { + /* memory */ + if ((curr->rsrc_type & RESTYPE) == MMASK) { + /* no bus structure exists in place yet */ + if (list_empty (&gbuses)) { + if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1))) + return rc; + list_add_tail (&newbus->bus_list, &gbuses); + debug ("gbuses = NULL, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + } else { + bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); + /* found our bus */ + if (bus_cur) { + rc = alloc_bus_range (&bus_cur, &newrange, curr, MEM, 0); + if (rc) + return rc; + } else { + /* went through all the buses and didn't find ours, need to create a new bus node */ + if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1))) + return rc; + + list_add_tail (&newbus->bus_list, &gbuses); + debug ("New Bus, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + } + } + } else if ((curr->rsrc_type & RESTYPE) == PFMASK) { + /* prefetchable memory */ + if (list_empty (&gbuses)) { + /* no bus structure exists in place yet */ + if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1))) + return rc; + list_add_tail (&newbus->bus_list, &gbuses); + debug ("gbuses = NULL, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + } else { + bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); + if (bus_cur) { + /* found our bus */ + rc = alloc_bus_range (&bus_cur, &newrange, curr, PFMEM, 0); + if (rc) + return rc; + } else { + /* went through all the buses and didn't find ours, need to create a new bus node */ + if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1))) + return rc; + list_add_tail (&newbus->bus_list, &gbuses); + debug ("1st Bus, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + } + } + } else if ((curr->rsrc_type & RESTYPE) == IOMASK) { + /* IO */ + if (list_empty (&gbuses)) { + /* no bus structure exists in place yet */ + if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1))) + return rc; + list_add_tail (&newbus->bus_list, &gbuses); + debug ("gbuses = NULL, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + } else { + bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); + if (bus_cur) { + rc = alloc_bus_range (&bus_cur, &newrange, curr, IO, 0); + if (rc) + return rc; + } else { + /* went through all the buses and didn't find ours, need to create a new bus node */ + if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1))) + return rc; + list_add_tail (&newbus->bus_list, &gbuses); + debug ("1st Bus, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + } + } + + } else { + ; /* type is reserved WHAT TO DO IN THIS CASE??? + NOTHING TO DO??? */ + } + } else { + /* regular pci device resource */ + if ((curr->rsrc_type & RESTYPE) == MMASK) { + /* Memory resource */ + new_mem = alloc_resources (curr); + if (!new_mem) + return -ENOMEM; + new_mem->type = MEM; + /* + * if it didn't find the bus, means PCI dev + * came b4 the Primary Bus info, so need to + * create a bus rangeno becomes a problem... + * assign a -1 and then update once the range + * actually appears... + */ + if (ibmphp_add_resource (new_mem) < 0) { + newbus = alloc_error_bus (curr); + if (!newbus) + return -ENOMEM; + newbus->firstMem = new_mem; + ++newbus->needMemUpdate; + new_mem->rangeno = -1; + } + debug ("Memory resource for device %x, bus %x, [%x - %x]\n", new_mem->devfunc, new_mem->busno, new_mem->start, new_mem->end); + + } else if ((curr->rsrc_type & RESTYPE) == PFMASK) { + /* PFMemory resource */ + new_pfmem = alloc_resources (curr); + if (!new_pfmem) + return -ENOMEM; + new_pfmem->type = PFMEM; + new_pfmem->fromMem = FALSE; + if (ibmphp_add_resource (new_pfmem) < 0) { + newbus = alloc_error_bus (curr); + if (!newbus) + return -ENOMEM; + newbus->firstPFMem = new_pfmem; + ++newbus->needPFMemUpdate; + new_pfmem->rangeno = -1; + } + + debug ("PFMemory resource for device %x, bus %x, [%x - %x]\n", new_pfmem->devfunc, new_pfmem->busno, new_pfmem->start, new_pfmem->end); + } else if ((curr->rsrc_type & RESTYPE) == IOMASK) { + /* IO resource */ + new_io = alloc_resources (curr); + if (!new_io) + return -ENOMEM; + new_io->type = IO; + + /* + * if it didn't find the bus, means PCI dev + * came b4 the Primary Bus info, so need to + * create a bus rangeno becomes a problem... + * Can assign a -1 and then update once the + * range actually appears... + */ + if (ibmphp_add_resource (new_io) < 0) { + newbus = alloc_error_bus (curr); + if (!newbus) + return -ENOMEM; + newbus->firstIO = new_io; + ++newbus->needIOUpdate; + new_io->rangeno = -1; + } + debug ("IO resource for device %x, bus %x, [%x - %x]\n", new_io->devfunc, new_io->busno, new_io->start, new_io->end); + } + } + } + + debug ("after the while loop in rsrc_init \n"); + + list_for_each (tmp, &gbuses) { + bus_cur = list_entry (tmp, struct bus_node, bus_list); + /* This is to get info about PPB resources, since EBDA doesn't put this info into the primary bus info */ + rc = update_bridge_ranges (&bus_cur); + if (rc) + return rc; + } + debug ("b4 once_over in rsrc_init \n"); + rc = once_over (); /* This is to align ranges (so no -1) */ + if (rc) + return rc; + debug ("after once_over in rsrc_init \n"); + return 0; +} + +/******************************************************************************** + * This function adds a range into a sorted list of ranges per bus for a particular + * range type, it then calls another routine to update the range numbers on the + * pci devices' resources for the appropriate resource + * + * Input: type of the resource, range to add, current bus + * Output: 0 or -1, bus and range ptrs + ********************************************************************************/ +static int add_range (int type, struct range_node *range, struct bus_node *bus_cur) +{ + struct range_node *range_cur = NULL; + struct range_node *range_prev; + int count = 0, i_init; + int noRanges = 0; + + switch (type) { + case MEM: + range_cur = bus_cur->rangeMem; + noRanges = bus_cur->noMemRanges; + break; + case PFMEM: + range_cur = bus_cur->rangePFMem; + noRanges = bus_cur->noPFMemRanges; + break; + case IO: + range_cur = bus_cur->rangeIO; + noRanges = bus_cur->noIORanges; + break; + } + + range_prev = NULL; + while (range_cur) { + if (range->start < range_cur->start) + break; + range_prev = range_cur; + range_cur = range_cur->next; + count = count + 1; + } + if (!count) { + /* our range will go at the beginning of the list */ + switch (type) { + case MEM: + bus_cur->rangeMem = range; + break; + case PFMEM: + bus_cur->rangePFMem = range; + break; + case IO: + bus_cur->rangeIO = range; + break; + } + range->next = range_cur; + range->rangeno = 1; + i_init = 0; + } else if (!range_cur) { + /* our range will go at the end of the list */ + range->next = NULL; + range_prev->next = range; + range->rangeno = range_prev->rangeno + 1; + return 0; + } else { + /* the range is in the middle */ + range_prev->next = range; + range->next = range_cur; + range->rangeno = range_cur->rangeno; + i_init = range_prev->rangeno; + } + + for (count = i_init; count < noRanges; ++count) { + ++range_cur->rangeno; + range_cur = range_cur->next; + } + + update_resources (bus_cur, type, i_init + 1); + return 0; +} + +/******************************************************************************* + * This routine goes through the list of resources of type 'type' and updates + * the range numbers that they correspond to. It was called from add_range fnc + * + * Input: bus, type of the resource, the rangeno starting from which to update + ******************************************************************************/ +static void update_resources (struct bus_node *bus_cur, int type, int rangeno) +{ + struct resource_node *res = NULL; + u8 eol = FALSE; /* end of list indicator */ + + switch (type) { + case MEM: + if (bus_cur->firstMem) + res = bus_cur->firstMem; + break; + case PFMEM: + if (bus_cur->firstPFMem) + res = bus_cur->firstPFMem; + break; + case IO: + if (bus_cur->firstIO) + res = bus_cur->firstIO; + break; + } + + if (res) { + while (res) { + if (res->rangeno == rangeno) + break; + if (res->next) + res = res->next; + else if (res->nextRange) + res = res->nextRange; + else { + eol = TRUE; + break; + } + } + + if (!eol) { + /* found the range */ + while (res) { + ++res->rangeno; + res = res->next; + } + } + } +} + +static void fix_me (struct resource_node *res, struct bus_node *bus_cur, struct range_node *range) +{ + char * str = ""; + switch (res->type) { + case IO: + str = "io"; + break; + case MEM: + str = "mem"; + break; + case PFMEM: + str = "pfmem"; + break; + } + + while (res) { + if (res->rangeno == -1) { + while (range) { + if ((res->start >= range->start) && (res->end <= range->end)) { + res->rangeno = range->rangeno; + debug ("%s->rangeno in fix_resources is %d\n", str, res->rangeno); + switch (res->type) { + case IO: + --bus_cur->needIOUpdate; + break; + case MEM: + --bus_cur->needMemUpdate; + break; + case PFMEM: + --bus_cur->needPFMemUpdate; + break; + } + break; + } + range = range->next; + } + } + if (res->next) + res = res->next; + else + res = res->nextRange; + } + +} + +/***************************************************************************** + * This routine reassigns the range numbers to the resources that had a -1 + * This case can happen only if upon initialization, resources taken by pci dev + * appear in EBDA before the resources allocated for that bus, since we don't + * know the range, we assign -1, and this routine is called after a new range + * is assigned to see the resources with unknown range belong to the added range + * + * Input: current bus + * Output: none, list of resources for that bus are fixed if can be + *******************************************************************************/ +static void fix_resources (struct bus_node *bus_cur) +{ + struct range_node *range; + struct resource_node *res; + + debug ("%s - bus_cur->busno = %d\n", __FUNCTION__, bus_cur->busno); + + if (bus_cur->needIOUpdate) { + res = bus_cur->firstIO; + range = bus_cur->rangeIO; + fix_me (res, bus_cur, range); + } + if (bus_cur->needMemUpdate) { + res = bus_cur->firstMem; + range = bus_cur->rangeMem; + fix_me (res, bus_cur, range); + } + if (bus_cur->needPFMemUpdate) { + res = bus_cur->firstPFMem; + range = bus_cur->rangePFMem; + fix_me (res, bus_cur, range); + } +} + +/******************************************************************************* + * This routine adds a resource to the list of resources to the appropriate bus + * based on their resource type and sorted by their starting addresses. It assigns + * the ptrs to next and nextRange if needed. + * + * Input: 3 diff. resources (nulled out if not needed) + * Output: ptrs assigned (to the node) + * 0 or -1 + *******************************************************************************/ +int ibmphp_add_resource (struct resource_node *res) +{ + struct resource_node *res_cur; + struct resource_node *res_prev; + struct bus_node *bus_cur; + struct range_node *range_cur = NULL; + struct resource_node *res_start = NULL; + + debug ("%s - enter\n", __FUNCTION__); + + bus_cur = find_bus_wprev (res->busno, NULL, 0); + + if (!bus_cur) { + /* didn't find a bus, smth's wrong!!! */ + err ("no bus in the system, either pci_dev's wrong or allocation failed\n"); + return -ENODEV; + } + + /* Normal case */ + switch (res->type) { + case IO: + range_cur = bus_cur->rangeIO; + res_start = bus_cur->firstIO; + break; + case MEM: + range_cur = bus_cur->rangeMem; + res_start = bus_cur->firstMem; + break; + case PFMEM: + range_cur = bus_cur->rangePFMem; + res_start = bus_cur->firstPFMem; + break; + default: + err ("cannot read the type of the resource to add... problem \n"); + return -EINVAL; + } + while (range_cur) { + if ((res->start >= range_cur->start) && (res->end <= range_cur->end)) { + res->rangeno = range_cur->rangeno; + break; + } + range_cur = range_cur->next; + } + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * this is again the case of rangeno = -1 + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + + if (!range_cur) { + switch (res->type) { + case IO: + ++bus_cur->needIOUpdate; + break; + case MEM: + ++bus_cur->needMemUpdate; + break; + case PFMEM: + ++bus_cur->needPFMemUpdate; + break; + } + res->rangeno = -1; + } + + debug ("The range is %d\n", res->rangeno); + if (!res_start) { + /* no first{IO,Mem,Pfmem} on the bus, 1st IO/Mem/Pfmem resource ever */ + switch (res->type) { + case IO: + bus_cur->firstIO = res; + break; + case MEM: + bus_cur->firstMem = res; + break; + case PFMEM: + bus_cur->firstPFMem = res; + break; + } + res->next = NULL; + res->nextRange = NULL; + } else { + res_cur = res_start; + res_prev = NULL; + + debug ("res_cur->rangeno is %d\n", res_cur->rangeno); + + while (res_cur) { + if (res_cur->rangeno >= res->rangeno) + break; + res_prev = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + } + + if (!res_cur) { + /* at the end of the resource list */ + debug ("i should be here, [%x - %x]\n", res->start, res->end); + res_prev->nextRange = res; + res->next = NULL; + res->nextRange = NULL; + } else if (res_cur->rangeno == res->rangeno) { + /* in the same range */ + while (res_cur) { + if (res->start < res_cur->start) + break; + res_prev = res_cur; + res_cur = res_cur->next; + } + if (!res_cur) { + /* the last resource in this range */ + res_prev->next = res; + res->next = NULL; + res->nextRange = res_prev->nextRange; + res_prev->nextRange = NULL; + } else if (res->start < res_cur->start) { + /* at the beginning or middle of the range */ + if (!res_prev) { + switch (res->type) { + case IO: + bus_cur->firstIO = res; + break; + case MEM: + bus_cur->firstMem = res; + break; + case PFMEM: + bus_cur->firstPFMem = res; + break; + } + } else if (res_prev->rangeno == res_cur->rangeno) + res_prev->next = res; + else + res_prev->nextRange = res; + + res->next = res_cur; + res->nextRange = NULL; + } + } else { + /* this is the case where it is 1st occurence of the range */ + if (!res_prev) { + /* at the beginning of the resource list */ + res->next = NULL; + switch (res->type) { + case IO: + res->nextRange = bus_cur->firstIO; + bus_cur->firstIO = res; + break; + case MEM: + res->nextRange = bus_cur->firstMem; + bus_cur->firstMem = res; + break; + case PFMEM: + res->nextRange = bus_cur->firstPFMem; + bus_cur->firstPFMem = res; + break; + } + } else if (res_cur->rangeno > res->rangeno) { + /* in the middle of the resource list */ + res_prev->nextRange = res; + res->next = NULL; + res->nextRange = res_cur; + } + } + } + + debug ("%s - exit\n", __FUNCTION__); + return 0; +} + +/**************************************************************************** + * This routine will remove the resource from the list of resources + * + * Input: io, mem, and/or pfmem resource to be deleted + * Ouput: modified resource list + * 0 or error code + ****************************************************************************/ +int ibmphp_remove_resource (struct resource_node *res) +{ + struct bus_node *bus_cur; + struct resource_node *res_cur = NULL; + struct resource_node *res_prev; + struct resource_node *mem_cur; + char * type = ""; + + bus_cur = find_bus_wprev (res->busno, NULL, 0); + + if (!bus_cur) { + err ("cannot find corresponding bus of the io resource to remove " + "bailing out...\n"); + return -ENODEV; + } + + switch (res->type) { + case IO: + res_cur = bus_cur->firstIO; + type = "io"; + break; + case MEM: + res_cur = bus_cur->firstMem; + type = "mem"; + break; + case PFMEM: + res_cur = bus_cur->firstPFMem; + type = "pfmem"; + break; + default: + err ("unknown type for resource to remove \n"); + return -EINVAL; + } + res_prev = NULL; + + while (res_cur) { + /* ???????????DO WE _NEED_ TO BE CHECKING FOR END AS WELL?????????? */ + if ((res_cur->start == res->start) && (res_cur->end == res->end)) + break; + res_prev = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + } + + if (!res_cur) { + if (res->type == PFMEM) { + /* + * case where pfmem might be in the PFMemFromMem list + * so will also need to remove the corresponding mem + * entry + */ + res_cur = bus_cur->firstPFMemFromMem; + res_prev = NULL; + + while (res_cur) { + if ((res_cur->start == res->start) && (res_cur->end == res->end)) { + mem_cur = bus_cur->firstMem; + while (mem_cur) { + if ((mem_cur->start == res_cur->start) + && (mem_cur->end == res_cur->end)) + break; + if (mem_cur->next) + mem_cur = mem_cur->next; + else + mem_cur = mem_cur->nextRange; + } + if (!mem_cur) { + err ("cannot find corresponding mem node for pfmem...\n"); + return -EINVAL; + } + + ibmphp_remove_resource (mem_cur); + if (!res_prev) + bus_cur->firstPFMemFromMem = res_cur->next; + else + res_prev->next = res_cur->next; + kfree (res_cur); + return 0; + } + res_prev = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + } + if (!res_cur) { + err ("cannot find pfmem to delete...\n"); + return -EINVAL; + } + } else { + err ("the %s resource is not in the list to be deleted...\n", type); + return -EINVAL; + } + } + if (!res_prev) { + /* first device to be deleted */ + if (res_cur->next) { + switch (res->type) { + case IO: + bus_cur->firstIO = res_cur->next; + break; + case MEM: + bus_cur->firstMem = res_cur->next; + break; + case PFMEM: + bus_cur->firstPFMem = res_cur->next; + break; + } + } else if (res_cur->nextRange) { + switch (res->type) { + case IO: + bus_cur->firstIO = res_cur->nextRange; + break; + case MEM: + bus_cur->firstMem = res_cur->nextRange; + break; + case PFMEM: + bus_cur->firstPFMem = res_cur->nextRange; + break; + } + } else { + switch (res->type) { + case IO: + bus_cur->firstIO = NULL; + break; + case MEM: + bus_cur->firstMem = NULL; + break; + case PFMEM: + bus_cur->firstPFMem = NULL; + break; + } + } + kfree (res_cur); + return 0; + } else { + if (res_cur->next) { + if (res_prev->rangeno == res_cur->rangeno) + res_prev->next = res_cur->next; + else + res_prev->nextRange = res_cur->next; + } else if (res_cur->nextRange) { + res_prev->next = NULL; + res_prev->nextRange = res_cur->nextRange; + } else { + res_prev->next = NULL; + res_prev->nextRange = NULL; + } + kfree (res_cur); + return 0; + } + + return 0; +} + +static struct range_node * find_range (struct bus_node *bus_cur, struct resource_node * res) +{ + struct range_node * range = NULL; + + switch (res->type) { + case IO: + range = bus_cur->rangeIO; + break; + case MEM: + range = bus_cur->rangeMem; + break; + case PFMEM: + range = bus_cur->rangePFMem; + break; + default: + err ("cannot read resource type in find_range \n"); + } + + while (range) { + if (res->rangeno == range->rangeno) + break; + range = range->next; + } + return range; +} + +/***************************************************************************** + * This routine will check to make sure the io/mem/pfmem->len that the device asked for + * can fit w/i our list of available IO/MEM/PFMEM resources. If cannot, returns -EINVAL, + * otherwise, returns 0 + * + * Input: resource + * Ouput: the correct start and end address are inputted into the resource node, + * 0 or -EINVAL + *****************************************************************************/ +int ibmphp_check_resource (struct resource_node *res, u8 bridge) +{ + struct bus_node *bus_cur; + struct range_node *range = NULL; + struct resource_node *res_prev; + struct resource_node *res_cur = NULL; + u32 len_cur = 0, start_cur = 0, len_tmp = 0; + int noranges = 0; + u32 tmp_start; /* this is to make sure start address is divisible by the length needed */ + u32 tmp_divide; + u8 flag = FALSE; + + if (!res) + return -EINVAL; + + if (bridge) { + /* The rules for bridges are different, 4K divisible for IO, 1M for (pf)mem*/ + if (res->type == IO) + tmp_divide = IOBRIDGE; + else + tmp_divide = MEMBRIDGE; + } else + tmp_divide = res->len; + + bus_cur = find_bus_wprev (res->busno, NULL, 0); + + if (!bus_cur) { + /* didn't find a bus, smth's wrong!!! */ + err ("no bus in the system, either pci_dev's wrong or allocation failed \n"); + return -EINVAL; + } + + debug ("%s - enter\n", __FUNCTION__); + debug ("bus_cur->busno is %d\n", bus_cur->busno); + + /* This is a quick fix to not mess up with the code very much. i.e., + * 2000-2fff, len = 1000, but when we compare, we need it to be fff */ + res->len -= 1; + + switch (res->type) { + case IO: + res_cur = bus_cur->firstIO; + noranges = bus_cur->noIORanges; + break; + case MEM: + res_cur = bus_cur->firstMem; + noranges = bus_cur->noMemRanges; + break; + case PFMEM: + res_cur = bus_cur->firstPFMem; + noranges = bus_cur->noPFMemRanges; + break; + default: + err ("wrong type of resource to check \n"); + return -EINVAL; + } + res_prev = NULL; + + while (res_cur) { + range = find_range (bus_cur, res_cur); + debug ("%s - rangeno = %d\n", __FUNCTION__, res_cur->rangeno); + + if (!range) { + err ("no range for the device exists... bailing out...\n"); + return -EINVAL; + } + + /* found our range */ + if (!res_prev) { + /* first time in the loop */ + if ((res_cur->start != range->start) && ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) { + debug ("len_tmp = %x\n", len_tmp); + + if ((len_tmp < len_cur) || (len_cur == 0)) { + + if ((range->start % tmp_divide) == 0) { + /* just perfect, starting address is divisible by length */ + flag = TRUE; + len_cur = len_tmp; + start_cur = range->start; + } else { + /* Needs adjusting */ + tmp_start = range->start; + flag = FALSE; + + while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { + if ((tmp_start % tmp_divide) == 0) { + flag = TRUE; + len_cur = len_tmp; + start_cur = tmp_start; + break; + } + tmp_start += tmp_divide - tmp_start % tmp_divide; + if (tmp_start >= res_cur->start - 1) + break; + } + } + + if (flag && len_cur == res->len) { + debug ("but we are not here, right?\n"); + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } + } + } + if (!res_cur->next) { + /* last device on the range */ + if ((range->end != res_cur->end) && ((len_tmp = range->end - (res_cur->end + 1)) >= res->len)) { + debug ("len_tmp = %x\n", len_tmp); + if ((len_tmp < len_cur) || (len_cur == 0)) { + + if (((res_cur->end + 1) % tmp_divide) == 0) { + /* just perfect, starting address is divisible by length */ + flag = TRUE; + len_cur = len_tmp; + start_cur = res_cur->end + 1; + } else { + /* Needs adjusting */ + tmp_start = res_cur->end + 1; + flag = FALSE; + + while ((len_tmp = range->end - tmp_start) >= res->len) { + if ((tmp_start % tmp_divide) == 0) { + flag = TRUE; + len_cur = len_tmp; + start_cur = tmp_start; + break; + } + tmp_start += tmp_divide - tmp_start % tmp_divide; + if (tmp_start >= range->end) + break; + } + } + if (flag && len_cur == res->len) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } + } + } + + if (res_prev) { + if (res_prev->rangeno != res_cur->rangeno) { + /* 1st device on this range */ + if ((res_cur->start != range->start) && + ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) { + if ((len_tmp < len_cur) || (len_cur == 0)) { + if ((range->start % tmp_divide) == 0) { + /* just perfect, starting address is divisible by length */ + flag = TRUE; + len_cur = len_tmp; + start_cur = range->start; + } else { + /* Needs adjusting */ + tmp_start = range->start; + flag = FALSE; + + while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { + if ((tmp_start % tmp_divide) == 0) { + flag = TRUE; + len_cur = len_tmp; + start_cur = tmp_start; + break; + } + tmp_start += tmp_divide - tmp_start % tmp_divide; + if (tmp_start >= res_cur->start - 1) + break; + } + } + + if (flag && len_cur == res->len) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } + } + } else { + /* in the same range */ + if ((len_tmp = res_cur->start - 1 - res_prev->end - 1) >= res->len) { + if ((len_tmp < len_cur) || (len_cur == 0)) { + if (((res_prev->end + 1) % tmp_divide) == 0) { + /* just perfect, starting address's divisible by length */ + flag = TRUE; + len_cur = len_tmp; + start_cur = res_prev->end + 1; + } else { + /* Needs adjusting */ + tmp_start = res_prev->end + 1; + flag = FALSE; + + while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { + if ((tmp_start % tmp_divide) == 0) { + flag = TRUE; + len_cur = len_tmp; + start_cur = tmp_start; + break; + } + tmp_start += tmp_divide - tmp_start % tmp_divide; + if (tmp_start >= res_cur->start - 1) + break; + } + } + + if (flag && len_cur == res->len) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } + } + } + } + /* end if (res_prev) */ + res_prev = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + } /* end of while */ + + + if (!res_prev) { + /* 1st device ever */ + /* need to find appropriate range */ + switch (res->type) { + case IO: + range = bus_cur->rangeIO; + break; + case MEM: + range = bus_cur->rangeMem; + break; + case PFMEM: + range = bus_cur->rangePFMem; + break; + } + while (range) { + if ((len_tmp = range->end - range->start) >= res->len) { + if ((len_tmp < len_cur) || (len_cur == 0)) { + if ((range->start % tmp_divide) == 0) { + /* just perfect, starting address's divisible by length */ + flag = TRUE; + len_cur = len_tmp; + start_cur = range->start; + } else { + /* Needs adjusting */ + tmp_start = range->start; + flag = FALSE; + + while ((len_tmp = range->end - tmp_start) >= res->len) { + if ((tmp_start % tmp_divide) == 0) { + flag = TRUE; + len_cur = len_tmp; + start_cur = tmp_start; + break; + } + tmp_start += tmp_divide - tmp_start % tmp_divide; + if (tmp_start >= range->end) + break; + } + } + + if (flag && len_cur == res->len) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } + } + range = range->next; + } /* end of while */ + + if ((!range) && (len_cur == 0)) { + /* have gone through the list of devices and ranges and haven't found n.e.thing */ + err ("no appropriate range.. bailing out...\n"); + return -EINVAL; + } else if (len_cur) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } + + if (!res_cur) { + debug ("prev->rangeno = %d, noranges = %d\n", res_prev->rangeno, noranges); + if (res_prev->rangeno < noranges) { + /* if there're more ranges out there to check */ + switch (res->type) { + case IO: + range = bus_cur->rangeIO; + break; + case MEM: + range = bus_cur->rangeMem; + break; + case PFMEM: + range = bus_cur->rangePFMem; + break; + } + while (range) { + if ((len_tmp = range->end - range->start) >= res->len) { + if ((len_tmp < len_cur) || (len_cur == 0)) { + if ((range->start % tmp_divide) == 0) { + /* just perfect, starting address's divisible by length */ + flag = TRUE; + len_cur = len_tmp; + start_cur = range->start; + } else { + /* Needs adjusting */ + tmp_start = range->start; + flag = FALSE; + + while ((len_tmp = range->end - tmp_start) >= res->len) { + if ((tmp_start % tmp_divide) == 0) { + flag = TRUE; + len_cur = len_tmp; + start_cur = tmp_start; + break; + } + tmp_start += tmp_divide - tmp_start % tmp_divide; + if (tmp_start >= range->end) + break; + } + } + + if (flag && len_cur == res->len) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } + } + range = range->next; + } /* end of while */ + + if ((!range) && (len_cur == 0)) { + /* have gone through the list of devices and ranges and haven't found n.e.thing */ + err ("no appropriate range.. bailing out...\n"); + return -EINVAL; + } else if (len_cur) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } else { + /* no more ranges to check on */ + if (len_cur) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } else { + /* have gone through the list of devices and haven't found n.e.thing */ + err ("no appropriate range.. bailing out...\n"); + return -EINVAL; + } + } + } /* end if(!res_cur) */ + return -EINVAL; +} + +/******************************************************************************** + * This routine is called from remove_card if the card contained PPB. + * It will remove all the resources on the bus as well as the bus itself + * Input: Bus + * Ouput: 0, -ENODEV + ********************************************************************************/ +int ibmphp_remove_bus (struct bus_node *bus, u8 parent_busno) +{ + struct resource_node *res_cur; + struct resource_node *res_tmp; + struct bus_node *prev_bus; + int rc; + + prev_bus = find_bus_wprev (parent_busno, NULL, 0); + + if (!prev_bus) { + err ("something terribly wrong. Cannot find parent bus to the one to remove\n"); + return -ENODEV; + } + + debug ("In ibmphp_remove_bus... prev_bus->busno is %x\n", prev_bus->busno); + + rc = remove_ranges (bus, prev_bus); + if (rc) + return rc; + + if (bus->firstIO) { + res_cur = bus->firstIO; + while (res_cur) { + res_tmp = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + kfree (res_tmp); + res_tmp = NULL; + } + bus->firstIO = NULL; + } + if (bus->firstMem) { + res_cur = bus->firstMem; + while (res_cur) { + res_tmp = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + kfree (res_tmp); + res_tmp = NULL; + } + bus->firstMem = NULL; + } + if (bus->firstPFMem) { + res_cur = bus->firstPFMem; + while (res_cur) { + res_tmp = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + kfree (res_tmp); + res_tmp = NULL; + } + bus->firstPFMem = NULL; + } + + if (bus->firstPFMemFromMem) { + res_cur = bus->firstPFMemFromMem; + while (res_cur) { + res_tmp = res_cur; + res_cur = res_cur->next; + + kfree (res_tmp); + res_tmp = NULL; + } + bus->firstPFMemFromMem = NULL; + } + + list_del (&bus->bus_list); + kfree (bus); + return 0; +} + +/****************************************************************************** + * This routine deletes the ranges from a given bus, and the entries from the + * parent's bus in the resources + * Input: current bus, previous bus + * Output: 0, -EINVAL + ******************************************************************************/ +static int remove_ranges (struct bus_node *bus_cur, struct bus_node *bus_prev) +{ + struct range_node *range_cur; + struct range_node *range_tmp; + int i; + struct resource_node *res = NULL; + + if (bus_cur->noIORanges) { + range_cur = bus_cur->rangeIO; + for (i = 0; i < bus_cur->noIORanges; i++) { + if (ibmphp_find_resource (bus_prev, range_cur->start, &res, IO) < 0) + return -EINVAL; + ibmphp_remove_resource (res); + + range_tmp = range_cur; + range_cur = range_cur->next; + kfree (range_tmp); + range_tmp = NULL; + } + bus_cur->rangeIO = NULL; + } + if (bus_cur->noMemRanges) { + range_cur = bus_cur->rangeMem; + for (i = 0; i < bus_cur->noMemRanges; i++) { + if (ibmphp_find_resource (bus_prev, range_cur->start, &res, MEM) < 0) + return -EINVAL; + + ibmphp_remove_resource (res); + range_tmp = range_cur; + range_cur = range_cur->next; + kfree (range_tmp); + range_tmp = NULL; + } + bus_cur->rangeMem = NULL; + } + if (bus_cur->noPFMemRanges) { + range_cur = bus_cur->rangePFMem; + for (i = 0; i < bus_cur->noPFMemRanges; i++) { + if (ibmphp_find_resource (bus_prev, range_cur->start, &res, PFMEM) < 0) + return -EINVAL; + + ibmphp_remove_resource (res); + range_tmp = range_cur; + range_cur = range_cur->next; + kfree (range_tmp); + range_tmp = NULL; + } + bus_cur->rangePFMem = NULL; + } + return 0; +} + +/* + * find the resource node in the bus + * Input: Resource needed, start address of the resource, type or resource + */ +int ibmphp_find_resource (struct bus_node *bus, u32 start_address, struct resource_node **res, int flag) +{ + struct resource_node *res_cur = NULL; + char * type = ""; + + switch (flag) { + case IO: + res_cur = bus->firstIO; + type = "io"; + break; + case MEM: + res_cur = bus->firstMem; + type = "mem"; + break; + case PFMEM: + res_cur = bus->firstPFMem; + type = "pfmem"; + break; + default: + err ("wrong type of flag \n"); + return -EINVAL; + } + + while (res_cur) { + if (res_cur->start == start_address) { + *res = res_cur; + break; + } + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + } + + if (!res_cur) { + if (flag == PFMEM) { + res_cur = bus->firstPFMemFromMem; + while (res_cur) { + if (res_cur->start == start_address) { + *res = res_cur; + break; + } + res_cur = res_cur->next; + } + if (!res_cur) { + err ("SOS...cannot find %s resource in the bus. \n", type); + return -EINVAL; + } + } else { + err ("SOS... cannot find %s resource in the bus. \n", type); + return -EINVAL; + } + } + + if (*res) + debug ("*res->start = %x \n", (*res)->start); + + return 0; +} + +/*********************************************************************** + * This routine will free the resource structures used by the + * system. It is called from cleanup routine for the module + * Parameters: none + * Returns: none + ***********************************************************************/ +void ibmphp_free_resources (void) +{ + struct bus_node *bus_cur = NULL; + struct bus_node *bus_tmp; + struct range_node *range_cur; + struct range_node *range_tmp; + struct resource_node *res_cur; + struct resource_node *res_tmp; + struct list_head *tmp; + struct list_head *next; + int i = 0; + flags = 1; + + list_for_each_safe (tmp, next, &gbuses) { + bus_cur = list_entry (tmp, struct bus_node, bus_list); + if (bus_cur->noIORanges) { + range_cur = bus_cur->rangeIO; + for (i = 0; i < bus_cur->noIORanges; i++) { + if (!range_cur) + break; + range_tmp = range_cur; + range_cur = range_cur->next; + kfree (range_tmp); + range_tmp = NULL; + } + } + if (bus_cur->noMemRanges) { + range_cur = bus_cur->rangeMem; + for (i = 0; i < bus_cur->noMemRanges; i++) { + if (!range_cur) + break; + range_tmp = range_cur; + range_cur = range_cur->next; + kfree (range_tmp); + range_tmp = NULL; + } + } + if (bus_cur->noPFMemRanges) { + range_cur = bus_cur->rangePFMem; + for (i = 0; i < bus_cur->noPFMemRanges; i++) { + if (!range_cur) + break; + range_tmp = range_cur; + range_cur = range_cur->next; + kfree (range_tmp); + range_tmp = NULL; + } + } + + if (bus_cur->firstIO) { + res_cur = bus_cur->firstIO; + while (res_cur) { + res_tmp = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + kfree (res_tmp); + res_tmp = NULL; + } + bus_cur->firstIO = NULL; + } + if (bus_cur->firstMem) { + res_cur = bus_cur->firstMem; + while (res_cur) { + res_tmp = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + kfree (res_tmp); + res_tmp = NULL; + } + bus_cur->firstMem = NULL; + } + if (bus_cur->firstPFMem) { + res_cur = bus_cur->firstPFMem; + while (res_cur) { + res_tmp = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + kfree (res_tmp); + res_tmp = NULL; + } + bus_cur->firstPFMem = NULL; + } + + if (bus_cur->firstPFMemFromMem) { + res_cur = bus_cur->firstPFMemFromMem; + while (res_cur) { + res_tmp = res_cur; + res_cur = res_cur->next; + + kfree (res_tmp); + res_tmp = NULL; + } + bus_cur->firstPFMemFromMem = NULL; + } + + bus_tmp = bus_cur; + list_del (&bus_cur->bus_list); + kfree (bus_tmp); + bus_tmp = NULL; + } +} + +/********************************************************************************* + * This function will go over the PFmem resources to check if the EBDA allocated + * pfmem out of memory buckets of the bus. If so, it will change the range numbers + * and a flag to indicate that this resource is out of memory. It will also move the + * Pfmem out of the pfmem resource list to the PFMemFromMem list, and will create + * a new Mem node + * This routine is called right after initialization + *******************************************************************************/ +static int once_over (void) +{ + struct resource_node *pfmem_cur; + struct resource_node *pfmem_prev; + struct resource_node *mem; + struct bus_node *bus_cur; + struct list_head *tmp; + + list_for_each (tmp, &gbuses) { + bus_cur = list_entry (tmp, struct bus_node, bus_list); + if ((!bus_cur->rangePFMem) && (bus_cur->firstPFMem)) { + for (pfmem_cur = bus_cur->firstPFMem, pfmem_prev = NULL; pfmem_cur; pfmem_prev = pfmem_cur, pfmem_cur = pfmem_cur->next) { + pfmem_cur->fromMem = TRUE; + if (pfmem_prev) + pfmem_prev->next = pfmem_cur->next; + else + bus_cur->firstPFMem = pfmem_cur->next; + + if (!bus_cur->firstPFMemFromMem) + pfmem_cur->next = NULL; + else + /* we don't need to sort PFMemFromMem since we're using mem node for + all the real work anyways, so just insert at the beginning of the + list + */ + pfmem_cur->next = bus_cur->firstPFMemFromMem; + + bus_cur->firstPFMemFromMem = pfmem_cur; + + mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!mem) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (mem, 0, sizeof (struct resource_node)); + mem->type = MEM; + mem->busno = pfmem_cur->busno; + mem->devfunc = pfmem_cur->devfunc; + mem->start = pfmem_cur->start; + mem->end = pfmem_cur->end; + mem->len = pfmem_cur->len; + if (ibmphp_add_resource (mem) < 0) + err ("Trouble...trouble... EBDA allocated pfmem from mem, but system doesn't display it has this space... unless not PCI device...\n"); + pfmem_cur->rangeno = mem->rangeno; + } /* end for pfmem */ + } /* end if */ + } /* end list_for_each bus */ + return 0; +} + +int ibmphp_add_pfmem_from_mem (struct resource_node *pfmem) +{ + struct bus_node *bus_cur = find_bus_wprev (pfmem->busno, NULL, 0); + + if (!bus_cur) { + err ("cannot find bus of pfmem to add...\n"); + return -ENODEV; + } + + if (bus_cur->firstPFMemFromMem) + pfmem->next = bus_cur->firstPFMemFromMem; + else + pfmem->next = NULL; + + bus_cur->firstPFMemFromMem = pfmem; + + return 0; +} + +/* This routine just goes through the buses to see if the bus already exists. + * It is called from ibmphp_find_sec_number, to find out a secondary bus number for + * bridged cards + * Parameters: bus_number + * Returns: Bus pointer or NULL + */ +struct bus_node *ibmphp_find_res_bus (u8 bus_number) +{ + return find_bus_wprev (bus_number, NULL, 0); +} + +static inline struct bus_node *find_bus_wprev (u8 bus_number, struct bus_node **prev, u8 flag) +{ + struct bus_node *bus_cur; + struct list_head *tmp; + struct list_head *tmp_prev; + + list_for_each (tmp, &gbuses) { + tmp_prev = tmp->prev; + bus_cur = list_entry (tmp, struct bus_node, bus_list); + if (flag) + *prev = list_entry (tmp_prev, struct bus_node, bus_list); + if (bus_cur->busno == bus_number) + return bus_cur; + } + + return NULL; +} + +void ibmphp_print_test (void) +{ + int i = 0; + struct bus_node *bus_cur = NULL; + struct range_node *range; + struct resource_node *res; + struct list_head *tmp; + + if ((!list_empty(&gbuses)) && flags) { + err ("The GBUSES is not NULL?!?!?!?!?\n"); + return; + } + + list_for_each (tmp, &gbuses) { + bus_cur = list_entry (tmp, struct bus_node, bus_list); + debug ("This is bus # %d. There are \n", bus_cur->busno); + debug ("IORanges = %d\t", bus_cur->noIORanges); + debug ("MemRanges = %d\t", bus_cur->noMemRanges); + debug ("PFMemRanges = %d\n", bus_cur->noPFMemRanges); + debug ("The IO Ranges are as follows:\n"); + if (bus_cur->rangeIO) { + range = bus_cur->rangeIO; + for (i = 0; i < bus_cur->noIORanges; i++) { + debug ("rangeno is %d\n", range->rangeno); + debug ("[%x - %x]\n", range->start, range->end); + range = range->next; + } + } + + debug ("The Mem Ranges are as follows:\n"); + if (bus_cur->rangeMem) { + range = bus_cur->rangeMem; + for (i = 0; i < bus_cur->noMemRanges; i++) { + debug ("rangeno is %d\n", range->rangeno); + debug ("[%x - %x]\n", range->start, range->end); + range = range->next; + } + } + + debug ("The PFMem Ranges are as follows:\n"); + + if (bus_cur->rangePFMem) { + range = bus_cur->rangePFMem; + for (i = 0; i < bus_cur->noPFMemRanges; i++) { + debug ("rangeno is %d\n", range->rangeno); + debug ("[%x - %x]\n", range->start, range->end); + range = range->next; + } + } + + debug ("The resources on this bus are as follows\n"); + + debug ("IO...\n"); + if (bus_cur->firstIO) { + res = bus_cur->firstIO; + while (res) { + debug ("The range # is %d\n", res->rangeno); + debug ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); + debug ("[%x - %x], len=%x\n", res->start, res->end, res->len); + if (res->next) + res = res->next; + else if (res->nextRange) + res = res->nextRange; + else + break; + } + } + debug ("Mem...\n"); + if (bus_cur->firstMem) { + res = bus_cur->firstMem; + while (res) { + debug ("The range # is %d\n", res->rangeno); + debug ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); + debug ("[%x - %x], len=%x\n", res->start, res->end, res->len); + if (res->next) + res = res->next; + else if (res->nextRange) + res = res->nextRange; + else + break; + } + } + debug ("PFMem...\n"); + if (bus_cur->firstPFMem) { + res = bus_cur->firstPFMem; + while (res) { + debug ("The range # is %d\n", res->rangeno); + debug ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); + debug ("[%x - %x], len=%x\n", res->start, res->end, res->len); + if (res->next) + res = res->next; + else if (res->nextRange) + res = res->nextRange; + else + break; + } + } + + debug ("PFMemFromMem...\n"); + if (bus_cur->firstPFMemFromMem) { + res = bus_cur->firstPFMemFromMem; + while (res) { + debug ("The range # is %d\n", res->rangeno); + debug ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); + debug ("[%x - %x], len=%x\n", res->start, res->end, res->len); + res = res->next; + } + } + } +} + +/* This routine will read the windows for any PPB we have and update the + * range info for the secondary bus, and will also input this info into + * primary bus, since BIOS doesn't. This is for PPB that are in the system + * on bootup + * Input: primary busno + * Returns: none + * Note: this function doesn't take into account IO restrictions etc, + * so will only work for bridges with no video/ISA devices behind them It + * also will not work for onboard PPB's that can have more than 1 *bus + * behind them All these are TO DO. + * Also need to add more error checkings... (from fnc returns etc) + */ +static int update_bridge_ranges (struct bus_node **bus) +{ + u8 sec_busno, device, function, busno, hdr_type, start_io_address, end_io_address; + u16 vendor_id, upper_io_start, upper_io_end, start_mem_address, end_mem_address; + u32 start_address, end_address, upper_start, upper_end; + struct bus_node *bus_sec; + struct bus_node *bus_cur; + struct resource_node *io; + struct resource_node *mem; + struct resource_node *pfmem; + struct range_node *range; + bus_cur = *bus; + busno = bus_cur->busno; + + debug ("inside update_bridge_ranges \n"); + debug ("bus_cur->busno = %x\n", bus_cur->busno); + + for (device = 0; device < 32; device++) { + for (function = 0x00; function < 0x08; function++) { + pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_VENDOR_ID, &vendor_id); + + if (vendor_id != PCI_VENDOR_ID_NOTVALID) { + /* found correct device!!! */ + pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_HEADER_TYPE, &hdr_type); + + switch (hdr_type) { + case PCI_HEADER_TYPE_NORMAL: + function = 0x8; + break; + case PCI_HEADER_TYPE_MULTIDEVICE: + break; + case PCI_HEADER_TYPE_BRIDGE: + function = 0x8; + case PCI_HEADER_TYPE_MULTIBRIDGE: + /* We assume here that only 1 bus behind the bridge + TO DO: add functionality for several: + temp = secondary; + while (temp < subordinate) { + ... + temp++; + } + */ + pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_SECONDARY_BUS, &sec_busno); + bus_sec = find_bus_wprev (sec_busno, NULL, 0); + pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_IO_BASE, &start_io_address); + pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_IO_LIMIT, &end_io_address); + pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_IO_BASE_UPPER16, &upper_io_start); + pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_IO_LIMIT_UPPER16, &upper_io_end); + start_address = (start_io_address & PCI_IO_RANGE_MASK) << 8; + start_address |= (upper_io_start << 16); + end_address = (end_io_address & PCI_IO_RANGE_MASK) << 8; + end_address |= (upper_io_end << 16); + + if ((start_address) && (start_address <= end_address)) { + + range = kmalloc (sizeof (struct range_node), GFP_KERNEL); + + if (!range) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (range, 0, sizeof (struct range_node)); + range->start = start_address; + range->end = end_address + 0xfff; + + if (bus_sec->noIORanges > 0) + add_range (IO, range, bus_sec); + else { + /* 1st IO Range on the bus */ + range->rangeno = 1; + bus_sec->rangeIO = range; + } + + ++bus_sec->noIORanges; + fix_resources (bus_sec); + + io = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!io) { + kfree (range); + err ("out of system memory \n"); + return -ENOMEM; + } + memset (io, 0, sizeof (struct resource_node)); + io->type = IO; + io->busno = bus_cur->busno; + io->devfunc = ((device << 3) | (function & 0x7)); + io->start = start_address; + io->end = end_address + 0xfff; + io->len = io->end - io->start + 1; + + ibmphp_add_resource (io); + } + + pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_MEMORY_BASE, &start_mem_address); + pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_MEMORY_LIMIT, &end_mem_address); + + start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16; + end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16; + + if ((start_address) && (start_address <= end_address)) { + + range = kmalloc (sizeof (struct range_node), GFP_KERNEL); + if (!range) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (range, 0, sizeof (struct range_node)); + range->start = start_address; + range->end = end_address + 0xfffff; + + if (bus_sec->noMemRanges > 0) + add_range (MEM, range, bus_sec); + else { + /* 1st Mem Range on the bus */ + range->rangeno = 1; + bus_sec->rangeMem = range; + } + + ++bus_sec->noMemRanges; + fix_resources (bus_sec); + + mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!mem) { + kfree (range); + err ("out of system memory \n"); + return -ENOMEM; + } + memset (mem, 0, sizeof (struct resource_node)); + mem->type = MEM; + mem->busno = bus_cur->busno; + mem->devfunc = ((device << 3) | (function & 0x7)); + mem->start = start_address; + mem->end = end_address + 0xfffff; + mem->len = mem->end - mem->start + 1; + ibmphp_add_resource (mem); + } + pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PREF_MEMORY_BASE, &start_mem_address); + pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PREF_MEMORY_LIMIT, &end_mem_address); + pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PREF_BASE_UPPER32, &upper_start); + pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PREF_LIMIT_UPPER32, &upper_end); + start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16; + end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16; +#if BITS_PER_LONG == 64 + start_address |= ((long) upper_start) << 32; + end_address |= ((long) upper_end) << 32; +#endif + + if ((start_address) && (start_address <= end_address)) { + + range = kmalloc (sizeof (struct range_node), GFP_KERNEL); + if (!range) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (range, 0, sizeof (struct range_node)); + range->start = start_address; + range->end = end_address + 0xfffff; + + if (bus_sec->noPFMemRanges > 0) + add_range (PFMEM, range, bus_sec); + else { + /* 1st PFMem Range on the bus */ + range->rangeno = 1; + bus_sec->rangePFMem = range; + } + + ++bus_sec->noPFMemRanges; + fix_resources (bus_sec); + + pfmem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!pfmem) { + kfree (range); + err ("out of system memory \n"); + return -ENOMEM; + } + memset (pfmem, 0, sizeof (struct resource_node)); + pfmem->type = PFMEM; + pfmem->busno = bus_cur->busno; + pfmem->devfunc = ((device << 3) | (function & 0x7)); + pfmem->start = start_address; + pfmem->end = end_address + 0xfffff; + pfmem->len = pfmem->end - pfmem->start + 1; + pfmem->fromMem = FALSE; + ibmphp_add_resource (pfmem); + } + break; + } /* end of switch */ + } /* end if vendor */ + } /* end for function */ + } /* end for device */ + + bus = &bus_cur; + return 0; +} diff -Nru a/drivers/hotplug/pci_hotplug_core.c b/drivers/hotplug/pci_hotplug_core.c --- a/drivers/hotplug/pci_hotplug_core.c Thu Mar 7 18:17:41 2002 +++ b/drivers/hotplug/pci_hotplug_core.c Thu Mar 7 18:17:41 2002 @@ -1,8 +1,8 @@ /* * PCI HotPlug Controller Core * - * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001 IBM Corp. + * Copyright (c) 2001-2002 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001-2002 IBM Corp. * * All rights reserved. * @@ -23,6 +23,8 @@ * * Send feedback to * + * Filesystem portion based on work done by Pat Mochel on ddfs/driverfs + * */ #include @@ -54,7 +56,7 @@ /* local variables */ static int debug; -#define DRIVER_VERSION "0.3" +#define DRIVER_VERSION "0.4" #define DRIVER_AUTHOR "Greg Kroah-Hartman " #define DRIVER_DESC "PCI Hot Plug PCI Core" @@ -74,7 +76,6 @@ }; static struct super_operations pcihpfs_ops; -static struct address_space_operations pcihpfs_aops; static struct file_operations pcihpfs_dir_operations; static struct file_operations default_file_operations; static struct inode_operations pcihpfs_dir_inode_operations; @@ -112,7 +113,6 @@ inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blocks = 0; inode->i_rdev = NODEV; - inode->i_mapping->a_ops = &pcihpfs_aops; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; switch (mode & S_IFMT) { default: @@ -154,18 +154,6 @@ return pcihpfs_mknod (dir, dentry, mode | S_IFREG, 0); } -static int pcihpfs_link (struct dentry *old_dentry, struct inode *dir, - struct dentry *dentry) -{ - struct inode *inode = old_dentry->d_inode; - - inode->i_nlink++; - atomic_inc(&inode->i_count); - dget(dentry); - d_instantiate(dentry, inode); - return 0; -} - static inline int pcihpfs_positive (struct dentry *dentry) { return dentry->d_inode && !d_unhashed(dentry); @@ -196,29 +184,15 @@ if (pcihpfs_empty(dentry)) { struct inode *inode = dentry->d_inode; + lock_kernel(); inode->i_nlink--; + unlock_kernel(); dput(dentry); error = 0; } return error; } -static int pcihpfs_rename (struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - int error = -ENOTEMPTY; - - if (pcihpfs_empty(new_dentry)) { - struct inode *inode = new_dentry->d_inode; - if (inode) { - inode->i_nlink--; - dput(new_dentry); - } - error = 0; - } - return error; -} - #define pcihpfs_rmdir pcihpfs_unlink /* default file operations */ @@ -238,6 +212,7 @@ { loff_t retval = -EINVAL; + lock_kernel(); switch(orig) { case 0: if (offset > 0) { @@ -254,6 +229,7 @@ default: break; } + unlock_kernel(); return retval; } @@ -265,18 +241,9 @@ return 0; } -static int default_sync_file (struct file *file, struct dentry *dentry, int datasync) -{ - return 0; -} - -static struct address_space_operations pcihpfs_aops = { -}; - static struct file_operations pcihpfs_dir_operations = { read: generic_read_dir, readdir: dcache_readdir, - fsync: default_sync_file, }; static struct file_operations default_file_operations = { @@ -284,8 +251,6 @@ write: default_write_file, open: default_open, llseek: default_file_lseek, - fsync: default_sync_file, - mmap: generic_file_mmap, }; /* file ops for the "power" files */ @@ -296,8 +261,6 @@ write: power_write_file, open: default_open, llseek: default_file_lseek, - fsync: default_sync_file, - mmap: generic_file_mmap, }; /* file ops for the "attention" files */ @@ -308,8 +271,6 @@ write: attention_write_file, open: default_open, llseek: default_file_lseek, - fsync: default_sync_file, - mmap: generic_file_mmap, }; /* file ops for the "latch" files */ @@ -319,8 +280,6 @@ write: default_write_file, open: default_open, llseek: default_file_lseek, - fsync: default_sync_file, - mmap: generic_file_mmap, }; /* file ops for the "presence" files */ @@ -330,8 +289,6 @@ write: default_write_file, open: default_open, llseek: default_file_lseek, - fsync: default_sync_file, - mmap: generic_file_mmap, }; /* file ops for the "test" files */ @@ -341,19 +298,15 @@ write: test_write_file, open: default_open, llseek: default_file_lseek, - fsync: default_sync_file, - mmap: generic_file_mmap, }; static struct inode_operations pcihpfs_dir_inode_operations = { create: pcihpfs_create, lookup: pcihpfs_lookup, - link: pcihpfs_link, unlink: pcihpfs_unlink, mkdir: pcihpfs_mkdir, rmdir: pcihpfs_rmdir, mknod: pcihpfs_mknod, - rename: pcihpfs_rename, }; static struct super_operations pcihpfs_ops = { @@ -485,7 +438,7 @@ if (!parent) { dbg("Ah! can not find a parent!\n"); - return -EFAULT; + return -EINVAL; } *dentry = NULL; @@ -686,7 +639,7 @@ default: err ("Illegal value specified for power\n"); - retval = -EFAULT; + retval = -EINVAL; } exit: @@ -1019,7 +972,7 @@ if (slot == NULL) return -ENODEV; if ((slot->info == NULL) || (slot->ops == NULL)) - return -EFAULT; + return -EINVAL; core = kmalloc (sizeof (struct hotplug_slot_core), GFP_KERNEL); if (!core) @@ -1030,7 +983,7 @@ if (get_slot_from_name (slot->name) != NULL) { spin_unlock (&list_lock); kfree (core); - return -EFAULT; + return -EINVAL; } slot->core_priv = core; @@ -1076,6 +1029,12 @@ return 0; } +static inline void update_inode_time (struct inode *inode) +{ + if (inode) + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; +} + /** * pci_hp_change_slot_info - changes the slot's information structure in the core * @name: the name of the slot whose info has changed @@ -1089,6 +1048,7 @@ int pci_hp_change_slot_info (const char *name, struct hotplug_slot_info *info) { struct hotplug_slot *temp; + struct hotplug_slot_core *core; if (info == NULL) return -ENODEV; @@ -1099,6 +1059,24 @@ spin_unlock (&list_lock); return -ENODEV; } + + /* + * check all fields in the info structure, and update timestamps + * for the files referring to the fields that have now changed. + */ + core = temp->core_priv; + if ((core->power_dentry) && + (temp->info->power_status != info->power_status)) + update_inode_time (core->power_dentry->d_inode); + if ((core->attention_dentry) && + (temp->info->attention_status != info->attention_status)) + update_inode_time (core->attention_dentry->d_inode); + if ((core->latch_dentry) && + (temp->info->latch_status != info->latch_status)) + update_inode_time (core->latch_dentry->d_inode); + if ((core->adapter_dentry) && + (temp->info->adapter_status != info->adapter_status)) + update_inode_time (core->adapter_dentry->d_inode); memcpy (temp->info, info, sizeof (struct hotplug_slot_info)); spin_unlock (&list_lock); diff -Nru a/drivers/ide/Config.help b/drivers/ide/Config.help --- a/drivers/ide/Config.help Thu Mar 7 18:17:46 2002 +++ b/drivers/ide/Config.help Thu Mar 7 18:17:46 2002 @@ -581,13 +581,6 @@ People with SCSI-only systems can say N here. -CONFIG_BLK_DEV_4DRIVES - Certain older chipsets, including the Tekram 690CD, use a single set - of I/O ports at 0x1f0 to control up to four drives, instead of the - customary two drives per port. Support for this can be enabled at - runtime using the "ide0=four" kernel boot parameter if you say Y - here. - CONFIG_BLK_DEV_ALI14XX This driver is enabled at runtime using the "ide0=ali14xx" kernel boot parameter. It enables support for the secondary IDE interface @@ -806,19 +799,6 @@ . If you are unsure, say N here. - -CONFIG_IDE_TASK_IOCTL - This is a direct raw access to the media. It is a complex but - elegant solution to test and validate the domain of the hardware and - perform below the driver data recovery if needed. This is the most - basic form of media-forensics. - - If you are unsure, say N here. - -CONFIG_BLK_DEV_IDEDMA_FORCED - This is an old piece of lost code from Linux 2.0 Kernels. - - Generally say N here. CONFIG_IDEDMA_ONLYDISK This is used if you know your ATAPI Devices are going to fail DMA diff -Nru a/drivers/ide/Config.in b/drivers/ide/Config.in --- a/drivers/ide/Config.in Thu Mar 7 18:17:46 2002 +++ b/drivers/ide/Config.in Thu Mar 7 18:17:46 2002 @@ -33,9 +33,7 @@ dep_tristate ' Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE dep_tristate ' SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE $CONFIG_SCSI - bool ' IDE Taskfile Access' CONFIG_IDE_TASK_IOCTL - - comment 'IDE chipset support/bugfixes' + comment 'IDE chipset support' if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then dep_bool ' CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640 $CONFIG_X86 dep_bool ' CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED $CONFIG_BLK_DEV_CMD640 @@ -47,7 +45,6 @@ bool ' Sharing PCI IDE interrupts support' CONFIG_IDEPCI_SHARE_IRQ bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD - dep_bool ' Force enable legacy 2.0.X HOSTS to use DMA' CONFIG_BLK_DEV_IDEDMA_FORCED $CONFIG_BLK_DEV_IDEDMA_PCI dep_bool ' Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO $CONFIG_BLK_DEV_IDEDMA_PCI dep_bool ' Enable DMA only for disks ' CONFIG_IDEDMA_ONLYDISK $CONFIG_IDEDMA_PCI_AUTO define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PCI @@ -143,7 +140,6 @@ bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then comment 'Note: most of these also require special kernel boot parameters' - bool ' Generic 4 drives/port support' CONFIG_BLK_DEV_4DRIVES bool ' ALI M14xx support' CONFIG_BLK_DEV_ALI14XX bool ' DTC-2278 support' CONFIG_BLK_DEV_DTC2278 bool ' Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B diff -Nru a/drivers/ide/aec62xx.c b/drivers/ide/aec62xx.c --- a/drivers/ide/aec62xx.c Thu Mar 7 18:17:42 2002 +++ b/drivers/ide/aec62xx.c Thu Mar 7 18:17:42 2002 @@ -48,7 +48,6 @@ static int aec62xx_get_info(char *, char **, off_t, int); extern int (*aec62xx_display_info)(char *, char **, off_t, int); /* ide-proc.c */ -extern char *ide_media_verbose(ide_drive_t *); static struct pci_dev *bmide_dev; static int aec62xx_get_info (char *buffer, char **addr, off_t offset, int count) @@ -203,22 +202,18 @@ static byte pci_bus_clock_list (byte speed, struct chipset_bus_clock_list_entry * chipset_table) { - int bus_speed = system_bus_clock(); - for ( ; chipset_table->xfer_speed ; chipset_table++) if (chipset_table->xfer_speed == speed) { - return ((byte) ((bus_speed <= 33) ? chipset_table->chipset_settings_33 : chipset_table->chipset_settings_34)); + return ((byte) ((system_bus_speed <= 33) ? chipset_table->chipset_settings_33 : chipset_table->chipset_settings_34)); } return 0x00; } static byte pci_bus_clock_list_ultra (byte speed, struct chipset_bus_clock_list_entry * chipset_table) { - int bus_speed = system_bus_clock(); - for ( ; chipset_table->xfer_speed ; chipset_table++) if (chipset_table->xfer_speed == speed) { - return ((byte) ((bus_speed <= 33) ? chipset_table->ultra_settings_33 : chipset_table->ultra_settings_34)); + return ((byte) ((system_bus_speed <= 33) ? chipset_table->ultra_settings_33 : chipset_table->ultra_settings_34)); } return 0x00; } @@ -314,8 +309,8 @@ unsigned long dma_base = hwif->dma_base; byte speed = -1; - if (drive->media != ide_disk) - return ((int) ide_dma_off_quietly); + if (drive->type != ATA_DISK) + return ide_dma_off_quietly; if (((id->dma_ultra & 0x0010) || (id->dma_ultra & 0x0008) || @@ -360,7 +355,7 @@ byte speed = -1; byte ultra66 = eighty_ninty_three(drive); - if (drive->media != ide_disk) + if (drive->type != ATA_DISK) return ((int) ide_dma_off_quietly); if ((id->dma_ultra & 0x0010) && (ultra) && (ultra66)) { @@ -522,11 +517,11 @@ #endif /* CONFIG_BLK_DEV_IDEDMA */ #endif /* CONFIG_AEC62XX_TUNING */ -unsigned int __init pci_init_aec62xx (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_aec62xx (struct pci_dev *dev) { if (dev->resource[PCI_ROM_RESOURCE].start) { pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); - printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start); + printk("%s: ROM enabled at 0x%08lx\n", dev->name, dev->resource[PCI_ROM_RESOURCE].start); } #if defined(DISPLAY_AEC62XX_TIMINGS) && defined(CONFIG_PROC_FS) diff -Nru a/drivers/ide/ali14xx.c b/drivers/ide/ali14xx.c --- a/drivers/ide/ali14xx.c Thu Mar 7 18:17:44 2002 +++ b/drivers/ide/ali14xx.c Thu Mar 7 18:17:44 2002 @@ -1,6 +1,4 @@ /* - * linux/drivers/ide/ali14xx.c Version 0.03 Feb 09, 1996 - * * Copyright (C) 1996 Linus Torvalds & author (see below) */ @@ -37,8 +35,6 @@ * mode 4 for a while now with no trouble.) -Derek */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #include #include #include @@ -119,15 +115,14 @@ byte param1, param2, param3, param4; unsigned long flags; ide_pio_data_t d; - int bus_speed = system_bus_clock(); pio = ide_get_best_pio_mode(drive, pio, ALI_MAX_PIO, &d); /* calculate timing, according to PIO mode */ time1 = d.cycle_time; time2 = ide_pio_timings[pio].active_time; - param3 = param1 = (time2 * bus_speed + 999) / 1000; - param4 = param2 = (time1 * bus_speed + 999) / 1000 - param1; + param3 = param1 = (time2 * system_bus_speed + 999) / 1000; + param4 = param2 = (time1 * system_bus_speed + 999) / 1000 - param1; if (pio < 3) { param3 += 8; param4 += 8; diff -Nru a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c --- a/drivers/ide/alim15x3.c Thu Mar 7 18:17:38 2002 +++ b/drivers/ide/alim15x3.c Thu Mar 7 18:17:38 2002 @@ -247,7 +247,6 @@ int s_time, a_time, c_time; byte s_clc, a_clc, r_clc; unsigned long flags; - int bus_speed = system_bus_clock(); int port = hwif->index ? 0x5c : 0x58; int portFIFO = hwif->channel ? 0x55 : 0x54; byte cd_dma_fifo = 0; @@ -255,18 +254,18 @@ pio = ide_get_best_pio_mode(drive, pio, 5, &d); s_time = ide_pio_timings[pio].setup_time; a_time = ide_pio_timings[pio].active_time; - if ((s_clc = (s_time * bus_speed + 999) / 1000) >= 8) + if ((s_clc = (s_time * system_bus_speed + 999) / 1000) >= 8) s_clc = 0; - if ((a_clc = (a_time * bus_speed + 999) / 1000) >= 8) + if ((a_clc = (a_time * system_bus_speed + 999) / 1000) >= 8) a_clc = 0; c_time = ide_pio_timings[pio].cycle_time; #if 0 - if ((r_clc = ((c_time - s_time - a_time) * bus_speed + 999) / 1000) >= 16) + if ((r_clc = ((c_time - s_time - a_time) * system_bus_speed + 999) / 1000) >= 16) r_clc = 0; #endif - if (!(r_clc = (c_time * bus_speed + 999) / 1000 - a_clc - s_clc)) { + if (!(r_clc = (c_time * system_bus_speed + 999) / 1000 - a_clc - s_clc)) { r_clc = 1; } else { if (r_clc >= 16) @@ -279,7 +278,7 @@ * PIO mode => ATA FIFO on, ATAPI FIFO off */ pci_read_config_byte(dev, portFIFO, &cd_dma_fifo); - if (drive->media==ide_disk) { + if (drive->type == ATA_DISK) { if (hwif->index) { pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0x0F) | 0x50); } else { @@ -425,9 +424,9 @@ } else if ((m5229_revision < 0xC2) && #ifndef CONFIG_WDC_ALI15X3 ((chip_is_1543c_e && strstr(id->model, "WDC ")) || - (drive->media!=ide_disk))) { + (drive->type != ATA_DISK))) { #else /* CONFIG_WDC_ALI15X3 */ - (drive->media!=ide_disk)) { + (drive->type != ATA_DISK)) { #endif /* CONFIG_WDC_ALI15X3 */ return 0; } else { @@ -442,7 +441,7 @@ ide_dma_action_t dma_func = ide_dma_on; byte can_ultra_dma = ali15x3_can_ultra(drive); - if ((m5229_revision<=0x20) && (drive->media!=ide_disk)) + if ((m5229_revision<=0x20) && (drive->type != ATA_DISK)) return hwif->dmaproc(ide_dma_off_quietly, drive); if ((id != NULL) && ((id->capability & 1) != 0) && hwif->autodma) { @@ -495,7 +494,7 @@ case ide_dma_check: return ali15x3_config_drive_for_dma(drive); case ide_dma_write: - if ((m5229_revision < 0xC2) && (drive->media != ide_disk)) + if ((m5229_revision < 0xC2) && (drive->type != ATA_DISK)) return 1; /* try PIO instead of DMA */ break; default: @@ -505,7 +504,7 @@ } #endif /* CONFIG_BLK_DEV_IDEDMA */ -unsigned int __init pci_init_ali15x3 (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_ali15x3(struct pci_dev *dev) { unsigned long fixdma_base = pci_resource_start(dev, 4); @@ -524,7 +523,7 @@ outb(inb(fixdma_base+2) & 0x60, fixdma_base+2); if (inb(fixdma_base+2) & 0x80) - printk("%s: simplex device: DMA will fail!!\n", name); + printk("%s: simplex device: DMA will fail!!\n", dev->name); } #if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) diff -Nru a/drivers/ide/amd74xx.c b/drivers/ide/amd74xx.c --- a/drivers/ide/amd74xx.c Thu Mar 7 18:17:45 2002 +++ b/drivers/ide/amd74xx.c Thu Mar 7 18:17:45 2002 @@ -34,7 +34,6 @@ static int amd74xx_get_info(char *, char **, off_t, int); extern int (*amd74xx_display_info)(char *, char **, off_t, int); /* ide-proc.c */ -extern char *ide_media_verbose(ide_drive_t *); static struct pci_dev *bmide_dev; static int amd74xx_get_info (char *buffer, char **addr, off_t offset, int count) @@ -406,13 +405,13 @@ } #endif /* CONFIG_BLK_DEV_IDEDMA */ -unsigned int __init pci_init_amd74xx (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_amd74xx(struct pci_dev *dev) { unsigned long fixdma_base = pci_resource_start(dev, 4); #ifdef CONFIG_BLK_DEV_IDEDMA if (!amd74xx_swdma_check(dev)) - printk("%s: disabling single-word DMA support (revision < C4)\n", name); + printk("%s: disabling single-word DMA support (revision < C4)\n", dev->name); #endif /* CONFIG_BLK_DEV_IDEDMA */ if (!fixdma_base) { @@ -426,7 +425,7 @@ outb(inb(fixdma_base+2) & 0x60, fixdma_base+2); if (inb(fixdma_base+2) & 0x80) - printk("%s: simplex device: DMA will fail!!\n", name); + printk("%s: simplex device: DMA will fail!!\n", dev->name); } #if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS) if (!amd74xx_proc) { diff -Nru a/drivers/ide/ataraid.c b/drivers/ide/ataraid.c --- a/drivers/ide/ataraid.c Thu Mar 7 18:17:44 2002 +++ b/drivers/ide/ataraid.c Thu Mar 7 18:17:44 2002 @@ -70,7 +70,7 @@ static int ataraid_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int minor; - minor = MINOR(inode->i_rdev)>>SHIFT; + minor = minor(inode->i_rdev)>>SHIFT; if ((ataraid_ops[minor])&&(ataraid_ops[minor]->ioctl)) return (ataraid_ops[minor]->ioctl)(inode,file,cmd,arg); @@ -80,7 +80,7 @@ static int ataraid_open(struct inode * inode, struct file * filp) { int minor; - minor = MINOR(inode->i_rdev)>>SHIFT; + minor = minor(inode->i_rdev)>>SHIFT; if ((ataraid_ops[minor])&&(ataraid_ops[minor]->open)) return (ataraid_ops[minor]->open)(inode,filp); @@ -91,7 +91,7 @@ static int ataraid_release(struct inode * inode, struct file * filp) { int minor; - minor = MINOR(inode->i_rdev)>>SHIFT; + minor = minor(inode->i_rdev)>>SHIFT; if ((ataraid_ops[minor])&&(ataraid_ops[minor]->release)) return (ataraid_ops[minor]->release)(inode,filp); @@ -102,7 +102,7 @@ { int minor; int retval; - minor = MINOR(bh->b_rdev)>>SHIFT; + minor = minor(bh->b_rdev)>>SHIFT; if ((ataraid_ops[minor])&&(ataraid_ops[minor]->make_request)) { @@ -229,7 +229,7 @@ void ataraid_register_disk(int device,long size) { - register_disk(&ataraid_gendisk, MKDEV(ATAMAJOR,16*device),16, + register_disk(&ataraid_gendisk, mk_kdev(ATAMAJOR,16*device),16, &ataraid_fops,size); } diff -Nru a/drivers/ide/cmd640.c b/drivers/ide/cmd640.c --- a/drivers/ide/cmd640.c Thu Mar 7 18:17:43 2002 +++ b/drivers/ide/cmd640.c Thu Mar 7 18:17:43 2002 @@ -1,12 +1,10 @@ /* - * linux/drivers/ide/cmd640.c Version 1.02 Sep 01, 1996 - * * Copyright (C) 1995-1996 Linus Torvalds & authors (see below) */ /* * Original authors: abramov@cecmow.enet.dec.com (Igor Abramov) - * mlord@pobox.com (Mark Lord) + * mlord@pobox.com (Mark Lord) * * See linux/MAINTAINERS for address of current maintainer. * @@ -23,7 +21,7 @@ * * A.Hartgers@stud.tue.nl, JZDQC@CUNYVM.CUNY.edu, abramov@cecmow.enet.dec.com, * bardj@utopia.ppp.sn.no, bart@gaga.tue.nl, bbol001@cs.auckland.ac.nz, - * chrisc@dbass.demon.co.uk, dalecki@namu26.Num.Math.Uni-Goettingen.de, + * chrisc@dbass.demon.co.uk, dalecki@evision-ventures.com, * derekn@vw.ece.cmu.edu, florian@btp2x3.phy.uni-bayreuth.de, * flynn@dei.unipd.it, gadio@netvision.net.il, godzilla@futuris.net, * j@pobox.com, jkemp1@mises.uni-paderborn.de, jtoppe@hiwaay.net, @@ -98,7 +96,6 @@ * (patch courtesy of Zoltan Hidvegi) */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ #define CMD640_PREFETCH_MASKS 1 #include @@ -596,14 +593,13 @@ { int setup_time, active_time, recovery_time, clock_time; byte setup_count, active_count, recovery_count, recovery_count2, cycle_count; - int bus_speed = system_bus_clock(); if (pio_mode > 5) pio_mode = 5; setup_time = ide_pio_timings[pio_mode].setup_time; active_time = ide_pio_timings[pio_mode].active_time; recovery_time = cycle_time - (setup_time + active_time); - clock_time = 1000 / bus_speed; + clock_time = 1000 / system_bus_speed; cycle_count = (cycle_time + clock_time - 1) / clock_time; setup_count = (setup_time + clock_time - 1) / clock_time; diff -Nru a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c --- a/drivers/ide/cmd64x.c Thu Mar 7 18:17:42 2002 +++ b/drivers/ide/cmd64x.c Thu Mar 7 18:17:42 2002 @@ -88,7 +88,6 @@ static int cmd64x_get_info(char *, char **, off_t, int); static int cmd680_get_info(char *, char **, off_t, int); extern int (*cmd64x_display_info)(char *, char **, off_t, int); /* ide-proc.c */ -extern char *ide_media_verbose(ide_drive_t *); static struct pci_dev *bmide_dev; static int cmd64x_get_info (char *buffer, char **addr, off_t offset, int count) @@ -239,7 +238,7 @@ */ if (channel) { drive->drive_data = setup_count; - setup_count = IDE_MAX(drives[0].drive_data, drives[1].drive_data); + setup_count = max(drives[0].drive_data, drives[1].drive_data); cmdprintk("Secondary interface, setup_count = %d\n", setup_count); } @@ -283,8 +282,6 @@ int setup_time, active_time, recovery_time, clock_time, pio_mode, cycle_time; byte recovery_count2, cycle_count; int setup_count, active_count, recovery_count; - int bus_speed = system_bus_clock(); - /*byte b;*/ ide_pio_data_t d; switch (mode_wanted) { @@ -309,7 +306,7 @@ setup_time = ide_pio_timings[pio_mode].setup_time; active_time = ide_pio_timings[pio_mode].active_time; recovery_time = cycle_time - (setup_time + active_time); - clock_time = 1000 / bus_speed; + clock_time = 1000 / system_bus_speed; cycle_count = (cycle_time + clock_time - 1) / clock_time; setup_count = (setup_time + clock_time - 1) / clock_time; @@ -450,7 +447,8 @@ u8 regU = 0; u8 regD = 0; - if ((drive->media != ide_disk) && (speed < XFER_SW_DMA_0)) return 1; + if ((drive->type != ATA_DISK) && (speed < XFER_SW_DMA_0)) + return 1; (void) pci_read_config_byte(dev, pciD, ®D); (void) pci_read_config_byte(dev, pciU, ®U); @@ -643,8 +641,8 @@ break; } - if (drive->media != ide_disk) { - cmdprintk("CMD64X: drive->media != ide_disk at double check, inital check failed!!\n"); + if (drive->type != ATA_DISK) { + cmdprintk("CMD64X: drive is not a disk at double check, inital check failed!!\n"); return ((int) ide_dma_off); } @@ -790,7 +788,7 @@ } if ((id != NULL) && ((id->capability & 1) != 0) && - hwif->autodma && (drive->media == ide_disk)) { + hwif->autodma && (drive->type == ATA_DISK)) { /* Consult the list of known "bad" drives */ if (ide_dmaproc(ide_dma_bad_drive, drive)) { dma_func = ide_dma_off; @@ -956,7 +954,7 @@ return 0; } -void cmd680_reset (ide_drive_t *drive) +static void cmd680_reset (ide_drive_t *drive) { #if 0 ide_hwif_t *hwif = HWIF(drive); @@ -968,9 +966,9 @@ #endif } -unsigned int cmd680_pci_init (struct pci_dev *dev, const char *name) +static unsigned int cmd680_pci_init(struct pci_dev *dev) { - u8 tmpbyte = 0; + u8 tmpbyte = 0; pci_write_config_byte(dev, 0x80, 0x00); pci_write_config_byte(dev, 0x84, 0x00); pci_read_config_byte(dev, 0x8A, &tmpbyte); @@ -994,7 +992,7 @@ return 0; } -unsigned int cmd64x_pci_init (struct pci_dev *dev, const char *name) +static unsigned int cmd64x_pci_init(struct pci_dev *dev) { unsigned char mrdmode; unsigned int class_rev; @@ -1005,7 +1003,7 @@ #ifdef __i386__ if (dev->resource[PCI_ROM_RESOURCE].start) { pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); - printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start); + printk("%s: ROM enabled at 0x%08lx\n", dev->name, dev->resource[PCI_ROM_RESOURCE].start); } #endif @@ -1013,7 +1011,7 @@ case PCI_DEVICE_ID_CMD_643: break; case PCI_DEVICE_ID_CMD_646: - printk("%s: chipset revision 0x%02X, ", name, class_rev); + printk("%s: chipset revision 0x%02X, ", dev->name, class_rev); switch(class_rev) { case 0x07: case 0x05: @@ -1083,11 +1081,11 @@ return 0; } -unsigned int __init pci_init_cmd64x (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_cmd64x(struct pci_dev *dev) { if (dev->device == PCI_DEVICE_ID_CMD_680) - return cmd680_pci_init (dev, name); - return cmd64x_pci_init (dev, name); + return cmd680_pci_init (dev); + return cmd64x_pci_init (dev); } unsigned int cmd680_ata66 (ide_hwif_t *hwif) diff -Nru a/drivers/ide/cs5530.c b/drivers/ide/cs5530.c --- a/drivers/ide/cs5530.c Thu Mar 7 18:17:45 2002 +++ b/drivers/ide/cs5530.c Thu Mar 7 18:17:45 2002 @@ -37,7 +37,6 @@ static int cs5530_get_info(char *, char **, off_t, int); extern int (*cs5530_display_info)(char *, char **, off_t, int); /* ide-proc.c */ -extern char *ide_media_verbose(ide_drive_t *); static struct pci_dev *bmide_dev; static int cs5530_get_info (char *buffer, char **addr, off_t offset, int count) @@ -251,7 +250,7 @@ /* * Initialize the cs5530 bridge for reliable IDE DMA operation. */ -unsigned int __init pci_init_cs5530 (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_cs5530(struct pci_dev *dev) { struct pci_dev *master_0 = NULL, *cs5530_0 = NULL; unsigned short pcicmd = 0; @@ -278,11 +277,11 @@ } } if (!master_0) { - printk("%s: unable to locate PCI MASTER function\n", name); + printk("%s: unable to locate PCI MASTER function\n", dev->name); return 0; } if (!cs5530_0) { - printk("%s: unable to locate CS5530 LEGACY function\n", name); + printk("%s: unable to locate CS5530 LEGACY function\n", dev->name); return 0; } diff -Nru a/drivers/ide/cy82c693.c b/drivers/ide/cy82c693.c --- a/drivers/ide/cy82c693.c Thu Mar 7 18:17:40 2002 +++ b/drivers/ide/cy82c693.c Thu Mar 7 18:17:40 2002 @@ -141,7 +141,6 @@ static void compute_clocks (byte pio, pio_clocks_t *p_pclk) { int clk1, clk2; - int bus_speed = system_bus_clock(); /* get speed of PCI bus */ /* we don't check against CY82C693's min and max speed, * so you can play with the idebus=xx parameter @@ -151,17 +150,17 @@ pio = CY82C693_MAX_PIO; /* let's calc the address setup time clocks */ - p_pclk->address_time = (byte)calc_clk(ide_pio_timings[pio].setup_time, bus_speed); + p_pclk->address_time = (byte)calc_clk(ide_pio_timings[pio].setup_time, system_bus_speed); /* let's calc the active and recovery time clocks */ - clk1 = calc_clk(ide_pio_timings[pio].active_time, bus_speed); + clk1 = calc_clk(ide_pio_timings[pio].active_time, system_bus_speed); /* calc recovery timing */ clk2 = ide_pio_timings[pio].cycle_time - ide_pio_timings[pio].active_time - ide_pio_timings[pio].setup_time; - clk2 = calc_clk(clk2, bus_speed); + clk2 = calc_clk(clk2, system_bus_speed); clk1 = (clk1<<4)|clk2; /* combine active and recovery clocks */ @@ -384,7 +383,7 @@ * the device prior to INIT. */ -unsigned int __init pci_init_cy82c693(struct pci_dev *dev, const char *name) +unsigned int __init pci_init_cy82c693(struct pci_dev *dev) { #ifdef CY82C693_SETDMA_CLOCK byte data; @@ -400,7 +399,7 @@ data = IN_BYTE(CY82_DATA_PORT); #if CY82C693_DEBUG_INFO - printk (KERN_INFO "%s: Peripheral Configuration Register: 0x%X\n", name, data); + printk (KERN_INFO "%s: Peripheral Configuration Register: 0x%X\n", dev->name, data); #endif /* CY82C693_DEBUG_INFO */ /* @@ -421,7 +420,7 @@ OUT_BYTE(data, CY82_DATA_PORT); #if CY82C693_DEBUG_INFO - printk (KERN_INFO "%s: New Peripheral Configuration Register: 0x%X\n", name, data); + printk (KERN_INFO "%s: New Peripheral Configuration Register: 0x%X\n", dev->name, data); #endif /* CY82C693_DEBUG_INFO */ #endif /* CY82C693_SETDMA_CLOCK */ @@ -434,7 +433,7 @@ void __init ide_init_cy82c693(ide_hwif_t *hwif) { hwif->chipset = ide_cy82c693; - hwif->tuneproc = &cy82c693_tune_drive; + hwif->tuneproc = cy82c693_tune_drive; hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; hwif->autodma = 0; @@ -442,7 +441,7 @@ #ifdef CONFIG_BLK_DEV_IDEDMA if (hwif->dma_base) { hwif->highmem = 1; - hwif->dmaproc = &cy82c693_dmaproc; + hwif->dmaproc = cy82c693_dmaproc; if (!noautodma) hwif->autodma = 1; } diff -Nru a/drivers/ide/dtc2278.c b/drivers/ide/dtc2278.c --- a/drivers/ide/dtc2278.c Thu Mar 7 18:17:44 2002 +++ b/drivers/ide/dtc2278.c Thu Mar 7 18:17:44 2002 @@ -1,10 +1,6 @@ /* - * linux/drivers/ide/dtc2278.c Version 0.02 Feb 10, 1996 - * * Copyright (C) 1996 Linus Torvalds & author (see below) */ - -#undef REALLY_SLOW_IO /* most systems can safely undef this */ #include #include diff -Nru a/drivers/ide/hd.c b/drivers/ide/hd.c --- a/drivers/ide/hd.c Thu Mar 7 18:17:39 2002 +++ b/drivers/ide/hd.c Thu Mar 7 18:17:39 2002 @@ -558,7 +558,7 @@ reset_hd(); return; } - dev = MINOR(CURRENT->rq_dev); + dev = minor(CURRENT->rq_dev); block = CURRENT->sector; nsect = CURRENT->nr_sectors; if (dev >= (NR_HD<<6) || (dev & 0x3f) || @@ -568,7 +568,7 @@ kdevname(CURRENT->rq_dev)); else printk("hd%c: bad access: block=%d, count=%d\n", - (MINOR(CURRENT->rq_dev)>>6)+'a', block, nsect); + (minor(CURRENT->rq_dev)>>6)+'a', block, nsect); end_request(0); goto repeat; } @@ -626,7 +626,7 @@ struct hd_geometry *loc = (struct hd_geometry *) arg; int dev; - if ((!inode) || !(inode->i_rdev)) + if ((!inode) || kdev_none(inode->i_rdev)) return -EINVAL; dev = DEVICE_NR(inode->i_rdev); if (dev >= NR_HD) @@ -654,7 +654,7 @@ case BLKROGET: case BLKFLSBUF: case BLKPG: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); default: return -EINVAL; @@ -822,7 +822,7 @@ hd_gendisk.nr_real = NR_HD; for(drive=0; drive < NR_HD; drive++) - register_disk(&hd_gendisk, MKDEV(MAJOR_NR,drive<<6), 1<<6, + register_disk(&hd_gendisk, mk_kdev(MAJOR_NR,drive<<6), 1<<6, &hd_fops, hd_info[drive].head * hd_info[drive].sect * hd_info[drive].cyl); } diff -Nru a/drivers/ide/hpt34x.c b/drivers/ide/hpt34x.c --- a/drivers/ide/hpt34x.c Thu Mar 7 18:17:38 2002 +++ b/drivers/ide/hpt34x.c Thu Mar 7 18:17:38 2002 @@ -56,7 +56,6 @@ static int hpt34x_get_info(char *, char **, off_t, int); extern int (*hpt34x_display_info)(char *, char **, off_t, int); /* ide-proc.c */ -extern char *ide_media_verbose(ide_drive_t *); static struct pci_dev *bmide_dev; static int hpt34x_get_info (char *buffer, char **addr, off_t offset, int count) @@ -210,7 +209,7 @@ struct hd_driveid *id = drive->id; byte speed = 0x00; - if (drive->media != ide_disk) + if (drive->type != ATA_DISK) return ((int) ide_dma_off_quietly); hpt34x_clear_chipset(drive); @@ -333,7 +332,7 @@ outb(reading, dma_base); /* specify r/w */ outb(inb(dma_base+2)|6, dma_base+2); /* clear INTR & ERROR flags */ drive->waiting_for_dma = 1; - if (drive->media != ide_disk) + if (drive->type != ATA_DISK) return 0; ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); /* issue cmd to drive */ OUT_BYTE((reading == 9) ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); @@ -357,7 +356,7 @@ */ #define HPT34X_PCI_INIT_REG 0x80 -unsigned int __init pci_init_hpt34x (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_hpt34x(struct pci_dev *dev) { int i = 0; unsigned long hpt34xIoBase = pci_resource_start(dev, 4); diff -Nru a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c --- a/drivers/ide/hpt366.c Thu Mar 7 18:17:39 2002 +++ b/drivers/ide/hpt366.c Thu Mar 7 18:17:39 2002 @@ -355,7 +355,6 @@ #if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) static int hpt366_get_info(char *, char **, off_t, int); extern int (*hpt366_display_info)(char *, char **, off_t, int); /* ide-proc.c */ -extern char *ide_media_verbose(ide_drive_t *); static int hpt366_get_info (char *buffer, char **addr, off_t offset, int count) { @@ -375,7 +374,8 @@ class_rev &= 0xff; p += sprintf(p, "\nController: %d\n", i); - p += sprintf(p, "Chipset: HPT%s\n", chipset_nums[class_rev]); + p += sprintf(p, "Chipset: HPT%s\n", + class_rev < sizeof(chipset_nums) / sizeof(char *) ? chipset_nums[class_rev] : "???"); p += sprintf(p, "--------------- Primary Channel " "--------------- Secondary Channel " "--------------\n"); @@ -579,7 +579,7 @@ static int hpt3xx_tune_chipset (ide_drive_t *drive, byte speed) { - if ((drive->media != ide_disk) && (speed < XFER_SW_DMA_0)) + if ((drive->type != ATA_DISK) && (speed < XFER_SW_DMA_0)) return -1; if (!drive->init_speed) @@ -664,7 +664,7 @@ byte ultra66 = eighty_ninty_three(drive); int rval; - if ((drive->media != ide_disk) && (speed < XFER_SW_DMA_0)) + if ((drive->type != ATA_DISK) && (speed < XFER_SW_DMA_0)) return ((int) ide_dma_off_quietly); if ((id->dma_ultra & 0x0020) && @@ -1097,7 +1097,7 @@ udelay(100); } -unsigned int __init pci_init_hpt366 (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_hpt366(struct pci_dev *dev) { byte test = 0; @@ -1120,12 +1120,11 @@ if (test != 0x08) pci_write_config_byte(dev, PCI_MAX_LAT, 0x08); - if (pci_rev_check_hpt3xx(dev)) { + if (pci_rev_check_hpt3xx(dev)) init_hpt370(dev); + + if (n_hpt_devs < HPT366_MAX_DEVS) hpt_devs[n_hpt_devs++] = dev; - } else { - hpt_devs[n_hpt_devs++] = dev; - } #if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) if (!hpt366_proc) { diff -Nru a/drivers/ide/hptraid.c b/drivers/ide/hptraid.c --- a/drivers/ide/hptraid.c Thu Mar 7 18:17:45 2002 +++ b/drivers/ide/hptraid.c Thu Mar 7 18:17:45 2002 @@ -75,16 +75,16 @@ unsigned char val; unsigned long sectors; - if (!inode || !inode->i_rdev) + if (!inode || kdev_none(inode->i_rdev)) return -EINVAL; - minor = MINOR(inode->i_rdev)>>SHIFT; + minor = minor(inode->i_rdev)>>SHIFT; switch (cmd) { case BLKGETSIZE: /* Return device size */ if (!arg) return -EINVAL; - sectors = ataraid_gendisk.part[MINOR(inode->i_rdev)].nr_sects; - if (MINOR(inode->i_rdev)&15) + sectors = ataraid_gendisk.part[minor(inode->i_rdev)].nr_sects; + if (minor(inode->i_rdev)&15) return put_user(sectors, (unsigned long *) arg); return put_user(raid[minor].sectors , (unsigned long *) arg); break; @@ -102,7 +102,7 @@ if (put_user(val, (byte *) &loc->sectors)) return -EFAULT; bios_cyl = raid[minor].sectors/63/255; if (put_user(bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT; - if (put_user((unsigned)ataraid_gendisk.part[MINOR(inode->i_rdev)].start_sect, + if (put_user((unsigned)ataraid_gendisk.part[minor(inode->i_rdev)].start_sect, (unsigned long *) &loc->start)) return -EFAULT; return 0; } @@ -118,7 +118,7 @@ if (put_user(val, (byte *) &loc->sectors)) return -EFAULT; bios_cyl = raid[minor].sectors/63/255; if (put_user(bios_cyl, (unsigned int *) &loc->cylinders)) return -EFAULT; - if (put_user((unsigned)ataraid_gendisk.part[MINOR(inode->i_rdev)].start_sect, + if (put_user((unsigned)ataraid_gendisk.part[minor(inode->i_rdev)].start_sect, (unsigned long *) &loc->start)) return -EFAULT; return 0; } @@ -126,7 +126,7 @@ case BLKROSET: case BLKROGET: case BLKSSZGET: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); default: return -EINVAL; @@ -167,7 +167,7 @@ /* Partitions need adding of the start sector of the partition to the requested sector */ - rsect += ataraid_gendisk.part[MINOR(bh->b_rdev)].start_sect; + rsect += ataraid_gendisk.part[minor(bh->b_rdev)].start_sect; /* Woops we need to split the request to avoid crossing a stride barrier */ if ((rsect/thisraid->stride) != ((rsect+(bh->b_size/512)-1)/thisraid->stride)) { @@ -248,7 +248,7 @@ kdev_t dev; ide_drive_t *ideinfo; - dev = MKDEV(major,minor); + dev = mk_kdev(major,minor); ideinfo = get_info_ptr (dev); if (ideinfo==NULL) return 0; @@ -268,7 +268,7 @@ static void __init probedisk(int major, int minor,int device) { int i; - struct block_device *bdev = bdget(MKDEV(major,minor)); + struct block_device *bdev = bdget(mk_kdev(major,minor)); struct gendisk *gd; if (!bdev) @@ -301,14 +301,14 @@ /* now blank the /proc/partitions table for the wrong partition table, so that scripts don't accidentally mount it and crash the kernel */ /* XXX: the 0 is an utter hack --hch */ - gd=get_gendisk(MKDEV(major, 0)); + gd=get_gendisk(mk_kdev(major, 0)); if (gd!=NULL) { int j; for (j=1+(minor<minor_shift);j<((minor+1)<minor_shift);j++) gd->part[j].nr_sects=0; } - raid[device].disk[i].device = MKDEV(major,minor); + raid[device].disk[i].device = mk_kdev(major,minor); raid[device].disk[i].sectors = maxsectors(major,minor); raid[device].stride = (1< #include #include @@ -143,7 +136,7 @@ if (select != current_select || timing != current_timing) { current_select = select; current_timing = timing; - if (drive->media != ide_disk || !drive->present) + if (drive->type != ATA_DISK || !drive->present) select |= HT_PREFETCH_MODE; (void) inb(HT_CONFIG_PORT); (void) inb(HT_CONFIG_PORT); @@ -207,7 +200,6 @@ int active_time, recovery_time; int active_cycles, recovery_cycles; ide_pio_data_t d; - int bus_speed = system_bus_clock(); if (pio) { pio = ide_get_best_pio_mode(drive, pio, 5, &d); @@ -224,8 +216,8 @@ /* * Cycle times should be Vesa bus cycles */ - active_cycles = (active_time * bus_speed + 999) / 1000; - recovery_cycles = (recovery_time * bus_speed + 999) / 1000; + active_cycles = (active_time * system_bus_speed + 999) / 1000; + recovery_cycles = (recovery_time * system_bus_speed + 999) / 1000; /* * Upper and lower limits */ diff -Nru a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c --- a/drivers/ide/ide-cd.c Thu Mar 7 18:17:43 2002 +++ b/drivers/ide/ide-cd.c Thu Mar 7 18:17:43 2002 @@ -1633,6 +1633,7 @@ return startstop; } +#define IDE_LARGE_SEEK(b1,b2,t) (((b1) > (b2) + (t)) || ((b2) > (b1) + (t))) /**************************************************************************** * cdrom driver request routine. @@ -2906,85 +2907,41 @@ return 0; } -int ide_cdrom_init(void); -int ide_cdrom_reinit (ide_drive_t *drive); - -static ide_driver_t ide_cdrom_driver = { - name: "ide-cdrom", - media: ide_cdrom, - busy: 0, - supports_dma: 1, - supports_dsc_overlap: 1, +static struct ata_operations ide_cdrom_driver = { + owner: THIS_MODULE, cleanup: ide_cdrom_cleanup, standby: NULL, - flushcache: NULL, do_request: ide_do_rw_cdrom, end_request: NULL, ioctl: ide_cdrom_ioctl, open: ide_cdrom_open, release: ide_cdrom_release, - media_change: ide_cdrom_check_media_change, + check_media_change: ide_cdrom_check_media_change, revalidate: ide_cdrom_revalidate, pre_reset: NULL, capacity: ide_cdrom_capacity, special: NULL, - proc: NULL, - driver_init: ide_cdrom_init, - driver_reinit: ide_cdrom_reinit, + proc: NULL }; /* options */ -char *ignore = NULL; +static char *ignore = NULL; MODULE_PARM(ignore, "s"); MODULE_DESCRIPTION("ATAPI CD-ROM Driver"); -int ide_cdrom_reinit (ide_drive_t *drive) -{ - struct cdrom_info *info; - int failed = 0; - - MOD_INC_USE_COUNT; - info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL); - if (info == NULL) { - printk ("%s: Can't allocate a cdrom structure\n", drive->name); - return 1; - } - if (ide_register_subdriver (drive, &ide_cdrom_driver)) { - printk ("%s: Failed to register the driver with ide.c\n", drive->name); - kfree (info); - return 1; - } - memset (info, 0, sizeof (struct cdrom_info)); - drive->driver_data = info; - DRIVER(drive)->busy++; - if (ide_cdrom_setup (drive)) { - DRIVER(drive)->busy--; - if (ide_cdrom_cleanup (drive)) - printk ("%s: ide_cdrom_cleanup failed in ide_cdrom_init\n", drive->name); - return 1; - } - DRIVER(drive)->busy--; - failed--; - - ide_register_module(&ide_cdrom_driver); - MOD_DEC_USE_COUNT; - return 0; -} - static void __exit ide_cdrom_exit(void) { ide_drive_t *drive; int failed = 0; - while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, &ide_cdrom_driver, failed)) != NULL) + while ((drive = ide_scan_devices(ATA_ROM, "ide-cdrom", &ide_cdrom_driver, failed)) != NULL) if (ide_cdrom_cleanup (drive)) { printk ("%s: cleanup_module() called while still busy\n", drive->name); failed++; } - ide_unregister_module (&ide_cdrom_driver); } - + int ide_cdrom_init(void) { ide_drive_t *drive; @@ -2992,7 +2949,7 @@ int failed = 0; MOD_INC_USE_COUNT; - while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, NULL, failed++)) != NULL) { + while ((drive = ide_scan_devices (ATA_ROM, "ide-cdrom", NULL, failed++)) != NULL) { /* skip drives that we were told to ignore */ if (ignore != NULL) { if (strstr(ignore, drive->name)) { @@ -3016,17 +2973,20 @@ } memset (info, 0, sizeof (struct cdrom_info)); drive->driver_data = info; - DRIVER(drive)->busy++; + + /* ATA-PATTERN */ + ata_ops(drive)->busy++; if (ide_cdrom_setup (drive)) { - DRIVER(drive)->busy--; + ata_ops(drive)->busy--; if (ide_cdrom_cleanup (drive)) printk ("%s: ide_cdrom_cleanup failed in ide_cdrom_init\n", drive->name); continue; } - DRIVER(drive)->busy--; + ata_ops(drive)->busy--; + failed--; } - ide_register_module(&ide_cdrom_driver); + revalidate_drives(); MOD_DEC_USE_COUNT; return 0; } diff -Nru a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c --- a/drivers/ide/ide-disk.c Thu Mar 7 18:17:36 2002 +++ b/drivers/ide/ide-disk.c Thu Mar 7 18:17:36 2002 @@ -1,10 +1,6 @@ /* - * linux/drivers/ide/ide-disk.c Version 1.13 Nov 28, 2001 - * * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) - */ - -/* + * * Mostly written by Mark Lord * and Gadi Oxman * and Andre Hedrick @@ -34,8 +30,6 @@ #define IDEDISK_VERSION "1.13" -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #include #include #include @@ -351,7 +345,19 @@ return 0; } -static int do_idedisk_flushcache(ide_drive_t *drive); +static int idedisk_flushcache(ide_drive_t *drive) +{ + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + if (drive->id->cfs_enable_2 & 0x2400) { + taskfile.command = WIN_FLUSH_CACHE_EXT; + } else { + taskfile.command = WIN_FLUSH_CACHE; + } + return ide_wait_taskfile(drive, &taskfile, &hobfile, NULL); +} static void idedisk_release (struct inode *inode, struct file *filp, ide_drive_t *drive) { @@ -367,20 +373,16 @@ drive->doorlocking = 0; } if ((drive->id->cfs_enable_2 & 0x3000) && drive->wcache) - if (do_idedisk_flushcache(drive)) + if (idedisk_flushcache(drive)) printk (KERN_INFO "%s: Write Cache FAILED Flushing!\n", drive->name); MOD_DEC_USE_COUNT; } -static int idedisk_media_change (ide_drive_t *drive) -{ - return drive->removable; /* if removable, always assume it was changed */ -} - -static void idedisk_revalidate (ide_drive_t *drive) +static int idedisk_check_media_change (ide_drive_t *drive) { - ide_revalidate_drive(drive); + /* if removable, always assume it was changed */ + return drive->removable; } /* @@ -712,7 +714,7 @@ taskfile.low_cylinder = SMART_LCYL_PASS; taskfile.high_cylinder = SMART_HCYL_PASS; taskfile.command = WIN_SMART; - (void) smart_enable(drive); + smart_enable(drive); return ide_wait_taskfile(drive, &taskfile, &hobfile, buf); } @@ -727,7 +729,7 @@ taskfile.low_cylinder = SMART_LCYL_PASS; taskfile.high_cylinder = SMART_HCYL_PASS; taskfile.command = WIN_SMART; - (void) smart_enable(drive); + smart_enable(drive); return ide_wait_taskfile(drive, &taskfile, &hobfile, buf); } @@ -836,12 +838,12 @@ if (!(drive->id->cfs_enable_2 & 0x3000)) return 1; - (void) ide_wait_taskfile(drive, &taskfile, &hobfile, NULL); + ide_wait_taskfile(drive, &taskfile, &hobfile, NULL); drive->wcache = arg; return 0; } -static int do_idedisk_standby (ide_drive_t *drive) +static int idedisk_standby (ide_drive_t *drive) { struct hd_drive_task_hdr taskfile; struct hd_drive_hob_hdr hobfile; @@ -851,20 +853,6 @@ return ide_wait_taskfile(drive, &taskfile, &hobfile, NULL); } -static int do_idedisk_flushcache (ide_drive_t *drive) -{ - struct hd_drive_task_hdr taskfile; - struct hd_drive_hob_hdr hobfile; - memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); - memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); - if (drive->id->cfs_enable_2 & 0x2400) { - taskfile.command = WIN_FLUSH_CACHE_EXT; - } else { - taskfile.command = WIN_FLUSH_CACHE; - } - return ide_wait_taskfile(drive, &taskfile, &hobfile, NULL); -} - static int set_acoustic (ide_drive_t *drive, int arg) { struct hd_drive_task_hdr taskfile; @@ -876,7 +864,7 @@ taskfile.sector_count = arg; taskfile.command = WIN_SETFEATURES; - (void) ide_wait_taskfile(drive, &taskfile, &hobfile, NULL); + ide_wait_taskfile(drive, &taskfile, &hobfile, NULL); drive->acoustic = arg; return 0; } @@ -915,13 +903,22 @@ ide_add_setting(drive, "max_failures", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->max_failures, NULL); } -static void idedisk_setup (ide_drive_t *drive) +/* This is just a hook for the overall driver tree. + * + * FIXME: This is soon goig to replace the custom linked list games played up + * to great extend between the different components of the IDE drivers. + */ + +static struct device_driver idedisk_devdrv = {}; + +static void idedisk_setup(ide_drive_t *drive) { int i; - + struct hd_driveid *id = drive->id; unsigned long capacity; - + int drvid = -1; + idedisk_add_settings(drive); if (id == NULL) @@ -933,7 +930,7 @@ */ if (drive->removable && !drive_is_flashcard(drive)) { /* - * Removable disks (eg. SYQUEST); ignore 'WD' drives + * Removable disks (eg. SYQUEST); ignore 'WD' drives. */ if (id->model[0] != 'W' || id->model[1] != 'D') { drive->doorlocking = 1; @@ -942,13 +939,26 @@ for (i = 0; i < MAX_DRIVES; ++i) { ide_hwif_t *hwif = HWIF(drive); - if (drive != &hwif->drives[i]) continue; + if (drive != &hwif->drives[i]) + continue; + drvid = i; hwif->gd->de_arr[i] = drive->de; if (drive->removable) hwif->gd->flags[i] |= GENHD_FL_REMOVABLE; break; } + /* Register us within the device tree. + */ + + if (drvid != -1) { + sprintf(drive->device.bus_id, "%d", drvid); + sprintf(drive->device.name, "ide-disk"); + drive->device.driver = &idedisk_devdrv; + drive->device.parent = &HWIF(drive)->device; + device_register(&drive->device); + } + /* Extract geometry if we did not already have one for the drive */ if (!drive->cyl || !drive->head || !drive->sect) { drive->cyl = drive->bios_cyl = id->cyls; @@ -1023,77 +1033,42 @@ static int idedisk_cleanup (ide_drive_t *drive) { + put_device(&drive->device); if ((drive->id->cfs_enable_2 & 0x3000) && drive->wcache) - if (do_idedisk_flushcache(drive)) + if (idedisk_flushcache(drive)) printk (KERN_INFO "%s: Write Cache FAILED Flushing!\n", drive->name); return ide_unregister_subdriver(drive); } -int idedisk_init (void); -int idedisk_reinit(ide_drive_t *drive); - /* * IDE subdriver functions, registered with ide.c */ -static ide_driver_t idedisk_driver = { - name: "ide-disk", - media: ide_disk, - busy: 0, - supports_dma: 1, - supports_dsc_overlap: 0, +static struct ata_operations idedisk_driver = { + owner: THIS_MODULE, cleanup: idedisk_cleanup, - standby: do_idedisk_standby, - flushcache: do_idedisk_flushcache, + standby: idedisk_standby, do_request: do_rw_disk, end_request: NULL, ioctl: NULL, open: idedisk_open, release: idedisk_release, - media_change: idedisk_media_change, - revalidate: idedisk_revalidate, + check_media_change: idedisk_check_media_change, + revalidate: NULL, /* use default method */ pre_reset: idedisk_pre_reset, capacity: idedisk_capacity, special: idedisk_special, - proc: idedisk_proc, - driver_init: idedisk_init, - driver_reinit: idedisk_reinit, + proc: idedisk_proc }; MODULE_DESCRIPTION("ATA DISK Driver"); -int idedisk_reinit (ide_drive_t *drive) -{ - int failed = 0; - - MOD_INC_USE_COUNT; - - if (ide_register_subdriver (drive, &idedisk_driver)) { - printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name); - return 1; - } - DRIVER(drive)->busy++; - idedisk_setup(drive); - if ((!drive->head || drive->head > 16) && !drive->select.b.lba) { - printk(KERN_ERR "%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n", drive->name, drive->head); - (void) idedisk_cleanup(drive); - DRIVER(drive)->busy--; - return 1; - } - DRIVER(drive)->busy--; - failed--; - - ide_register_module(&idedisk_driver); - MOD_DEC_USE_COUNT; - return 0; -} - static void __exit idedisk_exit (void) { ide_drive_t *drive; int failed = 0; - while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, &idedisk_driver, failed)) != NULL) { + while ((drive = ide_scan_devices(ATA_DISK, "ide-disk", &idedisk_driver, failed)) != NULL) { if (idedisk_cleanup (drive)) { printk (KERN_ERR "%s: cleanup_module() called while still busy\n", drive->name); failed++; @@ -1105,7 +1080,6 @@ ide_remove_proc_entries(drive->proc, idedisk_proc); #endif } - ide_unregister_module(&idedisk_driver); } int idedisk_init (void) @@ -1114,23 +1088,20 @@ int failed = 0; MOD_INC_USE_COUNT; - while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, NULL, failed++)) != NULL) { + while ((drive = ide_scan_devices(ATA_DISK, "ide-disk", NULL, failed++)) != NULL) { if (ide_register_subdriver (drive, &idedisk_driver)) { printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name); continue; } - DRIVER(drive)->busy++; idedisk_setup(drive); if ((!drive->head || drive->head > 16) && !drive->select.b.lba) { printk(KERN_ERR "%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n", drive->name, drive->head); - (void) idedisk_cleanup(drive); - DRIVER(drive)->busy--; + idedisk_cleanup(drive); continue; } - DRIVER(drive)->busy--; failed--; } - ide_register_module(&idedisk_driver); + revalidate_drives(); MOD_DEC_USE_COUNT; return 0; } diff -Nru a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c --- a/drivers/ide/ide-dma.c Thu Mar 7 18:17:39 2002 +++ b/drivers/ide/ide-dma.c Thu Mar 7 18:17:39 2002 @@ -1,6 +1,4 @@ /* - * linux/drivers/ide/ide-dma.c Version 4.10 June 9, 2000 - * * Copyright (c) 1999-2000 Andre Hedrick * May be copied or modified under the terms of the GNU General Public License */ @@ -15,7 +13,7 @@ /* * This module provides support for the bus-master IDE DMA functions * of various PCI chipsets, including the Intel PIIX (i82371FB for - * the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and + * the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and * 440 chipsets), and the PIIX4 (i82371AB for the 430 TX chipset) * ("PIIX" stands for "PCI ISA IDE Xcellerator"). * @@ -73,8 +71,6 @@ * check_drive_lists(ide_drive_t *drive, int good_bad) * * ATA-66/100 and recovery functions, I forgot the rest...... - * SELECT_READ_WRITE(hwif,drive,func) for active tuning based on IO direction. - * */ #include @@ -426,7 +422,7 @@ return 0; } -int report_drive_dmaing (ide_drive_t *drive) +static int report_drive_dmaing (ide_drive_t *drive) { struct hd_driveid *id = drive->id; @@ -470,7 +466,7 @@ ide_hwif_t *hwif = HWIF(drive); #ifdef CONFIG_IDEDMA_ONLYDISK - if (drive->media != ide_disk) + if (drive->type != ATA_DISK) config_allows_dma = 0; #endif @@ -555,7 +551,7 @@ { u64 addr = BLK_BOUNCE_HIGH; - if (on && drive->media == ide_disk && HWIF(drive)->highmem) { + if (on && drive->type == ATA_DISK && HWIF(drive)->highmem) { if (!PCI_DMA_BUS_IS_PHYS) addr = BLK_BOUNCE_ANY; else @@ -606,20 +602,23 @@ case ide_dma_read: reading = 1 << 3; case ide_dma_write: - SELECT_READ_WRITE(hwif,drive,func); + /* active tuning based on IO direction */ + if (hwif->rwproc) + hwif->rwproc(drive, func); + if (!(count = ide_build_dmatable(drive, func))) return 1; /* try PIO instead of DMA */ outl(hwif->dmatable_dma, dma_base + 4); /* PRD table */ outb(reading, dma_base); /* specify r/w */ outb(inb(dma_base+2)|6, dma_base+2); /* clear INTR & ERROR flags */ drive->waiting_for_dma = 1; - if (drive->media != ide_disk) + if (drive->type != ATA_DISK) return 0; #ifdef CONFIG_BLK_DEV_IDEDMA_TIMEOUT ide_set_handler(drive, &ide_dma_intr, 2*WAIT_CMD, NULL); /* issue cmd to drive */ -#else /* !CONFIG_BLK_DEV_IDEDMA_TIMEOUT */ +#else ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, dma_timer_expiry); /* issue cmd to drive */ -#endif /* CONFIG_BLK_DEV_IDEDMA_TIMEOUT */ +#endif if ((HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE) && (drive->addressing == 1)) { ide_task_t *args = HWGROUP(drive)->rq->special; @@ -728,9 +727,8 @@ } /* - * This can be called for a dynamically installed interface. Don't __init it + * This can be called for a dynamically installed interface. Don't __init it */ - void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_ports) { printk(" %s: BM-DMA at 0x%04lx-0x%04lx", hwif->name, dma_base, dma_base + num_ports - 1); @@ -767,82 +765,4 @@ dma_alloc_failure: printk(" -- ERROR, UNABLE TO ALLOCATE DMA TABLES\n"); -} - -/* - * Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space: - */ -unsigned long __init ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) -{ - unsigned long dma_base = 0; - struct pci_dev *dev = hwif->pci_dev; - -#ifdef CONFIG_BLK_DEV_IDEDMA_FORCED - int second_chance = 0; - -second_chance_to_dma: -#endif /* CONFIG_BLK_DEV_IDEDMA_FORCED */ - - if (hwif->mate && hwif->mate->dma_base) { - dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8); - } else { - dma_base = pci_resource_start(dev, 4); - if (!dma_base) { - printk("%s: dma_base is invalid (0x%04lx)\n", name, dma_base); - dma_base = 0; - } - } - -#ifdef CONFIG_BLK_DEV_IDEDMA_FORCED - if ((!dma_base) && (!second_chance)) { - unsigned long set_bmiba = 0; - second_chance++; - switch(dev->vendor) { - case PCI_VENDOR_ID_AL: - set_bmiba = DEFAULT_BMALIBA; break; - case PCI_VENDOR_ID_VIA: - set_bmiba = DEFAULT_BMCRBA; break; - case PCI_VENDOR_ID_INTEL: - set_bmiba = DEFAULT_BMIBA; break; - default: - return dma_base; - } - pci_write_config_dword(dev, 0x20, set_bmiba|1); - goto second_chance_to_dma; - } -#endif /* CONFIG_BLK_DEV_IDEDMA_FORCED */ - - if (dma_base) { - if (extra) /* PDC20246, PDC20262, HPT343, & HPT366 */ - request_region(dma_base+16, extra, name); - dma_base += hwif->channel ? 8 : 0; - hwif->dma_extra = extra; - - switch(dev->device) { - case PCI_DEVICE_ID_AL_M5219: - case PCI_DEVICE_ID_AMD_VIPER_7409: - case PCI_DEVICE_ID_CMD_643: - outb(inb(dma_base+2) & 0x60, dma_base+2); - if (inb(dma_base+2) & 0x80) { - printk("%s: simplex device: DMA forced\n", name); - } - break; - default: - /* - * If the device claims "simplex" DMA, - * this means only one of the two interfaces - * can be trusted with DMA at any point in time. - * So we should enable DMA only on one of the - * two interfaces. - */ - if ((inb(dma_base+2) & 0x80)) { /* simplex device? */ - if ((!hwif->drives[0].present && !hwif->drives[1].present) || - (hwif->mate && hwif->mate->dma_base)) { - printk("%s: simplex device: DMA disabled\n", name); - dma_base = 0; - } - } - } - } - return dma_base; } diff -Nru a/drivers/ide/ide-features.c b/drivers/ide/ide-features.c --- a/drivers/ide/ide-features.c Thu Mar 7 18:17:45 2002 +++ b/drivers/ide/ide-features.c Thu Mar 7 18:17:45 2002 @@ -70,22 +70,6 @@ } /* - * - */ -char *ide_media_verbose (ide_drive_t *drive) -{ - switch (drive->media) { - case ide_scsi: return("scsi "); - case ide_disk: return("disk "); - case ide_optical: return("optical"); - case ide_cdrom: return("cdrom "); - case ide_tape: return("tape "); - case ide_floppy: return("floppy "); - default: return("???????"); - } -} - -/* * A Verbose noise maker for debugging on the attempted dmaing calls. */ char *ide_dmafunc_verbose (ide_dma_action_t dmafunc) diff -Nru a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c --- a/drivers/ide/ide-floppy.c Thu Mar 7 18:17:40 2002 +++ b/drivers/ide/ide-floppy.c Thu Mar 7 18:17:40 2002 @@ -1113,7 +1113,7 @@ pc->retries++; pc->actually_transferred=0; /* We haven't transferred any data yet */ pc->current_position=pc->buffer; - bcount.all = IDE_MIN(pc->request_transfer, 63 * 1024); + bcount.all = min(pc->request_transfer, 63 * 1024); #ifdef CONFIG_BLK_DEV_IDEDMA if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) { @@ -1819,19 +1819,11 @@ /* * Check media change. Use a simple algorithm for now. */ -static int idefloppy_media_change (ide_drive_t *drive) +static int idefloppy_check_media_change (ide_drive_t *drive) { idefloppy_floppy_t *floppy = drive->driver_data; - - return test_and_clear_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags); -} -/* - * Revalidate the new media. Should set blk_size[] - */ -static void idefloppy_revalidate (ide_drive_t *drive) -{ - ide_revalidate_drive(drive); + return test_and_clear_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags); } /* @@ -2046,70 +2038,26 @@ #endif /* CONFIG_PROC_FS */ -int idefloppy_init(void); -int idefloppy_reinit(ide_drive_t *drive); - /* * IDE subdriver functions, registered with ide.c */ -static ide_driver_t idefloppy_driver = { - name: "ide-floppy", - media: ide_floppy, - busy: 0, - supports_dma: 1, - supports_dsc_overlap: 0, +static struct ata_operations idefloppy_driver = { + owner: THIS_MODULE, cleanup: idefloppy_cleanup, standby: NULL, - flushcache: NULL, do_request: idefloppy_do_request, end_request: idefloppy_end_request, ioctl: idefloppy_ioctl, open: idefloppy_open, release: idefloppy_release, - media_change: idefloppy_media_change, - revalidate: idefloppy_revalidate, + check_media_change: idefloppy_check_media_change, + revalidate: NULL, /* use default method */ pre_reset: NULL, capacity: idefloppy_capacity, special: NULL, - proc: idefloppy_proc, - driver_init: idefloppy_init, - driver_reinit: idefloppy_reinit, + proc: idefloppy_proc }; -int idefloppy_reinit (ide_drive_t *drive) -{ - idefloppy_floppy_t *floppy; - int failed = 0; - - MOD_INC_USE_COUNT; - while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, NULL, failed++)) != NULL) { - if (!idefloppy_identify_device (drive, drive->id)) { - printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name); - continue; - } - if (drive->scsi) { - printk("ide-floppy: passing drive %s to ide-scsi emulation.\n", drive->name); - continue; - } - if ((floppy = (idefloppy_floppy_t *) kmalloc (sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL) { - printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name); - continue; - } - if (ide_register_subdriver (drive, &idefloppy_driver)) { - printk (KERN_ERR "ide-floppy: %s: Failed to register the driver with ide.c\n", drive->name); - kfree (floppy); - continue; - } - DRIVER(drive)->busy++; - idefloppy_setup (drive, floppy); - DRIVER(drive)->busy--; - failed--; - } - ide_register_module(&idefloppy_driver); - MOD_DEC_USE_COUNT; - return 0; -} - MODULE_DESCRIPTION("ATAPI FLOPPY Driver"); static void __exit idefloppy_exit (void) @@ -2117,7 +2065,7 @@ ide_drive_t *drive; int failed = 0; - while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, &idefloppy_driver, failed)) != NULL) { + while ((drive = ide_scan_devices(ATA_FLOPPY, "ide-floppy", &idefloppy_driver, failed)) != NULL) { if (idefloppy_cleanup (drive)) { printk ("%s: cleanup_module() called while still busy\n", drive->name); failed++; @@ -2130,7 +2078,6 @@ ide_remove_proc_entries(drive->proc, idefloppy_proc); #endif } - ide_unregister_module(&idefloppy_driver); } /* @@ -2144,7 +2091,7 @@ printk("ide-floppy driver " IDEFLOPPY_VERSION "\n"); MOD_INC_USE_COUNT; - while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, NULL, failed++)) != NULL) { + while ((drive = ide_scan_devices (ATA_FLOPPY, "ide-floppy", NULL, failed++)) != NULL) { if (!idefloppy_identify_device (drive, drive->id)) { printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name); continue; @@ -2162,12 +2109,14 @@ kfree (floppy); continue; } - DRIVER(drive)->busy++; + /* ATA-PATTERN */ + ata_ops(drive)->busy++; idefloppy_setup (drive, floppy); - DRIVER(drive)->busy--; + ata_ops(drive)->busy--; + failed--; } - ide_register_module(&idefloppy_driver); + revalidate_drives(); MOD_DEC_USE_COUNT; return 0; } diff -Nru a/drivers/ide/ide-geometry.c b/drivers/ide/ide-geometry.c --- a/drivers/ide/ide-geometry.c Thu Mar 7 18:17:36 2002 +++ b/drivers/ide/ide-geometry.c Thu Mar 7 18:17:36 2002 @@ -1,118 +1,40 @@ /* * linux/drivers/ide/ide-geometry.c + * + * Sun Feb 24 23:13:03 CET 2002: Patch by Andries Brouwer to remove the + * confused CMOS probe applied. This is solving more problems then it my + * (unexpectedly) introduce. */ + #include #include #include #include -#ifdef CONFIG_BLK_DEV_IDE - -/* - * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc - * controller that is BIOS compatible with ST-506, and thus showing up in our - * BIOS table, but not register compatible, and therefore not present in CMOS. - * - * Furthermore, we will assume that our ST-506 drives are the primary - * drives in the system -- the ones reflected as drive 1 or 2. The first - * drive is stored in the high nibble of CMOS byte 0x12, the second in the low - * nibble. This will be either a 4 bit drive type or 0xf indicating use byte - * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. A non-zero value - * means we have an AT controller hard disk for that drive. - * - * Of course, there is no guarantee that either drive is actually on the - * "primary" IDE interface, but we don't bother trying to sort that out here. - * If a drive is not actually on the primary interface, then these parameters - * will be ignored. This results in the user having to supply the logical - * drive geometry as a boot parameter for each drive not on the primary i/f. - */ -/* - * The only "perfect" way to handle this would be to modify the setup.[cS] code - * to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info - * for us during initialization. I have the necessary docs -- any takers? -ml - */ -/* - * I did this, but it doesnt work - there is no reasonable way to find the - * correspondence between the BIOS numbering of the disks and the Linux - * numbering. -aeb - * - * The code below is bad. One of the problems is that drives 1 and 2 - * may be SCSI disks (even when IDE disks are present), so that - * the geometry we read here from BIOS is attributed to the wrong disks. - * Consequently, also the former "drive->present = 1" below was a mistake. - * - * Eventually the entire routine below should be removed. - * - * 17-OCT-2000 rjohnson@analogic.com Added spin-locks for reading CMOS - * chip. - */ - -void probe_cmos_for_drives (ide_hwif_t *hwif) -{ -#ifdef __i386__ - extern struct drive_info_struct drive_info; - byte cmos_disks, *BIOS = (byte *) &drive_info; - int unit; - unsigned long flags; - -#ifdef CONFIG_BLK_DEV_PDC4030 - if (hwif->chipset == ide_pdc4030 && hwif->channel != 0) - return; -#endif /* CONFIG_BLK_DEV_PDC4030 */ - spin_lock_irqsave(&rtc_lock, flags); - cmos_disks = CMOS_READ(0x12); - spin_unlock_irqrestore(&rtc_lock, flags); - /* Extract drive geometry from CMOS+BIOS if not already setup */ - for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = &hwif->drives[unit]; - - if ((cmos_disks & (0xf0 >> (unit*4))) - && !drive->present && !drive->nobios) { - unsigned short cyl = *(unsigned short *)BIOS; - unsigned char head = *(BIOS+2); - unsigned char sect = *(BIOS+14); - if (cyl > 0 && head > 0 && sect > 0 && sect < 64) { - drive->cyl = drive->bios_cyl = cyl; - drive->head = drive->bios_head = head; - drive->sect = drive->bios_sect = sect; - drive->ctl = *(BIOS+8); - } else { - printk("hd%c: C/H/S=%d/%d/%d from BIOS ignored\n", - unit+'a', cyl, head, sect); - } - } - - BIOS += 16; - } -#endif -} -#endif /* CONFIG_BLK_DEV_IDE */ - - #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) extern ide_drive_t * get_info_ptr(kdev_t); -extern unsigned long current_capacity (ide_drive_t *); /* * If heads is nonzero: find a translation with this many heads and S=63. * Otherwise: find out how OnTrack Disk Manager would translate the disk. */ static void -ontrack(ide_drive_t *drive, int heads, unsigned int *c, int *h, int *s) { +ontrack(ide_drive_t *drive, int heads, unsigned int *c, int *h, int *s) +{ static const byte dm_head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0}; const byte *headp = dm_head_vals; unsigned long total; /* - * The specs say: take geometry as obtained from Identify, - * compute total capacity C*H*S from that, and truncate to - * 1024*255*63. Now take S=63, H the first in the sequence - * 4, 8, 16, 32, 64, 128, 255 such that 63*H*1024 >= total. - * [Please tell aeb@cwi.nl in case this computes a - * geometry different from what OnTrack uses.] + * The specs say: take geometry as obtained from Identify, compute + * total capacity C*H*S from that, and truncate to 1024*255*63. Now + * take S=63, H the first in the sequence 4, 8, 16, 32, 64, 128, 255 + * such that 63*H*1024 >= total. [Please tell aeb@cwi.nl in case this + * computes a geometry different from what OnTrack uses.] */ - total = DRIVER(drive)->capacity(drive); + + total = ata_capacity(drive); *s = 63; @@ -214,7 +136,7 @@ ret = 1; } - drive->part[0].nr_sects = current_capacity(drive); + drive->part[0].nr_sects = ata_capacity(drive); if (ret) printk("%s%s [%d/%d/%d]", msg, msg1, diff -Nru a/drivers/ide/ide-pci.c b/drivers/ide/ide-pci.c --- a/drivers/ide/ide-pci.c Thu Mar 7 18:17:46 2002 +++ b/drivers/ide/ide-pci.c Thu Mar 7 18:17:46 2002 @@ -1,15 +1,13 @@ /* - * linux/drivers/ide/ide-pci.c Version 1.05 June 9, 2000 - * * Copyright (c) 1998-2000 Andre Hedrick - * * Copyright (c) 1995-1998 Mark Lord + * * May be copied or modified under the terms of the GNU General Public License */ /* * This module provides support for automatic detection and - * configuration of all PCI IDE interfaces present in a system. + * configuration of all PCI IDE interfaces present in a system. */ /* @@ -32,458 +30,303 @@ #include #include -#define DEVID_PIIXa ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0}) -#define DEVID_PIIXb ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_1}) -#define DEVID_MPIIX ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX}) -#define DEVID_PIIX3 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1}) -#define DEVID_PIIX4 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB}) -#define DEVID_ICH0 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_1}) -#define DEVID_PIIX4E2 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_1}) -#define DEVID_ICH ((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_ICH2 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_9}) -#define DEVID_ICH2M ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_8}) -#define DEVID_ICH3 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10}) -#define DEVID_VIA_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561}) -#define DEVID_MR_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C576_1}) -#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}) -#define DEVID_PDC20262 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262}) -#define DEVID_PDC20265 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20265}) -#define DEVID_PDC20267 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20267}) -#define DEVID_PDC20268 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268}) -#define DEVID_PDC20268R ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268R}) -#define DEVID_PDC20269 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20269}) -#define DEVID_PDC20275 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20275}) -#define DEVID_RZ1000 ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000}) -#define DEVID_RZ1001 ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001}) -#define DEVID_SAMURAI ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE}) -#define DEVID_CMD640 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_640}) -#define DEVID_CMD643 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_643}) -#define DEVID_CMD646 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646}) -#define DEVID_CMD648 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_648}) -#define DEVID_CMD649 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_649}) -#define DEVID_CMD680 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_680}) -#define DEVID_SIS5513 ((ide_pci_devid_t){PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513}) -#define DEVID_OPTI621 ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621}) -#define DEVID_OPTI621V ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558}) -#define DEVID_OPTI621X ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C825}) -#define DEVID_TRM290 ((ide_pci_devid_t){PCI_VENDOR_ID_TEKRAM, PCI_DEVICE_ID_TEKRAM_DC290}) -#define DEVID_NS87410 ((ide_pci_devid_t){PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410}) -#define DEVID_NS87415 ((ide_pci_devid_t){PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415}) -#define DEVID_HT6565 ((ide_pci_devid_t){PCI_VENDOR_ID_HOLTEK, PCI_DEVICE_ID_HOLTEK_6565}) -#define DEVID_AEC6210 ((ide_pci_devid_t){PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF}) -#define DEVID_AEC6260 ((ide_pci_devid_t){PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860}) -#define DEVID_AEC6260R ((ide_pci_devid_t){PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860R}) -#define DEVID_W82C105 ((ide_pci_devid_t){PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105}) -#define DEVID_UM8673F ((ide_pci_devid_t){PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8673F}) -#define DEVID_UM8886A ((ide_pci_devid_t){PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886A}) -#define DEVID_UM8886BF ((ide_pci_devid_t){PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF}) -#define DEVID_HPT34X ((ide_pci_devid_t){PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343}) -#define DEVID_HPT366 ((ide_pci_devid_t){PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366}) -#define DEVID_ALI15X3 ((ide_pci_devid_t){PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229}) -#define DEVID_CY82C693 ((ide_pci_devid_t){PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693}) -#define DEVID_HINT ((ide_pci_devid_t){0x3388, 0x8013}) -#define DEVID_CS5530 ((ide_pci_devid_t){PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE}) -#define DEVID_AMD7401 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_COBRA_7401}) -#define DEVID_AMD7409 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7409}) -#define DEVID_AMD7411 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7411}) -#define DEVID_AMD7441 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7441}) -#define DEVID_PDCADMA ((ide_pci_devid_t){PCI_VENDOR_ID_PDC, PCI_DEVICE_ID_PDC_1841}) -#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_CSB5 ((ide_pci_devid_t){PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE}) -#define DEVID_ITE8172G ((ide_pci_devid_t){PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G}) +/* Missing PCI device IDs: */ +#define PCI_VENDOR_ID_HINT 0x3388 +#define PCI_DEVICE_ID_HINT 0x8013 #define IDE_IGNORE ((void *)-1) #define IDE_NO_DRIVER ((void *)-2) #ifdef CONFIG_BLK_DEV_AEC62XX -extern unsigned int pci_init_aec62xx(struct pci_dev *, const char *); +extern unsigned int pci_init_aec62xx(struct pci_dev *); extern unsigned int ata66_aec62xx(ide_hwif_t *); extern void ide_init_aec62xx(ide_hwif_t *); extern void ide_dmacapable_aec62xx(ide_hwif_t *, unsigned long); -#define PCI_AEC62XX &pci_init_aec62xx -#define ATA66_AEC62XX &ata66_aec62xx -#define INIT_AEC62XX &ide_init_aec62xx -#define DMA_AEC62XX &ide_dmacapable_aec62xx -#else -#define PCI_AEC62XX NULL -#define ATA66_AEC62XX NULL -#define INIT_AEC62XX IDE_NO_DRIVER -#define DMA_AEC62XX NULL #endif #ifdef CONFIG_BLK_DEV_ALI15X3 -extern unsigned int pci_init_ali15x3(struct pci_dev *, const char *); +extern unsigned int pci_init_ali15x3(struct pci_dev *); extern unsigned int ata66_ali15x3(ide_hwif_t *); extern void ide_init_ali15x3(ide_hwif_t *); extern void ide_dmacapable_ali15x3(ide_hwif_t *, unsigned long); -#define PCI_ALI15X3 &pci_init_ali15x3 -#define ATA66_ALI15X3 &ata66_ali15x3 -#define INIT_ALI15X3 &ide_init_ali15x3 -#define DMA_ALI15X3 &ide_dmacapable_ali15x3 -#else -#define PCI_ALI15X3 NULL -#define ATA66_ALI15X3 NULL -#define INIT_ALI15X3 IDE_NO_DRIVER -#define DMA_ALI15X3 NULL #endif #ifdef CONFIG_BLK_DEV_AMD74XX -extern unsigned int pci_init_amd74xx(struct pci_dev *, const char *); +extern unsigned int pci_init_amd74xx(struct pci_dev *); extern unsigned int ata66_amd74xx(ide_hwif_t *); extern void ide_init_amd74xx(ide_hwif_t *); extern void ide_dmacapable_amd74xx(ide_hwif_t *, unsigned long); -#define PCI_AMD74XX &pci_init_amd74xx -#define ATA66_AMD74XX &ata66_amd74xx -#define INIT_AMD74XX &ide_init_amd74xx -#define DMA_AMD74XX &ide_dmacapable_amd74xx -#else -#define PCI_AMD74XX NULL -#define ATA66_AMD74XX NULL -#define INIT_AMD74XX IDE_NO_DRIVER -#define DMA_AMD74XX NULL #endif #ifdef CONFIG_BLK_DEV_CMD64X -extern unsigned int pci_init_cmd64x(struct pci_dev *, const char *); +extern unsigned int pci_init_cmd64x(struct pci_dev *); extern unsigned int ata66_cmd64x(ide_hwif_t *); extern void ide_init_cmd64x(ide_hwif_t *); extern void ide_dmacapable_cmd64x(ide_hwif_t *, unsigned long); -#define PCI_CMD64X &pci_init_cmd64x -#define ATA66_CMD64X &ata66_cmd64x -#define INIT_CMD64X &ide_init_cmd64x -#else -#define PCI_CMD64X NULL -#define ATA66_CMD64X NULL -#ifdef __sparc_v9__ -#define INIT_CMD64X IDE_IGNORE -#else -#define INIT_CMD64X IDE_NO_DRIVER -#endif #endif #ifdef CONFIG_BLK_DEV_CY82C693 -extern unsigned int pci_init_cy82c693(struct pci_dev *, const char *); +extern unsigned int pci_init_cy82c693(struct pci_dev *); extern void ide_init_cy82c693(ide_hwif_t *); -#define PCI_CY82C693 &pci_init_cy82c693 -#define INIT_CY82C693 &ide_init_cy82c693 -#else -#define PCI_CY82C693 NULL -#define INIT_CY82C693 IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_CS5530 -extern unsigned int pci_init_cs5530(struct pci_dev *, const char *); +extern unsigned int pci_init_cs5530(struct pci_dev *); extern void ide_init_cs5530(ide_hwif_t *); -#define PCI_CS5530 &pci_init_cs5530 -#define INIT_CS5530 &ide_init_cs5530 -#else -#define PCI_CS5530 NULL -#define INIT_CS5530 IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_HPT34X -extern unsigned int pci_init_hpt34x(struct pci_dev *, const char *); +extern unsigned int pci_init_hpt34x(struct pci_dev *); extern void ide_init_hpt34x(ide_hwif_t *); -#define PCI_HPT34X &pci_init_hpt34x -#define INIT_HPT34X &ide_init_hpt34x -#else -#define PCI_HPT34X NULL -#define INIT_HPT34X IDE_IGNORE #endif #ifdef CONFIG_BLK_DEV_HPT366 extern byte hpt363_shared_irq; extern byte hpt363_shared_pin; -extern unsigned int pci_init_hpt366(struct pci_dev *, const char *); + +extern unsigned int pci_init_hpt366(struct pci_dev *); extern unsigned int ata66_hpt366(ide_hwif_t *); extern void ide_init_hpt366(ide_hwif_t *); extern void ide_dmacapable_hpt366(ide_hwif_t *, unsigned long); -#define PCI_HPT366 &pci_init_hpt366 -#define ATA66_HPT366 &ata66_hpt366 -#define INIT_HPT366 &ide_init_hpt366 -#define DMA_HPT366 &ide_dmacapable_hpt366 #else +/* FIXME: those have to be killed */ static byte hpt363_shared_irq; static byte hpt363_shared_pin; -#define PCI_HPT366 NULL -#define ATA66_HPT366 NULL -#define INIT_HPT366 IDE_NO_DRIVER -#define DMA_HPT366 NULL #endif #ifdef CONFIG_BLK_DEV_NS87415 extern void ide_init_ns87415(ide_hwif_t *); -#define INIT_NS87415 &ide_init_ns87415 -#else -#define INIT_NS87415 IDE_IGNORE #endif #ifdef CONFIG_BLK_DEV_OPTI621 extern void ide_init_opti621(ide_hwif_t *); -#define INIT_OPTI621 &ide_init_opti621 -#else -#define INIT_OPTI621 IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_PDC_ADMA -extern unsigned int pci_init_pdcadma(struct pci_dev *, const char *); +extern unsigned int pci_init_pdcadma(struct pci_dev *); extern unsigned int ata66_pdcadma(ide_hwif_t *); extern void ide_init_pdcadma(ide_hwif_t *); extern void ide_dmacapable_pdcadma(ide_hwif_t *, unsigned long); -#define PCI_PDCADMA &pci_init_pdcadma -#define ATA66_PDCADMA &ata66_pdcadma -#define INIT_PDCADMA &ide_init_pdcadma -#define DMA_PDCADMA &ide_dmacapable_pdcadma -#else -#define PCI_PDCADMA IDE_IGNORE -#define ATA66_PDCADMA IDE_IGNORE -#define INIT_PDCADMA IDE_IGNORE -#define DMA_PDCADMA IDE_IGNORE #endif #ifdef CONFIG_BLK_DEV_PDC202XX -extern unsigned int pci_init_pdc202xx(struct pci_dev *, const char *); +extern unsigned int pci_init_pdc202xx(struct pci_dev *); extern unsigned int ata66_pdc202xx(ide_hwif_t *); extern void ide_init_pdc202xx(ide_hwif_t *); -#define PCI_PDC202XX &pci_init_pdc202xx -#define ATA66_PDC202XX &ata66_pdc202xx -#define INIT_PDC202XX &ide_init_pdc202xx -#else -#define PCI_PDC202XX IDE_IGNORE -#define ATA66_PDC202XX IDE_IGNORE -#define INIT_PDC202XX IDE_IGNORE #endif #ifdef CONFIG_BLK_DEV_PIIX -extern unsigned int pci_init_piix(struct pci_dev *, const char *); +extern unsigned int pci_init_piix(struct pci_dev *); extern unsigned int ata66_piix(ide_hwif_t *); extern void ide_init_piix(ide_hwif_t *); -#define PCI_PIIX &pci_init_piix -#define ATA66_PIIX &ata66_piix -#define INIT_PIIX &ide_init_piix -#else -#define PCI_PIIX NULL -#define ATA66_PIIX NULL -#define INIT_PIIX IDE_NO_DRIVER #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 unsigned int pci_init_it8172(struct pci_dev *); 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 IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_RZ1000 extern void ide_init_rz1000(ide_hwif_t *); -#define INIT_RZ1000 &ide_init_rz1000 -#else -#define INIT_RZ1000 IDE_IGNORE #endif -#define INIT_SAMURAI NULL - #ifdef CONFIG_BLK_DEV_SVWKS -extern unsigned int pci_init_svwks(struct pci_dev *, const char *); +extern unsigned int pci_init_svwks(struct pci_dev *); extern unsigned int ata66_svwks(ide_hwif_t *); extern void ide_init_svwks(ide_hwif_t *); -#define PCI_SVWKS &pci_init_svwks -#define ATA66_SVWKS &ata66_svwks -#define INIT_SVWKS &ide_init_svwks -#else -#define PCI_SVWKS NULL -#define ATA66_SVWKS NULL -#define INIT_SVWKS IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_SIS5513 -extern unsigned int pci_init_sis5513(struct pci_dev *, const char *); +extern unsigned int pci_init_sis5513(struct pci_dev *); extern unsigned int ata66_sis5513(ide_hwif_t *); extern void ide_init_sis5513(ide_hwif_t *); -#define PCI_SIS5513 &pci_init_sis5513 -#define ATA66_SIS5513 &ata66_sis5513 -#define INIT_SIS5513 &ide_init_sis5513 -#else -#define PCI_SIS5513 NULL -#define ATA66_SIS5513 NULL -#define INIT_SIS5513 IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_SLC90E66 -extern unsigned int pci_init_slc90e66(struct pci_dev *, const char *); +extern unsigned int pci_init_slc90e66(struct pci_dev *); extern unsigned int ata66_slc90e66(ide_hwif_t *); extern void ide_init_slc90e66(ide_hwif_t *); -#define PCI_SLC90E66 &pci_init_slc90e66 -#define ATA66_SLC90E66 &ata66_slc90e66 -#define INIT_SLC90E66 &ide_init_slc90e66 -#else -#define PCI_SLC90E66 NULL -#define ATA66_SLC90E66 NULL -#define INIT_SLC90E66 IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_SL82C105 -extern unsigned int pci_init_sl82c105(struct pci_dev *, const char *); +extern unsigned int pci_init_sl82c105(struct pci_dev *); extern void dma_init_sl82c105(ide_hwif_t *, unsigned long); extern void ide_init_sl82c105(ide_hwif_t *); -#define PCI_W82C105 &pci_init_sl82c105 -#define DMA_W82C105 &dma_init_sl82c105 -#define INIT_W82C105 &ide_init_sl82c105 -#else -#define PCI_W82C105 NULL -#define DMA_W82C105 NULL -#define INIT_W82C105 IDE_IGNORE #endif #ifdef CONFIG_BLK_DEV_TRM290 extern void ide_init_trm290(ide_hwif_t *); -#define INIT_TRM290 &ide_init_trm290 -#else -#define INIT_TRM290 IDE_IGNORE #endif #ifdef CONFIG_BLK_DEV_VIA82CXXX -extern unsigned int pci_init_via82cxxx(struct pci_dev *, const char *); +extern unsigned int pci_init_via82cxxx(struct pci_dev *); extern unsigned int ata66_via82cxxx(ide_hwif_t *); extern void ide_init_via82cxxx(ide_hwif_t *); extern void ide_dmacapable_via82cxxx(ide_hwif_t *, unsigned long); -#define PCI_VIA82CXXX &pci_init_via82cxxx -#define ATA66_VIA82CXXX &ata66_via82cxxx -#define INIT_VIA82CXXX &ide_init_via82cxxx -#define DMA_VIA82CXXX &ide_dmacapable_via82cxxx -#else -#define PCI_VIA82CXXX NULL -#define ATA66_VIA82CXXX NULL -#define INIT_VIA82CXXX IDE_NO_DRIVER -#define DMA_VIA82CXXX NULL #endif typedef struct ide_pci_enablebit_s { - byte reg; /* byte pci reg holding the enable-bit */ - byte mask; /* mask to isolate the enable-bit */ - byte val; /* value of masked reg when "enabled" */ + u8 reg; /* pci configuration register holding the enable-bit */ + u8 mask; /* mask used to isolate the enable-bit */ + u8 val; /* expected value of masked register when "enabled" */ } ide_pci_enablebit_t; +/* Flags used to untangle quirk handling. + */ +#define ATA_F_DMA 0x01 +#define ATA_F_NODMA 0x02 /* no DMA mode supported at all */ +#define ATA_F_NOADMA 0x04 /* DMA has to be enabled explicitely */ +#define ATA_F_FIXIRQ 0x08 /* fixed irq wiring */ +#define ATA_F_SER 0x10 /* serialize on first and second channel interrupts */ +#define ATA_F_IRQ 0x20 /* trust IRQ information from config */ +#define ATA_F_PHACK 0x40 /* apply PROMISE hacks */ +#define ATA_F_HPTHACK 0x80 /* apply HPT366 hacks */ + typedef struct ide_pci_device_s { - ide_pci_devid_t devid; - char *name; - unsigned int (*init_chipset)(struct pci_dev *dev, const char *name); + unsigned short vendor; + unsigned short device; + unsigned int (*init_chipset)(struct pci_dev *dev); unsigned int (*ata66_check)(ide_hwif_t *hwif); - void (*init_hwif)(ide_hwif_t *hwif); + void (*init_hwif)(ide_hwif_t *hwif); void (*dma_init)(ide_hwif_t *hwif, unsigned long dmabase); ide_pci_enablebit_t enablebits[2]; - byte bootable; + unsigned int bootable; unsigned int extra; + unsigned int flags; } ide_pci_device_t; -static ide_pci_device_t ide_pci_chipsets[] __initdata = { - {DEVID_PIIXa, "PIIX", NULL, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, - {DEVID_PIIXb, "PIIX", NULL, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, - {DEVID_MPIIX, "MPIIX", NULL, NULL, INIT_PIIX, NULL, {{0x6D,0x80,0x80}, {0x6F,0x80,0x80}}, ON_BOARD, 0 }, - {DEVID_PIIX3, "PIIX3", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, - {DEVID_PIIX4, "PIIX4", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, - {DEVID_ICH0, "ICH0", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, - {DEVID_PIIX4E2, "PIIX4", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, - {DEVID_ICH, "ICH", PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, - {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_ICH2, "ICH2", PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, - {DEVID_ICH2M, "ICH2-M", PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, - {DEVID_ICH3, "ICH3", 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_MR_IDE, "VP_IDE", PCI_VIA82CXXX, ATA66_VIA82CXXX,INIT_VIA82CXXX, DMA_VIA82CXXX, {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, 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 }, -#ifdef CONFIG_PDC202XX_FORCE - {DEVID_PDC20246,"PDC20246", PCI_PDC202XX, NULL, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 16 }, - {DEVID_PDC20262,"PDC20262", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 48 }, - {DEVID_PDC20265,"PDC20265", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 48 }, - {DEVID_PDC20267,"PDC20267", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 48 }, -#else /* !CONFIG_PDC202XX_FORCE */ - {DEVID_PDC20246,"PDC20246", PCI_PDC202XX, NULL, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 16 }, - {DEVID_PDC20262,"PDC20262", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 48 }, - {DEVID_PDC20265,"PDC20265", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 48 }, - {DEVID_PDC20267,"PDC20267", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 48 }, -#endif - {DEVID_PDC20268,"PDC20268", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, - /* Promise used a different PCI ident for the raid card apparently to try and - prevent Linux detecting it and using our own raid code. We want to detect - it for the ataraid drivers, so we have to list both here.. */ - {DEVID_PDC20268R,"PDC20270", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, - {DEVID_PDC20269,"PDC20269", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, - {DEVID_PDC20275,"PDC20275", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, - {DEVID_RZ1000, "RZ1000", NULL, NULL, INIT_RZ1000, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_RZ1001, "RZ1001", NULL, NULL, INIT_RZ1000, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_SAMURAI, "SAMURAI", NULL, NULL, INIT_SAMURAI, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_CMD640, "CMD640", NULL, NULL, IDE_IGNORE, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_NS87410, "NS87410", NULL, NULL, NULL, NULL, {{0x43,0x08,0x08}, {0x47,0x08,0x08}}, ON_BOARD, 0 }, - {DEVID_SIS5513, "SIS5513", PCI_SIS5513, ATA66_SIS5513, INIT_SIS5513, NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, ON_BOARD, 0 }, - {DEVID_CMD643, "CMD643", PCI_CMD64X, NULL, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_CMD646, "CMD646", PCI_CMD64X, NULL, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x51,0x80,0x80}}, ON_BOARD, 0 }, - {DEVID_CMD648, "CMD648", PCI_CMD64X, ATA66_CMD64X, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_CMD649, "CMD649", PCI_CMD64X, ATA66_CMD64X, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_CMD680, "CMD680", PCI_CMD64X, ATA66_CMD64X, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_HT6565, "HT6565", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_OPTI621, "OPTI621", NULL, NULL, INIT_OPTI621, NULL, {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, ON_BOARD, 0 }, - {DEVID_OPTI621X,"OPTI621X", NULL, NULL, INIT_OPTI621, NULL, {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, ON_BOARD, 0 }, - {DEVID_TRM290, "TRM290", NULL, NULL, INIT_TRM290, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_NS87415, "NS87415", NULL, NULL, INIT_NS87415, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_AEC6210, "AEC6210", PCI_AEC62XX, NULL, INIT_AEC62XX, DMA_AEC62XX, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, OFF_BOARD, 0 }, - {DEVID_AEC6260, "AEC6260", PCI_AEC62XX, ATA66_AEC62XX, INIT_AEC62XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, NEVER_BOARD, 0 }, - {DEVID_AEC6260R,"AEC6260R", PCI_AEC62XX, ATA66_AEC62XX, INIT_AEC62XX, NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, OFF_BOARD, 0 }, - {DEVID_W82C105, "W82C105", PCI_W82C105, NULL, INIT_W82C105, DMA_W82C105, {{0x40,0x01,0x01}, {0x40,0x10,0x10}}, ON_BOARD, 0 }, - {DEVID_UM8673F, "UM8673F", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_UM8886A, "UM8886A", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_UM8886BF,"UM8886BF", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_HPT34X, "HPT34X", PCI_HPT34X, NULL, INIT_HPT34X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, NEVER_BOARD, 16 }, - {DEVID_HPT366, "HPT366", PCI_HPT366, ATA66_HPT366, INIT_HPT366, DMA_HPT366, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 240 }, - {DEVID_ALI15X3, "ALI15X3", PCI_ALI15X3, ATA66_ALI15X3, INIT_ALI15X3, DMA_ALI15X3, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_CY82C693,"CY82C693", PCI_CY82C693, NULL, INIT_CY82C693, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_HINT, "HINT_IDE", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_CS5530, "CS5530", PCI_CS5530, NULL, INIT_CS5530, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_AMD7401, "AMD7401", NULL, NULL, NULL, DMA_AMD74XX, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0 }, - {DEVID_AMD7409, "AMD7409", PCI_AMD74XX, ATA66_AMD74XX, INIT_AMD74XX, DMA_AMD74XX, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0 }, - {DEVID_AMD7411, "AMD7411", PCI_AMD74XX, ATA66_AMD74XX, INIT_AMD74XX, DMA_AMD74XX, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0 }, - {DEVID_AMD7441, "AMD7441", PCI_AMD74XX, ATA66_AMD74XX, INIT_AMD74XX, DMA_AMD74XX, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0 }, - {DEVID_PDCADMA, "PDCADMA", PCI_PDCADMA, ATA66_PDCADMA, INIT_PDCADMA, DMA_PDCADMA, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_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_SVWKS, ATA66_SVWKS, INIT_SVWKS, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_CSB5, "ServerWorks CSB5", PCI_SVWKS, ATA66_SVWKS, INIT_SVWKS, 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 }}; +static ide_pci_device_t pci_chipsets[] __initdata = { +#ifdef CONFIG_BLK_DEV_PIIX + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0, NULL, NULL, ide_init_piix, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_1, NULL, NULL, ide_init_piix, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX, NULL, NULL, ide_init_piix, NULL, {{0x6D,0x80,0x80}, {0x6F,0x80,0x80}}, ON_BOARD, 0, ATA_F_NODMA }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, pci_init_piix, NULL, ide_init_piix, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, pci_init_piix, NULL, ide_init_piix, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_1, pci_init_piix, NULL, ide_init_piix, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_1, pci_init_piix, NULL, ide_init_piix, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_1, pci_init_piix, ata66_piix, ide_init_piix, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82372FB_1, pci_init_piix, ata66_piix, ide_init_piix, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_init_piix, NULL, ide_init_piix, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, ATA_F_NOADMA }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_9, pci_init_piix, ata66_piix, ide_init_piix, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_8, pci_init_piix, ata66_piix, ide_init_piix, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10, pci_init_piix, ata66_piix, ide_init_piix, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, +#endif +#ifdef CONFIG_BLK_DEV_VIA82CXXX + {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C576_1, pci_init_via82cxxx, ata66_via82cxxx, ide_init_via82cxxx, ide_dmacapable_via82cxxx, {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, ON_BOARD, 0, ATA_F_NOADMA }, + {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, pci_init_via82cxxx, ata66_via82cxxx, ide_init_via82cxxx, ide_dmacapable_via82cxxx, {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, ON_BOARD, 0, ATA_F_NOADMA }, +#endif +#ifdef CONFIG_BLK_DEV_PDC202XX +# ifdef CONFIG_PDC202XX_FORCE + {PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246, pci_init_pdc202xx, NULL, ide_init_pdc202xx, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 16, ATA_F_IRQ | ATA_F_DMA }, + {PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262, pci_init_pdc202xx, ata66_pdc202xx, ide_init_pdc202xx, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 48, ATA_F_IRQ | ATA_F_PHACK | ATA_F_DMA}, + {PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20265, pci_init_pdc202xx, ata66_pdc202xx, ide_init_pdc202xx, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 48, ATA_F_IRQ | ATA_F_PHACK | ATA_F_DMA}, + {PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20267, pci_init_pdc202xx, ata66_pdc202xx, ide_init_pdc202xx, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 48, ATA_F_IRQ | ATA_F_DMA }, +# else + {PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246, pci_init_pdc202xx, NULL, ide_init_pdc202xx, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 16, ATA_F_IRQ | ATA_F_DMA }, + {PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262, pci_init_pdc202xx, ata66_pdc202xx, ide_init_pdc202xx, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 48, ATA_F_IRQ | ATA_F_PHACK | ATA_F_DMA }, + {PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20265, pci_init_pdc202xx, ata66_pdc202xx, ide_init_pdc202xx, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 48, ATA_F_IRQ | ATA_F_PHACK | ATA_F_DMA }, + {PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20267, pci_init_pdc202xx, ata66_pdc202xx, ide_init_pdc202xx, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 48, ATA_F_IRQ | ATA_F_DMA }, +# endif + {PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268, pci_init_pdc202xx, ata66_pdc202xx, ide_init_pdc202xx, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0, ATA_F_IRQ | ATA_F_DMA }, + /* Promise used a different PCI identification for the raid card + * apparently to try and prevent Linux detecting it and using our own + * raid code. We want to detect it for the ataraid drivers, so we have + * to list both here.. */ + {PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268R, pci_init_pdc202xx, ata66_pdc202xx, ide_init_pdc202xx, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0, ATA_F_IRQ | ATA_F_DMA }, + {PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20269, pci_init_pdc202xx, ata66_pdc202xx, ide_init_pdc202xx, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0, ATA_F_IRQ | ATA_F_DMA }, + {PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20275, pci_init_pdc202xx, ata66_pdc202xx, ide_init_pdc202xx, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0, ATA_F_IRQ | ATA_F_DMA }, +#endif +#ifdef CONFIG_BLK_DEV_RZ1000 + {PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, NULL, NULL, ide_init_rz1000, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, NULL, NULL, ide_init_rz1000, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, +#endif +#ifdef CONFIG_BLK_DEV_SIS5513 + {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, pci_init_sis5513, ata66_sis5513, ide_init_sis5513, NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, ON_BOARD, 0, ATA_F_NOADMA }, +#endif +#ifdef CONFIG_BLK_DEV_CMD64X + {PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_643, pci_init_cmd64x, NULL, ide_init_cmd64x, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646, pci_init_cmd64x, NULL, ide_init_cmd64x, NULL, {{0x00,0x00,0x00}, {0x51,0x80,0x80}}, ON_BOARD, 0, ATA_F_DMA }, + {PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_648, pci_init_cmd64x, ata66_cmd64x, ide_init_cmd64x, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, ATA_F_DMA }, + {PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_649, pci_init_cmd64x, ata66_cmd64x, ide_init_cmd64x, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, ATA_F_DMA }, + {PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_680, pci_init_cmd64x, ata66_cmd64x, ide_init_cmd64x, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, ATA_F_DMA }, +#endif +#ifdef CONFIG_BLK_DEV_OPTI621 + {PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, NULL, NULL, ide_init_opti621, NULL, {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C825, NULL, NULL, ide_init_opti621, NULL, {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, ON_BOARD, 0, 0 }, +#endif +#ifdef CONFIG_BLK_DEV_TRM290 + {PCI_VENDOR_ID_TEKRAM, PCI_DEVICE_ID_TEKRAM_DC290, NULL, NULL, ide_init_trm290, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, +#endif +#ifdef CONFIG_BLK_DEV_NS87415 + {PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415, NULL, NULL, ide_init_ns87415, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, +#endif +#ifdef CONFIG_BLK_DEV_AEC62XX + {PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF, pci_init_aec62xx, NULL, ide_init_aec62xx, ide_dmacapable_aec62xx, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, OFF_BOARD, 0, ATA_F_SER | ATA_F_IRQ | ATA_F_DMA }, + {PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860, pci_init_aec62xx, ata66_aec62xx, ide_init_aec62xx, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, NEVER_BOARD, 0, ATA_F_IRQ | ATA_F_NOADMA | ATA_F_DMA }, + {PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860R, pci_init_aec62xx, ata66_aec62xx, ide_init_aec62xx, NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, OFF_BOARD, 0, ATA_F_IRQ | ATA_F_DMA }, +#endif +#ifdef CONFIG_BLK_DEV_SL82C105 + {PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, pci_init_sl82c105, NULL, ide_init_sl82c105, dma_init_sl82c105, {{0x40,0x01,0x01}, {0x40,0x10,0x10}}, ON_BOARD, 0, 0 }, +#endif +#ifdef CONFIG_BLK_DEV_HPT34X + {PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343, pci_init_hpt34x, NULL, ide_init_hpt34x, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, NEVER_BOARD, 16, ATA_F_NOADMA | ATA_F_DMA }, +#endif +#ifdef CONFIG_BLK_DEV_HPT366 + {PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366, pci_init_hpt366, ata66_hpt366, ide_init_hpt366, ide_dmacapable_hpt366, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 240, ATA_F_IRQ | ATA_F_HPTHACK | ATA_F_DMA }, +#endif +#ifdef CONFIG_BLK_DEV_ALI15X3 + {PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229, pci_init_ali15x3, ata66_ali15x3, ide_init_ali15x3, ide_dmacapable_ali15x3, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, +#endif +#ifdef CONFIG_BLK_DEV_CY82C693 + {PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693, pci_init_cy82c693, NULL, ide_init_cy82c693, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, ATA_F_DMA }, +#endif +#ifdef CONFIG_BLK_DEV_CS5530 + {PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE, pci_init_cs5530, NULL, ide_init_cs5530, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, ATA_F_DMA }, +#endif +#ifdef CONFIG_BLK_DEV_AMD74XX + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_COBRA_7401, NULL, NULL, NULL, ide_dmacapable_amd74xx, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7409, pci_init_amd74xx, ata66_amd74xx, ide_init_amd74xx, ide_dmacapable_amd74xx, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7411, pci_init_amd74xx, ata66_amd74xx, ide_init_amd74xx, ide_dmacapable_amd74xx, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7441, pci_init_amd74xx, ata66_amd74xx, ide_init_amd74xx, ide_dmacapable_amd74xx, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0, 0 }, +#endif +#ifdef CONFIG_BLK_DEV_PDC_ADMA + {PCI_VENDOR_ID_PDC, PCI_DEVICE_ID_PDC_1841, pci_init_pdcadma, ata66_pdcadma, ide_init_pdcadma, ide_dmacapable_pdcadma, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0, ATA_F_NODMA }, +#endif +#ifdef CONFIG_BLK_DEV_SLC90E66 + {PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_1, pci_init_slc90e66, ata66_slc90e66, ide_init_slc90e66, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, +#endif +#ifdef CONFIG_BLK_DEV_SVWKS + {PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, pci_init_svwks, ata66_svwks, ide_init_svwks, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, ATA_F_DMA }, + {PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, pci_init_svwks, ata66_svwks, ide_init_svwks, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, +#endif +#ifdef CONFIG_BLK_DEV_IT8172 + {PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G, pci_init_it8172, NULL, ide_init_it8172, NULL, {{0x00,0x00,0x00}, {0x40,0x00,0x01}}, ON_BOARD, 0, 0 }, +#endif + /* Those are id's of chips we don't deal currently with, + * but which still need some generic quirk handling. + */ + {PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_640, NULL, NULL, IDE_IGNORE, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, NULL, NULL, NULL, NULL, {{0x43,0x08,0x08}, {0x47,0x08,0x08}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_HINT, PCI_DEVICE_ID_HINT, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_HOLTEK, PCI_DEVICE_ID_HOLTEK_6565, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, + {PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8673F, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, ATA_F_FIXIRQ }, + {PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886A, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, ATA_F_FIXIRQ }, + {PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, ATA_F_FIXIRQ }, + {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, ATA_F_NOADMA }, + {PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366, NULL, NULL, IDE_NO_DRIVER, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 240, ATA_F_IRQ | ATA_F_HPTHACK }, + {0, 0, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }}; /* - * This allows offboard ide-pci cards the enable a BIOS, verify interrupt + * This allows off board ide-pci cards the enable a BIOS, verify interrupt * settings of split-mirror pci-config space, place chipset into init-mode, * and/or preserve an interrupt if the card is not native ide support. */ -static unsigned int __init ide_special_settings (struct pci_dev *dev, const char *name) +static unsigned int __init trust_pci_irq(ide_pci_device_t *d, struct pci_dev *dev) { - switch(dev->device) { - case PCI_DEVICE_ID_TTI_HPT366: - case PCI_DEVICE_ID_PROMISE_20246: - case PCI_DEVICE_ID_PROMISE_20262: - case PCI_DEVICE_ID_PROMISE_20265: - case PCI_DEVICE_ID_PROMISE_20267: - case PCI_DEVICE_ID_PROMISE_20268: - case PCI_DEVICE_ID_PROMISE_20268R: - case PCI_DEVICE_ID_PROMISE_20269: - case PCI_DEVICE_ID_PROMISE_20275: - case PCI_DEVICE_ID_ARTOP_ATP850UF: - case PCI_DEVICE_ID_ARTOP_ATP860: - case PCI_DEVICE_ID_ARTOP_ATP860R: - return dev->irq; - default: - break; - } + if (d->flags & ATA_F_IRQ) + return dev->irq; + return 0; } @@ -491,7 +334,7 @@ * Match a PCI IDE port against an entry in ide_hwifs[], * based on io_base port if possible. */ -static ide_hwif_t __init *ide_match_hwif (unsigned long io_base, byte bootable, const char *name) +static ide_hwif_t __init *lookup_hwif (unsigned long io_base, int bootable, const char *name) { int h; ide_hwif_t *hwif; @@ -553,9 +396,10 @@ return NULL; } -static int __init ide_setup_pci_baseregs (struct pci_dev *dev, const char *name) +static int __init setup_pci_baseregs (struct pci_dev *dev, const char *name) { - byte reg, progif = 0; + u8 reg; + u8 progif = 0; /* * Place both IDE interfaces into PCI "native" mode: @@ -566,7 +410,7 @@ return 1; } printk("%s: placing both ports into native PCI mode\n", name); - (void) pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5); + pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5); if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || (progif & 5) != 5) { printk("%s: rewrite of PROGIF failed, wanted 0x%04x, got 0x%04x\n", name, progif|5, progif); return 1; @@ -587,24 +431,252 @@ return 0; } +#ifdef CONFIG_BLK_DEV_IDEDMA + /* - * ide_setup_pci_device() looks at the primary/secondary interfaces - * on a PCI IDE device and, if they are enabled, prepares the IDE driver - * for use with them. This generic code works for most PCI chipsets. + * Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space: + */ +static unsigned long __init get_dma_base(ide_hwif_t *hwif, int extra, const char *name) +{ + unsigned long dma_base = 0; + struct pci_dev *dev = hwif->pci_dev; + + /* + * If we are on the second channel, the dma base address will be one + * entry away from the primary interface. + */ + + if (hwif->mate && hwif->mate->dma_base) + dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8); + else + dma_base = pci_resource_start(dev, 4); + + if (!dma_base) + return 0; + + if (extra) /* PDC20246, PDC20262, HPT343, & HPT366 */ + request_region(dma_base + 16, extra, name); + + dma_base += hwif->channel ? 8 : 0; + hwif->dma_extra = extra; + + if ((dev->vendor == PCI_VENDOR_ID_AL && dev->device == PCI_DEVICE_ID_AL_M5219) || + (dev->vendor == PCI_VENDOR_ID_AMD && dev->device == PCI_DEVICE_ID_AMD_VIPER_7409) || + (dev->vendor == PCI_VENDOR_ID_CMD && dev->device == PCI_DEVICE_ID_CMD_643)) { + outb(inb(dma_base + 2) & 0x60, dma_base+2); + if (inb(dma_base + 2) & 0x80) + printk(KERN_INFO "%s: simplex device: DMA forced\n", name); + } else { + + /* + * If the device claims "simplex" DMA, this means only one of + * the two interfaces can be trusted with DMA at any point in + * time. So we should enable DMA only on one of the two + * interfaces. + */ + + if ((inb(dma_base + 2) & 0x80)) { + if ((!hwif->drives[0].present && !hwif->drives[1].present) || + (hwif->mate && hwif->mate->dma_base)) { + printk("%s: simplex device: DMA disabled\n", name); + dma_base = 0; + } + } + } + + return dma_base; +} + +/* + * Setup DMA transfers on a channel. + */ +static void __init setup_channel_dma(ide_hwif_t *hwif, struct pci_dev *dev, + ide_pci_device_t *d, + int port, + u8 class_rev, + int pciirq, ide_hwif_t **mate, + int autodma, unsigned short *pcicmd) +{ + unsigned long dma_base; + + if (d->flags & ATA_F_NOADMA) + autodma = 0; + + if (autodma) + hwif->autodma = 1; + + if (!((d->flags & ATA_F_DMA) || ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 0x80)))) + return; + + dma_base = get_dma_base(hwif, (!*mate && d->extra) ? d->extra : 0, dev->name); + if (dma_base && !(*pcicmd & PCI_COMMAND_MASTER)) { + + /* + * Set up BM-DMA capability (PnP BIOS should have done this already) + */ + if (!(d->vendor == PCI_VENDOR_ID_CYRIX && d->device == PCI_DEVICE_ID_CYRIX_5530_IDE)) + hwif->autodma = 0; /* default DMA off if we had to configure it here */ + pci_write_config_word(dev, PCI_COMMAND, *pcicmd | PCI_COMMAND_MASTER); + if (pci_read_config_word(dev, PCI_COMMAND, pcicmd) || !(*pcicmd & PCI_COMMAND_MASTER)) { + printk("%s: %s error updating PCICMD\n", hwif->name, dev->name); + dma_base = 0; + } + } + if (dma_base) { + if (d->dma_init) + d->dma_init(hwif, dma_base); + else + ide_setup_dma(hwif, dma_base, 8); + } else + printk("%s: %s Bus-Master DMA was disabled by BIOS\n", hwif->name, dev->name); +} +#endif + +/* + * Setup a particular port on an ATA host controller. + * + * This gets called once for the master and for the slave interface. + */ +static int __init setup_host_channel(struct pci_dev *dev, + ide_pci_device_t *d, + int port, + u8 class_rev, + int pciirq, ide_hwif_t **mate, + int autodma, + unsigned short *pcicmd) +{ + unsigned long base = 0; + unsigned long ctl = 0; + ide_pci_enablebit_t *e = &(d->enablebits[port]); + ide_hwif_t *hwif; + + u8 tmp; + if (port == 1) { + + /* 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 (d->flags & ATA_F_PHACK) + goto controller_ok; + } + + /* Test whatever the port is enabled. + */ + if (e->reg) { + if (pci_read_config_byte(dev, e->reg, &tmp)) + return 0; /* error! */ + if ((tmp & e->mask) != e->val) + return 0; + } + + /* Nothing to be done for the second port. + */ + if (port == 1) { + if ((d->flags & ATA_F_HPTHACK) && (class_rev < 0x03)) + return 0; + } +controller_ok: + if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE || (dev->class & (port ? 4 : 1)) != 0) { + ctl = dev->resource[(2 * port) + 1].start; + base = dev->resource[2 * port].start; + if (!(ctl & PCI_BASE_ADDRESS_IO_MASK) || !(base & PCI_BASE_ADDRESS_IO_MASK)) { + printk(KERN_WARNING "%s: error: IO reported as MEM by BIOS!\n", dev->name); + /* try it with the default values */ + ctl = 0; + base = 0; + } + } + if (ctl && !base) { + printk(KERN_WARNING "%s: error: missing MEM base info from BIOS!\n", dev->name); + /* we will still try to get along with the default */ + } + if (base && !ctl) { + printk(KERN_WARNING "%s: error: missing IO base info from BIOS!\n", dev->name); + /* we will still try to get along with the default */ + } + + /* Fill in the default values: */ + if (!ctl) + ctl = port ? 0x374 : 0x3f4; + if (!base) + base = port ? 0x170 : 0x1f0; + + if ((hwif = lookup_hwif(base, d->bootable, dev->name)) == NULL) + return -ENOMEM; /* no room in ide_hwifs[] */ + + if (hwif->io_ports[IDE_DATA_OFFSET] != base) { + ide_init_hwif_ports(&hwif->hw, base, (ctl | 2), NULL); + memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports)); + hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET]; + } + + hwif->chipset = ide_pci; + hwif->pci_dev = dev; + hwif->channel = port; + if (!hwif->irq) + hwif->irq = pciirq; + + /* Setup the mate interface if we have two channels. + */ + if (*mate) { + hwif->mate = *mate; + (*mate)->mate = hwif; + if (d->flags & ATA_F_SER) { + hwif->serialized = 1; + (*mate)->serialized = 1; + } + } + + /* Cross wired IRQ lines on UMC chips and no DMA transfers.*/ + if (d->flags & ATA_F_FIXIRQ) { + hwif->irq = port ? 15 : 14; + goto no_dma; + } + if (d->flags & ATA_F_NODMA) + goto no_dma; + + /* Check whatever this interface is UDMA4 mode capable. */ + if (hwif->udma_four) { + printk("%s: warning: ATA-66/100 forced bit set!\n", dev->name); + } else { + if (d->ata66_check) + hwif->udma_four = d->ata66_check(hwif); + } + +#ifdef CONFIG_BLK_DEV_IDEDMA + setup_channel_dma(hwif, dev, d, port, class_rev, pciirq, mate, autodma, pcicmd); +#endif + +no_dma: + if (d->init_hwif) /* Call chipset-specific routine for each enabled hwif */ + d->init_hwif(hwif); + + *mate = hwif; + + return 0; +} + +/* + * Looks at the primary/secondary channels on a PCI IDE device and, if they + * are enabled, prepares the IDE driver for use with them. This generic code + * works for most PCI chipsets. * - * One thing that is not standardized is the location of the - * primary/secondary interface "enable/disable" bits. For chipsets that - * we "know" about, this information is in the ide_pci_device_t struct; - * for all other chipsets, we just assume both interfaces are enabled. + * One thing that is not standardized is the location of the primary/secondary + * interface "enable/disable" bits. For chipsets that we "know" about, this + * information is in the ide_pci_device_t struct; for all other chipsets, we + * just assume both interfaces are enabled. */ -static void __init ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d) + +static void __init setup_pci_device(struct pci_dev *dev, ide_pci_device_t *d) { - unsigned int port, at_least_one_hwif_enabled = 0, autodma = 0, pciirq = 0; - unsigned short pcicmd = 0, tried_config = 0; - byte tmp = 0; - ide_hwif_t *hwif, *mate = NULL; + int autodma = 0; + int pciirq = 0; + unsigned short pcicmd = 0; + unsigned short tried_config = 0; + ide_hwif_t *mate = NULL; unsigned int class_rev; - static int secondpdc = 0; #ifdef CONFIG_IDEDMA_AUTO if (!noautodma) @@ -612,65 +684,62 @@ #endif if (d->init_hwif == IDE_NO_DRIVER) { - printk(KERN_WARNING "%s: detected chipset, but driver not compiled in!\n", d->name); + printk(KERN_WARNING "%s: detected chipset, but driver not compiled in!\n", dev->name); d->init_hwif = NULL; } if (pci_enable_device(dev)) { - printk(KERN_WARNING "%s: (ide_setup_pci_device:) Could not enable device.\n", d->name); + printk(KERN_WARNING "%s: Could not enable PCI device.\n", dev->name); return; } check_if_enabled: if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd)) { - printk("%s: error accessing PCI regs\n", d->name); + printk("%s: error accessing PCI regs\n", dev->name); return; } if (!(pcicmd & PCI_COMMAND_IO)) { /* is device disabled? */ /* * PnP BIOS was *supposed* to have set this device up for us, - * but we can do it ourselves, so long as the BIOS has assigned an IRQ - * (or possibly the device is using a "legacy header" for IRQs). - * Maybe the user deliberately *disabled* the device, - * but we'll eventually ignore it again if no drives respond. + * but we can do it ourselves, so long as the BIOS has assigned + * an IRQ (or possibly the device is using a "legacy header" + * for IRQs). Maybe the user deliberately *disabled* the + * device, but we'll eventually ignore it again if no drives + * respond. */ if (tried_config++ - || ide_setup_pci_baseregs(dev, d->name) + || setup_pci_baseregs(dev, dev->name) || pci_write_config_word(dev, PCI_COMMAND, pcicmd | PCI_COMMAND_IO)) { - printk("%s: device disabled (BIOS)\n", d->name); + printk("%s: device disabled (BIOS)\n", dev->name); return; } autodma = 0; /* default DMA off if we had to configure it here */ goto check_if_enabled; } if (tried_config) - printk("%s: device enabled (Linux)\n", d->name); + printk("%s: device enabled (Linux)\n", dev->name); pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); class_rev &= 0xff; - if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X)) { - /* see comments in hpt34x.c on why..... */ - char *chipset_names[] = {"HPT343", "HPT345"}; - strcpy(d->name, chipset_names[(pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0]); + if (d->vendor == PCI_VENDOR_ID_TTI && PCI_DEVICE_ID_TTI_HPT343) { + /* see comments in hpt34x.c to see why... */ d->bootable = (pcicmd & PCI_COMMAND_MEMORY) ? OFF_BOARD : NEVER_BOARD; } - printk("%s: chipset revision %d\n", d->name, class_rev); + printk("%s: chipset revision %d\n", dev->name, class_rev); /* * Can we trust the reported IRQ? */ pciirq = dev->irq; - - if (dev->class >> 8 == PCI_CLASS_STORAGE_RAID) - { + + if (dev->class >> 8 == PCI_CLASS_STORAGE_RAID) { /* By rights we want to ignore these, but the Promise Fastrak people have some strange ideas about proprietary so we have - to act otherwise on those. The supertrak however we need + to act otherwise on those. The Supertrak however we need to skip */ - if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20265)) - { + if (d->vendor == PCI_VENDOR_ID_PROMISE && d->device == PCI_DEVICE_ID_PROMISE_20265) { printk(KERN_INFO "ide: Found promise 20265 in RAID mode.\n"); if(dev->bus->self && dev->bus->self->vendor == PCI_VENDOR_ID_INTEL && dev->bus->self->device == PCI_DEVICE_ID_INTEL_I960) @@ -679,180 +748,43 @@ return; } } - /* Its attached to something else, just a random bridge. + /* Its attached to something else, just a random bridge. Suspect a fastrak and fall through */ } if ((dev->class & ~(0xfa)) != ((PCI_CLASS_STORAGE_IDE << 8) | 5)) { - printk("%s: not 100%% native mode: will probe irqs later\n", d->name); + printk("%s: not 100%% native mode: will probe irqs later\n", dev->name); /* - * This allows offboard ide-pci cards the enable a BIOS, + * This allows off board ide-pci cards the enable a BIOS, * verify interrupt settings of split-mirror pci-config * space, place chipset into init-mode, and/or preserve * an interrupt if the card is not native ide support. */ - pciirq = (d->init_chipset) ? d->init_chipset(dev, d->name) : ide_special_settings(dev, d->name); + if (d->init_chipset) + pciirq = d->init_chipset(dev); + else + pciirq = trust_pci_irq(d, dev); } else if (tried_config) { - printk("%s: will probe irqs later\n", d->name); + printk("%s: will probe IRQs later\n", dev->name); pciirq = 0; } else if (!pciirq) { - printk("%s: bad irq (%d): will probe later\n", d->name, pciirq); + printk("%s: bad IRQ (%d): will probe later\n", dev->name, pciirq); pciirq = 0; } else { if (d->init_chipset) - (void) d->init_chipset(dev, d->name); + d->init_chipset(dev); #ifdef __sparc__ printk("%s: 100%% native mode on irq %s\n", - d->name, __irq_itoa(pciirq)); + dev->name, __irq_itoa(pciirq)); #else - printk("%s: 100%% native mode on irq %d\n", d->name, pciirq); + printk("%s: 100%% native mode on irq %d\n", dev->name, pciirq); #endif } /* - * Set up the IDE ports + * Set up IDE chanells. First the primary, then the secondary. */ - 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 ((IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20262)) && (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) { - ctl = dev->resource[(2*port)+1].start; - base = dev->resource[2*port].start; - if (!(ctl & PCI_BASE_ADDRESS_IO_MASK) || - !(base & PCI_BASE_ADDRESS_IO_MASK)) { - printk("%s: IO baseregs (BIOS) are reported as MEM, report to .\n", d->name); -#if 0 - /* FIXME! This really should check that it really gets the IO/MEM part right! */ - continue; -#endif - } - } - if ((ctl && !base) || (base && !ctl)) { - printk("%s: inconsistent baseregs (BIOS) for port %d, skipping\n", d->name, port); - continue; - } - if (!ctl) - ctl = port ? 0x374 : 0x3f4; /* use default value */ - if (!base) - base = port ? 0x170 : 0x1f0; /* use default value */ - if ((hwif = ide_match_hwif(base, d->bootable, d->name)) == NULL) - continue; /* no room in ide_hwifs[] */ - if (hwif->io_ports[IDE_DATA_OFFSET] != base) { - ide_init_hwif_ports(&hwif->hw, base, (ctl | 2), NULL); - memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports)); - hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET]; - } - hwif->chipset = ide_pci; - hwif->pci_dev = dev; - hwif->pci_devid = d->devid; - hwif->channel = port; - if (!hwif->irq) - hwif->irq = pciirq; - if (mate) { - hwif->mate = mate; - mate->mate = hwif; - if (IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210)) { - hwif->serialized = 1; - mate->serialized = 1; - } - } - if (IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886A) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886BF) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8673F)) { - hwif->irq = hwif->channel ? 15 : 14; - goto bypass_umc_dma; - } - if (IDE_PCI_DEVID_EQ(d->devid, DEVID_MPIIX)) - goto bypass_piix_dma; - if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDCADMA)) - goto bypass_legacy_dma; - if (hwif->udma_four) { - printk("%s: ATA-66/100 forced bit set (WARNING)!!\n", d->name); - } else { - hwif->udma_four = (d->ata66_check) ? d->ata66_check(hwif) : 0; - } -#ifdef CONFIG_BLK_DEV_IDEDMA - if (IDE_PCI_DEVID_EQ(d->devid, DEVID_SIS5513) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6260) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_PIIX4NX) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_VIA_IDE) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_MR_IDE) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_VP_IDE)) - autodma = 0; - if (autodma) - hwif->autodma = 1; - - if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20262) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20265) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20267) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20268) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20268R) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20269) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20275) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6260) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6260R) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_CS5530) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_CY82C693) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_CMD646) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_CMD648) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_CMD649) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_CMD680) || - IDE_PCI_DEVID_EQ(d->devid, DEVID_OSB4) || - ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 0x80))) { - unsigned long dma_base = ide_get_or_set_dma_base(hwif, (!mate && d->extra) ? d->extra : 0, d->name); - if (dma_base && !(pcicmd & PCI_COMMAND_MASTER)) { - /* - * Set up BM-DMA capability (PnP BIOS should have done this) - */ - if (!IDE_PCI_DEVID_EQ(d->devid, DEVID_CS5530)) - hwif->autodma = 0; /* default DMA off if we had to configure it here */ - (void) pci_write_config_word(dev, PCI_COMMAND, pcicmd | PCI_COMMAND_MASTER); - if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd) || !(pcicmd & PCI_COMMAND_MASTER)) { - printk("%s: %s error updating PCICMD\n", hwif->name, d->name); - dma_base = 0; - } - } - if (dma_base) { - if (d->dma_init) { - d->dma_init(hwif, dma_base); - } else { - ide_setup_dma(hwif, dma_base, 8); - } - } else { - printk("%s: %s Bus-Master DMA disabled (BIOS)\n", hwif->name, d->name); - } - } -#endif /* CONFIG_BLK_DEV_IDEDMA */ -bypass_legacy_dma: -bypass_piix_dma: -bypass_umc_dma: - if (d->init_hwif) /* Call chipset-specific routine for each enabled hwif */ - d->init_hwif(hwif); - mate = hwif; - at_least_one_hwif_enabled = 1; - } - if (!at_least_one_hwif_enabled) - printk("%s: neither IDE port enabled (BIOS)\n", d->name); + setup_host_channel(dev, d, 0, class_rev, pciirq, &mate, autodma, &pcicmd); + setup_host_channel(dev, d, 1, class_rev, pciirq, &mate, autodma, &pcicmd); } static void __init pdc20270_device_order_fixup (struct pci_dev *dev, ide_pci_device_t *d) @@ -860,9 +792,9 @@ struct pci_dev *dev2 = NULL, *findev; ide_pci_device_t *d2; - if ((dev->bus->self && - dev->bus->self->vendor == PCI_VENDOR_ID_DEC) && - (dev->bus->self->device == PCI_DEVICE_ID_DEC_21150)) { + if (dev->bus->self && + dev->bus->self->vendor == PCI_VENDOR_ID_DEC && + dev->bus->self->device == PCI_DEVICE_ID_DEC_21150) { if (PCI_SLOT(dev->devfn) & 2) { return; } @@ -871,7 +803,8 @@ if ((findev->vendor == dev->vendor) && (findev->device == dev->device) && (PCI_SLOT(findev->devfn) & 2)) { - byte irq = 0, irq2 = 0; + u8 irq = 0; + u8 irq2 = 0; dev2 = findev; pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); pci_read_config_byte(dev2, PCI_INTERRUPT_LINE, &irq2); @@ -884,13 +817,13 @@ } } - printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn); - ide_setup_pci_device(dev, d); + printk("%s: IDE controller on PCI bus %02x dev %02x\n", dev->name, dev->bus->number, dev->devfn); + setup_pci_device(dev, d); if (!dev2) return; d2 = d; - printk("%s: IDE controller on PCI bus %02x dev %02x\n", d2->name, dev2->bus->number, dev2->devfn); - ide_setup_pci_device(dev2, d2); + printk("%s: IDE controller on PCI bus %02x dev %02x\n", dev2->name, dev2->bus->number, dev2->devfn); + setup_pci_device(dev2, d2); } static void __init hpt366_device_order_fixup (struct pci_dev *dev, ide_pci_device_t *d) @@ -899,7 +832,6 @@ ide_pci_device_t *d2; unsigned char pin1 = 0, pin2 = 0; unsigned int class_rev; - char *chipset_names[] = {"HPT366", "HPT366", "HPT368", "HPT370", "HPT370A"}; if (PCI_FUNC(dev->devfn) & 1) return; @@ -907,20 +839,18 @@ pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); class_rev &= 0xff; - strcpy(d->name, chipset_names[class_rev]); - switch(class_rev) { case 4: - case 3: printk("%s: IDE controller on PCI slot %s\n", d->name, dev->slot_name); - ide_setup_pci_device(dev, d); + case 3: printk("%s: IDE controller on PCI slot %s\n", dev->name, dev->slot_name); + setup_pci_device(dev, d); return; default: break; } pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin1); pci_for_each_dev(findev) { - if ((findev->vendor == dev->vendor) && - (findev->device == dev->device) && + if (findev->vendor == dev->vendor && + findev->device == dev->device && ((findev->devfn - dev->devfn) == 1) && (PCI_FUNC(findev->devfn) & 1)) { dev2 = findev; @@ -929,7 +859,7 @@ hpt363_shared_irq = (dev->irq == dev2->irq) ? 1 : 0; 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); + printk("%s: onboard version of chipset, pin1=%d pin2=%d\n", dev->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); @@ -940,48 +870,55 @@ break; } } - printk("%s: IDE controller on PCI slot %s\n", d->name, dev->slot_name); - ide_setup_pci_device(dev, d); + printk("%s: IDE controller on PCI slot %s\n", dev->name, dev->slot_name); + setup_pci_device(dev, d); if (!dev2) return; d2 = d; - printk("%s: IDE controller on PCI slot %s\n", d2->name, dev2->slot_name); - ide_setup_pci_device(dev2, d2); + printk("%s: IDE controller on PCI slot %s\n", dev2->name, dev2->slot_name); + setup_pci_device(dev2, d2); } /* - * ide_scan_pcibus() gets invoked at boot time from ide.c. - * It finds all PCI IDE controllers and calls ide_setup_pci_device for them. + * This finds all PCI IDE controllers and calls appropriate initialization + * functions for them. */ -void __init ide_scan_pcidev (struct pci_dev *dev) +static void __init ide_scan_pcidev(struct pci_dev *dev) { - ide_pci_devid_t devid; + unsigned short vendor; + unsigned short device; ide_pci_device_t *d; - devid.vid = dev->vendor; - devid.did = dev->device; - for (d = ide_pci_chipsets; d->devid.vid && !IDE_PCI_DEVID_EQ(d->devid, devid); ++d); + vendor = dev->vendor; + device = dev->device; + + /* Look up the chipset information. + */ + d = pci_chipsets; + while (d->vendor && !(d->vendor == vendor && d->device == device)) + ++d; + if (d->init_hwif == IDE_IGNORE) - printk("%s: ignored by ide_scan_pci_device() (uses own driver)\n", d->name); - else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_OPTI621V) && !(PCI_FUNC(dev->devfn) & 1)) + printk("%s: has been ignored by PCI bus scan\n", dev->name); + else if ((d->vendor == PCI_VENDOR_ID_OPTI && d->device == PCI_DEVICE_ID_OPTI_82C558) && !(PCI_FUNC(dev->devfn) & 1)) return; - else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_CY82C693) && (!(PCI_FUNC(dev->devfn) & 1) || !((dev->class >> 8) == PCI_CLASS_STORAGE_IDE))) + else if ((d->vendor == PCI_VENDOR_ID_CONTAQ && d->device == PCI_DEVICE_ID_CONTAQ_82C693) && (!(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))) + else if ((d->vendor == PCI_VENDOR_ID_ITE && d->device == PCI_DEVICE_ID_ITE_IT8172G) && (!(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)) + else if ((d->vendor == PCI_VENDOR_ID_UMC && d->device == PCI_DEVICE_ID_UMC_UM8886A) && !(PCI_FUNC(dev->devfn) & 1)) return; /* UM8886A/BF pair */ - else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366)) + else if (d->flags & ATA_F_HPTHACK) hpt366_device_order_fixup(dev, d); - else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20268R)) + else if (d->vendor == PCI_VENDOR_ID_PROMISE && d->device == PCI_DEVICE_ID_PROMISE_20268R) pdc20270_device_order_fixup(dev, d); - else if (!IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL) || (dev->class >> 8) == PCI_CLASS_STORAGE_IDE) { - if (IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL)) - printk("%s: unknown IDE controller on PCI slot %s, VID=%04x, DID=%04x\n", - d->name, dev->slot_name, devid.vid, devid.did); + else if (!(d->vendor == 0 && d->device == 0) || (dev->class >> 8) == PCI_CLASS_STORAGE_IDE) { + if (d->vendor == 0 && d->device == 0) + printk("%s: unknown IDE controller on PCI slot %s, vendor=%04x, device=%04x\n", + dev->name, dev->slot_name, vendor, device); else - printk("%s: IDE controller on PCI slot %s\n", d->name, dev->slot_name); - ide_setup_pci_device(dev, d); + printk("%s: IDE controller on PCI slot %s\n", dev->name, dev->slot_name); + setup_pci_device(dev, d); } } diff -Nru a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c --- a/drivers/ide/ide-pnp.c Thu Mar 7 18:17:41 2002 +++ b/drivers/ide/ide-pnp.c Thu Mar 7 18:17:41 2002 @@ -57,6 +57,7 @@ static int __init pnpide_generic_init(struct pci_dev *dev, int enable) { hw_regs_t hw; + ide_hwif_t *hwif; int index; if (!enable) @@ -69,10 +70,11 @@ generic_ide_offsets, (ide_ioreg_t) DEV_IO(dev, 1), 0, NULL, DEV_IRQ(dev, 0)); - index = ide_register_hw(&hw, NULL); + index = ide_register_hw(&hw, &hwif); if (index != -1) { - printk("ide%d: %s IDE interface\n", index, DEV_NAME(dev)); + hwif->pci_dev = dev; + printk("ide%d: %s IDE interface\n", index, DEV_NAME(dev)); return 0; } diff -Nru a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c --- a/drivers/ide/ide-probe.c Thu Mar 7 18:17:36 2002 +++ b/drivers/ide/ide-probe.c Thu Mar 7 18:17:36 2002 @@ -1,10 +1,6 @@ /* - * linux/drivers/ide/ide-probe.c Version 1.07 March 18, 2001 - * * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) - */ - -/* + * * Mostly written by Mark Lord * and Gadi Oxman * and Andre Hedrick @@ -26,11 +22,9 @@ * with new flag : drive->ata_flash : 1; * Version 1.06 stream line request queue and prep for cascade project. * Version 1.07 max_sect <= 255; slower disks would get behind and - * then fall over when they get to 256. Paul G. + * then fall over when they get to 256. Paul G. */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #include #include #include @@ -46,6 +40,7 @@ #include #include #include +#include #include #include @@ -93,7 +88,7 @@ printk("%s: EATA SCSI HBA %.10s\n", drive->name, id->model); goto err_misc; } -#endif /* CONFIG_SCSI_EATA_DMA || CONFIG_SCSI_EATA_PIO */ +#endif /* * WIN_IDENTIFY returns little-endian info, @@ -127,19 +122,19 @@ printk(" -- not supported on 2nd Promise port\n"); goto err_misc; } -#endif /* CONFIG_BLK_DEV_PDC4030 */ +#endif switch (type) { - case ide_floppy: + case ATA_FLOPPY: if (!strstr(id->model, "CD-ROM")) { if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP")) printk("cdrom or floppy?, assuming "); - if (drive->media != ide_cdrom) { + if (drive->type != ATA_ROM) { printk ("FLOPPY"); break; } } - type = ide_cdrom; /* Early cdrom models used zero */ - case ide_cdrom: + type = ATA_ROM; /* Early cdrom models used zero */ + case ATA_ROM: drive->removable = 1; #ifdef CONFIG_PPC /* kludge for Apple PowerBook internal zip */ @@ -151,10 +146,10 @@ #endif printk ("CD/DVD-ROM"); break; - case ide_tape: + case ATA_TAPE: printk ("TAPE"); break; - case ide_optical: + case ATA_MOD: printk ("OPTICAL"); drive->removable = 1; break; @@ -163,7 +158,7 @@ break; } printk (" drive\n"); - drive->media = type; + drive->type = type; return; } @@ -183,9 +178,13 @@ mate->noprobe = 1; } } - drive->media = ide_disk; + drive->type = ATA_DISK; printk("ATA DISK drive\n"); - QUIRK_LIST(HWIF(drive),drive); + + /* Initialize our quirk list. */ + if (HWIF(drive)->quirkproc) + drive->quirk_list = HWIF(drive)->quirkproc(drive); + return; err_misc: @@ -281,24 +280,15 @@ if (autoprobe) { int irq; - OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* mask device irq */ - (void) GET_STAT(); /* clear drive IRQ */ + OUT_BYTE(drive->ctl | 0x02, IDE_CONTROL_REG); /* mask device irq */ + GET_STAT(); /* clear drive IRQ */ udelay(5); irq = probe_irq_off(cookie); if (!HWIF(drive)->irq) { - if (irq > 0) { + if (irq > 0) HWIF(drive)->irq = irq; - } else { /* Mmmm.. multiple IRQs.. don't know which was ours */ + else /* Mmmm.. multiple IRQs.. don't know which was ours */ printk("%s: IRQ probe failed (0x%lx)\n", drive->name, cookie); -#ifdef CONFIG_BLK_DEV_CMD640 -#ifdef CMD640_DUMP_REGS - if (HWIF(drive)->chipset == ide_cmd640) { - printk("%s: Hmmm.. probably a driver problem.\n", drive->name); - CMD640_DUMP_REGS; - } -#endif /* CMD640_DUMP_REGS */ -#endif /* CONFIG_BLK_DEV_CMD640 */ - } } } return retval; @@ -326,12 +316,12 @@ int rc; ide_hwif_t *hwif = HWIF(drive); if (drive->present) { /* avoid waiting for inappropriate probes */ - if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY)) + if ((drive->type != ATA_DISK) && (cmd == WIN_IDENTIFY)) return 4; } #ifdef DEBUG - printk("probing for %s: present=%d, media=%d, probetype=%s\n", - drive->name, drive->present, drive->media, + printk("probing for %s: present=%d, type=%d, probetype=%s\n", + drive->name, drive->present, drive->type, (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI"); #endif ide_delay_50ms(); /* needed for some systems (e.g. crw9624 as drive0 with disk as slave) */ @@ -420,10 +410,10 @@ if (!drive->present) return; /* drive not found */ if (drive->id == NULL) { /* identification failed? */ - if (drive->media == ide_disk) { + if (drive->type == ATA_DISK) { printk ("%s: non-IDE drive, CHS=%d/%d/%d\n", drive->name, drive->cyl, drive->head, drive->sect); - } else if (drive->media == ide_cdrom) { + } else if (drive->type == ATA_ROM) { printk("%s: ATAPI cdrom (?)\n", drive->name); } else { drive->present = 0; /* nuke it */ @@ -465,37 +455,48 @@ static void hwif_register (ide_hwif_t *hwif) { + /* Register this hardware interface within the global device tree. + */ + sprintf(hwif->device.bus_id, "%04x", hwif->io_ports[IDE_DATA_OFFSET]); + sprintf(hwif->device.name, "ide"); + hwif->device.driver_data = hwif; +#ifdef CONFIG_BLK_DEV_IDEPCI + if (hwif->pci_dev) + hwif->device.parent = &hwif->pci_dev->dev; + else +#endif + hwif->device.parent = NULL; /* Would like to do = &device_legacy */ + device_register(&hwif->device); + if (((unsigned long)hwif->io_ports[IDE_DATA_OFFSET] | 7) == ((unsigned long)hwif->io_ports[IDE_STATUS_OFFSET])) { ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 8, hwif->name); hwif->straight8 = 1; - goto jump_straight8; - } - - if (hwif->io_ports[IDE_DATA_OFFSET]) - ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 1, hwif->name); - if (hwif->io_ports[IDE_ERROR_OFFSET]) - ide_request_region(hwif->io_ports[IDE_ERROR_OFFSET], 1, hwif->name); - if (hwif->io_ports[IDE_NSECTOR_OFFSET]) - ide_request_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1, hwif->name); - if (hwif->io_ports[IDE_SECTOR_OFFSET]) - ide_request_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1, hwif->name); - if (hwif->io_ports[IDE_LCYL_OFFSET]) - ide_request_region(hwif->io_ports[IDE_LCYL_OFFSET], 1, hwif->name); - if (hwif->io_ports[IDE_HCYL_OFFSET]) - ide_request_region(hwif->io_ports[IDE_HCYL_OFFSET], 1, hwif->name); - if (hwif->io_ports[IDE_SELECT_OFFSET]) - ide_request_region(hwif->io_ports[IDE_SELECT_OFFSET], 1, hwif->name); - if (hwif->io_ports[IDE_STATUS_OFFSET]) - ide_request_region(hwif->io_ports[IDE_STATUS_OFFSET], 1, hwif->name); + } else { + if (hwif->io_ports[IDE_DATA_OFFSET]) + ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 1, hwif->name); + if (hwif->io_ports[IDE_ERROR_OFFSET]) + ide_request_region(hwif->io_ports[IDE_ERROR_OFFSET], 1, hwif->name); + if (hwif->io_ports[IDE_NSECTOR_OFFSET]) + ide_request_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1, hwif->name); + if (hwif->io_ports[IDE_SECTOR_OFFSET]) + ide_request_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1, hwif->name); + if (hwif->io_ports[IDE_LCYL_OFFSET]) + ide_request_region(hwif->io_ports[IDE_LCYL_OFFSET], 1, hwif->name); + if (hwif->io_ports[IDE_HCYL_OFFSET]) + ide_request_region(hwif->io_ports[IDE_HCYL_OFFSET], 1, hwif->name); + if (hwif->io_ports[IDE_SELECT_OFFSET]) + ide_request_region(hwif->io_ports[IDE_SELECT_OFFSET], 1, hwif->name); + if (hwif->io_ports[IDE_STATUS_OFFSET]) + ide_request_region(hwif->io_ports[IDE_STATUS_OFFSET], 1, hwif->name); -jump_straight8: + } if (hwif->io_ports[IDE_CONTROL_OFFSET]) ide_request_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1, hwif->name); #if defined(CONFIG_AMIGA) || defined(CONFIG_MAC) if (hwif->io_ports[IDE_IRQ_OFFSET]) ide_request_region(hwif->io_ports[IDE_IRQ_OFFSET], 1, hwif->name); -#endif /* (CONFIG_AMIGA) || (CONFIG_MAC) */ +#endif } /* @@ -509,19 +510,12 @@ if (hwif->noprobe) return; -#ifdef CONFIG_BLK_DEV_IDE - if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA) { - extern void probe_cmos_for_drives(ide_hwif_t *); - probe_cmos_for_drives (hwif); - } -#endif - - if ((hwif->chipset != ide_4drives || !hwif->mate->present) && + if ( #if CONFIG_BLK_DEV_PDC4030 (hwif->chipset != ide_pdc4030 || hwif->channel == 0) && -#endif /* CONFIG_BLK_DEV_PDC4030 */ - (hwif_check_regions(hwif))) { +#endif + hwif_check_regions(hwif)) { int msgout = 0; for (unit = 0; unit < MAX_DRIVES; ++unit) { ide_drive_t *drive = &hwif->drives[unit]; @@ -533,7 +527,7 @@ } if (!msgout) printk("%s: ports already in use, skipping probe\n", hwif->name); - return; + return; } __save_flags(flags); /* local CPU only */ @@ -547,9 +541,7 @@ probe_for_drive (drive); if (drive->present && !hwif->present) { hwif->present = 1; - if (hwif->chipset != ide_4drives || !hwif->mate->present) { - hwif_register(hwif); - } + hwif_register(hwif); } } if (hwif->io_ports[IDE_CONTROL_OFFSET] && hwif->reset) { @@ -577,32 +569,6 @@ } } -#if MAX_HWIFS > 1 -/* - * save_match() is used to simplify logic in init_irq() below. - * - * A loophole here is that we may not know about a particular - * hwif's irq until after that hwif is actually probed/initialized.. - * This could be a problem for the case where an hwif is on a - * dual interface that requires serialization (eg. cmd640) and another - * hwif using one of the same irqs is initialized beforehand. - * - * This routine detects and reports such situations, but does not fix them. - */ -static void save_match (ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match) -{ - ide_hwif_t *m = *match; - - if (m && m->hwgroup && m->hwgroup != new->hwgroup) { - if (!new->hwgroup) - return; - printk("%s: potential irq problem with %s and %s\n", hwif->name, new->name, m->name); - } - if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */ - *match = new; -} -#endif /* MAX_HWIFS > 1 */ - /* * init request queue */ @@ -631,6 +597,33 @@ blk_queue_max_phys_segments(q, PRD_ENTRIES); } +#if MAX_HWIFS > 1 + +/* + * This is used to simplify logic in init_irq() below. + * + * A loophole here is that we may not know about a particular hwif's irq until + * after that hwif is actually probed/initialized.. This could be a problem + * for the case where an hwif is on a dual interface that requires + * serialization (eg. cmd640) and another hwif using one of the same irqs is + * initialized beforehand. + * + * This routine detects and reports such situations, but does not fix them. + */ +static void save_match(ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match) +{ + ide_hwif_t *m = *match; + + if (m && m->hwgroup && m->hwgroup != new->hwgroup) { + if (!new->hwgroup) + return; + printk("%s: potential irq problem with %s and %s\n", hwif->name, new->name, m->name); + } + if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */ + *match = new; +} +#endif + /* * This routine sets up the irq for an ide interface, and creates a new * hwgroup for the irq/hwif if none was previously assigned. @@ -651,15 +644,14 @@ ide_hwgroup_t *hwgroup, *new_hwgroup; ide_hwif_t *match = NULL; - + /* Allocate the buffer and potentially sleep first */ - + new_hwgroup = kmalloc(sizeof(ide_hwgroup_t),GFP_KERNEL); - - save_flags(flags); /* all CPUs */ - cli(); /* all CPUs */ + spin_lock_irqsave(&ide_lock, flags); hwif->hwgroup = NULL; + #if MAX_HWIFS > 1 /* * Group up with any other hwifs that share our irq(s). @@ -669,9 +661,8 @@ if (h->hwgroup) { /* scan only initialized hwif's */ if (hwif->irq == h->irq) { hwif->sharing_irq = h->sharing_irq = 1; - if (hwif->chipset != ide_pci || h->chipset != ide_pci) { + if (hwif->chipset != ide_pci || h->chipset != ide_pci) save_match(hwif, h, &match); - } } if (hwif->serialized) { if (hwif->mate && hwif->mate->irq == h->irq) @@ -683,7 +674,7 @@ } } } -#endif /* MAX_HWIFS > 1 */ +#endif /* * If we are still without a hwgroup, then form a new one */ @@ -694,7 +685,7 @@ } else { hwgroup = new_hwgroup; if (!hwgroup) { - restore_flags(flags); /* all CPUs */ + spin_unlock_irqrestore(&ide_lock, flags); return 1; } memset(hwgroup, 0, sizeof(ide_hwgroup_t)); @@ -714,9 +705,9 @@ if (!match || match->irq != hwif->irq) { #ifdef CONFIG_IDEPCI_SHARE_IRQ int sa = IDE_CHIPSET_IS_PCI(hwif->chipset) ? SA_SHIRQ : SA_INTERRUPT; -#else /* !CONFIG_IDEPCI_SHARE_IRQ */ +#else int sa = IDE_CHIPSET_IS_PCI(hwif->chipset) ? SA_INTERRUPT|SA_SHIRQ : SA_INTERRUPT; -#endif /* CONFIG_IDEPCI_SHARE_IRQ */ +#endif if (hwif->io_ports[IDE_CONTROL_OFFSET]) OUT_BYTE(0x08, hwif->io_ports[IDE_CONTROL_OFFSET]); /* clear nIEN */ @@ -724,13 +715,13 @@ if (ide_request_irq(hwif->irq, &ide_intr, sa, hwif->name, hwgroup)) { if (!match) kfree(hwgroup); - restore_flags(flags); /* all CPUs */ + spin_unlock_irqrestore(&ide_lock, flags); return 1; } } /* - * Everything is okay, so link us into the hwgroup + * Everything is okay, so link us into the hwgroup. */ hwif->hwgroup = hwgroup; hwif->next = hwgroup->hwif->next; @@ -752,7 +743,7 @@ printk("%s : Adding missed hwif to hwgroup!!\n", hwif->name); #endif } - restore_flags(flags); /* all CPUs; safe now that hwif->hwgroup is set up */ + spin_unlock_irqrestore(&ide_lock, flags); #if !defined(__mc68000__) && !defined(CONFIG_APUS) && !defined(__sparc__) printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", hwif->name, @@ -784,60 +775,51 @@ static void init_gendisk (ide_hwif_t *hwif) { struct gendisk *gd; - unsigned int unit, units, minors, i; + unsigned int unit, minors, i; extern devfs_handle_t ide_devfs_handle; -#if 1 - units = MAX_DRIVES; -#else - /* figure out maximum drive number on the interface */ - for (units = MAX_DRIVES; units > 0; --units) { - if (hwif->drives[units-1].present) - break; - } -#endif - - minors = units * (1<sizes = kmalloc (minors * sizeof(int), GFP_KERNEL); if (!gd->sizes) goto err_kmalloc_gd_sizes; + gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL); if (!gd->part) goto err_kmalloc_gd_part; + memset(gd->part, 0, minors * sizeof(struct hd_struct)); + blksize_size[hwif->major] = kmalloc (minors*sizeof(int), GFP_KERNEL); if (!blksize_size[hwif->major]) goto err_kmalloc_bs; - - memset(gd->part, 0, minors * sizeof(struct hd_struct)); - for (i = 0; i < minors; ++i) blksize_size[hwif->major][i] = BLOCK_SIZE; - for (unit = 0; unit < units; ++unit) + + for (unit = 0; unit < MAX_DRIVES; ++unit) hwif->drives[unit].part = &gd->part[unit << PARTN_BITS]; gd->major = hwif->major; /* our major device number */ gd->major_name = IDE_MAJOR_NAME; /* treated special in genhd.c */ gd->minor_shift = PARTN_BITS; /* num bits for partitions */ - gd->nr_real = units; /* current num real drives */ + gd->nr_real = MAX_DRIVES; /* current num real drives */ gd->next = NULL; /* linked list of major devs */ gd->fops = ide_fops; /* file operations */ - gd->de_arr = kmalloc (sizeof *gd->de_arr * units, GFP_KERNEL); - gd->flags = kmalloc (sizeof *gd->flags * units, GFP_KERNEL); + gd->de_arr = kmalloc(sizeof(*gd->de_arr) * MAX_DRIVES, GFP_KERNEL); + gd->flags = kmalloc(sizeof(*gd->flags) * MAX_DRIVES, GFP_KERNEL); if (gd->de_arr) - memset (gd->de_arr, 0, sizeof *gd->de_arr * units); + memset(gd->de_arr, 0, sizeof(*gd->de_arr) * MAX_DRIVES); if (gd->flags) - memset (gd->flags, 0, sizeof *gd->flags * units); + memset(gd->flags, 0, sizeof(*gd->flags) * MAX_DRIVES); hwif->gd = gd; add_gendisk(gd); - for (unit = 0; unit < units; ++unit) { -#if 1 - char name[64]; + for (unit = 0; unit < MAX_DRIVES; ++unit) { + char name[80]; ide_add_generic_settings(hwif->drives + unit); hwif->drives[unit].dn = ((hwif->channel ? 2 : 0) + unit); sprintf (name, "host%d/bus%d/target%d/lun%d", @@ -846,19 +828,6 @@ hwif->channel, unit, hwif->drives[unit].lun); if (hwif->drives[unit].present) hwif->drives[unit].de = devfs_mk_dir(ide_devfs_handle, name, NULL); -#else - if (hwif->drives[unit].present) { - char name[64]; - - ide_add_generic_settings(hwif->drives + unit); - hwif->drives[unit].dn = ((hwif->channel ? 2 : 0) + unit); - sprintf (name, "host%d/bus%d/target%d/lun%d", - (hwif->channel && hwif->mate) ? hwif->mate->index : hwif->index, - hwif->channel, unit, hwif->drives[unit].lun); - hwif->drives[unit].de = - devfs_mk_dir (ide_devfs_handle, name, NULL); - } -#endif } return; @@ -869,7 +838,7 @@ err_kmalloc_gd_sizes: kfree(gd); err_kmalloc_gd: - printk(KERN_WARNING "(ide::init_gendisk) Out of memory\n"); + printk(KERN_CRIT "(ide::init_gendisk) Out of memory\n"); return; } diff -Nru a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c --- a/drivers/ide/ide-proc.c Thu Mar 7 18:17:37 2002 +++ b/drivers/ide/ide-proc.c Thu Mar 7 18:17:37 2002 @@ -127,242 +127,8 @@ int (*via_display_info)(char *, char **, off_t, int) = NULL; #endif /* CONFIG_BLK_DEV_VIA82CXXX */ -static int ide_getxdigit(char c) -{ - int digit; - if (isdigit(c)) - digit = c - '0'; - else if (isxdigit(c)) - digit = tolower(c) - 'a' + 10; - else - digit = -1; - return digit; -} - -static int xx_xx_parse_error (const char *data, unsigned long len, const char *msg) -{ - char errbuf[16]; - int i; - if (len >= sizeof(errbuf)) - len = sizeof(errbuf) - 1; - for (i = 0; i < len; ++i) { - char c = data[i]; - if (!c || c == '\n') - c = '\0'; - else if (iscntrl(c)) - c = '?'; - errbuf[i] = c; - } - errbuf[i] = '\0'; - printk("proc_ide: error: %s: '%s'\n", msg, errbuf); - return -EINVAL; -} - static struct proc_dir_entry * proc_ide_root = NULL; -static int proc_ide_write_config - (struct file *file, const char *buffer, unsigned long count, void *data) -{ - ide_hwif_t *hwif = data; - int for_real = 0; - unsigned long startn = 0, n, flags; - const char *start = NULL, *msg = NULL; - - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) - return -EACCES; - /* - * Skip over leading whitespace - */ - while (count && isspace(*buffer)) { - --count; - ++buffer; - } - /* - * Do one full pass to verify all parameters, - * then do another to actually write the regs. - */ - save_flags(flags); /* all CPUs */ - do { - const char *p; - if (for_real) { - unsigned long timeout = jiffies + (3 * HZ); - ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup); - ide_hwgroup_t *mategroup = NULL; - if (hwif->mate && hwif->mate->hwgroup) - mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup); - cli(); /* all CPUs; ensure all writes are done together */ - while (test_bit(IDE_BUSY, &mygroup->flags) || (mategroup && test_bit(IDE_BUSY, &mategroup->flags))) { - sti(); /* all CPUs */ - if (0 < (signed long)(jiffies - timeout)) { - printk("/proc/ide/%s/config: channel(s) busy, cannot write\n", hwif->name); - restore_flags(flags); /* all CPUs */ - return -EBUSY; - } - cli(); /* all CPUs */ - } - } - p = buffer; - n = count; - while (n > 0) { - int d, digits; - unsigned int reg = 0, val = 0, is_pci; - start = p; - startn = n--; - switch (*p++) { - case 'R': is_pci = 0; - break; - case 'P': is_pci = 1; -#ifdef CONFIG_BLK_DEV_IDEPCI - if (hwif->pci_dev && !IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL)) - break; -#endif /* CONFIG_BLK_DEV_IDEPCI */ - msg = "not a PCI device"; - goto parse_error; - default: msg = "expected 'R' or 'P'"; - goto parse_error; - } - digits = 0; - while (n > 0 && (d = ide_getxdigit(*p)) >= 0) { - reg = (reg << 4) | d; - --n; - ++p; - ++digits; - } - if (!digits || (digits > 4) || (is_pci && reg > 0xff)) { - msg = "bad/missing register number"; - goto parse_error; - } - if (n-- == 0 || *p++ != ':') { - msg = "missing ':'"; - goto parse_error; - } - digits = 0; - while (n > 0 && (d = ide_getxdigit(*p)) >= 0) { - val = (val << 4) | d; - --n; - ++p; - ++digits; - } - if (digits != 2 && digits != 4 && digits != 8) { - msg = "bad data, 2/4/8 digits required"; - goto parse_error; - } - if (n > 0 && !isspace(*p)) { - msg = "expected whitespace after data"; - goto parse_error; - } - while (n > 0 && isspace(*p)) { - --n; - ++p; - } -#ifdef CONFIG_BLK_DEV_IDEPCI - if (is_pci && (reg & ((digits >> 1) - 1))) { - msg = "misaligned access"; - goto parse_error; - } -#endif /* CONFIG_BLK_DEV_IDEPCI */ - if (for_real) { -#if 0 - printk("proc_ide_write_config: type=%c, reg=0x%x, val=0x%x, digits=%d\n", is_pci ? "PCI" : "non-PCI", reg, val, digits); -#endif - if (is_pci) { -#ifdef CONFIG_BLK_DEV_IDEPCI - int rc = 0; - struct pci_dev *dev = hwif->pci_dev; - switch (digits) { - case 2: msg = "byte"; - rc = pci_write_config_byte(dev, reg, val); - break; - case 4: msg = "word"; - rc = pci_write_config_word(dev, reg, val); - break; - case 8: msg = "dword"; - rc = pci_write_config_dword(dev, reg, val); - break; - } - if (rc) { - restore_flags(flags); /* all CPUs */ - printk("proc_ide_write_config: error writing %s at bus %02x dev %02x reg 0x%x value 0x%x\n", - msg, dev->bus->number, dev->devfn, reg, val); - printk("proc_ide_write_config: error %d\n", rc); - return -EIO; - } -#endif /* CONFIG_BLK_DEV_IDEPCI */ - } else { /* not pci */ -#if !defined(__mc68000__) && !defined(CONFIG_APUS) - -/* - * Geert Uytterhoeven - * - * unless you can explain me what it really does. - * On m68k, we don't have outw() and outl() yet, - * and I need a good reason to implement it. - * - * BTW, IMHO the main remaining portability problem with the IDE driver - * is that it mixes IO (ioport) and MMIO (iomem) access on different platforms. - * - * I think all accesses should be done using - * - * ide_in[bwl](ide_device_instance, offset) - * ide_out[bwl](ide_device_instance, value, offset) - * - * so the architecture specific code can #define ide_{in,out}[bwl] to the - * appropriate function. - * - */ - switch (digits) { - case 2: outb(val, reg); - break; - case 4: outw(val, reg); - break; - case 8: outl(val, reg); - break; - } -#endif /* !__mc68000__ && !CONFIG_APUS */ - } - } - } - } while (!for_real++); - restore_flags(flags); /* all CPUs */ - return count; -parse_error: - restore_flags(flags); /* all CPUs */ - printk("parse error\n"); - return xx_xx_parse_error(start, startn, msg); -} - -static int proc_ide_read_config - (char *page, char **start, off_t off, int count, int *eof, void *data) -{ - char *out = page; - int len; - -#ifdef CONFIG_BLK_DEV_IDEPCI - ide_hwif_t *hwif = data; - struct pci_dev *dev = hwif->pci_dev; - if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL) && dev && dev->bus) { - int reg = 0; - - out += sprintf(out, "pci bus %02x device %02x vid %04x did %04x channel %d\n", - dev->bus->number, dev->devfn, hwif->pci_devid.vid, hwif->pci_devid.did, hwif->channel); - do { - byte val; - int rc = pci_read_config_byte(dev, reg, &val); - if (rc) { - printk("proc_ide_read_config: error %d reading bus %02x dev %02x reg 0x%02x\n", - rc, dev->bus->number, dev->devfn, reg); - out += sprintf(out, "??%c", (++reg & 0xf) ? ' ' : '\n'); - } else - out += sprintf(out, "%02x%c", val, (++reg & 0xf) ? ' ' : '\n'); - } while (reg < 0x100); - } else -#endif /* CONFIG_BLK_DEV_IDEPCI */ - out += sprintf(out, "(none)\n"); - len = out - page; - PROC_IDE_READ_RETURN(page,start,off,count,eof,len); -} - - static int ide_getdigit(char c) { int digit; @@ -373,20 +139,6 @@ return digit; } -static int proc_ide_read_drivers - (char *page, char **start, off_t off, int count, int *eof, void *data) -{ - char *out = page; - int len; - struct ide_driver_s * driver; - - for (driver = ide_drivers; driver; driver = driver->next) { - out += sprintf(out, "%s\n",driver->name); - } - len = out - page; - PROC_IDE_READ_RETURN(page,start,off,count,eof,len); -} - static int proc_ide_read_imodel (char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -409,7 +161,6 @@ case ide_trm290: name = "trm290"; break; case ide_cmd646: name = "cmd646"; break; case ide_cy82c693: name = "cy82c693"; break; - case ide_4drives: name = "4drives"; break; case ide_pmac: name = "mac-io"; break; default: name = "(unknown)"; break; } @@ -450,7 +201,7 @@ memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); taskfile.sector_count = 0x01; - taskfile.command = (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY ; + taskfile.command = (drive->type == ATA_DISK) ? WIN_IDENTIFY : WIN_PIDENTIFY ; return ide_wait_taskfile(drive, &taskfile, &hobfile, buf); } @@ -546,8 +297,8 @@ } if (*p != ':') goto parse_error; - len = IDE_MIN(p - start, MAX_LEN); - strncpy(name, start, IDE_MIN(len, MAX_LEN)); + len = min(p - start, MAX_LEN); + strncpy(name, start, min(len, MAX_LEN)); name[len] = 0; if (n > 0) { @@ -555,7 +306,7 @@ p++; } else goto parse_error; - + digits = 0; while (n > 0 && (d = ide_getdigit(*p)) >= 0) { val = (val * 10) + d; @@ -594,14 +345,14 @@ int proc_ide_read_capacity (char *page, char **start, off_t off, int count, int *eof, void *data) { - ide_drive_t *drive = data; - ide_driver_t *driver = drive->driver; - int len; + ide_drive_t *drive = data; + struct ata_operations *driver = drive->driver; + int len; if (!driver) len = sprintf(page, "(none)\n"); else - len = sprintf(page,"%llu\n", (unsigned long long) drive->driver->capacity(drive)); + len = sprintf(page,"%llu\n", (unsigned long long) ata_capacity(drive)); PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } @@ -629,58 +380,31 @@ PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } -static int proc_ide_read_driver - (char *page, char **start, off_t off, int count, int *eof, void *data) -{ - ide_drive_t *drive = (ide_drive_t *) data; - ide_driver_t *driver = drive->driver; - int len; - - if (!driver) - len = sprintf(page, "(none)\n"); - else - len = sprintf(page, "%s\n", driver->name); - PROC_IDE_READ_RETURN(page,start,off,count,eof,len); -} - -static int proc_ide_write_driver - (struct file *file, const char *buffer, unsigned long count, void *data) -{ - ide_drive_t *drive = data; - - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - if (ide_replace_subdriver(drive, buffer)) - return -EINVAL; - return count; -} - static int proc_ide_read_media (char *page, char **start, off_t off, int count, int *eof, void *data) { ide_drive_t *drive = data; - const char *media; + const char *type; int len; - switch (drive->media) { - case ide_disk: media = "disk\n"; + switch (drive->type) { + case ATA_DISK: type = "disk\n"; break; - case ide_cdrom: media = "cdrom\n"; + case ATA_ROM: type = "cdrom\n"; break; - case ide_tape: media = "tape\n"; + case ATA_TAPE: type = "tape\n"; break; - case ide_floppy:media = "floppy\n"; + case ATA_FLOPPY:type = "floppy\n"; break; - default: media = "UNKNOWN\n"; + default: type = "UNKNOWN\n"; break; } - strcpy(page,media); - len = strlen(media); + strcpy(page,type); + len = strlen(type); PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } static ide_proc_entry_t generic_drive_entries[] = { - { "driver", S_IFREG|S_IRUGO, proc_ide_read_driver, proc_ide_write_driver }, { "identify", S_IFREG|S_IRUSR, proc_ide_read_identify, NULL }, { "media", S_IFREG|S_IRUGO, proc_ide_read_media, NULL }, { "model", S_IFREG|S_IRUGO, proc_ide_read_dmodel, NULL }, @@ -724,7 +448,7 @@ for (d = 0; d < MAX_DRIVES; d++) { ide_drive_t *drive = &hwif->drives[d]; - ide_driver_t *driver = drive->driver; + struct ata_operations *driver = drive->driver; if (!drive->present) continue; @@ -745,35 +469,9 @@ } } -void recreate_proc_ide_device(ide_hwif_t *hwif, ide_drive_t *drive) -{ - struct proc_dir_entry *ent; - struct proc_dir_entry *parent = hwif->proc; - char name[64]; - - if (drive->present && !drive->proc) { - drive->proc = proc_mkdir(drive->name, parent); - if (drive->proc) - ide_add_proc_entries(drive->proc, generic_drive_entries, drive); - -/* - * assume that we have these already, however, should test FIXME! - * if (driver) { - * ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive); - * ide_add_proc_entries(drive->proc, driver->proc, drive); - * } - * - */ - sprintf(name,"ide%d/%s", (drive->name[2]-'a')/2, drive->name); - ent = proc_symlink(drive->name, proc_ide_root, name); - if (!ent) - return; - } -} - -void destroy_proc_ide_device(ide_hwif_t *hwif, ide_drive_t *drive) +static void destroy_proc_ide_device(ide_hwif_t *hwif, ide_drive_t *drive) { - ide_driver_t *driver = drive->driver; + struct ata_operations *driver = drive->driver; if (drive->proc) { if (driver) @@ -799,7 +497,6 @@ static ide_proc_entry_t hwif_entries[] = { { "channel", S_IFREG|S_IRUGO, proc_ide_read_channel, NULL }, - { "config", S_IFREG|S_IRUGO|S_IWUSR,proc_ide_read_config, proc_ide_write_config }, { "mate", S_IFREG|S_IRUGO, proc_ide_read_mate, NULL }, { "model", S_IFREG|S_IRUGO, proc_ide_read_imodel, NULL }, { NULL, 0, NULL, NULL } @@ -851,9 +548,6 @@ if (!proc_ide_root) return; create_proc_ide_interfaces(); - - create_proc_read_entry("drivers", 0, proc_ide_root, - proc_ide_read_drivers, NULL); #ifdef CONFIG_BLK_DEV_AEC62XX if ((aec62xx_display_info) && (aec62xx_proc)) diff -Nru a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c --- a/drivers/ide/ide-tape.c Thu Mar 7 18:17:39 2002 +++ b/drivers/ide/ide-tape.c Thu Mar 7 18:17:39 2002 @@ -1504,7 +1504,7 @@ return; } #endif /* IDETAPE_DEBUG_BUGS */ - count = IDE_MIN (bio->bi_size - pc->b_count, bcount); + count = min(bio->bi_size - pc->b_count, bcount); atapi_input_bytes (drive, bio_data(bio) + pc->b_count, count); bcount -= count; pc->b_count += bio->bi_size; @@ -1529,7 +1529,7 @@ return; } #endif /* IDETAPE_DEBUG_BUGS */ - count = IDE_MIN (pc->b_count, bcount); + count = min(pc->b_count, bcount); atapi_output_bytes (drive, bio_data(bio), count); bcount -= count; pc->b_data += count; @@ -1559,7 +1559,7 @@ return; } #endif /* IDETAPE_DEBUG_BUGS */ - count = IDE_MIN (bio->bi_size, bcount); + count = min(bio->bi_size, bcount); pc->b_count = count; if (pc->b_count == bio->bi_size) bio = bio->bi_next; @@ -1759,8 +1759,8 @@ #endif /* IDETAPE_DEBUG_LOG */ tape->max_stages += increase; - tape->max_stages = IDE_MAX(tape->max_stages, tape->min_pipeline); - tape->max_stages = IDE_MIN(tape->max_stages, tape->max_pipeline); + tape->max_stages = max(tape->max_stages, tape->min_pipeline); + tape->max_stages = min(tape->max_stages, tape->max_pipeline); } /* @@ -2084,7 +2084,7 @@ if (!status.b.drq) { /* No more interrupts */ cmd_time = (jiffies - tape->cmd_start_time) * 1000 / HZ; - tape->max_cmd_time = IDE_MAX(cmd_time, tape->max_cmd_time); + tape->max_cmd_time = max(cmd_time, tape->max_cmd_time); #if IDETAPE_DEBUG_LOG if (tape->debug_level >= 2) printk (KERN_INFO "ide-tape: Packet command completed, %d bytes transferred\n", pc->actually_transferred); @@ -2442,7 +2442,7 @@ tape->uncontrolled_pipeline_head_time = jiffies; } } - tape->pipeline_head_speed = IDE_MAX(tape->uncontrolled_pipeline_head_speed, tape->controlled_pipeline_head_speed); + tape->pipeline_head_speed = max(tape->uncontrolled_pipeline_head_speed, tape->controlled_pipeline_head_speed); if (tape->speed_control == 0) { tape->max_insert_speed = 5000; } else if (tape->speed_control == 1) { @@ -2459,7 +2459,7 @@ (tape->pipeline_head_speed * full / 100 - tape->pipeline_head_speed * empty / 100) * tape->nr_pending_stages / tape->max_stages; } else tape->max_insert_speed = tape->speed_control; - tape->max_insert_speed = IDE_MAX(tape->max_insert_speed, 500); + tape->max_insert_speed = max(tape->max_insert_speed, 500); } static ide_startstop_t idetape_media_access_finished (ide_drive_t *drive) @@ -2920,7 +2920,7 @@ return; } #endif /* IDETAPE_DEBUG_BUGS */ - count = IDE_MIN (bio->bi_size - tape->b_count, n); + count = min(bio->bi_size - tape->b_count, n); copy_from_user (bio_data(bio) + tape->b_count, buf, count); n -= count; bio->bi_size += count; @@ -2946,7 +2946,7 @@ return; } #endif /* IDETAPE_DEBUG_BUGS */ - count = IDE_MIN (tape->b_count, n); + count = min(tape->b_count, n); copy_to_user (buf, tape->b_data, count); n -= count; tape->b_data += count; @@ -3878,7 +3878,7 @@ printk(KERN_INFO "ide-tape: bug, bio NULL\n"); break; } - min = IDE_MIN(i, bio->bi_size - atomic_read(&bio->bi_cnt)); + min = min(i, bio->bi_size - atomic_read(&bio->bi_cnt)); memset(bio_data(bio) + bio->bi_size, 0, min); atomic_add(min, &bio->bi_cnt); i -= min; @@ -4149,11 +4149,11 @@ while (bcount) { bio = tape->merge_stage->bio; - count = IDE_MIN (tape->stage_size, bcount); + count = min(tape->stage_size, bcount); bcount -= count; blocks = count / tape->tape_block_size; while (count) { - atomic_set(&bio->bi_cnt, IDE_MIN (count, bio->bi_size)); + atomic_set(&bio->bi_cnt, min(count, bio->bi_size)); memset (bio_data(bio), 0, bio->bi_size); count -= atomic_read(&bio->bi_cnt); bio = bio->bi_next; @@ -4596,7 +4596,7 @@ if (count == 0) return (0); if (tape->merge_stage_size) { - actually_read = IDE_MIN (tape->merge_stage_size, count); + actually_read = 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; @@ -4615,7 +4615,7 @@ bytes_read=idetape_add_chrdev_read_request (drive, tape->capabilities.ctl); if (bytes_read <= 0) goto finish; - temp = IDE_MIN (count, bytes_read); + temp = 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; @@ -4890,7 +4890,7 @@ tape->merge_stage_size = 0; } #endif /* IDETAPE_DEBUG_BUGS */ - actually_written = IDE_MIN (tape->stage_size - tape->merge_stage_size, count); + actually_written = 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; @@ -6049,7 +6049,7 @@ * Select the "best" DSC read/write polling frequency * and pipeline size. */ - speed = IDE_MAX (tape->capabilities.speed, tape->capabilities.max_speed); + speed = max(tape->capabilities.speed, tape->capabilities.max_speed); tape->max_stages = speed * 1000 * 10 / tape->stage_size; @@ -6075,7 +6075,7 @@ * Ensure that the number we got makes sense; limit * it within IDETAPE_DSC_RW_MIN and IDETAPE_DSC_RW_MAX. */ - tape->best_dsc_rw_frequency = IDE_MAX (IDE_MIN (t, IDETAPE_DSC_RW_MAX), IDETAPE_DSC_RW_MIN); + tape->best_dsc_rw_frequency = max(min(t, IDETAPE_DSC_RW_MAX), IDETAPE_DSC_RW_MIN); printk (KERN_INFO "ide-tape: %s <-> %s: %dKBps, %d*%dkB buffer, %dkB pipeline, %lums tDSC%s\n", drive->name, tape->name, tape->capabilities.speed, (tape->capabilities.buffer_size * 512) / tape->stage_size, tape->stage_size / 1024, tape->max_stages * tape->stage_size / 1024, @@ -6098,8 +6098,11 @@ } idetape_chrdevs[minor].drive = NULL; restore_flags (flags); /* all CPUs (overkill?) */ - DRIVER(drive)->busy = 0; - (void) ide_unregister_subdriver (drive); + + /* FIXME: this appears to be totally wrong! */ + ata_ops(drive)->busy = 0; + + ide_unregister_subdriver (drive); drive->driver_data = NULL; devfs_unregister (tape->de_r); devfs_unregister (tape->de_n); @@ -6137,33 +6140,30 @@ #endif -int idetape_init (void); -int idetape_reinit(ide_drive_t *drive); +static void idetape_revalidate(ide_drive_t *_dummy) +{ + /* We don't have to handle any partition information here, which is the + * default behaviour of this method. + */ +} /* * IDE subdriver functions, registered with ide.c */ -static ide_driver_t idetape_driver = { - name: "ide-tape", - media: ide_tape, - busy: 1, - supports_dma: 1, - supports_dsc_overlap: 1, +static struct ata_operations idetape_driver = { + owner: THIS_MODULE, cleanup: idetape_cleanup, standby: NULL, - flushcache: NULL, do_request: idetape_do_request, end_request: idetape_end_request, ioctl: idetape_blkdev_ioctl, open: idetape_blkdev_open, release: idetape_blkdev_release, - media_change: NULL, - revalidate: NULL, + check_media_change: NULL, + revalidate: idetape_revalidate, pre_reset: idetape_pre_reset, capacity: NULL, - proc: idetape_proc, - driver_init: idetape_init, - driver_reinit: idetape_reinit, + proc: idetape_proc }; /* @@ -6178,92 +6178,6 @@ release: idetape_chrdev_release, }; -int idetape_reinit (ide_drive_t *drive) -{ -#if 0 - idetape_tape_t *tape; - int minor, failed = 0, supported = 0; -/* DRIVER(drive)->busy++; */ - MOD_INC_USE_COUNT; -#if ONSTREAM_DEBUG - printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_init\n"); -#endif - if (!idetape_chrdev_present) - for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++ ) - idetape_chrdevs[minor].drive = NULL; - - if ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) == NULL) { - ide_register_module (&idetape_module); - MOD_DEC_USE_COUNT; -#if ONSTREAM_DEBUG - printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); -#endif - return 0; - } - if (!idetape_chrdev_present && - devfs_register_chrdev (IDETAPE_MAJOR, "ht", &idetape_fops)) { - printk (KERN_ERR "ide-tape: Failed to register character device interface\n"); - MOD_DEC_USE_COUNT; -#if ONSTREAM_DEBUG - printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); -#endif - return -EBUSY; - } - do { - if (!idetape_identify_device (drive, drive->id)) { - printk (KERN_ERR "ide-tape: %s: not supported by this version of ide-tape\n", drive->name); - continue; - } - if (drive->scsi) { - if (strstr(drive->id->model, "OnStream DI-30")) { - printk("ide-tape: ide-scsi emulation is not supported for %s.\n", drive->id->model); - } else { - printk("ide-tape: passing drive %s to ide-scsi emulation.\n", drive->name); - continue; - } - } - tape = (idetape_tape_t *) kmalloc (sizeof (idetape_tape_t), GFP_KERNEL); - if (tape == NULL) { - printk (KERN_ERR "ide-tape: %s: Can't allocate a tape structure\n", drive->name); - continue; - } - if (ide_register_subdriver (drive, &idetape_driver)) { - printk (KERN_ERR "ide-tape: %s: Failed to register the driver with ide.c\n", drive->name); - kfree (tape); - continue; - } - for (minor = 0; idetape_chrdevs[minor].drive != NULL; minor++); - idetape_setup (drive, tape, minor); - idetape_chrdevs[minor].drive = drive; - tape->de_r = - devfs_register (drive->de, "mt", DEVFS_FL_DEFAULT, - HWIF(drive)->major, minor, - S_IFCHR | S_IRUGO | S_IWUGO, - &idetape_fops, NULL); - tape->de_n = - devfs_register (drive->de, "mtn", DEVFS_FL_DEFAULT, - HWIF(drive)->major, minor + 128, - S_IFCHR | S_IRUGO | S_IWUGO, - &idetape_fops, NULL); - devfs_register_tape (tape->de_r); - supported++; failed--; - } while ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) != NULL); - if (!idetape_chrdev_present && !supported) { - devfs_unregister_chrdev (IDETAPE_MAJOR, "ht"); - } else - idetape_chrdev_present = 1; - ide_register_module (&idetape_module); - MOD_DEC_USE_COUNT; -#if ONSTREAM_DEBUG - printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); -#endif - - return 0; -#else - return 1; -#endif -} - MODULE_DESCRIPTION("ATAPI Streaming TAPE Driver"); MODULE_LICENSE("GPL"); @@ -6277,7 +6191,6 @@ if (drive != NULL && idetape_cleanup (drive)) printk (KERN_ERR "ide-tape: %s: cleanup_module() called while still busy\n", drive->name); } - ide_unregister_module(&idetape_driver); } /* @@ -6297,8 +6210,8 @@ for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++ ) idetape_chrdevs[minor].drive = NULL; - if ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) == NULL) { - ide_register_module (&idetape_driver); + if ((drive = ide_scan_devices(ATA_TAPE, "ide-tape", NULL, failed++)) == NULL) { + revalidate_drives(); MOD_DEC_USE_COUNT; #if ONSTREAM_DEBUG printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); @@ -6352,12 +6265,12 @@ &idetape_fops, NULL); devfs_register_tape (tape->de_r); supported++; failed--; - } while ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) != NULL); + } while ((drive = ide_scan_devices(ATA_TAPE, "ide-tape", NULL, failed++)) != NULL); if (!idetape_chrdev_present && !supported) { devfs_unregister_chrdev (IDETAPE_MAJOR, "ht"); } else idetape_chrdev_present = 1; - ide_register_module (&idetape_driver); + revalidate_drives(); MOD_DEC_USE_COUNT; #if ONSTREAM_DEBUG printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); diff -Nru a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c --- a/drivers/ide/ide-taskfile.c Thu Mar 7 18:17:45 2002 +++ b/drivers/ide/ide-taskfile.c Thu Mar 7 18:17:45 2002 @@ -1,12 +1,8 @@ /* - * linux/drivers/ide/ide-taskfile.c Version 0.20 Oct 11, 2000 - * * Copyright (C) 2000 Michael Cornwell * Copyright (C) 2000 Andre Hedrick * * May be copied or modified under the terms of the GNU General Public License - * - * IDE_DEBUG(__LINE__); */ #include @@ -42,11 +38,22 @@ #define DTF(x...) #endif -inline u32 task_read_24 (ide_drive_t *drive) +/* + * for now, taskfile requests are special :/ + */ +static inline char *ide_map_rq(struct request *rq, unsigned long *flags) { - return (IN_BYTE(IDE_HCYL_REG)<<16) | - (IN_BYTE(IDE_LCYL_REG)<<8) | - IN_BYTE(IDE_SECTOR_REG); + if (rq->bio) + return bio_kmap_irq(rq->bio, flags) + ide_rq_offset(rq); + else + return rq->buffer + task_rq_offset(rq); +} + +static inline void ide_unmap_rq(struct request *rq, char *to, + unsigned long *flags) +{ + if (rq->bio) + bio_kunmap_irq(to, flags); } static void ata_bswap_data (void *buffer, int wcount) @@ -67,12 +74,13 @@ * of the sector count register location, with interrupts disabled * to ensure that the reads all happen together. */ -static inline void task_vlb_sync (ide_ioreg_t port) { - (void) IN_BYTE (port); - (void) IN_BYTE (port); - (void) IN_BYTE (port); +static inline void task_vlb_sync(ide_ioreg_t port) +{ + IN_BYTE (port); + IN_BYTE (port); + IN_BYTE (port); } -#endif /* SUPPORT_VLB_SYNC */ +#endif /* * This is used for most PIO data transfers *from* the IDE interface @@ -103,7 +111,7 @@ insl(IDE_DATA_REG, buffer, wcount); __restore_flags(flags); /* local CPU only */ } else -#endif /* SUPPORT_VLB_SYNC */ +#endif insl(IDE_DATA_REG, buffer, wcount); } else { #if SUPPORT_SLOW_DATA_PORTS @@ -114,7 +122,7 @@ *ptr++ = inw_p(IDE_DATA_REG); } } else -#endif /* SUPPORT_SLOW_DATA_PORTS */ +#endif insw(IDE_DATA_REG, buffer, wcount<<1); } } @@ -143,7 +151,7 @@ outsl(IDE_DATA_REG, buffer, wcount); __restore_flags(flags); /* local CPU only */ } else -#endif /* SUPPORT_VLB_SYNC */ +#endif outsl(IDE_DATA_REG, buffer, wcount); } else { #if SUPPORT_SLOW_DATA_PORTS @@ -154,7 +162,7 @@ outw_p(*ptr++, IDE_DATA_REG); } } else -#endif /* SUPPORT_SLOW_DATA_PORTS */ +#endif outsw(IDE_DATA_REG, buffer, wcount<<1); } } @@ -255,7 +263,90 @@ return 1; /* drive ready: *might* be interrupting */ } -ide_startstop_t bio_mulout_intr (ide_drive_t *drive); +/* + * Polling wait until the drive is ready. + * + * Stuff the first sector(s) by implicitly calling the handler driectly + * therafter. + */ +void ata_poll_drive_ready(ide_drive_t *drive) +{ + int i; + + + if (drive_is_ready(drive)) + return; + + /* FIXME: Replace hard-coded 100, what about error handling? + */ + for (i = 0; i < 100; ++i) { + if (drive_is_ready(drive)) + break; + } +} +static ide_startstop_t bio_mulout_intr(ide_drive_t *drive); + +/* + * Handler for command write multiple + * Called directly from execute_drive_cmd for the first bunch of sectors, + * afterwards only by the ISR + */ +static ide_startstop_t task_mulout_intr (ide_drive_t *drive) +{ + unsigned int msect, nsect; + byte stat = GET_STAT(); + byte io_32bit = drive->io_32bit; + struct request *rq = HWGROUP(drive)->rq; + ide_hwgroup_t *hwgroup = HWGROUP(drive); + char *pBuf = NULL; + unsigned long flags; + + /* + * (ks/hs): Handle last IRQ on multi-sector transfer, + * occurs after all data was sent in this chunk + */ + if (rq->current_nr_sectors == 0) { + if (stat & (ERR_STAT|DRQ_STAT)) + return ide_error(drive, "task_mulout_intr", stat); + + /* + * there may be more, ide_do_request will restart it if + * necessary + */ + ide_end_request(drive, 1); + return ide_stopped; + } + + if (!OK_STAT(stat,DATA_READY,BAD_R_STAT)) { + if (stat & (ERR_STAT|DRQ_STAT)) { + return ide_error(drive, "task_mulout_intr", stat); + } + /* no data yet, so wait for another interrupt */ + if (hwgroup->handler == NULL) + ide_set_handler(drive, &task_mulout_intr, WAIT_CMD, NULL); + return ide_started; + } + + /* (ks/hs): See task_mulin_intr */ + msect = drive->mult_count; + nsect = rq->current_nr_sectors; + if (nsect > msect) + nsect = msect; + + pBuf = ide_map_rq(rq, &flags); + DTF("Multiwrite: %p, nsect: %d , rq->current_nr_sectors: %ld\n", + pBuf, nsect, rq->current_nr_sectors); + drive->io_32bit = 0; + taskfile_output_data(drive, pBuf, nsect * SECTOR_WORDS); + ide_unmap_rq(rq, pBuf, &flags); + drive->io_32bit = io_32bit; + rq->errors = 0; + rq->current_nr_sectors -= nsect; + if (hwgroup->handler == NULL) + ide_set_handler(drive, &task_mulout_intr, WAIT_CMD, NULL); + return ide_started; +} + ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) { task_struct_t *taskfile = (task_struct_t *) task->tfRegister; @@ -293,7 +384,7 @@ ide_set_handler (drive, task->handler, WAIT_CMD, NULL); OUT_BYTE(taskfile->command, IDE_COMMAND_REG); /* - * warning check for race between handler and prehandler for + * Warning check for race between handler and prehandler for * writing first block of data. however since we are well * inside the boundaries of the seek, we should be okay. */ @@ -348,310 +439,12 @@ } } -#if 0 -ide_startstop_t flagged_taskfile (ide_drive_t *drive, ide_task_t *task) -{ - task_struct_t *taskfile = (task_struct_t *) task->tfRegister; - hob_struct_t *hobfile = (hob_struct_t *) task->hobRegister; - struct hd_driveid *id = drive->id; - - /* - * (KS) Check taskfile in/out flags. - * If set, then execute as it is defined. - * If not set, then define default settings. - * The default values are: - * write and read all taskfile registers (except data) - * write and read the hob registers (sector,nsector,lcyl,hcyl) - */ - if (task->tf_out_flags.all == 0) { - task->tf_out_flags.all = IDE_TASKFILE_STD_OUT_FLAGS; - if ((id->command_set_2 & 0x0400) && - (id->cfs_enable_2 & 0x0400) && - (drive->addressing == 1)) { - task->tf_out_flags.all != (IDE_HOB_STD_OUT_FLAGS << 8); - } - } - - if (task->tf_in_flags.all == 0) { - task->tf_in_flags.all = IDE_TASKFILE_STD_IN_FLAGS; - if ((id->command_set_2 & 0x0400) && - (id->cfs_enable_2 & 0x0400) && - (drive->addressing == 1)) { - task->tf_in_flags.all != (IDE_HOB_STD_IN_FLAGS << 8); - } - } - - if (IDE_CONTROL_REG) - OUT_BYTE(drive->ctl, IDE_CONTROL_REG); /* clear nIEN */ - SELECT_MASK(HWIF(drive), drive, 0); - - if (task->tf_out_flags.b.data) { - unsigned short data = taskfile->data + (hobfile->data << 8); - OUT_WORD (data, IDE_DATA_REG); - } - - /* (KS) send hob registers first */ - if (task->tf_out_flags.b.nsector_hob) - OUT_BYTE(hobfile->sector_count, IDE_NSECTOR_REG); - if (task->tf_out_flags.b.sector_hob) - OUT_BYTE(hobfile->sector_number, IDE_SECTOR_REG); - if (task->tf_out_flags.b.lcyl_hob) - OUT_BYTE(hobfile->low_cylinder, IDE_LCYL_REG); - if (task->tf_out_flags.b.hcyl_hob) - OUT_BYTE(hobfile->high_cylinder, IDE_HCYL_REG); - - - /* (KS) Send now the standard registers */ - if (task->tf_out_flags.b.error_feature) - OUT_BYTE(taskfile->feature, IDE_FEATURE_REG); - /* refers to number of sectors to transfer */ - if (task->tf_out_flags.b.nsector) - OUT_BYTE(taskfile->sector_count, IDE_NSECTOR_REG); - /* refers to sector offset or start sector */ - if (task->tf_out_flags.b.sector) - OUT_BYTE(taskfile->sector_number, IDE_SECTOR_REG); - if (task->tf_out_flags.b.lcyl) - OUT_BYTE(taskfile->low_cylinder, IDE_LCYL_REG); - if (task->tf_out_flags.b.hcyl) - OUT_BYTE(taskfile->high_cylinder, IDE_HCYL_REG); - - /* - * (KS) Do not modify the specified taskfile. We want to have a - * universal pass through, so we must execute ALL specified values. - * - * (KS) The drive head register is mandatory. - * Don't care about the out flags ! - */ - OUT_BYTE(taskfile->device_head | drive->select.all, IDE_SELECT_REG); - if (task->handler != NULL) { - ide_set_handler (drive, task->handler, WAIT_CMD, NULL); - OUT_BYTE(taskfile->command, IDE_COMMAND_REG); - /* - * warning check for race between handler and prehandler for - * writing first block of data. however since we are well - * inside the boundaries of the seek, we should be okay. - */ - if (task->prehandler != NULL) { - return task->prehandler(drive, task->rq); - } - } else { - /* for dma commands we down set the handler */ - if (drive->using_dma && !(HWIF(drive)->dmaproc(((taskfile->command == WIN_WRITEDMA) || (taskfile->command == WIN_WRITEDMA_EXT)) ? ide_dma_write : ide_dma_read, drive))); - } - - return ide_started; -} -#endif - -#if 0 -/* - * Error reporting, in human readable form (luxurious, but a memory hog). - */ -byte taskfile_dump_status (ide_drive_t *drive, const char *msg, byte stat) -{ - unsigned long flags; - byte err = 0; - - __save_flags (flags); /* local CPU only */ - ide__sti(); /* local CPU only */ - printk("%s: %s: status=0x%02x", drive->name, msg, stat); -#if FANCY_STATUS_DUMPS - printk(" { "); - if (stat & BUSY_STAT) - printk("Busy "); - else { - if (stat & READY_STAT) printk("DriveReady "); - if (stat & WRERR_STAT) printk("DeviceFault "); - if (stat & SEEK_STAT) printk("SeekComplete "); - if (stat & DRQ_STAT) printk("DataRequest "); - if (stat & ECC_STAT) printk("CorrectedError "); - if (stat & INDEX_STAT) printk("Index "); - if (stat & ERR_STAT) printk("Error "); - } - printk("}"); -#endif /* FANCY_STATUS_DUMPS */ - printk("\n"); - if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) { - err = GET_ERR(); - printk("%s: %s: error=0x%02x", drive->name, msg, err); -#if FANCY_STATUS_DUMPS - if (drive->media == ide_disk) { - printk(" { "); - if (err & ABRT_ERR) printk("DriveStatusError "); - if (err & ICRC_ERR) printk("%s", (err & ABRT_ERR) ? "BadCRC " : "BadSector "); - if (err & ECC_ERR) printk("UncorrectableError "); - if (err & ID_ERR) printk("SectorIdNotFound "); - if (err & TRK0_ERR) printk("TrackZeroNotFound "); - if (err & MARK_ERR) printk("AddrMarkNotFound "); - printk("}"); - if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR || (err & (ECC_ERR|ID_ERR|MARK_ERR))) { - if ((drive->id->command_set_2 & 0x0400) && - (drive->id->cfs_enable_2 & 0x0400) && - (drive->addressing == 1)) { - __u64 sectors = 0; - u32 low = 0, high = 0; - low = task_read_24(drive); - OUT_BYTE(0x80, IDE_CONTROL_REG); - high = task_read_24(drive); - sectors = ((__u64)high << 24) | low; - printk(", LBAsect=%lld", sectors); - } else { - byte cur = IN_BYTE(IDE_SELECT_REG); - if (cur & 0x40) { /* using LBA? */ - printk(", LBAsect=%ld", (unsigned long) - ((cur&0xf)<<24) - |(IN_BYTE(IDE_HCYL_REG)<<16) - |(IN_BYTE(IDE_LCYL_REG)<<8) - | IN_BYTE(IDE_SECTOR_REG)); - } else { - printk(", CHS=%d/%d/%d", - (IN_BYTE(IDE_HCYL_REG)<<8) + - IN_BYTE(IDE_LCYL_REG), - cur & 0xf, - IN_BYTE(IDE_SECTOR_REG)); - } - } - if (HWGROUP(drive)->rq) - printk(", sector=%llu", (__u64) HWGROUP(drive)->rq->sector); - } - } -#endif /* FANCY_STATUS_DUMPS */ - printk("\n"); - } - __restore_flags (flags); /* local CPU only */ - return err; -} - -/* - * Clean up after success/failure of an explicit taskfile operation. - */ -void ide_end_taskfile (ide_drive_t *drive, byte stat, byte err) -{ - unsigned long flags; - struct request *rq; - ide_task_t *args; - task_ioreg_t command; - - spin_lock_irqsave(&ide_lock, flags); - rq = HWGROUP(drive)->rq; - spin_unlock_irqrestore(&ide_lock, flags); - args = (ide_task_t *) rq->special; - - command = args->tfRegister[IDE_COMMAND_OFFSET]; - - rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); - - args->tfRegister[IDE_ERROR_OFFSET] = err; - args->tfRegister[IDE_NSECTOR_OFFSET] = IN_BYTE(IDE_NSECTOR_REG); - args->tfRegister[IDE_SECTOR_OFFSET] = IN_BYTE(IDE_SECTOR_REG); - args->tfRegister[IDE_LCYL_OFFSET] = IN_BYTE(IDE_LCYL_REG); - args->tfRegister[IDE_HCYL_OFFSET] = IN_BYTE(IDE_HCYL_REG); - args->tfRegister[IDE_SELECT_OFFSET] = IN_BYTE(IDE_SELECT_REG); - args->tfRegister[IDE_STATUS_OFFSET] = stat; - if ((drive->id->command_set_2 & 0x0400) && - (drive->id->cfs_enable_2 & 0x0400) && - (drive->addressing == 1)) { - OUT_BYTE(drive->ctl|0x80, IDE_CONTROL_REG_HOB); - args->hobRegister[IDE_FEATURE_OFFSET_HOB] = IN_BYTE(IDE_FEATURE_REG); - args->hobRegister[IDE_NSECTOR_OFFSET_HOB] = IN_BYTE(IDE_NSECTOR_REG); - args->hobRegister[IDE_SECTOR_OFFSET_HOB] = IN_BYTE(IDE_SECTOR_REG); - args->hobRegister[IDE_LCYL_OFFSET_HOB] = IN_BYTE(IDE_LCYL_REG); - args->hobRegister[IDE_HCYL_OFFSET_HOB] = IN_BYTE(IDE_HCYL_REG); - } - -/* taskfile_settings_update(drive, args, command); */ - - spin_lock_irqsave(&ide_lock, flags); - blkdev_dequeue_request(rq); - HWGROUP(drive)->rq = NULL; - end_that_request_last(rq); - spin_unlock_irqrestore(&ide_lock, flags); -} - -/* - * try_to_flush_leftover_data() is invoked in response to a drive - * unexpectedly having its DRQ_STAT bit set. As an alternative to - * resetting the drive, this routine tries to clear the condition - * by read a sector's worth of data from the drive. Of course, - * this may not help if the drive is *waiting* for data from *us*. - */ -void task_try_to_flush_leftover_data (ide_drive_t *drive) -{ - int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS; - - if (drive->media != ide_disk) - return; - while (i > 0) { - u32 buffer[16]; - unsigned int wcount = (i > 16) ? 16 : i; - i -= wcount; - taskfile_input_data (drive, buffer, wcount); - } -} - -/* - * taskfile_error() takes action based on the error returned by the drive. - */ -ide_startstop_t taskfile_error (ide_drive_t *drive, const char *msg, byte stat) -{ - struct request *rq; - byte err; - - err = taskfile_dump_status(drive, msg, stat); - if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) - return ide_stopped; - /* retry only "normal" I/O: */ - if (rq->flags & REQ_DRIVE_TASKFILE) { - rq->errors = 1; - ide_end_taskfile(drive, stat, err); - return ide_stopped; - } - if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */ - rq->errors |= ERROR_RESET; - } else { - if (drive->media == ide_disk && (stat & ERR_STAT)) { - /* err has different meaning on cdrom and tape */ - if (err == ABRT_ERR) { - if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY) - return ide_stopped; /* some newer drives don't support WIN_SPECIFY */ - } else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR)) { - drive->crc_count++; /* UDMA crc error -- just retry the operation */ - } else if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */ - rq->errors = ERROR_MAX; - else if (err & TRK0_ERR) /* help it find track zero */ - rq->errors |= ERROR_RECAL; - } - /* pre bio (rq->cmd != WRITE) */ - if ((stat & DRQ_STAT) && rq_data_dir(rq) == READ) - task_try_to_flush_leftover_data(drive); - } - if (GET_STAT() & (BUSY_STAT|DRQ_STAT)) - OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG); /* force an abort */ - - if (rq->errors >= ERROR_MAX) { - if (drive->driver != NULL) - DRIVER(drive)->end_request(0, HWGROUP(drive)); - else - ide_end_request(drive, 0); - } else { - if ((rq->errors & ERROR_RESET) == ERROR_RESET) { - ++rq->errors; - return ide_do_reset(drive); - } - if ((rq->errors & ERROR_RECAL) == ERROR_RECAL) - drive->special.b.recalibrate = 1; - ++rq->errors; - } - return ide_stopped; -} -#endif - /* * Handler for special commands without a data phase from ide-disk */ /* - * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd. + * This is invoked on completion of a WIN_SETMULT cmd. */ ide_startstop_t set_multmode_intr (ide_drive_t *drive) { @@ -662,15 +455,15 @@ } else { drive->mult_req = drive->mult_count = 0; drive->special.b.recalibrate = 1; - (void) ide_dump_status(drive, "set_multmode", stat); + ide_dump_status(drive, "set_multmode", stat); } return ide_stopped; } /* - * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd. + * This is invoked on completion of a WIN_SPECIFY cmd. */ -ide_startstop_t set_geometry_intr (ide_drive_t *drive) +static ide_startstop_t set_geometry_intr (ide_drive_t *drive) { byte stat; @@ -685,9 +478,9 @@ } /* - * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd. + * This is invoked on completion of a WIN_RESTORE (recalibrate) cmd. */ -ide_startstop_t recal_intr (ide_drive_t *drive) +static ide_startstop_t recal_intr (ide_drive_t *drive) { byte stat = GET_STAT(); @@ -718,7 +511,7 @@ /* * Handler for command with PIO data-in phase */ -ide_startstop_t task_in_intr (ide_drive_t *drive) +static ide_startstop_t task_in_intr (ide_drive_t *drive) { byte stat = GET_STAT(); byte io_32bit = drive->io_32bit; @@ -733,7 +526,7 @@ if (!(stat & BUSY_STAT)) { DTF("task_in_intr to Soon wait for next interrupt\n"); ide_set_handler(drive, &task_in_intr, WAIT_CMD, NULL); - return ide_started; + return ide_started; } } DTF("stat: %02x\n", stat); @@ -759,134 +552,7 @@ return ide_stopped; } -#undef ALTSTAT_SCREW_UP - -#ifdef ALTSTAT_SCREW_UP -/* - * (ks/hs): Poll Alternate Status Register to ensure - * that drive is not busy. - */ -byte altstat_multi_busy (ide_drive_t *drive, byte stat, const char *msg) -{ - int i; - - DTF("multi%s: ASR = %x\n", msg, stat); - if (stat & BUSY_STAT) { - /* (ks/hs): FIXME: Replace hard-coded 100, error handling? */ - for (i=0; i<100; i++) { - stat = GET_ALTSTAT(); - if ((stat & BUSY_STAT) == 0) - break; - } - } - /* - * (ks/hs): Read Status AFTER Alternate Status Register - */ - return(GET_STAT()); -} - -/* - * (ks/hs): Poll Alternate status register to wait for drive - * to become ready for next transfer - */ -byte altstat_multi_poll (ide_drive_t *drive, byte stat, const char *msg) -{ - /* (ks/hs): FIXME: Error handling, time-out? */ - while (stat & BUSY_STAT) - stat = GET_ALTSTAT(); - DTF("multi%s: nsect=1, ASR = %x\n", msg, stat); - return(GET_STAT()); /* (ks/hs): Clear pending IRQ */ -} -#endif /* ALTSTAT_SCREW_UP */ - -/* - * Handler for command with Read Multiple - */ -ide_startstop_t task_mulin_intr (ide_drive_t *drive) -{ - unsigned int msect, nsect; - -#ifdef ALTSTAT_SCREW_UP - byte stat = altstat_multi_busy(drive, GET_ALTSTAT(), "read"); -#else - byte stat = GET_STAT(); -#endif /* ALTSTAT_SCREW_UP */ - - byte io_32bit = drive->io_32bit; - struct request *rq = HWGROUP(drive)->rq; - char *pBuf = NULL; - unsigned long flags; - - if (!OK_STAT(stat,DATA_READY,BAD_R_STAT)) { - if (stat & (ERR_STAT|DRQ_STAT)) { - return ide_error(drive, "task_mulin_intr", stat); - } - /* no data yet, so wait for another interrupt */ - ide_set_handler(drive, task_mulin_intr, WAIT_CMD, NULL); - return ide_started; - } - - /* (ks/hs): Fixed Multi-Sector transfer */ - msect = drive->mult_count; - -#ifdef ALTSTAT_SCREW_UP - /* - * Screw the request we do not support bad data-phase setups! - * Either read and learn the ATA standard or crash yourself! - */ - if (!msect) { - /* - * (ks/hs): Drive supports multi-sector transfer, - * drive->mult_count was not set - */ - nsect = 1; - while (rq->current_nr_sectors) { - pBuf = ide_map_rq(rq, &flags); - DTF("Multiread: %p, nsect: %d, rq->current_nr_sectors: %ld\n", pBuf, nsect, rq->current_nr_sectors); - drive->io_32bit = 0; - taskfile_input_data(drive, pBuf, nsect * SECTOR_WORDS); - ide_unmap_rq(rq, pBuf, &flags); - drive->io_32bit = io_32bit; - rq->errors = 0; - rq->current_nr_sectors -= nsect; - stat = altstat_multi_poll(drive, GET_ALTSTAT(), "read"); - } - ide_end_request(drive, 1); - return ide_stopped; - } -#endif /* ALTSTAT_SCREW_UP */ - - do { - nsect = rq->current_nr_sectors; - if (nsect > msect) - nsect = msect; - - pBuf = ide_map_rq(rq, &flags); - - DTF("Multiread: %p, nsect: %d , rq->current_nr_sectors: %ld\n", - pBuf, nsect, rq->current_nr_sectors); - drive->io_32bit = 0; - taskfile_input_data(drive, pBuf, nsect * SECTOR_WORDS); - ide_unmap_rq(rq, pBuf, &flags); - drive->io_32bit = io_32bit; - rq->errors = 0; - rq->current_nr_sectors -= nsect; - msect -= nsect; - if (!rq->current_nr_sectors) { - if (!ide_end_request(drive, 1)) - return ide_stopped; - } - } while (msect); - - - /* - * more data left - */ - ide_set_handler(drive, task_mulin_intr, WAIT_CMD, NULL); - return ide_started; -} - -ide_startstop_t pre_task_out_intr (ide_drive_t *drive, struct request *rq) +static ide_startstop_t pre_task_out_intr (ide_drive_t *drive, struct request *rq) { ide_task_t *args = rq->special; ide_startstop_t startstop; @@ -906,21 +572,7 @@ rq->current_nr_sectors--; ide_unmap_rq(rq, buf, &flags); } else { - /* - * (ks/hs): Stuff the first sector(s) - * by implicitly calling the handler - */ - if (!(drive_is_ready(drive))) { - int i; - /* - * (ks/hs): FIXME: Replace hard-coded - * 100, error handling? - */ - for (i=0; i<100; i++) { - if (drive_is_ready(drive)) - break; - } - } + ata_poll_drive_ready(drive); return args->handler(drive); } return ide_started; @@ -929,7 +581,7 @@ /* * Handler for command with PIO data-out phase */ -ide_startstop_t task_out_intr (ide_drive_t *drive) +static ide_startstop_t task_out_intr(ide_drive_t *drive) { byte stat = GET_STAT(); byte io_32bit = drive->io_32bit; @@ -960,98 +612,7 @@ return ide_started; } -/* - * Handler for command write multiple - * Called directly from execute_drive_cmd for the first bunch of sectors, - * afterwards only by the ISR - */ -ide_startstop_t task_mulout_intr (ide_drive_t *drive) -{ - unsigned int msect, nsect; - -#ifdef ALTSTAT_SCREW_UP - byte stat = altstat_multi_busy(drive, GET_ALTSTAT(), "write"); -#else - byte stat = GET_STAT(); -#endif /* ALTSTAT_SCREW_UP */ - - byte io_32bit = drive->io_32bit; - struct request *rq = HWGROUP(drive)->rq; - ide_hwgroup_t *hwgroup = HWGROUP(drive); - char *pBuf = NULL; - unsigned long flags; - - /* - * (ks/hs): Handle last IRQ on multi-sector transfer, - * occurs after all data was sent in this chunk - */ - if (rq->current_nr_sectors == 0) { - if (stat & (ERR_STAT|DRQ_STAT)) - return ide_error(drive, "task_mulout_intr", stat); - - /* - * there may be more, ide_do_request will restart it if - * necessary - */ - ide_end_request(drive, 1); - return ide_stopped; - } - - if (!OK_STAT(stat,DATA_READY,BAD_R_STAT)) { - if (stat & (ERR_STAT|DRQ_STAT)) { - return ide_error(drive, "task_mulout_intr", stat); - } - /* no data yet, so wait for another interrupt */ - if (hwgroup->handler == NULL) - ide_set_handler(drive, &task_mulout_intr, WAIT_CMD, NULL); - return ide_started; - } - - /* (ks/hs): See task_mulin_intr */ - msect = drive->mult_count; - -#ifdef ALTSTAT_SCREW_UP - /* - * Screw the request we do not support bad data-phase setups! - * Either read and learn the ATA standard or crash yourself! - */ - if (!msect) { - nsect = 1; - while (rq->current_nr_sectors) { - pBuf = ide_map_rq(rq, &flags); - DTF("Multiwrite: %p, nsect: %d, rq->current_nr_sectors: %ld\n", pBuf, nsect, rq->current_nr_sectors); - drive->io_32bit = 0; - taskfile_output_data(drive, pBuf, nsect * SECTOR_WORDS); - ide_unmap_rq(pBuf, &flags); - drive->io_32bit = io_32bit; - rq->errors = 0; - rq->current_nr_sectors -= nsect; - stat = altstat_multi_poll(drive, GET_ALTSTAT(), "write"); - } - ide_end_request(drive, 1); - return ide_stopped; - } -#endif /* ALTSTAT_SCREW_UP */ - - nsect = rq->current_nr_sectors; - if (nsect > msect) - nsect = msect; - - pBuf = ide_map_rq(rq, &flags); - DTF("Multiwrite: %p, nsect: %d , rq->current_nr_sectors: %ld\n", - pBuf, nsect, rq->current_nr_sectors); - drive->io_32bit = 0; - taskfile_output_data(drive, pBuf, nsect * SECTOR_WORDS); - ide_unmap_rq(rq, pBuf, &flags); - drive->io_32bit = io_32bit; - rq->errors = 0; - rq->current_nr_sectors -= nsect; - if (hwgroup->handler == NULL) - ide_set_handler(drive, &task_mulout_intr, WAIT_CMD, NULL); - return ide_started; -} - -ide_startstop_t pre_bio_out_intr (ide_drive_t *drive, struct request *rq) +static ide_startstop_t pre_bio_out_intr(ide_drive_t *drive, struct request *rq) { ide_task_t *args = rq->special; ide_startstop_t startstop; @@ -1064,34 +625,14 @@ if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) return startstop; - /* - * (ks/hs): Stuff the first sector(s) - * by implicitly calling the handler - */ - if (!(drive_is_ready(drive))) { - int i; - /* - * (ks/hs): FIXME: Replace hard-coded - * 100, error handling? - */ - for (i=0; i<100; i++) { - if (drive_is_ready(drive)) - break; - } - } - + ata_poll_drive_ready(drive); return args->handler(drive); } -ide_startstop_t bio_mulout_intr (ide_drive_t *drive) +static ide_startstop_t bio_mulout_intr (ide_drive_t *drive) { -#ifdef ALTSTAT_SCREW_UP - byte stat = altstat_multi_busy(drive, GET_ALTSTAT(), "write"); -#else byte stat = GET_STAT(); -#endif /* ALTSTAT_SCREW_UP */ - byte io_32bit = drive->io_32bit; struct request *rq = &HWGROUP(drive)->wrq; ide_hwgroup_t *hwgroup = HWGROUP(drive); @@ -1129,15 +670,15 @@ } do { - char *buffer; - int nsect = rq->current_nr_sectors; + char *buffer; + int nsect = rq->current_nr_sectors; unsigned long flags; if (nsect > mcount) nsect = mcount; mcount -= nsect; - buffer = ide_map_buffer(rq, &flags); + buffer = bio_kmap_irq(rq->bio, &flags) + ide_rq_offset(rq); rq->sector += nsect; rq->nr_sectors -= nsect; rq->current_nr_sectors -= nsect; @@ -1161,7 +702,7 @@ * re-entering us on the last transfer. */ taskfile_output_data(drive, buffer, nsect * SECTOR_WORDS); - ide_unmap_buffer(buffer, &flags); + bio_kunmap_irq(buffer, &flags); } while (mcount); drive->io_32bit = io_32bit; @@ -1203,6 +744,60 @@ return(NULL); } +/* + * Handler for command with Read Multiple + */ +static ide_startstop_t task_mulin_intr(ide_drive_t *drive) +{ + unsigned int msect, nsect; + byte stat = GET_STAT(); + byte io_32bit = drive->io_32bit; + struct request *rq = HWGROUP(drive)->rq; + char *pBuf = NULL; + unsigned long flags; + + if (!OK_STAT(stat,DATA_READY,BAD_R_STAT)) { + if (stat & (ERR_STAT|DRQ_STAT)) { + return ide_error(drive, "task_mulin_intr", stat); + } + /* no data yet, so wait for another interrupt */ + ide_set_handler(drive, task_mulin_intr, WAIT_CMD, NULL); + return ide_started; + } + + /* (ks/hs): Fixed Multi-Sector transfer */ + msect = drive->mult_count; + + do { + nsect = rq->current_nr_sectors; + if (nsect > msect) + nsect = msect; + + pBuf = ide_map_rq(rq, &flags); + + DTF("Multiread: %p, nsect: %d , rq->current_nr_sectors: %ld\n", + pBuf, nsect, rq->current_nr_sectors); + drive->io_32bit = 0; + taskfile_input_data(drive, pBuf, nsect * SECTOR_WORDS); + ide_unmap_rq(rq, pBuf, &flags); + drive->io_32bit = io_32bit; + rq->errors = 0; + rq->current_nr_sectors -= nsect; + msect -= nsect; + if (!rq->current_nr_sectors) { + if (!ide_end_request(drive, 1)) + return ide_stopped; + } + } while (msect); + + + /* + * more data left + */ + ide_set_handler(drive, task_mulin_intr, WAIT_CMD, NULL); + return ide_started; +} + /* Called by internal to feature out type of command being called */ ide_handler_t * ide_handler_parser (struct hd_drive_task_hdr *taskfile, struct hd_drive_hob_hdr *hobfile) { @@ -1298,8 +893,8 @@ case WIN_QUEUED_SERVICE: case WIN_PACKETCMD: default: - return(NULL); - } + return NULL; + } } /* Called by ioctl to feature out type of command being called */ @@ -1432,7 +1027,7 @@ /* * This function is intended to be used prior to invoking ide_do_drive_cmd(). */ -void ide_init_drive_taskfile (struct request *rq) +static void ide_init_drive_taskfile (struct request *rq) { memset(rq, 0, sizeof(*rq)); rq->flags = REQ_DRIVE_TASKFILE; @@ -1445,7 +1040,7 @@ * * ide_raw_taskfile is the one that user-space executes. */ -int ide_wait_taskfile (ide_drive_t *drive, struct hd_drive_task_hdr *taskfile, struct hd_drive_hob_hdr *hobfile, byte *buf) +int ide_wait_taskfile(ide_drive_t *drive, struct hd_drive_task_hdr *taskfile, struct hd_drive_hob_hdr *hobfile, byte *buf) { struct request rq; ide_task_t args; @@ -1481,7 +1076,7 @@ return ide_do_drive_cmd(drive, &rq, ide_wait); } -int ide_raw_taskfile (ide_drive_t *drive, ide_task_t *args, byte *buf) +int ide_raw_taskfile(ide_drive_t *drive, ide_task_t *args, byte *buf) { struct request rq; ide_init_drive_taskfile(&rq); @@ -1494,24 +1089,11 @@ return ide_do_drive_cmd(drive, &rq, ide_wait); } - -#ifdef CONFIG_IDE_TASK_IOCTL_DEBUG -char * ide_ioctl_verbose (unsigned int cmd) -{ - return("unknown"); -} - -char * ide_task_cmd_verbose (byte task) -{ - return("unknown"); -} -#endif /* CONFIG_IDE_TASK_IOCTL_DEBUG */ - /* * The taskfile glue table * * reqtask.data_phase reqtask.req_cmd - * args.command_type args.handler + * args.command_type args.handler * * TASKFILE_P_OUT_DMAQ ?? ?? * TASKFILE_P_IN_DMAQ ?? ?? @@ -1537,202 +1119,35 @@ * TASKFILE_IN IDE_DRIVE_TASK_IN task_in_intr * TASKFILE_NO_DATA IDE_DRIVE_TASK_NO_DATA task_no_data_intr * - * IDE_DRIVE_TASK_SET_XFER task_no_data_intr - * IDE_DRIVE_TASK_INVALID + * IDE_DRIVE_TASK_SET_XFER task_no_data_intr + * IDE_DRIVE_TASK_INVALID * */ -#define MAX_DMA (256*SECTOR_WORDS) - -int ide_taskfile_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +/* + * Issue ATA command and wait for completion. use for implementing commands in + * kernel. + * + * The caller has to make sure buf is never NULL! + */ +static int ide_wait_cmd(ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *argbuf) { - ide_task_request_t *req_task; - ide_task_t args; - - byte *outbuf = NULL; - byte *inbuf = NULL; - task_ioreg_t *argsptr = args.tfRegister; - task_ioreg_t *hobsptr = args.hobRegister; - int err = 0; - int tasksize = sizeof(struct ide_task_request_s); - int taskin = 0; - int taskout = 0; - - req_task = kmalloc(tasksize, GFP_KERNEL); - if (req_task == NULL) return -ENOMEM; - memset(req_task, 0, tasksize); - if (copy_from_user(req_task, (void *) arg, tasksize)) { - kfree(req_task); - return -EFAULT; - } - - taskout = (int) req_task->out_size; - taskin = (int) req_task->in_size; - - if (taskout) { - int outtotal = tasksize; - outbuf = kmalloc(taskout, GFP_KERNEL); - if (outbuf == NULL) { - err = -ENOMEM; - goto abort; - } - memset(outbuf, 0, taskout); - if (copy_from_user(outbuf, (void *)arg + outtotal, taskout)) { - err = -EFAULT; - goto abort; - } - } - - if (taskin) { - int intotal = tasksize + taskout; - inbuf = kmalloc(taskin, GFP_KERNEL); - if (inbuf == NULL) { - err = -ENOMEM; - goto abort; - } - memset(inbuf, 0, taskin); - if (copy_from_user(inbuf, (void *)arg + intotal , taskin)) { - err = -EFAULT; - goto abort; - } - } - - memset(argsptr, 0, HDIO_DRIVE_TASK_HDR_SIZE); - memset(hobsptr, 0, HDIO_DRIVE_HOB_HDR_SIZE); - memcpy(argsptr, req_task->io_ports, HDIO_DRIVE_TASK_HDR_SIZE); - memcpy(hobsptr, req_task->hob_ports, HDIO_DRIVE_HOB_HDR_SIZE); - - args.tf_in_flags = req_task->in_flags; - args.tf_out_flags = req_task->out_flags; - args.data_phase = req_task->data_phase; - args.command_type = req_task->req_cmd; - -#ifdef CONFIG_IDE_TASK_IOCTL_DEBUG - DTF("%s: ide_ioctl_cmd %s: ide_task_cmd %s\n", - drive->name, - ide_ioctl_verbose(cmd), - ide_task_cmd_verbose(args.tfRegister[IDE_COMMAND_OFFSET])); -#endif /* CONFIG_IDE_TASK_IOCTL_DEBUG */ - - switch(req_task->data_phase) { - case TASKFILE_OUT_DMAQ: - case TASKFILE_OUT_DMA: - args.prehandler = NULL; - args.handler = NULL; - args.posthandler = NULL; - err = ide_raw_taskfile(drive, &args, outbuf); - break; - case TASKFILE_IN_DMAQ: - case TASKFILE_IN_DMA: - args.prehandler = NULL; - args.handler = NULL; - args.posthandler = NULL; - err = ide_raw_taskfile(drive, &args, inbuf); - break; - case TASKFILE_IN_OUT: -#if 0 - args.prehandler = &pre_task_out_intr; - args.handler = &task_out_intr; - args.posthandler = NULL; - err = ide_raw_taskfile(drive, &args, outbuf); - args.prehandler = NULL; - args.handler = &task_in_intr; - args.posthandler = NULL; - err = ide_raw_taskfile(drive, &args, inbuf); - break; -#else - err = -EFAULT; - goto abort; -#endif - case TASKFILE_MULTI_OUT: - if (drive->mult_count) { - args.prehandler = &pre_task_out_intr; - args.handler = &task_mulout_intr; - args.posthandler = NULL; - err = ide_raw_taskfile(drive, &args, outbuf); - } else { - /* (hs): give up if multcount is not set */ - printk("%s: %s Multimode Write " \ - "multcount is not set\n", - drive->name, __FUNCTION__); - err = -EPERM; - goto abort; - } - break; - case TASKFILE_OUT: - args.prehandler = &pre_task_out_intr; - args.handler = &task_out_intr; - args.posthandler = NULL; - err = ide_raw_taskfile(drive, &args, outbuf); - break; - case TASKFILE_MULTI_IN: - if (drive->mult_count) { - args.prehandler = NULL; - args.handler = &task_mulin_intr; - args.posthandler = NULL; - err = ide_raw_taskfile(drive, &args, inbuf); - } else { - /* (hs): give up if multcount is not set */ - printk("%s: %s Multimode Read failure " \ - "multcount is not set\n", - drive->name, __FUNCTION__); - err = -EPERM; - goto abort; - } - break; - case TASKFILE_IN: - args.prehandler = NULL; - args.handler = &task_in_intr; - args.posthandler = NULL; - err = ide_raw_taskfile(drive, &args, inbuf); - break; - case TASKFILE_NO_DATA: - args.prehandler = NULL; - args.handler = &task_no_data_intr; - args.posthandler = NULL; - err = ide_raw_taskfile(drive, &args, NULL); - break; - default: - args.prehandler = NULL; - args.handler = NULL; - args.posthandler = NULL; - err = -EFAULT; - goto abort; - } + struct request rq; - memcpy(req_task->io_ports, &(args.tfRegister), HDIO_DRIVE_TASK_HDR_SIZE); - memcpy(req_task->hob_ports, &(args.hobRegister), HDIO_DRIVE_HOB_HDR_SIZE); - req_task->in_flags = args.tf_in_flags; - req_task->out_flags = args.tf_out_flags; + /* FIXME: Do we really have to zero out the buffer? + */ + memset(argbuf, 0, 4 + SECTOR_WORDS * 4 * sectors); + ide_init_drive_cmd(&rq); + rq.buffer = argbuf; + *argbuf++ = cmd; + *argbuf++ = nsect; + *argbuf++ = feature; + *argbuf++ = sectors; - if (copy_to_user((void *)arg, req_task, tasksize)) { - err = -EFAULT; - goto abort; - } - if (taskout) { - int outtotal = tasksize; - if (copy_to_user((void *)arg+outtotal, outbuf, taskout)) { - err = -EFAULT; - goto abort; - } - } - if (taskin) { - int intotal = tasksize + taskout; - if (copy_to_user((void *)arg+intotal, inbuf, taskin)) { - err = -EFAULT; - goto abort; - } - } -abort: - kfree(req_task); - if (outbuf != NULL) - kfree(outbuf); - if (inbuf != NULL) - kfree(inbuf); - return err; + return ide_do_drive_cmd(drive, &rq, ide_wait); } -int ide_cmd_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +int ide_cmd_ioctl(ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int err = 0; byte args[4], *argbuf = args; @@ -1788,19 +1203,26 @@ int ide_task_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int err = 0; - byte args[7], *argbuf = args; + u8 args[7]; + u8 *argbuf; int argsize = 7; + struct request rq; + + argbuf = args; if (copy_from_user(args, (void *)arg, 7)) return -EFAULT; - err = ide_wait_cmd_task(drive, argbuf); + + ide_init_drive_cmd(&rq); + rq.flags = REQ_DRIVE_TASK; + rq.buffer = argbuf; + err = ide_do_drive_cmd(drive, &rq, ide_wait); if (copy_to_user((void *)arg, argbuf, argsize)) err = -EFAULT; return err; } EXPORT_SYMBOL(drive_is_ready); -EXPORT_SYMBOL(task_read_24); EXPORT_SYMBOL(ata_input_data); EXPORT_SYMBOL(ata_output_data); EXPORT_SYMBOL(atapi_input_bytes); @@ -1809,166 +1231,15 @@ EXPORT_SYMBOL(taskfile_output_data); EXPORT_SYMBOL(do_rw_taskfile); EXPORT_SYMBOL(do_taskfile); -// EXPORT_SYMBOL(flagged_taskfile); - -//EXPORT_SYMBOL(ide_end_taskfile); EXPORT_SYMBOL(set_multmode_intr); -EXPORT_SYMBOL(set_geometry_intr); -EXPORT_SYMBOL(recal_intr); EXPORT_SYMBOL(task_no_data_intr); -EXPORT_SYMBOL(task_in_intr); -EXPORT_SYMBOL(task_mulin_intr); -EXPORT_SYMBOL(pre_task_out_intr); -EXPORT_SYMBOL(task_out_intr); -EXPORT_SYMBOL(task_mulout_intr); -EXPORT_SYMBOL(ide_init_drive_taskfile); EXPORT_SYMBOL(ide_wait_taskfile); EXPORT_SYMBOL(ide_raw_taskfile); EXPORT_SYMBOL(ide_pre_handler_parser); EXPORT_SYMBOL(ide_handler_parser); EXPORT_SYMBOL(ide_cmd_type_parser); -EXPORT_SYMBOL(ide_taskfile_ioctl); EXPORT_SYMBOL(ide_cmd_ioctl); EXPORT_SYMBOL(ide_task_ioctl); - - -#ifdef CONFIG_PKT_TASK_IOCTL - -#if 0 -{ - -{ /* start cdrom */ - - struct cdrom_info *info = drive->driver_data; - - if (info->dma) { - if (info->cmd == READ) { - info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive); - } else if (info->cmd == WRITE) { - info->dma = !HWIF(drive)->dmaproc(ide_dma_write, drive); - } else { - printk("ide-cd: DMA set, but not allowed\n"); - } - } - - /* Set up the controller registers. */ - OUT_BYTE (info->dma, IDE_FEATURE_REG); - OUT_BYTE (0, IDE_NSECTOR_REG); - OUT_BYTE (0, IDE_SECTOR_REG); - - OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG); - OUT_BYTE (xferlen >> 8 , IDE_HCYL_REG); - if (IDE_CONTROL_REG) - OUT_BYTE (drive->ctl, IDE_CONTROL_REG); - - if (info->dma) - (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); - - if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { - ide_set_handler (drive, handler, WAIT_CMD, cdrom_timer_expiry); - OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ - return ide_started; - } else { - OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ - return (*handler) (drive); - } - -} /* end cdrom */ - -{ /* start floppy */ - - idefloppy_floppy_t *floppy = drive->driver_data; - idefloppy_bcount_reg_t bcount; - int dma_ok = 0; - - floppy->pc=pc; /* Set the current packet command */ - - pc->retries++; - pc->actually_transferred=0; /* We haven't transferred any data yet */ - pc->current_position=pc->buffer; - bcount.all = IDE_MIN(pc->request_transfer, 63 * 1024); - -#ifdef CONFIG_BLK_DEV_IDEDMA - if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) { - (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); -#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); - -#ifdef CONFIG_BLK_DEV_IDEDMA - if (dma_ok) { /* Begin DMA, if necessary */ - set_bit (PC_DMA_IN_PROGRESS, &pc->flags); - (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); - } -#endif /* CONFIG_BLK_DEV_IDEDMA */ - -} /* end floppy */ - -{ /* start tape */ - - idetape_tape_t *tape = drive->driver_data; - -#ifdef CONFIG_BLK_DEV_IDEDMA - if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) { - printk (KERN_WARNING "ide-tape: DMA disabled, reverting to PIO\n"); - (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); -#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); -#ifdef CONFIG_BLK_DEV_IDEDMA - if (dma_ok) { /* Begin DMA, if necessary */ - set_bit (PC_DMA_IN_PROGRESS, &pc->flags); - (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); - } -#endif /* CONFIG_BLK_DEV_IDEDMA */ - if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) { - ide_set_handler(drive, &idetape_transfer_pc, IDETAPE_WAIT_CMD, NULL); - OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); - return ide_started; - } else { - OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); - return idetape_transfer_pc(drive); - } - -} /* end tape */ - -} -#endif - -int pkt_taskfile_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ -#if 0 - switch(req_task->data_phase) { - case TASKFILE_P_OUT_DMAQ: - case TASKFILE_P_IN_DMAQ: - case TASKFILE_P_OUT_DMA: - case TASKFILE_P_IN_DMA: - case TASKFILE_P_OUT: - case TASKFILE_P_IN: - } -#endif - return -ENOMSG; -} - -EXPORT_SYMBOL(pkt_taskfile_ioctl); - -#endif /* CONFIG_PKT_TASK_IOCTL */ diff -Nru a/drivers/ide/ide.c b/drivers/ide/ide.c --- a/drivers/ide/ide.c Thu Mar 7 18:17:43 2002 +++ b/drivers/ide/ide.c Thu Mar 7 18:17:43 2002 @@ -1,10 +1,6 @@ /* - * linux/drivers/ide/ide.c Version 6.31 June 9, 2000 - * * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) - */ - -/* + * * Mostly written by Mark Lord * and Gadi Oxman * and Andre Hedrick @@ -114,21 +110,12 @@ * Native ATA-100 support * Prep for Cascades Project * Version 6.32 4GB highmem support for DMA, and mapping of those for - * PIO transfer (Jens Axboe) + * PIO transfer (Jens Axboe) * * Some additional driver compile-time options are in ./include/linux/ide.h - * - * To do, in likely order of completion: - * - modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f - * */ -#define REVISION "Revision: 6.32" -#define VERSION "Id: ide.c 6.32 2001/05/24" - -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - -#define _IDE_C /* Tell ide.h it's really us */ +#define VERSION "7.0.0" #include #include @@ -144,8 +131,8 @@ #include #include #ifndef MODULE -#include -#endif /* MODULE */ +# include +#endif #include #include #include @@ -153,6 +140,8 @@ #include #include #include +#include +#include #include #include @@ -162,52 +151,272 @@ #include "ide_modes.h" -#ifdef CONFIG_KMOD -#include -#endif /* CONFIG_KMOD */ +/* + * Those will be moved into separate header files eventually. + */ +#ifdef CONFIG_BLK_DEV_RZ1000 +extern void ide_probe_for_rz100x(void); +#endif +#ifdef CONFIG_ETRAX_IDE +extern void init_e100_ide(void); +#endif +#ifdef CONFIG_BLK_DEV_CMD640 +extern void ide_probe_for_cmd640x(void); +#endif +#ifdef CONFIG_BLK_DEV_PDC4030 +extern int ide_probe_for_pdc4030(void); +#endif +#ifdef CONFIG_BLK_DEV_IDE_PMAC +extern void pmac_ide_probe(void); +#endif +#ifdef CONFIG_BLK_DEV_IDE_ICSIDE +extern void icside_init(void); +#endif +#ifdef CONFIG_BLK_DEV_IDE_RAPIDE +extern void rapide_init(void); +#endif +#ifdef CONFIG_BLK_DEV_GAYLE +extern void gayle_init(void); +#endif +#ifdef CONFIG_BLK_DEV_FALCON_IDE +extern void falconide_init(void); +#endif +#ifdef CONFIG_BLK_DEV_MAC_IDE +extern void macide_init(void); +#endif +#ifdef CONFIG_BLK_DEV_Q40IDE +extern void q40ide_init(void); +#endif +#ifdef CONFIG_BLK_DEV_BUDDHA +extern void buddha_init(void); +#endif +#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP) +extern void pnpide_init(int); +#endif -/* default maximum number of failures */ -#define IDE_DEFAULT_MAX_FAILURES 1 +/* + * Constant tables for PIO mode programming: + */ +const ide_pio_timings_t ide_pio_timings[6] = { + { 70, 165, 600 }, /* PIO Mode 0 */ + { 50, 125, 383 }, /* PIO Mode 1 */ + { 30, 100, 240 }, /* PIO Mode 2 */ + { 30, 80, 180 }, /* PIO Mode 3 with IORDY */ + { 25, 70, 120 }, /* PIO Mode 4 with IORDY */ + { 20, 50, 100 } /* PIO Mode 5 with IORDY (nonstandard) */ +}; + +/* + * Black list. Some drives incorrectly report their maximal PIO mode, + * at least in respect to CMD640. Here we keep info on some known drives. + */ +static struct ide_pio_info { + const char *name; + int pio; +} ide_pio_blacklist[] = { +/* { "Conner Peripherals 1275MB - CFS1275A", 4 }, */ + { "Conner Peripherals 540MB - CFS540A", 3 }, + + { "WDC AC2700", 3 }, + { "WDC AC2540", 3 }, + { "WDC AC2420", 3 }, + { "WDC AC2340", 3 }, + { "WDC AC2250", 0 }, + { "WDC AC2200", 0 }, + { "WDC AC21200", 4 }, + { "WDC AC2120", 0 }, + { "WDC AC2850", 3 }, + { "WDC AC1270", 3 }, + { "WDC AC1170", 1 }, + { "WDC AC1210", 1 }, + { "WDC AC280", 0 }, +/* { "WDC AC21000", 4 }, */ + { "WDC AC31000", 3 }, + { "WDC AC31200", 3 }, +/* { "WDC AC31600", 4 }, */ + + { "Maxtor 7131 AT", 1 }, + { "Maxtor 7171 AT", 1 }, + { "Maxtor 7213 AT", 1 }, + { "Maxtor 7245 AT", 1 }, + { "Maxtor 7345 AT", 1 }, + { "Maxtor 7546 AT", 3 }, + { "Maxtor 7540 AV", 3 }, + + { "SAMSUNG SHD-3121A", 1 }, + { "SAMSUNG SHD-3122A", 1 }, + { "SAMSUNG SHD-3172A", 1 }, + +/* { "ST51080A", 4 }, + * { "ST51270A", 4 }, + * { "ST31220A", 4 }, + * { "ST31640A", 4 }, + * { "ST32140A", 4 }, + * { "ST3780A", 4 }, + */ + { "ST5660A", 3 }, + { "ST3660A", 3 }, + { "ST3630A", 3 }, + { "ST3655A", 3 }, + { "ST3391A", 3 }, + { "ST3390A", 1 }, + { "ST3600A", 1 }, + { "ST3290A", 0 }, + { "ST3144A", 0 }, + { "ST3491A", 1 }, /* reports 3, should be 1 or 2 (depending on + * drive) according to Seagates FIND-ATA program */ + + { "QUANTUM ELS127A", 0 }, + { "QUANTUM ELS170A", 0 }, + { "QUANTUM LPS240A", 0 }, + { "QUANTUM LPS210A", 3 }, + { "QUANTUM LPS270A", 3 }, + { "QUANTUM LPS365A", 3 }, + { "QUANTUM LPS540A", 3 }, + { "QUANTUM LIGHTNING 540A", 3 }, + { "QUANTUM LIGHTNING 730A", 3 }, + + { "QUANTUM FIREBALL_540", 3 }, /* Older Quantum Fireballs don't work */ + { "QUANTUM FIREBALL_640", 3 }, + { "QUANTUM FIREBALL_1080", 3 }, + { "QUANTUM FIREBALL_1280", 3 }, + { NULL, 0 } +}; -static const byte ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, IDE4_MAJOR, IDE5_MAJOR, IDE6_MAJOR, IDE7_MAJOR, IDE8_MAJOR, IDE9_MAJOR }; +/* default maximum number of failures */ +#define IDE_DEFAULT_MAX_FAILURES 1 -static int idebus_parameter; /* holds the "idebus=" parameter */ -static int system_bus_speed; /* holds what we think is VESA/PCI bus speed */ -static int initializing; /* set while initializing built-in drivers */ +static int idebus_parameter; /* holds the "idebus=" parameter */ +int system_bus_speed; /* holds what we think is VESA/PCI bus speed */ +static int initializing; /* set while initializing built-in drivers */ /* - * protects global structures etc, we want to split this into per-hwgroup - * instead. + * Protects access to global structures etc. */ spinlock_t ide_lock __cacheline_aligned = SPIN_LOCK_UNLOCKED; #ifdef CONFIG_BLK_DEV_IDEPCI -static int ide_scan_direction; /* THIS was formerly 2.2.x pci=reverse */ -#endif /* CONFIG_BLK_DEV_IDEPCI */ +static int ide_scan_direction; /* THIS was formerly 2.2.x pci=reverse */ +#endif #if defined(__mc68000__) || defined(CONFIG_APUS) /* - * ide_lock is used by the Atari code to obtain access to the IDE interrupt, + * This is used by the Atari code to obtain access to the IDE interrupt, * which is shared between several drivers. */ static int ide_intr_lock; -#endif /* __mc68000__ || CONFIG_APUS */ +#endif int noautodma = 0; /* - * This is the anchor of the single linked list of ide device type drivers. + * This is declared extern in ide.h, for access by other IDE modules: */ -struct ide_driver_s *ide_drivers; +ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */ + /* - * This is declared extern in ide.h, for access by other IDE modules: + * This routine searches the ide_pio_blacklist for an entry + * matching the start/whole of the supplied model name. + * + * Returns -1 if no match found. + * Otherwise returns the recommended PIO mode from ide_pio_blacklist[]. + */ +int ide_scan_pio_blacklist (char *model) +{ + struct ide_pio_info *p; + + for (p = ide_pio_blacklist; p->name != NULL; p++) { + if (strncmp(p->name, model, strlen(p->name)) == 0) + return p->pio; + } + return -1; +} + +/* + * This routine returns the recommended PIO settings for a given drive, + * based on the drive->id information and the ide_pio_blacklist[]. + * This is used by most chipset support modules when "auto-tuning". */ -ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */ + +/* + * Drive PIO mode auto selection + */ +byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d) +{ + int pio_mode; + int cycle_time = 0; + int use_iordy = 0; + struct hd_driveid* id = drive->id; + int overridden = 0; + int blacklisted = 0; + + if (mode_wanted != 255) { + pio_mode = mode_wanted; + } else if (!drive->id) { + pio_mode = 0; + } else if ((pio_mode = ide_scan_pio_blacklist(id->model)) != -1) { + overridden = 1; + blacklisted = 1; + use_iordy = (pio_mode > 2); + } else { + pio_mode = id->tPIO; + if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */ + pio_mode = 2; + overridden = 1; + } + if (id->field_valid & 2) { /* drive implements ATA2? */ + if (id->capability & 8) { /* drive supports use_iordy? */ + use_iordy = 1; + cycle_time = id->eide_pio_iordy; + if (id->eide_pio_modes & 7) { + overridden = 0; + if (id->eide_pio_modes & 4) + pio_mode = 5; + else if (id->eide_pio_modes & 2) + pio_mode = 4; + else + pio_mode = 3; + } + } else { + cycle_time = id->eide_pio; + } + } + +#if 0 + if (drive->id->major_rev_num & 0x0004) printk("ATA-2 "); +#endif + + /* + * Conservative "downgrade" for all pre-ATA2 drives + */ + if (pio_mode && pio_mode < 4) { + pio_mode--; + overridden = 1; +#if 0 + use_iordy = (pio_mode > 2); +#endif + if (cycle_time && cycle_time < ide_pio_timings[pio_mode].cycle_time) + cycle_time = 0; /* use standard timing */ + } + } + if (pio_mode > max_mode) { + pio_mode = max_mode; + cycle_time = 0; + } + if (d) { + d->pio_mode = pio_mode; + d->cycle_time = cycle_time ? cycle_time : ide_pio_timings[pio_mode].cycle_time; + d->use_iordy = use_iordy; + d->overridden = overridden; + d->blacklisted = blacklisted; + } + return pio_mode; +} #if (DISK_RECOVERY_TIME > 0) /* - * For really screwy hardware (hey, at least it *can* be used with Linux) + * For really screwed hardware (hey, at least it *can* be used with Linux) * we can enforce a minimum delay time between successive operations. */ static unsigned long read_timer (void) @@ -218,7 +427,7 @@ __save_flags(flags); /* local CPU only */ __cli(); /* local CPU only */ t = jiffies * 11932; - outb_p(0, 0x43); + outb_p(0, 0x43); i = inb_p(0x40); i |= inb(0x40) << 8; __restore_flags(flags); /* local CPU only */ @@ -238,6 +447,11 @@ */ static void init_hwif_data (unsigned int index) { + static const byte ide_major[] = { + IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, IDE4_MAJOR, + IDE5_MAJOR, IDE6_MAJOR, IDE7_MAJOR, IDE8_MAJOR, IDE9_MAJOR + }; + unsigned int unit; hw_regs_t hw; ide_hwif_t *hwif = &ide_hwifs[index]; @@ -256,16 +470,13 @@ if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA) hwif->noprobe = 1; /* may be overridden by ide_setup() */ #endif /* CONFIG_BLK_DEV_HD */ - hwif->major = ide_hwif_to_major[index]; - hwif->name[0] = 'i'; - hwif->name[1] = 'd'; - hwif->name[2] = 'e'; - hwif->name[3] = '0' + index; + hwif->major = ide_major[index]; + sprintf(hwif->name, "ide%d", index); hwif->bus_state = BUSSTATE_ON; for (unit = 0; unit < MAX_DRIVES; ++unit) { ide_drive_t *drive = &hwif->drives[unit]; - drive->media = ide_disk; + drive->type = ATA_DISK; drive->select.all = (unit<<4)|0xa0; drive->hwif = hwif; drive->ctl = 0x08; @@ -273,9 +484,7 @@ drive->bad_wstat = BAD_W_STAT; drive->special.b.recalibrate = 1; drive->special.b.set_geometry = 1; - drive->name[0] = 'h'; - drive->name[1] = 'd'; - drive->name[2] = 'a' + (index * MAX_DRIVES) + unit; + sprintf(drive->name, "hd%c", 'a' + (index * MAX_DRIVES) + unit); drive->max_failures = IDE_DEFAULT_MAX_FAILURES; init_waitqueue_head(&drive->wqueue); } @@ -305,7 +514,7 @@ return; /* already initialized */ magic_cookie = 0; - /* Initialise all interface structures */ + /* Initialize all interface structures */ for (index = 0; index < MAX_HWIFS; ++index) init_hwif_data(index); @@ -313,15 +522,16 @@ ide_init_default_hwifs(); idebus_parameter = 0; - system_bus_speed = 0; } /* - * CompactFlash cards and their brethern pretend to be removable hard disks, except: + * CompactFlash cards and their relatives pretend to be removable hard disks, except: * (1) they never have a slave unit, and - * (2) they don't have doorlock mechanisms. + * (2) they don't have a door lock mechanisms. * This test catches them, and is invoked elsewhere when setting appropriate config bits. * + * FIXME FIXME: Yes this is for certain applicable for all of them as time has shown. + * * FIXME: This treatment is probably applicable for *all* PCMCIA (PC CARD) devices, * so in linux 2.3.x we should change this to just treat all PCMCIA drives this way, * and get rid of the model-name tests below (too big of an interface change for 2.2.x). @@ -333,7 +543,8 @@ struct hd_driveid *id = drive->id; if (drive->removable && id != NULL) { - if (id->config == 0x848a) return 1; /* CompactFlash */ + if (id->config == 0x848a) + return 1; /* CompactFlash */ if (!strncmp(id->model, "KODAK ATA_FLASH", 15) /* Kodak */ || !strncmp(id->model, "Hitachi CV", 10) /* Hitachi */ || !strncmp(id->model, "SunDisk SDCFB", 13) /* SunDisk */ @@ -347,30 +558,6 @@ return 0; /* no, it is not a flash memory card */ } -/* - * ide_system_bus_speed() returns what we think is the system VESA/PCI - * bus speed (in MHz). This is used for calculating interface PIO timings. - * The default is 40 for known PCI systems, 50 otherwise. - * The "idebus=xx" parameter can be used to override this value. - * The actual value to be used is computed/displayed the first time through. - */ -int ide_system_bus_speed (void) -{ - if (!system_bus_speed) { - if (idebus_parameter) - system_bus_speed = idebus_parameter; /* user supplied value */ -#ifdef CONFIG_PCI - else if (pci_present()) - system_bus_speed = 33; /* safe default value for PCI */ -#endif /* CONFIG_PCI */ - else - system_bus_speed = 50; /* safe default value for VESA and PCI */ - printk("ide: Assuming %dMHz system bus speed for PIO modes%s\n", system_bus_speed, - idebus_parameter ? "" : "; override with idebus=xx"); - } - return system_bus_speed; -} - int __ide_end_request(ide_drive_t *drive, int uptodate, int nr_secs) { struct request *rq; @@ -435,20 +622,80 @@ spin_unlock_irqrestore(&ide_lock, flags); } +static void ata_pre_reset (ide_drive_t *drive) +{ + if (ata_ops(drive) && ata_ops(drive)->pre_reset) + ata_ops(drive)->pre_reset(drive); + + if (!drive->keep_settings && !drive->using_dma) { + drive->unmask = 0; + drive->io_32bit = 0; + } + + if (drive->using_dma) { + /* check the DMA crc count */ + if (drive->crc_count) { + HWIF(drive)->dmaproc(ide_dma_off_quietly, drive); + if ((HWIF(drive)->speedproc) != NULL) + HWIF(drive)->speedproc(drive, ide_auto_reduce_xfer(drive)); + if (drive->current_speed >= XFER_SW_DMA_0) + HWIF(drive)->dmaproc(ide_dma_on, drive); + } else + HWIF(drive)->dmaproc(ide_dma_off, drive); + } +} + /* - * current_capacity() returns the capacity (in sectors) of a drive - * according to its current geometry/LBA settings. + * The capacity of a drive according to its current geometry/LBA settings in + * sectors. */ -unsigned long current_capacity (ide_drive_t *drive) +unsigned long ata_capacity(ide_drive_t *drive) { - if (!drive->present) + if (!drive->present || !drive->driver) return 0; - if (drive->driver != NULL) - return DRIVER(drive)->capacity(drive); - return 0; + + if (ata_ops(drive) && ata_ops(drive)->capacity) + return ata_ops(drive)->capacity(drive); + + /* FIXME: This magic number seems to be bogous. */ + return 0x7fffffff; +} + +/* + * This is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT commands to + * a drive. It used to do much more, but has been scaled back. + */ +static ide_startstop_t ata_special (ide_drive_t *drive) +{ + special_t *s = &drive->special; + +#ifdef DEBUG + printk("%s: ata_special: 0x%02x\n", drive->name, s->all); +#endif + if (s->b.set_tune) { + ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc; + s->b.set_tune = 0; + if (tuneproc != NULL) + tuneproc(drive, drive->tune_req); + } else if (drive->driver != NULL) { + if (ata_ops(drive)->special) + return ata_ops(drive)->special(drive); + else { + drive->special.all = 0; + drive->mult_req = 0; + + return ide_stopped; + } + } else if (s->all) { + printk("%s: bad special flag: 0x%02x\n", drive->name, s->all); + s->all = 0; + } + + return ide_stopped; } extern struct block_device_operations ide_fops[]; + /* * ide_geninit() is called exactly *once* for each interface. */ @@ -462,22 +709,21 @@ if (!drive->present) continue; - if (drive->media!=ide_disk && drive->media!=ide_floppy) + if (drive->type != ATA_DISK && drive->type != ATA_FLOPPY) continue; register_disk(gd,mk_kdev(hwif->major,unit<forced_geom && drive->noprobe) ? 1 : -#endif /* CONFIG_BLK_DEV_ISAPNP */ - 1<poll_timeout = 0; /* done polling */ return ide_stopped; } -static void check_dma_crc (ide_drive_t *drive) -{ - if (drive->crc_count) { - (void) HWIF(drive)->dmaproc(ide_dma_off_quietly, drive); - if ((HWIF(drive)->speedproc) != NULL) - HWIF(drive)->speedproc(drive, ide_auto_reduce_xfer(drive)); - if (drive->current_speed >= XFER_SW_DMA_0) - (void) HWIF(drive)->dmaproc(ide_dma_on, drive); - } else { - (void) HWIF(drive)->dmaproc(ide_dma_off, drive); - } -} - -static void pre_reset (ide_drive_t *drive) -{ - if (drive->driver != NULL) - DRIVER(drive)->pre_reset(drive); - - if (!drive->keep_settings) { - if (drive->using_dma) { - check_dma_crc(drive); - } else { - drive->unmask = 0; - drive->io_32bit = 0; - } - return; - } - if (drive->using_dma) - check_dma_crc(drive); -} - /* * do_reset1() attempts to recover a confused drive by resetting it. * Unfortunately, resetting a disk drive actually resets all devices on @@ -600,7 +815,7 @@ * Unfortunately, the IDE interface does not generate an interrupt to let * us know when the reset operation has finished, so we must poll for this. * Equally poor, though, is the fact that this may a very long time to complete, - * (up to 30 seconds worstcase). So, instead of busy-waiting here for it, + * (up to 30 seconds worst case). So, instead of busy-waiting here for it, * we set a timer to poll at 50ms intervals. */ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) @@ -614,8 +829,8 @@ __cli(); /* local CPU only */ /* For an ATAPI device, first try an ATAPI SRST. */ - if (drive->media != ide_disk && !do_not_try_atapi) { - pre_reset(drive); + if (drive->type != ATA_DISK && !do_not_try_atapi) { + ata_pre_reset(drive); SELECT_DRIVE(hwif,drive); udelay (20); OUT_BYTE (WIN_SRST, IDE_COMMAND_REG); @@ -630,7 +845,7 @@ * for any of the drives on this interface. */ for (unit = 0; unit < MAX_DRIVES; ++unit) - pre_reset(&hwif->drives[unit]); + ata_pre_reset(&hwif->drives[unit]); #if OK_TO_RESET_CONTROLLER if (!IDE_CONTROL_REG) { @@ -664,7 +879,7 @@ if (hwif->resetproc != NULL) hwif->resetproc(drive); -#endif /* OK_TO_RESET_CONTROLLER */ +#endif __restore_flags (flags); /* local CPU only */ return ide_started; @@ -783,7 +998,7 @@ err = GET_ERR(); printk("%s: %s: error=0x%02x", drive->name, msg, err); #if FANCY_STATUS_DUMPS - if (drive->media == ide_disk) { + if (drive->type == ATA_DISK) { printk(" { "); if (err & ABRT_ERR) printk("DriveStatusError "); if (err & ICRC_ERR) printk("%s", (err & ABRT_ERR) ? "BadCRC " : "BadSector "); @@ -842,7 +1057,7 @@ { int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS; - if (drive->media != ide_disk) + if (drive->type != ATA_DISK) return; while (i > 0) { u32 buffer[16]; @@ -866,11 +1081,6 @@ /* retry only "normal" I/O: */ if (!(rq->flags & REQ_CMD)) { rq->errors = 1; -#if 0 - if (rq->flags & REQ_DRIVE_TASKFILE) - ide_end_taskfile(drive, stat, err); - else -#endif ide_end_drive_cmd(drive, stat, err); return ide_stopped; } @@ -878,7 +1088,7 @@ if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */ rq->errors |= ERROR_RESET; } else { - if (drive->media == ide_disk && (stat & ERR_STAT)) { + if (drive->type == ATA_DISK && (stat & ERR_STAT)) { /* err has different meaning on cdrom and tape */ if (err == ABRT_ERR) { if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY) @@ -898,8 +1108,9 @@ OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG); /* force an abort */ if (rq->errors >= ERROR_MAX) { - if (drive->driver != NULL) - DRIVER(drive)->end_request(drive, 0); + /* ATA-PATTERN */ + if (ata_ops(drive) && ata_ops(drive)->end_request) + ata_ops(drive)->end_request(drive, 0); else ide_end_request(drive, 0); } else { @@ -955,31 +1166,6 @@ } /* - * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT - * commands to a drive. It used to do much more, but has been scaled back. - */ -static ide_startstop_t do_special (ide_drive_t *drive) -{ - special_t *s = &drive->special; - -#ifdef DEBUG - printk("%s: do_special: 0x%02x\n", drive->name, s->all); -#endif - if (s->b.set_tune) { - ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc; - s->b.set_tune = 0; - if (tuneproc != NULL) - tuneproc(drive, drive->tune_req); - } else if (drive->driver != NULL) { - return DRIVER(drive)->special(drive); - } else if (s->all) { - printk("%s: bad special flag: 0x%02x\n", drive->name, s->all); - s->all = 0; - } - return ide_stopped; -} - -/* * This routine busy-waits for the drive status to be not "busy". * It then checks the status for all of the "good" bits and none * of the "bad" bits, and if all is okay it returns 0. All other @@ -994,7 +1180,7 @@ byte stat; int i; unsigned long flags; - + /* bail early if we've exceeded max_failures */ if (drive->max_failures && (drive->failures > drive->max_failures)) { *startstop = ide_stopped; @@ -1040,39 +1226,13 @@ if (rq->flags & REQ_DRIVE_TASKFILE) { ide_task_t *args = rq->special; - if (!(args)) goto args_error; - -#ifdef CONFIG_IDE_TASK_IOCTL_DEBUG - { - printk(KERN_INFO "%s: ", drive->name); -// printk("TF.0=x%02x ", args->tfRegister[IDE_DATA_OFFSET]); - printk("TF.1=x%02x ", args->tfRegister[IDE_FEATURE_OFFSET]); - printk("TF.2=x%02x ", args->tfRegister[IDE_NSECTOR_OFFSET]); - printk("TF.3=x%02x ", args->tfRegister[IDE_SECTOR_OFFSET]); - printk("TF.4=x%02x ", args->tfRegister[IDE_LCYL_OFFSET]); - printk("TF.5=x%02x ", args->tfRegister[IDE_HCYL_OFFSET]); - printk("TF.6=x%02x ", args->tfRegister[IDE_SELECT_OFFSET]); - printk("TF.7=x%02x\n", args->tfRegister[IDE_COMMAND_OFFSET]); - printk(KERN_INFO "%s: ", drive->name); -// printk("HTF.0=x%02x ", args->hobRegister[IDE_DATA_OFFSET_HOB]); - printk("HTF.1=x%02x ", args->hobRegister[IDE_FEATURE_OFFSET_HOB]); - printk("HTF.2=x%02x ", args->hobRegister[IDE_NSECTOR_OFFSET_HOB]); - printk("HTF.3=x%02x ", args->hobRegister[IDE_SECTOR_OFFSET_HOB]); - printk("HTF.4=x%02x ", args->hobRegister[IDE_LCYL_OFFSET_HOB]); - printk("HTF.5=x%02x ", args->hobRegister[IDE_HCYL_OFFSET_HOB]); - printk("HTF.6=x%02x ", args->hobRegister[IDE_SELECT_OFFSET_HOB]); - printk("HTF.7=x%02x\n", args->hobRegister[IDE_CONTROL_OFFSET_HOB]); - } -#endif /* CONFIG_IDE_TASK_IOCTL_DEBUG */ + if (!(args)) + goto args_error; -// if (args->tf_out_flags.all == 0) { - do_taskfile(drive, + do_taskfile(drive, (struct hd_drive_task_hdr *)&args->tfRegister, (struct hd_drive_hob_hdr *)&args->hobRegister, args->handler); -// } else { -// return flagged_taskfile(drive, args); -// } if (((args->command_type == IDE_DRIVE_TASK_RAW_WRITE) || (args->command_type == IDE_DRIVE_TASK_OUT)) && @@ -1169,8 +1329,8 @@ block = rq->sector; /* Strange disk manager remap */ - if ((rq->flags & REQ_CMD) && - (drive->media == ide_disk || drive->media == ide_floppy)) { + if ((rq->flags & REQ_CMD) && + (drive->type == ATA_DISK || drive->type == ATA_FLOPPY)) { block += drive->sect0; } /* Yecch - this will shift the entire interval, @@ -1185,24 +1345,29 @@ SELECT_DRIVE(hwif, drive); if (ide_wait_stat(&startstop, drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) { - printk("%s: drive not ready for command\n", drive->name); + printk(KERN_WARNING "%s: drive not ready for command\n", drive->name); return startstop; } if (!drive->special.all) { if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK | REQ_DRIVE_TASKFILE)) return execute_drive_cmd(drive, rq); - if (drive->driver != NULL) { - return (DRIVER(drive)->do_request(drive, rq, block)); + if (ata_ops(drive)) { + if (ata_ops(drive)->do_request) + return ata_ops(drive)->do_request(drive, rq, block); + else { + ide_end_request(drive, 0); + return ide_stopped; + } } - printk("%s: media type %d not supported\n", - drive->name, drive->media); + printk(KERN_WARNING "%s: device type %d not supported\n", + drive->name, drive->type); goto kill_rq; } - return do_special(drive); + return ata_special(drive); kill_rq: - if (drive->driver != NULL) - DRIVER(drive)->end_request(drive, 0); + if (ata_ops(drive) && ata_ops(drive)->end_request) + ata_ops(drive)->end_request(drive, 0); else ide_end_request(drive, 0); return ide_stopped; @@ -1243,7 +1408,7 @@ { ide_drive_t *drive, *best; -repeat: +repeat: best = NULL; drive = hwgroup->drive; do { @@ -1270,7 +1435,7 @@ && 0 < (signed long)(WAKEUP(drive) - (jiffies - best->service_time)) && 0 < (signed long)((jiffies + t) - WAKEUP(drive))) { - ide_stall_queue(best, IDE_MIN(t, 10 * WAIT_MIN_SLEEP)); + ide_stall_queue(best, min(t, 10L * WAIT_MIN_SLEEP)); goto repeat; } } while ((drive = drive->next) != best); @@ -1309,7 +1474,7 @@ * will start the next request from the queue. If no more work remains, * the driver will clear the hwgroup->flags IDE_BUSY flag and exit. */ -static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) +static void ide_do_request(ide_hwgroup_t *hwgroup, int masked_irq) { ide_drive_t *drive; ide_hwif_t *hwif; @@ -1356,7 +1521,11 @@ hwif = HWIF(drive); if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif && hwif->io_ports[IDE_CONTROL_OFFSET]) { /* set nIEN for previous hwif */ - SELECT_INTERRUPT(hwif, drive); + + if (hwif->intrproc) + hwif->intrproc(drive); + else + OUT_BYTE((drive)->ctl|2, hwif->io_ports[IDE_CONTROL_OFFSET]); } hwgroup->hwif = hwif; hwgroup->drive = drive; @@ -1393,7 +1562,7 @@ } /* - * ide_get_queue() returns the queue which corresponds to a given device. + * Returns the queue which corresponds to a given device. */ request_queue_t *ide_get_queue (kdev_t dev) { @@ -1412,7 +1581,7 @@ /* * un-busy the hwgroup etc, and clear any pending DMA status. we want to - * retry the current request in pio mode instead of risking tossing it + * retry the current request in PIO mode instead of risking tossing it * all away */ void ide_dma_timeout_retry(ide_drive_t *drive) @@ -1423,13 +1592,13 @@ /* * end current dma transaction */ - (void) hwif->dmaproc(ide_dma_end, drive); + hwif->dmaproc(ide_dma_end, drive); /* * complain a little, later we might remove some of this verbosity */ printk("%s: timeout waiting for DMA\n", drive->name); - (void) hwif->dmaproc(ide_dma_timeout, drive); + hwif->dmaproc(ide_dma_timeout, drive); /* * disable dma for now, but remember that we did so because of @@ -1438,7 +1607,7 @@ */ drive->retry_pio++; drive->state = DMA_PIO_RETRY; - (void) hwif->dmaproc(ide_dma_off_quietly, drive); + hwif->dmaproc(ide_dma_off_quietly, drive); /* * un-busy drive etc (hwgroup->busy is cleared on return) and @@ -1465,7 +1634,7 @@ ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data; ide_handler_t *handler; ide_expiry_t *expiry; - unsigned long flags; + unsigned long flags; unsigned long wait; /* @@ -1566,7 +1735,7 @@ * drive is ready to accept one, in which case we know the drive is not * trying to interrupt us. And ide_set_handler() is always invoked before * completing the issuance of any new drive command, so we will not be - * accidently invoked as a result of any valid command completion interrupt. + * accidentally invoked as a result of any valid command completion interrupt. * */ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) @@ -1626,8 +1795,8 @@ * so in that case we just ignore it and hope it goes away. */ #ifdef CONFIG_BLK_DEV_IDEPCI - if (IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL)) -#endif /* CONFIG_BLK_DEV_IDEPCI */ + if (hwif->pci_dev && !hwif->pci_dev->vendor) +#endif { /* * Probably not a shared PCI interrupt, @@ -1639,7 +1808,7 @@ /* * Whack the status register, just in case we have a leftover pending IRQ. */ - (void) IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); + IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); #endif /* CONFIG_BLK_DEV_IDEPCI */ } goto out_lock; @@ -1791,16 +1960,6 @@ } -/* Common for ide-floppy.c and ide-disk.c */ -void ide_revalidate_drive (ide_drive_t *drive) -{ - struct gendisk *g = HWIF(drive)->gd; - int minor = (drive->select.b.unit << g->minor_shift); - kdev_t dev = mk_kdev(g->major, minor); - - grok_partitions(dev, current_capacity(drive)); -} - /* * This routine is called to flush all partitions and partition tables * for a changed disk, and then re-read the new partition table. @@ -1829,24 +1988,32 @@ spin_unlock_irqrestore(&ide_lock, flags); res = wipe_partitions(i_rdev); - if (res) - goto leave; - - if (DRIVER(drive)->revalidate) - DRIVER(drive)->revalidate(drive); + if (!res) { + if (ata_ops(drive) && ata_ops(drive)->revalidate) { + ata_get(ata_ops(drive)); + /* this is a no-op for tapes and SCSI based access */ + ata_ops(drive)->revalidate(drive); + ata_put(ata_ops(drive)); + } else + grok_partitions(i_rdev, ata_capacity(drive)); + } - leave: drive->busy = 0; wake_up(&drive->wqueue); MOD_DEC_USE_COUNT; return res; } -static void revalidate_drives (void) +/* + * Look again for all drives in the system on all interfaces. This is used + * after a new driver category has been loaded as module. + */ +void revalidate_drives(void) { ide_hwif_t *hwif; ide_drive_t *drive; - int index, unit; + int index; + int unit; for (index = 0; index < MAX_HWIFS; ++index) { hwif = &ide_hwifs[index]; @@ -1855,13 +2022,13 @@ if (drive->revalidate) { drive->revalidate = 0; if (!initializing) - (void) ide_revalidate_disk(mk_kdev(hwif->major, unit<major, unit<next) - d->driver_init(); revalidate_drives(); } -static int ide_open (struct inode * inode, struct file * filp) +static int ide_open(struct inode * inode, struct file * filp) { ide_drive_t *drive; @@ -1891,28 +2055,52 @@ return -ENXIO; if (drive->driver == NULL) ide_driver_module(); + + /* Request a particular device type module. + * + * FIXME: The function which should rather requests the drivers is + * ide_driver_module(), since it seems illogical and even a bit + * dangerous to delay this until open time! + */ + #ifdef CONFIG_KMOD if (drive->driver == NULL) { - if (drive->media == ide_disk) - (void) request_module("ide-disk"); - if (drive->media == ide_cdrom) - (void) request_module("ide-cd"); - if (drive->media == ide_tape) - (void) request_module("ide-tape"); - if (drive->media == ide_floppy) - (void) request_module("ide-floppy"); -#if defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) - if (drive->media == ide_scsi) - (void) request_module("ide-scsi"); -#endif /* defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) */ + char *module = NULL; + + switch (drive->type) { + case ATA_DISK: + module = "ide-disk"; + break; + case ATA_ROM: + module = "ide-cd"; + break; + case ATA_TAPE: + module = "ide-tape"; + break; + case ATA_FLOPPY: + module = "ide-floppy"; + break; + case ATA_SCSI: + module = "ide-scsi"; + break; + default: + /* nothing we can do about it */ ; + } + if (module) + request_module(module); } -#endif /* CONFIG_KMOD */ +#endif while (drive->busy) sleep_on(&drive->wqueue); - drive->usage++; - if (drive->driver != NULL) - return DRIVER(drive)->open(inode, filp, drive); - printk ("%s: driver not present\n", drive->name); + ++drive->usage; + if (ata_ops(drive) && ata_ops(drive)->open) + return ata_ops(drive)->open(inode, filp, drive); + else { + --drive->usage; + return -ENODEV; + } + + printk(KERN_INFO "%s: driver not present\n", drive->name); drive->usage--; return -ENXIO; } @@ -1921,32 +2109,17 @@ * Releasing a block device means we sync() it, so that it can safely * be forgotten about... */ -static int ide_release (struct inode * inode, struct file * file) +static int ide_release(struct inode * inode, struct file * file) { ide_drive_t *drive; - if ((drive = get_info_ptr(inode->i_rdev)) != NULL) { - drive->usage--; - if (drive->driver != NULL) - DRIVER(drive)->release(inode, file, drive); - } - return 0; -} - -int ide_replace_subdriver (ide_drive_t *drive, const char *driver) -{ - if (!drive->present || drive->busy || drive->usage) - goto abort; - if (drive->driver != NULL && DRIVER(drive)->cleanup(drive)) - goto abort; - strncpy(drive->driver_req, driver, 9); - ide_driver_module(); - drive->driver_req[0] = 0; - ide_driver_module(); - if (DRIVER(drive) && !strcmp(DRIVER(drive)->name, driver)) + if (!(drive = get_info_ptr(inode->i_rdev))) return 0; -abort: - return 1; + + drive->usage--; + if (ata_ops(drive) && ata_ops(drive)->release) + ata_ops(drive)->release(inode, file, drive); + return 0; } #ifdef CONFIG_PROC_FS @@ -1988,7 +2161,7 @@ #if defined(CONFIG_AMIGA) || defined(CONFIG_MAC) if (hwif->io_ports[IDE_IRQ_OFFSET]) ide_release_region(hwif->io_ports[IDE_IRQ_OFFSET], 1); -#endif /* (CONFIG_AMIGA) || (CONFIG_MAC) */ +#endif } void ide_unregister (unsigned int index) @@ -2009,17 +2182,23 @@ hwif = &ide_hwifs[index]; if (!hwif->present) goto abort; + put_device(&hwif->device); for (unit = 0; unit < MAX_DRIVES; ++unit) { drive = &hwif->drives[unit]; if (!drive->present) continue; if (drive->busy || drive->usage) goto abort; - if (drive->driver != NULL && DRIVER(drive)->cleanup(drive)) - goto abort; + if (ata_ops(drive)) { + if (ata_ops(drive)->cleanup) { + if (ata_ops(drive)->cleanup(drive)) + goto abort; + } else + ide_unregister_subdriver(drive); + } } hwif->present = 0; - + /* * All clear? Then blow away the buffer cache */ @@ -2119,7 +2298,7 @@ hwif->gd = NULL; } old_hwif = *hwif; - init_hwif_data (index); /* restore hwif data to pristine status */ + init_hwif_data(index); /* restore hwif data to pristine status */ hwif->hwgroup = old_hwif.hwgroup; hwif->tuneproc = old_hwif.tuneproc; hwif->speedproc = old_hwif.speedproc; @@ -2140,17 +2319,15 @@ hwif->proc = old_hwif.proc; #ifndef CONFIG_BLK_DEV_IDECS hwif->irq = old_hwif.irq; -#endif /* CONFIG_BLK_DEV_IDECS */ +#endif hwif->major = old_hwif.major; hwif->chipset = old_hwif.chipset; hwif->autodma = old_hwif.autodma; hwif->udma_four = old_hwif.udma_four; #ifdef CONFIG_BLK_DEV_IDEPCI hwif->pci_dev = old_hwif.pci_dev; - hwif->pci_devid = old_hwif.pci_devid; -#endif /* CONFIG_BLK_DEV_IDEPCI */ +#endif hwif->straight8 = old_hwif.straight8; - hwif->hwif_data = old_hwif.hwif_data; abort: restore_flags(flags); /* all CPUs */ } @@ -2195,7 +2372,7 @@ * Register an IDE interface, specifing exactly the registers etc * Set init=1 iff calling before probes have taken place. */ -int ide_register_hw (hw_regs_t *hw, ide_hwif_t **hwifp) +int ide_register_hw(hw_regs_t *hw, ide_hwif_t **hwifp) { int index, retry = 1; ide_hwif_t *hwif; @@ -2245,7 +2422,7 @@ * Compatability function with existing drivers. If you want * something different, use the function above. */ -int ide_register (int arg1, int arg2, int irq) +int ide_register(int arg1, int arg2, int irq) { hw_regs_t hw; ide_init_hwif_ports(&hw, (ide_ioreg_t) arg1, (ide_ioreg_t) arg2, NULL); @@ -2356,7 +2533,7 @@ /* * FIXME: This should be changed to enqueue a special request - * to the driver to change settings, and then wait on a sema for completion. + * to the driver to change settings, and then wait on a semaphore for completion. * The current scheme of polling is kludgey, though safe enough. */ int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val) @@ -2406,7 +2583,7 @@ static int set_using_dma (ide_drive_t *drive, int arg) { - if (!drive->driver || !DRIVER(drive)->supports_dma) + if (!drive->driver) return -EPERM; if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc) return -EPERM; @@ -2448,33 +2625,6 @@ ide_add_setting(drive, "number", SETTING_RW, -1, -1, TYPE_BYTE, 0, 3, 1, 1, &drive->dn, NULL); } -int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf) -{ - struct request rq; - byte buffer[4]; - - if (!buf) - buf = buffer; - memset(buf, 0, 4 + SECTOR_WORDS * 4 * sectors); - ide_init_drive_cmd(&rq); - rq.buffer = buf; - *buf++ = cmd; - *buf++ = nsect; - *buf++ = feature; - *buf++ = sectors; - return ide_do_drive_cmd(drive, &rq, ide_wait); -} - -int ide_wait_cmd_task (ide_drive_t *drive, byte *buf) -{ - struct request rq; - - ide_init_drive_cmd(&rq); - rq.flags = REQ_DRIVE_TASK; - rq.buffer = buf; - return ide_do_drive_cmd(drive, &rq, ide_wait); -} - /* * Delay for *at least* 50ms. As we don't know how much time is left * until the next tick occurs, we wait an extra tick to be safe. @@ -2493,66 +2643,6 @@ #endif /* CONFIG_BLK_DEV_IDECS */ } -int system_bus_clock (void) -{ - return((int) ((!system_bus_speed) ? ide_system_bus_speed() : system_bus_speed )); -} - -int ide_reinit_drive (ide_drive_t *drive) -{ - switch (drive->media) { -#ifdef CONFIG_BLK_DEV_IDECD - case ide_cdrom: - { - extern int ide_cdrom_reinit(ide_drive_t *drive); - if (ide_cdrom_reinit(drive)) - return 1; - break; - } -#endif /* CONFIG_BLK_DEV_IDECD */ -#ifdef CONFIG_BLK_DEV_IDEDISK - case ide_disk: - { - extern int idedisk_reinit(ide_drive_t *drive); - if (idedisk_reinit(drive)) - return 1; - break; - } -#endif /* CONFIG_BLK_DEV_IDEDISK */ -#ifdef CONFIG_BLK_DEV_IDEFLOPPY - case ide_floppy: - { - extern int idefloppy_reinit(ide_drive_t *drive); - if (idefloppy_reinit(drive)) - return 1; - break; - } -#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ -#ifdef CONFIG_BLK_DEV_IDETAPE - case ide_tape: - { - extern int idetape_reinit(ide_drive_t *drive); - if (idetape_reinit(drive)) - return 1; - break; - } -#endif /* CONFIG_BLK_DEV_IDETAPE */ -#ifdef CONFIG_BLK_DEV_IDESCSI -/* - * { - * extern int idescsi_reinit(ide_drive_t *drive); - * if (idescsi_reinit(drive)) - * return 1; - * break; - * } - */ -#endif /* CONFIG_BLK_DEV_IDESCSI */ - default: - return 1; - } - return 0; -} - static int ide_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { @@ -2594,7 +2684,9 @@ { struct hd_geometry *loc = (struct hd_geometry *) arg; unsigned short bios_cyl = drive->bios_cyl; /* truncate */ - if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL; + + if (!loc || (drive->type != ATA_DISK && drive->type != ATA_FLOPPY)) + return -EINVAL; if (put_user(drive->bios_head, (byte *) &loc->heads)) return -EFAULT; if (put_user(drive->bios_sect, (byte *) &loc->sectors)) return -EFAULT; if (put_user(bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT; @@ -2606,7 +2698,9 @@ case HDIO_GETGEO_BIG: { struct hd_big_geometry *loc = (struct hd_big_geometry *) arg; - if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL; + + if (!loc || (drive->type != ATA_DISK && drive->type != ATA_FLOPPY)) + return -EINVAL; if (put_user(drive->bios_head, (byte *) &loc->heads)) return -EFAULT; if (put_user(drive->bios_sect, (byte *) &loc->sectors)) return -EFAULT; @@ -2619,7 +2713,8 @@ case HDIO_GETGEO_BIG_RAW: { struct hd_big_geometry *loc = (struct hd_big_geometry *) arg; - if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL; + if (!loc || (drive->type != ATA_DISK && drive->type != ATA_FLOPPY)) + return -EINVAL; if (put_user(drive->head, (byte *) &loc->heads)) return -EFAULT; if (put_user(drive->sect, (byte *) &loc->sectors)) return -EFAULT; if (put_user(drive->cyl, (unsigned int *) &loc->cylinders)) return -EFAULT; @@ -2629,7 +2724,8 @@ } case BLKRRPART: /* Re-read partition tables */ - if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; return ide_revalidate_disk(inode->i_rdev); case HDIO_OBSOLETE_IDENTITY: @@ -2650,24 +2746,6 @@ drive->nice2 << IDE_NICE_2, (long *) arg); -#ifdef CONFIG_IDE_TASK_IOCTL - case HDIO_DRIVE_TASKFILE: - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) - return -EACCES; - switch(drive->media) { - case ide_disk: - return ide_taskfile_ioctl(drive, inode, file, cmd, arg); -#ifdef CONFIG_PKT_TASK_IOCTL - case ide_cdrom: - case ide_tape: - case ide_floppy: - return pkt_taskfile_ioctl(drive, inode, file, cmd, arg); -#endif /* CONFIG_PKT_TASK_IOCTL */ - default: - return -ENOMSG; - } -#endif /* CONFIG_IDE_TASK_IOCTL */ - case HDIO_DRIVE_CMD: if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) return -EACCES; @@ -2695,12 +2773,11 @@ return 0; case HDIO_SET_NICE: if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (drive->driver == NULL) - return -EPERM; if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1)))) return -EPERM; drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1; - if (drive->dsc_overlap && !DRIVER(drive)->supports_dsc_overlap) { + /* Only CD-ROM's and tapes support DSC overlap. */ + if (drive->dsc_overlap && !(drive->type == ATA_ROM || drive->type == ATA_TAPE)) { drive->dsc_overlap = 0; return -EPERM; } @@ -2716,28 +2793,15 @@ spin_lock_irqsave(&ide_lock, flags); if (hwgroup->handler != NULL) { printk("%s: ide_set_handler: handler not null; %p\n", drive->name, hwgroup->handler); - (void) hwgroup->handler(drive); -// hwgroup->handler = NULL; -// hwgroup->expiry = NULL; + hwgroup->handler(drive); hwgroup->timer.expires = jiffies + 0;; del_timer(&hwgroup->timer); } spin_unlock_irqrestore(&ide_lock, flags); - #endif - (void) ide_do_reset(drive); - if (drive->suspend_reset) { -/* - * APM WAKE UP todo !! - * int nogoodpower = 1; - * while(nogoodpower) { - * check_power1() or check_power2() - * nogoodpower = 0; - * } - * HWIF(drive)->multiproc(drive); - */ + ide_do_reset(drive); + if (drive->suspend_reset) return ide_revalidate_disk(inode->i_rdev); - } return 0; } case BLKGETSIZE: @@ -2751,7 +2815,7 @@ case BLKELVSET: case BLKBSZGET: case BLKBSZSET: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); /* * uniform packet command handling @@ -2775,9 +2839,9 @@ return 0; default: - if (drive->driver != NULL) - return DRIVER(drive)->ioctl(drive, inode, file, cmd, arg); - return -EPERM; + if (ata_ops(drive) && ata_ops(drive)->ioctl) + return ata_ops(drive)->ioctl(drive, inode, file, cmd, arg); + return -EINVAL; } } @@ -2787,8 +2851,14 @@ if ((drive = get_info_ptr(i_rdev)) == NULL) return -ENODEV; - if (drive->driver != NULL) - return DRIVER(drive)->media_change(drive); + if (ata_ops(drive)) { + ata_get(ata_ops(drive)); + if (ata_ops(drive)->check_media_change) + return ata_ops(drive)->check_media_change(drive); + else + return 1; /* assume it was changed */ + ata_put(ata_ops(drive)); + } return 0; } @@ -2884,9 +2954,11 @@ } /* - * ide_setup() gets called VERY EARLY during initialization, - * to handle kernel "command line" strings beginning with "hdx=" - * or "ide". Here is the complete set currently supported: + * This gets called VERY EARLY during initialization, to handle kernel "command + * line" strings beginning with "hdx=" or "ide".It gets called even before the + * actual module gets initialized. + * + * Here is the complete set currently supported comand line options: * * "hdx=" is recognized for all "x" from "a" to "h", such as "hdc". * "idex=" is recognized for all "x" from "0" to "3", such as "ide1". @@ -2926,7 +2998,7 @@ * As for VLB, it is safest to not specify it. * * "idex=noprobe" : do not attempt to access/use this interface - * "idex=base" : probe for an interface at the addr specified, + * "idex=base" : probe for an interface at the address specified, * where "base" is usually 0x1f0 or 0x170 * and "ctl" is assumed to be "base"+0x206 * "idex=base,ctl" : specify both base and ctl @@ -2972,9 +3044,8 @@ const char max_drive = 'a' + ((MAX_HWIFS * MAX_DRIVES) - 1); const char max_hwif = '0' + (MAX_HWIFS - 1); - - if (strncmp(s,"hd",2) == 0 && s[2] == '=') /* hd= is for hd.c */ - return 0; /* driver and not us */ + if (!strncmp(s, "hd=", 3)) /* hd= is for hd.c driver and not us */ + return 0; if (strncmp(s,"ide",3) && strncmp(s,"idebus",6) && @@ -2992,7 +3063,7 @@ ide_doubler = 1; return 1; } -#endif /* CONFIG_BLK_DEV_IDEDOUBLER */ +#endif if (!strcmp(s, "ide=nodma")) { printk("IDE: Prevented DMA\n"); @@ -3050,7 +3121,7 @@ goto done; case -4: /* "cdrom" */ drive->present = 1; - drive->media = ide_cdrom; + drive->type = ATA_ROM; hwif->noprobe = 0; goto done; case -5: /* "serialize" */ @@ -3087,7 +3158,7 @@ goto bad_option; #endif /* defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) */ case 3: /* cyl,head,sect */ - drive->media = ide_disk; + drive->type = ATA_DISK; drive->cyl = drive->bios_cyl = vals[0]; drive->head = drive->bios_head = vals[1]; drive->sect = drive->bios_sect = vals[2]; @@ -3124,8 +3195,8 @@ */ const char *ide_words[] = { "noprobe", "serialize", "autotune", "noautotune", "reset", "dma", "ata66", - "minus8", "minus9", "minus10", - "four", "qd65xx", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", NULL }; + "minus8", "minus9", "minus10", "minus11", + "qd65xx", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", NULL }; hw = s[3] - '0'; hwif = &ide_hwifs[hw]; i = match_parm(&s[4], ide_words, vals, 3); @@ -3200,18 +3271,7 @@ goto done; } #endif /* CONFIG_BLK_DEV_QD65XX */ -#ifdef CONFIG_BLK_DEV_4DRIVES - case -11: /* "four" drives on one set of ports */ - { - ide_hwif_t *mate = &ide_hwifs[hw^1]; - mate->drives[0].select.all ^= 0x20; - mate->drives[1].select.all ^= 0x20; - hwif->chipset = mate->chipset = ide_4drives; - mate->irq = hwif->irq; - memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports)); - goto do_serialize; - } -#endif /* CONFIG_BLK_DEV_4DRIVES */ + case -11: /* minus11 */ case -10: /* minus10 */ case -9: /* minus9 */ case -8: /* minus8 */ @@ -3278,229 +3338,16 @@ return 1; } -/* - * probe_for_hwifs() finds/initializes "known" IDE interfaces - */ -static void __init probe_for_hwifs (void) -{ -#ifdef CONFIG_PCI - if (pci_present()) - { -#ifdef CONFIG_BLK_DEV_IDEPCI - ide_scan_pcibus(ide_scan_direction); -#else -#ifdef CONFIG_BLK_DEV_RZ1000 - { - extern void ide_probe_for_rz100x(void); - ide_probe_for_rz100x(); - } -#endif /* CONFIG_BLK_DEV_RZ1000 */ -#endif /* CONFIG_BLK_DEV_IDEPCI */ - } -#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); - ide_probe_for_cmd640x(); - } -#endif /* CONFIG_BLK_DEV_CMD640 */ -#ifdef CONFIG_BLK_DEV_PDC4030 - { - extern int ide_probe_for_pdc4030(void); - (void) ide_probe_for_pdc4030(); - } -#endif /* CONFIG_BLK_DEV_PDC4030 */ -#ifdef CONFIG_BLK_DEV_IDE_PMAC - { - extern void pmac_ide_probe(void); - pmac_ide_probe(); - } -#endif /* CONFIG_BLK_DEV_IDE_PMAC */ -#ifdef CONFIG_BLK_DEV_IDE_ICSIDE - { - extern void icside_init(void); - icside_init(); - } -#endif /* CONFIG_BLK_DEV_IDE_ICSIDE */ -#ifdef CONFIG_BLK_DEV_IDE_RAPIDE - { - extern void rapide_init(void); - rapide_init(); - } -#endif /* CONFIG_BLK_DEV_IDE_RAPIDE */ -#ifdef CONFIG_BLK_DEV_GAYLE - { - extern void gayle_init(void); - gayle_init(); - } -#endif /* CONFIG_BLK_DEV_GAYLE */ -#ifdef CONFIG_BLK_DEV_FALCON_IDE - { - extern void falconide_init(void); - falconide_init(); - } -#endif /* CONFIG_BLK_DEV_FALCON_IDE */ -#ifdef CONFIG_BLK_DEV_MAC_IDE - { - extern void macide_init(void); - macide_init(); - } -#endif /* CONFIG_BLK_DEV_MAC_IDE */ -#ifdef CONFIG_BLK_DEV_Q40IDE - { - extern void q40ide_init(void); - q40ide_init(); - } -#endif /* CONFIG_BLK_DEV_Q40IDE */ -#ifdef CONFIG_BLK_DEV_BUDDHA - { - extern void buddha_init(void); - buddha_init(); - } -#endif /* CONFIG_BLK_DEV_BUDDHA */ -#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP) - { - extern void pnpide_init(int enable); - pnpide_init(1); - } -#endif /* CONFIG_BLK_DEV_ISAPNP */ -} - -void __init ide_init_builtin_drivers (void) -{ - /* - * Probe for special PCI and other "known" interface chipsets - */ - probe_for_hwifs (); - -#ifdef CONFIG_BLK_DEV_IDE -#if defined(__mc68000__) || defined(CONFIG_APUS) - if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) { - ide_get_lock(&ide_intr_lock, NULL, NULL);/* for atari only */ - disable_irq(ide_hwifs[0].irq); /* disable_irq_nosync ?? */ -// disable_irq_nosync(ide_hwifs[0].irq); - } -#endif /* __mc68000__ || CONFIG_APUS */ - - (void) ideprobe_init(); - -#if defined(__mc68000__) || defined(CONFIG_APUS) - if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) { - enable_irq(ide_hwifs[0].irq); - ide_release_lock(&ide_intr_lock);/* for atari only */ - } -#endif /* __mc68000__ || CONFIG_APUS */ -#endif /* CONFIG_BLK_DEV_IDE */ - -#ifdef CONFIG_PROC_FS - proc_ide_create(); -#endif - - /* - * Attempt to match drivers for the available drives - */ -#ifdef CONFIG_BLK_DEV_IDEDISK - (void) idedisk_init(); -#endif /* CONFIG_BLK_DEV_IDEDISK */ -#ifdef CONFIG_BLK_DEV_IDECD - (void) ide_cdrom_init(); -#endif /* CONFIG_BLK_DEV_IDECD */ -#ifdef CONFIG_BLK_DEV_IDETAPE - (void) idetape_init(); -#endif /* CONFIG_BLK_DEV_IDETAPE */ -#ifdef CONFIG_BLK_DEV_IDEFLOPPY - (void) idefloppy_init(); -#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ -#ifdef CONFIG_BLK_DEV_IDESCSI - #ifdef CONFIG_SCSI - (void) idescsi_init(); - #else - #warning ide scsi-emulation selected but no SCSI-subsystem in kernel - #endif -#endif /* CONFIG_BLK_DEV_IDESCSI */ -} - -static int default_cleanup (ide_drive_t *drive) -{ - return ide_unregister_subdriver(drive); -} - -static int default_standby (ide_drive_t *drive) -{ - return 0; -} - -static int default_flushcache (ide_drive_t *drive) -{ - return 0; -} - -static ide_startstop_t default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block) -{ - ide_end_request(drive, 0); - return ide_stopped; -} - /* This is the default end request function as well */ int ide_end_request(ide_drive_t *drive, int uptodate) { return __ide_end_request(drive, uptodate, 0); } -static int default_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return -EIO; -} - -static int default_open (struct inode *inode, struct file *filp, ide_drive_t *drive) -{ - drive->usage--; - return -EIO; -} - -static void default_release (struct inode *inode, struct file *filp, ide_drive_t *drive) -{ -} - -static int default_check_media_change (ide_drive_t *drive) -{ - return 1; -} - -static void default_pre_reset (ide_drive_t *drive) -{ -} - -static unsigned long default_capacity (ide_drive_t *drive) -{ - return 0x7fffffff; -} - -static ide_startstop_t default_special (ide_drive_t *drive) -{ - special_t *s = &drive->special; - - s->all = 0; - drive->mult_req = 0; - return ide_stopped; -} - -static int default_driver_reinit (ide_drive_t *drive) -{ - printk(KERN_ERR "%s: does not support hotswap of device class !\n", drive->name); - - return 0; -} - -ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n) +/* + * Lookup IDE devices, which requested a particular driver + */ +ide_drive_t *ide_scan_devices(byte type, const char *name, struct ata_operations *driver, int n) { unsigned int unit, index, i; @@ -3513,14 +3360,17 @@ char *req = drive->driver_req; if (*req && !strstr(name, req)) continue; - if (drive->present && drive->media == media && drive->driver == driver && ++i > n) + if (drive->present && drive->type == type && drive->driver == driver && ++i > n) return drive; } } return NULL; } -int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver) +/* + * This is in fact registering a drive not a driver. + */ +int ide_register_subdriver(ide_drive_t *drive, struct ata_operations *driver) { unsigned long flags; @@ -3531,77 +3381,64 @@ return 1; } - drive->driver = driver; - - /* Fill in the default handlers + /* FIXME: This will be pushed to the drivers! Thus allowing us to + * save one parameter here eparate this out. */ - if (driver->cleanup == NULL) - driver->cleanup = default_cleanup; - if (driver->standby == NULL) - driver->standby = default_standby; - if (driver->flushcache == NULL) - driver->flushcache = default_flushcache; - if (driver->do_request == NULL) - driver->do_request = default_do_request; - if (driver->end_request == NULL) - driver->end_request = ide_end_request; - if (driver->ioctl == NULL) - driver->ioctl = default_ioctl; - if (driver->open == NULL) - driver->open = default_open; - if (driver->release == NULL) - driver->release = default_release; - if (driver->media_change == NULL) - driver->media_change = default_check_media_change; - if (driver->pre_reset == NULL) - driver->pre_reset = default_pre_reset; - if (driver->capacity == NULL) - driver->capacity = default_capacity; - if (driver->special == NULL) - driver->special = default_special; - if (driver->driver_reinit == NULL) - driver->driver_reinit = default_driver_reinit; + drive->driver = driver; restore_flags(flags); /* all CPUs */ + /* FIXME: Check what this magic number is supposed to be about? */ if (drive->autotune != 2) { - if (driver->supports_dma && HWIF(drive)->dmaproc != NULL) { + if (HWIF(drive)->dmaproc != NULL) { + /* - * Force DMAing for the beginning of the check. - * Some chipsets appear to do interesting things, - * if not checked and cleared. + * Force DMAing for the beginning of the check. Some + * chipsets appear to do interesting things, if not + * checked and cleared. + * * PARANOIA!!! */ - (void) (HWIF(drive)->dmaproc(ide_dma_off_quietly, drive)); - (void) (HWIF(drive)->dmaproc(ide_dma_check, drive)); + + HWIF(drive)->dmaproc(ide_dma_off_quietly, drive); + HWIF(drive)->dmaproc(ide_dma_check, drive); } - drive->dsc_overlap = (drive->next != drive && driver->supports_dsc_overlap); + /* Only CD-ROMs and tape drives support DSC overlap. */ + drive->dsc_overlap = (drive->next != drive + && (drive->type == ATA_ROM || drive->type == ATA_TAPE)); drive->nice1 = 1; } drive->revalidate = 1; drive->suspend_reset = 0; #ifdef CONFIG_PROC_FS ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive); - ide_add_proc_entries(drive->proc, driver->proc, drive); + if (ata_ops(drive)) + ide_add_proc_entries(drive->proc, ata_ops(drive)->proc, drive); #endif return 0; } -int ide_unregister_subdriver (ide_drive_t *drive) +/* + * This is in fact the default cleanup routine. + * + * FIXME: Check whatever we maybe don't call it twice!. + */ +int ide_unregister_subdriver(ide_drive_t *drive) { unsigned long flags; save_flags(flags); /* all CPUs */ cli(); /* all CPUs */ - if (drive->usage || drive->busy || drive->driver == NULL || DRIVER(drive)->busy) { + if (drive->usage || drive->busy || !ata_ops(drive) || ata_ops(drive)->busy) { restore_flags(flags); /* all CPUs */ return 1; } #if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP) && defined(MODULE) pnpide_init(0); -#endif /* CONFIG_BLK_DEV_ISAPNP */ +#endif #ifdef CONFIG_PROC_FS - ide_remove_proc_entries(drive->proc, DRIVER(drive)->proc); + if (ata_ops(drive)) + ide_remove_proc_entries(drive->proc, ata_ops(drive)->proc); ide_remove_proc_entries(drive->proc, generic_subdriver_entries); #endif auto_remove_settings(drive); @@ -3610,30 +3447,29 @@ return 0; } -int ide_register_module (struct ide_driver_s *d) -{ - struct ide_driver_s *p = ide_drivers; - while (p) { - if (p == d) - return 1; - p = p->next; - } - d->next = ide_drivers; - ide_drivers = d; - revalidate_drives(); +/* + * Register an ATA driver for a particular device type. + */ + +int register_ata_driver(unsigned int type, struct ata_operations *driver) +{ return 0; } -void ide_unregister_module (struct ide_driver_s *d) -{ - struct ide_driver_s **p; +EXPORT_SYMBOL(register_ata_driver); + +/* + * Unregister an ATA driver for a particular device type. + */ - for (p = &ide_drivers; (*p) && (*p) != d; p = &((*p)->next)); - if (*p) - *p = (*p)->next; +int unregister_ata_driver(unsigned int type, struct ata_operations *driver) +{ + return 0; } +EXPORT_SYMBOL(unregister_ata_driver); + struct block_device_operations ide_fops[] = {{ owner: THIS_MODULE, open: ide_open, @@ -3644,9 +3480,8 @@ }}; EXPORT_SYMBOL(ide_hwifs); -EXPORT_SYMBOL(ide_register_module); -EXPORT_SYMBOL(ide_unregister_module); EXPORT_SYMBOL(ide_spin_wait_hwgroup); +EXPORT_SYMBOL(revalidate_drives); /* * Probe module @@ -3657,10 +3492,8 @@ EXPORT_SYMBOL(drive_is_flashcard); EXPORT_SYMBOL(ide_timer_expiry); EXPORT_SYMBOL(ide_intr); -EXPORT_SYMBOL(ide_fops); EXPORT_SYMBOL(ide_get_queue); EXPORT_SYMBOL(ide_add_generic_settings); -EXPORT_SYMBOL(ide_devfs_handle); EXPORT_SYMBOL(do_ide_request); /* * Driver module @@ -3668,7 +3501,6 @@ EXPORT_SYMBOL(ide_scan_devices); EXPORT_SYMBOL(ide_register_subdriver); EXPORT_SYMBOL(ide_unregister_subdriver); -EXPORT_SYMBOL(ide_replace_subdriver); EXPORT_SYMBOL(ide_set_handler); EXPORT_SYMBOL(ide_dump_status); EXPORT_SYMBOL(ide_error); @@ -3681,20 +3513,14 @@ EXPORT_SYMBOL(ide_end_drive_cmd); EXPORT_SYMBOL(__ide_end_request); EXPORT_SYMBOL(ide_end_request); -EXPORT_SYMBOL(ide_revalidate_drive); EXPORT_SYMBOL(ide_revalidate_disk); EXPORT_SYMBOL(ide_cmd); -EXPORT_SYMBOL(ide_wait_cmd); -EXPORT_SYMBOL(ide_wait_cmd_task); EXPORT_SYMBOL(ide_delay_50ms); EXPORT_SYMBOL(ide_stall_queue); #ifdef CONFIG_PROC_FS EXPORT_SYMBOL(ide_add_proc_entries); EXPORT_SYMBOL(ide_remove_proc_entries); EXPORT_SYMBOL(proc_ide_read_geometry); -EXPORT_SYMBOL(create_proc_ide_interfaces); -EXPORT_SYMBOL(recreate_proc_ide_device); -EXPORT_SYMBOL(destroy_proc_ide_device); #endif EXPORT_SYMBOL(ide_add_setting); EXPORT_SYMBOL(ide_remove_setting); @@ -3704,11 +3530,6 @@ EXPORT_SYMBOL(ide_unregister); EXPORT_SYMBOL(ide_setup_ports); EXPORT_SYMBOL(get_info_ptr); -EXPORT_SYMBOL(current_capacity); - -EXPORT_SYMBOL(system_bus_clock); - -EXPORT_SYMBOL(ide_reinit_drive); static int ide_notify_reboot (struct notifier_block *this, unsigned long event, void *x) { @@ -3738,12 +3559,14 @@ /* set the drive to standby */ printk("%s ", drive->name); - if (event != SYS_RESTART) - if (drive->driver != NULL && DRIVER(drive)->standby(drive)) - continue; + if (ata_ops(drive)) { + if (event != SYS_RESTART) + if (ata_ops(drive)->standby && ata_ops(drive)->standby(drive)) + continue; - if (drive->driver != NULL && DRIVER(drive)->cleanup(drive)) - continue; + if (ata_ops(drive)->cleanup) + ata_ops(drive)->cleanup(drive); + } } } printk("\n"); @@ -3757,24 +3580,138 @@ }; /* - * This is gets invoked once during initialization, to set *everything* up + * This is the global initialization entry point. */ -int __init ide_init (void) +static int __init ata_module_init(void) { - static char banner_printed; int i; - if (!banner_printed) { - printk(KERN_INFO "Uniform Multi-Platform E-IDE driver " REVISION "\n"); - ide_devfs_handle = devfs_mk_dir (NULL, "ide", NULL); - system_bus_speed = ide_system_bus_speed(); - banner_printed = 1; - } + printk(KERN_INFO "Uniform Multi-Platform E-IDE driver ver.:" VERSION "\n"); + + ide_devfs_handle = devfs_mk_dir (NULL, "ide", NULL); + + /* Initialize system bus speed. + * + * This can be changed by a particular chipse initialization module. + * Otherwise we assume 33MHz as a safe value for PCI bus based systems. + * 50MHz will be assumed for abolitions like VESA, since higher values + * result in more conservative timing setups. + * + * The kernel parameter idebus=XX overrides the default settings. + */ + + system_bus_speed = 50; + if (idebus_parameter) + system_bus_speed = idebus_parameter; +#ifdef CONFIG_PCI + else if (pci_present()) + system_bus_speed = 33; +#endif + + printk("ide: system bus speed %dMHz\n", system_bus_speed); init_ide_data (); initializing = 1; - ide_init_builtin_drivers(); + + /* + * Detect and initialize "known" IDE host chip types. + */ +#ifdef CONFIG_PCI + if (pci_present()) { +# ifdef CONFIG_BLK_DEV_IDEPCI + ide_scan_pcibus(ide_scan_direction); +# else +# ifdef CONFIG_BLK_DEV_RZ1000 + ide_probe_for_rz100x(); +# endif +# endif + } +#endif + +#ifdef CONFIG_ETRAX_IDE + init_e100_ide(); +#endif +#ifdef CONFIG_BLK_DEV_CMD640 + ide_probe_for_cmd640x(); +#endif +#ifdef CONFIG_BLK_DEV_PDC4030 + ide_probe_for_pdc4030(); +#endif +#ifdef CONFIG_BLK_DEV_IDE_PMAC + pmac_ide_probe(); +#endif +#ifdef CONFIG_BLK_DEV_IDE_ICSIDE + icside_init(); +#endif +#ifdef CONFIG_BLK_DEV_IDE_RAPIDE + rapide_init(); +#endif +#ifdef CONFIG_BLK_DEV_GAYLE + gayle_init(); +#endif +#ifdef CONFIG_BLK_DEV_FALCON_IDE + falconide_init(); +#endif +#ifdef CONFIG_BLK_DEV_MAC_IDE + macide_init(); +#endif +#ifdef CONFIG_BLK_DEV_Q40IDE + q40ide_init(); +#endif +#ifdef CONFIG_BLK_DEV_BUDDHA + buddha_init(); +#endif +#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP) + pnpide_init(1); +#endif + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULES) +# if defined(__mc68000__) || defined(CONFIG_APUS) + if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) { + ide_get_lock(&ide_intr_lock, NULL, NULL);/* for atari only */ + disable_irq(ide_hwifs[0].irq); /* disable_irq_nosync ?? */ +// disable_irq_nosync(ide_hwifs[0].irq); + } +# endif + + ideprobe_init(); + +# if defined(__mc68000__) || defined(CONFIG_APUS) + if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) { + enable_irq(ide_hwifs[0].irq); + ide_release_lock(&ide_intr_lock);/* for atari only */ + } +# endif +#endif + +#ifdef CONFIG_PROC_FS + proc_ide_create(); +#endif + + /* + * Initialize all device type driver modules. + */ +#ifdef CONFIG_BLK_DEV_IDEDISK + idedisk_init(); +#endif +#ifdef CONFIG_BLK_DEV_IDECD + ide_cdrom_init(); +#endif +#ifdef CONFIG_BLK_DEV_IDETAPE + idetape_init(); +#endif +#ifdef CONFIG_BLK_DEV_IDEFLOPPY + idefloppy_init(); +#endif +#ifdef CONFIG_BLK_DEV_IDESCSI +# ifdef CONFIG_SCSI + idescsi_init(); +# else + #warning ATA SCSI emulation selected but no SCSI-subsystem in kernel +# endif +#endif + initializing = 0; for (i = 0; i < MAX_HWIFS; ++i) { @@ -3787,8 +3724,7 @@ return 0; } -#ifdef MODULE -char *options = NULL; +static char *options = NULL; MODULE_PARM(options,"s"); MODULE_LICENSE("GPL"); @@ -3799,40 +3735,44 @@ if (line == NULL || !*line) return; while ((line = next) != NULL) { - if ((next = strchr(line,' ')) != NULL) + if ((next = strchr(line,' ')) != NULL) *next++ = 0; if (!ide_setup(line)) printk ("Unknown option '%s'\n", line); } } -int init_module (void) +static int __init init_ata (void) { parse_options(options); - return ide_init(); + return ata_module_init(); } -void cleanup_module (void) +static void __exit cleanup_ata (void) { int index; unregister_reboot_notifier(&ide_notifier); for (index = 0; index < MAX_HWIFS; ++index) { ide_unregister(index); -#if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI) +# if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI) if (ide_hwifs[index].dma_base) - (void) ide_release_dma(&ide_hwifs[index]); -#endif /* (CONFIG_BLK_DEV_IDEDMA) && !(CONFIG_DMA_NONPCI) */ + ide_release_dma(&ide_hwifs[index]); +# endif /* (CONFIG_BLK_DEV_IDEDMA) && !(CONFIG_DMA_NONPCI) */ } -#ifdef CONFIG_PROC_FS +# ifdef CONFIG_PROC_FS proc_ide_destroy(); -#endif +# endif devfs_unregister (ide_devfs_handle); } -#else /* !MODULE */ +module_init(init_ata); +module_exit(cleanup_ata); + +#ifndef MODULE +/* command line option parser */ __setup("", ide_setup); -#endif /* MODULE */ +#endif diff -Nru a/drivers/ide/ide_modes.h b/drivers/ide/ide_modes.h --- a/drivers/ide/ide_modes.h Thu Mar 7 18:17:39 2002 +++ b/drivers/ide/ide_modes.h Thu Mar 7 18:17:39 2002 @@ -11,9 +11,6 @@ /* * Shared data/functions for determining best PIO mode for an IDE drive. - * Most of this stuff originally lived in cmd640.c, and changes to the - * ide_pio_blacklist[] table should be made with EXTREME CAUTION to avoid - * breaking the fragile cmd640.c support. */ #ifdef CONFIG_BLK_DEV_IDE_MODES @@ -37,200 +34,9 @@ byte blacklisted; unsigned int cycle_time; } ide_pio_data_t; - -#ifndef _IDE_C -int ide_scan_pio_blacklist (char *model); -byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d); +extern int ide_scan_pio_blacklist (char *model); +extern byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d); extern const ide_pio_timings_t ide_pio_timings[6]; - -#else /* _IDE_C */ - -const ide_pio_timings_t ide_pio_timings[6] = { - { 70, 165, 600 }, /* PIO Mode 0 */ - { 50, 125, 383 }, /* PIO Mode 1 */ - { 30, 100, 240 }, /* PIO Mode 2 */ - { 30, 80, 180 }, /* PIO Mode 3 with IORDY */ - { 25, 70, 120 }, /* PIO Mode 4 with IORDY */ - { 20, 50, 100 } /* PIO Mode 5 with IORDY (nonstandard) */ -}; - -/* - * Black list. Some drives incorrectly report their maximal PIO mode, - * at least in respect to CMD640. Here we keep info on some known drives. - */ -static struct ide_pio_info { - const char *name; - int pio; -} ide_pio_blacklist [] = { -/* { "Conner Peripherals 1275MB - CFS1275A", 4 }, */ - { "Conner Peripherals 540MB - CFS540A", 3 }, - - { "WDC AC2700", 3 }, - { "WDC AC2540", 3 }, - { "WDC AC2420", 3 }, - { "WDC AC2340", 3 }, - { "WDC AC2250", 0 }, - { "WDC AC2200", 0 }, - { "WDC AC21200", 4 }, - { "WDC AC2120", 0 }, - { "WDC AC2850", 3 }, - { "WDC AC1270", 3 }, - { "WDC AC1170", 1 }, - { "WDC AC1210", 1 }, - { "WDC AC280", 0 }, -/* { "WDC AC21000", 4 }, */ - { "WDC AC31000", 3 }, - { "WDC AC31200", 3 }, -/* { "WDC AC31600", 4 }, */ - - { "Maxtor 7131 AT", 1 }, - { "Maxtor 7171 AT", 1 }, - { "Maxtor 7213 AT", 1 }, - { "Maxtor 7245 AT", 1 }, - { "Maxtor 7345 AT", 1 }, - { "Maxtor 7546 AT", 3 }, - { "Maxtor 7540 AV", 3 }, - - { "SAMSUNG SHD-3121A", 1 }, - { "SAMSUNG SHD-3122A", 1 }, - { "SAMSUNG SHD-3172A", 1 }, - -/* { "ST51080A", 4 }, - * { "ST51270A", 4 }, - * { "ST31220A", 4 }, - * { "ST31640A", 4 }, - * { "ST32140A", 4 }, - * { "ST3780A", 4 }, - */ - { "ST5660A", 3 }, - { "ST3660A", 3 }, - { "ST3630A", 3 }, - { "ST3655A", 3 }, - { "ST3391A", 3 }, - { "ST3390A", 1 }, - { "ST3600A", 1 }, - { "ST3290A", 0 }, - { "ST3144A", 0 }, - { "ST3491A", 1 }, /* reports 3, should be 1 or 2 (depending on */ - /* drive) according to Seagates FIND-ATA program */ - - { "QUANTUM ELS127A", 0 }, - { "QUANTUM ELS170A", 0 }, - { "QUANTUM LPS240A", 0 }, - { "QUANTUM LPS210A", 3 }, - { "QUANTUM LPS270A", 3 }, - { "QUANTUM LPS365A", 3 }, - { "QUANTUM LPS540A", 3 }, - { "QUANTUM LIGHTNING 540A", 3 }, - { "QUANTUM LIGHTNING 730A", 3 }, - - { "QUANTUM FIREBALL_540", 3 }, /* Older Quantum Fireballs don't work */ - { "QUANTUM FIREBALL_640", 3 }, - { "QUANTUM FIREBALL_1080", 3 }, - { "QUANTUM FIREBALL_1280", 3 }, - { NULL, 0 } -}; - -/* - * This routine searches the ide_pio_blacklist for an entry - * matching the start/whole of the supplied model name. - * - * Returns -1 if no match found. - * Otherwise returns the recommended PIO mode from ide_pio_blacklist[]. - */ -int ide_scan_pio_blacklist (char *model) -{ - struct ide_pio_info *p; - - for (p = ide_pio_blacklist; p->name != NULL; p++) { - if (strncmp(p->name, model, strlen(p->name)) == 0) - return p->pio; - } - return -1; -} - -/* - * This routine returns the recommended PIO settings for a given drive, - * based on the drive->id information and the ide_pio_blacklist[]. - * This is used by most chipset support modules when "auto-tuning". - */ - -/* - * Drive PIO mode auto selection - */ -byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d) -{ - int pio_mode; - int cycle_time = 0; - int use_iordy = 0; - struct hd_driveid* id = drive->id; - int overridden = 0; - int blacklisted = 0; - - if (mode_wanted != 255) { - pio_mode = mode_wanted; - } else if (!drive->id) { - pio_mode = 0; - } else if ((pio_mode = ide_scan_pio_blacklist(id->model)) != -1) { - overridden = 1; - blacklisted = 1; - use_iordy = (pio_mode > 2); - } else { - pio_mode = id->tPIO; - if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */ - pio_mode = 2; - overridden = 1; - } - if (id->field_valid & 2) { /* drive implements ATA2? */ - if (id->capability & 8) { /* drive supports use_iordy? */ - use_iordy = 1; - cycle_time = id->eide_pio_iordy; - if (id->eide_pio_modes & 7) { - overridden = 0; - if (id->eide_pio_modes & 4) - pio_mode = 5; - else if (id->eide_pio_modes & 2) - pio_mode = 4; - else - pio_mode = 3; - } - } else { - cycle_time = id->eide_pio; - } - } - -#if 0 - if (drive->id->major_rev_num & 0x0004) printk("ATA-2 "); #endif - - /* - * Conservative "downgrade" for all pre-ATA2 drives - */ - if (pio_mode && pio_mode < 4) { - pio_mode--; - overridden = 1; -#if 0 - use_iordy = (pio_mode > 2); #endif - if (cycle_time && cycle_time < ide_pio_timings[pio_mode].cycle_time) - cycle_time = 0; /* use standard timing */ - } - } - if (pio_mode > max_mode) { - pio_mode = max_mode; - cycle_time = 0; - } - if (d) { - d->pio_mode = pio_mode; - d->cycle_time = cycle_time ? cycle_time : ide_pio_timings[pio_mode].cycle_time; - d->use_iordy = use_iordy; - d->overridden = overridden; - d->blacklisted = blacklisted; - } - return pio_mode; -} - -#endif /* _IDE_C */ -#endif /* CONFIG_BLK_DEV_IDE_MODES */ -#endif /* _IDE_MODES_H */ diff -Nru a/drivers/ide/it8172.c b/drivers/ide/it8172.c --- a/drivers/ide/it8172.c Thu Mar 7 18:17:38 2002 +++ b/drivers/ide/it8172.c Thu Mar 7 18:17:38 2002 @@ -53,7 +53,6 @@ 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); @@ -232,7 +231,7 @@ #endif /* defined(CONFIG_BLK_DEV_IDEDMA) && (CONFIG_IT8172_TUNING) */ -unsigned int __init pci_init_it8172 (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_it8172 (struct pci_dev *dev) { unsigned char progif; diff -Nru a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c --- a/drivers/ide/ns87415.c Thu Mar 7 18:17:42 2002 +++ b/drivers/ide/ns87415.c Thu Mar 7 18:17:42 2002 @@ -103,7 +103,7 @@ ns87415_prepare_drive(drive, 0); /* DMA failed: select PIO xfer */ return 1; case ide_dma_check: - if (drive->media != ide_disk) + if (drive->type != ATA_DISK) return ide_dmaproc(ide_dma_off_quietly, drive); /* Fallthrough... */ default: diff -Nru a/drivers/ide/opti621.c b/drivers/ide/opti621.c --- a/drivers/ide/opti621.c Thu Mar 7 18:17:39 2002 +++ b/drivers/ide/opti621.c Thu Mar 7 18:17:39 2002 @@ -1,10 +1,6 @@ /* - * linux/drivers/ide/opti621.c Version 0.6 Jan 02, 1999 - * * Copyright (C) 1996-1998 Linus Torvalds & authors (see below) - */ - -/* + * * Authors: * Jaromir Koutek , * Jan Harkes , @@ -62,9 +58,9 @@ * by hdparm. * * Version 0.1, Nov 8, 1996 - * by Jaromir Koutek, for 2.1.8. + * by Jaromir Koutek, for 2.1.8. * Initial version of driver. - * + * * Version 0.2 * Number 0.2 skipped. * @@ -80,14 +76,13 @@ * by Jaromir Koutek * Updates for use with (again) new IDE block driver. * Update of documentation. - * + * * Version 0.6, Jan 2, 1999 * by Jaromir Koutek * Reversed to version 0.3 of the driver, because * 0.5 doesn't work. */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ #define OPTI621_DEBUG /* define for debug messages */ #include @@ -164,7 +159,7 @@ } } -int cmpt_clk(int time, int bus_speed) +static int cmpt_clk(int time, int bus_speed) /* Returns (rounded up) time in clocks for time in ns, * with bus_speed in MHz. * Example: bus_speed = 40 MHz, time = 80 ns @@ -216,14 +211,13 @@ { if (pio != PIO_NOT_EXIST) { int adr_setup, data_pls; - int bus_speed = system_bus_clock(); adr_setup = ide_pio_timings[pio].setup_time; data_pls = ide_pio_timings[pio].active_time; - clks->address_time = cmpt_clk(adr_setup, bus_speed); - clks->data_time = cmpt_clk(data_pls, bus_speed); + clks->address_time = cmpt_clk(adr_setup, system_bus_speed); + clks->data_time = cmpt_clk(data_pls, system_bus_speed); clks->recovery_time = cmpt_clk(ide_pio_timings[pio].cycle_time - - adr_setup-data_pls, bus_speed); + - adr_setup-data_pls, system_bus_speed); if (clks->address_time<1) clks->address_time = 1; if (clks->address_time>4) clks->address_time = 4; if (clks->data_time<1) clks->data_time = 1; diff -Nru a/drivers/ide/pdc202xx.c b/drivers/ide/pdc202xx.c --- a/drivers/ide/pdc202xx.c Thu Mar 7 18:17:46 2002 +++ b/drivers/ide/pdc202xx.c Thu Mar 7 18:17:46 2002 @@ -63,7 +63,6 @@ static int pdc202xx_get_info(char *, char **, off_t, int); extern int (*pdc202xx_display_info)(char *, char **, off_t, int); /* ide-proc.c */ -extern char *ide_media_verbose(ide_drive_t *); static struct pci_dev *bmide_dev; char *pdc202xx_pio_verbose (u32 drive_pci) @@ -412,7 +411,8 @@ default: return -1; } - if ((drive->media != ide_disk) && (speed < XFER_SW_DMA_0)) return -1; + if ((drive->type != ATA_DISK) && (speed < XFER_SW_DMA_0)) + return -1; pci_read_config_dword(dev, drive_pci, &drive_conf); pci_read_config_byte(dev, (drive_pci), &AP); @@ -820,7 +820,8 @@ } if (jumpbit) { - if (drive->media != ide_disk) return ide_dma_off_quietly; + if (drive->type != ATA_DISK) + return ide_dma_off_quietly; if (id->capability & 4) { /* IORDY_EN & PREFETCH_EN */ OUT_BYTE((iordy + adj), indexreg); OUT_BYTE((IN_BYTE(datareg)|0x03), datareg); @@ -869,13 +870,14 @@ chipset_is_set: - if (drive->media != ide_disk) return ide_dma_off_quietly; + if (drive->type != ATA_DISK) + return ide_dma_off_quietly; pci_read_config_byte(dev, (drive_pci), &AP); if (id->capability & 4) /* IORDY_EN */ pci_write_config_byte(dev, (drive_pci), AP|IORDY_EN); pci_read_config_byte(dev, (drive_pci), &AP); - if (drive->media == ide_disk) /* PREFETCH_EN */ + if (drive->type == ATA_DISK) /* PREFETCH_EN */ pci_write_config_byte(dev, (drive_pci), AP|PREFETCH_EN); jumpbit_is_set: @@ -1102,7 +1104,7 @@ return 0; } -unsigned int __init pci_init_pdc202xx (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_pdc202xx(struct pci_dev *dev) { unsigned long high_16 = pci_resource_start(dev, 4); byte udma_speed_flag = IN_BYTE(high_16 + 0x001f); @@ -1112,7 +1114,7 @@ if (dev->resource[PCI_ROM_RESOURCE].start) { pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); - printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start); + printk("%s: ROM enabled at 0x%08lx\n", dev->name, dev->resource[PCI_ROM_RESOURCE].start); } switch (dev->device) { @@ -1151,7 +1153,7 @@ pci_read_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, &irq2); /* 0xbc */ if (irq != irq2) { pci_write_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, irq); /* 0xbc */ - printk("%s: pci-config space interrupt mirror fixed.\n", name); + printk("%s: pci-config space interrupt mirror fixed.\n", dev->name); } } break; @@ -1163,14 +1165,14 @@ printk("%s: (U)DMA Burst Bit %sABLED " \ "Primary %s Mode " \ "Secondary %s Mode.\n", - name, + dev->name, (udma_speed_flag & 1) ? "EN" : "DIS", (primary_mode & 1) ? "MASTER" : "PCI", (secondary_mode & 1) ? "MASTER" : "PCI" ); #ifdef CONFIG_PDC202XX_BURST if (!(udma_speed_flag & 1)) { - printk("%s: FORCING BURST BIT 0x%02x -> 0x%02x ", name, udma_speed_flag, (udma_speed_flag|1)); + printk("%s: FORCING BURST BIT 0x%02x -> 0x%02x ", dev->name, udma_speed_flag, (udma_speed_flag|1)); OUT_BYTE(udma_speed_flag|1, high_16 + 0x001f); printk("%sCTIVE\n", (IN_BYTE(high_16 + 0x001f) & 1) ? "A" : "INA"); } @@ -1179,14 +1181,14 @@ #ifdef CONFIG_PDC202XX_MASTER if (!(primary_mode & 1)) { printk("%s: FORCING PRIMARY MODE BIT 0x%02x -> 0x%02x ", - name, primary_mode, (primary_mode|1)); + dev->name, primary_mode, (primary_mode|1)); OUT_BYTE(primary_mode|1, high_16 + 0x001a); printk("%s\n", (IN_BYTE(high_16 + 0x001a) & 1) ? "MASTER" : "PCI"); } if (!(secondary_mode & 1)) { printk("%s: FORCING SECONDARY MODE BIT 0x%02x -> 0x%02x ", - name, secondary_mode, (secondary_mode|1)); + dev->name, secondary_mode, (secondary_mode|1)); OUT_BYTE(secondary_mode|1, high_16 + 0x001b); printk("%s\n", (IN_BYTE(high_16 + 0x001b) & 1) ? "MASTER" : "PCI"); } diff -Nru a/drivers/ide/pdc4030.c b/drivers/ide/pdc4030.c --- a/drivers/ide/pdc4030.c Thu Mar 7 18:17:40 2002 +++ b/drivers/ide/pdc4030.c Thu Mar 7 18:17:40 2002 @@ -332,14 +332,14 @@ if (nsect > sectors_avail) nsect = sectors_avail; sectors_avail -= nsect; - to = ide_map_buffer(rq, &flags); + to = bio_kmap_irq(rq->bio, &flags) + ide_rq_offset(rq); ata_input_data(drive, to, nsect * SECTOR_WORDS); #ifdef DEBUG_READ printk(KERN_DEBUG "%s: promise_read: sectors(%ld-%ld), " "buf=0x%08lx, rem=%ld\n", drive->name, rq->sector, rq->sector+nsect-1, (unsigned long) to, rq->nr_sectors-nsect); #endif - ide_unmap_buffer(to, &flags); + bio_kunmap_irq(to, &flags); rq->sector += nsect; rq->errors = 0; rq->nr_sectors -= nsect; @@ -437,7 +437,7 @@ nsect = mcount; mcount -= nsect; - buffer = ide_map_buffer(rq, &flags); + buffer = bio_kmap_irq(rq->bio, flags) + ide_rq_offset(rq); rq->sector += nsect; rq->nr_sectors -= nsect; rq->current_nr_sectors -= nsect; @@ -461,7 +461,7 @@ * re-entering us on the last transfer. */ taskfile_output_data(drive, buffer, nsect<<7); - ide_unmap_buffer(buffer, &flags); + bio_kunmap_irq(buffer, &flags); } while (mcount); return 0; diff -Nru a/drivers/ide/pdcadma.c b/drivers/ide/pdcadma.c --- a/drivers/ide/pdcadma.c Thu Mar 7 18:17:40 2002 +++ b/drivers/ide/pdcadma.c Thu Mar 7 18:17:40 2002 @@ -34,7 +34,6 @@ static int pdcadma_get_info(char *, char **, off_t, int); extern int (*pdcadma_display_info)(char *, char **, off_t, int); /* ide-proc.c */ -extern char *ide_media_verbose(ide_drive_t *); static struct pci_dev *bmide_dev; static int pdcadma_get_info (char *buffer, char **addr, off_t offset, int count) @@ -71,7 +70,7 @@ } #endif /* CONFIG_BLK_DEV_IDEDMA */ -unsigned int __init pci_init_pdcadma (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_pdcadma(struct pci_dev *dev) { #if defined(DISPLAY_PDCADMA_TIMINGS) && defined(CONFIG_PROC_FS) if (!pdcadma_proc) { diff -Nru a/drivers/ide/pdcraid.c b/drivers/ide/pdcraid.c --- a/drivers/ide/pdcraid.c Thu Mar 7 18:17:44 2002 +++ b/drivers/ide/pdcraid.c Thu Mar 7 18:17:44 2002 @@ -102,17 +102,17 @@ unsigned int minor; unsigned long sectors; - if (!inode || !inode->i_rdev) + if (!inode || kdev_none(inode->i_rdev)) return -EINVAL; - minor = MINOR(inode->i_rdev)>>SHIFT; + minor = minor(inode->i_rdev)>>SHIFT; switch (cmd) { case BLKGETSIZE: /* Return device size */ if (!arg) return -EINVAL; - sectors = ataraid_gendisk.part[MINOR(inode->i_rdev)].nr_sects; - if (MINOR(inode->i_rdev)&15) + sectors = ataraid_gendisk.part[minor(inode->i_rdev)].nr_sects; + if (minor(inode->i_rdev)&15) return put_user(sectors, (unsigned long *) arg); return put_user(raid[minor].sectors , (unsigned long *) arg); break; @@ -127,7 +127,7 @@ if (put_user(raid[minor].geom.heads, (byte *) &loc->heads)) return -EFAULT; if (put_user(raid[minor].geom.sectors, (byte *) &loc->sectors)) return -EFAULT; if (put_user(bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT; - if (put_user((unsigned)ataraid_gendisk.part[MINOR(inode->i_rdev)].start_sect, + if (put_user((unsigned)ataraid_gendisk.part[minor(inode->i_rdev)].start_sect, (unsigned long *) &loc->start)) return -EFAULT; return 0; } @@ -139,7 +139,7 @@ if (put_user(raid[minor].geom.heads, (byte *) &loc->heads)) return -EFAULT; if (put_user(raid[minor].geom.sectors, (byte *) &loc->sectors)) return -EFAULT; if (put_user(raid[minor].geom.cylinders, (unsigned int *) &loc->cylinders)) return -EFAULT; - if (put_user((unsigned)ataraid_gendisk.part[MINOR(inode->i_rdev)].start_sect, + if (put_user((unsigned)ataraid_gendisk.part[minor(inode->i_rdev)].start_sect, (unsigned long *) &loc->start)) return -EFAULT; return 0; } @@ -148,7 +148,7 @@ case BLKROSET: case BLKROGET: case BLKSSZGET: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); default: printk("Invalid ioctl \n"); @@ -463,7 +463,7 @@ major = devlist[devindex].major; minor = devlist[devindex].minor; - bdev = bdget(MKDEV(major,minor)); + bdev = bdget(mk_kdev(major,minor)); if (!bdev) return; @@ -491,7 +491,7 @@ (prom.raid.disk[i].device == prom.raid.device) ) { raid[device].disk[i].bdev = bdev; - raid[device].disk[i].device = MKDEV(major,minor); + raid[device].disk[i].device = mk_kdev(major,minor); raid[device].disk[i].sectors = prom.raid.disk_secs; raid[device].stride = (1<drives[1] == drive); int master_port = HWIF(drive)->index ? 0x42 : 0x40; int slave_port = 0x44; @@ -235,8 +234,8 @@ 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))); + 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) @@ -451,7 +450,7 @@ } #endif /* defined(CONFIG_BLK_DEV_IDEDMA) && (CONFIG_PIIX_TUNING) */ -unsigned int __init pci_init_piix (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_piix(struct pci_dev *dev) { #if defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS) if (!piix_proc) { diff -Nru a/drivers/ide/qd65xx.c b/drivers/ide/qd65xx.c --- a/drivers/ide/qd65xx.c Thu Mar 7 18:17:41 2002 +++ b/drivers/ide/qd65xx.c Thu Mar 7 18:17:41 2002 @@ -1,6 +1,4 @@ /* - * linux/drivers/ide/qd65xx.c Version 0.07 Sep 30, 2001 - * * Copyright (C) 1996-2001 Linus Torvalds & author (see below) */ @@ -9,24 +7,20 @@ * Version 0.04 Added second channel tuning * Version 0.05 Enhanced tuning ; added qd6500 support * Version 0.06 Added dos driver's list - * Version 0.07 Second channel bug fix + * Version 0.07 Second channel bug fix * * QDI QD6500/QD6580 EIDE controller fast support * * Please set local bus speed using kernel parameter idebus - * for example, "idebus=33" stands for 33Mhz VLbus + * for example, "idebus=33" stands for 33Mhz VLbus * To activate controller support, use "ide0=qd65xx" * To enable tuning, use "ide0=autotune" * To enable second channel tuning (qd6580 only), use "ide1=autotune" - */ - -/* + * * Rewritten from the work of Colten Edwards by * Samuel Thibault */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #include #include #include @@ -139,12 +133,12 @@ { byte active_cycle,recovery_cycle; - if (ide_system_bus_speed()<=33) { - active_cycle = 9 - IDE_IN(active_time * ide_system_bus_speed() / 1000 + 1, 2, 9); - recovery_cycle = 15 - IDE_IN(recovery_time * ide_system_bus_speed() / 1000 + 1, 0, 15); + if (system_bus_speed <= 33) { + active_cycle = 9 - IDE_IN(active_time * system_bus_speed / 1000 + 1, 2, 9); + recovery_cycle = 15 - IDE_IN(recovery_time * system_bus_speed / 1000 + 1, 0, 15); } else { - active_cycle = 8 - IDE_IN(active_time * ide_system_bus_speed() / 1000 + 1, 1, 8); - recovery_cycle = 18 - IDE_IN(recovery_time * ide_system_bus_speed() / 1000 + 1, 3, 18); + active_cycle = 8 - IDE_IN(active_time * system_bus_speed / 1000 + 1, 1, 8); + recovery_cycle = 18 - IDE_IN(recovery_time * system_bus_speed / 1000 + 1, 3, 18); } return((recovery_cycle<<4) | 0x08 | active_cycle); @@ -158,8 +152,8 @@ static byte qd6580_compute_timing (int active_time, int recovery_time) { - byte active_cycle = 17-IDE_IN(active_time * ide_system_bus_speed() / 1000 + 1, 2, 17); - byte recovery_cycle = 15-IDE_IN(recovery_time * ide_system_bus_speed() / 1000 + 1, 2, 15); + byte active_cycle = 17-IDE_IN(active_time * system_bus_speed / 1000 + 1, 2, 17); + byte recovery_cycle = 15-IDE_IN(recovery_time * system_bus_speed / 1000 + 1, 2, 15); return((recovery_cycle<<4) | active_cycle); } @@ -262,7 +256,7 @@ if (drive->id && !qd_find_disk_type(drive,&active_time,&recovery_time)) { pio = ide_get_best_pio_mode(drive, pio, 255, &d); - pio = IDE_MIN(pio,4); + pio = min(pio,4); switch (pio) { case 0: break; @@ -293,7 +287,7 @@ printk(KERN_INFO "%s: PIO mode%d\n",drive->name,pio); } - if (!HWIF(drive)->channel && drive->media != ide_disk) { + if (!HWIF(drive)->channel && drive->type != ATA_DISK) { qd_write_reg(0x5f,QD_CONTROL_PORT); printk(KERN_WARNING "%s: ATAPI: disabled read-ahead FIFO and post-write buffer on %s.\n",drive->name,HWIF(drive)->name); } diff -Nru a/drivers/ide/rz1000.c b/drivers/ide/rz1000.c --- a/drivers/ide/rz1000.c Thu Mar 7 18:17:38 2002 +++ b/drivers/ide/rz1000.c Thu Mar 7 18:17:38 2002 @@ -1,10 +1,6 @@ /* - * linux/drivers/ide/rz1000.c Version 0.05 December 8, 1997 - * * Copyright (C) 1995-1998 Linus Torvalds & author (see below) - */ - -/* + * * Principal Author: mlord@pobox.com (Mark Lord) * * See linux/MAINTAINERS for address of current maintainer. @@ -14,8 +10,6 @@ * * Dunno if this fixes both ports, or only the primary port (?). */ - -#undef REALLY_SLOW_IO /* most systems can safely undef this */ #include /* for CONFIG_BLK_DEV_IDEPCI */ #include diff -Nru a/drivers/ide/serverworks.c b/drivers/ide/serverworks.c --- a/drivers/ide/serverworks.c Thu Mar 7 18:17:36 2002 +++ b/drivers/ide/serverworks.c Thu Mar 7 18:17:36 2002 @@ -105,7 +105,6 @@ static int svwks_get_info(char *, char **, off_t, int); extern int (*svwks_display_info)(char *, char **, off_t, int); /* ide-proc.c */ -extern char *ide_media_verbose(ide_drive_t *); static int svwks_get_info (char *buffer, char **addr, off_t offset, int count) { @@ -547,7 +546,7 @@ } #endif /* CONFIG_BLK_DEV_IDEDMA */ -unsigned int __init pci_init_svwks (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_svwks(struct pci_dev *dev) { unsigned int reg; byte btr; @@ -661,8 +660,10 @@ hwif->autodma = 0; #else /* CONFIG_BLK_DEV_IDEDMA */ if (hwif->dma_base) { +#ifdef CONFIG_IDEDMA_AUTO if (!noautodma) hwif->autodma = 1; +#endif hwif->dmaproc = &svwks_dmaproc; hwif->highmem = 1; } else { diff -Nru a/drivers/ide/sis5513.c b/drivers/ide/sis5513.c --- a/drivers/ide/sis5513.c Thu Mar 7 18:17:42 2002 +++ b/drivers/ide/sis5513.c Thu Mar 7 18:17:42 2002 @@ -238,7 +238,7 @@ byte rw_prefetch = (0x11 << drive->dn); pci_read_config_byte(dev, 0x4b, ®4bh); - if (drive->media != ide_disk) + if (drive->type != ATA_DISK) return; if ((reg4bh & rw_prefetch) != rw_prefetch) @@ -567,7 +567,7 @@ } #endif /* CONFIG_BLK_DEV_IDEDMA */ -unsigned int __init pci_init_sis5513 (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_sis5513(struct pci_dev *dev) { struct pci_dev *host; int i = 0; diff -Nru a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c --- a/drivers/ide/sl82c105.c Thu Mar 7 18:17:41 2002 +++ b/drivers/ide/sl82c105.c Thu Mar 7 18:17:41 2002 @@ -8,8 +8,6 @@ * Drive tuning added from Rebel.com's kernel sources * -- Russell King (15/11/98) linux@arm.linux.org.uk */ - -#include #include #include #include @@ -225,7 +223,7 @@ /* * Enable the PCI device */ -unsigned int __init pci_init_sl82c105(struct pci_dev *dev, const char *msg) +unsigned int __init pci_init_sl82c105(struct pci_dev *dev) { unsigned char ctrl_stat; diff -Nru a/drivers/ide/slc90e66.c b/drivers/ide/slc90e66.c --- a/drivers/ide/slc90e66.c Thu Mar 7 18:17:37 2002 +++ b/drivers/ide/slc90e66.c Thu Mar 7 18:17:37 2002 @@ -60,7 +60,6 @@ static int slc90e66_get_info(char *, char **, off_t, int); extern int (*slc90e66_display_info)(char *, char **, off_t, int); /* ide-proc.c */ -extern char *ide_media_verbose(ide_drive_t *); static struct pci_dev *bmide_dev; static int slc90e66_get_info (char *buffer, char **addr, off_t offset, int count) @@ -347,7 +346,7 @@ } #endif /* CONFIG_BLK_DEV_IDEDMA */ -unsigned int __init pci_init_slc90e66 (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_slc90e66(struct pci_dev *dev) { #if defined(DISPLAY_SLC90E66_TIMINGS) && defined(CONFIG_PROC_FS) if (!slc90e66_proc) { diff -Nru a/drivers/ide/trm290.c b/drivers/ide/trm290.c --- a/drivers/ide/trm290.c Thu Mar 7 18:17:39 2002 +++ b/drivers/ide/trm290.c Thu Mar 7 18:17:39 2002 @@ -192,7 +192,7 @@ outl(hwif->dmatable_dma|reading|writing, hwif->dma_base); drive->waiting_for_dma = 1; outw((count * 2) - 1, hwif->dma_base+2); /* start DMA */ - if (drive->media != ide_disk) + if (drive->type != ATA_DISK) return 0; ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); diff -Nru a/drivers/ide/via82cxxx.c b/drivers/ide/via82cxxx.c --- a/drivers/ide/via82cxxx.c Thu Mar 7 18:17:41 2002 +++ b/drivers/ide/via82cxxx.c Thu Mar 7 18:17:41 2002 @@ -393,7 +393,7 @@ * and initialize its drive independent registers. */ -unsigned int __init pci_init_via82cxxx(struct pci_dev *dev, const char *name) +unsigned int __init pci_init_via82cxxx(struct pci_dev *dev) { struct pci_dev *isa = NULL; unsigned char t, v; @@ -484,7 +484,7 @@ * Determine system bus clock. */ - via_clock = system_bus_clock() * 1000; + via_clock = system_bus_speed * 1000; switch (via_clock) { case 33000: via_clock = 33333; break; diff -Nru a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c --- a/drivers/ieee1394/dv1394.c Thu Mar 7 18:17:39 2002 +++ b/drivers/ieee1394/dv1394.c Thu Mar 7 18:17:39 2002 @@ -185,78 +185,19 @@ /* Memory management functions */ /*******************************/ -#define MDEBUG(x) do { } while(0) /* Debug memory management */ - -/* [DaveM] I've recoded most of this so that: - * 1) It's easier to tell what is happening - * 2) It's more portable, especially for translating things - * out of vmalloc mapped areas in the kernel. - * 3) Less unnecessary translations happen. - * - * The code used to assume that the kernel vmalloc mappings - * existed in the page tables of every process, this is simply - * not guarenteed. We now use pgd_offset_k which is the - * defined way to get at the kernel page tables. - */ - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) -#define page_address(x) (x) -#endif - -/* Given PGD from the address space's page table, return the kernel - * virtual mapping of the physical memory mapped at ADR. - */ -static inline struct page *uvirt_to_page(pgd_t *pgd, unsigned long adr) -{ - pmd_t *pmd; - pte_t *ptep, pte; - struct page *ret = NULL; - - if (!pgd_none(*pgd)) { - pmd = pmd_offset(pgd, adr); - if (!pmd_none(*pmd)) { - preempt_disable(); - ptep = pte_offset_map(pmd, adr); - pte = *ptep; - pte_unmap(pte); - preempt_enable(); - if(pte_present(pte)) - ret = pte_page(pte); - } - } - 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, and for - * handling page faults on the rvmalloc()ed buffer - */ -static inline unsigned long kvirt_to_pa(unsigned long adr) -{ - unsigned long va, kva, ret; - - va = VMALLOC_VMADDR(adr); - kva = (unsigned long) page_address(uvirt_to_page(pgd_offset_k(va), va)); - kva |= adr & (PAGE_SIZE-1); /* restore the offset */ - ret = __pa(kva); - MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); - return ret; -} - static void * rvmalloc(unsigned long size) { void * mem; - unsigned long adr, page; - + unsigned long adr; + + size=PAGE_ALIGN(size); mem=vmalloc_32(size); if (mem) { 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))); + mem_map_reserve(vmalloc_to_page((void *)adr)); adr+=PAGE_SIZE; size-=PAGE_SIZE; } @@ -266,13 +207,12 @@ static void rvfree(void * mem, unsigned long size) { - unsigned long adr, page; - + unsigned long adr; + if (mem) { adr=(unsigned long) mem; - while (size > 0) { - page = kvirt_to_pa(adr); - mem_map_unreserve(virt_to_page(__va(page))); + while ((long) size > 0) { + mem_map_unreserve(vmalloc_to_page((void *)adr)); adr+=PAGE_SIZE; size-=PAGE_SIZE; } @@ -1166,9 +1106,9 @@ /* fill the sglist with the kernel addresses of pages in the non-contiguous buffer */ for(i = 0; i < video->user_dma.n_pages; i++) { - unsigned long va = VMALLOC_VMADDR( (unsigned long) video->user_buf + i * PAGE_SIZE ); + unsigned long va = (unsigned long) video->user_buf + i * PAGE_SIZE; - video->user_dma.sglist[i].page = uvirt_to_page(pgd_offset_k(va), va); + video->user_dma.sglist[i].page = vmalloc_to_page((void *)va); video->user_dma.sglist[i].length = PAGE_SIZE; } @@ -1492,7 +1432,7 @@ static struct page * dv1394_nopage(struct vm_area_struct * area, unsigned long address, int write_access) { unsigned long offset; - unsigned long page, kernel_virt_addr; + unsigned long kernel_virt_addr; struct page *ret = NOPAGE_SIGBUS; struct video_card *video = (struct video_card*) area->vm_private_data; @@ -1510,10 +1450,7 @@ offset = address - area->vm_start; kernel_virt_addr = (unsigned long) video->user_buf + offset; - - page = kvirt_to_pa(kernel_virt_addr); - - ret = virt_to_page(__va(page)); + ret = vmalloc_to_page((void *)kernel_virt_addr); get_page(ret); out: diff -Nru a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c --- a/drivers/ieee1394/video1394.c Thu Mar 7 18:17:39 2002 +++ b/drivers/ieee1394/video1394.c Thu Mar 7 18:17:39 2002 @@ -159,61 +159,35 @@ /* Memory management functions */ /*******************************/ -#define MDEBUG(x) do { } while(0) /* Debug memory management */ - -/* [DaveM] I've recoded most of this so that: - * 1) It's easier to tell what is happening - * 2) It's more portable, especially for translating things - * out of vmalloc mapped areas in the kernel. - * 3) Less unnecessary translations happen. - * - * The code used to assume that the kernel vmalloc mappings - * existed in the page tables of every process, this is simply - * not guaranteed. We now use pgd_offset_k which is the - * defined way to get at the kernel page tables. - */ - -static inline unsigned long uvirt_to_bus(unsigned long adr) -{ - unsigned long kva, ret; - - kva = page_address(vmalloc_to_page(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; + unsigned long kva, ret; - va = VMALLOC_VMADDR(adr); - kva = page_address(vmalloc_to_page(va)); + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ 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. + * This is used when initializing the contents of the area. */ static inline unsigned long kvirt_to_pa(unsigned long adr) { - unsigned long va, kva, ret; + unsigned long kva, ret; - va = VMALLOC_VMADDR(adr); - kva = page_address(vmalloc_to_page(va)); + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ ret = __pa(kva); - MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); return ret; } static void * rvmalloc(unsigned long size) { void * mem; - unsigned long adr, page; - + unsigned long adr; + + size=PAGE_ALIGN(size); mem=vmalloc_32(size); if (mem) { @@ -222,8 +196,7 @@ adr=(unsigned long) mem; while (size > 0) { - page = kvirt_to_pa(adr); - mem_map_reserve(virt_to_page(__va(page))); + mem_map_reserve(vmalloc_to_page((void *)adr)); adr+=PAGE_SIZE; size-=PAGE_SIZE; } @@ -233,15 +206,14 @@ static void rvfree(void * mem, unsigned long size) { - unsigned long adr, page; - + unsigned long adr; + if (mem) { adr=(unsigned long) mem; - while (size > 0) + while ((long) size > 0) { - page = kvirt_to_pa(adr); - mem_map_unreserve(virt_to_page(__va(page))); + mem_map_unreserve(vmalloc_to_page((void *)adr)); adr+=PAGE_SIZE; size-=PAGE_SIZE; } @@ -1381,7 +1353,7 @@ if (video->current_ctx == NULL) { PRINT(KERN_ERR, ohci->id, "Current iso context not set"); } else - res = do_iso_mmap(ohci, video->current_ctx, + res = do_iso_mmap(vma, ohci, video->current_ctx, (char *)vma->vm_start, (unsigned long)(vma->vm_end-vma->vm_start)); unlock_kernel(); diff -Nru a/drivers/input/gameport/cs461x.c b/drivers/input/gameport/cs461x.c --- a/drivers/input/gameport/cs461x.c Thu Mar 7 18:17:37 2002 +++ b/drivers/input/gameport/cs461x.c Thu Mar 7 18:17:37 2002 @@ -25,8 +25,6 @@ #define CS461X_FULL_MAP */ -#define COOKED_MODE - #ifndef PCI_VENDOR_ID_CIRRUS #define PCI_VENDOR_ID_CIRRUS 0x1013 @@ -122,6 +120,9 @@ static unsigned long ba0_addr; static unsigned int *ba0; +static char phys[32]; +static char name[] = "CS416x Gameport"; + #ifdef CS461X_FULL_MAP static unsigned long ba1_addr; static union ba1_t { @@ -206,14 +207,11 @@ static int cs461x_gameport_open(struct gameport *gameport, int mode) { switch (mode) { -#ifdef COOKED_MODE - case GAMEPORT_MODE_COOKED: - return 0; -#endif - case GAMEPORT_MODE_RAW: - return 0; - default: - return -1; + case GAMEPORT_MODE_COOKED: + case GAMEPORT_MODE_RAW: + return 0; + default: + return -1; } return 0; } @@ -274,9 +272,7 @@ return -ENOMEM; } #endif - printk(KERN_INFO "CS461x PCI: %lx[%d]\n", - ba0_addr, CS461X_BA0_SIZE); - + if (!(port = kmalloc(sizeof(struct gameport), GFP_KERNEL))) { printk(KERN_ERR "Memory allocation failed.\n"); cs461x_free(pdev); @@ -287,19 +283,25 @@ pci_set_drvdata(pdev, port); port->open = cs461x_gameport_open; - port->read = cs461x_gameport_read; port->trigger = cs461x_gameport_trigger; -#ifdef COOKED_MODE + port->read = cs461x_gameport_read; port->cooked_read = cs461x_gameport_cooked_read; -#endif + + sprintf(phys, "pci%s/gameport0", pdev->slot_name); + + port->name = name; + port->phys = phys; + port->idbus = BUS_PCI; + port->idvendor = pdev->vendor; + port->idproduct = pdev->device; cs461x_pokeBA0(BA0_JSIO, 0xFF); // ? cs461x_pokeBA0(BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW); gameport_register_port(port); - printk(KERN_INFO "gameport%d: CS461x Gameport speed %d kHz\n", - port->number, port->speed); + printk(KERN_INFO "gameport: %s on pci%s speed %d kHz\n", + name, pdev->slot_name, port->speed); return 0; } @@ -310,22 +312,22 @@ } static struct pci_driver cs461x_pci_driver = { - name: "PCI Gameport", + name: "CS461x Gameport", id_table: cs461x_pci_tbl, probe: cs461x_pci_probe, - remove: cs461x_pci_remove, + remove: __devexit_p(cs461x_pci_remove), }; -int __init js_cs461x_init(void) +int __init cs461x_init(void) { return pci_module_init(&cs461x_pci_driver); } -void __exit js_cs461x_exit(void) +void __exit cs461x_exit(void) { pci_unregister_driver(&cs461x_pci_driver); } -module_init(js_cs461x_init); -module_exit(js_cs461x_exit); +module_init(cs461x_init); +module_exit(cs461x_exit); diff -Nru a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c --- a/drivers/input/gameport/emu10k1-gp.c Thu Mar 7 18:17:45 2002 +++ b/drivers/input/gameport/emu10k1-gp.c Thu Mar 7 18:17:45 2002 @@ -1,13 +1,11 @@ /* - * $Id: emu10k1-gp.c,v 1.2 2001/04/24 07:48:56 vojtech Exp $ + * $Id: emu10k1-gp.c,v 1.8 2002/01/22 20:40:46 vojtech Exp $ * * Copyright (c) 2001 Vojtech Pavlik - * - * Sponsored by SuSE */ /* - * EMU10k1 - SB Live! - gameport driver for Linux + * EMU10k1 - SB Live / Audigy - gameport driver for Linux */ /* @@ -27,7 +25,7 @@ * * 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 + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -41,6 +39,7 @@ #include MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("EMU10k1 gameport driver"); MODULE_LICENSE("GPL"); struct emu { @@ -48,10 +47,12 @@ struct emu *next; struct gameport gameport; int size; + char phys[32]; }; static struct pci_device_id emu_tbl[] __devinitdata = { - { 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID }, /* SB Live! gameport */ + { 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID }, /* SB Live gameport */ + { 0x1102, 0x7003, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy gameport */ { 0, } }; @@ -60,15 +61,10 @@ static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int ioport, iolen; - int rc; - struct emu *port; + struct emu *emu; - rc = pci_enable_device(pdev); - if (rc) { - printk(KERN_ERR "emu10k1-gp: Cannot enable emu10k1 gameport (bus %d, devfn %d) error=%d\n", - pdev->bus->number, pdev->devfn, rc); - return rc; - } + if (pci_enable_device(pdev)) + return -EBUSY; ioport = pci_resource_start(pdev, 0); iolen = pci_resource_len(pdev, 0); @@ -76,39 +72,48 @@ if (!request_region(ioport, iolen, "emu10k1-gp")) return -EBUSY; - if (!(port = kmalloc(sizeof(struct emu), GFP_KERNEL))) { + if (!(emu = kmalloc(sizeof(struct emu), GFP_KERNEL))) { printk(KERN_ERR "emu10k1-gp: Memory allocation failed.\n"); release_region(ioport, iolen); return -ENOMEM; } - memset(port, 0, sizeof(struct emu)); + memset(emu, 0, sizeof(struct emu)); + + sprintf(emu->phys, "pci%s/gameport0", pdev->slot_name); + + emu->size = iolen; + emu->dev = pdev; + + emu->gameport.io = ioport; + emu->gameport.name = pdev->name; + emu->gameport.phys = emu->phys; + emu->gameport.idbus = BUS_PCI; + emu->gameport.idvendor = pdev->vendor; + emu->gameport.idproduct = pdev->device; - port->gameport.io = ioport; - port->size = iolen; - port->dev = pdev; - pci_set_drvdata(pdev, port); + pci_set_drvdata(pdev, emu); - gameport_register_port(&port->gameport); + gameport_register_port(&emu->gameport); - printk(KERN_INFO "gameport%d: Emu10k1 Gameport at %#x size %d speed %d kHz\n", - port->gameport.number, port->gameport.io, iolen, port->gameport.speed); + printk(KERN_INFO "gameport: %s at pci%s speed %d kHz\n", + pdev->name, pdev->slot_name, emu->gameport.speed); return 0; } static void __devexit emu_remove(struct pci_dev *pdev) { - struct emu *port = pci_get_drvdata(pdev); - gameport_unregister_port(&port->gameport); - release_region(port->gameport.io, port->size); - kfree(port); + struct emu *emu = pci_get_drvdata(pdev); + gameport_unregister_port(&emu->gameport); + release_region(emu->gameport.io, emu->size); + kfree(emu); } static struct pci_driver emu_driver = { name: "Emu10k1 Gameport", id_table: emu_tbl, probe: emu_probe, - remove: emu_remove, + remove: __devexit_p(emu_remove), }; int __init emu_init(void) diff -Nru a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c --- a/drivers/input/gameport/gameport.c Thu Mar 7 18:17:45 2002 +++ b/drivers/input/gameport/gameport.c Thu Mar 7 18:17:45 2002 @@ -1,9 +1,7 @@ /* - * $Id: gameport.c,v 1.5 2000/05/29 10:54:53 vojtech Exp $ + * $Id: gameport.c,v 1.18 2002/01/22 20:41:14 vojtech Exp $ * - * Copyright (c) 1999-2000 Vojtech Pavlik - * - * Sponsored by SuSE + * Copyright (c) 1999-2001 Vojtech Pavlik */ /* @@ -26,8 +24,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -40,7 +38,8 @@ #include #include -MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Generic gameport layer"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(gameport_register_port); @@ -54,7 +53,6 @@ static struct gameport *gameport_list; static struct gameport_dev *gameport_dev; -static int gameport_number; /* * gameport_measure_speed() measures the gameport i/o speed. @@ -122,7 +120,6 @@ void gameport_register_port(struct gameport *gameport) { - gameport->number = gameport_number++; gameport->next = gameport_list; gameport_list = gameport; @@ -140,8 +137,6 @@ if (gameport->dev && gameport->dev->disconnect) gameport->dev->disconnect(gameport); - - gameport_number--; } void gameport_register_device(struct gameport_dev *dev) diff -Nru a/drivers/input/gameport/lightning.c b/drivers/input/gameport/lightning.c --- a/drivers/input/gameport/lightning.c Thu Mar 7 18:17:42 2002 +++ b/drivers/input/gameport/lightning.c Thu Mar 7 18:17:42 2002 @@ -1,9 +1,7 @@ /* - * $Id: lightning.c,v 1.13 2001/04/26 10:24:46 vojtech Exp $ + * $Id: lightning.c,v 1.20 2002/01/22 20:41:31 vojtech Exp $ * * Copyright (c) 1998-2001 Vojtech Pavlik - * - * Sponsored by SuSE */ /* @@ -26,8 +24,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -51,14 +49,18 @@ #define L4_BUSY 0x01 #define L4_TIMEOUT 80 /* 80 us */ -MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("PDPI Lightning 4 gamecard driver"); MODULE_LICENSE("GPL"); struct l4 { struct gameport gameport; unsigned char port; + char phys[32]; } *l4_port[8]; +char l4_name[] = "PDPI Lightning 4"; + /* * l4_wait_ready() waits for the L4 to become ready. */ @@ -77,7 +79,7 @@ static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons) { - struct l4 *l4 = gameport->private; + struct l4 *l4 = gameport->driver; unsigned char status; int i, result = -1; @@ -110,7 +112,7 @@ static int l4_open(struct gameport *gameport, int mode) { - struct l4 *l4 = gameport->private; + struct l4 *l4 = gameport->driver; if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED) return -1; outb(L4_SELECT_ANALOG, L4_PORT); @@ -188,7 +190,7 @@ { int i, t; int cal[4]; - struct l4 *l4 = gameport->private; + struct l4 *l4 = gameport->driver; if (l4_getcal(l4->port, cal)) return -1; @@ -247,12 +249,18 @@ l4 = l4_port[i * 4 + j] = l4_port[i * 4] + j; l4->port = i * 4 + j; + sprintf(l4->phys, "isa%04x/gameport%d", L4_PORT, 4 * i + j); + gameport = &l4->gameport; - gameport->private = l4; + gameport->driver = l4; gameport->open = l4_open; gameport->cooked_read = l4_cooked_read; gameport->calibrate = l4_calibrate; + gameport->name = l4_name; + gameport->phys = l4->phys; + gameport->idbus = BUS_ISA; + if (!i && !j) gameport->io = L4_PORT; @@ -262,9 +270,7 @@ gameport_register_port(gameport); } - printk(KERN_INFO "gameport%d,%d,%d,%d: PDPI Lightning 4 %s card v%d.%d at %#x\n", - l4_port[i * 4 + 0]->gameport.number, l4_port[i * 4 + 1]->gameport.number, - l4_port[i * 4 + 2]->gameport.number, l4_port[i * 4 + 3]->gameport.number, + printk(KERN_INFO "gameport: PDPI Lightning 4 %s card v%d.%d at %#x\n", i ? "secondary" : "primary", rev >> 4, rev, L4_PORT); cards++; diff -Nru a/drivers/input/gameport/ns558.c b/drivers/input/gameport/ns558.c --- a/drivers/input/gameport/ns558.c Thu Mar 7 18:17:41 2002 +++ b/drivers/input/gameport/ns558.c Thu Mar 7 18:17:41 2002 @@ -1,10 +1,8 @@ /* - * $Id: ns558.c,v 1.29 2001/04/24 07:48:56 vojtech Exp $ + * $Id: ns558.c,v 1.43 2002/01/24 19:23:21 vojtech Exp $ * * Copyright (c) 1999-2001 Vojtech Pavlik * Copyright (c) 1999 Brian Gerst - * - * Sponsored by SuSE */ /* @@ -28,7 +26,7 @@ * * 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 + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -42,6 +40,7 @@ #include MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Classic gameport (ISA/PnP) driver"); MODULE_LICENSE("GPL"); #define NS558_ISA 1 @@ -56,6 +55,8 @@ struct pci_dev *dev; struct ns558 *next; struct gameport gameport; + char phys[32]; + char name[32]; }; static struct ns558 *ns558; @@ -141,12 +142,18 @@ port->type = NS558_ISA; port->size = (1 << i); port->gameport.io = io & (-1 << i); + port->gameport.phys = port->phys; + port->gameport.name = port->name; + port->gameport.idbus = BUS_ISA; + + sprintf(port->phys, "isa%04x/gameport0", io & (-1 << i)); + sprintf(port->name, "NS558 ISA"); request_region(port->gameport.io, (1 << i), "ns558-isa"); gameport_register_port(&port->gameport); - printk(KERN_INFO "gameport%d: NS558 ISA at %#x", port->gameport.number, port->gameport.io); + printk(KERN_INFO "gameport: NS558 ISA at %#x", port->gameport.io); if (port->size > 1) printk(" size %d", port->size); printk(" speed %d kHz\n", port->gameport.speed); @@ -155,17 +162,33 @@ #ifdef __ISAPNP__ +#define NS558_DEVICE(a,b,c,d)\ + card_vendor: ISAPNP_ANY_ID, card_device: ISAPNP_ANY_ID,\ + vendor: ISAPNP_VENDOR(a,b,c), function: ISAPNP_DEVICE(d) + static struct isapnp_device_id pnp_devids[] = { - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('@','P','@'), ISAPNP_DEVICE(0x0001), 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('@','P','@'), ISAPNP_DEVICE(0x2001), 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x7001), 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x7002), 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('C','S','C'), ISAPNP_DEVICE(0x0010), 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('C','S','C'), ISAPNP_DEVICE(0x0110), 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('C','S','C'), ISAPNP_DEVICE(0x0b35), 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('C','S','C'), ISAPNP_DEVICE(0x0010), 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('C','S','C'), ISAPNP_DEVICE(0x0110), 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P','N','P'), ISAPNP_DEVICE(0xb02f), 0 }, + { NS558_DEVICE('@','P','@',0x0001) }, /* ALS 100 */ + { NS558_DEVICE('@','P','@',0x0020) }, /* ALS 200 */ + { NS558_DEVICE('@','P','@',0x1001) }, /* ALS 100+ */ + { NS558_DEVICE('@','P','@',0x2001) }, /* ALS 120 */ + { NS558_DEVICE('A','S','B',0x16fd) }, /* AdLib NSC16 */ + { NS558_DEVICE('A','Z','T',0x3001) }, /* AZT1008 */ + { NS558_DEVICE('C','D','C',0x0001) }, /* Opl3-SAx */ + { NS558_DEVICE('C','S','C',0x0001) }, /* CS4232 */ + { NS558_DEVICE('C','S','C',0x000f) }, /* CS4236 */ + { NS558_DEVICE('C','S','C',0x0101) }, /* CS4327 */ + { NS558_DEVICE('C','T','L',0x7001) }, /* SB16 */ + { NS558_DEVICE('C','T','L',0x7002) }, /* AWE64 */ + { NS558_DEVICE('C','T','L',0x7005) }, /* Vibra16 */ + { NS558_DEVICE('E','N','S',0x2020) }, /* SoundscapeVIVO */ + { NS558_DEVICE('E','S','S',0x0001) }, /* ES1869 */ + { NS558_DEVICE('E','S','S',0x0005) }, /* ES1878 */ + { NS558_DEVICE('E','S','S',0x6880) }, /* ES688 */ + { NS558_DEVICE('I','B','M',0x0012) }, /* CS4232 */ + { NS558_DEVICE('O','P','T',0x0001) }, /* OPTi Audio16 */ + { NS558_DEVICE('Y','M','H',0x0006) }, /* Opl3-SA */ + { NS558_DEVICE('Y','M','H',0x0022) }, /* Opl3-SAx */ + { NS558_DEVICE('P','N','P',0xb02f) }, /* Generic */ { 0, }, }; @@ -203,13 +226,24 @@ port->next = next; port->type = NS558_PNP; - port->gameport.io = ioport; port->size = iolen; port->dev = dev; + port->gameport.io = ioport; + port->gameport.phys = port->phys; + port->gameport.name = port->name; + port->gameport.idbus = BUS_ISAPNP; + port->gameport.idvendor = dev->vendor; + port->gameport.idproduct = dev->device; + port->gameport.idversion = 0x100; + + sprintf(port->phys, "isapnp%d.%d/gameport0", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + sprintf(port->name, "%s", dev->name[0] ? dev->name : "NS558 PnP Gameport"); + gameport_register_port(&port->gameport); - printk(KERN_INFO "gameport%d: NS558 PnP at %#x", port->gameport.number, port->gameport.io); + printk(KERN_INFO "gameport: NS558 PnP at isapnp%d.%d io %#x", + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), port->gameport.io); if (iolen > 1) printk(" size %d", iolen); printk(" speed %d kHz\n", port->gameport.speed); diff -Nru a/drivers/input/gameport/pcigame.c b/drivers/input/gameport/pcigame.c --- a/drivers/input/gameport/pcigame.c Thu Mar 7 18:17:39 2002 +++ b/drivers/input/gameport/pcigame.c Thu Mar 7 18:17:39 2002 @@ -180,7 +180,7 @@ name: "pcigame", id_table: pcigame_id_table, probe: pcigame_probe, - remove: pcigame_remove, + remove: __devexit_p(pcigame_remove), }; int __init pcigame_init(void) diff -Nru a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c --- a/drivers/input/joystick/a3d.c Thu Mar 7 18:17:36 2002 +++ b/drivers/input/joystick/a3d.c Thu Mar 7 18:17:36 2002 @@ -1,9 +1,7 @@ /* - * $Id: a3d.c,v 1.14 2001/04/26 10:24:46 vojtech Exp $ + * $Id: a3d.c,v 1.21 2002/01/22 20:11:50 vojtech Exp $ * * Copyright (c) 1998-2001 Vojtech Pavlik - * - * Sponsored by SuSE */ /* @@ -26,8 +24,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -37,6 +35,10 @@ #include #include +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("FP-Gaming Assasin 3D joystick driver"); +MODULE_LICENSE("GPL"); + #define A3D_MAX_START 400 /* 400 us */ #define A3D_MAX_STROBE 60 /* 40 us */ #define A3D_DELAY_READ 3 /* 3 ms */ @@ -63,6 +65,8 @@ int used; int reads; int bads; + char phys[32]; + char adcphys[32]; }; /* @@ -190,7 +194,7 @@ int a3d_adc_cooked_read(struct gameport *gameport, int *axes, int *buttons) { - struct a3d *a3d = gameport->private; + struct a3d *a3d = gameport->driver; int i; for (i = 0; i < 4; i++) axes[i] = (a3d->axes[i] < 254) ? a3d->axes[i] : -1; @@ -205,7 +209,7 @@ int a3d_adc_open(struct gameport *gameport, int mode) { - struct a3d *a3d = gameport->private; + struct a3d *a3d = gameport->driver; if (mode != GAMEPORT_MODE_COOKED) return -1; if (!a3d->used++) @@ -219,7 +223,7 @@ static void a3d_adc_close(struct gameport *gameport) { - struct a3d *a3d = gameport->private; + struct a3d *a3d = gameport->driver; if (!--a3d->used) del_timer(&a3d->timer); } @@ -280,10 +284,12 @@ if (!a3d->mode || a3d->mode > 5) { printk(KERN_WARNING "a3d.c: Unknown A3D device detected " - "(gameport%d, id=%d), contact \n", gameport->number, a3d->mode); + "(%s, id=%d), contact \n", gameport->phys, a3d->mode); goto fail2; } + sprintf(a3d->phys, "%s/input0", gameport->phys); + sprintf(a3d->adcphys, "%s/gameport0", gameport->phys); if (a3d->mode == A3D_MODE_PXL) { @@ -323,17 +329,23 @@ a3d->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y); a3d->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_RIGHT) | BIT(BTN_LEFT) | BIT(BTN_MIDDLE); - a3d->adc.private = a3d; + a3d->adc.driver = a3d; a3d->adc.open = a3d_adc_open; a3d->adc.close = a3d_adc_close; a3d->adc.cooked_read = a3d_adc_cooked_read; a3d->adc.fuzz = 1; + a3d->adc.name = a3d_names[a3d->mode]; + a3d->adc.phys = a3d->adcphys; + a3d->adc.idbus = BUS_GAMEPORT; + a3d->adc.idvendor = GAMEPORT_ID_VENDOR_MADCATZ; + a3d->adc.idproduct = a3d->mode; + a3d->adc.idversion = 0x0100; + a3d_read(a3d, data); gameport_register_port(&a3d->adc); - printk(KERN_INFO "gameport%d: %s on gameport%d.0\n", - a3d->adc.number, a3d_names[a3d->mode], gameport->number); + printk(KERN_INFO "gameport: %s on %s\n", a3d_names[a3d->mode], gameport->phys); } a3d->dev.private = a3d; @@ -341,14 +353,14 @@ a3d->dev.close = a3d_close; a3d->dev.name = a3d_names[a3d->mode]; + a3d->dev.phys = a3d->phys; a3d->dev.idbus = BUS_GAMEPORT; a3d->dev.idvendor = GAMEPORT_ID_VENDOR_MADCATZ; a3d->dev.idproduct = a3d->mode; a3d->dev.idversion = 0x0100; input_register_device(&a3d->dev); - printk(KERN_INFO "input%d: %s on gameport%d.0\n", - a3d->dev.number, a3d_names[a3d->mode], gameport->number); + printk(KERN_INFO "input: %s on %s\n", a3d_names[a3d->mode], a3d->phys); return; fail2: gameport_close(gameport); @@ -384,5 +396,3 @@ module_init(a3d_init); module_exit(a3d_exit); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c --- a/drivers/input/joystick/adi.c Thu Mar 7 18:17:42 2002 +++ b/drivers/input/joystick/adi.c Thu Mar 7 18:17:42 2002 @@ -1,9 +1,7 @@ /* - * $Id: adi.c,v 1.15 2001/01/09 13:32:39 vojtech Exp $ + * $Id: adi.c,v 1.23 2002/01/22 20:26:17 vojtech Exp $ * - * Copyright (c) 1998-2000 Vojtech Pavlik - * - * Sponsored by SuSE + * Copyright (c) 1998-2001 Vojtech Pavlik */ /* @@ -26,8 +24,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -39,6 +37,10 @@ #include #include +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Logitech ADI joystick family driver"); +MODULE_LICENSE("GPL"); + /* * Times, array sizes, flags, ids. */ @@ -55,6 +57,7 @@ #define ADI_MIN_ID_LENGTH 66 #define ADI_MAX_NAME_LENGTH 48 #define ADI_MAX_CNAME_LENGTH 16 +#define ADI_MAX_PHYS_LENGTH 32 #define ADI_FLAG_HAT 0x04 #define ADI_FLAG_10BIT 0x08 @@ -118,6 +121,7 @@ short *key; char name[ADI_MAX_NAME_LENGTH]; char cname[ADI_MAX_CNAME_LENGTH]; + char phys[ADI_MAX_PHYS_LENGTH]; unsigned char data[ADI_MAX_LENGTH]; }; @@ -392,7 +396,7 @@ } } -static void adi_init_input(struct adi *adi, struct adi_port *port) +static void adi_init_input(struct adi *adi, struct adi_port *port, int half) { int i, t; char buf[ADI_MAX_NAME_LENGTH]; @@ -403,6 +407,7 @@ sprintf(buf, adi_names[t], adi->id); sprintf(adi->name, "Logitech %s", buf); + sprintf(adi->phys, "%s/input%d", port->gameport->phys, half); adi->abs = adi_abs[t]; adi->key = adi_key[t]; @@ -411,6 +416,7 @@ adi->dev.close = adi_close; adi->dev.name = adi->name; + adi->dev.phys = adi->phys; adi->dev.idbus = BUS_GAMEPORT; adi->dev.idvendor = GAMEPORT_ID_VENDOR_LOGITECH; adi->dev.idproduct = adi->id; @@ -419,7 +425,7 @@ adi->dev.private = port; adi->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad > 0)) * 2; i++) + for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++) set_bit(adi->abs[i], &adi->dev.absbit); for (i = 0; i < adi->buttons; i++) @@ -432,7 +438,7 @@ if (!adi->length) return; - for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad > 0)) * 2; i++) { + for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++) { t = adi->abs[i]; x = adi->dev.abs[t]; @@ -495,7 +501,7 @@ for (i = 0; i < 2; i++) { adi_id_decode(port->adi + i, port); - adi_init_input(port->adi + i, port); + adi_init_input(port->adi + i, port, i); } if (!port->adi[0].length && !port->adi[1].length) { @@ -514,8 +520,8 @@ if (port->adi[i].length > 0) { adi_init_center(port->adi + i); input_register_device(&port->adi[i].dev); - printk(KERN_INFO "input%d: %s [%s] on gameport%d.%d\n", - port->adi[i].dev.number, port->adi[i].name, port->adi[i].cname, gameport->number, i); + printk(KERN_INFO "input: %s [%s] on %s\n", + port->adi[i].name, port->adi[i].cname, gameport->phys); } } @@ -553,5 +559,3 @@ module_init(adi_init); module_exit(adi_exit); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c --- a/drivers/input/joystick/amijoy.c Thu Mar 7 18:17:45 2002 +++ b/drivers/input/joystick/amijoy.c Thu Mar 7 18:17:45 2002 @@ -1,9 +1,7 @@ /* - * $Id: amijoy.c,v 1.5 2000/07/21 22:52:24 vojtech Exp $ + * $Id: amijoy.c,v 1.13 2002/01/22 20:26:32 vojtech Exp $ * - * Copyright (c) 1998-2000 Vojtech Pavlik - * - * Sponsored by SuSE + * Copyright (c) 1998-2001 Vojtech Pavlik */ /* @@ -26,8 +24,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -40,13 +38,15 @@ #include #include -MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Driver for Amiga joysticks"); MODULE_PARM(amijoy, "1-2i"); MODULE_LICENSE("GPL"); static int amijoy[2] = { 0, 1 }; static int amijoy_used[2] = { 0, 0 }; static struct input_dev amijoy_dev[2]; +static char *amijoy_phys[2] = { "amijoy/input0", "amijoy/input1" }; static char *amijoy_name = "Amiga joystick"; @@ -64,9 +64,9 @@ input_report_key(amijoy_dev + i, BTN_TRIGGER, button); - input_report_abs(amijoy_dev + i, ABS_X, ((data >> 1) & 1) - ((data >> 9) & 1); + input_report_abs(amijoy_dev + i, ABS_X, ((data >> 1) & 1) - ((data >> 9) & 1)); data = ~(data ^ (data << 1)); - input_report_abs(amijoy_dev + i, ABS_Y, ((data >> 1) & 1) - ((data >> 9) & 1); + input_report_abs(amijoy_dev + i, ABS_Y, ((data >> 1) & 1) - ((data >> 9) & 1)); } } @@ -133,6 +133,7 @@ } amijoy->dev[i].name = amijoy_name; + amijoy->dev[i].phys = amijoy_phys[i]; amijoy->dev[i].idbus = BUS_AMIGA; amijoy->dev[i].idvendor = 0x0001; amijoy->dev[i].idproduct = 0x0003; @@ -141,7 +142,7 @@ amijoy_dev[i].private = amijoy_used + i; input_register_device(amijoy_dev + i); - printk(KERN_INFO "input%d: %s at joy%ddat\n", amijoy_dev[i].number, amijoy_name, i); + printk(KERN_INFO "input: %s at joy%ddat\n", amijoy_name, i); } return 0; } diff -Nru a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c --- a/drivers/input/joystick/analog.c Thu Mar 7 18:17:42 2002 +++ b/drivers/input/joystick/analog.c Thu Mar 7 18:17:42 2002 @@ -1,9 +1,7 @@ /* - * $Id: analog.c,v 1.52 2000/06/07 13:07:06 vojtech Exp $ + * $Id: analog.c,v 1.68 2002/01/22 20:18:32 vojtech Exp $ * - * Copyright (c) 1996-2000 Vojtech Pavlik - * - * Sponsored by SuSE + * Copyright (c) 1996-2001 Vojtech Pavlik */ /* @@ -26,8 +24,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -41,8 +39,8 @@ #include #include -MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Analog joystick and gamepad driver for Linux"); +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Analog joystick and gamepad driver"); MODULE_LICENSE("GPL"); /* @@ -95,6 +93,7 @@ #define ANALOG_FUZZ_MAGIC 36 /* 36 u*ms/loop */ #define ANALOG_MAX_NAME_LENGTH 128 +#define ANALOG_MAX_PHYS_LENGTH 32 static short analog_axes[] = { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE }; static short analog_hats[] = { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y }; @@ -111,6 +110,7 @@ int mask; short *buttons; char name[ANALOG_MAX_NAME_LENGTH]; + char phys[ANALOG_MAX_PHYS_LENGTH]; }; struct analog_port { @@ -138,15 +138,29 @@ #ifdef __i386__ #define TSC_PRESENT (test_bit(X86_FEATURE_TSC, &boot_cpu_data.x86_capability)) -#define GET_TIME(x) do { if (TSC_PRESENT) rdtscl(x); else { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } } while (0) +#define GET_TIME(x) do { if (TSC_PRESENT) rdtscl(x); else x = get_time_pit(); } while (0) #define DELTA(x,y) (TSC_PRESENT?((y)-(x)):((x)-(y)+((x)<(y)?1193180L/HZ:0))) #define TIME_NAME (TSC_PRESENT?"TSC":"PIT") +static unsigned int get_time_pit(void) +{ + extern spinlock_t i8253_lock; + unsigned long flags; + unsigned int count; + + spin_lock_irqsave(&i8253_lock, flags); + outb_p(0x00, 0x43); + count = inb_p(0x40); + count |= inb_p(0x40) << 8; + spin_unlock_irqrestore(&i8253_lock, flags); + + return count; +} #elif __x86_64__ #define GET_TIME(x) rdtscl(x) #define DELTA(x,y) ((y)-(x)) #define TIME_NAME "TSC" #elif __alpha__ -#define GET_TIME(x) ((x) = get_cycles()) +#define GET_TIME(x) do { x = get_cycles(x); } while (0) #define DELTA(x,y) ((y)-(x)) #define TIME_NAME "PCC" #else @@ -418,10 +432,12 @@ int i, j, t, v, w, x, y, z; analog_name(analog); + sprintf(analog->phys, "%s/input%d", port->gameport->phys, index); analog->buttons = (analog->mask & ANALOG_GAMEPAD) ? analog_pad_btn : analog_joy_btn; analog->dev.name = analog->name; + analog->dev.phys = analog->phys; analog->dev.idbus = BUS_GAMEPORT; analog->dev.idvendor = GAMEPORT_ID_VENDOR_ANALOG; analog->dev.idproduct = analog->mask >> 4; @@ -491,15 +507,14 @@ input_register_device(&analog->dev); - printk(KERN_INFO "input%d: %s at gameport%d.%d", - analog->dev.number, analog->name, port->gameport->number, index); + printk(KERN_INFO "input: %s at %s", analog->name, port->gameport->phys); if (port->cooked) printk(" [ADC port]\n"); else printk(" [%s timer, %d %sHz clock, %d ns res]\n", TIME_NAME, port->speed > 10000 ? (port->speed + 800) / 1000 : port->speed, - port->speed > 10000 ? "M" : "k", + port->speed > 10000 ? "M" : "k", port->speed > 10000 ? (port->loop * 1000) / (port->speed / 1000) : (port->loop * 1000000) / port->speed); } @@ -519,12 +534,13 @@ if ((port->mask & 3) != 3 && port->mask != 0xc) { printk(KERN_WARNING "analog.c: Unknown joystick device found " - "(data=%#x, gameport%d), probably not analog joystick.\n", - port->mask, port->gameport->number); + "(data=%#x, %s), probably not analog joystick.\n", + port->mask, port->gameport->phys); return -1; } - i = port->gameport->number < ANALOG_PORTS ? analog_options[port->gameport->number] : 0xff; + + i = analog_options[0]; /* FIXME !!! - need to specify options for different ports */ analog[0].mask = i & 0xfffff; @@ -604,8 +620,8 @@ gameport_trigger(gameport); while ((gameport_read(port->gameport) & port->mask) && (v < t)) v++; - if (v < (u >> 1) && port->gameport->number < ANALOG_PORTS) { - analog_options[port->gameport->number] |= + if (v < (u >> 1)) { /* FIXME - more than one port */ + analog_options[0] |= /* FIXME - more than one port */ ANALOG_SAITEK | ANALOG_BTNS_CHF | ANALOG_HBTN_CHF | ANALOG_HAT1_CHF; return 0; } @@ -666,9 +682,9 @@ if (port->analog[i].mask) input_unregister_device(&port->analog[i].dev); gameport_close(gameport); - printk(KERN_INFO "analog.c: %d out of %d reads (%d%%) on gameport%d failed\n", + printk(KERN_INFO "analog.c: %d out of %d reads (%d%%) on %s failed\n", port->bads, port->reads, port->reads ? (port->bads * 100 / port->reads) : 0, - port->gameport->number); + port->gameport->phys); kfree(port); } diff -Nru a/drivers/input/joystick/cobra.c b/drivers/input/joystick/cobra.c --- a/drivers/input/joystick/cobra.c Thu Mar 7 18:17:45 2002 +++ b/drivers/input/joystick/cobra.c Thu Mar 7 18:17:45 2002 @@ -1,13 +1,11 @@ /* - * $Id: cobra.c,v 1.10 2000/06/08 10:23:45 vojtech Exp $ + * $Id: cobra.c,v 1.19 2002/01/22 20:26:52 vojtech Exp $ * - * Copyright (c) 1999-2000 Vojtech Pavlik - * - * Sponsored by SuSE + * Copyright (c) 1999-2001 Vojtech Pavlik */ /* - * Creative Labd Blaster GamePad Cobra driver for Linux + * Creative Labs Blaster GamePad Cobra driver for Linux */ /* @@ -26,8 +24,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -37,6 +35,10 @@ #include #include +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Creative Labs Blaster GamePad Cobra driver"); +MODULE_LICENSE("GPL"); + #define COBRA_MAX_STROBE 45 /* 45 us max wait for first strobe */ #define COBRA_REFRESH_TIME HZ/50 /* 20 ms between reads */ #define COBRA_LENGTH 36 @@ -53,6 +55,7 @@ int reads; int bads; unsigned char exists; + char phys[2][32]; }; static unsigned char cobra_read_packet(struct gameport *gameport, unsigned int *data) @@ -121,6 +124,7 @@ if ((r = cobra_read_packet(cobra->gameport, data)) != cobra->exists) cobra->bads++; + else for (i = 0; i < 2; i++) if (cobra->exists & r & (1 << i)) { @@ -177,8 +181,8 @@ for (i = 0; i < 2; i++) if ((cobra->exists >> i) & data[i] & 1) { - printk(KERN_WARNING "cobra.c: Device on gameport%d.%d has the Ext bit set. ID is: %d" - " Contact vojtech@suse.cz\n", gameport->number, i, (data[i] >> 2) & 7); + printk(KERN_WARNING "cobra.c: Device %d on %s has the Ext bit set. ID is: %d" + " Contact vojtech@ucw.cz\n", i, gameport->phys, (data[i] >> 2) & 7); cobra->exists &= ~(1 << i); } @@ -188,11 +192,14 @@ for (i = 0; i < 2; i++) if ((cobra->exists >> i) & 1) { + sprintf(cobra->phys[i], "%s/input%d", gameport->phys, i); + cobra->dev[i].private = cobra; cobra->dev[i].open = cobra_open; cobra->dev[i].close = cobra_close; cobra->dev[i].name = cobra_name; + cobra->dev[i].phys = cobra->phys[i]; cobra->dev[i].idbus = BUS_GAMEPORT; cobra->dev[i].idvendor = GAMEPORT_ID_VENDOR_CREATIVE; cobra->dev[i].idproduct = 0x0008; @@ -208,11 +215,9 @@ cobra->dev[i].absmin[ABS_Y] = -1; cobra->dev[i].absmax[ABS_Y] = 1; input_register_device(cobra->dev + i); - printk(KERN_INFO "input%d: %s on gameport%d.%d\n", - cobra->dev[i].number, cobra_name, gameport->number, i); + printk(KERN_INFO "input: %s on %s\n", cobra_name, gameport->phys); } - return; fail2: gameport_close(gameport); fail1: kfree(cobra); @@ -248,5 +253,3 @@ module_init(cobra_init); module_exit(cobra_exit); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c --- a/drivers/input/joystick/db9.c Thu Mar 7 18:17:40 2002 +++ b/drivers/input/joystick/db9.c Thu Mar 7 18:17:40 2002 @@ -1,12 +1,10 @@ /* - * $Id: db9.c,v 1.6 2000/06/25 10:57:50 vojtech Exp $ + * $Id: db9.c,v 1.12 2002/01/22 20:27:05 vojtech Exp $ * - * Copyright (c) 1999 Vojtech Pavlik + * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: * Andree Borrmann Mats Sjövall - * - * Sponsored by SuSE */ /* @@ -29,8 +27,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -40,8 +38,10 @@ #include #include -MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver"); MODULE_LICENSE("GPL"); + MODULE_PARM(db9, "2i"); MODULE_PARM(db9_2, "2i"); MODULE_PARM(db9_3, "2i"); @@ -87,6 +87,7 @@ struct pardevice *pd; int mode; int used; + char phys[2][32]; }; static struct db9 *db9_base[3]; @@ -342,11 +343,14 @@ for (i = 0; i < 1 + (db9->mode == DB9_MULTI_0802_2); i++) { + sprintf(db9->phys[i], "%s/input%d", db9->pd->port->name, i); + db9->dev[i].private = db9; db9->dev[i].open = db9_open; db9->dev[i].close = db9_close; db9->dev[i].name = db9_name[db9->mode]; + db9->dev[i].phys = db9->phys[i]; db9->dev[i].idbus = BUS_PARPORT; db9->dev[i].idvendor = 0x0002; db9->dev[i].idproduct = config[1]; @@ -362,8 +366,7 @@ db9->dev[i].absmin[ABS_Y] = -1; db9->dev[i].absmax[ABS_Y] = 1; input_register_device(db9->dev + i); - printk(KERN_INFO "input%d: %s on %s\n", - db9->dev[i].number, db9_name[db9->mode], db9->pd->port->name); + printk(KERN_INFO "input: %s on %s\n", db9->dev[i].name, db9->pd->port->name); } return db9; diff -Nru a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c --- a/drivers/input/joystick/gamecon.c Thu Mar 7 18:17:44 2002 +++ b/drivers/input/joystick/gamecon.c Thu Mar 7 18:17:44 2002 @@ -1,17 +1,15 @@ /* - * $Id: gamecon.c,v 1.14 2001/04/29 22:42:14 vojtech Exp $ + * $Id: gamecon.c,v 1.21 2002/01/22 20:27:27 vojtech Exp $ * * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: * Andree Borrmann John Dahlstrom * David Kuder Nathan Hand - * - * Sponsored by SuSE */ /* - * NES, SNES, N64, Multi1, Multi2, PSX gamepad driver for Linux + * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux */ /* @@ -30,8 +28,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -41,8 +39,10 @@ #include #include -MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("NES, SNES, N64, MultiSystem, PSX gamepad driver"); MODULE_LICENSE("GPL"); + MODULE_PARM(gc, "2-6i"); MODULE_PARM(gc_2,"2-6i"); MODULE_PARM(gc_3,"2-6i"); @@ -65,6 +65,7 @@ struct timer_list timer; unsigned char pads[GC_MAX + 1]; int used; + char phys[5][32]; }; static struct gc *gc_base[3]; @@ -585,12 +586,15 @@ default: gc->pads[GC_PSX] &= ~gc_status_bit[i]; printk(KERN_WARNING "gamecon.c: Unsupported PSX controller %#x," - " please report to .\n", psx); + " please report to .\n", psx); } break; } + sprintf(gc->phys[i], "%s/input%d", gc->pd->port->name, i); + gc->dev[i].name = gc_names[config[i + 1]]; + gc->dev[i].phys = gc->phys[i]; gc->dev[i].idbus = BUS_PARPORT; gc->dev[i].idvendor = 0x0001; gc->dev[i].idproduct = config[i + 1]; @@ -608,7 +612,7 @@ for (i = 0; i < 5; i++) if (gc->pads[0] & gc_status_bit[i]) { input_register_device(gc->dev + i); - printk(KERN_INFO "input%d: %s on %s\n", gc->dev[i].number, gc->dev[i].name, gc->pd->port->name); + printk(KERN_INFO "input: %s on %s\n", gc->dev[i].name, gc->pd->port->name); } return gc; diff -Nru a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c --- a/drivers/input/joystick/gf2k.c Thu Mar 7 18:17:39 2002 +++ b/drivers/input/joystick/gf2k.c Thu Mar 7 18:17:39 2002 @@ -1,9 +1,7 @@ /* - * $Id: gf2k.c,v 1.12 2000/06/04 14:53:44 vojtech Exp $ + * $Id: gf2k.c,v 1.19 2002/01/22 20:27:43 vojtech Exp $ * - * Copyright (c) 1998-2000 Vojtech Pavlik - * - * Sponsored by SuSE + * Copyright (c) 1998-2001 Vojtech Pavlik */ /* @@ -26,8 +24,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -38,6 +36,10 @@ #include #include +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Genius Flight 2000 joystick driver"); +MODULE_LICENSE("GPL"); + #define GF2K_START 400 /* The time we wait for the first bit [400 us] */ #define GF2K_STROBE 40 /* The time we wait for the first bit [40 us] */ #define GF2K_TIMEOUT 4 /* Wait for everything to settle [4 ms] */ @@ -85,6 +87,7 @@ int used; unsigned char id; unsigned char length; + char phys[32]; }; /* @@ -278,11 +281,13 @@ #endif if (gf2k->id > GF2K_ID_MAX || !gf2k_axes[gf2k->id]) { - printk(KERN_WARNING "gf2k.c: Not yet supported joystick on gameport%d. [id: %d type:%s]\n", - gameport->number, gf2k->id, gf2k->id > GF2K_ID_MAX ? "Unknown" : gf2k_names[gf2k->id]); + printk(KERN_WARNING "gf2k.c: Not yet supported joystick on %s. [id: %d type:%s]\n", + gameport->phys, gf2k->id, gf2k->id > GF2K_ID_MAX ? "Unknown" : gf2k_names[gf2k->id]); goto fail2; } + sprintf(gf2k->phys, "%s/input0", gameport->phys); + gf2k->length = gf2k_lens[gf2k->id]; gf2k->dev.private = gf2k; @@ -291,6 +296,7 @@ gf2k->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); gf2k->dev.name = gf2k_names[gf2k->id]; + gf2k->dev.phys = gf2k->phys; gf2k->dev.idbus = BUS_GAMEPORT; gf2k->dev.idvendor = GAMEPORT_ID_VENDOR_GENIUS; gf2k->dev.idproduct = gf2k->id; @@ -323,8 +329,7 @@ } input_register_device(&gf2k->dev); - printk(KERN_INFO "input%d: %s on gameport%d.0\n", - gf2k->dev.number, gf2k_names[gf2k->id], gameport->number); + printk(KERN_INFO "input: %s on %s\n", gf2k_names[gf2k->id], gameport->phys); return; fail2: gameport_close(gameport); @@ -357,5 +362,3 @@ module_init(gf2k_init); module_exit(gf2k_exit); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/input/joystick/grip.c b/drivers/input/joystick/grip.c --- a/drivers/input/joystick/grip.c Thu Mar 7 18:17:42 2002 +++ b/drivers/input/joystick/grip.c Thu Mar 7 18:17:42 2002 @@ -1,9 +1,7 @@ /* - * $Id: grip.c,v 1.14 2000/06/06 21:13:36 vojtech Exp $ + * $Id: grip.c,v 1.21 2002/01/22 20:27:57 vojtech Exp $ * - * Copyright (c) 1998-2000 Vojtech Pavlik - * - * Sponsored by SuSE + * Copyright (c) 1998-2001 Vojtech Pavlik */ /* @@ -26,8 +24,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -37,6 +35,10 @@ #include #include +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Gravis GrIP protocol joystick driver"); +MODULE_LICENSE("GPL"); + #define GRIP_MODE_GPP 1 #define GRIP_MODE_BD 2 #define GRIP_MODE_XT 3 @@ -59,6 +61,7 @@ int used; int reads; int bads; + char phys[2][32]; }; static int grip_btn_gpp[] = { BTN_START, BTN_SELECT, BTN_TR2, BTN_Y, 0, BTN_TL2, BTN_A, BTN_B, BTN_X, 0, BTN_TL, BTN_TR, -1 }; @@ -340,12 +343,15 @@ for (i = 0; i < 2; i++) if (grip->mode[i]) { + sprintf(grip->phys[i], "%s/input%d", gameport->phys, i); + grip->dev[i].private = grip; grip->dev[i].open = grip_open; grip->dev[i].close = grip_close; grip->dev[i].name = grip_name[grip->mode[i]]; + grip->dev[i].phys = grip->phys[i]; grip->dev[i].idbus = BUS_GAMEPORT; grip->dev[i].idvendor = GAMEPORT_ID_VENDOR_GRAVIS; grip->dev[i].idproduct = grip->mode[i]; @@ -382,8 +388,8 @@ input_register_device(grip->dev + i); - printk(KERN_INFO "input%d: %s on gameport%d.%d\n", - grip->dev[i].number, grip_name[grip->mode[i]], gameport->number, i); + printk(KERN_INFO "input: %s on %s\n", + grip_name[grip->mode[i]], gameport->phys); } return; @@ -421,5 +427,3 @@ module_init(grip_init); module_exit(grip_exit); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/input/joystick/interact.c b/drivers/input/joystick/interact.c --- a/drivers/input/joystick/interact.c Thu Mar 7 18:17:38 2002 +++ b/drivers/input/joystick/interact.c Thu Mar 7 18:17:38 2002 @@ -1,12 +1,10 @@ /* - * $Id: interact.c,v 1.8 2000/05/29 11:19:51 vojtech Exp $ + * $Id: interact.c,v 1.16 2002/01/22 20:28:25 vojtech Exp $ * - * Copyright (c) 2000 Vojtech Pavlik + * Copyright (c) 2001 Vojtech Pavlik * * Based on the work of: * Toby Deshane - * - * Sponsored by SuSE */ /* @@ -29,8 +27,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -41,6 +39,10 @@ #include #include +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("InterAct digital joystick driver"); +MODULE_LICENSE("GPL"); + #define INTERACT_MAX_START 400 /* 400 us */ #define INTERACT_MAX_STROBE 40 /* 40 us */ #define INTERACT_MAX_LENGTH 32 /* 32 bits */ @@ -58,6 +60,7 @@ int reads; unsigned char type; unsigned char length; + char phys[32]; }; static short interact_abs_hhfx[] = @@ -137,40 +140,41 @@ if (interact_read_packet(interact->gameport, interact->length, data) < interact->length) { interact->bads++; - } else + } else { - for (i = 0; i < 3; i++) - data[i] <<= INTERACT_MAX_LENGTH - interact->length; + for (i = 0; i < 3; i++) + data[i] <<= INTERACT_MAX_LENGTH - interact->length; - switch (interact->type) { + switch (interact->type) { - case INTERACT_TYPE_HHFX: + case INTERACT_TYPE_HHFX: - for (i = 0; i < 4; i++) - input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i >> 1) << 3)) & 0xff); + for (i = 0; i < 4; i++) + input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i >> 1) << 3)) & 0xff); - for (i = 0; i < 2; i++) - input_report_abs(dev, ABS_HAT0Y - i, - ((data[1] >> ((i << 1) + 17)) & 1) - ((data[1] >> ((i << 1) + 16)) & 1)); + for (i = 0; i < 2; i++) + input_report_abs(dev, ABS_HAT0Y - i, + ((data[1] >> ((i << 1) + 17)) & 1) - ((data[1] >> ((i << 1) + 16)) & 1)); - for (i = 0; i < 8; i++) - input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1); + for (i = 0; i < 8; i++) + input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1); - for (i = 0; i < 4; i++) - input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i + 20)) & 1); + for (i = 0; i < 4; i++) + input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i + 20)) & 1); - break; + break; - case INTERACT_TYPE_PP8D: + case INTERACT_TYPE_PP8D: - for (i = 0; i < 2; i++) - input_report_abs(dev, interact_abs_pp8d[i], - ((data[0] >> ((i << 1) + 20)) & 1) - ((data[0] >> ((i << 1) + 21)) & 1)); + for (i = 0; i < 2; i++) + input_report_abs(dev, interact_abs_pp8d[i], + ((data[0] >> ((i << 1) + 20)) & 1) - ((data[0] >> ((i << 1) + 21)) & 1)); - for (i = 0; i < 8; i++) - input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1); + for (i = 0; i < 8; i++) + input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1); - break; + break; + } } mod_timer(&interact->timer, jiffies + INTERACT_REFRESH_TIME); @@ -235,11 +239,13 @@ break; if (!interact_type[i].length) { - printk(KERN_WARNING "interact.c: Unknown joystick on gameport%d. [len %d d0 %08x d1 %08x i2 %08x]\n", - gameport->number, i, data[0], data[1], data[2]); + printk(KERN_WARNING "interact.c: Unknown joystick on %s. [len %d d0 %08x d1 %08x i2 %08x]\n", + gameport->phys, i, data[0], data[1], data[2]); goto fail2; } + sprintf(interact->phys, "%s/input0", gameport->phys); + interact->type = i; interact->length = interact_type[i].length; @@ -248,6 +254,7 @@ interact->dev.close = interact_close; interact->dev.name = interact_type[i].name; + interact->dev.phys = interact->phys; interact->dev.idbus = BUS_GAMEPORT; interact->dev.idvendor = GAMEPORT_ID_VENDOR_INTERACT; interact->dev.idproduct = interact_type[i].id; @@ -270,8 +277,8 @@ set_bit(t, interact->dev.keybit); input_register_device(&interact->dev); - printk(KERN_INFO "input%d: %s on gameport%d.0\n", - interact->dev.number, interact_type[interact->type].name, gameport->number); + printk(KERN_INFO "input: %s on %s\n", + interact_type[interact->type].name, gameport->phys); return; fail2: gameport_close(gameport); @@ -304,5 +311,3 @@ module_init(interact_init); module_exit(interact_exit); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c --- a/drivers/input/joystick/magellan.c Thu Mar 7 18:17:37 2002 +++ b/drivers/input/joystick/magellan.c Thu Mar 7 18:17:37 2002 @@ -1,9 +1,7 @@ /* - * $Id: magellan.c,v 1.8 2000/05/31 13:17:12 vojtech Exp $ + * $Id: magellan.c,v 1.16 2002/01/22 20:28:39 vojtech Exp $ * - * Copyright (c) 1999-2000 Vojtech Pavlik - * - * Sponsored by SuSE + * Copyright (c) 1999-2001 Vojtech Pavlik */ /* @@ -27,7 +25,7 @@ * * 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 + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -37,15 +35,19 @@ #include #include +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Magellan and SpaceMouse 6dof controller driver"); +MODULE_LICENSE("GPL"); + /* * Definitions & global arrays. */ #define MAGELLAN_MAX_LENGTH 32 -static int magellan_buttons[] = { BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8}; -static int magellan_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ}; -static char *magellan_name = "LogiCad3D Magellan"; +static int magellan_buttons[] = { BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 }; +static int magellan_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ }; +static char *magellan_name = "LogiCad3D Magellan / SpaceMouse"; /* * Per-Magellan data. @@ -55,6 +57,7 @@ struct input_dev dev; int idx; unsigned char data[MAGELLAN_MAX_LENGTH]; + char phys[32]; }; /* @@ -162,8 +165,11 @@ magellan->dev.absmax[t] = 360; } + sprintf(magellan->phys, "%s/input0", serio->phys); + magellan->dev.private = magellan; magellan->dev.name = magellan_name; + magellan->dev.phys = magellan->phys; magellan->dev.idbus = BUS_RS232; magellan->dev.idvendor = SERIO_MAGELLAN; magellan->dev.idproduct = 0x0001; @@ -178,7 +184,7 @@ input_register_device(&magellan->dev); - printk(KERN_INFO "input%d: %s on serio%d\n", magellan->dev.number, magellan_name, serio->number); + printk(KERN_INFO "input: %s on %s\n", magellan_name, serio->phys); } /* @@ -208,5 +214,3 @@ module_init(magellan_init); module_exit(magellan_exit); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/input/joystick/sidewinder.c b/drivers/input/joystick/sidewinder.c --- a/drivers/input/joystick/sidewinder.c Thu Mar 7 18:17:46 2002 +++ b/drivers/input/joystick/sidewinder.c Thu Mar 7 18:17:46 2002 @@ -1,9 +1,7 @@ /* - * $Id: sidewinder.c,v 1.20 2001/05/19 08:14:54 vojtech Exp $ + * $Id: sidewinder.c,v 1.29 2002/01/22 20:28:51 vojtech Exp $ * * Copyright (c) 1998-2001 Vojtech Pavlik - * - * Sponsored by SuSE */ /* @@ -26,8 +24,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -38,12 +36,16 @@ #include #include +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Microsoft SideWinder joystick family driver"); +MODULE_LICENSE("GPL"); + /* * These are really magic values. Changing them can make a problem go away, * as well as break everything. */ -#undef SW_DEBUG +#define SW_DEBUG #define SW_START 400 /* The time we wait for the first bit [400 us] */ #define SW_STROBE 45 /* Max time per bit [45 us] */ @@ -114,6 +116,7 @@ struct timer_list timer; struct input_dev dev[4]; char name[64]; + char phys[4][32]; int length; int type; int bits; @@ -411,8 +414,8 @@ if (sw->type == SW_ID_3DP && sw->length == 66 && i != 66) { /* Broken packet, try to fix */ if (i == 64 && !sw_check(sw_get_bits(buf,0,64,1))) { /* Last init failed, 1 bit mode */ - printk(KERN_WARNING "sidewinder.c: Joystick in wrong mode on gameport%d" - " - going to reinitialize.\n", sw->gameport->number); + printk(KERN_WARNING "sidewinder.c: Joystick in wrong mode on %s" + " - going to reinitialize.\n", sw->gameport->phys); sw->fail = SW_FAIL; /* Reinitialize */ i = 128; /* Bogus value */ } @@ -437,8 +440,8 @@ if (sw->type == SW_ID_3DP && sw->length == 66 /* Many packets OK */ && sw->ok > SW_OK) { - printk(KERN_INFO "sidewinder.c: No more trouble on gameport%d" - " - enabling optimization again.\n", sw->gameport->number); + printk(KERN_INFO "sidewinder.c: No more trouble on %s" + " - enabling optimization again.\n", sw->gameport->phys); sw->length = 22; } @@ -450,15 +453,15 @@ if (sw->type == SW_ID_3DP && sw->length == 22 && sw->fail > SW_BAD) { /* Consecutive bad packets */ - printk(KERN_INFO "sidewinder.c: Many bit errors on gameport%d" - " - disabling optimization.\n", sw->gameport->number); + printk(KERN_INFO "sidewinder.c: Many bit errors on %s" + " - disabling optimization.\n", sw->gameport->phys); sw->length = 66; } if (sw->fail < SW_FAIL) return -1; /* Not enough, don't reinitialize yet */ - printk(KERN_WARNING "sidewinder.c: Too many bit errors on gameport%d" - " - reinitializing joystick.\n", sw->gameport->number); + printk(KERN_WARNING "sidewinder.c: Too many bit errors on %s" + " - reinitializing joystick.\n", sw->gameport->phys); if (!i && sw->type == SW_ID_3DP) { /* 3D Pro can be in analog mode */ udelay(3 * SW_TIMEOUT); @@ -582,8 +585,8 @@ if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) goto fail1; - dbg("Init 0: Opened gameport %d, io %#x, speed %d", - gameport->number, gameport->io, gameport->speed); + dbg("Init 0: Opened %s, io %#x, speed %d", + gameport->phys, gameport->io, gameport->speed); i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Read normal packet */ m |= sw_guess_mode(buf, i); /* Data packet (1-bit) can carry mode info [FSP] */ @@ -673,7 +676,7 @@ if (sw->type == -1) { printk(KERN_WARNING "sidewinder.c: unknown joystick device detected " - "on gameport%d, contact \n", gameport->number); + "on %s, contact \n", gameport->phys); sw_print_packet("ID", j * 3, idbuf, 3); sw_print_packet("Data", i * m, buf, m); goto fail2; @@ -691,6 +694,7 @@ int bits, code; sprintf(sw->name, "Microsoft SideWinder %s", sw_name[sw->type]); + sprintf(sw->phys[i], "%s/input%d", gameport->phys, i); sw->dev[i].private = sw; @@ -698,6 +702,7 @@ sw->dev[i].close = sw_close; sw->dev[i].name = sw->name; + sw->dev[i].phys = sw->phys[i]; sw->dev[i].idbus = BUS_GAMEPORT; sw->dev[i].idvendor = GAMEPORT_ID_VENDOR_MICROSOFT; sw->dev[i].idproduct = sw->type; @@ -719,8 +724,8 @@ set_bit(code, sw->dev[i].keybit); input_register_device(sw->dev + i); - printk(KERN_INFO "input%d: %s%s on gameport%d.%d [%d-bit id %d data %d]\n", - sw->dev[i].number, sw->name, comment, gameport->number, i, m, l, k); + printk(KERN_INFO "input: %s%s on %s [%d-bit id %d data %d]\n", + sw->name, comment, gameport->phys, m, l, k); } return; @@ -757,5 +762,3 @@ module_init(sw_init); module_exit(sw_exit); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c --- a/drivers/input/joystick/spaceball.c Thu Mar 7 18:17:37 2002 +++ b/drivers/input/joystick/spaceball.c Thu Mar 7 18:17:37 2002 @@ -1,17 +1,15 @@ /* - * $Id: spaceball.c,v 1.8 2000/11/23 11:42:39 vojtech Exp $ + * $Id: spaceball.c,v 1.17 2002/01/22 20:29:03 vojtech Exp $ * - * Copyright (c) 1999-2000 Vojtech Pavlik + * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: * David Thompson * Joseph Krahn - * - * Sponsored by SuSE */ /* - * SpaceTec SpaceBall 4000 FLX driver for Linux + * SpaceTec SpaceBall 2003/3003/4000 FLX driver for Linux */ /* @@ -30,8 +28,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -41,13 +39,29 @@ #include #include +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("SpaceTec SpaceBall 2003/3003/4000 FLX driver"); +MODULE_LICENSE("GPL"); + /* * Constants. */ -#define JS_SBALL_MAX_LENGTH 128 +#define SPACEBALL_MAX_LENGTH 128 +#define SPACEBALL_MAX_ID 8 + +#define SPACEBALL_1003 1 +#define SPACEBALL_2003B 3 +#define SPACEBALL_2003C 4 +#define SPACEBALL_3003C 7 +#define SPACEBALL_4000FLX 8 +#define SPACEBALL_4000FLX_L 9 + static int spaceball_axes[] = { ABS_X, ABS_Z, ABS_Y, ABS_RX, ABS_RZ, ABS_RY }; -static char *spaceball_name = "SpaceTec SpaceBall 4000 FLX"; +static char *spaceball_names[] = { + "?", "SpaceTec SpaceBall 1003", "SpaceTec SpaceBall 2003", "SpaceTec SpaceBall 2003B", + "SpaceTec SpaceBall 2003C", "SpaceTec SpaceBall 3003", "SpaceTec SpaceBall SpaceController", + "SpaceTec SpaceBall 3003C", "SpaceTec SpaceBall 4000FLX", "SpaceTec SpaceBall 4000FLX Lefty" }; /* * Per-Ball data. @@ -58,7 +72,8 @@ struct serio *serio; int idx; int escape; - unsigned char data[JS_SBALL_MAX_LENGTH]; + unsigned char data[SPACEBALL_MAX_LENGTH]; + char phys[32]; }; /* @@ -74,35 +89,53 @@ if (spaceball->idx < 2) return; - printk("%c %d\n", spaceball->data[0], spaceball->idx); - switch (spaceball->data[0]) { - case '@': /* Reset packet */ - spaceball->data[spaceball->idx - 1] = 0; - for (i = 1; i < spaceball->idx && spaceball->data[i] == ' '; i++); - printk(KERN_INFO "input%d: %s [%s] on serio%d\n", - spaceball->dev.number, spaceball_name, spaceball->data + i, spaceball->serio->number); - break; - case 'D': /* Ball data */ if (spaceball->idx != 15) return; - for (i = 0; i < 6; i++) { + for (i = 0; i < 6; i++) input_report_abs(dev, spaceball_axes[i], (__s16)((data[2 * i + 3] << 8) | data[2 * i + 2])); - } break; - case '.': /* Button data, part2 */ + case 'K': /* Button data */ if (spaceball->idx != 3) return; - input_report_key(dev, BTN_0, data[2] & 1); - input_report_key(dev, BTN_1, data[2] & 2); + input_report_key(dev, BTN_1, (data[2] & 0x01) || (data[2] & 0x20)); + input_report_key(dev, BTN_2, data[2] & 0x02); + input_report_key(dev, BTN_3, data[2] & 0x04); + input_report_key(dev, BTN_4, data[2] & 0x08); + input_report_key(dev, BTN_5, data[1] & 0x01); + input_report_key(dev, BTN_6, data[1] & 0x02); + input_report_key(dev, BTN_7, data[1] & 0x04); + input_report_key(dev, BTN_8, data[1] & 0x10); break; - case '?': /* Error packet */ + case '.': /* Advanced button data */ + if (spaceball->idx != 3) return; + input_report_key(dev, BTN_1, data[2] & 0x01); + input_report_key(dev, BTN_2, data[2] & 0x02); + input_report_key(dev, BTN_3, data[2] & 0x04); + input_report_key(dev, BTN_4, data[2] & 0x08); + input_report_key(dev, BTN_5, data[2] & 0x10); + input_report_key(dev, BTN_6, data[2] & 0x20); + input_report_key(dev, BTN_7, data[2] & 0x80); + input_report_key(dev, BTN_8, data[1] & 0x01); + input_report_key(dev, BTN_9, data[1] & 0x02); + input_report_key(dev, BTN_A, data[1] & 0x04); + input_report_key(dev, BTN_B, data[1] & 0x08); + input_report_key(dev, BTN_C, data[1] & 0x10); + input_report_key(dev, BTN_MODE, data[1] & 0x20); + break; + + case 'E': /* Device error */ spaceball->data[spaceball->idx - 1] = 0; printk(KERN_ERR "spaceball: Device error. [%s]\n", spaceball->data + 1); break; + + case '?': /* Bad command packet */ + spaceball->data[spaceball->idx - 1] = 0; + printk(KERN_ERR "spaceball: Bad command. [%s]\n", spaceball->data + 1); + break; } } @@ -136,11 +169,9 @@ data &= 0x1f; } default: - if (spaceball->escape) { + if (spaceball->escape) spaceball->escape = 0; - printk(KERN_WARNING "spaceball.c: Unknown escaped character: %#x (%c)\n", data, data); - } - if (spaceball->idx < JS_SBALL_MAX_LENGTH) + if (spaceball->idx < SPACEBALL_MAX_LENGTH) spaceball->data[spaceball->idx++] = data; return; } @@ -167,9 +198,12 @@ static void spaceball_connect(struct serio *serio, struct serio_dev *dev) { struct spaceball *spaceball; - int i, t; + int i, t, id; - if (serio->type != (SERIO_RS232 | SERIO_SPACEBALL)) + if ((serio->type & ~SERIO_ID) != (SERIO_RS232 | SERIO_SPACEBALL)) + return; + + if ((id = (serio->type & SERIO_ID) >> 8) > SPACEBALL_MAX_ID) return; if (!(spaceball = kmalloc(sizeof(struct spaceball), GFP_KERNEL))) @@ -177,7 +211,18 @@ memset(spaceball, 0, sizeof(struct spaceball)); spaceball->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - spaceball->dev.keybit[LONG(BTN_0)] = BIT(BTN_0) | BIT(BTN_1); + + switch (id) { + case SPACEBALL_4000FLX: + case SPACEBALL_4000FLX_L: + spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_9); + spaceball->dev.keybit[LONG(BTN_A)] |= BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C) | BIT(BTN_MODE); + default: + spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) + | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7) | BIT(BTN_8); + case SPACEBALL_3003C: + spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_1) | BIT(BTN_8); + } for (i = 0; i < 6; i++) { t = spaceball_axes[i]; @@ -191,10 +236,13 @@ spaceball->serio = serio; spaceball->dev.private = spaceball; - spaceball->dev.name = spaceball_name; + sprintf(spaceball->phys, "%s/input0", serio->phys); + + spaceball->dev.name = spaceball_names[id]; + spaceball->dev.phys = spaceball->phys; spaceball->dev.idbus = BUS_RS232; spaceball->dev.idvendor = SERIO_SPACEBALL; - spaceball->dev.idproduct = 0x0001; + spaceball->dev.idproduct = id; spaceball->dev.idversion = 0x0100; serio->private = spaceball; @@ -205,6 +253,9 @@ } input_register_device(&spaceball->dev); + + printk(KERN_INFO "input: %s on serio%s\n", + spaceball_names[id], serio->phys); } /* @@ -234,5 +285,3 @@ module_init(spaceball_init); module_exit(spaceball_exit); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c --- a/drivers/input/joystick/spaceorb.c Thu Mar 7 18:17:43 2002 +++ b/drivers/input/joystick/spaceorb.c Thu Mar 7 18:17:43 2002 @@ -1,12 +1,10 @@ /* - * $Id: spaceorb.c,v 1.7 2000/05/29 11:19:51 vojtech Exp $ + * $Id: spaceorb.c,v 1.15 2002/01/22 20:29:19 vojtech Exp $ * - * Copyright (c) 1999-2000 Vojtech Pavlik + * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: * David Thompson - * - * Sponsored by SuSE */ /* @@ -29,8 +27,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -40,15 +38,19 @@ #include #include +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("SpaceTec SpaceOrb 360 and Avenger 6dof controller driver"); +MODULE_LICENSE("GPL"); + /* * Constants. */ #define SPACEORB_MAX_LENGTH 64 -static int spaceorb_buttons[] = { BTN_TL, BTN_TR, BTN_Y, BTN_X, BTN_B, BTN_A, BTN_MODE}; -static int spaceorb_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ}; -static char *spaceorb_name = "SpaceTec SpaceOrb 360"; +static int spaceorb_buttons[] = { BTN_TL, BTN_TR, BTN_Y, BTN_X, BTN_B, BTN_A }; +static int spaceorb_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ }; +static char *spaceorb_name = "SpaceTec SpaceOrb 360 / Avenger"; /* * Per-Orb data. @@ -59,6 +61,7 @@ struct serio *serio; int idx; unsigned char data[SPACEORB_MAX_LENGTH]; + char phys[32]; }; static unsigned char spaceorb_xor[] = "SpaceWare"; @@ -88,8 +91,8 @@ case 'R': /* Reset packet */ spaceorb->data[spaceorb->idx - 1] = 0; for (i = 1; i < spaceorb->idx && spaceorb->data[i] == ' '; i++); - printk(KERN_INFO "input%d: %s [%s] on serio%d\n", - spaceorb->dev.number, spaceorb_name, spaceorb->data + i, spaceorb->serio->number); + printk(KERN_INFO "input: %s [%s] on %s\n", + spaceorb_name, spaceorb->data + i, spaceorb->serio->phys); break; case 'D': /* Ball + button data */ @@ -103,7 +106,7 @@ axes[5] = ((data[9] & 0x3f) << 4) | (data[10] >> 3); for (i = 0; i < 6; i++) input_report_abs(dev, spaceorb_axes[i], axes[i] - ((axes[i] & 0x200) ? 1024 : 0)); - for (i = 0; i < 8; i++) + for (i = 0; i < 6; i++) input_report_key(dev, spaceorb_buttons[i], (data[1] >> i) & 1); break; @@ -167,8 +170,8 @@ spaceorb->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - for (i = 0; i < 7; i++) - set_bit(spaceorb_buttons[i], &spaceorb->dev.keybit); + for (i = 0; i < 6; i++) + set_bit(spaceorb_buttons[i], spaceorb->dev.keybit); for (i = 0; i < 6; i++) { t = spaceorb_axes[i]; @@ -180,7 +183,10 @@ spaceorb->serio = serio; spaceorb->dev.private = spaceorb; + sprintf(spaceorb->phys, "%s/input0", serio->phys); + spaceorb->dev.name = spaceorb_name; + spaceorb->dev.phys = spaceorb->phys; spaceorb->dev.idbus = BUS_RS232; spaceorb->dev.idvendor = SERIO_SPACEORB; spaceorb->dev.idproduct = 0x0001; @@ -223,5 +229,3 @@ module_init(spaceorb_init); module_exit(spaceorb_exit); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c --- a/drivers/input/joystick/stinger.c Thu Mar 7 18:17:36 2002 +++ b/drivers/input/joystick/stinger.c Thu Mar 7 18:17:36 2002 @@ -1,10 +1,8 @@ /* - * $Id: stinger.c,v 1.4 2001/05/23 09:25:02 vojtech Exp $ + * $Id: stinger.c,v 1.10 2002/01/22 20:29:31 vojtech Exp $ * * Copyright (c) 2000-2001 Vojtech Pavlik * Copyright (c) 2000 Mark Fletcher - * - * Sponsored by SuSE */ /* @@ -28,7 +26,7 @@ * * 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 + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -38,6 +36,10 @@ #include #include +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Gravis Stinger gamepad driver"); +MODULE_LICENSE("GPL"); + /* * Constants. */ @@ -54,6 +56,7 @@ struct input_dev dev; int idx; unsigned char data[STINGER_MAX_LENGTH]; + char phys[32]; }; /* @@ -145,7 +148,10 @@ BIT(BTN_START) | BIT(BTN_SELECT); stinger->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + sprintf(stinger->phys, "%s/serio0", serio->phys); + stinger->dev.name = stinger_name; + stinger->dev.phys = stinger->phys; stinger->dev.idbus = BUS_RS232; stinger->dev.idvendor = SERIO_STINGER; stinger->dev.idproduct = 0x0001; @@ -168,7 +174,7 @@ input_register_device(&stinger->dev); - printk(KERN_INFO "input%d: %s on serio%d\n", stinger->dev.number, stinger_name, serio->number); + printk(KERN_INFO "input: %s on %s\n", stinger_name, serio->phys); } /* @@ -198,5 +204,3 @@ module_init(stinger_init); module_exit(stinger_exit); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c --- a/drivers/input/joystick/tmdc.c Thu Mar 7 18:17:42 2002 +++ b/drivers/input/joystick/tmdc.c Thu Mar 7 18:17:42 2002 @@ -1,13 +1,10 @@ /* - * $Id: tmdc.c,v 1.23 2000/11/29 19:52:24 vojtech Exp $ + * $Id: tmdc.c,v 1.31 2002/01/22 20:29:52 vojtech Exp $ * - * Copyright (c) 1998-2000 Vojtech Pavlik - * - * Sponsored by SuSE + * Copyright (c) 1998-2001 Vojtech Pavlik * * Based on the work of: * Trystan Larey-Williams - * */ /* @@ -30,8 +27,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -42,6 +39,10 @@ #include #include +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("ThrustMaster DirectConnect joystick driver"); +MODULE_LICENSE("GPL"); + #define TMDC_MAX_START 400 /* 400 us */ #define TMDC_MAX_STROBE 45 /* 45 us */ #define TMDC_MAX_LENGTH 13 @@ -94,6 +95,7 @@ struct timer_list timer; struct input_dev dev[2]; char name[2][64]; + char phys[2][32]; int mode[2]; signed char *abs[2]; short *btn[2]; @@ -172,6 +174,7 @@ if ((r = tmdc_read_packet(tmdc->gameport, data)) != tmdc->exists) bad = 1; + else for (j = 0; j < 2; j++) if (r & (1 << j) & tmdc->exists) { @@ -302,11 +305,14 @@ sprintf(tmdc->name[j], models[m].name, models[m].abs, (data[j][TMDC_BYTE_DEF] & 0xf) << 3, tmdc->mode[j]); + sprintf(tmdc->phys[j], "%s/input%d", gameport->phys, j); + tmdc->dev[j].private = tmdc; tmdc->dev[j].open = tmdc_open; tmdc->dev[j].close = tmdc_close; tmdc->dev[j].name = tmdc->name[j]; + tmdc->dev[j].phys = tmdc->phys[j]; tmdc->dev[j].idbus = BUS_GAMEPORT; tmdc->dev[j].idvendor = GAMEPORT_ID_VENDOR_THRUSTMASTER; tmdc->dev[j].idproduct = models[m].id; @@ -336,8 +342,7 @@ } input_register_device(tmdc->dev + j); - printk(KERN_INFO "input%d: %s on gameport%d.%d\n", - tmdc->dev[j].number, tmdc->name[j], gameport->number, j); + printk(KERN_INFO "input: %s on %s\n", tmdc->name[j], gameport->phys); } return; @@ -374,5 +379,3 @@ module_init(tmdc_init); module_exit(tmdc_exit); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c --- a/drivers/input/joystick/turbografx.c Thu Mar 7 18:17:45 2002 +++ b/drivers/input/joystick/turbografx.c Thu Mar 7 18:17:45 2002 @@ -1,12 +1,10 @@ /* - * $Id: turbografx.c,v 1.8 2000/05/29 20:39:38 vojtech Exp $ + * $Id: turbografx.c,v 1.14 2002/01/22 20:30:39 vojtech Exp $ * - * Copyright (c) 1998-2000 Vojtech Pavlik + * Copyright (c) 1998-2001 Vojtech Pavlik * * Based on the work of: * Steffen Schwenke - * - * Sponsored by SuSE */ /* @@ -29,8 +27,8 @@ * 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 + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -39,8 +37,10 @@ #include #include -MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("TurboGraFX parallel port interface driver"); MODULE_LICENSE("GPL"); + MODULE_PARM(tgfx, "2-8i"); MODULE_PARM(tgfx_2, "2-8i"); MODULE_PARM(tgfx_3, "2-8i"); @@ -69,6 +69,7 @@ struct pardevice *pd; struct timer_list timer; struct input_dev dev[7]; + char phys[7][32]; int sticks; int used; } *tgfx_base[3]; @@ -174,7 +175,10 @@ tgfx->dev[i].open = tgfx_open; tgfx->dev[i].close = tgfx_close; + sprintf(tgfx->phys[i], "%s/input0", tgfx->pd->port->name); + tgfx->dev[i].name = tgfx_name; + tgfx->dev[i].phys = tgfx->phys[i]; tgfx->dev[i].idbus = BUS_PARPORT; tgfx->dev[i].idvendor = 0x0003; tgfx->dev[i].idproduct = config[i+1]; @@ -190,8 +194,8 @@ tgfx->dev[i].absmin[ABS_Y] = -1; tgfx->dev[i].absmax[ABS_Y] = 1; input_register_device(tgfx->dev + i); - printk(KERN_INFO "input%d: %d-button Multisystem joystick on %s\n", - tgfx->dev[i].number, config[i+1], tgfx->pd->port->name); + printk(KERN_INFO "input: %d-button Multisystem joystick on %s\n", + config[i+1], tgfx->pd->port->name); } if (!tgfx->sticks) { diff -Nru a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c --- a/drivers/input/joystick/warrior.c Thu Mar 7 18:17:45 2002 +++ b/drivers/input/joystick/warrior.c Thu Mar 7 18:17:45 2002 @@ -1,9 +1,7 @@ /* - * $Id: warrior.c,v 1.8 2000/05/31 13:17:12 vojtech Exp $ + * $Id: warrior.c,v 1.14 2002/01/22 20:32:10 vojtech Exp $ * - * Copyright (c) 1999-2000 Vojtech Pavlik - * - * Sponsored by SuSE + * Copyright (c) 1999-2001 Vojtech Pavlik */ /* @@ -27,7 +25,7 @@ * * 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 + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -37,6 +35,10 @@ #include #include +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Logitech WingMan Warrior joystick driver"); +MODULE_LICENSE("GPL"); + /* * Constants. */ @@ -53,6 +55,7 @@ struct input_dev dev; int idx, len; unsigned char data[WARRIOR_MAX_LENGTH]; + char phys[32]; }; /* @@ -149,7 +152,10 @@ warrior->dev.relbit[0] = BIT(REL_DIAL); warrior->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y); + sprintf(warrior->phys, "%s/input0", serio->phys); + warrior->dev.name = warrior_name; + warrior->dev.phys = warrior->phys; warrior->dev.idbus = BUS_RS232; warrior->dev.idvendor = SERIO_WARRIOR; warrior->dev.idproduct = 0x0001; @@ -180,7 +186,7 @@ input_register_device(&warrior->dev); - printk(KERN_INFO "input%d: Logitech WingMan Warrior on serio%d\n", warrior->dev.number, serio->number); + printk(KERN_INFO "input: Logitech WingMan Warrior on %s\n", serio->phys); } /* @@ -210,5 +216,3 @@ module_init(warrior_init); module_exit(warrior_exit); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c --- a/drivers/input/serio/serio.c Thu Mar 7 18:17:37 2002 +++ b/drivers/input/serio/serio.c Thu Mar 7 18:17:37 2002 @@ -1,9 +1,7 @@ /* - * $Id: serio.c,v 1.5 2000/06/04 17:44:59 vojtech Exp $ + * $Id: serio.c,v 1.15 2002/01/22 21:12:03 vojtech Exp $ * - * Copyright (c) 1999-2000 Vojtech Pavlik - * - * Sponsored by SuSE + * Copyright (c) 1999-2001 Vojtech Pavlik */ /* @@ -27,14 +25,16 @@ * * 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 + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include #include #include +#include MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Serio abstraction core"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(serio_register_port); @@ -47,7 +47,6 @@ static struct serio *serio_list; static struct serio_dev *serio_dev; -static int serio_number; static void serio_find_dev(struct serio *serio) { @@ -69,7 +68,6 @@ void serio_register_port(struct serio *serio) { - serio->number = serio_number++; serio->next = serio_list; serio_list = serio; serio_find_dev(serio); @@ -84,8 +82,6 @@ if (serio->dev && serio->dev->disconnect) serio->dev->disconnect(serio); - - serio_number--; } void serio_register_device(struct serio_dev *dev) diff -Nru a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c --- a/drivers/input/serio/serport.c Thu Mar 7 18:17:42 2002 +++ b/drivers/input/serio/serport.c Thu Mar 7 18:17:42 2002 @@ -1,9 +1,7 @@ /* - * $Id: serport.c,v 1.7 2001/05/25 19:00:27 jdeneux Exp $ + * $Id: serport_old.c,v 1.10 2002/01/24 19:52:57 vojtech Exp $ * * Copyright (c) 1999-2001 Vojtech Pavlik - * - * Sponsored by SuSE */ /* @@ -28,7 +26,7 @@ * * 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 + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -39,12 +37,19 @@ #include #include +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Input device TTY line discipline"); +MODULE_LICENSE("GPL"); + struct serport { struct tty_struct *tty; wait_queue_head_t wait; struct serio serio; + char phys[32]; }; +char serport_name[] = "Serial port"; + /* * Callback functions from the serio code. */ @@ -75,6 +80,8 @@ static int serport_ldisc_open(struct tty_struct *tty) { struct serport *serport; + char ttyname[64]; + int i; MOD_INC_USE_COUNT; @@ -85,9 +92,19 @@ memset(serport, 0, sizeof(struct serport)); + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); serport->tty = tty; tty->disc_data = serport; + strcpy(ttyname, tty->driver.name); + for (i = 0; ttyname[i] != 0 && ttyname[i] != '/'; i++); + ttyname[i] = 0; + + sprintf(serport->phys, "%s%d/serio0", ttyname, minor(tty->device) - tty->driver.minor_start); + + serport->serio.name = serport_name; + serport->serio.phys = serport->phys; + serport->serio.type = SERIO_RS232; serport->serio.write = serport_serio_write; serport->serio.open = serport_serio_open; @@ -156,14 +173,14 @@ serio_register_port(&serport->serio); - printk(KERN_INFO "serio%d: Serial port %s\n", serport->serio.number, name); + printk(KERN_INFO "serio: Serial port %s\n", name); add_wait_queue(&serport->wait, &wait); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); while(serport->serio.type && !signal_pending(current)) schedule(); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&serport->wait, &wait); serio_unregister_port(&serport->serio); @@ -187,6 +204,14 @@ return -EINVAL; } +static void serport_ldisc_write_wakeup(struct tty_struct * tty) +{ + struct serport *sp = (struct serport *) tty->disc_data; + + serio_dev_write_wakeup(&sp->serio); + +} + /* * The line discipline structure. */ @@ -199,6 +224,7 @@ ioctl: serport_ldisc_ioctl, receive_buf: serport_ldisc_receive, receive_room: serport_ldisc_room, + write_wakeup: serport_ldisc_write_wakeup }; /* @@ -222,5 +248,3 @@ module_init(serport_init); module_exit(serport_exit); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/isdn/Config.help b/drivers/isdn/Config.help --- a/drivers/isdn/Config.help Thu Mar 7 18:17:44 2002 +++ b/drivers/isdn/Config.help Thu Mar 7 18:17:44 2002 @@ -161,6 +161,10 @@ Enable this if you like to use ISDN in US on a NI1 basic rate interface. +CONFIG_HISAX_MAX_CARDS + This option allows you to specify the maximum number of cards which + the HiSax driver will be able to handle. + CONFIG_HISAX_16_0 This enables HiSax support for the Teles ISDN-cards S0-16.0, S0-8 and many compatibles. diff -Nru a/drivers/isdn/Config.in b/drivers/isdn/Config.in --- a/drivers/isdn/Config.in Thu Mar 7 18:17:45 2002 +++ b/drivers/isdn/Config.in Thu Mar 7 18:17:45 2002 @@ -84,6 +84,7 @@ dep_tristate 'ST5481 USB ISDN modem (EXPERIMENTAL)' CONFIG_HISAX_ST5481 $CONFIG_HISAX $CONFIG_USB $CONFIG_EXPERIMENTAL dep_tristate 'AVM Fritz!Card PCI/PCIv2/PnP support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_PCIPNP $CONFIG_HISAX $CONFIG_EXPERIMENTAL dep_tristate 'AVM Fritz!Card classic support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_CLASSIC $CONFIG_HISAX $CONFIG_EXPERIMENTAL + dep_tristate 'HFC PCI support (EXPERIMENTAL)' CONFIG_HISAX_HFCPCI $CONFIG_HISAX $CONFIG_EXPERIMENTAL fi endmenu diff -Nru a/drivers/isdn/act2000/act2000_isa.c b/drivers/isdn/act2000/act2000_isa.c --- a/drivers/isdn/act2000/act2000_isa.c Thu Mar 7 18:17:43 2002 +++ b/drivers/isdn/act2000/act2000_isa.c Thu Mar 7 18:17:43 2002 @@ -178,7 +178,8 @@ card->flags &= ~ACT2000_FLAGS_PVALID; } if (!check_region(portbase, ISA_REGION)) { - request_region(portbase, ACT2000_PORTLEN, card->regname); + if (request_region(portbase, ACT2000_PORTLEN, card->regname) == NULL) + return -EIO; card->port = portbase; card->flags |= ACT2000_FLAGS_PVALID; return 0; diff -Nru a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c --- a/drivers/isdn/avmb1/capi.c Thu Mar 7 18:17:46 2002 +++ b/drivers/isdn/avmb1/capi.c Thu Mar 7 18:17:46 2002 @@ -1535,7 +1535,7 @@ /* -------- init function and module interface ---------------------- */ -static void __exit alloc_exit(void) +static void alloc_exit(void) { if (capidev_cachep) { (void)kmem_cache_destroy(capidev_cachep); diff -Nru a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile --- a/drivers/isdn/hisax/Makefile Thu Mar 7 18:17:42 2002 +++ b/drivers/isdn/hisax/Makefile Thu Mar 7 18:17:42 2002 @@ -64,6 +64,7 @@ obj-$(CONFIG_HISAX_ST5481) += hisax_st5481.o obj-$(CONFIG_HISAX_FRITZ_PCIPNP) += hisax_isac.o hisax_fcpcipnp.o obj-$(CONFIG_HISAX_FRITZ_CLASSIC) += hisax_isac.o hisax_hscx.o hisax_fcclassic.o +obj-$(CONFIG_HISAX_FRITZ_PCIPNP) += hisax_hfcpci.o CERT := $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?) CFLAGS_cert.o := -DCERTIFICATION=$(CERT) diff -Nru a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c --- a/drivers/isdn/hisax/hisax_fcpcipnp.c Thu Mar 7 18:17:39 2002 +++ b/drivers/isdn/hisax/hisax_fcpcipnp.c Thu Mar 7 18:17:39 2002 @@ -945,14 +945,14 @@ static struct pci_driver fcpci_driver = { name: "fcpci", probe: fcpci_probe, - remove: fcpci_remove, + remove: __devexit_p(fcpci_remove), id_table: fcpci_ids, }; static struct isapnp_driver fcpnp_driver = { name: "fcpnp", probe: fcpnp_probe, - remove: fcpnp_remove, + remove: __devexit_p(fcpnp_remove), id_table: fcpnp_ids, }; diff -Nru a/drivers/isdn/hisax/hisax_hfcpci.c b/drivers/isdn/hisax/hisax_hfcpci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/isdn/hisax/hisax_hfcpci.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,1646 @@ +/* + * Driver for HFC PCI based cards + * + * Author Kai Germaschewski + * Copyright 2002 by Kai Germaschewski + * 2000 by Karsten Keil + * 2000 by Werner Cornelius + * + * based upon Werner Cornelius's original hfc_pci.c driver + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +// XXX timer3 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hisax_hfcpci.h" + +// debugging cruft +#define __debug_variable debug +#include "hisax_debug.h" + +#ifdef CONFIG_HISAX_DEBUG +static int debug = 0; +MODULE_PARM(debug, "i"); +#endif + +MODULE_AUTHOR("Kai Germaschewski /Werner Cornelius "); +MODULE_DESCRIPTION("HFC PCI ISDN driver"); + +#define ID(ven, dev, name) \ + { vendor: PCI_VENDOR_ID_##ven, \ + device: PCI_DEVICE_ID_##dev, \ + subvendor: PCI_ANY_ID, \ + subdevice: PCI_ANY_ID, \ + class: 0, \ + class_mask: 0, \ + driver_data: (unsigned long) name } + +static struct pci_device_id hfcpci_ids[] __devinitdata = { + ID(CCD, CCD_2BD0, "CCD/Billion/Asuscom 2BD0"), + ID(CCD, CCD_B000, "Billion B000"), + ID(CCD, CCD_B006, "Billion B006"), + ID(CCD, CCD_B007, "Billion B007"), + ID(CCD, CCD_B008, "Billion B008"), + ID(CCD, CCD_B009, "Billion B009"), + ID(CCD, CCD_B00A, "Billion B00A"), + ID(CCD, CCD_B00B, "Billion B00B"), + ID(CCD, CCD_B00C, "Billion B00C"), + ID(CCD, CCD_B100, "Seyeon"), + ID(ABOCOM, ABOCOM_2BD1, "Abocom/Magitek"), + ID(ASUSTEK, ASUSTEK_0675, "Asuscom/Askey"), + ID(BERKOM, BERKOM_T_CONCEPT, "German Telekom T-Concept"), + ID(BERKOM, BERKOM_A1T, "German Telekom A1T"), + ID(ANIGMA, ANIGMA_MC145575, "Motorola MC145575"), + ID(ZOLTRIX, ZOLTRIX_2BD0, "Zoltrix 2BD0"), + ID(DIGI, DIGI_DF_M_IOM2_E, "Digi DataFire Micro V IOM2 (Europe)"), + ID(DIGI, DIGI_DF_M_E, "Digi DataFire Micro V (Europe)"), + ID(DIGI, DIGI_DF_M_IOM2_A, "Digi DataFire Micro V IOM2 (America)"), + ID(DIGI, DIGI_DF_M_A, "Digi DataFire Micro V (America)"), + { } +}; +MODULE_DEVICE_TABLE(pci, hfcpci_ids); + +#undef ID + +static int protocol = 2; /* EURO-ISDN Default */ +MODULE_PARM(protocol, "i"); + +// ---------------------------------------------------------------------- +// + +#define DBG_WARN 0x0001 +#define DBG_INFO 0x0002 +#define DBG_IRQ 0x0010 +#define DBG_L1M 0x0020 +#define DBG_PR 0x0040 +#define DBG_D_XMIT 0x0100 +#define DBG_D_RECV 0x0200 +#define DBG_B_XMIT 0x1000 +#define DBG_B_RECV 0x2000 + +/* memory window base address offset (in config space) */ + +#define HFCPCI_MWBA 0x80 + +/* GCI/IOM bus monitor registers */ + +#define HCFPCI_C_I 0x08 +#define HFCPCI_TRxR 0x0C +#define HFCPCI_MON1_D 0x28 +#define HFCPCI_MON2_D 0x2C + + +/* GCI/IOM bus timeslot registers */ + +#define HFCPCI_B1_SSL 0x80 +#define HFCPCI_B2_SSL 0x84 +#define HFCPCI_AUX1_SSL 0x88 +#define HFCPCI_AUX2_SSL 0x8C +#define HFCPCI_B1_RSL 0x90 +#define HFCPCI_B2_RSL 0x94 +#define HFCPCI_AUX1_RSL 0x98 +#define HFCPCI_AUX2_RSL 0x9C + +/* GCI/IOM bus data registers */ + +#define HFCPCI_B1_D 0xA0 +#define HFCPCI_B2_D 0xA4 +#define HFCPCI_AUX1_D 0xA8 +#define HFCPCI_AUX2_D 0xAC + +/* GCI/IOM bus configuration registers */ + +#define HFCPCI_MST_EMOD 0xB4 +#define HFCPCI_MST_MODE 0xB8 +#define HFCPCI_CONNECT 0xBC + + +/* Interrupt and status registers */ + +#define HFCPCI_FIFO_EN 0x44 +#define HFCPCI_TRM 0x48 +#define HFCPCI_B_MODE 0x4C +#define HFCPCI_CHIP_ID 0x58 +#define HFCPCI_CIRM 0x60 +#define HFCPCI_CTMT 0x64 +#define HFCPCI_INT_M1 0x68 +#define HFCPCI_INT_M2 0x6C +#define HFCPCI_INT_S1 0x78 +#define HFCPCI_INT_S2 0x7C +#define HFCPCI_STATUS 0x70 + +/* S/T section registers */ + +#define HFCPCI_STATES 0xC0 +#define HFCPCI_SCTRL 0xC4 +#define HFCPCI_SCTRL_E 0xC8 +#define HFCPCI_SCTRL_R 0xCC +#define HFCPCI_SQ 0xD0 +#define HFCPCI_CLKDEL 0xDC +#define HFCPCI_B1_REC 0xF0 +#define HFCPCI_B1_SEND 0xF0 +#define HFCPCI_B2_REC 0xF4 +#define HFCPCI_B2_SEND 0xF4 +#define HFCPCI_D_REC 0xF8 +#define HFCPCI_D_SEND 0xF8 +#define HFCPCI_E_REC 0xFC + + +/* bits in status register (READ) */ +#define HFCPCI_PCI_PROC 0x02 +#define HFCPCI_NBUSY 0x04 +#define HFCPCI_TIMER_ELAP 0x10 +#define HFCPCI_STATINT 0x20 +#define HFCPCI_FRAMEINT 0x40 +#define HFCPCI_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define HFCPCI_CLTIMER 0x80 +#define HFCPCI_TIM3_125 0x04 +#define HFCPCI_TIM25 0x10 +#define HFCPCI_TIM50 0x14 +#define HFCPCI_TIM400 0x18 +#define HFCPCI_TIM800 0x1C +#define HFCPCI_AUTO_TIMER 0x20 +#define HFCPCI_TRANSB2 0x02 +#define HFCPCI_TRANSB1 0x01 + +/* bits in CIRM (Write) */ +#define HFCPCI_AUX_MSK 0x07 +#define HFCPCI_RESET 0x08 +#define HFCPCI_B1_REV 0x40 +#define HFCPCI_B2_REV 0x80 + +/* bits in INT_M1 and INT_S1 */ +#define HFCPCI_INTS_B1TRANS 0x01 +#define HFCPCI_INTS_B2TRANS 0x02 +#define HFCPCI_INTS_DTRANS 0x04 +#define HFCPCI_INTS_B1REC 0x08 +#define HFCPCI_INTS_B2REC 0x10 +#define HFCPCI_INTS_DREC 0x20 +#define HFCPCI_INTS_L1STATE 0x40 +#define HFCPCI_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define HFCPCI_PROC_TRANS 0x01 +#define HFCPCI_GCI_I_CHG 0x02 +#define HFCPCI_GCI_MON_REC 0x04 +#define HFCPCI_IRQ_ENABLE 0x08 +#define HFCPCI_PMESEL 0x80 + +/* bits in STATES */ +#define HFCPCI_STATE_MSK 0x0F +#define HFCPCI_LOAD_STATE 0x10 +#define HFCPCI_ACTIVATE 0x20 +#define HFCPCI_DO_ACTION 0x40 +#define HFCPCI_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define HFCPCI_MASTER 0x01 +#define HFCPCI_SLAVE 0x00 +/* remaining bits are for codecs control */ + +/* bits in HFCD_SCTRL */ +#define SCTRL_B1_ENA 0x01 +#define SCTRL_B2_ENA 0x02 +#define SCTRL_MODE_TE 0x00 +#define SCTRL_MODE_NT 0x04 +#define SCTRL_LOW_PRIO 0x08 +#define SCTRL_SQ_ENA 0x10 +#define SCTRL_TEST 0x20 +#define SCTRL_NONE_CAP 0x40 +#define SCTRL_PWR_DOWN 0x80 + +/* bits in SCTRL_E */ +#define HFCPCI_AUTO_AWAKE 0x01 +#define HFCPCI_DBIT_1 0x04 +#define HFCPCI_IGNORE_COL 0x08 +#define HFCPCI_CHG_B1_B2 0x80 + +/* bits in FIFO_EN register */ +#define HFCPCI_FIFOEN_B1 0x03 +#define HFCPCI_FIFOEN_B2 0x0C +#define HFCPCI_FIFOEN_DTX 0x10 +#define HFCPCI_FIFOEN_DRX 0x20 +#define HFCPCI_FIFOEN_B1TX 0x01 +#define HFCPCI_FIFOEN_B1RX 0x02 +#define HFCPCI_FIFOEN_B2TX 0x04 +#define HFCPCI_FIFOEN_B2RX 0x08 + +/* + * thresholds for transparent B-channel mode + * change mask and threshold simultaneously + */ +#define HFCPCI_BTRANS_THRESHOLD 128 +#define HFCPCI_BTRANS_THRESMASK 0x00 + +#define CLKDEL_TE 0x0e /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + +#define MAX_D_FRAMES 0x10 +#define MAX_B_FRAMES 0x20 +#define B_FIFO_START 0x0200 +#define B_FIFO_END 0x2000 +#define B_FIFO_SIZE (B_FIFO_END - B_FIFO_START) +#define D_FIFO_START 0x0000 +#define D_FIFO_END 0x0200 +#define D_FIFO_SIZE (D_FIFO_END - D_FIFO_START) + +// ---------------------------------------------------------------------- +// push messages to the upper layers + +static inline void D_L1L2(struct hfcpci_adapter *adapter, int pr, void *arg) +{ + struct hisax_if *ifc = (struct hisax_if *) &adapter->d_if; + + DBG(DBG_PR, "pr %#x", pr); + ifc->l1l2(ifc, pr, arg); +} + +static inline void B_L1L2(struct hfcpci_bcs *bcs, int pr, void *arg) +{ + struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if; + + DBG(DBG_PR, "pr %#x", pr); + ifc->l1l2(ifc, pr, arg); +} + +// ---------------------------------------------------------------------- +// MMIO + +static inline void +hfcpci_writeb(struct hfcpci_adapter *adapter, u8 b, unsigned char offset) +{ + writeb(b, adapter->mmio + offset); +} + +static inline u8 +hfcpci_readb(struct hfcpci_adapter *adapter, unsigned char offset) +{ + return readb(adapter->mmio + offset); +} + +// ---------------------------------------------------------------------- +// magic to define the various F/Z counter accesses + +#define DECL_B_F(r, f) \ +static inline u8 \ +get_b_##r##_##f (struct hfcpci_bcs *bcs) \ +{ \ + u16 off = bcs->channel ? OFF_B2_##r##_##f : OFF_B1_##r##_##f; \ + \ + return *(bcs->adapter->fifo + off); \ +} \ + \ +static inline void \ +set_b_##r##_##f (struct hfcpci_bcs *bcs, u8 f) \ +{ \ + u16 off = bcs->channel ? OFF_B2_##r##_##f : OFF_B1_##r##_##f; \ + \ + *(bcs->adapter->fifo + off) = f; \ +} + +#define OFF_B1_rx_f1 0x6080 +#define OFF_B2_rx_f1 0x6180 +#define OFF_B1_rx_f2 0x6081 +#define OFF_B2_rx_f2 0x6181 + +#define OFF_B1_tx_f1 0x2080 +#define OFF_B2_tx_f1 0x2180 +#define OFF_B1_tx_f2 0x2081 +#define OFF_B2_tx_f2 0x2181 + +DECL_B_F(rx, f1) +DECL_B_F(rx, f2) +DECL_B_F(tx, f1) +DECL_B_F(tx, f2) + +#undef DECL_B_F + +#define DECL_B_Z(r, z) \ +static inline u16 \ +get_b_##r##_##z (struct hfcpci_bcs *bcs, u8 f) \ +{ \ + u16 off = bcs->channel ? OFF_B2_##r##_##z : OFF_B1_##r##_##z; \ + \ + return le16_to_cpu(*((u16 *) (bcs->adapter->fifo + off + f * 4))); \ +} \ + \ +static inline void \ +set_b_##r##_##z(struct hfcpci_bcs *bcs, u8 f, u16 z) \ +{ \ + u16 off = bcs->channel ? OFF_B2_##r##_##z : OFF_B1_##r##_##z; \ + \ + *((u16 *) (bcs->adapter->fifo + off + f * 4)) = cpu_to_le16(z); \ +} + +#define OFF_B1_rx_z1 0x6000 +#define OFF_B2_rx_z1 0x6100 +#define OFF_B1_rx_z2 0x6002 +#define OFF_B2_rx_z2 0x6102 + +#define OFF_B1_tx_z1 0x2000 +#define OFF_B2_tx_z1 0x2100 +#define OFF_B1_tx_z2 0x2002 +#define OFF_B2_tx_z2 0x2102 + +DECL_B_Z(rx, z1) +DECL_B_Z(rx, z2) +DECL_B_Z(tx, z1) +DECL_B_Z(tx, z2) + +#undef DECL_B_Z + +#define DECL_D_F(r, f) \ +static inline u8 \ +get_d_##r##_##f (struct hfcpci_adapter *adapter) \ +{ \ + u16 off = OFF_D_##r##_##f; \ + \ + return *(adapter->fifo + off) & 0xf; \ +} \ + \ +static inline void \ +set_d_##r##_##f (struct hfcpci_adapter *adapter, u8 f) \ +{ \ + u16 off = OFF_D_##r##_##f; \ + \ + *(adapter->fifo + off) = f | 0x10; \ +} + +#define OFF_D_rx_f1 0x60a0 +#define OFF_D_rx_f2 0x60a1 + +#define OFF_D_tx_f1 0x20a0 +#define OFF_D_tx_f2 0x20a1 + +DECL_D_F(rx, f1) +DECL_D_F(rx, f2) +DECL_D_F(tx, f1) +DECL_D_F(tx, f2) + +#undef DECL_D_F + +#define DECL_D_Z(r, z) \ +static inline u16 \ +get_d_##r##_##z (struct hfcpci_adapter *adapter, u8 f) \ +{ \ + u16 off = OFF_D_##r##_##z; \ + \ + return le16_to_cpu(*((u16 *) (adapter->fifo + off + (f | 0x10) * 4)));\ +} \ + \ +static inline void \ +set_d_##r##_##z(struct hfcpci_adapter *adapter, u8 f, u16 z) \ +{ \ + u16 off = OFF_D_##r##_##z; \ + \ + *((u16 *) (adapter->fifo + off + (f | 0x10) * 4)) = cpu_to_le16(z); \ +} + +#define OFF_D_rx_z1 0x6080 +#define OFF_D_rx_z2 0x6082 + +#define OFF_D_tx_z1 0x2080 +#define OFF_D_tx_z2 0x2082 + +DECL_D_Z(rx, z1) +DECL_D_Z(rx, z2) +DECL_D_Z(tx, z1) +DECL_D_Z(tx, z2) + +#undef DECL_B_Z + +// ---------------------------------------------------------------------- +// fill b / d fifos + +static inline void +hfcpci_fill_d_fifo(struct hfcpci_adapter *adapter) +{ + u8 f1, f2; + u16 z1, z2; + int cnt, fcnt; + char *fifo_adr = adapter->fifo; + struct sk_buff *tx_skb = adapter->tx_skb; + + f1 = get_d_tx_f1(adapter); + f2 = get_d_tx_f2(adapter); + DBG(DBG_D_XMIT, "f1 %#x f2 %#x", f1, f2); + + fcnt = f1 - f2; + if (fcnt < 0) + fcnt += MAX_D_FRAMES; + + if (fcnt) { + printk("BUG\n"); + return; + } + + z1 = get_d_tx_z1(adapter, f1); + z2 = get_d_tx_z2(adapter, f1); //XXX + DBG(DBG_D_XMIT, "z1 %#x z2 %#x", z1, z2); + + cnt = z2 - z1; + if (cnt <= 0) + cnt += D_FIFO_SIZE; + + if (tx_skb->len > cnt) { + printk("BUG\n"); + return; + } + + cnt = tx_skb->len; + if (z1 + cnt <= D_FIFO_END) { + memcpy(fifo_adr + z1, tx_skb->data, cnt); + } else { + memcpy(fifo_adr + z1, tx_skb->data, D_FIFO_END - z1); + memcpy(fifo_adr + D_FIFO_START, + tx_skb->data + (D_FIFO_END - z1), + cnt - (D_FIFO_END - z1)); + } + z1 += cnt; + if (z1 >= D_FIFO_END) + z1 -= D_FIFO_SIZE; + + f1 = (f1 + 1) & (MAX_D_FRAMES - 1); + mb(); + set_d_tx_z1(adapter, f1, z1); + mb(); + set_d_tx_f1(adapter, f1); +} + +static inline void +hfcpci_fill_b_fifo_hdlc(struct hfcpci_bcs *bcs) +{ + u8 f1, f2; + u16 z1, z2; + int cnt, fcnt; + char *fifo_adr = bcs->adapter->fifo + (bcs->channel ? 0x2000 : 0x0000); + struct sk_buff *tx_skb = bcs->tx_skb; + + f1 = get_b_tx_f1(bcs); + f2 = get_b_tx_f2(bcs); + DBG(DBG_B_XMIT, "f1 %#x f2 %#x", f1, f2); + + fcnt = f1 - f2; + if (fcnt < 0) + fcnt += MAX_B_FRAMES; + + if (fcnt) { + printk("BUG\n"); + return; + } + + z1 = get_b_tx_z1(bcs, f1); + z2 = get_b_tx_z2(bcs, f1); //XXX + DBG(DBG_B_XMIT, "z1 %#x z2 %#x", z1, z2); + + cnt = z2 - z1; + if (cnt <= 0) + cnt += B_FIFO_SIZE; + + if (tx_skb->len > cnt) { + printk("BUG\n"); + return; + } + + cnt = tx_skb->len; + if (z1 + cnt <= B_FIFO_END) { + memcpy(fifo_adr + z1, tx_skb->data, cnt); + } else { + memcpy(fifo_adr + z1, tx_skb->data, B_FIFO_END - z1); + memcpy(fifo_adr + B_FIFO_START, + tx_skb->data + (B_FIFO_END - z1), + cnt - (B_FIFO_END - z1)); + } + z1 += cnt; + if (z1 >= B_FIFO_END) + z1 -= B_FIFO_SIZE; + + f1 = (f1 + 1) & (MAX_B_FRAMES - 1); + mb(); + set_b_tx_z1(bcs, f1, z1); + mb(); + set_b_tx_f1(bcs, f1); +} + +static inline void +hfcpci_fill_b_fifo_trans(struct hfcpci_bcs *bcs) +{ + int cnt; + char *fifo_adr = bcs->adapter->fifo + (bcs->channel ? 0x2000 : 0x0000); + struct sk_buff *tx_skb = bcs->tx_skb; + u8 f1, f2; + u16 z1, z2; + + f1 = get_b_tx_f1(bcs); + f2 = get_b_tx_f2(bcs); + + if (f1 != f2) + BUG(); + + z1 = get_b_tx_z1(bcs, f1); + z2 = get_b_tx_z2(bcs, f1); + + cnt = z2 - z1; + if (cnt <= 0) + cnt += B_FIFO_SIZE; + + if (tx_skb->len > cnt) + BUG(); + + if (z1 + cnt <= B_FIFO_END) { + memcpy(fifo_adr + z1, tx_skb->data, cnt); + } else { + memcpy(fifo_adr + z1, tx_skb->data, B_FIFO_END - z1); + memcpy(fifo_adr + B_FIFO_START, + tx_skb->data + (B_FIFO_END - z1), + cnt - (B_FIFO_END - z1)); + } + z1 += cnt; + if (z1 >= B_FIFO_END) + z1 -= B_FIFO_SIZE; + + mb(); + set_b_tx_z1(bcs, f1, z1); +} + +static inline void +hfcpci_fill_b_fifo(struct hfcpci_bcs *bcs) +{ + if (!bcs->tx_skb) { + DBG(DBG_WARN, "?"); + return; + } + + switch (bcs->mode) { + case L1_MODE_TRANS: + hfcpci_fill_b_fifo_trans(bcs); + break; + case L1_MODE_HDLC: + hfcpci_fill_b_fifo_hdlc(bcs); + break; + default: + DBG(DBG_WARN, "?"); + } +} + +static void hfcpci_clear_b_rx_fifo(struct hfcpci_bcs *bcs); +static void hfcpci_clear_b_tx_fifo(struct hfcpci_bcs *bcs); + +static void +hfcpci_b_mode(struct hfcpci_bcs *bcs, int mode) +{ + struct hfcpci_adapter *adapter = bcs->adapter; + + DBG(DBG_B_XMIT, "B%d mode %d --> %d", + bcs->channel + 1, bcs->mode, mode); + + if (bcs->mode == mode) + return; + + switch (mode) { + case L1_MODE_NULL: + if (bcs->channel == 0) { + adapter->sctrl &= ~SCTRL_B1_ENA; + adapter->sctrl_r &= ~SCTRL_B1_ENA; + adapter->fifo_en &= ~HFCPCI_FIFOEN_B1; + adapter->int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC); + } else { + adapter->sctrl &= ~SCTRL_B2_ENA; + adapter->sctrl_r &= ~SCTRL_B2_ENA; + adapter->fifo_en &= ~HFCPCI_FIFOEN_B2; + adapter->int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC); + } + break; + case L1_MODE_TRANS: + case L1_MODE_HDLC: + hfcpci_clear_b_rx_fifo(bcs); + hfcpci_clear_b_tx_fifo(bcs); + if (bcs->channel == 0) { + adapter->sctrl |= SCTRL_B1_ENA; + adapter->sctrl_r |= SCTRL_B1_ENA; + adapter->fifo_en |= HFCPCI_FIFOEN_B1; + adapter->int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC); + + if (mode == L1_MODE_TRANS) + adapter->ctmt |= 1; + else + adapter->ctmt &= ~1; + + } else { + adapter->sctrl |= SCTRL_B2_ENA; + adapter->sctrl_r |= SCTRL_B2_ENA; + adapter->fifo_en |= HFCPCI_FIFOEN_B2; + adapter->int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC); + + if (mode == L1_MODE_TRANS) + adapter->ctmt |= 2; + else + adapter->ctmt &= ~2; + + } + break; + } + hfcpci_writeb(adapter, adapter->int_m1, HFCPCI_INT_M1); + hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN); + hfcpci_writeb(adapter, adapter->sctrl, HFCPCI_SCTRL); + hfcpci_writeb(adapter, adapter->sctrl_r, HFCPCI_SCTRL_R); + hfcpci_writeb(adapter, adapter->ctmt, HFCPCI_CTMT); + hfcpci_writeb(adapter, adapter->conn, HFCPCI_CONNECT); + + bcs->mode = mode; +} + +// ---------------------------------------------------------------------- +// Layer 1 state machine + +static struct Fsm l1fsm; + +enum { + ST_L1_F0, + ST_L1_F2, + ST_L1_F3, + ST_L1_F4, + ST_L1_F5, + ST_L1_F6, + ST_L1_F7, + ST_L1_F8, +}; + +#define L1_STATE_COUNT (ST_L1_F8+1) + +static char *strL1State[] = +{ + "ST_L1_F0", + "ST_L1_F2", + "ST_L1_F3", + "ST_L1_F4", + "ST_L1_F5", + "ST_L1_F6", + "ST_L1_F7", + "ST_L1_F8", +}; + +enum { + EV_PH_F0, + EV_PH_1, + EV_PH_F2, + EV_PH_F3, + EV_PH_F4, + EV_PH_F5, + EV_PH_F6, + EV_PH_F7, + EV_PH_F8, + EV_PH_ACTIVATE_REQ, + EV_PH_DEACTIVATE_REQ, + EV_TIMER3, +}; + +#define L1_EVENT_COUNT (EV_TIMER3 + 1) + +static char *strL1Event[] = +{ + "EV_PH_F0", + "EV_PH_1", + "EV_PH_F2", + "EV_PH_F3", + "EV_PH_F4", + "EV_PH_F5", + "EV_PH_F6", + "EV_PH_F7", + "EV_PH_F8", + "EV_PH_ACTIVATE_REQ", + "EV_PH_DEACTIVATE_REQ", + "EV_TIMER3", +}; + +static void l1_ignore(struct FsmInst *fi, int event, void *arg) +{ +} + +static void l1_go_f3(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F3); +} + +static void l1_go_f3_deact_ind(struct FsmInst *fi, int event, void *arg) +{ + struct hfcpci_adapter *adapter = fi->userdata; + + FsmChangeState(fi, ST_L1_F3); + D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL); +} + +static void l1_go_f4(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F3); +} + +static void l1_go_f5(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F3); +} + +static void l1_go_f6(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F6); +} + +static void l1_go_f6_deact_ind(struct FsmInst *fi, int event, void *arg) +{ + struct hfcpci_adapter *adapter = fi->userdata; + + FsmChangeState(fi, ST_L1_F6); + D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL); +} + +static void l1_go_f7(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F7); +} + +static void l1_go_f7_act_ind(struct FsmInst *fi, int event, void *arg) +{ + struct hfcpci_adapter *adapter = fi->userdata; + + FsmChangeState(fi, ST_L1_F7); + D_L1L2(adapter, PH_ACTIVATE | INDICATION, NULL); +} + +static void l1_go_f8(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F8); +} + +static void l1_go_f8_deact_ind(struct FsmInst *fi, int event, void *arg) +{ + struct hfcpci_adapter *adapter = fi->userdata; + + FsmChangeState(fi, ST_L1_F8); + D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL); +} + +static void l1_act_req(struct FsmInst *fi, int event, void *arg) +{ + struct hfcpci_adapter *adapter = fi->userdata; + + hfcpci_writeb(adapter, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION, HFCPCI_STATES); +} + +static struct FsmNode L1FnList[] __initdata = +{ + {ST_L1_F2, EV_PH_F3, l1_go_f3}, + {ST_L1_F2, EV_PH_F6, l1_go_f6}, + {ST_L1_F2, EV_PH_F7, l1_go_f7_act_ind}, + + {ST_L1_F3, EV_PH_F3, l1_ignore}, + {ST_L1_F3, EV_PH_F4, l1_go_f4}, + {ST_L1_F3, EV_PH_F5, l1_go_f5}, + {ST_L1_F3, EV_PH_F6, l1_go_f6}, + {ST_L1_F3, EV_PH_F7, l1_go_f7_act_ind}, + {ST_L1_F3, EV_PH_ACTIVATE_REQ, l1_act_req}, + + {ST_L1_F4, EV_PH_F7, l1_ignore}, + {ST_L1_F4, EV_PH_F3, l1_go_f3}, + {ST_L1_F4, EV_PH_F5, l1_go_f5}, + {ST_L1_F4, EV_PH_F6, l1_go_f6}, + {ST_L1_F4, EV_PH_F7, l1_go_f7}, + + {ST_L1_F5, EV_PH_F7, l1_ignore}, + {ST_L1_F5, EV_PH_F3, l1_go_f3}, + {ST_L1_F5, EV_PH_F6, l1_go_f6}, + {ST_L1_F5, EV_PH_F7, l1_go_f7}, + + {ST_L1_F6, EV_PH_F7, l1_ignore}, + {ST_L1_F6, EV_PH_F3, l1_go_f3}, + {ST_L1_F6, EV_PH_F7, l1_go_f7_act_ind}, + {ST_L1_F6, EV_PH_F8, l1_go_f8}, + + {ST_L1_F7, EV_PH_F7, l1_ignore}, + {ST_L1_F7, EV_PH_F3, l1_go_f3_deact_ind}, + {ST_L1_F7, EV_PH_F6, l1_go_f6_deact_ind}, + {ST_L1_F7, EV_PH_F8, l1_go_f8_deact_ind}, + + {ST_L1_F8, EV_PH_F7, l1_ignore}, + {ST_L1_F8, EV_PH_F3, l1_go_f3}, + {ST_L1_F8, EV_PH_F6, l1_go_f6}, + {ST_L1_F8, EV_PH_F7, l1_go_f7_act_ind}, + +}; + +static void l1m_debug(struct FsmInst *fi, char *fmt, ...) +{ + va_list args; + char buf[256]; + + va_start(args, fmt); + vsprintf(buf, fmt, args); + DBG(DBG_L1M, "%s", buf); + va_end(args); +} + +// ---------------------------------------------------------------------- +// clear FIFOs + +static void +hfcpci_clear_d_rx_fifo(struct hfcpci_adapter *adapter) +{ + u8 fifo_state; + + DBG(DBG_D_RECV, ""); + + fifo_state = adapter->fifo_en & HFCPCI_FIFOEN_DRX; + + if (fifo_state) { // enabled + // XXX locking + adapter->fifo_en &= ~fifo_state; + hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN); + } + + adapter->last_fcnt = 0; + + set_d_rx_z1(adapter, MAX_D_FRAMES - 1, D_FIFO_END - 1); + set_d_rx_z2(adapter, MAX_D_FRAMES - 1, D_FIFO_END - 1); + mb(); + set_d_rx_f1(adapter, MAX_D_FRAMES - 1); + set_d_rx_f2(adapter, MAX_D_FRAMES - 1); + mb(); + + if (fifo_state) { + adapter->fifo_en |= fifo_state; + hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN); + } +} + +static void +hfcpci_clear_b_rx_fifo(struct hfcpci_bcs *bcs) +{ + struct hfcpci_adapter *adapter = bcs->adapter; + int nr = bcs->channel; + u8 fifo_state; + + DBG(DBG_B_RECV, ""); + + fifo_state = adapter->fifo_en & + (nr ? HFCPCI_FIFOEN_B2RX : HFCPCI_FIFOEN_B1RX); + + if (fifo_state) { // enabled + adapter->fifo_en &= ~fifo_state; + hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN); + } + + bcs->last_fcnt = 0; + + set_b_rx_z1(bcs, MAX_B_FRAMES - 1, B_FIFO_END - 1); + set_b_rx_z2(bcs, MAX_B_FRAMES - 1, B_FIFO_END - 1); + mb(); + set_b_rx_f1(bcs, MAX_B_FRAMES - 1); + set_b_rx_f2(bcs, MAX_B_FRAMES - 1); + mb(); + + if (fifo_state) { + adapter->fifo_en |= fifo_state; + hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN); + } +} + +// XXX clear d_tx_fifo? + +static void +hfcpci_clear_b_tx_fifo(struct hfcpci_bcs *bcs) +{ + struct hfcpci_adapter *adapter = bcs->adapter; + int nr = bcs->channel; + u8 fifo_state; + + fifo_state = adapter->fifo_en & + (nr ? HFCPCI_FIFOEN_B2TX : HFCPCI_FIFOEN_B1TX); + + if (fifo_state) { // enabled + adapter->fifo_en &= ~fifo_state; + hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN); + } + + bcs->last_fcnt = 0; + + set_b_rx_z1(bcs, MAX_B_FRAMES - 1, B_FIFO_END - 1); + set_b_rx_z2(bcs, MAX_B_FRAMES - 1, B_FIFO_END - 1); + mb(); + set_b_rx_f1(bcs, MAX_B_FRAMES - 1); + set_b_rx_f2(bcs, MAX_B_FRAMES - 1); + mb(); + + if (fifo_state) { + adapter->fifo_en |= fifo_state; + hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN); + } +} + +// ---------------------------------------------------------------------- +// receive messages from upper layers + +static void +hfcpci_d_l2l1(struct hisax_if *ifc, int pr, void *arg) +{ + struct hfcpci_adapter *adapter = ifc->priv; + struct sk_buff *skb = arg; + + DBG(DBG_PR, "pr %#x", pr); + + switch (pr) { + case PH_ACTIVATE | REQUEST: + FsmEvent(&adapter->l1m, EV_PH_ACTIVATE_REQ, NULL); + break; + case PH_DEACTIVATE | REQUEST: + FsmEvent(&adapter->l1m, EV_PH_DEACTIVATE_REQ, NULL); + break; + case PH_DATA | REQUEST: + DBG(DBG_PR, "PH_DATA REQUEST len %d", skb->len); + DBG_SKB(DBG_D_XMIT, skb); + if (adapter->l1m.state != ST_L1_F7) { + DBG(DBG_WARN, "L1 wrong state %d", adapter->l1m.state); + break; + } + if (adapter->tx_skb) + BUG(); + + adapter->tx_skb = skb; + hfcpci_fill_d_fifo(adapter); + break; + } +} + +static void +hfcpci_b_l2l1(struct hisax_if *ifc, int pr, void *arg) +{ + struct hfcpci_bcs *bcs = ifc->priv; + struct sk_buff *skb = arg; + int mode; + + DBG(DBG_PR, "pr %#x", pr); + + switch (pr) { + case PH_DATA | REQUEST: + if (bcs->tx_skb) + BUG(); + + bcs->tx_skb = skb; + DBG_SKB(DBG_B_XMIT, skb); + hfcpci_fill_b_fifo(bcs); + break; + case PH_ACTIVATE | REQUEST: + mode = (int) arg; + DBG(DBG_PR,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode); + hfcpci_b_mode(bcs, mode); + B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL); + break; + case PH_DEACTIVATE | REQUEST: + DBG(DBG_PR,"B%d,PH_DEACTIVATE_REQUEST", bcs->channel + 1); + hfcpci_b_mode(bcs, L1_MODE_NULL); + B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL); + break; + } +} + +// ---------------------------------------------------------------------- +// receive IRQ + +static inline void +hfcpci_d_recv_irq(struct hfcpci_adapter *adapter) +{ + struct sk_buff *skb; + char *fifo_adr = adapter->fifo + 0x4000; + char *p; + int cnt, fcnt; + int loop = 5; + u8 f1, f2; + u16 z1, z2; + + while (loop-- > 0) { + f1 = get_d_rx_f1(adapter); + f2 = get_d_rx_f2(adapter); + DBG(DBG_D_RECV, "f1 %#x f2 %#x", f1, f2); + + fcnt = f1 - f2; + if (fcnt < 0) + fcnt += 16; + + if (!fcnt) + return; + + if (fcnt < adapter->last_fcnt) + /* overrun */ + hfcpci_clear_d_rx_fifo(adapter); + // XXX init last_fcnt + + z1 = get_d_rx_z1(adapter, f2); + z2 = get_d_rx_z2(adapter, f2); + DBG(DBG_D_RECV, "z1 %#x z2 %#x", z1, z2); + + cnt = z1 - z2; + if (cnt < 0) + cnt += D_FIFO_SIZE; + cnt++; + + if (cnt < 4) { + DBG(DBG_WARN, "frame too short"); + goto next; + } + if (fifo_adr[z1] != 0) { + DBG(DBG_WARN, "CRC error"); + goto next; + } + cnt -= 3; + skb = dev_alloc_skb(cnt); + if (!skb) { + DBG(DBG_WARN, "no mem"); + goto next; + } + p = skb_put(skb, cnt); + if (z2 + cnt <= D_FIFO_END) { + memcpy(p, fifo_adr + z2, cnt); + } else { + memcpy(p, fifo_adr + z2, D_FIFO_END - z2); + memcpy(p + (D_FIFO_END - z2), fifo_adr + D_FIFO_START, + cnt - (D_FIFO_END - z2)); + } + + DBG_SKB(DBG_D_RECV, skb); + D_L1L2(adapter, PH_DATA | INDICATION, skb); + + next: + if (++z1 >= D_FIFO_END) + z1 -= D_FIFO_START; + + f2 = (f2 + 1) & (MAX_D_FRAMES - 1); + mb(); + set_d_rx_z2(adapter, f2, z1); + mb(); + set_d_rx_f2(adapter, f2); + + adapter->last_fcnt = fcnt - 1; + } +} + +static inline void +hfcpci_b_recv_hdlc_irq(struct hfcpci_adapter *adapter, int nr) +{ + struct hfcpci_bcs *bcs = &adapter->bcs[nr]; + struct sk_buff *skb; + char *fifo_adr = adapter->fifo + (nr ? 0x6000 : 0x4000); + char *p; + int cnt, fcnt; + int loop = 5; + u8 f1, f2; + u16 z1, z2; + + while (loop-- > 0) { + f1 = get_b_rx_f1(bcs); + f2 = get_b_rx_f2(bcs); + DBG(DBG_B_RECV, "f1 %d f2 %d", f1, f2); + + fcnt = f1 - f2; + if (fcnt < 0) + fcnt += 32; + + if (!fcnt) + return; + + if (fcnt < bcs->last_fcnt) + /* overrun */ + hfcpci_clear_b_rx_fifo(bcs); + // XXX init last_fcnt + + z1 = get_b_rx_z1(bcs, f2); + z2 = get_b_rx_z2(bcs, f2); + DBG(DBG_B_RECV, "z1 %d z2 %d", z1, z2); + + cnt = z1 - z2; + if (cnt < 0) + cnt += B_FIFO_SIZE; + cnt++; + + if (cnt < 4) { + DBG(DBG_WARN, "frame too short"); + goto next; + } + if (fifo_adr[z1] != 0) { + DBG(DBG_WARN, "CRC error"); + goto next; + } + cnt -= 3; + skb = dev_alloc_skb(cnt); + if (!skb) { + DBG(DBG_WARN, "no mem"); + goto next; + } + p = skb_put(skb, cnt); + if (z2 + cnt <= B_FIFO_END) { + memcpy(p, fifo_adr + z2, cnt); + } else { + memcpy(p, fifo_adr + z2, B_FIFO_END - z2); + memcpy(p + (B_FIFO_END - z2), fifo_adr + B_FIFO_START, + cnt - (B_FIFO_END - z2)); + } + + DBG_SKB(DBG_B_RECV, skb); + B_L1L2(bcs, PH_DATA | INDICATION, skb); + + next: + if (++z1 >= B_FIFO_END) + z1 -= B_FIFO_SIZE; + + f2 = (f2 + 1) & (MAX_B_FRAMES - 1); + mb(); + set_b_rx_z2(bcs, f2, z1); + mb(); + set_b_rx_f2(bcs, f2); + + bcs->last_fcnt = fcnt - 1; + } +} + +static inline void +hfcpci_b_recv_trans_irq(struct hfcpci_adapter *adapter, int nr) +{ + struct hfcpci_bcs *bcs = &adapter->bcs[nr]; + struct sk_buff *skb; + char *fifo_adr = adapter->fifo + (nr ? 0x6000 : 0x4000); + char *p; + int cnt; + int loop = 5; + u8 f1, f2; + u16 z1, z2; + + f1 = get_b_rx_f1(bcs); + f2 = get_b_rx_f2(bcs); + + if (f1 != f2) + BUG(); + + while (loop-- > 0) { + z1 = get_b_rx_z1(bcs, f2); + z2 = get_b_rx_z2(bcs, f2); + + cnt = z1 - z2; + if (!cnt) + /* no data available */ + return; + + if (cnt < 0) + cnt += B_FIFO_SIZE; + + if (cnt > HFCPCI_BTRANS_THRESHOLD) + cnt = HFCPCI_BTRANS_THRESHOLD; + + skb = dev_alloc_skb(cnt); + if (!skb) { + DBG(DBG_WARN, "no mem"); + goto next; + } + + p = skb_put(skb, cnt); + if (z2 + cnt <= 0x2000) { + memcpy(p, fifo_adr + z2, cnt); + } else { + memcpy(p, fifo_adr + z2, 0x2000 - z2); + p += 0x2000 - z2; + memcpy(p, fifo_adr + 0x200, cnt - (0x2000 - z2)); + } + + DBG_SKB(DBG_B_RECV, skb); + B_L1L2(bcs, PH_DATA | INDICATION, skb); + + next: + z2 += cnt; + if (z2 >= 0x2000) + z2 -= B_FIFO_SIZE; + + mb(); + set_b_rx_z2(bcs, f2, z2); + // XXX always receive buffers of a given size + } +} + +static inline void +hfcpci_b_recv_irq(struct hfcpci_adapter *adapter, int nr) +{ + DBG(DBG_B_RECV, ""); + + switch (adapter->bcs[nr].mode) { + case L1_MODE_NULL: + DBG(DBG_WARN, "?"); + break; + + case L1_MODE_HDLC: + hfcpci_b_recv_hdlc_irq(adapter, nr); + break; + + case L1_MODE_TRANS: + hfcpci_b_recv_trans_irq(adapter, nr); + break; + } +} + +// ---------------------------------------------------------------------- +// transmit IRQ + +// XXX make xmit FIFO deeper than 1 + +static inline void +hfcpci_d_xmit_irq(struct hfcpci_adapter *adapter) +{ + struct sk_buff *skb; + + DBG(DBG_D_XMIT, ""); + + skb = adapter->tx_skb; + if (!skb) { + DBG(DBG_WARN, "?"); + return; + } + + adapter->tx_skb = NULL; + D_L1L2(adapter, PH_DATA | CONFIRM, (void *) skb->truesize); + dev_kfree_skb_irq(skb); +} + +static inline void +hfcpci_b_xmit_irq(struct hfcpci_adapter *adapter, int nr) +{ + struct hfcpci_bcs *bcs = &adapter->bcs[nr]; + struct sk_buff *skb; + + DBG(DBG_B_XMIT, ""); + + skb = bcs->tx_skb; + if (!skb) { + DBG(DBG_WARN, "?"); + return; + } + + bcs->tx_skb = NULL; + B_L1L2(bcs, PH_DATA | CONFIRM, (void *) skb->truesize); + dev_kfree_skb_irq(skb); +} + +// ---------------------------------------------------------------------- +// Layer 1 state change IRQ + +static inline void +hfcpci_state_irq(struct hfcpci_adapter *adapter) +{ + u8 val; + + val = hfcpci_readb(adapter, HFCPCI_STATES); + DBG(DBG_L1M, "STATES %#x", val); + FsmEvent(&adapter->l1m, val & 0xf, NULL); +} + +// ---------------------------------------------------------------------- +// Timer IRQ + +static inline void +hfcpci_timer_irq(struct hfcpci_adapter *adapter) +{ + hfcpci_writeb(adapter, adapter->ctmt | HFCPCI_CLTIMER, HFCPCI_CTMT); +} + +// ---------------------------------------------------------------------- +// IRQ handler + +static void +hfcpci_irq(int intno, void *dev, struct pt_regs *regs) +{ + struct hfcpci_adapter *adapter = dev; + int loop = 15; + u8 val, stat; + + if (!(adapter->int_m2 & 0x08)) + return; /* not initialised */ // XX + + stat = hfcpci_readb(adapter, HFCPCI_STATUS); + if (!(stat & HFCPCI_ANYINT)) + return; + + spin_lock(&adapter->hw_lock); + while (loop-- > 0) { + val = hfcpci_readb(adapter, HFCPCI_INT_S1); + DBG(DBG_IRQ, "stat %02x s1 %02x", stat, val); + val &= adapter->int_m1; + + if (!val) + break; + + if (val & 0x08) + hfcpci_b_recv_irq(adapter, 0); + + if (val & 0x10) + hfcpci_b_recv_irq(adapter, 1); + + if (val & 0x01) + hfcpci_b_xmit_irq(adapter, 0); + + if (val & 0x02) + hfcpci_b_xmit_irq(adapter, 1); + + if (val & 0x20) + hfcpci_d_recv_irq(adapter); + + if (val & 0x04) + hfcpci_d_xmit_irq(adapter); + + if (val & 0x40) + hfcpci_state_irq(adapter); + + if (val & 0x80) + hfcpci_timer_irq(adapter); + } + spin_unlock(&adapter->hw_lock); +} + +// ---------------------------------------------------------------------- +// reset hardware + +static void +hfcpci_reset(struct hfcpci_adapter *adapter) +{ + /* disable all interrupts */ + adapter->int_m1 = 0; + adapter->int_m2 = 0; + hfcpci_writeb(adapter, adapter->int_m1, HFCPCI_INT_M1); + hfcpci_writeb(adapter, adapter->int_m2, HFCPCI_INT_M2); + + /* reset */ + hfcpci_writeb(adapter, HFCPCI_RESET, HFCPCI_CIRM); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((30 * HZ) / 1000); + hfcpci_writeb(adapter, 0, HFCPCI_CIRM); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((20 * HZ) / 1000); + if (hfcpci_readb(adapter, HFCPCI_STATUS) & 2) // XX + printk(KERN_WARNING "HFC-PCI init bit busy\n"); +} + +// ---------------------------------------------------------------------- +// init hardware + +static void +hfcpci_hw_init(struct hfcpci_adapter *adapter) +{ + adapter->fifo_en = 0x30; /* only D fifos enabled */ // XX + hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN); + + /* no echo connect , threshold */ + adapter->trm = HFCPCI_BTRANS_THRESMASK; + hfcpci_writeb(adapter, adapter->trm, HFCPCI_TRM); + + /* ST-Bit delay for TE-Mode */ + hfcpci_writeb(adapter, CLKDEL_TE, HFCPCI_CLKDEL); + + /* S/T Auto awake */ + adapter->sctrl_e = HFCPCI_AUTO_AWAKE; + hfcpci_writeb(adapter, adapter->sctrl_e, HFCPCI_SCTRL_E); + + /* no exchange */ + adapter->bswapped = 0; + /* we are in TE mode */ + adapter->nt_mode = 0; + + adapter->ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER; + hfcpci_writeb(adapter, adapter->ctmt, HFCPCI_CTMT); + + adapter->int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC | + HFCPCI_INTS_L1STATE; + hfcpci_writeb(adapter, adapter->int_m1, HFCPCI_INT_M1); + + /* clear already pending ints */ + hfcpci_readb(adapter, HFCPCI_INT_S1); + + adapter->l1m.state = 2; + hfcpci_writeb(adapter, HFCPCI_LOAD_STATE | 2, HFCPCI_STATES); // XX /* HFC ST 2 */ + udelay(10); + hfcpci_writeb(adapter, 2, HFCPCI_STATES); /* HFC ST 2 */ + + /* HFC Master Mode */ + adapter->mst_m = HFCPCI_MASTER; + hfcpci_writeb(adapter, adapter->mst_m, HFCPCI_MST_MODE); + + /* set tx_lo mode, error in datasheet ! */ + adapter->sctrl = 0x40; + hfcpci_writeb(adapter, adapter->sctrl, HFCPCI_SCTRL); + + adapter->sctrl_r = 0; + hfcpci_writeb(adapter, adapter->sctrl_r, HFCPCI_SCTRL_R); + + // XXX + /* Init GCI/IOM2 in master mode */ + /* Slots 0 and 1 are set for B-chan 1 and 2 */ + /* D- and monitor/CI channel are not enabled */ + /* STIO1 is used as output for data, B1+B2 from ST->IOM+HFC */ + /* STIO2 is used as data input, B1+B2 from IOM->ST */ + /* ST B-channel send disabled -> continous 1s */ + /* The IOM slots are always enabled */ + adapter->conn = 0; /* set data flow directions */ + hfcpci_writeb(adapter, adapter->conn, HFCPCI_CONNECT); + hfcpci_writeb(adapter, 0x80, HFCPCI_B1_SSL); /* B1-Slot 0 STIO1 out enabled */ + hfcpci_writeb(adapter, 0x81, HFCPCI_B2_SSL); /* B2-Slot 1 STIO1 out enabled */ + hfcpci_writeb(adapter, 0x80, HFCPCI_B1_RSL); /* B1-Slot 0 STIO2 in enabled */ + hfcpci_writeb(adapter, 0x81, HFCPCI_B2_RSL); /* B2-Slot 1 STIO2 in enabled */ + + /* Finally enable IRQ output */ + adapter->int_m2 = HFCPCI_IRQ_ENABLE; + hfcpci_writeb(adapter, adapter->int_m2, HFCPCI_INT_M2); + + hfcpci_readb(adapter, HFCPCI_INT_S2); +} + +// ---------------------------------------------------------------------- +// probe / remove + +static struct hfcpci_adapter * __devinit +new_adapter(struct pci_dev *pdev) +{ + struct hfcpci_adapter *adapter; + struct hisax_b_if *b_if[2]; + int i; + + adapter = kmalloc(sizeof(struct hfcpci_adapter), GFP_KERNEL); + if (!adapter) + return NULL; + + memset(adapter, 0, sizeof(struct hfcpci_adapter)); + + SET_MODULE_OWNER(&adapter->d_if); + adapter->d_if.ifc.priv = adapter; + adapter->d_if.ifc.l2l1 = hfcpci_d_l2l1; + + for (i = 0; i < 2; i++) { + adapter->bcs[i].adapter = adapter; + adapter->bcs[i].channel = i; + adapter->bcs[i].b_if.ifc.priv = &adapter->bcs[i]; + adapter->bcs[i].b_if.ifc.l2l1 = hfcpci_b_l2l1; + } + + pci_set_drvdata(pdev, adapter); + + for (i = 0; i < 2; i++) + b_if[i] = &adapter->bcs[i].b_if; + + hisax_register(&adapter->d_if, b_if, "hfcpci", protocol); + + return adapter; +} + +static void delete_adapter(struct hfcpci_adapter *adapter) +{ + hisax_unregister(&adapter->d_if); + kfree(adapter); +} + +static int __devinit hfcpci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct hfcpci_adapter *adapter; + int retval; + + DBG(DBG_INFO, ""); + retval = -ENOMEM; + adapter = new_adapter(pdev); + if (!adapter) + goto err; + + retval = pci_enable_device(pdev); + if (retval) + goto err_free; + + adapter->irq = pdev->irq; + retval = request_irq(adapter->irq, hfcpci_irq, SA_SHIRQ, + "hfcpci", adapter); + if (retval) + goto err_free; + + retval = -EBUSY; + if (!request_mem_region(pci_resource_start(pdev, 1), 256, "hfcpci")) + goto err_free_irq; + + adapter->mmio = ioremap(pci_resource_start(pdev, 1), 256); // XX pci_io + if (!adapter->mmio) + goto err_release_region; + + /* Allocate 32K for FIFOs */ + if (pci_set_dma_mask(pdev, 0xffffffff)) + goto err_unmap; + + adapter->fifo = pci_alloc_consistent(pdev, 32768, &adapter->fifo_dma); + if (!adapter->fifo) + goto err_unmap; + + pci_write_config_dword(pdev, HFCPCI_MWBA, (u32) adapter->fifo_dma); + pci_set_master(pdev); + + adapter->l1m.fsm = &l1fsm; + adapter->l1m.state = ST_L1_F0; +#ifdef CONFIG_HISAX_DEBUG + adapter->l1m.debug = 1; +#else + adapter->l1m.debug = 0; +#endif + adapter->l1m.userdata = adapter; + adapter->l1m.printdebug = l1m_debug; + FsmInitTimer(&adapter->l1m, &adapter->timer); + + hfcpci_reset(adapter); + hfcpci_hw_init(adapter); + + printk(KERN_INFO "hisax_hfcpci: found adapter %s at %s\n", + (char *) ent->driver_data, pdev->slot_name); + + return 0; + + err_unmap: + iounmap(adapter->mmio); + err_release_region: + release_mem_region(pci_resource_start(pdev, 1), 256); + err_free_irq: + free_irq(adapter->irq, adapter); + err_free: + delete_adapter(adapter); + err: + return retval; +} + +static void __devexit hfcpci_remove(struct pci_dev *pdev) +{ + struct hfcpci_adapter *adapter = pci_get_drvdata(pdev); + + hfcpci_reset(adapter); + +// del_timer(&cs->hw.hfcpci.timer); XX + + /* disable DMA */ + pci_disable_device(pdev); + pci_write_config_dword(pdev, HFCPCI_MWBA, 0); + pci_free_consistent(pdev, 32768, adapter->fifo, adapter->fifo_dma); + + iounmap(adapter->mmio); + release_mem_region(pci_resource_start(pdev, 1), 256); + free_irq(adapter->irq, adapter); + delete_adapter(adapter); +} + +static struct pci_driver hfcpci_driver = { + name: "hfcpci", + probe: hfcpci_probe, + remove: hfcpci_remove, + id_table: hfcpci_ids, +}; + +static int __init hisax_hfcpci_init(void) +{ + int retval; + + printk(KERN_INFO "hisax_hfcpcipnp: HFC PCI ISDN driver v0.0.1\n"); + + l1fsm.state_count = L1_STATE_COUNT; + l1fsm.event_count = L1_EVENT_COUNT; + l1fsm.strState = strL1State; + l1fsm.strEvent = strL1Event; + retval = FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList)); + if (retval) + goto err; + + retval = pci_module_init(&hfcpci_driver); + if (retval) + goto err_fsm; + + return 0; + + err_fsm: + FsmFree(&l1fsm); + err: + return retval; +} + +static void __exit hisax_hfcpci_exit(void) +{ + FsmFree(&l1fsm); + pci_unregister_driver(&hfcpci_driver); +} + +module_init(hisax_hfcpci_init); +module_exit(hisax_hfcpci_exit); diff -Nru a/drivers/isdn/hisax/hisax_hfcpci.h b/drivers/isdn/hisax/hisax_hfcpci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/isdn/hisax/hisax_hfcpci.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,40 @@ +#include "hisax_if.h" +#include "hisax_isac.h" +#include + +struct hfcpci_bcs { + struct hisax_b_if b_if; + struct hfcpci_adapter *adapter; + int mode; + int channel; + int last_fcnt; + + struct sk_buff *tx_skb; +}; + +struct hfcpci_adapter { + struct hisax_d_if d_if; + spinlock_t hw_lock; + unsigned int irq; + void *mmio; + u8 *fifo; + dma_addr_t fifo_dma; + + struct FsmInst l1m; + struct FsmTimer timer; + struct sk_buff *tx_skb; + int last_fcnt; + + u8 int_m1, int_m2; + u8 fifo_en; + u8 trm; + u8 sctrl, sctrl_r, sctrl_e; + u8 nt_mode; + u8 ctmt; + u8 mst_m; + u8 conn; + u8 bswapped; + + struct hfcpci_bcs bcs[2]; +}; + diff -Nru a/drivers/isdn/hisax/st5481_init.c b/drivers/isdn/hisax/st5481_init.c --- a/drivers/isdn/hisax/st5481_init.c Thu Mar 7 18:17:46 2002 +++ b/drivers/isdn/hisax/st5481_init.c Thu Mar 7 18:17:46 2002 @@ -178,7 +178,7 @@ static struct usb_driver st5481_usb_driver = { name: "st5481_usb", probe: probe_st5481, - disconnect: disconnect_st5481, + disconnect: __devexit_p(disconnect_st5481), id_table: st5481_ids, }; diff -Nru a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c --- a/drivers/isdn/hisax/st5481_usb.c Thu Mar 7 18:17:45 2002 +++ b/drivers/isdn/hisax/st5481_usb.c Thu Mar 7 18:17:45 2002 @@ -416,7 +416,7 @@ for (j = 0; j < 2; j++) { retval = -ENOMEM; - urb[j] = usb_alloc_urb(num_packet, GFP_KERNEL); + urb[j] = usb_alloc_urb(num_packets, GFP_KERNEL); if (!urb[j]) goto err; diff -Nru a/drivers/isdn/tpam/tpam_main.c b/drivers/isdn/tpam/tpam_main.c --- a/drivers/isdn/tpam/tpam_main.c Thu Mar 7 18:17:39 2002 +++ b/drivers/isdn/tpam/tpam_main.c Thu Mar 7 18:17:39 2002 @@ -254,7 +254,7 @@ name: "tpam", id_table: tpam_pci_tbl, probe: tpam_probe, - remove: tpam_remove, + remove: __devexit_p(tpam_remove), }; static int __init tpam_init(void) { diff -Nru a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c --- a/drivers/macintosh/via-pmu.c Thu Mar 7 18:17:42 2002 +++ b/drivers/macintosh/via-pmu.c Thu Mar 7 18:17:42 2002 @@ -1434,7 +1434,7 @@ * BenH: Moved to _after_ sleep request and changed video * drivers to vmalloc() during sleep request. This way, all * vmalloc's are done before actual sleep of block drivers */ - fsync_dev(0); + sys_sync(); /* Sleep can fail now. May not be very robust but useful for debugging */ ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE); @@ -1589,7 +1589,7 @@ * BenH: Moved to _after_ sleep request and changed video * drivers to vmalloc() during sleep request. This way, all * vmalloc's are done before actual sleep of block drivers */ - fsync_dev(0); + sys_sync(); /* Sleep can fail now. May not be very robust but useful for debugging */ ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE); @@ -1764,7 +1764,7 @@ * BenH: Moved to _after_ sleep request and changed video * drivers to vmalloc() during sleep request. This way, all * vmalloc's are done before actual sleep of block drivers */ - fsync_dev(0); + sys_sync(); /* Sleep can fail now. May not be very robust but useful for debugging */ ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE); diff -Nru a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c --- a/drivers/macintosh/via-pmu68k.c Thu Mar 7 18:17:39 2002 +++ b/drivers/macintosh/via-pmu68k.c Thu Mar 7 18:17:39 2002 @@ -916,7 +916,7 @@ /* Sync the disks. */ /* XXX It would be nice to have some way to ensure that * nobody is dirtying any new buffers while we wait. */ - fsync_dev(0); + sys_sync(); /* Turn off the display backlight */ save_backlight = backlight_enabled; diff -Nru a/drivers/md/lvm.c b/drivers/md/lvm.c --- a/drivers/md/lvm.c Thu Mar 7 18:17:37 2002 +++ b/drivers/md/lvm.c Thu Mar 7 18:17:37 2002 @@ -878,7 +878,7 @@ P_IOCTL("BLKFLSBUF\n"); - fsync_dev(inode->i_rdev); + fsync_bdev(inode->i_bdev); invalidate_buffers(inode->i_rdev); break; diff -Nru a/drivers/md/md.c b/drivers/md/md.c --- a/drivers/md/md.c Thu Mar 7 18:17:41 2002 +++ b/drivers/md/md.c Thu Mar 7 18:17:41 2002 @@ -645,8 +645,14 @@ if (!bdev) return -ENOMEM; err = blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_RAW); - if (!err) - rdev->bdev = bdev; + if (err) + return err; + err = bd_claim(bdev, lock_rdev); + if (err) { + blkdev_put(bdev, BDEV_RAW); + return err; + } + rdev->bdev = bdev; return err; } @@ -656,6 +662,7 @@ rdev->bdev = NULL; if (!bdev) MD_BUG(); + bd_release(bdev); blkdev_put(bdev, BDEV_RAW); } @@ -890,7 +897,8 @@ static int write_disk_sb(mdk_rdev_t * rdev) { - struct address_space *mapping = rdev->bdev->bd_inode->i_mapping; + struct block_device *bdev = rdev->bdev; + struct address_space *mapping = bdev->bd_inode->i_mapping; struct page *page; unsigned offs; int error; @@ -929,7 +937,7 @@ } printk(KERN_INFO "(write) %s's sb offset: %ld\n", partition_name(dev), sb_offset); - fsync_dev(dev); + fsync_bdev(bdev); page = grab_cache_page(mapping, sb_offset/(PAGE_CACHE_SIZE/BLOCK_SIZE)); offs = sb_offset % (PAGE_CACHE_SIZE/BLOCK_SIZE); if (!page) @@ -946,7 +954,7 @@ UnlockPage(page); wait_on_page(page); page_cache_release(page); - fsync_dev(dev); + fsync_bdev(bdev); skip: return 0; unlock: @@ -1085,13 +1093,6 @@ } memset(rdev, 0, sizeof(*rdev)); - if (is_mounted(newdev)) { - printk(KERN_WARNING "md: can not import %s, has active inodes!\n", - partition_name(newdev)); - err = -EBUSY; - goto abort_free; - } - if ((err = alloc_disk_sb(rdev))) goto abort_free; @@ -2624,7 +2625,7 @@ case BLKFLSBUF: case BLKBSZGET: case BLKBSZSET: - err = blk_ioctl(dev, cmd, arg); + err = blk_ioctl(inode->i_bdev, cmd, arg); goto abort; default:; diff -Nru a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c --- a/drivers/media/radio/radio-gemtek-pci.c Thu Mar 7 18:17:38 2002 +++ b/drivers/media/radio/radio-gemtek-pci.c Thu Mar 7 18:17:38 2002 @@ -424,7 +424,7 @@ name: "gemtek_pci", id_table: gemtek_pci_id, probe: gemtek_pci_probe, - remove: gemtek_pci_remove + remove: __devexit_p(gemtek_pci_remove) }; static int __init gemtek_pci_init_module( void ) diff -Nru a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c --- a/drivers/media/radio/radio-maxiradio.c Thu Mar 7 18:17:42 2002 +++ b/drivers/media/radio/radio-maxiradio.c Thu Mar 7 18:17:42 2002 @@ -376,7 +376,7 @@ name: "radio-maxiradio", id_table: maxiradio_pci_tbl, probe: maxiradio_init_one, - remove: maxiradio_remove_one, + remove: __devexit_p(maxiradio_remove_one), }; int __init maxiradio_radio_init(void) diff -Nru a/drivers/media/video/bttv-driver.c b/drivers/media/video/bttv-driver.c --- a/drivers/media/video/bttv-driver.c Thu Mar 7 18:17:40 2002 +++ b/drivers/media/video/bttv-driver.c Thu Mar 7 18:17:40 2002 @@ -141,58 +141,63 @@ * defined way to get at the kernel page tables. */ -static inline unsigned long uvirt_to_bus(unsigned long adr) -{ - unsigned long kva, ret; - kva = page_address(vmalloc_to_page(adr)); - ret = virt_to_bus((void *)kva); - MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); - return ret; +/* + * Take a vmalloc address, and turn it into + * the aliased kernel virtual address.. + * + * CAREFUL! Anybody who does this gets to sit + * in their own cr*p when it comes to virtual + * cache aliases. It's _your_ problem. + * + * Also, note how it only works within one page. + * If you're doing page-crossing stuff, you're on + * your own. + * + * THIS IS BROKEN CODE! You shouldn't do things + * like this. + */ +static inline void *vmalloc_to_virt(void *addr) +{ + struct page *page = vmalloc_to_page(addr); + return page_address(page) + (~PAGE_MASK & (unsigned long)addr); } -static inline unsigned long kvirt_to_bus(unsigned long adr) +static inline unsigned long kvirt_to_bus(unsigned long addr) { - unsigned long va, kva, ret; - - va = VMALLOC_VMADDR(adr); - kva = page_address(vmalloc_to_page(va)); - ret = virt_to_bus((void *)kva); - MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); + unsigned long ret; + ret = virt_to_bus(vmalloc_to_virt((void *)addr)); + MDEBUG(printk("kv2b(%lx-->%lx)", addr, 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. + * This is used when initializing the contents of the area. */ -static inline unsigned long kvirt_to_pa(unsigned long adr) +static inline unsigned long kvirt_to_pa(unsigned long addr) { - unsigned long va, kva, ret; - - va = VMALLOC_VMADDR(adr); - kva = page_address(vmalloc_to_page(va)); - ret = __pa(kva); - MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); + unsigned long ret; + ret = virt_to_phys(vmalloc_to_virt((void *)addr)); + MDEBUG(printk("kv2pa(%lx-->%lx)", addr, ret)); return ret; } -static void * rvmalloc(signed long size) +static void * rvmalloc(unsigned long size) { void * mem; - unsigned long adr, page; + unsigned long adr; + size=PAGE_ALIGN(size); mem=vmalloc_32(size); if (NULL == mem) - printk(KERN_INFO "bttv: vmalloc_32(%ld) failed\n",size); + printk(KERN_INFO "bttv: vmalloc_32(%lu) failed\n",size); else { /* Clear the ram out, no junk to the user */ memset(mem, 0, size); adr=(unsigned long) mem; while (size > 0) { - page = kvirt_to_pa(adr); - mem_map_reserve(virt_to_page(__va(page))); + mem_map_reserve(vmalloc_to_page((void *)adr)); adr+=PAGE_SIZE; size-=PAGE_SIZE; } @@ -200,17 +205,16 @@ return mem; } -static void rvfree(void * mem, signed long size) +static void rvfree(void * mem, unsigned long size) { - unsigned long adr, page; + unsigned long adr; if (mem) { adr=(unsigned long) mem; - while (size > 0) + while ((long) size > 0) { - page = kvirt_to_pa(adr); - mem_map_unreserve(virt_to_page(__va(page))); + mem_map_unreserve(vmalloc_to_page((void *)adr)); adr+=PAGE_SIZE; size-=PAGE_SIZE; } @@ -2794,7 +2798,8 @@ * Scan for a Bt848 card, request the irq and map the io memory */ -static void __devexit bttv_remove(struct pci_dev *pci_dev) +/* Can't be marked __devexit with a reference from bttv_probe. */ +static void bttv_remove(struct pci_dev *pci_dev) { u8 command; int j; diff -Nru a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c --- a/drivers/media/video/cpia.c Thu Mar 7 18:17:45 2002 +++ b/drivers/media/video/cpia.c Thu Mar 7 18:17:45 2002 @@ -181,15 +181,14 @@ **********************************************************************/ /* 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. + * This is used when initializing the contents of the area. */ static inline unsigned long kvirt_to_pa(unsigned long adr) { - unsigned long va, kva, ret; + unsigned long kva, ret; - va = VMALLOC_VMADDR(adr); - kva = page_address(vmalloc_to_page(va)); + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ ret = __pa(kva); return ret; } @@ -197,12 +196,9 @@ 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); + unsigned long adr; + size = PAGE_ALIGN(size); mem = vmalloc_32(size); if (!mem) return NULL; @@ -210,13 +206,9 @@ 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))); + mem_map_reserve(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; + size -= PAGE_SIZE; } return mem; @@ -224,23 +216,16 @@ static void rvfree(void *mem, unsigned long size) { - unsigned long adr, page; + unsigned long adr; 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))); + while ((long) size > 0) { + mem_map_unreserve(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; + size -= PAGE_SIZE; } vfree(mem); } diff -Nru a/drivers/media/video/meye.c b/drivers/media/video/meye.c --- a/drivers/media/video/meye.c Thu Mar 7 18:17:37 2002 +++ b/drivers/media/video/meye.c Thu Mar 7 18:17:37 2002 @@ -115,34 +115,29 @@ /* Memory allocation routines (stolen from bttv-driver.c) */ /****************************************************************************/ -#define MDEBUG(x) do {} while (0) -/* #define MDEBUG(x) x */ - /* 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. + * This is used when initializing the contents of the area. */ static inline unsigned long kvirt_to_pa(unsigned long adr) { - unsigned long va, kva, ret; + unsigned long kva, ret; - va = VMALLOC_VMADDR(adr); - kva = page_address(vmalloc_to_page(va)); + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ ret = __pa(kva); - MDEBUG(printk("kv2pa(%lx-->%lx)\n", adr, ret)); return ret; } -static void *rvmalloc(signed long size) { +static void *rvmalloc(unsigned long size) { void *mem; - unsigned long adr, page; + unsigned long adr; + size = PAGE_ALIGN(size); mem = vmalloc_32(size); if (mem) { 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))); + mem_map_reserve(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; size -= PAGE_SIZE; } @@ -150,14 +145,13 @@ return mem; } -static void rvfree(void * mem, signed long size) { - unsigned long adr, page; - +static void rvfree(void * mem, unsigned long size) { + unsigned long adr; + if (mem) { adr = (unsigned long) mem; - while (size > 0) { - page = kvirt_to_pa(adr); - mem_map_unreserve(virt_to_page(__va(page))); + while ((long) size > 0) { + mem_map_unreserve(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; size -= PAGE_SIZE; } @@ -1425,7 +1419,7 @@ name: "meye", id_table: meye_pci_tbl, probe: meye_probe, - remove: meye_remove, + remove: __devexit_p(meye_remove), }; static int __init meye_init_module(void) { diff -Nru a/drivers/media/video/zr36120.c b/drivers/media/video/zr36120.c --- a/drivers/media/video/zr36120.c Thu Mar 7 18:17:44 2002 +++ b/drivers/media/video/zr36120.c Thu Mar 7 18:17:44 2002 @@ -2024,7 +2024,7 @@ } static -void __exit release_zoran(int max) +void release_zoran(int max) { struct zoran *ztv; int i; diff -Nru a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c --- a/drivers/message/i2o/i2o_block.c Thu Mar 7 18:17:45 2002 +++ b/drivers/message/i2o/i2o_block.c Thu Mar 7 18:17:45 2002 @@ -116,7 +116,7 @@ #define I2O_BSA_DSC_VOLUME_CHANGED 0x000D #define I2O_BSA_DSC_TIMEOUT 0x000E -#define I2O_UNIT(dev) (i2ob_dev[MINOR((dev)) & 0xf0]) +#define I2O_UNIT(dev) (i2ob_dev[minor((dev)) & 0xf0]) #define I2O_LOCK(unit) (i2ob_dev[(unit)].req_queue->queue_lock) /* @@ -760,7 +760,7 @@ { u64 size; - if(do_i2ob_revalidate(MKDEV(MAJOR_NR, unit),0) != -EBUSY) + if(do_i2ob_revalidate(mk_kdev(MAJOR_NR, unit),0) != -EBUSY) continue; if(i2ob_query_device(&i2ob_dev[unit], 0x0004, 0, &size, 8) !=0 ) @@ -869,7 +869,7 @@ if(i2ob_backlog[c->unit] == NULL) i2ob_backlog_tail[c->unit] = NULL; - unit = MINOR(ireq->req->rq_dev); + unit = minor(ireq->req->rq_dev); i2ob_send(m, dev, ireq, i2ob[unit].start_sect, unit); } if(i2ob_backlog[c->unit]) @@ -905,7 +905,7 @@ if(req->rq_status == RQ_INACTIVE) return; - unit = MINOR(req->rq_dev); + unit = minor(req->rq_dev); dev = &i2ob_dev[(unit&0xF0)]; /* @@ -1038,7 +1038,7 @@ static int do_i2ob_revalidate(kdev_t dev, int maxu) { - int minor=MINOR(dev); + int minor=minor(dev); int i; minor&=0xF0; @@ -1053,7 +1053,7 @@ for( i = 15; i>=0 ; i--) { int m = minor+i; - invalidate_device(MKDEV(MAJOR_NR, m), 1); + invalidate_device(mk_kdev(MAJOR_NR, m), 1); i2ob_gendisk.part[m].start_sect = 0; i2ob_gendisk.part[m].nr_sects = 0; } @@ -1079,14 +1079,14 @@ if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (!inode || !inode->i_rdev) + if (!inode || kdev_none(inode->i_rdev)) return -EINVAL; switch (cmd) { case HDIO_GETGEO: { struct hd_geometry g; - int u = MINOR(inode->i_rdev) & 0xF0; + int u = minor(inode->i_rdev) & 0xF0; i2o_block_biosparam(i2ob_sizes[u]<<1, &g.cylinders, &g.heads, &g.sectors); g.start = get_start_sect(inode->i_rdev); @@ -1105,7 +1105,7 @@ case BLKROSET: case BLKROGET: case BLKPG: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); default: return -EINVAL; @@ -1121,7 +1121,7 @@ struct i2ob_device *dev; int minor; - minor = MINOR(inode->i_rdev); + minor = minor(inode->i_rdev); if (minor >= (MAX_I2OB<<4)) return -ENODEV; dev = &i2ob_dev[(minor&0xF0)]; @@ -1137,9 +1137,6 @@ 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--; @@ -1193,7 +1190,7 @@ if (!inode) return -EINVAL; - minor = MINOR(inode->i_rdev); + minor = minor(inode->i_rdev); if (minor >= MAX_I2OB<<4) return -ENODEV; dev=&i2ob_dev[(minor&0xF0)]; @@ -1384,7 +1381,7 @@ */ dev->req_queue = &i2ob_queues[c->unit]->req_queue; - grok_partitions(MKDEV(MAJOR_NR, unit), (long)(size>>9)); + grok_partitions(mk_kdev(MAJOR_NR, unit), (long)(size>>9)); /* * Register for the events we're interested in and that the @@ -1704,7 +1701,7 @@ */ static int i2ob_media_change(kdev_t dev) { - int i=MINOR(dev); + int i=minor(dev); i>>=4; if(i2ob_media_change_flag[i]) { @@ -1883,7 +1880,7 @@ * 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, + register_disk(&i2ob_gendisk, mk_kdev(MAJOR_NR,i<<4), 1<<4, &i2ob_fops, 0); i2ob_probe(); diff -Nru a/drivers/mtd/devices/blkmtd.c b/drivers/mtd/devices/blkmtd.c --- a/drivers/mtd/devices/blkmtd.c Thu Mar 7 18:17:37 2002 +++ b/drivers/mtd/devices/blkmtd.c Thu Mar 7 18:17:37 2002 @@ -1,10 +1,11 @@ /* - * $Id: blkmtd.c,v 1.3 2001/10/02 15:33:20 dwmw2 Exp $ + * $Id: blkmtd.c,v 1.7 2001/11/10 17:06:30 spse Exp $ + * * blkmtd.c - use a block device as a fake MTD * * Author: Simon Evans * - * Copyright (C) 2001 Simon Evans + * Copyright (C) 2001 Simon Evans * * Licence: GPL * @@ -13,7 +14,7 @@ * cache to cache access. Writes update the page cache with the * new data but make a copy of the new page(s) and then a kernel * thread writes pages out to the device in the background. This - * ensures tht writes are order even if a page is updated twice. + * ensures that writes are order even if a page is updated twice. * Also, since pages in the page cache are never marked as dirty, * we dont have to worry about writepage() being called on some * random page which may not be in the write order. @@ -30,7 +31,7 @@ * small memory systems and too small on large memory systems. * * Page cache usage may still be a bit wrong. Check we are doing - * everything proberly. + * everything properly. * * Somehow allow writes to dirty the page cache so we dont use too * much memory making copies of outgoing pages. Need to handle case @@ -39,19 +40,14 @@ * * Reading should read multiple pages at once rather than using * readpage() for each one. This is easy and will be fixed asap. - * - * Dont run the write_thread if readonly. This is also easy and will - * be fixed asap. - * - * Even though the multiple erase regions are used if the default erase - * block size doesnt match the device properly, erases currently wont - * work on the last page if it is not a full page. */ + #include #include #include +#include #include #include #include @@ -59,19 +55,30 @@ #include #include +#ifdef CONFIG_MTD_DEBUG +#ifdef CONFIG_PROC_FS +# include +# define BLKMTD_PROC_DEBUG + static struct proc_dir_entry *blkmtd_proc; +#endif +#endif + + /* Default erase size in K, always make it a multiple of PAGE_SIZE */ #define CONFIG_MTD_BLKDEV_ERASESIZE 128 -#define VERSION "1.1" +#define VERSION "1.7" extern int *blk_size[]; /* Info for the block device */ typedef struct mtd_raw_dev_data_s { struct block_device *binding; - int sector_size, sector_bits, total_sectors; + int sector_size, sector_bits; + int partial_last_page; // 0 if device ends on page boundary, else page no of last page + int last_page_sectors; // Number of sectors in last page if partial_last_page != 0 size_t totalsize; int readonly; struct address_space as; - struct file *file; + struct mtd_info mtd_info; } mtd_raw_dev_data_t; /* Info for each queue item in the write queue */ @@ -84,24 +91,28 @@ } mtdblkdev_write_queue_t; +/* Our erase page - always remains locked. */ +static struct page *erase_page; + /* Static info about the MTD, used in cleanup_module */ -static struct mtd_info *mtd_info; +static mtd_raw_dev_data_t *mtd_rawdevice; /* Write queue fixed size */ #define WRITE_QUEUE_SZ 512 /* Storage for the write queue */ -static mtdblkdev_write_queue_t write_queue[WRITE_QUEUE_SZ]; +static mtdblkdev_write_queue_t *write_queue; +static int write_queue_sz = WRITE_QUEUE_SZ; static int volatile write_queue_head; static int volatile write_queue_tail; static int volatile write_queue_cnt; static spinlock_t mbd_writeq_lock = SPIN_LOCK_UNLOCKED; /* Tell the write thread to finish */ -static volatile int write_task_finish = 0; +static volatile int write_task_finish; /* ipc with the write thread */ -#if LINUX_VERSION_CODE > 0x020300 +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) static DECLARE_MUTEX_LOCKED(thread_sem); static DECLARE_WAIT_QUEUE_HEAD(thr_wq); static DECLARE_WAIT_QUEUE_HEAD(mtbd_sync_wq); @@ -112,13 +123,14 @@ #endif - /* Module parameters passed by insmod/modprobe */ char *device; /* the block device to use */ int erasesz; /* optional default erase size */ int ro; /* optional read only flag */ int bs; /* optionally force the block size (avoid using) */ int count; /* optionally force the block count (avoid using) */ +int wqs; /* optionally set the write queue size */ + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) MODULE_LICENSE("GPL"); @@ -134,10 +146,10 @@ MODULE_PARM_DESC(bs, "force the block size in bytes"); MODULE_PARM(count, "i"); MODULE_PARM_DESC(count, "force the block count"); +MODULE_PARM(wqs, "i"); #endif - /* Page cache stuff */ /* writepage() - should never be called - catch it anyway */ @@ -149,13 +161,13 @@ /* readpage() - reads one page from the block device */ -static int blkmtd_readpage(struct file *file, struct page *page) +static int blkmtd_readpage(mtd_raw_dev_data_t *rawdevice, struct page *page) { int err; int sectornr, sectors, i; struct kiobuf *iobuf; - mtd_raw_dev_data_t *rawdevice = (mtd_raw_dev_data_t *)file->private_data; kdev_t dev; + unsigned long *blocks; if(!rawdevice) { printk("blkmtd: readpage: PANIC file->private_data == NULL\n"); @@ -167,7 +179,7 @@ bdevname(dev), page, page->index); if(Page_Uptodate(page)) { - DEBUG(1, "blkmtd: readpage page %ld is already upto date\n", page->index); + DEBUG(2, "blkmtd: readpage page %ld is already upto date\n", page->index); UnlockPage(page); return 0; } @@ -183,8 +195,9 @@ mtdblkdev_write_queue_t *item = &write_queue[i]; if(page->index >= item->pagenr && page->index < item->pagenr+item->pagecnt) { /* yes it is */ - int index = item->pagenr - page->index; - DEBUG(1, "blkmtd: readpage: found page %ld in outgoing write queue\n", + int index = page->index - item->pagenr; + + DEBUG(2, "blkmtd: readpage: found page %ld in outgoing write queue\n", page->index); if(item->iserase) { memset(page_address(page), 0xff, PAGE_SIZE); @@ -198,7 +211,7 @@ return 0; } i++; - i %= WRITE_QUEUE_SZ; + i %= write_queue_sz; } } spin_unlock(&mbd_writeq_lock); @@ -207,8 +220,24 @@ DEBUG(3, "blkmtd: readpage: getting kiovec\n"); err = alloc_kiovec(1, &iobuf); if (err) { + printk("blkmtd: cant allocate kiobuf\n"); + SetPageError(page); return err; } + + /* Pre 2.4.4 doesnt have space for the block list in the kiobuf */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long)); + if(blocks == NULL) { + printk("blkmtd: cant allocate iobuf blocks\n"); + free_kiovec(1, &iobuf); + SetPageError(page); + return -ENOMEM; + } +#else + blocks = iobuf->blocks; +#endif + iobuf->offset = 0; iobuf->nr_pages = 1; iobuf->length = PAGE_SIZE; @@ -216,16 +245,34 @@ iobuf->maplist[0] = page; sectornr = page->index << (PAGE_SHIFT - rawdevice->sector_bits); sectors = 1 << (PAGE_SHIFT - rawdevice->sector_bits); + if(rawdevice->partial_last_page && page->index == rawdevice->partial_last_page) { + DEBUG(3, "blkmtd: handling partial last page\n"); + sectors = rawdevice->last_page_sectors; + } DEBUG(3, "blkmtd: readpage: sectornr = %d sectors = %d\n", sectornr, sectors); for(i = 0; i < sectors; i++) { - iobuf->blocks[i] = sectornr++; + blocks[i] = sectornr++; } + /* If only a partial page read in, clear the rest of the page */ + if(rawdevice->partial_last_page && page->index == rawdevice->partial_last_page) { + int offset = rawdevice->last_page_sectors << rawdevice->sector_bits; + int count = PAGE_SIZE-offset; + DEBUG(3, "blkmtd: clear last partial page: offset = %d count = %d\n", offset, count); + memset(page_address(page)+offset, 0, count); + sectors = rawdevice->last_page_sectors; + } + DEBUG(3, "bklmtd: readpage: starting brw_kiovec\n"); - err = brw_kiovec(READ, 1, &iobuf, dev, iobuf->blocks, rawdevice->sector_size); + err = brw_kiovec(READ, 1, &iobuf, dev, blocks, rawdevice->sector_size); DEBUG(3, "blkmtd: readpage: finished, err = %d\n", err); iobuf->locked = 0; free_kiovec(1, &iobuf); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + kfree(blocks); +#endif + if(err != PAGE_SIZE) { printk("blkmtd: readpage: error reading page %ld\n", page->index); memset(page_address(page), 0, PAGE_SIZE); @@ -245,7 +292,7 @@ static struct address_space_operations blkmtd_aops = { writepage: blkmtd_writepage, - readpage: blkmtd_readpage, + readpage: NULL, }; @@ -255,6 +302,7 @@ int err; struct task_struct *tsk = current; struct kiobuf *iobuf; + unsigned long *blocks; DECLARE_WAITQUEUE(wait, tsk); DEBUG(1, "blkmtd: writetask: starting (pid = %d)\n", tsk->pid); @@ -266,8 +314,23 @@ recalc_sigpending(); spin_unlock_irq(&tsk->sigmask_lock); - if(alloc_kiovec(1, &iobuf)) + if(alloc_kiovec(1, &iobuf)) { + printk("blkmtd: write_queue_task cant allocate kiobuf\n"); return 0; + } + + /* Pre 2.4.4 doesnt have space for the block list in the kiobuf */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long)); + if(blocks == NULL) { + printk("blkmtd: write_queue_task cant allocate iobuf blocks\n"); + free_kiovec(1, &iobuf); + return 0; + } +#else + blocks = iobuf->blocks; +#endif + DEBUG(2, "blkmtd: writetask: entering main loop\n"); add_wait_queue(&thr_wq, &wait); @@ -278,30 +341,39 @@ /* If nothing in the queue, wake up anyone wanting to know when there is space in the queue then sleep for 2*HZ */ spin_unlock(&mbd_writeq_lock); - DEBUG(3, "blkmtd: writetask: queue empty\n"); + DEBUG(4, "blkmtd: writetask: queue empty\n"); if(waitqueue_active(&mtbd_sync_wq)) wake_up(&mtbd_sync_wq); interruptible_sleep_on_timeout(&thr_wq, 2*HZ); - DEBUG(3, "blkmtd: writetask: woken up\n"); + DEBUG(4, "blkmtd: writetask: woken up\n"); if(write_task_finish) break; } else { /* we have stuff to write */ mtdblkdev_write_queue_t *item = &write_queue[write_queue_tail]; struct page **pages = item->pages; - int pagecnt = item->pagecnt; - int pagenr = item->pagenr; + int i; + int sectornr = item->pagenr << (PAGE_SHIFT - item->rawdevice->sector_bits); + int sectorcnt = item->pagecnt << (PAGE_SHIFT - item->rawdevice->sector_bits); int max_sectors = KIO_MAX_SECTORS >> (item->rawdevice->sector_bits - 9); kdev_t dev = to_kdev_t(item->rawdevice->binding->bd_dev); - + + /* If we are writing to the last page on the device and it doesnt end + * on a page boundary, subtract the number of sectors that dont exist. + */ + if(item->rawdevice->partial_last_page && + (item->pagenr + item->pagecnt -1) == item->rawdevice->partial_last_page) { + sectorcnt -= (1 << (PAGE_SHIFT - item->rawdevice->sector_bits)); + sectorcnt += item->rawdevice->last_page_sectors; + } DEBUG(3, "blkmtd: writetask: got %d queue items\n", write_queue_cnt); set_current_state(TASK_RUNNING); spin_unlock(&mbd_writeq_lock); - DEBUG(2, "blkmtd: write_task: writing pagenr = %d pagecnt = %d", - item->pagenr, item->pagecnt); + DEBUG(2, "blkmtd: writetask: writing pagenr = %d pagecnt = %d sectornr = %d sectorcnt = %d\n", + item->pagenr, item->pagecnt, sectornr, sectorcnt); iobuf->offset = 0; iobuf->locked = 1; @@ -309,31 +381,33 @@ /* Loop through all the pages to be written in the queue item, remembering we can only write KIO_MAX_SECTORS at a time */ - while(pagecnt) { - int sectornr = pagenr << (PAGE_SHIFT - item->rawdevice->sector_bits); - int sectorcnt = pagecnt << (PAGE_SHIFT - item->rawdevice->sector_bits); + while(sectorcnt) { int cursectors = (sectorcnt < max_sectors) ? sectorcnt : max_sectors; int cpagecnt = (cursectors << item->rawdevice->sector_bits) + PAGE_SIZE-1; cpagecnt >>= PAGE_SHIFT; - for(i = 0; i < cpagecnt; i++) + for(i = 0; i < cpagecnt; i++) { + if(item->iserase) { + iobuf->maplist[i] = erase_page; + } else { iobuf->maplist[i] = *(pages++); + } + } for(i = 0; i < cursectors; i++) { - iobuf->blocks[i] = sectornr++; + blocks[i] = sectornr++; } iobuf->nr_pages = cpagecnt; iobuf->length = cursectors << item->rawdevice->sector_bits; DEBUG(3, "blkmtd: write_task: about to kiovec\n"); - err = brw_kiovec(WRITE, 1, &iobuf, dev, iobuf->blocks, item->rawdevice->sector_size); + err = brw_kiovec(WRITE, 1, &iobuf, dev, blocks, item->rawdevice->sector_size); DEBUG(3, "bklmtd: write_task: done, err = %d\n", err); if(err != (cursectors << item->rawdevice->sector_bits)) { /* if an error occured - set this to exit the loop */ - pagecnt = 0; + sectorcnt = 0; } else { - pagenr += cpagecnt; - pagecnt -= cpagecnt; + sectorcnt -= cursectors; } } @@ -343,12 +417,14 @@ spin_lock(&mbd_writeq_lock); write_queue_cnt--; write_queue_tail++; - write_queue_tail %= WRITE_QUEUE_SZ; - for(i = 0 ; i < item->pagecnt; i++) { - UnlockPage(item->pages[i]); - __free_pages(item->pages[i], 0); + write_queue_tail %= write_queue_sz; + if(!item->iserase) { + for(i = 0 ; i < item->pagecnt; i++) { + UnlockPage(item->pages[i]); + __free_pages(item->pages[i], 0); + } + kfree(item->pages); } - kfree(item->pages); item->pages = NULL; spin_unlock(&mbd_writeq_lock); /* Tell others there is some space in the write queue */ @@ -359,6 +435,11 @@ remove_wait_queue(&thr_wq, &wait); DEBUG(1, "blkmtd: writetask: exiting\n"); free_kiovec(1, &iobuf); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + kfree(blocks); +#endif + /* Tell people we have exitd */ up(&thread_sem); return 0; @@ -370,43 +451,45 @@ int pagenr, int pagecnt, int iserase) { struct page *outpage; - struct page **new_pages; + struct page **new_pages = NULL; mtdblkdev_write_queue_t *item; int i; DECLARE_WAITQUEUE(wait, current); - DEBUG(2, "mtdblkdev: queue_page_write: adding pagenr = %d pagecnt = %d\n", pagenr, pagecnt); + DEBUG(2, "blkmtd: queue_page_write: adding pagenr = %d pagecnt = %d\n", pagenr, pagecnt); if(!pagecnt) return 0; - if(pages == NULL) + if(pages == NULL && !iserase) return -EINVAL; /* create a array for the list of pages */ - new_pages = kmalloc(pagecnt * sizeof(struct page *), GFP_KERNEL); - if(new_pages == NULL) - return -ENOMEM; + if(!iserase) { + new_pages = kmalloc(pagecnt * sizeof(struct page *), GFP_KERNEL); + if(new_pages == NULL) + return -ENOMEM; - /* make copies of the pages in the page cache */ - for(i = 0; i < pagecnt; i++) { - outpage = alloc_pages(GFP_KERNEL, 0); - if(!outpage) { - while(i--) { - UnlockPage(new_pages[i]); - __free_pages(new_pages[i], 0); + /* make copies of the pages in the page cache */ + for(i = 0; i < pagecnt; i++) { + outpage = alloc_pages(GFP_KERNEL, 0); + if(!outpage) { + while(i--) { + UnlockPage(new_pages[i]); + __free_pages(new_pages[i], 0); + } + kfree(new_pages); + return -ENOMEM; } - kfree(new_pages); - return -ENOMEM; + lock_page(outpage); + memcpy(page_address(outpage), page_address(pages[i]), PAGE_SIZE); + new_pages[i] = outpage; } - lock_page(outpage); - memcpy(page_address(outpage), page_address(pages[i]), PAGE_SIZE); - new_pages[i] = outpage; } /* wait until there is some space in the write queue */ test_lock: spin_lock(&mbd_writeq_lock); - if(write_queue_cnt == WRITE_QUEUE_SZ) { + if(write_queue_cnt == write_queue_sz) { spin_unlock(&mbd_writeq_lock); DEBUG(3, "blkmtd: queue_page: Queue full\n"); current->state = TASK_UNINTERRUPTIBLE; @@ -415,11 +498,11 @@ schedule(); current->state = TASK_RUNNING; remove_wait_queue(&mtbd_sync_wq, &wait); - DEBUG(3, "blkmtd: queue_page: Queue has %d items in it\n", write_queue_cnt); + DEBUG(3, "blkmtd: queue_page_write: Queue has %d items in it\n", write_queue_cnt); goto test_lock; } - DEBUG(3, "blkmtd: queue_write_page: qhead: %d qtail: %d qcnt: %d\n", + DEBUG(3, "blkmtd: queue_page_write: qhead: %d qtail: %d qcnt: %d\n", write_queue_head, write_queue_tail, write_queue_cnt); /* fix up the queue item */ @@ -431,9 +514,9 @@ item->iserase = iserase; write_queue_head++; - write_queue_head %= WRITE_QUEUE_SZ; + write_queue_head %= write_queue_sz; write_queue_cnt++; - DEBUG(3, "blkmtd: queue_write_page: qhead: %d qtail: %d qcnt: %d\n", + DEBUG(3, "blkmtd: queue_page_write: qhead: %d qtail: %d qcnt: %d\n", write_queue_head, write_queue_tail, write_queue_cnt); spin_unlock(&mbd_writeq_lock); DEBUG(2, "blkmtd: queue_page_write: finished\n"); @@ -445,6 +528,8 @@ static int blkmtd_erase(struct mtd_info *mtd, struct erase_info *instr) { mtd_raw_dev_data_t *rawdevice = mtd->priv; + struct mtd_erase_region_info *einfo = mtd->eraseregions; + int numregions = mtd->numeraseregions; size_t from; u_long len; int err = 0; @@ -460,17 +545,23 @@ from = instr->addr; len = instr->len; - /* check page alignment of start and length */ - DEBUG(2, "blkmtd: erase: dev = `%s' from = %d len = %ld\n", + /* check erase region has valid start and length */ + DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%x len = 0x%lx\n", bdevname(rawdevice->binding->bd_dev), from, len); - if(from % PAGE_SIZE) { - printk("blkmtd: erase: addr not page aligned (addr = %d)\n", from); - instr->state = MTD_ERASE_FAILED; - err = -EIO; + while(numregions) { + DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n", + einfo->offset, einfo->erasesize, einfo->numblocks); + if(from >= einfo->offset && from < einfo->offset + (einfo->erasesize * einfo->numblocks)) { + if(len == einfo->erasesize && ( (from - einfo->offset) % einfo->erasesize == 0)) + break; + } + numregions--; + einfo++; } - if(len % PAGE_SIZE) { - printk("blkmtd: erase: len not a whole number of pages (len = %ld)\n", len); + if(!numregions) { + /* Not a valid erase block */ + printk("blkmtd: erase: invalid erase request 0x%lX @ 0x%08X\n", len, from); instr->state = MTD_ERASE_FAILED; err = -EIO; } @@ -481,9 +572,14 @@ struct page *page, **pages; int i = 0; + /* Handle the last page of the device not being whole */ + if(len < PAGE_SIZE) + len = PAGE_SIZE; + pagenr = from >> PAGE_SHIFT; pagecnt = len >> PAGE_SHIFT; DEBUG(3, "blkmtd: erase: pagenr = %d pagecnt = %d\n", pagenr, pagecnt); + pages = kmalloc(pagecnt * sizeof(struct page *), GFP_KERNEL); if(pages == NULL) { err = -ENOMEM; @@ -491,9 +587,10 @@ goto erase_out; } + while(pagecnt) { /* get the page via the page cache */ - DEBUG(3, "blkmtd: erase: doing grap_cache_page() for page %d\n", pagenr); + DEBUG(3, "blkmtd: erase: doing grab_cache_page() for page %d\n", pagenr); page = grab_cache_page(&rawdevice->as, pagenr); if(!page) { DEBUG(3, "blkmtd: erase: grab_cache_page() failed for page %d\n", pagenr); @@ -509,7 +606,7 @@ i++; } DEBUG(3, "blkmtd: erase: queuing page write\n"); - err = queue_page_write(rawdevice, pages, from >> PAGE_SHIFT, len >> PAGE_SHIFT, 1); + err = queue_page_write(rawdevice, NULL, from >> PAGE_SHIFT, len >> PAGE_SHIFT, 1); pagecnt = len >> PAGE_SHIFT; if(!err) { while(pagecnt--) { @@ -565,7 +662,7 @@ struct page *page; int cpylen; DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr); - page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice->file); + page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice); if(IS_ERR(page)) { return PTR_ERR(page); } @@ -681,7 +778,7 @@ struct page *page; DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %d offset = %d\n", pagenr, len1, offset); - page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice->file); + page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice); if(IS_ERR(page)) { kfree(pages); @@ -714,6 +811,7 @@ memcpy(page_address(page), buf, PAGE_SIZE); pages[pagecnt++] = page; UnlockPage(page); + SetPageUptodate(page); pagenr++; pagesc--; buf += PAGE_SIZE; @@ -726,7 +824,7 @@ /* do the third region */ struct page *page; DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %d\n", pagenr, len3); - page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice->file); + page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice); if(IS_ERR(page)) { err = PTR_ERR(page); goto write_err; @@ -764,6 +862,10 @@ static void blkmtd_sync(struct mtd_info *mtd) { DECLARE_WAITQUEUE(wait, current); + mtd_raw_dev_data_t *rawdevice = mtd->priv; + if(rawdevice->readonly) + return; + DEBUG(2, "blkmtd: sync: called\n"); stuff_inq: @@ -782,36 +884,144 @@ } spin_unlock(&mbd_writeq_lock); - DEBUG(2, "blkmtdL sync: finished\n"); + DEBUG(2, "blkmtd: sync: finished\n"); } + +#ifdef BLKMTD_PROC_DEBUG +/* procfs stuff */ +static int blkmtd_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int clean = 0, dirty = 0, locked = 0; + struct list_head *temp; + int i, len, pages = 0, cnt; + MOD_INC_USE_COUNT; + spin_lock(&mbd_writeq_lock); + cnt = write_queue_cnt; + i = write_queue_tail; + while(cnt) { + if(!write_queue[i].iserase) + pages += write_queue[i].pagecnt; + i++; + i %= write_queue_sz; + cnt--; + } + + /* Count the size of the page lists */ + list_for_each(temp, &mtd_rawdevice->as.clean_pages) { + clean++; + } + list_for_each(temp, &mtd_rawdevice->as.dirty_pages) { + dirty++; + } + list_for_each(temp, &mtd_rawdevice->as.locked_pages) { + locked++; + } + + len = sprintf(page, "Write queue head: %d\nWrite queue tail: %d\n" + "Write queue count: %d\nPages in queue: %d (%dK)\n" + "Clean Pages: %d\nDirty Pages: %d\nLocked Pages: %d\n" + "nrpages: %ld\n", + write_queue_head, write_queue_tail, write_queue_cnt, + pages, pages << (PAGE_SHIFT-10), clean, dirty, locked, + mtd_rawdevice->as.nrpages); + if(len <= count) + *eof = 1; + spin_unlock(&mbd_writeq_lock); + MOD_DEC_USE_COUNT; + return len; +} +#endif + + /* Cleanup and exit - sync the device and kill of the kernel thread */ static void __exit cleanup_blkmtd(void) { - if (mtd_info) { - mtd_raw_dev_data_t *rawdevice = mtd_info->priv; - // sync the device - if (rawdevice) { - blkmtd_sync(mtd_info); +#ifdef BLKMTD_PROC_DEBUG + if(blkmtd_proc) { + remove_proc_entry("blkmtd_debug", NULL); + } +#endif + + if (mtd_rawdevice) { + /* sync the device */ + if (!mtd_rawdevice->readonly) { + blkmtd_sync(&mtd_rawdevice->mtd_info); write_task_finish = 1; wake_up_interruptible(&thr_wq); down(&thread_sem); - if(rawdevice->binding != NULL) - blkdev_put(rawdevice->binding, BDEV_RAW); - filp_close(rawdevice->file, NULL); - kfree(mtd_info->priv); } - if(mtd_info->eraseregions) - kfree(mtd_info->eraseregions); - del_mtd_device(mtd_info); - kfree(mtd_info); - mtd_info = NULL; + del_mtd_device(&mtd_rawdevice->mtd_info); + if(mtd_rawdevice->binding != NULL) + blkdev_put(mtd_rawdevice->binding, BDEV_RAW); + + if(mtd_rawdevice->mtd_info.eraseregions) + kfree(mtd_rawdevice->mtd_info.eraseregions); + if(mtd_rawdevice->mtd_info.name) + kfree(mtd_rawdevice->mtd_info.name); + + kfree(mtd_rawdevice); + } + if(write_queue) + kfree(write_queue); + + if(erase_page) { + UnlockPage(erase_page); + __free_pages(erase_page, 0); } printk("blkmtd: unloaded for %s\n", device); } extern struct module __this_module; +#ifndef MODULE + +/* Handle kernel boot params */ + + +static int __init param_blkmtd_device(char *str) +{ + device = str; + return 1; +} + + +static int __init param_blkmtd_erasesz(char *str) +{ + erasesz = simple_strtol(str, NULL, 0); + return 1; +} + + +static int __init param_blkmtd_ro(char *str) +{ + ro = simple_strtol(str, NULL, 0); + return 1; +} + + +static int __init param_blkmtd_bs(char *str) +{ + bs = simple_strtol(str, NULL, 0); + return 1; +} + + +static int __init param_blkmtd_count(char *str) +{ + count = simple_strtol(str, NULL, 0); + return 1; +} + +__setup("blkmtd_device=", param_blkmtd_device); +__setup("blkmtd_erasesz=", param_blkmtd_erasesz); +__setup("blkmtd_ro=", param_blkmtd_ro); +__setup("blkmtd_bs=", param_blkmtd_bs); +__setup("blkmtd_count=", param_blkmtd_count); + +#endif + + /* for a given size and initial erase size, calculate the number and size of each erase region */ static int __init calc_erase_regions(struct mtd_erase_region_info *info, size_t erase_size, size_t total_size) @@ -840,12 +1050,16 @@ } +extern kdev_t name_to_kdev_t(char *line) __init; + /* Startup */ static int __init init_blkmtd(void) { +#ifdef MODULE struct file *file = NULL; struct inode *inode; - mtd_raw_dev_data_t *rawdevice = NULL; +#endif + int maj, min; int i, blocksize, blocksize_bits; loff_t size = 0; @@ -854,15 +1068,12 @@ kdev_t rdev; int err; int mode; - int totalsize = 0, total_sectors = 0; int regions; - mtd_info = NULL; - - // Check args + /* Check args */ if(device == 0) { printk("blkmtd: error, missing `device' name\n"); - return 1; + return -EINVAL; } if(ro) @@ -871,12 +1082,25 @@ if(erasesz) erase_size = erasesz; - DEBUG(1, "blkmtd: got device = `%s' erase size = %dK readonly = %s\n", device, erase_size, readonly ? "yes" : "no"); - // Get a handle on the device + if(wqs) { + if(wqs < 16) + wqs = 16; + if(wqs > 4*WRITE_QUEUE_SZ) + wqs = 4*WRITE_QUEUE_SZ; + write_queue_sz = wqs; + } + + DEBUG(1, "blkmtd: device = `%s' erase size = %dK readonly = %s queue size = %d\n", + device, erase_size, readonly ? "yes" : "no", write_queue_sz); + /* Get a handle on the device */ mode = (readonly) ? O_RDONLY : O_RDWR; + +#ifdef MODULE + file = filp_open(device, mode, 0); if(IS_ERR(file)) { - DEBUG(2, "blkmtd: open_namei returned %ld\n", PTR_ERR(file)); + printk("blkmtd: error, cant open device %s\n", device); + DEBUG(2, "blkmtd: filp_open returned %ld\n", PTR_ERR(file)); return 1; } @@ -889,11 +1113,19 @@ return 1; } rdev = inode->i_rdev; - //filp_close(file, NULL); - DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", - MAJOR(rdev), MINOR(rdev)); - maj = MAJOR(rdev); - min = MINOR(rdev); + filp_close(file, NULL); +#else + rdev = name_to_kdev_t(device); +#endif + + maj = major(rdev); + min = minor(rdev); + DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", maj, min); + + if(kdev_none(rdev)) { + printk("blkmtd: bad block device: `%s'\n", device); + return 1; + } if(maj == MTD_BLOCK_MAJOR) { printk("blkmtd: attempting to use an MTD device as a block device\n"); @@ -918,116 +1150,159 @@ size = ((loff_t) blk_size[maj][min] << BLOCK_SIZE_BITS) >> blocksize_bits; } } - total_sectors = size; size *= blocksize; - totalsize = size; DEBUG(1, "blkmtd: size = %ld\n", (long int)size); if(size == 0) { printk("blkmtd: cant determine size\n"); return 1; } - rawdevice = (mtd_raw_dev_data_t *)kmalloc(sizeof(mtd_raw_dev_data_t), GFP_KERNEL); - if(rawdevice == NULL) { + + mtd_rawdevice = (mtd_raw_dev_data_t *)kmalloc(sizeof(mtd_raw_dev_data_t), GFP_KERNEL); + if(mtd_rawdevice == NULL) { err = -ENOMEM; goto init_err; } - memset(rawdevice, 0, sizeof(mtd_raw_dev_data_t)); - // get the block device - rawdevice->binding = bdget(kdev_t_to_nr(MKDEV(maj, min))); - err = blkdev_get(rawdevice->binding, mode, 0, BDEV_RAW); + memset(mtd_rawdevice, 0, sizeof(mtd_raw_dev_data_t)); + /* get the block device */ + mtd_rawdevice->binding = bdget(kdev_t_to_nr(mk_kdev(maj, min))); + err = blkdev_get(mtd_rawdevice->binding, mode, 0, BDEV_RAW); if (err) { goto init_err; } - rawdevice->totalsize = totalsize; - rawdevice->total_sectors = total_sectors; - rawdevice->sector_size = blocksize; - rawdevice->sector_bits = blocksize_bits; - rawdevice->readonly = readonly; - - DEBUG(2, "sector_size = %d, sector_bits = %d\n", rawdevice->sector_size, rawdevice->sector_bits); - - mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); - if (mtd_info == NULL) { - err = -ENOMEM; + mtd_rawdevice->totalsize = size; + mtd_rawdevice->sector_size = blocksize; + mtd_rawdevice->sector_bits = blocksize_bits; + mtd_rawdevice->readonly = readonly; + + /* See if device ends on page boundary */ + if(size % PAGE_SIZE) { + mtd_rawdevice->partial_last_page = size >> PAGE_SHIFT; + mtd_rawdevice->last_page_sectors = (size & (PAGE_SIZE-1)) >> blocksize_bits; + } + + DEBUG(2, "sector_size = %d, sector_bits = %d, partial_last_page = %d last_page_sectors = %d\n", + mtd_rawdevice->sector_size, mtd_rawdevice->sector_bits, + mtd_rawdevice->partial_last_page, mtd_rawdevice->last_page_sectors); + + /* Setup the MTD structure */ + /* make the name contain the block device in */ + mtd_rawdevice->mtd_info.name = kmalloc(9 + strlen(device), GFP_KERNEL); + if(mtd_rawdevice->mtd_info.name == NULL) goto init_err; - } - memset(mtd_info, 0, sizeof(*mtd_info)); - // Setup the MTD structure - mtd_info->name = "blkmtd block device"; + sprintf(mtd_rawdevice->mtd_info.name, "blkmtd: %s", device); if(readonly) { - mtd_info->type = MTD_ROM; - mtd_info->flags = MTD_CAP_ROM; - mtd_info->erasesize = erase_size << 10; + mtd_rawdevice->mtd_info.type = MTD_ROM; + mtd_rawdevice->mtd_info.flags = MTD_CAP_ROM; + mtd_rawdevice->mtd_info.erasesize = erase_size << 10; } else { - mtd_info->type = MTD_RAM; - mtd_info->flags = MTD_CAP_RAM; - mtd_info->erasesize = erase_size << 10; - } - mtd_info->size = size; - mtd_info->erase = blkmtd_erase; - mtd_info->read = blkmtd_read; - mtd_info->write = blkmtd_write; - mtd_info->sync = blkmtd_sync; - mtd_info->point = 0; - mtd_info->unpoint = 0; + mtd_rawdevice->mtd_info.type = MTD_RAM; + mtd_rawdevice->mtd_info.flags = MTD_CAP_RAM; + mtd_rawdevice->mtd_info.erasesize = erase_size << 10; + } + mtd_rawdevice->mtd_info.size = size; + mtd_rawdevice->mtd_info.erase = blkmtd_erase; + mtd_rawdevice->mtd_info.read = blkmtd_read; + mtd_rawdevice->mtd_info.write = blkmtd_write; + mtd_rawdevice->mtd_info.sync = blkmtd_sync; + mtd_rawdevice->mtd_info.point = 0; + mtd_rawdevice->mtd_info.unpoint = 0; - mtd_info->priv = rawdevice; + mtd_rawdevice->mtd_info.priv = mtd_rawdevice; regions = calc_erase_regions(NULL, erase_size << 10, size); DEBUG(1, "blkmtd: init: found %d erase regions\n", regions); - mtd_info->eraseregions = kmalloc(regions * sizeof(struct mtd_erase_region_info), GFP_KERNEL); - if(mtd_info->eraseregions == NULL) { + mtd_rawdevice->mtd_info.eraseregions = kmalloc(regions * sizeof(struct mtd_erase_region_info), GFP_KERNEL); + if(mtd_rawdevice->mtd_info.eraseregions == NULL) { + err = -ENOMEM; + goto init_err; } - mtd_info->numeraseregions = regions; - calc_erase_regions(mtd_info->eraseregions, erase_size << 10, size); + mtd_rawdevice->mtd_info.numeraseregions = regions; + calc_erase_regions(mtd_rawdevice->mtd_info.eraseregions, erase_size << 10, size); /* setup the page cache info */ - INIT_LIST_HEAD(&rawdevice->as.clean_pages); - INIT_LIST_HEAD(&rawdevice->as.dirty_pages); - INIT_LIST_HEAD(&rawdevice->as.locked_pages); - rawdevice->as.nrpages = 0; - rawdevice->as.a_ops = &blkmtd_aops; - rawdevice->as.host = inode; - rawdevice->as.i_mmap = NULL; - rawdevice->as.i_mmap_shared = NULL; - spin_lock_init(&rawdevice->as.i_shared_lock); - rawdevice->as.gfp_mask = GFP_KERNEL; - rawdevice->file = file; - - file->private_data = rawdevice; + + mtd_rawdevice->as.nrpages = 0; + INIT_LIST_HEAD(&mtd_rawdevice->as.clean_pages); + INIT_LIST_HEAD(&mtd_rawdevice->as.dirty_pages); + INIT_LIST_HEAD(&mtd_rawdevice->as.locked_pages); + mtd_rawdevice->as.host = NULL; + spin_lock_init(&(mtd_rawdevice->as.i_shared_lock)); + + mtd_rawdevice->as.a_ops = &blkmtd_aops; + INIT_LIST_HEAD(&mtd_rawdevice->as.i_mmap); + INIT_LIST_HEAD(&mtd_rawdevice->as.i_mmap_shared); + mtd_rawdevice->as.gfp_mask = GFP_KERNEL; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) - mtd_info->module = THIS_MODULE; + mtd_rawdevice->mtd_info.module = THIS_MODULE; #endif - if (add_mtd_device(mtd_info)) { - err = -EIO; - goto init_err; - } - init_waitqueue_head(&thr_wq); - init_waitqueue_head(&mtbd_sync_wq); - DEBUG(3, "blkmtd: init: kernel task @ %p\n", write_queue_task); - DEBUG(2, "blkmtd: init: starting kernel task\n"); - kernel_thread(write_queue_task, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); - DEBUG(2, "blkmtd: init: started\n"); - printk("blkmtd loaded: version = %s using %s erase_size = %dK %s\n", VERSION, device, erase_size, (readonly) ? "(read-only)" : ""); - return 0; + if (add_mtd_device(&mtd_rawdevice->mtd_info)) { + err = -EIO; + goto init_err; + } + if(!mtd_rawdevice->readonly) { + /* Allocate the write queue */ + write_queue = kmalloc(write_queue_sz * sizeof(mtdblkdev_write_queue_t), GFP_KERNEL); + if(!write_queue) { + err = -ENOMEM; + goto init_err; + } + /* Set up the erase page */ + erase_page = alloc_pages(GFP_KERNEL, 0); + if(erase_page == NULL) { + err = -ENOMEM; + goto init_err; + } + memset(page_address(erase_page), 0xff, PAGE_SIZE); + lock_page(erase_page); + + init_waitqueue_head(&thr_wq); + init_waitqueue_head(&mtbd_sync_wq); + DEBUG(3, "blkmtd: init: kernel task @ %p\n", write_queue_task); + DEBUG(2, "blkmtd: init: starting kernel task\n"); + kernel_thread(write_queue_task, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + DEBUG(2, "blkmtd: init: started\n"); + printk("blkmtd loaded: version = %s using %s erase_size = %dK %s\n", + VERSION, device, erase_size, (readonly) ? "(read-only)" : ""); + } + +#ifdef BLKMTD_PROC_DEBUG + /* create proc entry */ + DEBUG(2, "Creating /proc/blkmtd_debug\n"); + blkmtd_proc = create_proc_read_entry("blkmtd_debug", 0444, + NULL, blkmtd_proc_read, NULL); + if(blkmtd_proc == NULL) { + printk("Cant create /proc/blkmtd_debug\n"); + } else { + blkmtd_proc->owner = THIS_MODULE; + } +#endif + + /* Everything is ok if we got here */ + return 0; + init_err: - if(!rawdevice) { - if(rawdevice->binding) - blkdev_put(rawdevice->binding, BDEV_RAW); - kfree(rawdevice); - rawdevice = NULL; - } - if(mtd_info) { - if(mtd_info->eraseregions) - kfree(mtd_info->eraseregions); - kfree(mtd_info); - mtd_info = NULL; - } - return err; + if(mtd_rawdevice) { + if(mtd_rawdevice->mtd_info.eraseregions) + kfree(mtd_rawdevice->mtd_info.eraseregions); + if(mtd_rawdevice->mtd_info.name) + kfree(mtd_rawdevice->mtd_info.name); + if(mtd_rawdevice->binding) + blkdev_put(mtd_rawdevice->binding, BDEV_RAW); + kfree(mtd_rawdevice); + } + + if(write_queue) { + kfree(write_queue); + write_queue = NULL; + } + + if(erase_page) + __free_pages(erase_page, 0); + return err; } module_init(init_blkmtd); diff -Nru a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c --- a/drivers/mtd/devices/pmc551.c Thu Mar 7 18:17:40 2002 +++ b/drivers/mtd/devices/pmc551.c Thu Mar 7 18:17:40 2002 @@ -715,7 +715,7 @@ } /* - * This is needed untill the driver is capable of reading the + * This is needed until the driver is capable of reading the * onboard I2C SROM to discover the "real" memory size. */ if(msize) { diff -Nru a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c --- a/drivers/mtd/ftl.c Thu Mar 7 18:17:41 2002 +++ b/drivers/mtd/ftl.c Thu Mar 7 18:17:41 2002 @@ -1144,7 +1144,7 @@ case BLKROSET: case BLKROGET: case BLKFLSBUF: - ret = blk_ioctl(inode->i_rdev, cmd, arg); + ret = blk_ioctl(inode->i_bdev, cmd, arg); break; default: ret = -EINVAL; diff -Nru a/drivers/mtd/maps/elan-104nc.c b/drivers/mtd/maps/elan-104nc.c --- a/drivers/mtd/maps/elan-104nc.c Thu Mar 7 18:17:44 2002 +++ b/drivers/mtd/maps/elan-104nc.c Thu Mar 7 18:17:44 2002 @@ -213,7 +213,7 @@ /* MTD device for all of the flash. */ static struct mtd_info *all_mtd; -static void __exit cleanup_elan_104nc(void) +static void cleanup_elan_104nc(void) { if( all_mtd ) { del_mtd_partitions( all_mtd ); diff -Nru a/drivers/mtd/maps/sbc_gxx.c b/drivers/mtd/maps/sbc_gxx.c --- a/drivers/mtd/maps/sbc_gxx.c Thu Mar 7 18:17:45 2002 +++ b/drivers/mtd/maps/sbc_gxx.c Thu Mar 7 18:17:45 2002 @@ -221,7 +221,7 @@ /* MTD device for all of the flash. */ static struct mtd_info *all_mtd; -static void __exit cleanup_sbc_gxx(void) +static void cleanup_sbc_gxx(void) { if( all_mtd ) { del_mtd_partitions( all_mtd ); diff -Nru a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c --- a/drivers/mtd/mtdblock.c Thu Mar 7 18:17:42 2002 +++ b/drivers/mtd/mtdblock.c Thu Mar 7 18:17:42 2002 @@ -16,11 +16,8 @@ #define MAJOR_NR MTD_BLOCK_MAJOR #define DEVICE_NAME "mtdblock" -#define DEVICE_REQUEST mtdblock_request #define DEVICE_NR(device) (device) -#define DEVICE_ON(device) -#define DEVICE_OFF(device) -#define DEVICE_NO_RANDOM +#define LOCAL_END_REQUEST #include /* for old kernels... */ #ifndef QUEUE_EMPTY @@ -283,7 +280,7 @@ if (!inode) return -EINVAL; - dev = MINOR(inode->i_rdev); + dev = minor(inode->i_rdev); if (dev >= MAX_MTD_DEVICES) return -EINVAL; @@ -373,7 +370,7 @@ invalidate_device(inode->i_rdev, 1); - dev = MINOR(inode->i_rdev); + dev = minor(inode->i_rdev); mtdblk = mtdblks[dev]; down(&mtdblk->cache_sem); @@ -417,18 +414,20 @@ INIT_REQUEST; req = CURRENT; spin_unlock_irq(&QUEUE->queue_lock); - mtdblk = mtdblks[MINOR(req->rq_dev)]; + mtdblk = mtdblks[minor(req->rq_dev)]; res = 0; - if (MINOR(req->rq_dev) >= MAX_MTD_DEVICES) + if (minor(req->rq_dev) >= MAX_MTD_DEVICES) panic(__FUNCTION__": minor out of bound"); + if (req->flags & REQ_CMD) + goto end_req; + if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9)) goto end_req; // Handle the request - switch (req->cmd) - { + switch (rq_data_dir(CURRENT)) { int err; case READ: @@ -459,7 +458,11 @@ end_req: spin_lock_irq(&QUEUE->queue_lock); - end_request(res); + if (!end_that_request_first(req, res, req->hard_cur_sectors)) { + blkdev_dequeue_request(req); + end_that_request_last(req); + } + } } @@ -519,7 +522,7 @@ { struct mtdblk_dev *mtdblk; - mtdblk = mtdblks[MINOR(inode->i_rdev)]; + mtdblk = mtdblks[minor(inode->i_rdev)]; #ifdef PARANOIA if (!mtdblk) @@ -537,7 +540,7 @@ if(!capable(CAP_SYS_ADMIN)) return -EACCES; #endif - fsync_dev(inode->i_rdev); + fsync_bdev(inode->i_bdev); invalidate_buffers(inode->i_rdev); down(&mtdblk->cache_sem); write_cached_data(mtdblk); @@ -597,6 +600,8 @@ } #endif +static spinlock_t mtddev_lock = SPIN_LOCK_UNLOCKED; + int __init init_mtdblock(void) { int i; @@ -630,7 +635,7 @@ blksize_size[MAJOR_NR] = mtd_blksizes; blk_size[MAJOR_NR] = mtd_sizes; - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request); + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request, &mtddev_lock); kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND); return 0; } diff -Nru a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c --- a/drivers/mtd/mtdblock_ro.c Thu Mar 7 18:17:44 2002 +++ b/drivers/mtd/mtdblock_ro.c Thu Mar 7 18:17:44 2002 @@ -16,13 +16,10 @@ #include #include +#define LOCAL_END_REQUEST #define MAJOR_NR MTD_BLOCK_MAJOR #define DEVICE_NAME "mtdblock" -#define DEVICE_REQUEST mtdblock_request #define DEVICE_NR(device) (device) -#define DEVICE_ON(device) -#define DEVICE_OFF(device) -#define DEVICE_NO_RANDOM #include #if LINUX_VERSION_CODE < 0x20300 @@ -52,7 +49,7 @@ if (inode == 0) return -EINVAL; - dev = MINOR(inode->i_rdev); + dev = minor(inode->i_rdev); mtd = get_mtd_device(NULL, dev); if (!mtd) @@ -81,7 +78,7 @@ invalidate_device(inode->i_rdev, 1); - dev = MINOR(inode->i_rdev); + dev = minor(inode->i_rdev); mtd = __get_mtd_device(NULL, dev); if (!mtd) { @@ -97,8 +94,15 @@ DEBUG(1, "ok\n"); release_return(0); -} +} +static inline void mtdblock_end_request(struct request *req, int uptodate) +{ + if (end_that_request_first(req, uptodate, req->hard_cur_sectors)) + return; + blkdev_dequeue_request(req); + end_that_request_last(req); +} static void mtdblock_request(RQFUNC_ARG) { @@ -113,19 +117,19 @@ INIT_REQUEST; current_request = CURRENT; - if (MINOR(current_request->rq_dev) >= MAX_MTD_DEVICES) + if (minor(current_request->rq_dev) >= MAX_MTD_DEVICES) { printk("mtd: Unsupported device!\n"); - end_request(0); + mtdblock_end_request(current_request, 0); continue; } // Grab our MTD structure - mtd = __get_mtd_device(NULL, MINOR(current_request->rq_dev)); + mtd = __get_mtd_device(NULL, minor(current_request->rq_dev)); if (!mtd) { printk("MTD device %d doesn't appear to exist any more\n", CURRENT_DEV); - end_request(0); + mtdblock_end_request(current_request, 0); } if (current_request->sector << 9 > mtd->size || @@ -133,7 +137,7 @@ { printk("mtd: Attempt to read past end of device!\n"); printk("size: %x, sector: %lx, nr_sectors %lx\n", mtd->size, current_request->sector, current_request->nr_sectors); - end_request(0); + mtdblock_end_request(current_request, 0); continue; } @@ -192,7 +196,7 @@ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) spin_lock_irq(&io_request_lock); #endif - end_request(res); + mtdblock_end_request(current_request, res); } } @@ -203,7 +207,7 @@ { struct mtd_info *mtd; - mtd = __get_mtd_device(NULL, MINOR(inode->i_rdev)); + mtd = __get_mtd_device(NULL, minor(inode->i_rdev)); if (!mtd) return -EINVAL; @@ -217,7 +221,7 @@ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) if(!capable(CAP_SYS_ADMIN)) return -EACCES; #endif - fsync_dev(inode->i_rdev); + fsync_bdev(inode->i_bdev); invalidate_buffers(inode->i_rdev); if (mtd->sync) mtd->sync(mtd); diff -Nru a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c --- a/drivers/mtd/mtdchar.c Thu Mar 7 18:17:45 2002 +++ b/drivers/mtd/mtdchar.c Thu Mar 7 18:17:45 2002 @@ -64,7 +64,7 @@ static int mtd_open(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + int minor = minor(inode->i_rdev); int devnum = minor >> 1; struct mtd_info *mtd; diff -Nru a/drivers/mtd/nand/spia.c b/drivers/mtd/nand/spia.c --- a/drivers/mtd/nand/spia.c Thu Mar 7 18:17:37 2002 +++ b/drivers/mtd/nand/spia.c Thu Mar 7 18:17:37 2002 @@ -113,7 +113,7 @@ this->ALE = 0x02; this->NCE = 0x04; - /* Scan to find existance of the device */ + /* Scan to find existence of the device */ if (nand_scan (spia_mtd)) { kfree (spia_mtd); return -ENXIO; diff -Nru a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c --- a/drivers/mtd/nftlcore.c Thu Mar 7 18:17:37 2002 +++ b/drivers/mtd/nftlcore.c Thu Mar 7 18:17:37 2002 @@ -156,7 +156,7 @@ #if LINUX_VERSION_CODE < 0x20328 resetup_one_dev(&nftl_gendisk, firstfree); #else - grok_partitions(MKDEV(MAJOR_NR,firstfree<nr_sects); #endif } @@ -779,7 +779,7 @@ struct NFTLrecord *nftl; int res; - nftl = NFTLs[MINOR(inode->i_rdev) >> NFTL_PARTN_BITS]; + nftl = NFTLs[minor(inode->i_rdev) >> NFTL_PARTN_BITS]; if (!nftl) return -EINVAL; @@ -795,7 +795,7 @@ } case BLKFLSBUF: if (!capable(CAP_SYS_ADMIN)) return -EACCES; - fsync_dev(inode->i_rdev); + fsync_bdev(inode->i_bdev); invalidate_buffers(inode->i_rdev); if (nftl->mtd->sync) nftl->mtd->sync(nftl->mtd); @@ -823,7 +823,7 @@ case BLKROSET: case BLKROGET: case BLKSSZGET: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); #endif default: @@ -853,7 +853,7 @@ (req->cmd == READ) ? "Read " : "Write", req->sector, req->current_nr_sectors); - dev = MINOR(req->rq_dev); + dev = minor(req->rq_dev); block = req->sector; nsect = req->current_nr_sectors; buffer = req->buffer; @@ -875,7 +875,7 @@ if (block + nsect > part_table[dev].nr_sects) { /* access past the end of device */ printk("nftl%c%d: bad access: block = %d, count = %d\n", - (MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect); + (minor(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect); up(&nftl->mutex); res = 0; /* fail */ goto repeat; @@ -933,7 +933,7 @@ static int nftl_open(struct inode *ip, struct file *fp) { - int nftlnum = MINOR(ip->i_rdev) >> NFTL_PARTN_BITS; + int nftlnum = minor(ip->i_rdev) >> NFTL_PARTN_BITS; struct NFTLrecord *thisNFTL; thisNFTL = NFTLs[nftlnum]; @@ -947,7 +947,7 @@ #endif if (!thisNFTL) { DEBUG(MTD_DEBUG_LEVEL2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n", - nftlnum, ip->i_rdev, ip, fp); + nftlnum, minor(ip->i_rdev), ip, fp); return -ENODEV; } @@ -967,7 +967,7 @@ { struct NFTLrecord *thisNFTL; - thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16]; + thisNFTL = NFTLs[minor(inode->i_rdev) / 16]; DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n"); diff -Nru a/drivers/net/3c503.c b/drivers/net/3c503.c --- a/drivers/net/3c503.c Thu Mar 7 18:17:45 2002 +++ b/drivers/net/3c503.c Thu Mar 7 18:17:45 2002 @@ -692,9 +692,11 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_EL2_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_EL2_CARDS) "i"); MODULE_PARM(xcvr, "1-" __MODULE_STRING(MAX_EL2_CARDS) "i"); -MODULE_PARM_DESC(io, "EtherLink II I/O base address(es)"); -MODULE_PARM_DESC(irq, "EtherLink II IRQ number(s) (assigned)"); -MODULE_PARM_DESC(xcvr, "EtherLink II tranceiver(s) (0=internal, 1=external)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); +MODULE_PARM_DESC(xcvr, "tranceiver(s) (0=internal, 1=external)"); +MODULE_DESCRIPTION("3Com ISA EtherLink II, II/16 (3c503, 3c503/16) driver"); +MODULE_LICENSE("GPL"); /* This is set up so that only a single autoprobe takes place per call. ISA device autoprobes on a running machine are not recommended. */ @@ -742,7 +744,6 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); /* diff -Nru a/drivers/net/3c505.c b/drivers/net/3c505.c --- a/drivers/net/3c505.c Thu Mar 7 18:17:44 2002 +++ b/drivers/net/3c505.c Thu Mar 7 18:17:44 2002 @@ -1350,87 +1350,6 @@ } } -/** - * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls - * @dev: network interface on which out-of-band action is to be performed - * @useraddr: userspace address to which data is to be read and returned - * - * Process the various commands of the SIOCETHTOOL interface. - */ - -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) -{ - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - debug = edata.data; - return 0; - } - - default: - break; - } - - return -EOPNOTSUPP; -} - -/** - * netdev_ioctl: Handle network interface ioctls - * @dev: network interface on which out-of-band action is to be performed - * @rq: user request data - * @cmd: command issued by user - * - * Process the various out-of-band ioctls passed to this driver. - */ - -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) -{ - int rc = 0; - - switch (cmd) { - case SIOCETHTOOL: - rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - break; - - default: - rc = -EOPNOTSUPP; - break; - } - - return rc; -} - - /****************************************************** * * initialise Etherlink Plus board diff -Nru a/drivers/net/3c509.c b/drivers/net/3c509.c --- a/drivers/net/3c509.c Thu Mar 7 18:17:37 2002 +++ b/drivers/net/3c509.c Thu Mar 7 18:17:37 2002 @@ -1103,14 +1103,15 @@ MODULE_PARM(irq,"1-8i"); MODULE_PARM(xcvr,"1-8i"); MODULE_PARM(max_interrupt_work, "i"); -MODULE_PARM_DESC(debug, "EtherLink III debug level (0-6)"); -MODULE_PARM_DESC(irq, "EtherLink III IRQ number(s) (assigned)"); -MODULE_PARM_DESC(xcvr,"EtherLink III tranceiver(s) (0=internal, 1=external)"); -MODULE_PARM_DESC(max_interrupt_work, "EtherLink III maximum events handled per interrupt"); +MODULE_PARM_DESC(debug, "debug level (0-6)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); +MODULE_PARM_DESC(xcvr,"tranceiver(s) (0=internal, 1=external)"); +MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt"); #ifdef __ISAPNP__ MODULE_PARM(nopnp, "i"); -MODULE_PARM_DESC(nopnp, "EtherLink III disable ISA PnP support (0-1)"); +MODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)"); #endif /* __ISAPNP__ */ +MODULE_DESCRIPTION("3Com Etherlink III (3c509, 3c509B) ISA/PnP ethernet driver"); int init_module(void) diff -Nru a/drivers/net/3c59x.c b/drivers/net/3c59x.c --- a/drivers/net/3c59x.c Thu Mar 7 18:17:36 2002 +++ b/drivers/net/3c59x.c Thu Mar 7 18:17:36 2002 @@ -2916,7 +2916,7 @@ static struct pci_driver vortex_driver = { name: "3c59x", probe: vortex_init_one, - remove: vortex_remove_one, + remove: __devexit_p(vortex_remove_one), id_table: vortex_pci_tbl, #ifdef CONFIG_PM suspend: vortex_suspend, diff -Nru a/drivers/net/8139cp.c b/drivers/net/8139cp.c --- a/drivers/net/8139cp.c Thu Mar 7 18:17:44 2002 +++ b/drivers/net/8139cp.c Thu Mar 7 18:17:44 2002 @@ -1,7 +1,8 @@ /* 8139cp.c: A Linux PCI Ethernet driver for the RealTek 8139C+ chips. */ /* - Copyright 2001 Jeff Garzik + Copyright 2001,2002 Jeff Garzik + Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) [tg3.c] Copyright (C) 2000, 2001 David S. Miller (davem@redhat.com) [sungem.c] Copyright 2001 Manfred Spraul [natsemi.c] Copyright 1999-2001 by Donald Becker. [natsemi.c] @@ -29,37 +30,45 @@ * Consider Rx interrupt mitigation using TimerIntr * Implement 8139C+ statistics dump; maybe not... h/w stats can be reset only by software reset - * Rx checksumming * Tx checksumming + * Handle netif_rx return value * ETHTOOL_GREGS, ETHTOOL_[GS]WOL, - * Jumbo frames / dev->change_mtu * Investigate using skb->priority with h/w VLAN priority * Investigate using High Priority Tx Queue with skb->priority * Adjust Rx FIFO threshold and Max Rx DMA burst on Rx FIFO error * Adjust Tx FIFO threshold and Max Tx DMA burst on Tx FIFO error * Implement Tx software interrupt mitigation via Tx descriptor bit + * The real minimum of CP_MIN_MTU is 4 bytes. However, + for this to be supported, one must(?) turn on packet padding. */ #define DRV_NAME "8139cp" -#define DRV_VERSION "0.0.6cvs" -#define DRV_RELDATE "Nov 19, 2001" +#define DRV_VERSION "0.0.7" +#define DRV_RELDATE "Feb 27, 2002" +#include #include #include +#include #include #include #include #include #include #include -#include #include +#include +#include #include #include +#define CP_VLAN_TAG_USED 0 +#define CP_VLAN_TX_TAG(tx_desc,vlan_tag_value) \ + do { (tx_desc)->opts2 = 0; } while (0) + /* These identify the driver base version and may not be removed. */ static char version[] __devinitdata = KERN_INFO DRV_NAME " 10/100 PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; @@ -107,8 +116,11 @@ #define TX_EARLY_THRESH 256 /* Early Tx threshold, in bytes */ /* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (6*HZ) +#define TX_TIMEOUT (6*HZ) +/* hardware minimum and maximum for a single frame's data payload */ +#define CP_MIN_MTU 60 /* TODO: allow lower, but pad */ +#define CP_MAX_MTU 4096 enum { /* NIC register offsets */ @@ -150,12 +162,17 @@ IPCS = (1 << 18), /* Calculate IP checksum */ UDPCS = (1 << 17), /* Calculate UDP/IP checksum */ TCPCS = (1 << 16), /* Calculate TCP/IP checksum */ + TxVlanTag = (1 << 17), /* Add VLAN tag */ + RxVlanTagged = (1 << 16), /* Rx VLAN tag available */ IPFail = (1 << 15), /* IP checksum failed */ UDPFail = (1 << 14), /* UDP/IP checksum failed */ TCPFail = (1 << 13), /* TCP/IP checksum failed */ NormalTxPoll = (1 << 6), /* One or more normal Tx packets to send */ PID1 = (1 << 17), /* 2 protocol id bits: 0==non-IP, */ PID0 = (1 << 16), /* 1==UDP/IP, 2==TCP/IP, 3==IP */ + RxProtoTCP = 2, + RxProtoUDP = 1, + RxProtoIP = 3, TxFIFOUnder = (1 << 25), /* Tx FIFO underrun */ TxOWC = (1 << 22), /* Tx Out-of-window collision */ TxLinkFail = (1 << 21), /* Link failed during Tx of packet */ @@ -205,6 +222,7 @@ TxOn = (1 << 2), /* Tx mode enable */ /* C+ mode command register */ + RxVlanOn = (1 << 6), /* Rx VLAN de-tagging enable */ RxChkSum = (1 << 5), /* Rx checksum offload enable */ PCIMulRW = (1 << 3), /* Enable PCI read/write multiple */ CpRxOn = (1 << 1), /* Rx mode enable */ @@ -275,6 +293,10 @@ unsigned rx_buf_sz; dma_addr_t ring_dma; +#if CP_VLAN_TAG_USED + struct vlan_group *vlgrp; +#endif + u32 msg_enable; struct net_device_stats net_stats; @@ -321,14 +343,32 @@ }; MODULE_DEVICE_TABLE(pci, cp_pci_tbl); -static inline void cp_rx_skb (struct cp_private *cp, struct sk_buff *skb) +static inline void cp_set_rxbufsize (struct cp_private *cp) +{ + unsigned int mtu = cp->dev->mtu; + + if (mtu > ETH_DATA_LEN) + /* MTU + ethernet header + FCS + optional VLAN tag */ + cp->rx_buf_sz = mtu + ETH_HLEN + 8; + else + cp->rx_buf_sz = PKT_BUF_SZ; +} + +static inline void cp_rx_skb (struct cp_private *cp, struct sk_buff *skb, + struct cp_desc *desc) { skb->protocol = eth_type_trans (skb, cp->dev); cp->net_stats.rx_packets++; cp->net_stats.rx_bytes += skb->len; cp->dev->last_rx = jiffies; - netif_rx (skb); + +#if CP_VLAN_TAG_USED + if (cp->vlgrp && (desc->opts2 & RxVlanTagged)) { + vlan_hwaccel_rx(skb, cp->vlgrp, desc->opts2 & 0xffff); + } else +#endif + netif_rx(skb); } static void cp_rx_err_acct (struct cp_private *cp, unsigned rx_tail, @@ -411,13 +451,26 @@ cp_rx_err_acct(cp, rx_tail, status, len); dev_kfree_skb_irq(copy_skb); } else - cp_rx_skb(cp, copy_skb); + cp_rx_skb(cp, copy_skb, &cp->rx_ring[rx_tail]); cp->frag_skb = NULL; } else { cp->frag_skb = copy_skb; } } +static inline unsigned int cp_rx_csum_ok (u32 status) +{ + unsigned int protocol = (status >> 16) & 0x3; + + if (likely((protocol == RxProtoTCP) && (!(status & TCPFail)))) + return 1; + else if ((protocol == RxProtoUDP) && (!(status & UDPFail))) + return 1; + else if ((protocol == RxProtoIP) && (!(status & IPFail))) + return 1; + return 0; +} + static void cp_rx (struct cp_private *cp) { unsigned rx_tail = cp->rx_tail; @@ -427,13 +480,15 @@ u32 status, len; dma_addr_t mapping; struct sk_buff *skb, *new_skb; + struct cp_desc *desc; unsigned buflen; skb = cp->rx_skb[rx_tail].skb; if (!skb) BUG(); - rmb(); - status = le32_to_cpu(cp->rx_ring[rx_tail].opts1); + + desc = &cp->rx_ring[rx_tail]; + status = le32_to_cpu(desc->opts1); if (status & DescOwn) break; @@ -466,7 +521,13 @@ pci_unmap_single(cp->pdev, mapping, buflen, PCI_DMA_FROMDEVICE); - skb->ip_summed = CHECKSUM_NONE; + + /* Handle checksum offloading for incoming packets. */ + if (cp_rx_csum_ok(status)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; + skb_put(skb, len); mapping = @@ -475,15 +536,14 @@ buflen, PCI_DMA_FROMDEVICE); cp->rx_skb[rx_tail].skb = new_skb; - cp_rx_skb(cp, skb); + cp_rx_skb(cp, skb, desc); rx_next: if (rx_tail == (CP_RX_RING_SIZE - 1)) - cp->rx_ring[rx_tail].opts1 = - cpu_to_le32(DescOwn | RingEnd | cp->rx_buf_sz); + desc->opts1 = cpu_to_le32(DescOwn | RingEnd | + cp->rx_buf_sz); else - cp->rx_ring[rx_tail].opts1 = - cpu_to_le32(DescOwn | cp->rx_buf_sz); + desc->opts1 = cpu_to_le32(DescOwn | cp->rx_buf_sz); cp->rx_ring[rx_tail].opts2 = 0; cp->rx_ring[rx_tail].addr_lo = cpu_to_le32(mapping); rx_tail = NEXT_RX(rx_tail); @@ -592,6 +652,9 @@ struct cp_private *cp = dev->priv; unsigned entry; u32 eor; +#if CP_VLAN_TAG_USED + u32 vlan_tag = 0; +#endif spin_lock_irq(&cp->lock); @@ -601,6 +664,11 @@ return 1; } +#if CP_VLAN_TAG_USED + if (cp->vlgrp && vlan_tx_tag_present(skb)) + vlan_tag = TxVlanTag | vlan_tx_tag_get(skb); +#endif + entry = cp->tx_head; eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; if (skb_shinfo(skb)->nr_frags == 0) { @@ -610,7 +678,7 @@ len = skb->len; mapping = pci_map_single(cp->pdev, skb->data, len, PCI_DMA_TODEVICE); eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; - txd->opts2 = 0; + CP_VLAN_TX_TAG(txd, vlan_tag); txd->addr_lo = cpu_to_le32(mapping); wmb(); @@ -663,7 +731,7 @@ ctrl |= LastFrag; txd = &cp->tx_ring[entry]; - txd->opts2 = 0; + CP_VLAN_TX_TAG(txd, vlan_tag); txd->addr_lo = cpu_to_le32(mapping); wmb(); @@ -677,7 +745,7 @@ } txd = &cp->tx_ring[first_entry]; - txd->opts2 = 0; + CP_VLAN_TX_TAG(txd, vlan_tag); txd->addr_lo = cpu_to_le32(first_mapping); wmb(); @@ -705,9 +773,6 @@ return 0; } -/* Set or clear the multicast filter for this adaptor. - This routine is not state sensitive and need not be SMP locked. */ - static void __cp_set_rx_mode (struct net_device *dev) { struct cp_private *cp = dev->priv; @@ -812,6 +877,12 @@ printk(KERN_ERR "%s: hardware reset timeout\n", cp->dev->name); } +static inline void cp_start_hw (struct cp_private *cp) +{ + cpw8(Cmd, RxOn | TxOn); + cpw16(CpCmd, PCIMulRW | RxChkSum | CpRxOn | CpTxOn); +} + static void cp_init_hw (struct cp_private *cp) { struct net_device *dev = cp->dev; @@ -824,8 +895,7 @@ cpw32_f (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0))); cpw32_f (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4))); - cpw8(Cmd, RxOn | TxOn); - cpw16(CpCmd, PCIMulRW | CpRxOn | CpTxOn); + cp_start_hw(cp); cpw8(TxThresh, 0x06); /* XXX convert magic num to a constant */ __cp_set_rx_mode(dev); @@ -957,8 +1027,6 @@ if (netif_msg_ifup(cp)) printk(KERN_DEBUG "%s: enabling interface\n", dev->name); - cp->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); - rc = cp_alloc_rings(cp); if (rc) return rc; @@ -993,6 +1061,36 @@ return 0; } +static int cp_change_mtu(struct net_device *dev, int new_mtu) +{ + struct cp_private *cp = dev->priv; + int rc; + + /* check for invalid MTU, according to hardware limits */ + if (new_mtu < CP_MIN_MTU || new_mtu > CP_MAX_MTU) + return -EINVAL; + + /* if network interface not up, no need for complexity */ + if (!netif_running(dev)) { + cp_set_rxbufsize(cp); /* set new rx buf size */ + return 0; + } + + spin_lock_irq(&cp->lock); + + cp_stop_hw(cp); /* stop h/w and free rings */ + cp_clean_rings(cp); + + cp_set_rxbufsize(cp); /* set new rx buf size */ + + rc = cp_init_rings(cp); /* realloc and restart h/w */ + cp_start_hw(cp); + + spin_unlock_irq(&cp->lock); + + return rc; +} + static char mii_2_8139_map[8] = { BasicModeCtrl, BasicModeStatus, @@ -1127,7 +1225,30 @@ return rc; } +#if CP_VLAN_TAG_USED +static int cp_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) +{ + struct cp_private *cp = dev->priv; + spin_lock_irq(&cp->lock); + cp->vlgrp = grp; + cpw16(CpCmd, cpr16(CpCmd) | RxVlanOn); + spin_unlock_irq(&cp->lock); + + return 0; +} + +static void cp_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) +{ + struct cp_private *cp = dev->priv; + + spin_lock_irq(&cp->lock); + cpw16(CpCmd, cpr16(CpCmd) & ~RxVlanOn); + if (cp->vlgrp) + cp->vlgrp->vlan_devices[vid] = NULL; + spin_unlock_irq(&cp->lock); +} +#endif /* Serial EEPROM section. */ @@ -1231,6 +1352,7 @@ cp->mii_if.mdio_read = mdio_read; cp->mii_if.mdio_write = mdio_write; cp->mii_if.phy_id = CP_INTERNAL_PHY; + cp_set_rxbufsize(cp); rc = pci_enable_device(pdev); if (rc) @@ -1284,6 +1406,7 @@ dev->hard_start_xmit = cp_start_xmit; dev->get_stats = cp_get_stats; dev->do_ioctl = cp_ioctl; + dev->change_mtu = cp_change_mtu; #if 0 dev->tx_timeout = cp_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; @@ -1291,6 +1414,11 @@ #ifdef CP_TX_CHECKSUM dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM; #endif +#if CP_VLAN_TAG_USED + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + dev->vlan_rx_register = cp_vlan_rx_register; + dev->vlan_rx_kill_vid = cp_vlan_rx_kill_vid; +#endif dev->irq = pdev->irq; @@ -1371,7 +1499,7 @@ name: DRV_NAME, id_table: cp_pci_tbl, probe: cp_init_one, - remove: cp_remove_one, + remove: __devexit_p(cp_remove_one), }; static int __init cp_init (void) diff -Nru a/drivers/net/8139too.c b/drivers/net/8139too.c --- a/drivers/net/8139too.c Thu Mar 7 18:17:36 2002 +++ b/drivers/net/8139too.c Thu Mar 7 18:17:36 2002 @@ -2499,7 +2499,7 @@ name: DRV_NAME, id_table: rtl8139_pci_tbl, probe: rtl8139_init_one, - remove: rtl8139_remove_one, + remove: __devexit_p(rtl8139_remove_one), #ifdef CONFIG_PM suspend: rtl8139_suspend, resume: rtl8139_resume, diff -Nru a/drivers/net/Config.help b/drivers/net/Config.help --- a/drivers/net/Config.help Thu Mar 7 18:17:37 2002 +++ b/drivers/net/Config.help Thu Mar 7 18:17:37 2002 @@ -3,6 +3,10 @@ MIPS-32-based Baget embedded system. This chipset is better known via the NE2100 cards. +CONFIG_LASI_82596 + Say Y here to support the on-board Intel 82596 ethernet controller + built into Hewlett-Packard PA-RISC machines. + CONFIG_MIPS_JAZZ_SONIC This is the driver for the onboard card of MIPS Magnum 4000, Acer PICA, Olivetti M700-10 and a few other identical OEM systems. @@ -214,6 +218,12 @@ pppd, along with binaries of a patched pppd package can be found at: . +CONFIG_PPPOATM + Support PPP (Point to Point Protocol) encapsulated in ATM frames. + This implementation does not yet comply with section 8 of RFC2364, + which can lead to bad results if the ATM peer loses state and + changes its encapsulation unilaterally. + CONFIG_NET_RADIO Support for wireless LANs and everything having to do with radio, but not with amateur radio or FM broadcasting. @@ -787,6 +797,14 @@ Support for the Sun GEM chip, aka Sun GigabitEthernet/P 2.0. See also . +CONFIG_TIGON3 + This driver supports Broadcom Tigon3 based gigabit Ethernet cards. + + 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 . This is + recommended. The module will be called tg3.o. + CONFIG_MYRI_SBUS This driver supports MyriCOM Sbus gigabit Ethernet cards. @@ -806,6 +824,42 @@ say M here and read . This is recommended. The module will be called dl2k.o. +CONFIG_E1000 + This driver supports Intel(R) PRO/1000 gigabit ethernet family of + adapters, which includes: + + Controller Adapter Name Board IDs + ---------- ------------ --------- + 82542 PRO/1000 Gigabit Server Adapter 700262-xxx, + 717037-xxx + 82543 PRO/1000 F Server Adapter 738640-xxx, + A38888-xxx, + A06512-xxx + 82543 PRO/1000 T Server Adapter A19845-xxx, + A33948-xxx + 82544 PRO/1000 XT Server Adapter A51580-xxx + 82544 PRO/1000 XF Server Adapter A50484-xxx + 82544 PRO/1000 T Desktop Adapter A62947-xxx + + For more information on how to identify your adapter, go to the + Adapter & Driver ID Guide at: + + + + For general information and support, go to the Intel support + website at: + + + + More specific information on configuring the driver is in + . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called e1000.o. If you want to compile it as a + module, say M here and read as well + as . + CONFIG_LANCE If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from @@ -817,6 +871,10 @@ say M here and read . This is recommended. The module will be called lance.o. +CONFIG_MIPS_AU1000_ENET + If you have an Alchemy Semi AU1000 ethernet controller + on an SGI MIPS system, say Y. Otherwise, say N. + CONFIG_SGI_IOC3_ETH If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from @@ -1275,39 +1333,6 @@ . The module will be called apricot.o. -CONFIG_DE4X5 - This is support for the DIGITAL series of PCI/EISA Ethernet cards. - 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 - . More specific - information is contained in - . - - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called de4x5.o. If you want to compile it as a - module, say M here and read as well - as . - -CONFIG_TULIP - This driver is developed for the SMC EtherPower series Ethernet - cards and also works with cards based on the DECchip - 21040/21041/21140 (Tulip series) chips. Some LinkSys PCI cards are - of this type. (If your card is NOT SMC EtherPower 10/100 PCI - (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 - . More specific - information is contained in - . - - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called tulip.o. If you want to compile it as a - module, say M here and read as well - as . - CONFIG_DGRS This is support for the Digi International RightSwitch series of PCI/EISA Ethernet switch cards. These include the SE-4 and the SE-6 @@ -1338,6 +1363,11 @@ cards. Specifications and data at . +CONFIG_LP486E + Say Y here to support the 82596-based on-board Ethernet controller + for the Panther motherboard, which is one of the two shipped in the + Intel Professional Workstation. + CONFIG_ETH16I If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from @@ -1377,17 +1407,15 @@ a module, say M here and read as well as . -CONFIG_DM9102 - This driver is for DM9102(A)/DM9132/DM9801 compatible PCI cards from - Davicom (). If you have such a network - (Ethernet) card, say Y. Some information is contained in the file - . +CONFIG_VIA_RHINE_MMIO + This instructs the driver to use PCI shared memory (MMIO) instead of + programmed I/O ports (PIO). Enabling this gives an improvement in + processing time in parts of the driver. - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called dmfe.o. If you want to compile it as a - module, say M here and read as well - as . + It is not known if this works reliably on all "rhine" based cards, + but it has been tested successfully on some DFE-530TX adapters. + + If unsure, say N. CONFIG_ES3210 If you have a network (Ethernet) card of this type, say Y and read @@ -1419,12 +1447,6 @@ This driver is for the Sundance "Alta" chip. More specific information and updates are available from . - -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 - . CONFIG_ZNET The Zenith Z-Note notebook computer has a built-in network diff -Nru a/drivers/net/Config.in b/drivers/net/Config.in --- a/drivers/net/Config.in Thu Mar 7 18:17:38 2002 +++ b/drivers/net/Config.in Thu Mar 7 18:17:38 2002 @@ -148,25 +148,18 @@ fi if [ "$CONFIG_NET_PCI" = "y" ]; then dep_tristate ' AMD PCnet32 PCI support' CONFIG_PCNET32 $CONFIG_PCI - dep_tristate ' Adaptec Starfire support (EXPERIMENTAL)' CONFIG_ADAPTEC_STARFIRE $CONFIG_PCI $CONFIG_EXPERIMENTAL + dep_tristate ' Adaptec Starfire/DuraLAN support' CONFIG_ADAPTEC_STARFIRE $CONFIG_PCI if [ "$CONFIG_ISA" = "y" -o "$CONFIG_EISA" = "y" ]; then dep_tristate ' Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200 $CONFIG_EXPERIMENTAL fi dep_tristate ' Apricot Xen-II on board Ethernet' CONFIG_APRICOT $CONFIG_ISA dep_tristate ' CS89x0 support' CONFIG_CS89x0 $CONFIG_ISA - dep_tristate ' Early DECchip Tulip (dc2104x) PCI support (EXPERIMENTAL)' CONFIG_DE2104X $CONFIG_PCI $CONFIG_EXPERIMENTAL - dep_tristate ' DECchip Tulip (dc2114x) PCI support' CONFIG_TULIP $CONFIG_PCI - if [ "$CONFIG_TULIP" = "y" -o "$CONFIG_TULIP" = "m" ]; then - dep_bool ' New bus configuration (EXPERIMENTAL)' CONFIG_TULIP_MWI $CONFIG_EXPERIMENTAL - bool ' Use PCI shared mem for NIC registers' CONFIG_TULIP_MMIO - fi if [ "$CONFIG_PCI" = "y" -o "$CONFIG_EISA" = "y" ]; then - tristate ' Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5 tristate ' Digi Intl. RightSwitch SE-X support' CONFIG_DGRS fi - dep_tristate ' Davicom DM910x/DM980x support' CONFIG_DM9102 $CONFIG_PCI - dep_tristate ' EtherExpressPro/100 support' CONFIG_EEPRO100 $CONFIG_PCI + dep_tristate ' EtherExpressPro/100 support (eepro100, original Becker driver)' CONFIG_EEPRO100 $CONFIG_PCI + dep_tristate ' EtherExpressPro/100 support (e100, Alternate Intel driver)' CONFIG_E100 $CONFIG_PCI dep_tristate ' Mylex EISA LNE390A/B support (EXPERIMENTAL)' CONFIG_LNE390 $CONFIG_EISA $CONFIG_EXPERIMENTAL dep_tristate ' Myson MTD-8xx PCI Ethernet support' CONFIG_FEALNX $CONFIG_PCI dep_tristate ' National Semiconductor DP8381x series PCI Ethernet support' CONFIG_NATSEMI $CONFIG_PCI @@ -190,7 +183,6 @@ fi dep_tristate ' VIA Rhine support' CONFIG_VIA_RHINE $CONFIG_PCI dep_mbool ' Use MMIO instead of PIO (EXPERIMENTAL)' CONFIG_VIA_RHINE_MMIO $CONFIG_VIA_RHINE $CONFIG_EXPERIMENTAL - dep_tristate ' Winbond W89c840 Ethernet support' CONFIG_WINBOND_840 $CONFIG_PCI if [ "$CONFIG_OBSOLETE" = "y" ]; then dep_bool ' Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET $CONFIG_ISA fi @@ -231,11 +223,13 @@ bool ' Omit support for old Tigon I based AceNICs' CONFIG_ACENIC_OMIT_TIGON_I fi dep_tristate 'D-Link DL2000-based Gigabit Ethernet support' CONFIG_DL2K $CONFIG_PCI +dep_tristate 'Intel(R) PRO/1000 Gigabit Ethernet support' CONFIG_E1000 $CONFIG_PCI dep_tristate 'MyriCOM Gigabit Ethernet support' CONFIG_MYRI_SBUS $CONFIG_SBUS dep_tristate 'National Semiconduct DP83820 support' CONFIG_NS83820 $CONFIG_PCI dep_tristate 'Packet Engines Hamachi GNIC-II support' CONFIG_HAMACHI $CONFIG_PCI dep_tristate 'Packet Engines Yellowfin Gigabit-NIC support (EXPERIMENTAL)' CONFIG_YELLOWFIN $CONFIG_PCI $CONFIG_EXPERIMENTAL dep_tristate 'SysKonnect SK-98xx support' CONFIG_SK98LIN $CONFIG_PCI +dep_tristate 'Broadcom Tigon3 support' CONFIG_TIGON3 $CONFIG_PCI endmenu @@ -325,6 +319,9 @@ source drivers/net/wan/Config.in +if [ "$CONFIG_PCI" = "y" -o "$CONFIG_EISA" = "y" -o "$CONFIG_CARDBUS" != "n" ]; then + source drivers/net/tulip/Config.in +fi if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then source drivers/net/pcmcia/Config.in fi diff -Nru a/drivers/net/Makefile b/drivers/net/Makefile --- a/drivers/net/Makefile Thu Mar 7 18:17:41 2002 +++ b/drivers/net/Makefile Thu Mar 7 18:17:41 2002 @@ -8,7 +8,7 @@ obj-n := obj- := -mod-subdirs := appletalk arcnet fc irda tokenring pcmcia wireless wan +mod-subdirs := appletalk arcnet fc irda tokenring tulip pcmcia wireless wan O_TARGET := net.o @@ -21,8 +21,11 @@ list-multi := rcpci.o rcpci-objs := rcpci45.o rclanmtl.o -ifeq ($(CONFIG_TULIP),y) - obj-y += tulip/tulip.o +ifeq ($(CONFIG_E100),y) + obj-y += e100/e100.o +endif +ifeq ($(CONFIG_E1000),y) + obj-y += e1000/e1000.o endif ifeq ($(CONFIG_ISDN_PPP),y) @@ -31,7 +34,9 @@ subdir-$(CONFIG_NET_PCMCIA) += pcmcia subdir-$(CONFIG_NET_WIRELESS) += wireless -subdir-$(CONFIG_TULIP) += tulip +subdir-$(CONFIG_NET_TULIP) += tulip +subdir-$(CONFIG_E100) += e100 +subdir-$(CONFIG_E1000) += e1000 subdir-$(CONFIG_IRDA) += irda subdir-$(CONFIG_TR) += tokenring subdir-$(CONFIG_WAN) += wan @@ -69,7 +74,6 @@ obj-$(CONFIG_TLAN) += tlan.o obj-$(CONFIG_EPIC100) += epic100.o mii.o obj-$(CONFIG_SIS900) += sis900.o -obj-$(CONFIG_DM9102) += dmfe.o obj-$(CONFIG_YELLOWFIN) += yellowfin.o obj-$(CONFIG_ACENIC) += acenic.o obj-$(CONFIG_VETH) += veth.o @@ -77,6 +81,7 @@ obj-$(CONFIG_NS83820) += ns83820.o obj-$(CONFIG_STNIC) += stnic.o 8390.o obj-$(CONFIG_FEALNX) += fealnx.o mii.o +obj-$(CONFIG_TIGON3) += tg3.o ifeq ($(CONFIG_SK98LIN),y) obj-y += sk98lin/sk98lin.o @@ -99,7 +104,7 @@ obj-$(CONFIG_AIRONET4500_PROC) += aironet4500_proc.o obj-$(CONFIG_AIRONET4500_CS) += aironet4500_proc.o -obj-$(CONFIG_WINBOND_840) += winbond-840.o mii.o +obj-$(CONFIG_WINBOND_840) += mii.o obj-$(CONFIG_SUNDANCE) += sundance.o obj-$(CONFIG_HAMACHI) += hamachi.o obj-$(CONFIG_NET) += Space.o setup.o net_init.o loopback.o @@ -171,8 +176,6 @@ obj-$(CONFIG_DEPCA) += depca.o obj-$(CONFIG_EWRK3) += ewrk3.o obj-$(CONFIG_ATP) += atp.o -obj-$(CONFIG_DE4X5) += de4x5.o -obj-$(CONFIG_DE2104X) += de2104x.o obj-$(CONFIG_NI5010) += ni5010.o obj-$(CONFIG_NI52) += ni52.o obj-$(CONFIG_NI65) += ni65.o diff -Nru a/drivers/net/ac3200.c b/drivers/net/ac3200.c --- a/drivers/net/ac3200.c Thu Mar 7 18:17:38 2002 +++ b/drivers/net/ac3200.c Thu Mar 7 18:17:38 2002 @@ -346,9 +346,11 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_AC32_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_AC32_CARDS) "i"); MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_AC32_CARDS) "i"); -MODULE_PARM_DESC(io, "ac3200 I/O base adress(es)"); -MODULE_PARM_DESC(irq, "ac3200 IRQ number(s)"); -MODULE_PARM_DESC(mem, "ac3200 Memory base address(es)"); +MODULE_PARM_DESC(io, "I/O base adress(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s)"); +MODULE_PARM_DESC(mem, "Memory base address(es)"); +MODULE_DESCRIPTION("Ansel AC3200 EISA ethernet driver"); +MODULE_LICENSE("GPL"); int init_module(void) @@ -395,7 +397,6 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); /* diff -Nru a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c --- a/drivers/net/arcnet/com20020-pci.c Thu Mar 7 18:17:39 2002 +++ b/drivers/net/arcnet/com20020-pci.c Thu Mar 7 18:17:39 2002 @@ -160,7 +160,7 @@ name: "com20020", id_table: com20020pci_id_table, probe: com20020pci_probe, - remove: com20020pci_remove + remove: __devexit_p(com20020pci_remove) }; static int __init com20020pci_init(void) diff -Nru a/drivers/net/de2104x.c b/drivers/net/de2104x.c --- a/drivers/net/de2104x.c Thu Mar 7 18:17:40 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2240 +0,0 @@ -/* de2104x.c: A Linux PCI Ethernet driver for Intel/Digital 21040/1 chips. */ -/* - Copyright 2001 Jeff Garzik - - Copyright 1994, 1995 Digital Equipment Corporation. [de4x5.c] - Written/copyright 1994-2001 by Donald Becker. [tulip.c] - - This software may be used and distributed according to the terms of - the GNU General Public License (GPL), incorporated herein by reference. - Drivers based on or derived from this code fall under the GPL and must - retain the authorship, copyright and license notice. This file is not - a complete program and may only be used when the entire operating - system is licensed under the GPL. - - See the file COPYING in this distribution for more information. - - TODO, in rough priority order: - * Support forcing media type with a module parameter, - like dl2k.c/sundance.c - * Constants (module parms?) for Rx work limit - * Complete reset on PciErr - * Jumbo frames / dev->change_mtu - * Adjust Rx FIFO threshold and Max Rx DMA burst on Rx FIFO error - * Adjust Tx FIFO threshold and Max Tx DMA burst on Tx FIFO error - * Implement Tx software interrupt mitigation via - Tx descriptor bit - - */ - -#define DRV_NAME "de2104x" -#define DRV_VERSION "0.5.4" -#define DRV_RELDATE "Jan 1, 2002" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* These identify the driver base version and may not be removed. */ -static char version[] __initdata = -KERN_INFO DRV_NAME " PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; - -MODULE_AUTHOR("Jeff Garzik "); -MODULE_DESCRIPTION("Intel/Digital 21040/1 series PCI Ethernet driver"); -MODULE_LICENSE("GPL"); - -static int debug = -1; -MODULE_PARM (debug, "i"); -MODULE_PARM_DESC (debug, "de2104x bitmapped message enable number"); - -/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ -#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ - || defined(__sparc_) || defined(__ia64__) \ - || defined(__sh__) || defined(__mips__) -static int rx_copybreak = 1518; -#else -static int rx_copybreak = 100; -#endif -MODULE_PARM (rx_copybreak, "i"); -MODULE_PARM_DESC (rx_copybreak, "de2104x Breakpoint at which Rx packets are copied"); - -#define PFX DRV_NAME ": " - -#define DE_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ - NETIF_MSG_PROBE | \ - NETIF_MSG_LINK | \ - NETIF_MSG_TIMER | \ - NETIF_MSG_IFDOWN | \ - NETIF_MSG_IFUP | \ - NETIF_MSG_RX_ERR | \ - NETIF_MSG_TX_ERR) - -#define DE_RX_RING_SIZE 64 -#define DE_TX_RING_SIZE 64 -#define DE_RING_BYTES \ - ((sizeof(struct de_desc) * DE_RX_RING_SIZE) + \ - (sizeof(struct de_desc) * DE_TX_RING_SIZE)) -#define NEXT_TX(N) (((N) + 1) & (DE_TX_RING_SIZE - 1)) -#define NEXT_RX(N) (((N) + 1) & (DE_RX_RING_SIZE - 1)) -#define TX_BUFFS_AVAIL(CP) \ - (((CP)->tx_tail <= (CP)->tx_head) ? \ - (CP)->tx_tail + (DE_TX_RING_SIZE - 1) - (CP)->tx_head : \ - (CP)->tx_tail - (CP)->tx_head - 1) - -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ -#define RX_OFFSET 2 - -#define DE_SETUP_SKB ((struct sk_buff *) 1) -#define DE_DUMMY_SKB ((struct sk_buff *) 2) -#define DE_SETUP_FRAME_WORDS 96 -#define DE_EEPROM_WORDS 256 -#define DE_EEPROM_SIZE (DE_EEPROM_WORDS * sizeof(u16)) -#define DE_MAX_MEDIA 5 - -#define DE_MEDIA_TP_AUTO 0 -#define DE_MEDIA_BNC 1 -#define DE_MEDIA_AUI 2 -#define DE_MEDIA_TP 3 -#define DE_MEDIA_TP_FD 4 -#define DE_MEDIA_INVALID DE_MAX_MEDIA -#define DE_MEDIA_FIRST 0 -#define DE_MEDIA_LAST (DE_MAX_MEDIA - 1) -#define DE_AUI_BNC (SUPPORTED_AUI | SUPPORTED_BNC) - -#define DE_TIMER_LINK (60 * HZ) -#define DE_TIMER_NO_LINK (5 * HZ) - -#define DE_NUM_REGS 16 -#define DE_REGS_SIZE (DE_NUM_REGS * sizeof(u32)) -#define DE_REGS_VER 1 - -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (6*HZ) - -#define DE_UNALIGNED_16(a) (u16)(get_unaligned((u16 *)(a))) - -/* This is a mysterious value that can be written to CSR11 in the 21040 (only) - to support a pre-NWay full-duplex signaling mechanism using short frames. - No one knows what it should be, but if left at its default value some - 10base2(!) packets trigger a full-duplex-request interrupt. */ -#define FULL_DUPLEX_MAGIC 0x6969 - -enum { - /* NIC registers */ - BusMode = 0x00, - TxPoll = 0x08, - RxPoll = 0x10, - RxRingAddr = 0x18, - TxRingAddr = 0x20, - MacStatus = 0x28, - MacMode = 0x30, - IntrMask = 0x38, - RxMissed = 0x40, - ROMCmd = 0x48, - CSR11 = 0x58, - SIAStatus = 0x60, - CSR13 = 0x68, - CSR14 = 0x70, - CSR15 = 0x78, - PCIPM = 0x40, - - /* BusMode bits */ - CmdReset = (1 << 0), - CacheAlign16 = 0x00008000, - BurstLen4 = 0x00000400, - - /* Rx/TxPoll bits */ - NormalTxPoll = (1 << 0), - NormalRxPoll = (1 << 0), - - /* Tx/Rx descriptor status bits */ - DescOwn = (1 << 31), - RxError = (1 << 15), - RxErrLong = (1 << 7), - RxErrCRC = (1 << 1), - RxErrFIFO = (1 << 0), - RxErrRunt = (1 << 11), - RxErrFrame = (1 << 14), - RingEnd = (1 << 25), - FirstFrag = (1 << 29), - LastFrag = (1 << 30), - TxError = (1 << 15), - TxFIFOUnder = (1 << 1), - TxLinkFail = (1 << 2) | (1 << 10) | (1 << 11), - TxMaxCol = (1 << 8), - TxOWC = (1 << 9), - TxJabber = (1 << 14), - SetupFrame = (1 << 27), - TxSwInt = (1 << 31), - - /* MacStatus bits */ - IntrOK = (1 << 16), - IntrErr = (1 << 15), - RxIntr = (1 << 6), - RxEmpty = (1 << 7), - TxIntr = (1 << 0), - TxEmpty = (1 << 2), - PciErr = (1 << 13), - TxState = (1 << 22) | (1 << 21) | (1 << 20), - RxState = (1 << 19) | (1 << 18) | (1 << 17), - LinkFail = (1 << 12), - LinkPass = (1 << 4), - RxStopped = (1 << 8), - TxStopped = (1 << 1), - - /* MacMode bits */ - TxEnable = (1 << 13), - RxEnable = (1 << 1), - RxTx = TxEnable | RxEnable, - FullDuplex = (1 << 9), - AcceptAllMulticast = (1 << 7), - AcceptAllPhys = (1 << 6), - BOCnt = (1 << 5), - MacModeClear = (1<<12) | (1<<11) | (1<<10) | (1<<8) | (1<<3) | - RxTx | BOCnt | AcceptAllPhys | AcceptAllMulticast, - - /* ROMCmd bits */ - EE_SHIFT_CLK = 0x02, /* EEPROM shift clock. */ - EE_CS = 0x01, /* EEPROM chip select. */ - EE_DATA_WRITE = 0x04, /* Data from the Tulip to EEPROM. */ - EE_WRITE_0 = 0x01, - EE_WRITE_1 = 0x05, - EE_DATA_READ = 0x08, /* Data from the EEPROM chip. */ - EE_ENB = (0x4800 | EE_CS), - - /* The EEPROM commands include the alway-set leading bit. */ - EE_READ_CMD = 6, - - /* RxMissed bits */ - RxMissedOver = (1 << 16), - RxMissedMask = 0xffff, - - /* SROM-related bits */ - SROMC0InfoLeaf = 27, - MediaBlockMask = 0x3f, - MediaCustomCSRs = (1 << 6), - - /* PCIPM bits */ - PM_Sleep = (1 << 31), - PM_Snooze = (1 << 30), - PM_Mask = PM_Sleep | PM_Snooze, - - /* SIAStatus bits */ - NWayState = (1 << 14) | (1 << 13) | (1 << 12), - NWayRestart = (1 << 12), - NonselPortActive = (1 << 9), - LinkFailStatus = (1 << 2), - NetCxnErr = (1 << 1), -}; - -static const u32 de_intr_mask = - IntrOK | IntrErr | RxIntr | RxEmpty | TxIntr | TxEmpty | - LinkPass | LinkFail | PciErr; - -/* - * Set the programmable burst length to 4 longwords for all: - * DMA errors result without these values. Cache align 16 long. - */ -static const u32 de_bus_mode = CacheAlign16 | BurstLen4; - -struct de_srom_media_block { - u8 opts; - u16 csr13; - u16 csr14; - u16 csr15; -} __attribute__((packed)); - -struct de_srom_info_leaf { - u16 default_media; - u8 n_blocks; - u8 unused; -} __attribute__((packed)); - -struct de_desc { - u32 opts1; - u32 opts2; - u32 addr1; - u32 addr2; -}; - -struct media_info { - u16 type; /* DE_MEDIA_xxx */ - u16 csr13; - u16 csr14; - u16 csr15; -}; - -struct ring_info { - struct sk_buff *skb; - dma_addr_t mapping; -}; - -struct de_private { - unsigned tx_head; - unsigned tx_tail; - unsigned rx_tail; - - void *regs; - struct net_device *dev; - spinlock_t lock; - - struct de_desc *rx_ring; - struct de_desc *tx_ring; - struct ring_info tx_skb[DE_TX_RING_SIZE]; - struct ring_info rx_skb[DE_RX_RING_SIZE]; - unsigned rx_buf_sz; - dma_addr_t ring_dma; - - u32 msg_enable; - - struct net_device_stats net_stats; - - struct pci_dev *pdev; - u32 macmode; - - u16 setup_frame[DE_SETUP_FRAME_WORDS]; - - u32 media_type; - u32 media_supported; - u32 media_advertise; - struct media_info media[DE_MAX_MEDIA]; - struct timer_list media_timer; - - u8 *ee_data; - unsigned board_idx; - unsigned de21040 : 1; - unsigned media_lock : 1; -}; - - -static void de_set_rx_mode (struct net_device *dev); -static void de_tx (struct de_private *de); -static void de_clean_rings (struct de_private *de); -static void de_media_interrupt (struct de_private *de, u32 status); -static void de21040_media_timer (unsigned long data); -static void de21041_media_timer (unsigned long data); -static unsigned int de_ok_to_advertise (struct de_private *de, u32 new_media); - - -static struct pci_device_id de_pci_tbl[] __initdata = { - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, - { }, -}; -MODULE_DEVICE_TABLE(pci, de_pci_tbl); - -static const char * const media_name[DE_MAX_MEDIA] = { - "10baseT auto", - "BNC", - "AUI", - "10baseT-HD", - "10baseT-FD" -}; - -/* 21040 transceiver register settings: - * TP AUTO(unused), BNC(unused), AUI, TP, TP FD*/ -static u16 t21040_csr13[] = { 0, 0, 0x8F09, 0x8F01, 0x8F01, }; -static u16 t21040_csr14[] = { 0, 0, 0x0705, 0xFFFF, 0xFFFD, }; -static u16 t21040_csr15[] = { 0, 0, 0x0006, 0x0000, 0x0000, }; - -/* 21041 transceiver register settings: TP AUTO, BNC, AUI, TP, TP FD*/ -static u16 t21041_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; -static u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x6F3F, 0x6F3D, }; -static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; - - -static inline unsigned long -msec_to_jiffies(unsigned long ms) -{ - return (((ms)*HZ+999)/1000); -} - - -#define dr32(reg) readl(de->regs + (reg)) -#define dw32(reg,val) writel((val), de->regs + (reg)) - - -static void de_rx_err_acct (struct de_private *de, unsigned rx_tail, - u32 status, u32 len) -{ - if (netif_msg_rx_err (de)) - printk (KERN_DEBUG - "%s: rx err, slot %d status 0x%x len %d\n", - de->dev->name, rx_tail, status, len); - - if ((status & 0x38000300) != 0x0300) { - /* Ingore earlier buffers. */ - if ((status & 0xffff) != 0x7fff) { - if (netif_msg_rx_err(de)) - printk(KERN_WARNING "%s: Oversized Ethernet frame " - "spanned multiple buffers, status %8.8x!\n", - de->dev->name, status); - de->net_stats.rx_length_errors++; - } - } else if (status & RxError) { - /* There was a fatal error. */ - de->net_stats.rx_errors++; /* end of a packet.*/ - if (status & 0x0890) de->net_stats.rx_length_errors++; - if (status & RxErrCRC) de->net_stats.rx_crc_errors++; - if (status & RxErrFIFO) de->net_stats.rx_fifo_errors++; - } -} - -static void de_rx (struct de_private *de) -{ - unsigned rx_tail = de->rx_tail; - unsigned rx_work = DE_RX_RING_SIZE; - unsigned drop = 0; - int rc; - - while (rx_work--) { - u32 status, len; - dma_addr_t mapping; - struct sk_buff *skb, *copy_skb; - unsigned copying_skb, buflen; - - skb = de->rx_skb[rx_tail].skb; - if (!skb) - BUG(); - rmb(); - status = le32_to_cpu(de->rx_ring[rx_tail].opts1); - if (status & DescOwn) - break; - - len = ((status >> 16) & 0x7ff) - 4; - mapping = de->rx_skb[rx_tail].mapping; - - if (unlikely(drop)) { - de->net_stats.rx_dropped++; - goto rx_next; - } - - if (unlikely((status & 0x38008300) != 0x0300)) { - de_rx_err_acct(de, rx_tail, status, len); - goto rx_next; - } - - copying_skb = (len <= rx_copybreak); - - if (unlikely(netif_msg_rx_status(de))) - printk(KERN_DEBUG "%s: rx slot %d status 0x%x len %d copying? %d\n", - de->dev->name, rx_tail, status, len, - copying_skb); - - buflen = copying_skb ? (len + RX_OFFSET) : de->rx_buf_sz; - copy_skb = dev_alloc_skb (buflen); - if (unlikely(!copy_skb)) { - de->net_stats.rx_dropped++; - drop = 1; - rx_work = 100; - goto rx_next; - } - copy_skb->dev = de->dev; - - if (!copying_skb) { - pci_unmap_single(de->pdev, mapping, - buflen, PCI_DMA_FROMDEVICE); - skb_put(skb, len); - - mapping = - de->rx_skb[rx_tail].mapping = - pci_map_single(de->pdev, copy_skb->tail, - buflen, PCI_DMA_FROMDEVICE); - de->rx_skb[rx_tail].skb = copy_skb; - } else { - pci_dma_sync_single(de->pdev, mapping, len, PCI_DMA_FROMDEVICE); - skb_reserve(copy_skb, RX_OFFSET); - memcpy(skb_put(copy_skb, len), skb->tail, len); - - /* We'll reuse the original ring buffer. */ - skb = copy_skb; - } - - skb->protocol = eth_type_trans (skb, de->dev); - - de->net_stats.rx_packets++; - de->net_stats.rx_bytes += skb->len; - de->dev->last_rx = jiffies; - rc = netif_rx (skb); - if (rc == NET_RX_DROP) - drop = 1; - -rx_next: - de->rx_ring[rx_tail].opts1 = cpu_to_le32(DescOwn); - if (rx_tail == (DE_RX_RING_SIZE - 1)) - de->rx_ring[rx_tail].opts2 = - cpu_to_le32(RingEnd | de->rx_buf_sz); - else - de->rx_ring[rx_tail].opts2 = cpu_to_le32(de->rx_buf_sz); - de->rx_ring[rx_tail].addr1 = cpu_to_le32(mapping); - rx_tail = NEXT_RX(rx_tail); - } - - if (!rx_work) - printk(KERN_WARNING "%s: rx work limit reached\n", de->dev->name); - - de->rx_tail = rx_tail; -} - -static void de_interrupt (int irq, void *dev_instance, struct pt_regs *regs) -{ - struct net_device *dev = dev_instance; - struct de_private *de = dev->priv; - u32 status; - - status = dr32(MacStatus); - if ((!(status & (IntrOK|IntrErr))) || (status == 0xFFFF)) - return; - - if (netif_msg_intr(de)) - printk(KERN_DEBUG "%s: intr, status %08x mode %08x desc %u/%u/%u\n", - dev->name, status, dr32(MacMode), de->rx_tail, de->tx_head, de->tx_tail); - - dw32(MacStatus, status); - - if (status & (RxIntr | RxEmpty)) { - de_rx(de); - if (status & RxEmpty) - dw32(RxPoll, NormalRxPoll); - } - - spin_lock(&de->lock); - - if (status & (TxIntr | TxEmpty)) - de_tx(de); - - if (status & (LinkPass | LinkFail)) - de_media_interrupt(de, status); - - spin_unlock(&de->lock); - - if (status & PciErr) { - u16 pci_status; - - pci_read_config_word(de->pdev, PCI_STATUS, &pci_status); - pci_write_config_word(de->pdev, PCI_STATUS, pci_status); - printk(KERN_ERR "%s: PCI bus error, status=%08x, PCI status=%04x\n", - dev->name, status, pci_status); - } -} - -static void de_tx (struct de_private *de) -{ - unsigned tx_head = de->tx_head; - unsigned tx_tail = de->tx_tail; - - while (tx_tail != tx_head) { - struct sk_buff *skb; - u32 status; - - rmb(); - status = le32_to_cpu(de->tx_ring[tx_tail].opts1); - if (status & DescOwn) - break; - - skb = de->tx_skb[tx_tail].skb; - if (!skb) - BUG(); - if (unlikely(skb == DE_DUMMY_SKB)) - goto next; - - if (unlikely(skb == DE_SETUP_SKB)) { - pci_unmap_single(de->pdev, de->tx_skb[tx_tail].mapping, - sizeof(de->setup_frame), PCI_DMA_TODEVICE); - goto next; - } - - pci_unmap_single(de->pdev, de->tx_skb[tx_tail].mapping, - skb->len, PCI_DMA_TODEVICE); - - if (status & LastFrag) { - if (status & TxError) { - if (netif_msg_tx_err(de)) - printk(KERN_DEBUG "%s: tx err, status 0x%x\n", - de->dev->name, status); - de->net_stats.tx_errors++; - if (status & TxOWC) - de->net_stats.tx_window_errors++; - if (status & TxMaxCol) - de->net_stats.tx_aborted_errors++; - if (status & TxLinkFail) - de->net_stats.tx_carrier_errors++; - if (status & TxFIFOUnder) - de->net_stats.tx_fifo_errors++; - } else { - de->net_stats.tx_packets++; - de->net_stats.tx_bytes += skb->len; - if (netif_msg_tx_done(de)) - printk(KERN_DEBUG "%s: tx done, slot %d\n", de->dev->name, tx_tail); - } - dev_kfree_skb_irq(skb); - } - -next: - de->tx_skb[tx_tail].skb = NULL; - - tx_tail = NEXT_TX(tx_tail); - } - - de->tx_tail = tx_tail; - - if (netif_queue_stopped(de->dev) && (TX_BUFFS_AVAIL(de) > (DE_TX_RING_SIZE / 4))) - netif_wake_queue(de->dev); -} - -static int de_start_xmit (struct sk_buff *skb, struct net_device *dev) -{ - struct de_private *de = dev->priv; - unsigned int entry, tx_free; - u32 mapping, len, flags = FirstFrag | LastFrag; - struct de_desc *txd; - - spin_lock_irq(&de->lock); - - tx_free = TX_BUFFS_AVAIL(de); - if (tx_free == 0) { - netif_stop_queue(dev); - spin_unlock_irq(&de->lock); - return 1; - } - tx_free--; - - entry = de->tx_head; - - txd = &de->tx_ring[entry]; - - len = skb->len; - mapping = pci_map_single(de->pdev, skb->data, len, PCI_DMA_TODEVICE); - if (entry == (DE_TX_RING_SIZE - 1)) - flags |= RingEnd; - if (!tx_free || (tx_free == (DE_TX_RING_SIZE / 2))) - flags |= TxSwInt; - flags |= len; - txd->opts2 = cpu_to_le32(flags); - txd->addr1 = cpu_to_le32(mapping); - - de->tx_skb[entry].skb = skb; - de->tx_skb[entry].mapping = mapping; - wmb(); - - txd->opts1 = cpu_to_le32(DescOwn); - wmb(); - - de->tx_head = NEXT_TX(entry); - if (netif_msg_tx_queued(de)) - printk(KERN_DEBUG "%s: tx queued, slot %d, skblen %d\n", - dev->name, entry, skb->len); - - if (tx_free == 0) - netif_stop_queue(dev); - - spin_unlock_irq(&de->lock); - - /* Trigger an immediate transmit demand. */ - dw32(TxPoll, NormalTxPoll); - dev->trans_start = jiffies; - - return 0; -} - -/* Set or clear the multicast filter for this adaptor. - Note that we only use exclusion around actually queueing the - new frame, not around filling de->setup_frame. This is non-deterministic - when re-entered but still correct. */ - -#undef set_bit_le -#define set_bit_le(i,p) do { ((char *)(p))[(i)/8] |= (1<<((i)%8)); } while(0) - -static void build_setup_frame_hash(u16 *setup_frm, struct net_device *dev) -{ - struct de_private *de = dev->priv; - u16 hash_table[32]; - struct dev_mc_list *mclist; - int i; - u16 *eaddrs; - - memset(hash_table, 0, sizeof(hash_table)); - set_bit_le(255, hash_table); /* Broadcast entry */ - /* This should work on big-endian machines as well. */ - for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; - i++, mclist = mclist->next) { - int index = ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff; - - set_bit_le(index, hash_table); - - for (i = 0; i < 32; i++) { - *setup_frm++ = hash_table[i]; - *setup_frm++ = hash_table[i]; - } - setup_frm = &de->setup_frame[13*6]; - } - - /* Fill the final entry with our physical address. */ - eaddrs = (u16 *)dev->dev_addr; - *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; -} - -static void build_setup_frame_perfect(u16 *setup_frm, struct net_device *dev) -{ - struct de_private *de = dev->priv; - struct dev_mc_list *mclist; - int i; - u16 *eaddrs; - - /* We have <= 14 addresses so we can use the wonderful - 16 address perfect filtering of the Tulip. */ - for (i = 0, mclist = dev->mc_list; i < dev->mc_count; - i++, mclist = mclist->next) { - eaddrs = (u16 *)mclist->dmi_addr; - *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; - *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; - *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; - } - /* Fill the unused entries with the broadcast address. */ - memset(setup_frm, 0xff, (15-i)*12); - setup_frm = &de->setup_frame[15*6]; - - /* Fill the final entry with our physical address. */ - eaddrs = (u16 *)dev->dev_addr; - *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; -} - - -static void __de_set_rx_mode (struct net_device *dev) -{ - struct de_private *de = dev->priv; - u32 macmode; - unsigned int entry; - u32 mapping; - struct de_desc *txd; - struct de_desc *dummy_txd = NULL; - - macmode = de->macmode & ~(AcceptAllMulticast | AcceptAllPhys); - - if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ - macmode |= AcceptAllMulticast | AcceptAllPhys; - goto out; - } - - if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { - /* Too many to filter well -- accept all multicasts. */ - macmode |= AcceptAllMulticast; - goto out; - } - - /* Note that only the low-address shortword of setup_frame is valid! - The values are doubled for big-endian architectures. */ - if (dev->mc_count > 14) /* Must use a multicast hash table. */ - build_setup_frame_hash (de->setup_frame, dev); - else - build_setup_frame_perfect (de->setup_frame, dev); - - /* - * Now add this frame to the Tx list. - */ - - entry = de->tx_head; - - /* Avoid a chip errata by prefixing a dummy entry. */ - if (entry != 0) { - de->tx_skb[entry].skb = DE_DUMMY_SKB; - - dummy_txd = &de->tx_ring[entry]; - dummy_txd->opts2 = (entry == (DE_TX_RING_SIZE - 1)) ? - cpu_to_le32(RingEnd) : 0; - dummy_txd->addr1 = 0; - - /* Must set DescOwned later to avoid race with chip */ - - entry = NEXT_TX(entry); - } - - de->tx_skb[entry].skb = DE_SETUP_SKB; - de->tx_skb[entry].mapping = mapping = - pci_map_single (de->pdev, de->setup_frame, - sizeof (de->setup_frame), PCI_DMA_TODEVICE); - - /* Put the setup frame on the Tx list. */ - txd = &de->tx_ring[entry]; - if (entry == (DE_TX_RING_SIZE - 1)) - txd->opts2 = cpu_to_le32(SetupFrame | RingEnd | sizeof (de->setup_frame)); - else - txd->opts2 = cpu_to_le32(SetupFrame | sizeof (de->setup_frame)); - txd->addr1 = cpu_to_le32(mapping); - wmb(); - - txd->opts1 = cpu_to_le32(DescOwn); - wmb(); - - if (dummy_txd) { - dummy_txd->opts1 = cpu_to_le32(DescOwn); - wmb(); - } - - de->tx_head = NEXT_TX(entry); - - if (TX_BUFFS_AVAIL(de) < 0) - BUG(); - if (TX_BUFFS_AVAIL(de) == 0) - netif_stop_queue(dev); - - /* Trigger an immediate transmit demand. */ - dw32(TxPoll, NormalTxPoll); - -out: - if (macmode != de->macmode) { - dw32 (MacMode, macmode); - de->macmode = macmode; - } -} - -static void de_set_rx_mode (struct net_device *dev) -{ - unsigned long flags; - struct de_private *de = dev->priv; - - spin_lock_irqsave (&de->lock, flags); - __de_set_rx_mode(dev); - spin_unlock_irqrestore (&de->lock, flags); -} - -static inline void de_rx_missed(struct de_private *de, u32 rx_missed) -{ - if (unlikely(rx_missed & RxMissedOver)) - de->net_stats.rx_missed_errors += RxMissedMask; - else - de->net_stats.rx_missed_errors += (rx_missed & RxMissedMask); -} - -static void __de_get_stats(struct de_private *de) -{ - u32 tmp = dr32(RxMissed); /* self-clearing */ - - de_rx_missed(de, tmp); -} - -static struct net_device_stats *de_get_stats(struct net_device *dev) -{ - struct de_private *de = dev->priv; - - /* The chip only need report frame silently dropped. */ - spin_lock_irq(&de->lock); - if (netif_running(dev) && netif_device_present(dev)) - __de_get_stats(de); - spin_unlock_irq(&de->lock); - - return &de->net_stats; -} - -static inline int de_is_running (struct de_private *de) -{ - return (dr32(MacStatus) & (RxState | TxState)) ? 1 : 0; -} - -static void de_stop_rxtx (struct de_private *de) -{ - u32 macmode; - unsigned int work = 1000; - - macmode = dr32(MacMode); - if (macmode & RxTx) { - dw32(MacMode, macmode & ~RxTx); - dr32(MacMode); - } - - while (--work > 0) { - if (!de_is_running(de)) - return; - cpu_relax(); - } - - printk(KERN_WARNING "%s: timeout expired stopping DMA\n", de->dev->name); -} - -static inline void de_start_rxtx (struct de_private *de) -{ - u32 macmode; - - macmode = dr32(MacMode); - if ((macmode & RxTx) != RxTx) { - dw32(MacMode, macmode | RxTx); - dr32(MacMode); - } -} - -static void de_stop_hw (struct de_private *de) -{ - - udelay(5); - dw32(IntrMask, 0); - - de_stop_rxtx(de); - - dw32(MacStatus, dr32(MacStatus)); - - udelay(10); - - de->rx_tail = 0; - de->tx_head = de->tx_tail = 0; -} - -static void de_link_up(struct de_private *de) -{ - if (!netif_carrier_ok(de->dev)) { - netif_carrier_on(de->dev); - if (netif_msg_link(de)) - printk(KERN_INFO "%s: link up, media %s\n", - de->dev->name, media_name[de->media_type]); - } -} - -static void de_link_down(struct de_private *de) -{ - if (netif_carrier_ok(de->dev)) { - netif_carrier_off(de->dev); - if (netif_msg_link(de)) - printk(KERN_INFO "%s: link down\n", de->dev->name); - } -} - -static void de_set_media (struct de_private *de) -{ - unsigned media = de->media_type; - - if (de_is_running(de)) - BUG(); - - if (de->de21040) - dw32(CSR11, FULL_DUPLEX_MAGIC); - dw32(CSR13, 0); /* Reset phy */ - dw32(CSR14, de->media[media].csr14); - dw32(CSR15, de->media[media].csr15); - dw32(CSR13, de->media[media].csr13); - - /* must delay 10ms before writing to other registers, - * especially CSR6 - */ - mdelay(10); - - if (media == DE_MEDIA_TP_FD) - de->macmode |= FullDuplex; - else - de->macmode &= ~FullDuplex; - - if (netif_msg_link(de)) { - printk(KERN_INFO "%s: set link %s\n" - KERN_INFO "%s: mode 0x%x, sia 0x%x,0x%x,0x%x,0x%x\n" - KERN_INFO "%s: set mode 0x%x, set sia 0x%x,0x%x,0x%x\n", - de->dev->name, media_name[media], - de->dev->name, dr32(MacMode), dr32(SIAStatus), - dr32(CSR13), dr32(CSR14), dr32(CSR15), - de->dev->name, de->macmode, de->media[media].csr13, - de->media[media].csr14, de->media[media].csr15); - } -} - -static void de_next_media (struct de_private *de, u32 *media, - unsigned int n_media) -{ - unsigned int i; - - for (i = 0; i < n_media; i++) { - if (de_ok_to_advertise(de, media[i])) { - de->media_type = media[i]; - return; - } - } -} - -static void de21040_media_timer (unsigned long data) -{ - struct de_private *de = (struct de_private *) data; - struct net_device *dev = de->dev; - u32 status = dr32(SIAStatus); - unsigned int carrier; - unsigned long flags; - - carrier = (status & NetCxnErr) ? 0 : 1; - - if (carrier) { - if (de->media_type != DE_MEDIA_AUI && (status & LinkFailStatus)) - goto no_link_yet; - - de->media_timer.expires = jiffies + DE_TIMER_LINK; - add_timer(&de->media_timer); - if (!netif_carrier_ok(dev)) - de_link_up(de); - else - if (netif_msg_timer(de)) - printk(KERN_INFO "%s: %s link ok, status %x\n", - dev->name, media_name[de->media_type], - status); - return; - } - - de_link_down(de); - - if (de->media_lock) - return; - - if (de->media_type == DE_MEDIA_AUI) { - u32 next_state = DE_MEDIA_TP; - de_next_media(de, &next_state, 1); - } else { - u32 next_state = DE_MEDIA_AUI; - de_next_media(de, &next_state, 1); - } - - spin_lock_irqsave(&de->lock, flags); - de_stop_rxtx(de); - spin_unlock_irqrestore(&de->lock, flags); - de_set_media(de); - de_start_rxtx(de); - -no_link_yet: - de->media_timer.expires = jiffies + DE_TIMER_NO_LINK; - add_timer(&de->media_timer); - - if (netif_msg_timer(de)) - printk(KERN_INFO "%s: no link, trying media %s, status %x\n", - dev->name, media_name[de->media_type], status); -} - -static unsigned int de_ok_to_advertise (struct de_private *de, u32 new_media) -{ - switch (new_media) { - case DE_MEDIA_TP_AUTO: - if (!(de->media_advertise & ADVERTISED_Autoneg)) - return 0; - if (!(de->media_advertise & (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full))) - return 0; - break; - case DE_MEDIA_BNC: - if (!(de->media_advertise & ADVERTISED_BNC)) - return 0; - break; - case DE_MEDIA_AUI: - if (!(de->media_advertise & ADVERTISED_AUI)) - return 0; - break; - case DE_MEDIA_TP: - if (!(de->media_advertise & ADVERTISED_10baseT_Half)) - return 0; - break; - case DE_MEDIA_TP_FD: - if (!(de->media_advertise & ADVERTISED_10baseT_Full)) - return 0; - break; - } - - return 1; -} - -static void de21041_media_timer (unsigned long data) -{ - struct de_private *de = (struct de_private *) data; - struct net_device *dev = de->dev; - u32 status = dr32(SIAStatus); - unsigned int carrier; - unsigned long flags; - - carrier = (status & NetCxnErr) ? 0 : 1; - - if (carrier) { - if ((de->media_type == DE_MEDIA_TP_AUTO || - de->media_type == DE_MEDIA_TP || - de->media_type == DE_MEDIA_TP_FD) && - (status & LinkFailStatus)) - goto no_link_yet; - - de->media_timer.expires = jiffies + DE_TIMER_LINK; - add_timer(&de->media_timer); - if (!netif_carrier_ok(dev)) - de_link_up(de); - else - if (netif_msg_timer(de)) - printk(KERN_INFO "%s: %s link ok, mode %x status %x\n", - dev->name, media_name[de->media_type], - dr32(MacMode), status); - return; - } - - de_link_down(de); - - /* if media type locked, don't switch media */ - if (de->media_lock) - goto set_media; - - /* if activity detected, use that as hint for new media type */ - if (status & NonselPortActive) { - unsigned int have_media = 1; - - /* if AUI/BNC selected, then activity is on TP port */ - if (de->media_type == DE_MEDIA_AUI || - de->media_type == DE_MEDIA_BNC) { - if (de_ok_to_advertise(de, DE_MEDIA_TP_AUTO)) - de->media_type = DE_MEDIA_TP_AUTO; - else - have_media = 0; - } - - /* TP selected. If there is only TP and BNC, then it's BNC */ - else if (((de->media_supported & DE_AUI_BNC) == SUPPORTED_BNC) && - de_ok_to_advertise(de, DE_MEDIA_BNC)) - de->media_type = DE_MEDIA_BNC; - - /* TP selected. If there is only TP and AUI, then it's AUI */ - else if (((de->media_supported & DE_AUI_BNC) == SUPPORTED_AUI) && - de_ok_to_advertise(de, DE_MEDIA_AUI)) - de->media_type = DE_MEDIA_AUI; - - /* otherwise, ignore the hint */ - else - have_media = 0; - - if (have_media) - goto set_media; - } - - /* - * Absent or ambiguous activity hint, move to next advertised - * media state. If de->media_type is left unchanged, this - * simply resets the PHY and reloads the current media settings. - */ - if (de->media_type == DE_MEDIA_AUI) { - u32 next_states[] = { DE_MEDIA_BNC, DE_MEDIA_TP_AUTO }; - de_next_media(de, next_states, ARRAY_SIZE(next_states)); - } else if (de->media_type == DE_MEDIA_BNC) { - u32 next_states[] = { DE_MEDIA_TP_AUTO, DE_MEDIA_AUI }; - de_next_media(de, next_states, ARRAY_SIZE(next_states)); - } else { - u32 next_states[] = { DE_MEDIA_AUI, DE_MEDIA_BNC, DE_MEDIA_TP_AUTO }; - de_next_media(de, next_states, ARRAY_SIZE(next_states)); - } - -set_media: - spin_lock_irqsave(&de->lock, flags); - de_stop_rxtx(de); - spin_unlock_irqrestore(&de->lock, flags); - de_set_media(de); - de_start_rxtx(de); - -no_link_yet: - de->media_timer.expires = jiffies + DE_TIMER_NO_LINK; - add_timer(&de->media_timer); - - if (netif_msg_timer(de)) - printk(KERN_INFO "%s: no link, trying media %s, status %x\n", - dev->name, media_name[de->media_type], status); -} - -static void de_media_interrupt (struct de_private *de, u32 status) -{ - if (status & LinkPass) { - de_link_up(de); - mod_timer(&de->media_timer, jiffies + DE_TIMER_LINK); - return; - } - - if (!(status & LinkFail)) - BUG(); - - if (netif_carrier_ok(de->dev)) { - de_link_down(de); - mod_timer(&de->media_timer, jiffies + DE_TIMER_NO_LINK); - } -} - -static int de_reset_mac (struct de_private *de) -{ - u32 status, tmp; - - /* - * Reset MAC. Copied from de4x5.c. - */ - - tmp = dr32 (BusMode); - if (tmp == 0xffffffff) - return -ENODEV; - mdelay (1); - - dw32 (BusMode, tmp | CmdReset); - mdelay (1); - - dw32 (BusMode, tmp); - mdelay (1); - - for (tmp = 0; tmp < 5; tmp++) { - dr32 (BusMode); - mdelay (1); - } - - mdelay (1); - - status = dr32(MacStatus); - if (status & (RxState | TxState)) - return -EBUSY; - if (status == 0xffffffff) - return -ENODEV; - return 0; -} - -static void de_adapter_wake (struct de_private *de) -{ - u32 pmctl; - - if (de->de21040) - return; - - pci_read_config_dword(de->pdev, PCIPM, &pmctl); - if (pmctl & PM_Mask) { - pmctl &= ~PM_Mask; - pci_write_config_dword(de->pdev, PCIPM, pmctl); - - /* de4x5.c delays, so we do too */ - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(msec_to_jiffies(10)); - } -} - -static void de_adapter_sleep (struct de_private *de) -{ - u32 pmctl; - - if (de->de21040) - return; - - pci_read_config_dword(de->pdev, PCIPM, &pmctl); - pmctl |= PM_Sleep; - pci_write_config_dword(de->pdev, PCIPM, pmctl); -} - -static int de_init_hw (struct de_private *de) -{ - struct net_device *dev = de->dev; - int rc; - - de_adapter_wake(de); - - de->macmode = dr32(MacMode) & ~MacModeClear; - - rc = de_reset_mac(de); - if (rc) - return rc; - - de_set_media(de); /* reset phy */ - - dw32(RxRingAddr, de->ring_dma); - dw32(TxRingAddr, de->ring_dma + (sizeof(struct de_desc) * DE_RX_RING_SIZE)); - - dw32(MacMode, RxTx | de->macmode); - - dr32(RxMissed); /* self-clearing */ - - dw32(IntrMask, de_intr_mask); - - de_set_rx_mode(dev); - - return 0; -} - -static int de_refill_rx (struct de_private *de) -{ - unsigned i; - - for (i = 0; i < DE_RX_RING_SIZE; i++) { - struct sk_buff *skb; - - skb = dev_alloc_skb(de->rx_buf_sz); - if (!skb) - goto err_out; - - skb->dev = de->dev; - - de->rx_skb[i].mapping = pci_map_single(de->pdev, - skb->tail, de->rx_buf_sz, PCI_DMA_FROMDEVICE); - de->rx_skb[i].skb = skb; - - de->rx_ring[i].opts1 = cpu_to_le32(DescOwn); - if (i == (DE_RX_RING_SIZE - 1)) - de->rx_ring[i].opts2 = - cpu_to_le32(RingEnd | de->rx_buf_sz); - else - de->rx_ring[i].opts2 = cpu_to_le32(de->rx_buf_sz); - de->rx_ring[i].addr1 = cpu_to_le32(de->rx_skb[i].mapping); - de->rx_ring[i].addr2 = 0; - } - - return 0; - -err_out: - de_clean_rings(de); - return -ENOMEM; -} - -static int de_init_rings (struct de_private *de) -{ - memset(de->tx_ring, 0, sizeof(struct de_desc) * DE_TX_RING_SIZE); - de->tx_ring[DE_TX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); - - de->rx_tail = 0; - de->tx_head = de->tx_tail = 0; - - return de_refill_rx (de); -} - -static int de_alloc_rings (struct de_private *de) -{ - de->rx_ring = pci_alloc_consistent(de->pdev, DE_RING_BYTES, &de->ring_dma); - if (!de->rx_ring) - return -ENOMEM; - de->tx_ring = &de->rx_ring[DE_RX_RING_SIZE]; - return de_init_rings(de); -} - -static void de_clean_rings (struct de_private *de) -{ - unsigned i; - - memset(de->rx_ring, 0, sizeof(struct de_desc) * DE_RX_RING_SIZE); - de->rx_ring[DE_RX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); - wmb(); - memset(de->tx_ring, 0, sizeof(struct de_desc) * DE_TX_RING_SIZE); - de->tx_ring[DE_TX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); - wmb(); - - for (i = 0; i < DE_RX_RING_SIZE; i++) { - if (de->rx_skb[i].skb) { - pci_unmap_single(de->pdev, de->rx_skb[i].mapping, - de->rx_buf_sz, PCI_DMA_FROMDEVICE); - dev_kfree_skb(de->rx_skb[i].skb); - } - } - - for (i = 0; i < DE_TX_RING_SIZE; i++) { - struct sk_buff *skb = de->tx_skb[i].skb; - if ((skb) && (skb != DE_DUMMY_SKB)) { - if (skb != DE_SETUP_SKB) { - dev_kfree_skb(skb); - de->net_stats.tx_dropped++; - pci_unmap_single(de->pdev, - de->tx_skb[i].mapping, - skb->len, PCI_DMA_TODEVICE); - } else { - pci_unmap_single(de->pdev, - de->tx_skb[i].mapping, - sizeof(de->setup_frame), - PCI_DMA_TODEVICE); - } - } - } - - memset(&de->rx_skb, 0, sizeof(struct ring_info) * DE_RX_RING_SIZE); - memset(&de->tx_skb, 0, sizeof(struct ring_info) * DE_TX_RING_SIZE); -} - -static void de_free_rings (struct de_private *de) -{ - de_clean_rings(de); - pci_free_consistent(de->pdev, DE_RING_BYTES, de->rx_ring, de->ring_dma); - de->rx_ring = NULL; - de->tx_ring = NULL; -} - -static int de_open (struct net_device *dev) -{ - struct de_private *de = dev->priv; - int rc; - unsigned long flags; - - if (netif_msg_ifup(de)) - printk(KERN_DEBUG "%s: enabling interface\n", dev->name); - - de->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); - - rc = de_alloc_rings(de); - if (rc) { - printk(KERN_ERR "%s: ring allocation failure, err=%d\n", - dev->name, rc); - return rc; - } - - rc = de_init_hw(de); - if (rc) { - printk(KERN_ERR "%s: h/w init failure, err=%d\n", - dev->name, rc); - goto err_out_free; - } - - rc = request_irq(dev->irq, de_interrupt, SA_SHIRQ, dev->name, dev); - if (rc) { - printk(KERN_ERR "%s: IRQ %d request failure, err=%d\n", - dev->name, dev->irq, rc); - goto err_out_hw; - } - - netif_start_queue(dev); - mod_timer(&de->media_timer, jiffies + DE_TIMER_NO_LINK); - - return 0; - -err_out_hw: - spin_lock_irqsave(&de->lock, flags); - de_stop_hw(de); - spin_unlock_irqrestore(&de->lock, flags); - -err_out_free: - de_free_rings(de); - return rc; -} - -static int de_close (struct net_device *dev) -{ - struct de_private *de = dev->priv; - unsigned long flags; - - if (netif_msg_ifdown(de)) - printk(KERN_DEBUG "%s: disabling interface\n", dev->name); - - del_timer_sync(&de->media_timer); - - spin_lock_irqsave(&de->lock, flags); - de_stop_hw(de); - netif_stop_queue(dev); - netif_carrier_off(dev); - spin_unlock_irqrestore(&de->lock, flags); - - free_irq(dev->irq, dev); - - de_free_rings(de); - de_adapter_sleep(de); - pci_disable_device(de->pdev); - return 0; -} - -static void de_tx_timeout (struct net_device *dev) -{ - struct de_private *de = dev->priv; - - printk(KERN_DEBUG "%s: NIC status %08x mode %08x sia %08x desc %u/%u/%u\n", - dev->name, dr32(MacStatus), dr32(MacMode), dr32(SIAStatus), - de->rx_tail, de->tx_head, de->tx_tail); - - del_timer_sync(&de->media_timer); - - disable_irq(dev->irq); - spin_lock_irq(&de->lock); - - de_stop_hw(de); - netif_stop_queue(dev); - netif_carrier_off(dev); - - spin_unlock_irq(&de->lock); - enable_irq(dev->irq); - - /* Update the error counts. */ - __de_get_stats(de); - - synchronize_irq(); - de_clean_rings(de); - - de_init_hw(de); - - netif_wake_queue(dev); -} - -static int de_get_regs(struct de_private *de, u8 *buf) -{ - int i; - u32 *rbuf = (u32 *)buf; - - /* read all CSRs */ - for (i = 0; i < DE_NUM_REGS; i++) - rbuf[i] = dr32(i * 8); - - /* handle self-clearing RxMissed counter, CSR8 */ - de_rx_missed(de, rbuf[8]); - - return 0; -} - -static int de_ethtool_gset(struct de_private *de, struct ethtool_cmd *ecmd) -{ - ecmd->supported = de->media_supported; - ecmd->transceiver = XCVR_INTERNAL; - ecmd->phy_address = 0; - ecmd->advertising = de->media_advertise; - - switch (de->media_type) { - case DE_MEDIA_AUI: - ecmd->port = PORT_AUI; - ecmd->speed = 5; - break; - case DE_MEDIA_BNC: - ecmd->port = PORT_BNC; - ecmd->speed = 2; - break; - default: - ecmd->port = PORT_TP; - ecmd->speed = SPEED_10; - break; - } - - if (de->macmode & FullDuplex) - ecmd->duplex = DUPLEX_FULL; - else - ecmd->duplex = DUPLEX_HALF; - - if (de->media_lock) - ecmd->autoneg = AUTONEG_DISABLE; - else - ecmd->autoneg = AUTONEG_ENABLE; - - /* ignore maxtxpkt, maxrxpkt for now */ - - return 0; -} - -static int de_ethtool_sset(struct de_private *de, struct ethtool_cmd *ecmd) -{ - u32 new_media; - unsigned int media_lock; - - if (ecmd->speed != SPEED_10 && ecmd->speed != 5 && ecmd->speed != 2) - return -EINVAL; - if (de->de21040 && ecmd->speed == 2) - return -EINVAL; - if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL) - return -EINVAL; - if (ecmd->port != PORT_TP && ecmd->port != PORT_AUI && ecmd->port != PORT_BNC) - return -EINVAL; - if (de->de21040 && ecmd->port == PORT_BNC) - return -EINVAL; - if (ecmd->transceiver != XCVR_INTERNAL) - return -EINVAL; - if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE) - return -EINVAL; - if (ecmd->advertising & ~de->media_supported) - return -EINVAL; - if (ecmd->autoneg == AUTONEG_ENABLE && - (!(ecmd->advertising & ADVERTISED_Autoneg))) - return -EINVAL; - - switch (ecmd->port) { - case PORT_AUI: - new_media = DE_MEDIA_AUI; - if (!(ecmd->advertising & ADVERTISED_AUI)) - return -EINVAL; - break; - case PORT_BNC: - new_media = DE_MEDIA_BNC; - if (!(ecmd->advertising & ADVERTISED_BNC)) - return -EINVAL; - break; - default: - if (ecmd->autoneg == AUTONEG_ENABLE) - new_media = DE_MEDIA_TP_AUTO; - else if (ecmd->duplex == DUPLEX_FULL) - new_media = DE_MEDIA_TP_FD; - else - new_media = DE_MEDIA_TP; - if (!(ecmd->advertising & ADVERTISED_TP)) - return -EINVAL; - if (!(ecmd->advertising & (ADVERTISED_10baseT_Full | ADVERTISED_10baseT_Half))) - return -EINVAL; - break; - } - - media_lock = (ecmd->autoneg == AUTONEG_ENABLE) ? 0 : 1; - - if ((new_media == de->media_type) && - (media_lock == de->media_lock) && - (ecmd->advertising == de->media_advertise)) - return 0; /* nothing to change */ - - de_link_down(de); - de_stop_rxtx(de); - - de->media_type = new_media; - de->media_lock = media_lock; - de->media_advertise = ecmd->advertising; - de_set_media(de); - - return 0; -} - -static int de_ethtool_ioctl (struct de_private *de, void *useraddr) -{ - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - strcpy (info.bus_info, de->pdev->slot_name); - info.eedump_len = DE_EEPROM_SIZE; - info.regdump_len = DE_REGS_SIZE; - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - /* get settings */ - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - spin_lock_irq(&de->lock); - de_ethtool_gset(de, &ecmd); - spin_unlock_irq(&de->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - /* set settings */ - case ETHTOOL_SSET: { - struct ethtool_cmd ecmd; - int r; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - spin_lock_irq(&de->lock); - r = de_ethtool_sset(de, &ecmd); - spin_unlock_irq(&de->lock); - return r; - } - - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST: { - u32 status; - - if (de->media_type != DE_MEDIA_TP_AUTO) - return -EINVAL; - if (netif_carrier_ok(de->dev)) - de_link_down(de); - - status = dr32(SIAStatus); - dw32(SIAStatus, (status & ~NWayState) | NWayRestart); - if (netif_msg_link(de)) - printk(KERN_INFO "%s: link nway restart, status %x,%x\n", - de->dev->name, status, dr32(SIAStatus)); - return 0; - } - - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; - edata.data = (netif_carrier_ok(de->dev)) ? 1 : 0; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = de->msg_enable; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - de->msg_enable = edata.data; - return 0; - } - - /* get registers */ - case ETHTOOL_GREGS: { - struct ethtool_regs regs; - u8 regbuf[DE_REGS_SIZE]; - int r; - - if (copy_from_user(®s, useraddr, sizeof(regs))) - return -EFAULT; - - if (regs.len > DE_REGS_SIZE) { - regs.len = DE_REGS_SIZE; - } - regs.version = (DE_REGS_VER << 2) | de->de21040; - if (copy_to_user(useraddr, ®s, sizeof(regs))) - return -EFAULT; - - useraddr += offsetof(struct ethtool_regs, data); - - spin_lock_irq(&de->lock); - r = de_get_regs(de, regbuf); - spin_unlock_irq(&de->lock); - - if (r) - return r; - if (copy_to_user(useraddr, regbuf, regs.len)) - return -EFAULT; - return 0; - } - - /* get SROM dump */ - case ETHTOOL_GEEPROM: { - struct ethtool_eeprom eeprom; - - if (!de->ee_data) - break; - if (copy_from_user(&eeprom, useraddr, sizeof(eeprom))) - return -EFAULT; - if ((eeprom.offset != 0) || (eeprom.magic != 0) || - (eeprom.len != DE_EEPROM_SIZE)) - return -EINVAL; - - useraddr += offsetof(struct ethtool_regs, data); - if (copy_to_user(useraddr, de->ee_data, DE_EEPROM_SIZE)) - return -EFAULT; - } - - default: - break; - } - - return -EOPNOTSUPP; -} - - -static int de_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct de_private *de = dev->priv; - int rc = 0; - - if (!netif_running(dev)) - return -EINVAL; - - switch (cmd) { - case SIOCETHTOOL: - return de_ethtool_ioctl(de, (void *) rq->ifr_data); - - default: - rc = -EOPNOTSUPP; - break; - } - - return rc; -} - -static void __init de21040_get_mac_address (struct de_private *de) -{ - unsigned i; - - dw32 (ROMCmd, 0); /* Reset the pointer with a dummy write. */ - - for (i = 0; i < 6; i++) { - int value, boguscnt = 100000; - do - value = dr32(ROMCmd); - while (value < 0 && --boguscnt > 0); - de->dev->dev_addr[i] = value; - if (boguscnt <= 0) - printk(KERN_WARNING PFX "timeout reading 21040 MAC address byte %u\n", i); - } -} - -static void __init de21040_get_media_info(struct de_private *de) -{ - unsigned int i; - - de->media_type = DE_MEDIA_TP; - de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Full | - SUPPORTED_10baseT_Half | SUPPORTED_AUI; - de->media_advertise = de->media_supported; - - for (i = 0; i < DE_MAX_MEDIA; i++) { - switch (i) { - case DE_MEDIA_AUI: - case DE_MEDIA_TP: - case DE_MEDIA_TP_FD: - de->media[i].type = i; - de->media[i].csr13 = t21040_csr13[i]; - de->media[i].csr14 = t21040_csr14[i]; - de->media[i].csr15 = t21040_csr15[i]; - break; - default: - de->media[i].type = DE_MEDIA_INVALID; - break; - } - } -} - -/* Note: this routine returns extra data bits for size detection. */ -static unsigned __init tulip_read_eeprom(void *regs, int location, int addr_len) -{ - int i; - unsigned retval = 0; - void *ee_addr = regs + ROMCmd; - int read_cmd = location | (EE_READ_CMD << addr_len); - - writel(EE_ENB & ~EE_CS, ee_addr); - writel(EE_ENB, ee_addr); - - /* Shift the read command bits out. */ - for (i = 4 + addr_len; i >= 0; i--) { - short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; - writel(EE_ENB | dataval, ee_addr); - readl(ee_addr); - writel(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); - readl(ee_addr); - retval = (retval << 1) | ((readl(ee_addr) & EE_DATA_READ) ? 1 : 0); - } - writel(EE_ENB, ee_addr); - readl(ee_addr); - - for (i = 16; i > 0; i--) { - writel(EE_ENB | EE_SHIFT_CLK, ee_addr); - readl(ee_addr); - retval = (retval << 1) | ((readl(ee_addr) & EE_DATA_READ) ? 1 : 0); - writel(EE_ENB, ee_addr); - readl(ee_addr); - } - - /* Terminate the EEPROM access. */ - writel(EE_ENB & ~EE_CS, ee_addr); - return retval; -} - -static void __init de21041_get_srom_info (struct de_private *de) -{ - unsigned i, sa_offset = 0, ofs; - u8 ee_data[DE_EEPROM_SIZE + 6] = {}; - unsigned ee_addr_size = tulip_read_eeprom(de->regs, 0xff, 8) & 0x40000 ? 8 : 6; - struct de_srom_info_leaf *il; - void *bufp; - - /* download entire eeprom */ - for (i = 0; i < DE_EEPROM_WORDS; i++) - ((u16 *)ee_data)[i] = - le16_to_cpu(tulip_read_eeprom(de->regs, i, ee_addr_size)); - - /* DEC now has a specification but early board makers - just put the address in the first EEPROM locations. */ - /* This does memcmp(eedata, eedata+16, 8) */ - for (i = 0; i < 8; i ++) - if (ee_data[i] != ee_data[16+i]) - sa_offset = 20; - - /* store MAC address */ - for (i = 0; i < 6; i ++) - de->dev->dev_addr[i] = ee_data[i + sa_offset]; - - /* get offset of controller 0 info leaf. ignore 2nd byte. */ - ofs = ee_data[SROMC0InfoLeaf]; - if (ofs >= (sizeof(ee_data) - sizeof(struct de_srom_info_leaf) - sizeof(struct de_srom_media_block))) - goto bad_srom; - - /* get pointer to info leaf */ - il = (struct de_srom_info_leaf *) &ee_data[ofs]; - - /* paranoia checks */ - if (il->n_blocks == 0) - goto bad_srom; - if ((sizeof(ee_data) - ofs) < - (sizeof(struct de_srom_info_leaf) + (sizeof(struct de_srom_media_block) * il->n_blocks))) - goto bad_srom; - - /* get default media type */ - switch (DE_UNALIGNED_16(&il->default_media)) { - case 0x0001: de->media_type = DE_MEDIA_BNC; break; - case 0x0002: de->media_type = DE_MEDIA_AUI; break; - case 0x0204: de->media_type = DE_MEDIA_TP_FD; break; - default: de->media_type = DE_MEDIA_TP_AUTO; break; - } - - if (netif_msg_probe(de)) - printk(KERN_INFO "de%d: SROM leaf offset %u, default media %s\n", - de->board_idx, ofs, - media_name[de->media_type]); - - /* init SIA register values to defaults */ - for (i = 0; i < DE_MAX_MEDIA; i++) { - de->media[i].type = DE_MEDIA_INVALID; - de->media[i].csr13 = 0xffff; - de->media[i].csr14 = 0xffff; - de->media[i].csr15 = 0xffff; - } - - /* parse media blocks to see what medias are supported, - * and if any custom CSR values are provided - */ - bufp = ((void *)il) + sizeof(*il); - for (i = 0; i < il->n_blocks; i++) { - struct de_srom_media_block *ib = bufp; - unsigned idx; - - /* index based on media type in media block */ - switch(ib->opts & MediaBlockMask) { - case 0: /* 10baseT */ - de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half - | SUPPORTED_Autoneg; - idx = DE_MEDIA_TP; - de->media[DE_MEDIA_TP_AUTO].type = DE_MEDIA_TP_AUTO; - break; - case 1: /* BNC */ - de->media_supported |= SUPPORTED_BNC; - idx = DE_MEDIA_BNC; - break; - case 2: /* AUI */ - de->media_supported |= SUPPORTED_AUI; - idx = DE_MEDIA_AUI; - break; - case 4: /* 10baseT-FD */ - de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Full - | SUPPORTED_Autoneg; - idx = DE_MEDIA_TP_FD; - de->media[DE_MEDIA_TP_AUTO].type = DE_MEDIA_TP_AUTO; - break; - default: - goto bad_srom; - } - - de->media[idx].type = idx; - - if (netif_msg_probe(de)) - printk(KERN_INFO "de%d: media block #%u: %s", - de->board_idx, i, - media_name[de->media[idx].type]); - - bufp += sizeof (ib->opts); - - if (ib->opts & MediaCustomCSRs) { - de->media[idx].csr13 = DE_UNALIGNED_16(&ib->csr13); - de->media[idx].csr14 = DE_UNALIGNED_16(&ib->csr14); - de->media[idx].csr15 = DE_UNALIGNED_16(&ib->csr15); - bufp += sizeof(ib->csr13) + sizeof(ib->csr14) + - sizeof(ib->csr15); - - if (netif_msg_probe(de)) - printk(" (%x,%x,%x)\n", - de->media[idx].csr13, - de->media[idx].csr14, - de->media[idx].csr15); - - } else if (netif_msg_probe(de)) - printk("\n"); - - if (bufp > ((void *)&ee_data[DE_EEPROM_SIZE - 3])) - break; - } - - de->media_advertise = de->media_supported; - -fill_defaults: - /* fill in defaults, for cases where custom CSRs not used */ - for (i = 0; i < DE_MAX_MEDIA; i++) { - if (de->media[i].csr13 == 0xffff) - de->media[i].csr13 = t21041_csr13[i]; - if (de->media[i].csr14 == 0xffff) - de->media[i].csr14 = t21041_csr14[i]; - if (de->media[i].csr15 == 0xffff) - de->media[i].csr15 = t21041_csr15[i]; - } - - de->ee_data = kmalloc(DE_EEPROM_SIZE, GFP_KERNEL); - if (de->ee_data) - memcpy(de->ee_data, &ee_data[0], DE_EEPROM_SIZE); - - return; - -bad_srom: - /* for error cases, it's ok to assume we support all these */ - for (i = 0; i < DE_MAX_MEDIA; i++) - de->media[i].type = i; - de->media_supported = - SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_Autoneg | - SUPPORTED_TP | - SUPPORTED_AUI | - SUPPORTED_BNC; - goto fill_defaults; -} - -static int __init de_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct net_device *dev; - struct de_private *de; - int rc; - void *regs; - long pciaddr; - static int board_idx = -1; - - board_idx++; - -#ifndef MODULE - if (board_idx == 0) - printk("%s", version); -#endif - - /* allocate a new ethernet device structure, and fill in defaults */ - dev = alloc_etherdev(sizeof(struct de_private)); - if (!dev) - return -ENOMEM; - - SET_MODULE_OWNER(dev); - dev->open = de_open; - dev->stop = de_close; - dev->set_multicast_list = de_set_rx_mode; - dev->hard_start_xmit = de_start_xmit; - dev->get_stats = de_get_stats; - dev->do_ioctl = de_ioctl; - dev->tx_timeout = de_tx_timeout; - dev->watchdog_timeo = TX_TIMEOUT; - - dev->irq = pdev->irq; - - de = dev->priv; - de->de21040 = ent->driver_data == 0 ? 1 : 0; - de->pdev = pdev; - de->dev = dev; - de->msg_enable = (debug < 0 ? DE_DEF_MSG_ENABLE : debug); - de->board_idx = board_idx; - spin_lock_init (&de->lock); - init_timer(&de->media_timer); - if (de->de21040) - de->media_timer.function = de21040_media_timer; - else - de->media_timer.function = de21041_media_timer; - de->media_timer.data = (unsigned long) de; - - netif_carrier_off(dev); - netif_stop_queue(dev); - - /* wake up device, assign resources */ - rc = pci_enable_device(pdev); - if (rc) - goto err_out_free; - - /* reserve PCI resources to ensure driver atomicity */ - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) - goto err_out_disable; - - /* check for invalid IRQ value */ - if (pdev->irq < 2) { - rc = -EIO; - printk(KERN_ERR PFX "invalid irq (%d) for pci dev %s\n", - pdev->irq, pdev->slot_name); - goto err_out_res; - } - - /* obtain and check validity of PCI I/O address */ - pciaddr = pci_resource_start(pdev, 1); - if (!pciaddr) { - rc = -EIO; - printk(KERN_ERR PFX "no MMIO resource for pci dev %s\n", - pdev->slot_name); - goto err_out_res; - } - if (pci_resource_len(pdev, 1) < DE_REGS_SIZE) { - rc = -EIO; - printk(KERN_ERR PFX "MMIO resource (%lx) too small on pci dev %s\n", - pci_resource_len(pdev, 1), pdev->slot_name); - goto err_out_res; - } - - /* remap CSR registers */ - regs = ioremap_nocache(pciaddr, DE_REGS_SIZE); - if (!regs) { - rc = -EIO; - printk(KERN_ERR PFX "Cannot map PCI MMIO (%lx@%lx) on pci dev %s\n", - pci_resource_len(pdev, 1), pciaddr, pdev->slot_name); - goto err_out_res; - } - dev->base_addr = (unsigned long) regs; - de->regs = regs; - - de_adapter_wake(de); - - /* make sure hardware is not running */ - rc = de_reset_mac(de); - if (rc) { - printk(KERN_ERR PFX "Cannot reset MAC, pci dev %s\n", - pdev->slot_name); - goto err_out_iomap; - } - - /* get MAC address, initialize default media type and - * get list of supported media - */ - if (de->de21040) { - de21040_get_mac_address(de); - de21040_get_media_info(de); - } else { - de21041_get_srom_info(de); - } - - /* register new network interface with kernel */ - rc = register_netdev(dev); - if (rc) - goto err_out_iomap; - - /* print info about board and interface just registered */ - printk (KERN_INFO "%s: %s at 0x%lx, " - "%02x:%02x:%02x:%02x:%02x:%02x, " - "IRQ %d\n", - dev->name, - de->de21040 ? "21040" : "21041", - dev->base_addr, - dev->dev_addr[0], dev->dev_addr[1], - dev->dev_addr[2], dev->dev_addr[3], - dev->dev_addr[4], dev->dev_addr[5], - dev->irq); - - pci_set_drvdata(pdev, dev); - - /* enable busmastering */ - pci_set_master(pdev); - - /* put adapter to sleep */ - de_adapter_sleep(de); - - return 0; - -err_out_iomap: - if (de->ee_data) - kfree(de->ee_data); - iounmap(regs); -err_out_res: - pci_release_regions(pdev); -err_out_disable: - pci_disable_device(pdev); -err_out_free: - kfree(dev); - return rc; -} - -static void __exit de_remove_one (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct de_private *de = dev->priv; - - if (!dev) - BUG(); - unregister_netdev(dev); - if (de->ee_data) - kfree(de->ee_data); - iounmap(de->regs); - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - kfree(dev); -} - -#ifdef CONFIG_PM - -static int de_suspend (struct pci_dev *pdev, u32 state) -{ - struct net_device *dev = pci_get_drvdata (pdev); - struct de_private *de = dev->priv; - - rtnl_lock(); - if (netif_running (dev)) { - del_timer_sync(&de->media_timer); - - disable_irq(dev->irq); - spin_lock_irq(&de->lock); - - de_stop_hw(de); - netif_stop_queue(dev); - netif_device_detach(dev); - netif_carrier_off(dev); - - spin_unlock_irq(&de->lock); - enable_irq(dev->irq); - - /* Update the error counts. */ - __de_get_stats(de); - - synchronize_irq(); - de_clean_rings(de); - - de_adapter_sleep(de); - pci_disable_device(pdev); - } else { - netif_device_detach(dev); - } - rtnl_unlock(); - return 0; -} - -static int de_resume (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata (pdev); - struct de_private *de = dev->priv; - - rtnl_lock(); - if (netif_device_present(dev)) - goto out; - if (netif_running(dev)) { - pci_enable_device(pdev); - de_init_hw(de); - netif_device_attach(dev); - } else { - netif_device_attach(dev); - } -out: - rtnl_unlock(); - return 0; -} - -#endif /* CONFIG_PM */ - -static struct pci_driver de_driver = { - name: DRV_NAME, - id_table: de_pci_tbl, - probe: de_init_one, - remove: de_remove_one, -#ifdef CONFIG_PM - suspend: de_suspend, - resume: de_resume, -#endif -}; - -static int __init de_init (void) -{ -#ifdef MODULE - printk("%s", version); -#endif - return pci_module_init (&de_driver); -} - -static void __exit de_exit (void) -{ - pci_unregister_driver (&de_driver); -} - -module_init(de_init); -module_exit(de_exit); diff -Nru a/drivers/net/de4x5.c b/drivers/net/de4x5.c --- a/drivers/net/de4x5.c Thu Mar 7 18:17:37 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,5918 +0,0 @@ -/* de4x5.c: A DIGITAL DC21x4x DECchip and DE425/DE434/DE435/DE450/DE500 - ethernet driver for Linux. - - Copyright 1994, 1995 Digital Equipment Corporation. - - Testing resources for this driver have been made available - in part by NASA Ames Research Center (mjacob@nas.nasa.gov). - - The author may be reached at davies@maniac.ultranet.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. - - Originally, this driver was written for the Digital Equipment - Corporation series of EtherWORKS ethernet cards: - - DE425 TP/COAX EISA - DE434 TP PCI - DE435 TP/COAX/AUI PCI - DE450 TP/COAX/AUI PCI - DE500 10/100 PCI Fasternet - - but it will now attempt to support all cards which conform to the - Digital Semiconductor SROM Specification. The driver currently - recognises the following chips: - - DC21040 (no SROM) - DC21041[A] - DC21140[A] - DC21142 - DC21143 - - So far the driver is known to work with the following cards: - - KINGSTON - Linksys - ZNYX342 - SMC8432 - SMC9332 (w/new SROM) - ZNYX31[45] - ZNYX346 10/100 4 port (can act as a 10/100 bridge!) - - The driver has been tested on a relatively busy network using the DE425, - DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred - 16M of data to a DECstation 5000/200 as follows: - - TCP UDP - TX RX TX RX - DE425 1030k 997k 1170k 1128k - DE434 1063k 995k 1170k 1125k - DE435 1063k 995k 1170k 1125k - DE500 1063k 998k 1170k 1125k in 10Mb/s mode - - All values are typical (in kBytes/sec) from a sample of 4 for each - measurement. Their error is +/-20k on a quiet (private) network and also - depend on what load the CPU has. - - ========================================================================= - This driver has been written substantially from scratch, although its - inheritance of style and stack interface from 'ewrk3.c' and in turn from - Donald Becker's 'lance.c' should be obvious. With the module autoload of - every usable DECchip board, I pinched Donald's 'next_module' field to - link my modules together. - - Upto 15 EISA cards can be supported under this driver, limited primarily - by the available IRQ lines. I have checked different configurations of - multiple depca, EtherWORKS 3 cards and de4x5 cards and have not found a - problem yet (provided you have at least depca.c v0.38) ... - - PCI support has been added to allow the driver to work with the DE434, - DE435, DE450 and DE500 cards. The I/O accesses are a bit of a kludge due - to the differences in the EISA and PCI CSR address offsets from the base - address. - - The ability to load this driver as a loadable module has been included - and used extensively during the driver development (to save those long - reboot sequences). Loadable module support under PCI and EISA has been - achieved by letting the driver autoprobe as if it were compiled into the - kernel. Do make sure you're not sharing interrupts with anything that - cannot accommodate interrupt sharing! - - To utilise this ability, you have to do 8 things: - - 0) have a copy of the loadable modules code installed on your system. - 1) copy de4x5.c from the /linux/drivers/net directory to your favourite - temporary directory. - 2) for fixed autoprobes (not recommended), edit the source code near - line 5594 to reflect the I/O address you're using, or assign these when - loading by: - - insmod de4x5 io=0xghh where g = bus number - hh = device number - - NB: autoprobing for modules is now supported by default. You may just - use: - - insmod de4x5 - - to load all available boards. For a specific board, still use - the 'io=?' above. - 3) compile de4x5.c, but include -DMODULE in the command line to ensure - that the correct bits are compiled (see end of source code). - 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a - kernel with the de4x5 configuration turned off and reboot. - 5) insmod de4x5 [io=0xghh] - 6) run the net startup bits for your new eth?? interface(s) manually - (usually /etc/rc.inet[12] at boot time). - 7) enjoy! - - To unload a module, turn off the associated interface(s) - 'ifconfig eth?? down' then 'rmmod de4x5'. - - Automedia detection is included so that in principal you can disconnect - from, e.g. TP, reconnect to BNC and things will still work (after a - pause whilst the driver figures out where its media went). My tests - using ping showed that it appears to work.... - - By default, the driver will now autodetect any DECchip based card. - Should you have a need to restrict the driver to DIGITAL only cards, you - can compile with a DEC_ONLY define, or if loading as a module, use the - 'dec_only=1' parameter. - - I've changed the timing routines to use the kernel timer and scheduling - functions so that the hangs and other assorted problems that occurred - while autosensing the media should be gone. A bonus for the DC21040 - auto media sense algorithm is that it can now use one that is more in - line with the rest (the DC21040 chip doesn't have a hardware timer). - The downside is the 1 'jiffies' (10ms) resolution. - - IEEE 802.3u MII interface code has been added in anticipation that some - products may use it in the future. - - The SMC9332 card has a non-compliant SROM which needs fixing - I have - patched this driver to detect it because the SROM format used complies - to a previous DEC-STD format. - - I have removed the buffer copies needed for receive on Intels. I cannot - remove them for Alphas since the Tulip hardware only does longword - aligned DMA transfers and the Alphas get alignment traps with non - longword aligned data copies (which makes them really slow). No comment. - - I have added SROM decoding routines to make this driver work with any - card that supports the Digital Semiconductor SROM spec. This will help - all cards running the dc2114x series chips in particular. Cards using - the dc2104x chips should run correctly with the basic driver. I'm in - debt to for the testing and feedback that helped get - this feature working. So far we have tested KINGSTON, SMC8432, SMC9332 - (with the latest SROM complying with the SROM spec V3: their first was - broken), ZNYX342 and LinkSys. ZYNX314 (dual 21041 MAC) and ZNYX 315 - (quad 21041 MAC) cards also appear to work despite their incorrectly - wired IRQs. - - I have added a temporary fix for interrupt problems when some SCSI cards - share the same interrupt as the DECchip based cards. The problem occurs - because the SCSI card wants to grab the interrupt as a fast interrupt - (runs the service routine with interrupts turned off) vs. this card - which really needs to run the service routine with interrupts turned on. - This driver will now add the interrupt service routine as a fast - interrupt if it is bounced from the slow interrupt. THIS IS NOT A - RECOMMENDED WAY TO RUN THE DRIVER and has been done for a limited time - until people sort out their compatibility issues and the kernel - interrupt service code is fixed. YOU SHOULD SEPARATE OUT THE FAST - INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not - run on the same interrupt. PCMCIA/CardBus is another can of worms... - - Finally, I think I have really fixed the module loading problem with - more than one DECchip based card. As a side effect, I don't mess with - the device structure any more which means that if more than 1 card in - 2.0.x is installed (4 in 2.1.x), the user will have to edit - linux/drivers/net/Space.c to make room for them. Hence, module loading - is the preferred way to use this driver, since it doesn't have this - limitation. - - Where SROM media detection is used and full duplex is specified in the - SROM, the feature is ignored unless lp->params.fdx is set at compile - time OR during a module load (insmod de4x5 args='eth??:fdx' [see - below]). This is because there is no way to automatically detect full - duplex links except through autonegotiation. When I include the - autonegotiation feature in the SROM autoconf code, this detection will - occur automatically for that case. - - Command line arguments are now allowed, similar to passing arguments - through LILO. This will allow a per adapter board set up of full duplex - and media. The only lexical constraints are: the board name (dev->name) - appears in the list before its parameters. The list of parameters ends - either at the end of the parameter list or with another board name. The - following parameters are allowed: - - fdx for full duplex - autosense to set the media/speed; with the following - sub-parameters: - TP, TP_NW, BNC, AUI, BNC_AUI, 100Mb, 10Mb, AUTO - - Case sensitivity is important for the sub-parameters. They *must* be - upper case. Examples: - - insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. - - For a compiled in driver, at or above line 548, place e.g. - #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP" - - Yes, I know full duplex isn't permissible on BNC or AUI; they're just - examples. By default, full duplex is turned off and AUTO is the default - autosense setting. In reality, I expect only the full duplex option to - be used. Note the use of single quotes in the two examples above and the - lack of commas to separate items. ALSO, you must get the requested media - correct in relation to what the adapter SROM says it has. There's no way - to determine this in advance other than by trial and error and common - sense, e.g. call a BNC connectored port 'BNC', not '10Mb'. - - Changed the bus probing. EISA used to be done first, followed by PCI. - Most people probably don't even know what a de425 is today and the EISA - probe has messed up some SCSI cards in the past, so now PCI is always - probed first followed by EISA if a) the architecture allows EISA and - either b) there have been no PCI cards detected or c) an EISA probe is - forced by the user. To force a probe include "force_eisa" in your - insmod "args" line; for built-in kernels either change the driver to do - this automatically or include #define DE4X5_FORCE_EISA on or before - line 1040 in the driver. - - TO DO: - ------ - - Revision History - ---------------- - - Version Date Description - - 0.1 17-Nov-94 Initial writing. ALPHA code release. - 0.2 13-Jan-95 Added PCI support for DE435's. - 0.21 19-Jan-95 Added auto media detection. - 0.22 10-Feb-95 Fix interrupt handler call . - Fix recognition bug reported by . - Add request/release_region code. - Add loadable modules support for PCI. - Clean up loadable modules support. - 0.23 28-Feb-95 Added DC21041 and DC21140 support. - Fix missed frame counter value and initialisation. - Fixed EISA probe. - 0.24 11-Apr-95 Change delay routine to use . - Change TX_BUFFS_AVAIL macro. - Change media autodetection to allow manual setting. - Completed DE500 (DC21140) support. - 0.241 18-Apr-95 Interim release without DE500 Autosense Algorithm. - 0.242 10-May-95 Minor changes. - 0.30 12-Jun-95 Timer fix for DC21140. - Portability changes. - Add ALPHA changes from . - Add DE500 semi automatic autosense. - Add Link Fail interrupt TP failure detection. - Add timer based link change detection. - Plugged a memory leak in de4x5_queue_pkt(). - 0.31 13-Jun-95 Fixed PCI stuff for 1.3.1. - 0.32 26-Jun-95 Added verify_area() calls in de4x5_ioctl() from a - suggestion by . - 0.33 8-Aug-95 Add shared interrupt support (not released yet). - 0.331 21-Aug-95 Fix de4x5_open() with fast CPUs. - Fix de4x5_interrupt(). - Fix dc21140_autoconf() mess. - No shared interrupt support. - 0.332 11-Sep-95 Added MII management interface routines. - 0.40 5-Mar-96 Fix setup frame timeout . - Add kernel timer code (h/w is too flaky). - Add MII based PHY autosense. - Add new multicasting code. - Add new autosense algorithms for media/mode - selection using kernel scheduling/timing. - Re-formatted. - Made changes suggested by : - Change driver to detect all DECchip based cards - with DEC_ONLY restriction a special case. - Changed driver to autoprobe as a module. No irq - checking is done now - assume BIOS is good! - Added SMC9332 detection - 0.41 21-Mar-96 Don't check for get_hw_addr checksum unless DEC card - only - Fix for multiple PCI cards reported by - Duh, put the SA_SHIRQ flag into request_interrupt(). - Fix SMC ethernet address in enet_det[]. - Print chip name instead of "UNKNOWN" during boot. - 0.42 26-Apr-96 Fix MII write TA bit error. - Fix bug in dc21040 and dc21041 autosense code. - Remove buffer copies on receive for Intels. - Change sk_buff handling during media disconnects to - eliminate DUP packets. - Add dynamic TX thresholding. - Change all chips to use perfect multicast filtering. - Fix alloc_device() bug - 0.43 21-Jun-96 Fix unconnected media TX retry bug. - Add Accton to the list of broken cards. - Fix TX under-run bug for non DC21140 chips. - Fix boot command probe bug in alloc_device() as - reported by and - . - Add cache locks to prevent a race condition as - reported by and - . - Upgraded alloc_device() code. - 0.431 28-Jun-96 Fix potential bug in queue_pkt() from discussion - with - 0.44 13-Aug-96 Fix RX overflow bug in 2114[023] chips. - Fix EISA probe bugs reported by - and . - 0.441 9-Sep-96 Change dc21041_autoconf() to probe quiet BNC media - with a loopback packet. - 0.442 9-Sep-96 Include AUI in dc21041 media printout. Bug reported - by - 0.45 8-Dec-96 Include endian functions for PPC use, from work - by and . - 0.451 28-Dec-96 Added fix to allow autoprobe for modules after - suggestion from . - 0.5 30-Jan-97 Added SROM decoding functions. - Updated debug flags. - Fix sleep/wakeup calls for PCI cards, bug reported - by . - Added multi-MAC, one SROM feature from discussion - with . - Added full module autoprobe capability. - Added attempt to use an SMC9332 with broken SROM. - Added fix for ZYNX multi-mac cards that didn't - get their IRQs wired correctly. - 0.51 13-Feb-97 Added endian fixes for the SROM accesses from - - Fix init_connection() to remove extra device reset. - Fix MAC/PHY reset ordering in dc21140m_autoconf(). - Fix initialisation problem with lp->timeout in - typeX_infoblock() from . - Fix MII PHY reset problem from work done by - . - 0.52 26-Apr-97 Some changes may not credit the right people - - a disk crash meant I lost some mail. - Change RX interrupt routine to drop rather than - defer packets to avoid hang reported by - . - Fix srom_exec() to return for COMPACT and type 1 - infoblocks. - Added DC21142 and DC21143 functions. - Added byte counters from - Added SA_INTERRUPT temporary fix from - . - 0.53 12-Nov-97 Fix the *_probe() to include 'eth??' name during - module load: bug reported by - - Fix multi-MAC, one SROM, to work with 2114x chips: - bug reported by . - Make above search independent of BIOS device scan - direction. - Completed DC2114[23] autosense functions. - 0.531 21-Dec-97 Fix DE500-XA 100Mb/s bug reported by - and - . - Added argument list to set up each board from either - a module's command line or a compiled in #define. - Added generic MII PHY functionality to deal with - newer PHY chips. - Fix the mess in 2.1.67. - 0.532 5-Jan-98 Fix bug in mii_get_phy() reported by - . - Fix bug in pci_probe() for 64 bit systems reported - by . - 0.533 9-Jan-98 Fix more 64 bit bugs reported by . - 0.534 24-Jan-98 Fix last (?) endian bug from - 0.535 21-Feb-98 Fix Ethernet Address PROM reset bug for DC21040. - 0.536 21-Mar-98 Change pci_probe() to use the pci_dev structure. - **Incompatible with 2.0.x from here.** - 0.540 5-Jul-98 Atomicize assertion of dev->interrupt for SMP - from - Add TP, AUI and BNC cases to 21140m_autoconf() for - case where a 21140 under SROM control uses, e.g. AUI - from problem report by - Add MII parallel detection to 2114x_autoconf() for - case where no autonegotiation partner exists from - problem report by . - Add ability to force connection type directly even - when using SROM control from problem report by - . - Updated the PCI interface to conform with the latest - version. I hope nothing is broken... - Add TX done interrupt modification from suggestion - by . - Fix is_anc_capable() bug reported by - . - Fix type[13]_infoblock() bug: during MII search, PHY - lp->rst not run because lp->ibn not initialised - - from report & fix by . - Fix probe bug with EISA & PCI cards present from - report by . - 0.541 24-Aug-98 Fix compiler problems associated with i386-string - ops from multiple bug reports and temporary fix - from . - Fix pci_probe() to correctly emulate the old - pcibios_find_class() function. - Add an_exception() for old ZYNX346 and fix compile - warning on PPC & SPARC, from . - Fix lastPCI to correctly work with compiled in - kernels and modules from bug report by - et al. - 0.542 15-Sep-98 Fix dc2114x_autoconf() to stop multiple messages - when media is unconnected. - Change dev->interrupt to lp->interrupt to ensure - alignment for Alpha's and avoid their unaligned - access traps. This flag is merely for log messages: - should do something more definitive though... - 0.543 30-Dec-98 Add SMP spin locking. - 0.544 8-May-99 Fix for buggy SROM in Motorola embedded boards using - a 21143 by . - Change PCI/EISA bus probing order. - 0.545 28-Nov-99 Further Moto SROM bug fix from - - Remove double checking for DEBUG_RX in de4x5_dbg_rx() - from report by - 0.546 22-Feb-01 Fixes Alpha XP1000 oops. The srom_search function - was causing a page fault when initializing the - variable 'pb', on a non de4x5 PCI device, in this - case a PCI bridge (DEC chip 21152). The value of - 'pb' is now only initialized if a de4x5 chip is - present. - - 0.547 08-Nov-01 Use library crc32 functions by - ========================================================================= -*/ - -static const char *version = "de4x5.c:V0.546 2001/02/22 davies@maniac.ultranet.com\n"; - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_PPC -#include -#endif /* CONFIG_PPC */ - -#include -#include -#include - -#include -#include -#include -#include - -#include "de4x5.h" - -#define c_char const char -#define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a))) - -/* -** MII Information -*/ -struct phy_table { - int reset; /* Hard reset required? */ - int id; /* IEEE OUI */ - int ta; /* One cycle TA time - 802.3u is confusing here */ - struct { /* Non autonegotiation (parallel) speed det. */ - int reg; - int mask; - int value; - } spd; -}; - -struct mii_phy { - int reset; /* Hard reset required? */ - int id; /* IEEE OUI */ - int ta; /* One cycle TA time */ - struct { /* Non autonegotiation (parallel) speed det. */ - int reg; - int mask; - int value; - } spd; - int addr; /* MII address for the PHY */ - u_char *gep; /* Start of GEP sequence block in SROM */ - u_char *rst; /* Start of reset sequence in SROM */ - u_int mc; /* Media Capabilities */ - u_int ana; /* NWay Advertisement */ - u_int fdx; /* Full DupleX capabilites for each media */ - u_int ttm; /* Transmit Threshold Mode for each media */ - u_int mci; /* 21142 MII Connector Interrupt info */ -}; - -#define DE4X5_MAX_PHY 8 /* Allow upto 8 attached PHY devices per board */ - -struct sia_phy { - u_char mc; /* Media Code */ - u_char ext; /* csr13-15 valid when set */ - int csr13; /* SIA Connectivity Register */ - int csr14; /* SIA TX/RX Register */ - int csr15; /* SIA General Register */ - int gepc; /* SIA GEP Control Information */ - int gep; /* SIA GEP Data */ -}; - -/* -** Define the know universe of PHY devices that can be -** recognised by this driver. -*/ -static struct phy_table phy_info[] = { - {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}}, /* National TX */ - {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}}, /* Broadcom T4 */ - {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */ - {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}, /* Cypress T4 */ - {0, 0x7810 , 1, {0x14, 0x0800, 0x0800}} /* Level One LTX970 */ -}; - -/* -** These GENERIC values assumes that the PHY devices follow 802.3u and -** allow parallel detection to set the link partner ability register. -** Detection of 100Base-TX [H/F Duplex] and 100Base-T4 is supported. -*/ -#define GENERIC_REG 0x05 /* Autoneg. Link Partner Advertisement Reg. */ -#define GENERIC_MASK MII_ANLPA_100M /* All 100Mb/s Technologies */ -#define GENERIC_VALUE MII_ANLPA_100M /* 100B-TX, 100B-TX FDX, 100B-T4 */ - -/* -** Define special SROM detection cases -*/ -static c_char enet_det[][ETH_ALEN] = { - {0x00, 0x00, 0xc0, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xe8, 0x00, 0x00, 0x00} -}; - -#define SMC 1 -#define ACCTON 2 - -/* -** SROM Repair definitions. If a broken SROM is detected a card may -** use this information to help figure out what to do. This is a -** "stab in the dark" and so far for SMC9332's only. -*/ -static c_char srom_repair_info[][100] = { - {0x00,0x1e,0x00,0x00,0x00,0x08, /* SMC9332 */ - 0x1f,0x01,0x8f,0x01,0x00,0x01,0x00,0x02, - 0x01,0x00,0x00,0x78,0xe0,0x01,0x00,0x50, - 0x00,0x18,} -}; - - -#ifdef DE4X5_DEBUG -static int de4x5_debug = DE4X5_DEBUG; -#else -/*static int de4x5_debug = (DEBUG_MII | DEBUG_SROM | DEBUG_PCICFG | DEBUG_MEDIA | DEBUG_VERSION);*/ -static int de4x5_debug = (DEBUG_MEDIA | DEBUG_VERSION); -#endif - -/* -** Allow per adapter set up. For modules this is simply a command line -** parameter, e.g.: -** insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. -** -** For a compiled in driver, place e.g. -** #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP" -** here -*/ -#ifdef DE4X5_PARM -static char *args = DE4X5_PARM; -#else -static char *args; -#endif - -struct parameters { - int fdx; - int autosense; -}; - -#define DE4X5_AUTOSENSE_MS 250 /* msec autosense tick (DE500) */ - -#define DE4X5_NDA 0xffe0 /* No Device (I/O) Address */ - -/* -** Ethernet PROM defines -*/ -#define PROBE_LENGTH 32 -#define ETH_PROM_SIG 0xAA5500FFUL - -/* -** Ethernet Info -*/ -#define PKT_BUF_SZ 1536 /* Buffer size for each Tx/Rx buffer */ -#define IEEE802_3_SZ 1518 /* Packet + CRC */ -#define MAX_PKT_SZ 1514 /* Maximum ethernet packet length */ -#define MAX_DAT_SZ 1500 /* Maximum ethernet data length */ -#define MIN_DAT_SZ 1 /* Minimum ethernet data length */ -#define PKT_HDR_LEN 14 /* Addresses and data length info */ -#define FAKE_FRAME_LEN (MAX_PKT_SZ + 1) -#define QUEUE_PKT_TIMEOUT (3*HZ) /* 3 second timeout */ - - -/* -** EISA bus defines -*/ -#define DE4X5_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */ -#define DE4X5_EISA_TOTAL_SIZE 0x100 /* I/O address extent */ - -#define MAX_EISA_SLOTS 16 -#define EISA_SLOT_INC 0x1000 -#define EISA_ALLOWED_IRQ_LIST {5, 9, 10, 11} - -#define DE4X5_SIGNATURE {"DE425","DE434","DE435","DE450","DE500"} -#define DE4X5_NAME_LENGTH 8 - -/* -** Ethernet PROM defines for DC21040 -*/ -#define PROBE_LENGTH 32 -#define ETH_PROM_SIG 0xAA5500FFUL - -/* -** PCI Bus defines -*/ -#define PCI_MAX_BUS_NUM 8 -#define DE4X5_PCI_TOTAL_SIZE 0x80 /* I/O address extent */ -#define DE4X5_CLASS_CODE 0x00020000 /* Network controller, Ethernet */ -#define NO_MORE_PCI -2 /* PCI bus search all done */ - -/* -** Memory Alignment. Each descriptor is 4 longwords long. To force a -** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and -** DESC_ALIGN. ALIGN aligns the start address of the private memory area -** and hence the RX descriptor ring's first entry. -*/ -#define ALIGN4 ((u_long)4 - 1) /* 1 longword align */ -#define ALIGN8 ((u_long)8 - 1) /* 2 longword align */ -#define ALIGN16 ((u_long)16 - 1) /* 4 longword align */ -#define ALIGN32 ((u_long)32 - 1) /* 8 longword align */ -#define ALIGN64 ((u_long)64 - 1) /* 16 longword align */ -#define ALIGN128 ((u_long)128 - 1) /* 32 longword align */ - -#define ALIGN ALIGN32 /* Keep the DC21040 happy... */ -#define CACHE_ALIGN CAL_16LONG -#define DESC_SKIP_LEN DSL_0 /* Must agree with DESC_ALIGN */ -/*#define DESC_ALIGN u32 dummy[4]; / * Must agree with DESC_SKIP_LEN */ -#define DESC_ALIGN - -#ifndef DEC_ONLY /* See README.de4x5 for using this */ -static int dec_only; -#else -static int dec_only = 1; -#endif - -/* -** DE4X5 IRQ ENABLE/DISABLE -*/ -#define ENABLE_IRQs { \ - imr |= lp->irq_en;\ - outl(imr, DE4X5_IMR); /* Enable the IRQs */\ -} - -#define DISABLE_IRQs {\ - imr = inl(DE4X5_IMR);\ - imr &= ~lp->irq_en;\ - outl(imr, DE4X5_IMR); /* Disable the IRQs */\ -} - -#define UNMASK_IRQs {\ - imr |= lp->irq_mask;\ - outl(imr, DE4X5_IMR); /* Unmask the IRQs */\ -} - -#define MASK_IRQs {\ - imr = inl(DE4X5_IMR);\ - imr &= ~lp->irq_mask;\ - outl(imr, DE4X5_IMR); /* Mask the IRQs */\ -} - -/* -** DE4X5 START/STOP -*/ -#define START_DE4X5 {\ - omr = inl(DE4X5_OMR);\ - omr |= OMR_ST | OMR_SR;\ - outl(omr, DE4X5_OMR); /* Enable the TX and/or RX */\ -} - -#define STOP_DE4X5 {\ - omr = inl(DE4X5_OMR);\ - omr &= ~(OMR_ST|OMR_SR);\ - outl(omr, DE4X5_OMR); /* Disable the TX and/or RX */ \ -} - -/* -** DE4X5 SIA RESET -*/ -#define RESET_SIA outl(0, DE4X5_SICR); /* Reset SIA connectivity regs */ - -/* -** DE500 AUTOSENSE TIMER INTERVAL (MILLISECS) -*/ -#define DE4X5_AUTOSENSE_MS 250 - -/* -** SROM Structure -*/ -struct de4x5_srom { - char sub_vendor_id[2]; - char sub_system_id[2]; - char reserved[12]; - char id_block_crc; - char reserved2; - char version; - char num_controllers; - char ieee_addr[6]; - char info[100]; - short chksum; -}; -#define SUB_VENDOR_ID 0x500a - -/* -** DE4X5 Descriptors. Make sure that all the RX buffers are contiguous -** and have sizes of both a power of 2 and a multiple of 4. -** A size of 256 bytes for each buffer could be chosen because over 90% of -** all packets in our network are <256 bytes long and 64 longword alignment -** is possible. 1536 showed better 'ttcp' performance. Take your pick. 32 TX -** descriptors are needed for machines with an ALPHA CPU. -*/ -#define NUM_RX_DESC 8 /* Number of RX descriptors */ -#define NUM_TX_DESC 32 /* Number of TX descriptors */ -#define RX_BUFF_SZ 1536 /* Power of 2 for kmalloc and */ - /* Multiple of 4 for DC21040 */ - /* Allows 512 byte alignment */ -struct de4x5_desc { - volatile s32 status; - u32 des1; - u32 buf; - u32 next; - DESC_ALIGN -}; - -/* -** The DE4X5 private structure -*/ -#define DE4X5_PKT_STAT_SZ 16 -#define DE4X5_PKT_BIN_SZ 128 /* Should be >=100 unless you - increase DE4X5_PKT_STAT_SZ */ - -struct pkt_stats { - u_int bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */ - u_int unicast; - u_int multicast; - u_int broadcast; - u_int excessive_collisions; - u_int tx_underruns; - u_int excessive_underruns; - u_int rx_runt_frames; - u_int rx_collision; - u_int rx_dribble; - u_int rx_overflow; -}; - -struct de4x5_private { - char adapter_name[80]; /* Adapter name */ - u_long interrupt; /* Aligned ISR flag */ - struct de4x5_desc *rx_ring; /* RX descriptor ring */ - struct de4x5_desc *tx_ring; /* TX descriptor ring */ - struct sk_buff *tx_skb[NUM_TX_DESC]; /* TX skb for freeing when sent */ - struct sk_buff *rx_skb[NUM_RX_DESC]; /* RX skb's */ - int rx_new, rx_old; /* RX descriptor ring pointers */ - int tx_new, tx_old; /* TX descriptor ring pointers */ - char setup_frame[SETUP_FRAME_LEN]; /* Holds MCA and PA info. */ - char frame[64]; /* Min sized packet for loopback*/ - spinlock_t lock; /* Adapter specific spinlock */ - struct net_device_stats stats; /* Public stats */ - struct pkt_stats pktStats; /* Private stats counters */ - char rxRingSize; - char txRingSize; - int bus; /* EISA or PCI */ - int bus_num; /* PCI Bus number */ - int device; /* Device number on PCI bus */ - int state; /* Adapter OPENED or CLOSED */ - int chipset; /* DC21040, DC21041 or DC21140 */ - s32 irq_mask; /* Interrupt Mask (Enable) bits */ - s32 irq_en; /* Summary interrupt bits */ - int media; /* Media (eg TP), mode (eg 100B)*/ - int c_media; /* Remember the last media conn */ - int fdx; /* media full duplex flag */ - int linkOK; /* Link is OK */ - int autosense; /* Allow/disallow autosensing */ - int tx_enable; /* Enable descriptor polling */ - int setup_f; /* Setup frame filtering type */ - int local_state; /* State within a 'media' state */ - struct mii_phy phy[DE4X5_MAX_PHY]; /* List of attached PHY devices */ - struct sia_phy sia; /* SIA PHY Information */ - int active; /* Index to active PHY device */ - int mii_cnt; /* Number of attached PHY's */ - int timeout; /* Scheduling counter */ - struct timer_list timer; /* Timer info for kernel */ - int tmp; /* Temporary global per card */ - struct { - void *priv; /* Original kmalloc'd mem addr */ - u_long lock; /* Lock the cache accesses */ - s32 csr0; /* Saved Bus Mode Register */ - s32 csr6; /* Saved Operating Mode Reg. */ - s32 csr7; /* Saved IRQ Mask Register */ - s32 gep; /* Saved General Purpose Reg. */ - s32 gepc; /* Control info for GEP */ - s32 csr13; /* Saved SIA Connectivity Reg. */ - s32 csr14; /* Saved SIA TX/RX Register */ - s32 csr15; /* Saved SIA General Register */ - int save_cnt; /* Flag if state already saved */ - struct sk_buff *skb; /* Save the (re-ordered) skb's */ - } cache; - struct de4x5_srom srom; /* A copy of the SROM */ - struct net_device *next_module; /* Link to the next module */ - int rx_ovf; /* Check for 'RX overflow' tag */ - int useSROM; /* For non-DEC card use SROM */ - int useMII; /* Infoblock using the MII */ - int asBitValid; /* Autosense bits in GEP? */ - int asPolarity; /* 0 => asserted high */ - int asBit; /* Autosense bit number in GEP */ - int defMedium; /* SROM default medium */ - int tcount; /* Last infoblock number */ - int infoblock_init; /* Initialised this infoblock? */ - int infoleaf_offset; /* SROM infoleaf for controller */ - s32 infoblock_csr6; /* csr6 value in SROM infoblock */ - int infoblock_media; /* infoblock media */ - int (*infoleaf_fn)(struct net_device *); /* Pointer to infoleaf function */ - u_char *rst; /* Pointer to Type 5 reset info */ - u_char ibn; /* Infoblock number */ - struct parameters params; /* Command line/ #defined params */ - struct pci_dev *pdev; /* Device cookie for DMA alloc */ - dma_addr_t dma_rings; /* DMA handle for rings */ - int dma_size; /* Size of the DMA area */ - char *rx_bufs; /* rx bufs on alpha, sparc, ... */ -}; - -/* -** Kludge to get around the fact that the CSR addresses have different -** offsets in the PCI and EISA boards. Also note that the ethernet address -** PROM is accessed differently. -*/ -static struct bus_type { - int bus; - int bus_num; - int device; - int chipset; - struct de4x5_srom srom; - int autosense; - int useSROM; -} bus; - -/* -** To get around certain poxy cards that don't provide an SROM -** for the second and more DECchip, I have to key off the first -** chip's address. I'll assume there's not a bad SROM iff: -** -** o the chipset is the same -** o the bus number is the same and > 0 -** o the sum of all the returned hw address bytes is 0 or 0x5fa -** -** Also have to save the irq for those cards whose hardware designers -** can't follow the PCI to PCI Bridge Architecture spec. -*/ -static struct { - int chipset; - int bus; - int irq; - u_char addr[ETH_ALEN]; -} last = {0,}; - -/* -** The transmit ring full condition is described by the tx_old and tx_new -** pointers by: -** tx_old = tx_new Empty ring -** tx_old = tx_new+1 Full ring -** tx_old+txRingSize = tx_new+1 Full ring (wrapped condition) -*/ -#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ - lp->tx_old+lp->txRingSize-lp->tx_new-1:\ - lp->tx_old -lp->tx_new-1) - -#define TX_PKT_PENDING (lp->tx_old != lp->tx_new) - -/* -** Public Functions -*/ -static int de4x5_open(struct net_device *dev); -static int de4x5_queue_pkt(struct sk_buff *skb, struct net_device *dev); -static void de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static int de4x5_close(struct net_device *dev); -static struct net_device_stats *de4x5_get_stats(struct net_device *dev); -static void de4x5_local_stats(struct net_device *dev, char *buf, int pkt_len); -static void set_multicast_list(struct net_device *dev); -static int de4x5_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); - -/* -** Private functions -*/ -static int de4x5_hw_init(struct net_device *dev, u_long iobase, struct pci_dev *pdev); -static int de4x5_init(struct net_device *dev); -static int de4x5_sw_reset(struct net_device *dev); -static int de4x5_rx(struct net_device *dev); -static int de4x5_tx(struct net_device *dev); -static int de4x5_ast(struct net_device *dev); -static int de4x5_txur(struct net_device *dev); -static int de4x5_rx_ovfc(struct net_device *dev); - -static int autoconf_media(struct net_device *dev); -static void create_packet(struct net_device *dev, char *frame, int len); -static void load_packet(struct net_device *dev, char *buf, u32 flags, struct sk_buff *skb); -static int dc21040_autoconf(struct net_device *dev); -static int dc21041_autoconf(struct net_device *dev); -static int dc21140m_autoconf(struct net_device *dev); -static int dc2114x_autoconf(struct net_device *dev); -static int srom_autoconf(struct net_device *dev); -static int de4x5_suspect_state(struct net_device *dev, int timeout, int prev_state, int (*fn)(struct net_device *, int), int (*asfn)(struct net_device *)); -static int dc21040_state(struct net_device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct net_device *, int)); -static int test_media(struct net_device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec); -static int test_for_100Mb(struct net_device *dev, int msec); -static int wait_for_link(struct net_device *dev); -static int test_mii_reg(struct net_device *dev, int reg, int mask, int pol, long msec); -static int is_spd_100(struct net_device *dev); -static int is_100_up(struct net_device *dev); -static int is_10_up(struct net_device *dev); -static int is_anc_capable(struct net_device *dev); -static int ping_media(struct net_device *dev, int msec); -static struct sk_buff *de4x5_alloc_rx_buff(struct net_device *dev, int index, int len); -static void de4x5_free_rx_buffs(struct net_device *dev); -static void de4x5_free_tx_buffs(struct net_device *dev); -static void de4x5_save_skbs(struct net_device *dev); -static void de4x5_rst_desc_ring(struct net_device *dev); -static void de4x5_cache_state(struct net_device *dev, int flag); -static void de4x5_put_cache(struct net_device *dev, struct sk_buff *skb); -static void de4x5_putb_cache(struct net_device *dev, struct sk_buff *skb); -static struct sk_buff *de4x5_get_cache(struct net_device *dev); -static void de4x5_setup_intr(struct net_device *dev); -static void de4x5_init_connection(struct net_device *dev); -static int de4x5_reset_phy(struct net_device *dev); -static void reset_init_sia(struct net_device *dev, s32 sicr, s32 strr, s32 sigr); -static int test_ans(struct net_device *dev, s32 irqs, s32 irq_mask, s32 msec); -static int test_tp(struct net_device *dev, s32 msec); -static int EISA_signature(char *name, s32 eisa_id); -static int PCI_signature(char *name, struct bus_type *lp); -static void DevicePresent(u_long iobase); -static void enet_addr_rst(u_long aprom_addr); -static int de4x5_bad_srom(struct bus_type *lp); -static short srom_rd(u_long address, u_char offset); -static void srom_latch(u_int command, u_long address); -static void srom_command(u_int command, u_long address); -static void srom_address(u_int command, u_long address, u_char offset); -static short srom_data(u_int command, u_long address); -/*static void srom_busy(u_int command, u_long address);*/ -static void sendto_srom(u_int command, u_long addr); -static int getfrom_srom(u_long addr); -static int srom_map_media(struct net_device *dev); -static int srom_infoleaf_info(struct net_device *dev); -static void srom_init(struct net_device *dev); -static void srom_exec(struct net_device *dev, u_char *p); -static int mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr); -static void mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr); -static int mii_rdata(u_long ioaddr); -static void mii_wdata(int data, int len, u_long ioaddr); -static void mii_ta(u_long rw, u_long ioaddr); -static int mii_swap(int data, int len); -static void mii_address(u_char addr, u_long ioaddr); -static void sendto_mii(u32 command, int data, u_long ioaddr); -static int getfrom_mii(u32 command, u_long ioaddr); -static int mii_get_oui(u_char phyaddr, u_long ioaddr); -static int mii_get_phy(struct net_device *dev); -static void SetMulticastFilter(struct net_device *dev); -static int get_hw_addr(struct net_device *dev); -static void srom_repair(struct net_device *dev, int card); -static int test_bad_enet(struct net_device *dev, int status); -static int an_exception(struct bus_type *lp); -#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) -static void eisa_probe(struct net_device *dev, u_long iobase); -#endif -static void pci_probe(struct net_device *dev, u_long iobase); -static void srom_search(struct pci_dev *pdev); -static char *build_setup_frame(struct net_device *dev, int mode); -static void disable_ast(struct net_device *dev); -static void enable_ast(struct net_device *dev, u32 time_out); -static long de4x5_switch_mac_port(struct net_device *dev); -static int gep_rd(struct net_device *dev); -static void gep_wr(s32 data, struct net_device *dev); -static void timeout(struct net_device *dev, void (*fn)(u_long data), u_long data, u_long msec); -static void yawn(struct net_device *dev, int state); -static void link_modules(struct net_device *dev, struct net_device *tmp); -static void de4x5_parse_params(struct net_device *dev); -static void de4x5_dbg_open(struct net_device *dev); -static void de4x5_dbg_mii(struct net_device *dev, int k); -static void de4x5_dbg_media(struct net_device *dev); -static void de4x5_dbg_srom(struct de4x5_srom *p); -static void de4x5_dbg_rx(struct sk_buff *skb, int len); -static int de4x5_strncmp(char *a, char *b, int n); -static int dc21041_infoleaf(struct net_device *dev); -static int dc21140_infoleaf(struct net_device *dev); -static int dc21142_infoleaf(struct net_device *dev); -static int dc21143_infoleaf(struct net_device *dev); -static int type0_infoblock(struct net_device *dev, u_char count, u_char *p); -static int type1_infoblock(struct net_device *dev, u_char count, u_char *p); -static int type2_infoblock(struct net_device *dev, u_char count, u_char *p); -static int type3_infoblock(struct net_device *dev, u_char count, u_char *p); -static int type4_infoblock(struct net_device *dev, u_char count, u_char *p); -static int type5_infoblock(struct net_device *dev, u_char count, u_char *p); -static int compact_infoblock(struct net_device *dev, u_char count, u_char *p); - -#ifdef MODULE -int init_module(void); -void cleanup_module(void); -static struct net_device *unlink_modules(struct net_device *p); -static struct net_device *insert_device(struct net_device *dev, u_long iobase, - int (*init)(struct net_device *)); -static int count_adapters(void); -static int loading_module = 1; -MODULE_PARM(de4x5_debug, "i"); -MODULE_PARM(dec_only, "i"); -MODULE_PARM(args, "s"); -MODULE_PARM_DESC(de4x5_debug, "de4x5 debug mask"); -MODULE_PARM_DESC(dec_only, "de4x5 probe only for Digital boards (0-1)"); -MODULE_PARM_DESC(args, "de4x5 full duplex and media type settings; see de4x5.c for details"); -MODULE_LICENSE("GPL"); - -# else -static int loading_module; -#endif /* MODULE */ - -static char name[DE4X5_NAME_LENGTH + 1]; -#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) -static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST; -static int lastEISA; -# ifdef DE4X5_FORCE_EISA /* Force an EISA bus probe or not */ -static int forceEISA = 1; -# else -static int forceEISA; -# endif -#endif -static int num_de4x5s; -static int cfrv, useSROM; -static int lastPCI = -1; -static struct net_device *lastModule; -static struct pci_dev *pdev; - -/* -** List the SROM infoleaf functions and chipsets -*/ -struct InfoLeaf { - int chipset; - int (*fn)(struct net_device *); -}; -static struct InfoLeaf infoleaf_array[] = { - {DC21041, dc21041_infoleaf}, - {DC21140, dc21140_infoleaf}, - {DC21142, dc21142_infoleaf}, - {DC21143, dc21143_infoleaf} -}; -#define INFOLEAF_SIZE (sizeof(infoleaf_array)/(sizeof(int)+sizeof(int *))) - -/* -** List the SROM info block functions -*/ -static int (*dc_infoblock[])(struct net_device *dev, u_char, u_char *) = { - type0_infoblock, - type1_infoblock, - type2_infoblock, - type3_infoblock, - type4_infoblock, - type5_infoblock, - compact_infoblock -}; - -#define COMPACT (sizeof(dc_infoblock)/sizeof(int *) - 1) - -/* -** Miscellaneous defines... -*/ -#define RESET_DE4X5 {\ - int i;\ - i=inl(DE4X5_BMR);\ - mdelay(1);\ - outl(i | BMR_SWR, DE4X5_BMR);\ - mdelay(1);\ - outl(i, DE4X5_BMR);\ - mdelay(1);\ - for (i=0;i<5;i++) {inl(DE4X5_BMR); mdelay(1);}\ - mdelay(1);\ -} - -#define PHY_HARD_RESET {\ - outl(GEP_HRST, DE4X5_GEP); /* Hard RESET the PHY dev. */\ - mdelay(1); /* Assert for 1ms */\ - outl(0x00, DE4X5_GEP);\ - mdelay(2); /* Wait for 2ms */\ -} - - -/* -** Autoprobing in modules is allowed here. See the top of the file for -** more info. -*/ -int __init -de4x5_probe(struct net_device *dev) -{ - u_long iobase = dev->base_addr; - - pci_probe(dev, iobase); -#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) - if ((lastPCI == NO_MORE_PCI) && ((num_de4x5s == 0) || forceEISA)) { - eisa_probe(dev, iobase); - } -#endif - - return (dev->priv ? 0 : -ENODEV); -} - -static int __init -de4x5_hw_init(struct net_device *dev, u_long iobase, struct pci_dev *pdev) -{ - struct bus_type *lp = &bus; - int i, status=0; - char *tmp; - - /* Ensure we're not sleeping */ - if (lp->bus == EISA) { - outb(WAKEUP, PCI_CFPM); - } else { - pcibios_write_config_byte(lp->bus_num, lp->device << 3, - PCI_CFDA_PSM, WAKEUP); - } - mdelay(10); - - RESET_DE4X5; - - if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) { - return -ENXIO; /* Hardware could not reset */ - } - - /* - ** Now find out what kind of DC21040/DC21041/DC21140 board we have. - */ - useSROM = FALSE; - if (lp->bus == PCI) { - PCI_signature(name, lp); - } else { - EISA_signature(name, EISA_ID0); - } - - if (*name == '\0') { /* Not found a board signature */ - return -ENXIO; - } - - dev->base_addr = iobase; - if (lp->bus == EISA) { - printk("%s: %s at 0x%04lx (EISA slot %ld)", - dev->name, name, iobase, ((iobase>>12)&0x0f)); - } else { /* PCI port address */ - printk("%s: %s at 0x%04lx (PCI bus %d, device %d)", dev->name, name, - iobase, lp->bus_num, lp->device); - } - - printk(", h/w address "); - status = get_hw_addr(dev); - for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */ - printk("%2.2x:", dev->dev_addr[i]); - } - printk("%2.2x,\n", dev->dev_addr[i]); - - if (status != 0) { - printk(" which has an Ethernet PROM CRC error.\n"); - return -ENXIO; - } else { - struct de4x5_private *lp; - - /* - ** Reserve a section of kernel memory for the adapter - ** private area and the TX/RX descriptor rings. - */ - dev->priv = (void *) kmalloc(sizeof(struct de4x5_private) + ALIGN, - GFP_KERNEL); - if (dev->priv == NULL) { - return -ENOMEM; - } - - /* - ** Align to a longword boundary - */ - tmp = dev->priv; - dev->priv = (void *)(((u_long)dev->priv + ALIGN) & ~ALIGN); - lp = (struct de4x5_private *)dev->priv; - memset(dev->priv, 0, sizeof(struct de4x5_private)); - lp->bus = bus.bus; - lp->bus_num = bus.bus_num; - lp->device = bus.device; - lp->chipset = bus.chipset; - lp->cache.priv = tmp; - lp->cache.gepc = GEP_INIT; - lp->asBit = GEP_SLNK; - lp->asPolarity = GEP_SLNK; - lp->asBitValid = TRUE; - lp->timeout = -1; - lp->useSROM = useSROM; - lp->pdev = pdev; - memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom)); - lp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; - de4x5_parse_params(dev); - - /* - ** Choose correct autosensing in case someone messed up - */ - lp->autosense = lp->params.autosense; - if (lp->chipset != DC21140) { - if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) { - lp->params.autosense = TP; - } - if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) { - lp->params.autosense = BNC; - } - } - lp->fdx = lp->params.fdx; - sprintf(lp->adapter_name,"%s (%s)", name, dev->name); - - lp->dma_size = (NUM_RX_DESC + NUM_TX_DESC) * sizeof(struct de4x5_desc); -#if defined(__alpha__) || defined(__powerpc__) || defined(__sparc_v9__) || defined(DE4X5_DO_MEMCPY) - lp->dma_size += RX_BUFF_SZ * NUM_RX_DESC + ALIGN; -#endif - lp->rx_ring = pci_alloc_consistent(pdev, lp->dma_size, &lp->dma_rings); - if (lp->rx_ring == NULL) { - kfree(lp->cache.priv); - lp->cache.priv = NULL; - return -ENOMEM; - } - - lp->tx_ring = lp->rx_ring + NUM_RX_DESC; - - /* - ** Set up the RX descriptor ring (Intels) - ** Allocate contiguous receive buffers, long word aligned (Alphas) - */ -#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) - for (i=0; irx_ring[i].status = 0; - lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); - lp->rx_ring[i].buf = 0; - lp->rx_ring[i].next = 0; - lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */ - } - -#else - { - dma_addr_t dma_rx_bufs; - - dma_rx_bufs = lp->dma_rings + (NUM_RX_DESC + NUM_TX_DESC) - * sizeof(struct de4x5_desc); - dma_rx_bufs = (dma_rx_bufs + ALIGN) & ~ALIGN; - lp->rx_bufs = (char *)(((long)(lp->rx_ring + NUM_RX_DESC - + NUM_TX_DESC) + ALIGN) & ~ALIGN); - for (i=0; irx_ring[i].status = 0; - lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); - lp->rx_ring[i].buf = - cpu_to_le32(dma_rx_bufs+i*RX_BUFF_SZ); - lp->rx_ring[i].next = 0; - lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */ - } - - } -#endif - - barrier(); - - request_region(iobase, (lp->bus == PCI ? DE4X5_PCI_TOTAL_SIZE : - DE4X5_EISA_TOTAL_SIZE), - lp->adapter_name); - - lp->rxRingSize = NUM_RX_DESC; - lp->txRingSize = NUM_TX_DESC; - - /* Write the end of list marker to the descriptor lists */ - lp->rx_ring[lp->rxRingSize - 1].des1 |= cpu_to_le32(RD_RER); - lp->tx_ring[lp->txRingSize - 1].des1 |= cpu_to_le32(TD_TER); - - /* Tell the adapter where the TX/RX rings are located. */ - outl(lp->dma_rings, DE4X5_RRBA); - outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), - DE4X5_TRBA); - - /* Initialise the IRQ mask and Enable/Disable */ - lp->irq_mask = IMR_RIM | IMR_TIM | IMR_TUM | IMR_UNM; - lp->irq_en = IMR_NIM | IMR_AIM; - - /* Create a loopback packet frame for later media probing */ - create_packet(dev, lp->frame, sizeof(lp->frame)); - - /* Check if the RX overflow bug needs testing for */ - i = cfrv & 0x000000fe; - if ((lp->chipset == DC21140) && (i == 0x20)) { - lp->rx_ovf = 1; - } - - /* Initialise the SROM pointers if possible */ - if (lp->useSROM) { - lp->state = INITIALISED; - if (srom_infoleaf_info(dev)) { - return -ENXIO; - } - srom_init(dev); - } - - lp->state = CLOSED; - - /* - ** Check for an MII interface - */ - if ((lp->chipset != DC21040) && (lp->chipset != DC21041)) { - mii_get_phy(dev); - } - -#ifndef __sparc_v9__ - printk(" and requires IRQ%d (provided by %s).\n", dev->irq, -#else - printk(" and requires IRQ%x (provided by %s).\n", dev->irq, -#endif - ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG")); - } - - if (de4x5_debug & DEBUG_VERSION) { - printk(version); - } - - /* The DE4X5-specific entries in the device structure. */ - dev->open = &de4x5_open; - dev->hard_start_xmit = &de4x5_queue_pkt; - dev->stop = &de4x5_close; - dev->get_stats = &de4x5_get_stats; - dev->set_multicast_list = &set_multicast_list; - dev->do_ioctl = &de4x5_ioctl; - - dev->mem_start = 0; - - /* Fill in the generic fields of the device structure. */ - ether_setup(dev); - - /* Let the adapter sleep to save power */ - yawn(dev, SLEEP); - - return status; -} - - -static int -de4x5_open(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int i, status = 0; - s32 omr; - - /* Allocate the RX buffers */ - for (i=0; irxRingSize; i++) { - if (de4x5_alloc_rx_buff(dev, i, 0) == NULL) { - de4x5_free_rx_buffs(dev); - return -EAGAIN; - } - } - - /* - ** Wake up the adapter - */ - yawn(dev, WAKEUP); - - /* - ** Re-initialize the DE4X5... - */ - status = de4x5_init(dev); - lp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; - lp->state = OPEN; - de4x5_dbg_open(dev); - - if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ, - lp->adapter_name, dev)) { - printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq); - if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ, - lp->adapter_name, dev)) { - printk("\n Cannot get IRQ- reconfigure your hardware.\n"); - disable_ast(dev); - de4x5_free_rx_buffs(dev); - de4x5_free_tx_buffs(dev); - yawn(dev, SLEEP); - lp->state = CLOSED; - return -EAGAIN; - } else { - printk("\n Succeeded, but you should reconfigure your hardware to avoid this.\n"); - printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n"); - } - } - - lp->interrupt = UNMASK_INTERRUPTS; - dev->trans_start = jiffies; - - START_DE4X5; - - de4x5_setup_intr(dev); - - if (de4x5_debug & DEBUG_OPEN) { - printk("\tsts: 0x%08x\n", inl(DE4X5_STS)); - printk("\tbmr: 0x%08x\n", inl(DE4X5_BMR)); - printk("\timr: 0x%08x\n", inl(DE4X5_IMR)); - printk("\tomr: 0x%08x\n", inl(DE4X5_OMR)); - printk("\tsisr: 0x%08x\n", inl(DE4X5_SISR)); - printk("\tsicr: 0x%08x\n", inl(DE4X5_SICR)); - printk("\tstrr: 0x%08x\n", inl(DE4X5_STRR)); - printk("\tsigr: 0x%08x\n", inl(DE4X5_SIGR)); - } - - MOD_INC_USE_COUNT; - - return status; -} - -/* -** Initialize the DE4X5 operating conditions. NB: a chip problem with the -** DC21140 requires using perfect filtering mode for that chip. Since I can't -** see why I'd want > 14 multicast addresses, I have changed all chips to use -** the perfect filtering mode. Keep the DMA burst length at 8: there seems -** to be data corruption problems if it is larger (UDP errors seen from a -** ttcp source). -*/ -static int -de4x5_init(struct net_device *dev) -{ - /* Lock out other processes whilst setting up the hardware */ - netif_stop_queue(dev); - - de4x5_sw_reset(dev); - - /* Autoconfigure the connected port */ - autoconf_media(dev); - - return 0; -} - -static int -de4x5_sw_reset(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int i, j, status = 0; - s32 bmr, omr; - - /* Select the MII or SRL port now and RESET the MAC */ - if (!lp->useSROM) { - if (lp->phy[lp->active].id != 0) { - lp->infoblock_csr6 = OMR_SDP | OMR_PS | OMR_HBD; - } else { - lp->infoblock_csr6 = OMR_SDP | OMR_TTM; - } - de4x5_switch_mac_port(dev); - } - - /* - ** Set the programmable burst length to 8 longwords for all the DC21140 - ** Fasternet chips and 4 longwords for all others: DMA errors result - ** without these values. Cache align 16 long. - */ - bmr = (lp->chipset==DC21140 ? PBL_8 : PBL_4) | DESC_SKIP_LEN | CACHE_ALIGN; - bmr |= ((lp->chipset & ~0x00ff)==DC2114x ? BMR_RML : 0); - outl(bmr, DE4X5_BMR); - - omr = inl(DE4X5_OMR) & ~OMR_PR; /* Turn off promiscuous mode */ - if (lp->chipset == DC21140) { - omr |= (OMR_SDP | OMR_SB); - } - lp->setup_f = PERFECT; - outl(lp->dma_rings, DE4X5_RRBA); - outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), - DE4X5_TRBA); - - lp->rx_new = lp->rx_old = 0; - lp->tx_new = lp->tx_old = 0; - - for (i = 0; i < lp->rxRingSize; i++) { - lp->rx_ring[i].status = cpu_to_le32(R_OWN); - } - - for (i = 0; i < lp->txRingSize; i++) { - lp->tx_ring[i].status = cpu_to_le32(0); - } - - barrier(); - - /* Build the setup frame depending on filtering mode */ - SetMulticastFilter(dev); - - load_packet(dev, lp->setup_frame, PERFECT_F|TD_SET|SETUP_FRAME_LEN, (struct sk_buff *)1); - outl(omr|OMR_ST, DE4X5_OMR); - - /* Poll for setup frame completion (adapter interrupts are disabled now) */ - sti(); /* Ensure timer interrupts */ - for (j=0, i=0;(i<500) && (j==0);i++) { /* Upto 500ms delay */ - mdelay(1); - if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1; - } - outl(omr, DE4X5_OMR); /* Stop everything! */ - - if (j == 0) { - printk("%s: Setup frame timed out, status %08x\n", dev->name, - inl(DE4X5_STS)); - status = -EIO; - } - - lp->tx_new = (++lp->tx_new) % lp->txRingSize; - lp->tx_old = lp->tx_new; - - return status; -} - -/* -** Writes a socket buffer address to the next available transmit descriptor. -*/ -static int -de4x5_queue_pkt(struct sk_buff *skb, struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int status = 0; - u_long flags = 0; - - netif_stop_queue(dev); - if (lp->tx_enable == NO) { /* Cannot send for now */ - return -1; - } - - /* - ** Clean out the TX ring asynchronously to interrupts - sometimes the - ** interrupts are lost by delayed descriptor status updates relative to - ** the irq assertion, especially with a busy PCI bus. - */ - spin_lock_irqsave(&lp->lock, flags); - de4x5_tx(dev); - spin_unlock_irqrestore(&lp->lock, flags); - - /* Test if cache is already locked - requeue skb if so */ - if (test_and_set_bit(0, (void *)&lp->cache.lock) && !lp->interrupt) - return -1; - - /* Transmit descriptor ring full or stale skb */ - if (netif_queue_stopped(dev) || (u_long) lp->tx_skb[lp->tx_new] > 1) { - if (lp->interrupt) { - de4x5_putb_cache(dev, skb); /* Requeue the buffer */ - } else { - de4x5_put_cache(dev, skb); - } - if (de4x5_debug & DEBUG_TX) { - printk("%s: transmit busy, lost media or stale skb found:\n STS:%08x\n tbusy:%d\n IMR:%08x\n OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), netif_queue_stopped(dev), inl(DE4X5_IMR), inl(DE4X5_OMR), ((u_long) lp->tx_skb[lp->tx_new] > 1) ? "YES" : "NO"); - } - } else if (skb->len > 0) { - /* If we already have stuff queued locally, use that first */ - if (lp->cache.skb && !lp->interrupt) { - de4x5_put_cache(dev, skb); - skb = de4x5_get_cache(dev); - } - - while (skb && !netif_queue_stopped(dev) && - (u_long) lp->tx_skb[lp->tx_new] <= 1) { - spin_lock_irqsave(&lp->lock, flags); - netif_stop_queue(dev); - load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb); - lp->stats.tx_bytes += skb->len; - outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */ - - lp->tx_new = (++lp->tx_new) % lp->txRingSize; - dev->trans_start = jiffies; - - if (TX_BUFFS_AVAIL) { - netif_start_queue(dev); /* Another pkt may be queued */ - } - skb = de4x5_get_cache(dev); - spin_unlock_irqrestore(&lp->lock, flags); - } - if (skb) de4x5_putb_cache(dev, skb); - } - - lp->cache.lock = 0; - - return status; -} - -/* -** The DE4X5 interrupt handler. -** -** I/O Read/Writes through intermediate PCI bridges are never 'posted', -** so that the asserted interrupt always has some real data to work with - -** if these I/O accesses are ever changed to memory accesses, ensure the -** STS write is read immediately to complete the transaction if the adapter -** is not on bus 0. Lost interrupts can still occur when the PCI bus load -** is high and descriptor status bits cannot be set before the associated -** interrupt is asserted and this routine entered. -*/ -static void -de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct de4x5_private *lp; - s32 imr, omr, sts, limit; - u_long iobase; - - if (dev == NULL) { - printk ("de4x5_interrupt(): irq %d for unknown device.\n", irq); - return; - } - lp = (struct de4x5_private *)dev->priv; - spin_lock(&lp->lock); - iobase = dev->base_addr; - - DISABLE_IRQs; /* Ensure non re-entrancy */ - - if (test_and_set_bit(MASK_INTERRUPTS, (void*) &lp->interrupt)) - printk("%s: Re-entering the interrupt handler.\n", dev->name); - - synchronize_irq(); - - for (limit=0; limit<8; limit++) { - sts = inl(DE4X5_STS); /* Read IRQ status */ - outl(sts, DE4X5_STS); /* Reset the board interrupts */ - - if (!(sts & lp->irq_mask)) break;/* All done */ - - if (sts & (STS_RI | STS_RU)) /* Rx interrupt (packet[s] arrived) */ - de4x5_rx(dev); - - if (sts & (STS_TI | STS_TU)) /* Tx interrupt (packet sent) */ - de4x5_tx(dev); - - if (sts & STS_LNF) { /* TP Link has failed */ - lp->irq_mask &= ~IMR_LFM; - } - - if (sts & STS_UNF) { /* Transmit underrun */ - de4x5_txur(dev); - } - - if (sts & STS_SE) { /* Bus Error */ - STOP_DE4X5; - printk("%s: Fatal bus error occurred, sts=%#8x, device stopped.\n", - dev->name, sts); - spin_unlock(&lp->lock); - return; - } - } - - /* Load the TX ring with any locally stored packets */ - if (!test_and_set_bit(0, (void *)&lp->cache.lock)) { - while (lp->cache.skb && !netif_queue_stopped(dev) && lp->tx_enable) { - de4x5_queue_pkt(de4x5_get_cache(dev), dev); - } - lp->cache.lock = 0; - } - - lp->interrupt = UNMASK_INTERRUPTS; - ENABLE_IRQs; - spin_unlock(&lp->lock); - - return; -} - -static int -de4x5_rx(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int entry; - s32 status; - - for (entry=lp->rx_new; (s32)le32_to_cpu(lp->rx_ring[entry].status)>=0; - entry=lp->rx_new) { - status = (s32)le32_to_cpu(lp->rx_ring[entry].status); - - if (lp->rx_ovf) { - if (inl(DE4X5_MFC) & MFC_FOCM) { - de4x5_rx_ovfc(dev); - break; - } - } - - if (status & RD_FS) { /* Remember the start of frame */ - lp->rx_old = entry; - } - - if (status & RD_LS) { /* Valid frame status */ - if (lp->tx_enable) lp->linkOK++; - if (status & RD_ES) { /* There was an error. */ - lp->stats.rx_errors++; /* Update the error stats. */ - if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++; - if (status & RD_CE) lp->stats.rx_crc_errors++; - if (status & RD_OF) lp->stats.rx_fifo_errors++; - if (status & RD_TL) lp->stats.rx_length_errors++; - if (status & RD_RF) lp->pktStats.rx_runt_frames++; - if (status & RD_CS) lp->pktStats.rx_collision++; - if (status & RD_DB) lp->pktStats.rx_dribble++; - if (status & RD_OF) lp->pktStats.rx_overflow++; - } else { /* A valid frame received */ - struct sk_buff *skb; - short pkt_len = (short)(le32_to_cpu(lp->rx_ring[entry].status) - >> 16) - 4; - - if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) { - printk("%s: Insufficient memory; nuking packet.\n", - dev->name); - lp->stats.rx_dropped++; - } else { - de4x5_dbg_rx(skb, pkt_len); - - /* Push up the protocol stack */ - skb->protocol=eth_type_trans(skb,dev); - de4x5_local_stats(dev, skb->data, pkt_len); - netif_rx(skb); - - /* Update stats */ - dev->last_rx = jiffies; - lp->stats.rx_packets++; - lp->stats.rx_bytes += pkt_len; - } - } - - /* Change buffer ownership for this frame, back to the adapter */ - for (;lp->rx_old!=entry;lp->rx_old=(++lp->rx_old)%lp->rxRingSize) { - lp->rx_ring[lp->rx_old].status = cpu_to_le32(R_OWN); - barrier(); - } - lp->rx_ring[entry].status = cpu_to_le32(R_OWN); - barrier(); - } - - /* - ** Update entry information - */ - lp->rx_new = (++lp->rx_new) % lp->rxRingSize; - } - - return 0; -} - -static inline void -de4x5_free_tx_buff(struct de4x5_private *lp, int entry) -{ - pci_unmap_single(lp->pdev, le32_to_cpu(lp->tx_ring[entry].buf), - le32_to_cpu(lp->tx_ring[entry].des1) & TD_TBS1, - PCI_DMA_TODEVICE); - if ((u_long) lp->tx_skb[entry] > 1) - dev_kfree_skb_irq(lp->tx_skb[entry]); - lp->tx_skb[entry] = NULL; -} - -/* -** Buffer sent - check for TX buffer errors. -*/ -static int -de4x5_tx(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int entry; - s32 status; - - for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) { - status = (s32)le32_to_cpu(lp->tx_ring[entry].status); - if (status < 0) { /* Buffer not sent yet */ - break; - } else if (status != 0x7fffffff) { /* Not setup frame */ - if (status & TD_ES) { /* An error happened */ - lp->stats.tx_errors++; - if (status & TD_NC) lp->stats.tx_carrier_errors++; - if (status & TD_LC) lp->stats.tx_window_errors++; - if (status & TD_UF) lp->stats.tx_fifo_errors++; - if (status & TD_EC) lp->pktStats.excessive_collisions++; - if (status & TD_DE) lp->stats.tx_aborted_errors++; - - if (TX_PKT_PENDING) { - outl(POLL_DEMAND, DE4X5_TPD);/* Restart a stalled TX */ - } - } else { /* Packet sent */ - lp->stats.tx_packets++; - if (lp->tx_enable) lp->linkOK++; - } - /* Update the collision counter */ - lp->stats.collisions += ((status & TD_EC) ? 16 : - ((status & TD_CC) >> 3)); - - /* Free the buffer. */ - if (lp->tx_skb[entry] != NULL) - de4x5_free_tx_buff(lp, entry); - } - - /* Update all the pointers */ - lp->tx_old = (++lp->tx_old) % lp->txRingSize; - } - - /* Any resources available? */ - if (TX_BUFFS_AVAIL && netif_queue_stopped(dev)) { - if (lp->interrupt) - netif_wake_queue(dev); - else - netif_start_queue(dev); - } - - return 0; -} - -static int -de4x5_ast(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int next_tick = DE4X5_AUTOSENSE_MS; - - disable_ast(dev); - - if (lp->useSROM) { - next_tick = srom_autoconf(dev); - } else if (lp->chipset == DC21140) { - next_tick = dc21140m_autoconf(dev); - } else if (lp->chipset == DC21041) { - next_tick = dc21041_autoconf(dev); - } else if (lp->chipset == DC21040) { - next_tick = dc21040_autoconf(dev); - } - lp->linkOK = 0; - enable_ast(dev, next_tick); - - return 0; -} - -static int -de4x5_txur(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int omr; - - omr = inl(DE4X5_OMR); - if (!(omr & OMR_SF) || (lp->chipset==DC21041) || (lp->chipset==DC21040)) { - omr &= ~(OMR_ST|OMR_SR); - outl(omr, DE4X5_OMR); - while (inl(DE4X5_STS) & STS_TS); - if ((omr & OMR_TR) < OMR_TR) { - omr += 0x4000; - } else { - omr |= OMR_SF; - } - outl(omr | OMR_ST | OMR_SR, DE4X5_OMR); - } - - return 0; -} - -static int -de4x5_rx_ovfc(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int omr; - - omr = inl(DE4X5_OMR); - outl(omr & ~OMR_SR, DE4X5_OMR); - while (inl(DE4X5_STS) & STS_RS); - - for (; (s32)le32_to_cpu(lp->rx_ring[lp->rx_new].status)>=0;) { - lp->rx_ring[lp->rx_new].status = cpu_to_le32(R_OWN); - lp->rx_new = (++lp->rx_new % lp->rxRingSize); - } - - outl(omr, DE4X5_OMR); - - return 0; -} - -static int -de4x5_close(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 imr, omr; - - disable_ast(dev); - - netif_stop_queue(dev); - - if (de4x5_debug & DEBUG_CLOSE) { - printk("%s: Shutting down ethercard, status was %8.8x.\n", - dev->name, inl(DE4X5_STS)); - } - - /* - ** We stop the DE4X5 here... mask interrupts and stop TX & RX - */ - DISABLE_IRQs; - STOP_DE4X5; - - /* Free the associated irq */ - free_irq(dev->irq, dev); - lp->state = CLOSED; - - /* Free any socket buffers */ - de4x5_free_rx_buffs(dev); - de4x5_free_tx_buffs(dev); - - MOD_DEC_USE_COUNT; - - /* Put the adapter to sleep to save power */ - yawn(dev, SLEEP); - - return 0; -} - -static struct net_device_stats * -de4x5_get_stats(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - lp->stats.rx_missed_errors = (int)(inl(DE4X5_MFC) & (MFC_OVFL | MFC_CNTR)); - - return &lp->stats; -} - -static void -de4x5_local_stats(struct net_device *dev, char *buf, int pkt_len) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int i; - - for (i=1; ipktStats.bins[i]++; - i = DE4X5_PKT_STAT_SZ; - } - } - if (buf[0] & 0x01) { /* Multicast/Broadcast */ - if ((*(s32 *)&buf[0] == -1) && (*(s16 *)&buf[4] == -1)) { - lp->pktStats.broadcast++; - } else { - lp->pktStats.multicast++; - } - } else if ((*(s32 *)&buf[0] == *(s32 *)&dev->dev_addr[0]) && - (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) { - lp->pktStats.unicast++; - } - - lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */ - if (lp->pktStats.bins[0] == 0) { /* Reset counters */ - memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats)); - } - - return; -} - -/* -** Removes the TD_IC flag from previous descriptor to improve TX performance. -** If the flag is changed on a descriptor that is being read by the hardware, -** I assume PCI transaction ordering will mean you are either successful or -** just miss asserting the change to the hardware. Anyway you're messing with -** a descriptor you don't own, but this shouldn't kill the chip provided -** the descriptor register is read only to the hardware. -*/ -static void -load_packet(struct net_device *dev, char *buf, u32 flags, struct sk_buff *skb) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int entry = (lp->tx_new ? lp->tx_new-1 : lp->txRingSize-1); - dma_addr_t buf_dma = pci_map_single(lp->pdev, buf, flags & TD_TBS1, PCI_DMA_TODEVICE); - - lp->tx_ring[lp->tx_new].buf = cpu_to_le32(buf_dma); - lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER); - lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags); - lp->tx_skb[lp->tx_new] = skb; - lp->tx_ring[entry].des1 &= cpu_to_le32(~TD_IC); - barrier(); - - lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN); - barrier(); -} - -/* -** Set or clear the multicast filter for this adaptor. -*/ -static void -set_multicast_list(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - /* First, double check that the adapter is open */ - if (lp->state == OPEN) { - if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ - u32 omr; - omr = inl(DE4X5_OMR); - omr |= OMR_PR; - outl(omr, DE4X5_OMR); - } else { - SetMulticastFilter(dev); - load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | - SETUP_FRAME_LEN, (struct sk_buff *)1); - - lp->tx_new = (++lp->tx_new) % lp->txRingSize; - outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */ - dev->trans_start = jiffies; - } - } - - return; -} - -/* -** Calculate the hash code and update the logical address filter -** from a list of ethernet multicast addresses. -** Little endian crc one liner from Matt Thomas, DEC. -*/ -static void -SetMulticastFilter(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - struct dev_mc_list *dmi=dev->mc_list; - u_long iobase = dev->base_addr; - int i, j, bit, byte; - u16 hashcode; - u32 omr, crc; - char *pa; - unsigned char *addrs; - - omr = inl(DE4X5_OMR); - omr &= ~(OMR_PR | OMR_PM); - pa = build_setup_frame(dev, ALL); /* Build the basic frame */ - - if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 14)) { - omr |= OMR_PM; /* Pass all multicasts */ - } else if (lp->setup_f == HASH_PERF) { /* Hash Filtering */ - for (i=0;imc_count;i++) { /* for each address in the list */ - addrs=dmi->dmi_addr; - dmi=dmi->next; - if ((*addrs & 0x01) == 1) { /* multicast address? */ - crc = ether_crc_le(ETH_ALEN, addrs); - hashcode = crc & HASH_BITS; /* hashcode is 9 LSb of CRC */ - - byte = hashcode >> 3; /* bit[3-8] -> byte in filter */ - bit = 1 << (hashcode & 0x07);/* bit[0-2] -> bit in byte */ - - byte <<= 1; /* calc offset into setup frame */ - if (byte & 0x02) { - byte -= 1; - } - lp->setup_frame[byte] |= bit; - } - } - } else { /* Perfect filtering */ - for (j=0; jmc_count; j++) { - addrs=dmi->dmi_addr; - dmi=dmi->next; - for (i=0; ibus = EISA; - - if (ioaddr == 0) { /* Autoprobing */ - iobase = EISA_SLOT_INC; /* Get the first slot address */ - i = 1; - maxSlots = MAX_EISA_SLOTS; - } else { /* Probe a specific location */ - iobase = ioaddr; - i = (ioaddr >> 12); - maxSlots = i + 1; - } - - for (status = -ENODEV; (i> 8) & 0x00ffff00; - vendor = (u_short) cfid; - - /* Read the EISA Configuration Registers */ - irq = inb(EISA_REG0); - irq = de4x5_irq[(irq >> 1) & 0x03]; - - if (is_DC2114x) { - device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143); - } - lp->chipset = device; - - /* Write the PCI Configuration Registers */ - outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS); - outl(0x00006000, PCI_CFLT); - outl(iobase, PCI_CBIO); - - DevicePresent(EISA_APROM); - - dev->irq = irq; - if ((status = de4x5_hw_init(dev, iobase, NULL)) == 0) { - num_de4x5s++; - if (loading_module) link_modules(lastModule, dev); - lastEISA = i; - return; - } - } - - if (ioaddr == 0) lastEISA = i; - - return; -} -#endif /* !(__sparc_v9__) && !(__powerpc__) && !defined(__alpha__) */ - -/* -** PCI bus I/O device probe -** NB: PCI I/O accesses and Bus Mastering are enabled by the PCI BIOS, not -** the driver. Some PCI BIOS's, pre V2.1, need the slot + features to be -** enabled by the user first in the set up utility. Hence we just check for -** enabled features and silently ignore the card if they're not. -** -** STOP PRESS: Some BIOS's __require__ the driver to enable the bus mastering -** bit. Here, check for I/O accesses and then set BM. If you put the card in -** a non BM slot, you're on your own (and complain to the PC vendor that your -** PC doesn't conform to the PCI standard)! -** -** This function is only compatible with the *latest* 2.1.x kernels. For 2.0.x -** kernels use the V0.535[n] drivers. -*/ -#define PCI_LAST_DEV 32 - -static void __init -pci_probe(struct net_device *dev, u_long ioaddr) -{ - u_char pb, pbus, dev_num, dnum, timer; - u_short vendor, index, status; - u_int irq = 0, device, class = DE4X5_CLASS_CODE; - u_long iobase = 0; /* Clear upper 32 bits in Alphas */ - struct bus_type *lp = &bus; - - if (lastPCI == NO_MORE_PCI) return; - - if (!pcibios_present()) { - lastPCI = NO_MORE_PCI; - return; /* No PCI bus in this machine! */ - } - - lp->bus = PCI; - lp->bus_num = 0; - - if ((ioaddr < 0x1000) && loading_module) { - pbus = (u_short)(ioaddr >> 8); - dnum = (u_short)(ioaddr & 0xff); - } else { - pbus = 0; - dnum = 0; - } - - for (index=lastPCI+1;(pdev = pci_find_class(class, pdev))!=NULL;index++) { - dev_num = PCI_SLOT(pdev->devfn); - pb = pdev->bus->number; - if ((pbus || dnum) && ((pbus != pb) || (dnum != dev_num))) continue; - - vendor = pdev->vendor; - device = pdev->device << 8; - if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue; - - /* Search for an SROM on this bus */ - if (lp->bus_num != pb) { - lp->bus_num = pb; - srom_search(pdev); - } - - /* Get the chip configuration revision register */ - pcibios_read_config_dword(pb, pdev->devfn, PCI_REVISION_ID, &cfrv); - - /* Set the device number information */ - lp->device = dev_num; - lp->bus_num = pb; - - /* Set the chipset information */ - if (is_DC2114x) { - device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143); - } - lp->chipset = device; - - /* Get the board I/O address (64 bits on sparc64) */ - iobase = pci_resource_start(pdev, 0); - - /* Fetch the IRQ to be used */ - irq = pdev->irq; - if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; - - /* Check if I/O accesses and Bus Mastering are enabled */ - pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status); -#ifdef __powerpc__ - if (!(status & PCI_COMMAND_IO)) { - status |= PCI_COMMAND_IO; - pcibios_write_config_word(pb, pdev->devfn, PCI_COMMAND, status); - pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status); - } -#endif /* __powerpc__ */ - if (!(status & PCI_COMMAND_IO)) continue; - - if (!(status & PCI_COMMAND_MASTER)) { - status |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pb, pdev->devfn, PCI_COMMAND, status); - pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status); - } - if (!(status & PCI_COMMAND_MASTER)) continue; - - /* Check the latency timer for values >= 0x60 */ - pcibios_read_config_byte(pb, pdev->devfn, PCI_LATENCY_TIMER, &timer); - if (timer < 0x60) { - pcibios_write_config_byte(pb, pdev->devfn, PCI_LATENCY_TIMER, 0x60); - } - - DevicePresent(DE4X5_APROM); - if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) { - dev->irq = irq; - if ((status = de4x5_hw_init(dev, iobase, pdev)) == 0) { - num_de4x5s++; - lastPCI = index; - if (loading_module) link_modules(lastModule, dev); - return; - } - } else if (ioaddr != 0) { - printk("%s: region already allocated at 0x%04lx.\n", dev->name, - iobase); - } - } - - lastPCI = NO_MORE_PCI; - - return; -} - -/* -** This function searches the current bus (which is >0) for a DECchip with an -** SROM, so that in multiport cards that have one SROM shared between multiple -** DECchips, we can find the base SROM irrespective of the BIOS scan direction. -** For single port cards this is a time waster... -*/ -static void __init -srom_search(struct pci_dev *dev) -{ - u_char pb; - u_short vendor, status; - u_int irq = 0, device; - u_long iobase = 0; /* Clear upper 32 bits in Alphas */ - int i, j; - struct bus_type *lp = &bus; - struct list_head *walk = &dev->bus_list; - - for (walk = walk->next; walk != &dev->bus_list; walk = walk->next) { - struct pci_dev *this_dev = pci_dev_b(walk); - - /* Skip the pci_bus list entry */ - if (list_entry(walk, struct pci_bus, devices) == dev->bus) continue; - - vendor = this_dev->vendor; - device = this_dev->device << 8; - if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue; - - /* Get the chip configuration revision register */ - pb = this_dev->bus->number; - pcibios_read_config_dword(pb, this_dev->devfn, PCI_REVISION_ID, &cfrv); - - /* Set the device number information */ - lp->device = PCI_SLOT(this_dev->devfn); - lp->bus_num = pb; - - /* Set the chipset information */ - if (is_DC2114x) { - device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143); - } - lp->chipset = device; - - /* Get the board I/O address (64 bits on sparc64) */ - iobase = pci_resource_start(this_dev, 0); - - /* Fetch the IRQ to be used */ - irq = this_dev->irq; - if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; - - /* Check if I/O accesses are enabled */ - pcibios_read_config_word(pb, this_dev->devfn, PCI_COMMAND, &status); - if (!(status & PCI_COMMAND_IO)) continue; - - /* Search for a valid SROM attached to this DECchip */ - DevicePresent(DE4X5_APROM); - for (j=0, i=0; isrom + SROM_HWADD + i); - } - if ((j != 0) && (j != 0x5fa)) { - last.chipset = device; - last.bus = pb; - last.irq = irq; - for (i=0; isrom + SROM_HWADD + i); - } - return; - } - } - - return; -} - -static void __init -link_modules(struct net_device *dev, struct net_device *tmp) -{ - struct net_device *p=dev; - - if (p) { - while (((struct de4x5_private *)(p->priv))->next_module) { - p = ((struct de4x5_private *)(p->priv))->next_module; - } - - if (dev != tmp) { - ((struct de4x5_private *)(p->priv))->next_module = tmp; - } else { - ((struct de4x5_private *)(p->priv))->next_module = NULL; - } - } - - return; -} - -/* -** Auto configure the media here rather than setting the port at compile -** time. This routine is called by de4x5_init() and when a loss of media is -** detected (excessive collisions, loss of carrier, no carrier or link fail -** [TP] or no recent receive activity) to check whether the user has been -** sneaky and changed the port on us. -*/ -static int -autoconf_media(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int next_tick = DE4X5_AUTOSENSE_MS; - - lp->linkOK = 0; - lp->c_media = AUTO; /* Bogus last media */ - disable_ast(dev); - inl(DE4X5_MFC); /* Zero the lost frames counter */ - lp->media = INIT; - lp->tcount = 0; - - if (lp->useSROM) { - next_tick = srom_autoconf(dev); - } else if (lp->chipset == DC21040) { - next_tick = dc21040_autoconf(dev); - } else if (lp->chipset == DC21041) { - next_tick = dc21041_autoconf(dev); - } else if (lp->chipset == DC21140) { - next_tick = dc21140m_autoconf(dev); - } - - enable_ast(dev, next_tick); - - return (lp->media); -} - -/* -** Autoconfigure the media when using the DC21040. AUI cannot be distinguished -** from BNC as the port has a jumper to set thick or thin wire. When set for -** BNC, the BNC port will indicate activity if it's not terminated correctly. -** The only way to test for that is to place a loopback packet onto the -** network and watch for errors. Since we're messing with the interrupt mask -** register, disable the board interrupts and do not allow any more packets to -** be queued to the hardware. Re-enable everything only when the media is -** found. -** I may have to "age out" locally queued packets so that the higher layer -** timeouts don't effectively duplicate packets on the network. -*/ -static int -dc21040_autoconf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int next_tick = DE4X5_AUTOSENSE_MS; - s32 imr; - - switch (lp->media) { - case INIT: - DISABLE_IRQs; - lp->tx_enable = NO; - lp->timeout = -1; - de4x5_save_skbs(dev); - if ((lp->autosense == AUTO) || (lp->autosense == TP)) { - lp->media = TP; - } else if ((lp->autosense == BNC) || (lp->autosense == AUI) || (lp->autosense == BNC_AUI)) { - lp->media = BNC_AUI; - } else if (lp->autosense == EXT_SIA) { - lp->media = EXT_SIA; - } else { - lp->media = NC; - } - lp->local_state = 0; - next_tick = dc21040_autoconf(dev); - break; - - case TP: - next_tick = dc21040_state(dev, 0x8f01, 0xffff, 0x0000, 3000, BNC_AUI, - TP_SUSPECT, test_tp); - break; - - case TP_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21040_autoconf); - break; - - case BNC: - case AUI: - case BNC_AUI: - next_tick = dc21040_state(dev, 0x8f09, 0x0705, 0x0006, 3000, EXT_SIA, - BNC_AUI_SUSPECT, ping_media); - break; - - case BNC_AUI_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, BNC_AUI, ping_media, dc21040_autoconf); - break; - - case EXT_SIA: - next_tick = dc21040_state(dev, 0x3041, 0x0000, 0x0006, 3000, - NC, EXT_SIA_SUSPECT, ping_media); - break; - - case EXT_SIA_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, EXT_SIA, ping_media, dc21040_autoconf); - break; - - case NC: - /* default to TP for all */ - reset_init_sia(dev, 0x8f01, 0xffff, 0x0000); - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; - } - lp->media = INIT; - lp->tx_enable = NO; - break; - } - - return next_tick; -} - -static int -dc21040_state(struct net_device *dev, int csr13, int csr14, int csr15, int timeout, - int next_state, int suspect_state, - int (*fn)(struct net_device *, int)) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int next_tick = DE4X5_AUTOSENSE_MS; - int linkBad; - - switch (lp->local_state) { - case 0: - reset_init_sia(dev, csr13, csr14, csr15); - lp->local_state++; - next_tick = 500; - break; - - case 1: - if (!lp->tx_enable) { - linkBad = fn(dev, timeout); - if (linkBad < 0) { - next_tick = linkBad & ~TIMER_CB; - } else { - if (linkBad && (lp->autosense == AUTO)) { - lp->local_state = 0; - lp->media = next_state; - } else { - de4x5_init_connection(dev); - } - } - } else if (!lp->linkOK && (lp->autosense == AUTO)) { - lp->media = suspect_state; - next_tick = 3000; - } - break; - } - - return next_tick; -} - -static int -de4x5_suspect_state(struct net_device *dev, int timeout, int prev_state, - int (*fn)(struct net_device *, int), - int (*asfn)(struct net_device *)) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int next_tick = DE4X5_AUTOSENSE_MS; - int linkBad; - - switch (lp->local_state) { - case 1: - if (lp->linkOK) { - lp->media = prev_state; - } else { - lp->local_state++; - next_tick = asfn(dev); - } - break; - - case 2: - linkBad = fn(dev, timeout); - if (linkBad < 0) { - next_tick = linkBad & ~TIMER_CB; - } else if (!linkBad) { - lp->local_state--; - lp->media = prev_state; - } else { - lp->media = INIT; - lp->tcount++; - } - } - - return next_tick; -} - -/* -** Autoconfigure the media when using the DC21041. AUI needs to be tested -** before BNC, because the BNC port will indicate activity if it's not -** terminated correctly. The only way to test for that is to place a loopback -** packet onto the network and watch for errors. Since we're messing with -** the interrupt mask register, disable the board interrupts and do not allow -** any more packets to be queued to the hardware. Re-enable everything only -** when the media is found. -*/ -static int -dc21041_autoconf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 sts, irqs, irq_mask, imr, omr; - int next_tick = DE4X5_AUTOSENSE_MS; - - switch (lp->media) { - case INIT: - DISABLE_IRQs; - lp->tx_enable = NO; - lp->timeout = -1; - de4x5_save_skbs(dev); /* Save non transmitted skb's */ - if ((lp->autosense == AUTO) || (lp->autosense == TP_NW)) { - lp->media = TP; /* On chip auto negotiation is broken */ - } else if (lp->autosense == TP) { - lp->media = TP; - } else if (lp->autosense == BNC) { - lp->media = BNC; - } else if (lp->autosense == AUI) { - lp->media = AUI; - } else { - lp->media = NC; - } - lp->local_state = 0; - next_tick = dc21041_autoconf(dev); - break; - - case TP_NW: - if (lp->timeout < 0) { - omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */ - outl(omr | OMR_FDX, DE4X5_OMR); - } - irqs = STS_LNF | STS_LNP; - irq_mask = IMR_LFM | IMR_LPM; - sts = test_media(dev, irqs, irq_mask, 0xef01, 0xffff, 0x0008, 2400); - if (sts < 0) { - next_tick = sts & ~TIMER_CB; - } else { - if (sts & STS_LNP) { - lp->media = ANS; - } else { - lp->media = AUI; - } - next_tick = dc21041_autoconf(dev); - } - break; - - case ANS: - if (!lp->tx_enable) { - irqs = STS_LNP; - irq_mask = IMR_LPM; - sts = test_ans(dev, irqs, irq_mask, 3000); - if (sts < 0) { - next_tick = sts & ~TIMER_CB; - } else { - if (!(sts & STS_LNP) && (lp->autosense == AUTO)) { - lp->media = TP; - next_tick = dc21041_autoconf(dev); - } else { - lp->local_state = 1; - de4x5_init_connection(dev); - } - } - } else if (!lp->linkOK && (lp->autosense == AUTO)) { - lp->media = ANS_SUSPECT; - next_tick = 3000; - } - break; - - case ANS_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, ANS, test_tp, dc21041_autoconf); - break; - - case TP: - if (!lp->tx_enable) { - if (lp->timeout < 0) { - omr = inl(DE4X5_OMR); /* Set up half duplex for TP */ - outl(omr & ~OMR_FDX, DE4X5_OMR); - } - irqs = STS_LNF | STS_LNP; - irq_mask = IMR_LFM | IMR_LPM; - sts = test_media(dev,irqs, irq_mask, 0xef01, 0xff3f, 0x0008, 2400); - if (sts < 0) { - next_tick = sts & ~TIMER_CB; - } else { - if (!(sts & STS_LNP) && (lp->autosense == AUTO)) { - if (inl(DE4X5_SISR) & SISR_NRA) { - lp->media = AUI; /* Non selected port activity */ - } else { - lp->media = BNC; - } - next_tick = dc21041_autoconf(dev); - } else { - lp->local_state = 1; - de4x5_init_connection(dev); - } - } - } else if (!lp->linkOK && (lp->autosense == AUTO)) { - lp->media = TP_SUSPECT; - next_tick = 3000; - } - break; - - case TP_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21041_autoconf); - break; - - case AUI: - if (!lp->tx_enable) { - if (lp->timeout < 0) { - omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ - outl(omr & ~OMR_FDX, DE4X5_OMR); - } - irqs = 0; - irq_mask = 0; - sts = test_media(dev,irqs, irq_mask, 0xef09, 0xf73d, 0x000e, 1000); - if (sts < 0) { - next_tick = sts & ~TIMER_CB; - } else { - if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { - lp->media = BNC; - next_tick = dc21041_autoconf(dev); - } else { - lp->local_state = 1; - de4x5_init_connection(dev); - } - } - } else if (!lp->linkOK && (lp->autosense == AUTO)) { - lp->media = AUI_SUSPECT; - next_tick = 3000; - } - break; - - case AUI_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc21041_autoconf); - break; - - case BNC: - switch (lp->local_state) { - case 0: - if (lp->timeout < 0) { - omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ - outl(omr & ~OMR_FDX, DE4X5_OMR); - } - irqs = 0; - irq_mask = 0; - sts = test_media(dev,irqs, irq_mask, 0xef09, 0xf73d, 0x0006, 1000); - if (sts < 0) { - next_tick = sts & ~TIMER_CB; - } else { - lp->local_state++; /* Ensure media connected */ - next_tick = dc21041_autoconf(dev); - } - break; - - case 1: - if (!lp->tx_enable) { - if ((sts = ping_media(dev, 3000)) < 0) { - next_tick = sts & ~TIMER_CB; - } else { - if (sts) { - lp->local_state = 0; - lp->media = NC; - } else { - de4x5_init_connection(dev); - } - } - } else if (!lp->linkOK && (lp->autosense == AUTO)) { - lp->media = BNC_SUSPECT; - next_tick = 3000; - } - break; - } - break; - - case BNC_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc21041_autoconf); - break; - - case NC: - omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */ - outl(omr | OMR_FDX, DE4X5_OMR); - reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */ - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; - } - lp->media = INIT; - lp->tx_enable = NO; - break; - } - - return next_tick; -} - -/* -** Some autonegotiation chips are broken in that they do not return the -** acknowledge bit (anlpa & MII_ANLPA_ACK) in the link partner advertisement -** register, except at the first power up negotiation. -*/ -static int -dc21140m_autoconf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int ana, anlpa, cap, cr, slnk, sr; - int next_tick = DE4X5_AUTOSENSE_MS; - u_long imr, omr, iobase = dev->base_addr; - - switch(lp->media) { - case INIT: - if (lp->timeout < 0) { - DISABLE_IRQs; - lp->tx_enable = FALSE; - lp->linkOK = 0; - de4x5_save_skbs(dev); /* Save non transmitted skb's */ - } - if ((next_tick = de4x5_reset_phy(dev)) < 0) { - next_tick &= ~TIMER_CB; - } else { - if (lp->useSROM) { - if (srom_map_media(dev) < 0) { - lp->tcount++; - return next_tick; - } - srom_exec(dev, lp->phy[lp->active].gep); - if (lp->infoblock_media == ANS) { - ana = lp->phy[lp->active].ana | MII_ANA_CSMA; - mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); - } - } else { - lp->tmp = MII_SR_ASSC; /* Fake out the MII speed set */ - SET_10Mb; - if (lp->autosense == _100Mb) { - lp->media = _100Mb; - } else if (lp->autosense == _10Mb) { - lp->media = _10Mb; - } else if ((lp->autosense == AUTO) && - ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { - ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); - ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); - mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); - lp->media = ANS; - } else if (lp->autosense == AUTO) { - lp->media = SPD_DET; - } else if (is_spd_100(dev) && is_100_up(dev)) { - lp->media = _100Mb; - } else { - lp->media = NC; - } - } - lp->local_state = 0; - next_tick = dc21140m_autoconf(dev); - } - break; - - case ANS: - switch (lp->local_state) { - case 0: - if (lp->timeout < 0) { - mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); - } - cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500); - if (cr < 0) { - next_tick = cr & ~TIMER_CB; - } else { - if (cr) { - lp->local_state = 0; - lp->media = SPD_DET; - } else { - lp->local_state++; - } - next_tick = dc21140m_autoconf(dev); - } - break; - - case 1: - if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) { - next_tick = sr & ~TIMER_CB; - } else { - lp->media = SPD_DET; - lp->local_state = 0; - if (sr) { /* Success! */ - lp->tmp = MII_SR_ASSC; - anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII); - ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); - if (!(anlpa & MII_ANLPA_RF) && - (cap = anlpa & MII_ANLPA_TAF & ana)) { - if (cap & MII_ANA_100M) { - lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); - lp->media = _100Mb; - } else if (cap & MII_ANA_10M) { - lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); - - lp->media = _10Mb; - } - } - } /* Auto Negotiation failed to finish */ - next_tick = dc21140m_autoconf(dev); - } /* Auto Negotiation failed to start */ - break; - } - break; - - case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ - if (lp->timeout < 0) { - lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS : - (~gep_rd(dev) & GEP_LNP)); - SET_100Mb_PDET; - } - if ((slnk = test_for_100Mb(dev, 6500)) < 0) { - next_tick = slnk & ~TIMER_CB; - } else { - if (is_spd_100(dev) && is_100_up(dev)) { - lp->media = _100Mb; - } else if ((!is_spd_100(dev) && (is_10_up(dev) & lp->tmp))) { - lp->media = _10Mb; - } else { - lp->media = NC; - } - next_tick = dc21140m_autoconf(dev); - } - break; - - case _100Mb: /* Set 100Mb/s */ - next_tick = 3000; - if (!lp->tx_enable) { - SET_100Mb; - de4x5_init_connection(dev); - } else { - if (!lp->linkOK && (lp->autosense == AUTO)) { - if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { - lp->media = INIT; - lp->tcount++; - next_tick = DE4X5_AUTOSENSE_MS; - } - } - } - break; - - case BNC: - case AUI: - case _10Mb: /* Set 10Mb/s */ - next_tick = 3000; - if (!lp->tx_enable) { - SET_10Mb; - de4x5_init_connection(dev); - } else { - if (!lp->linkOK && (lp->autosense == AUTO)) { - if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { - lp->media = INIT; - lp->tcount++; - next_tick = DE4X5_AUTOSENSE_MS; - } - } - } - break; - - case NC: - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; - } - lp->media = INIT; - lp->tx_enable = FALSE; - break; - } - - return next_tick; -} - -/* -** This routine may be merged into dc21140m_autoconf() sometime as I'm -** changing how I figure out the media - but trying to keep it backwards -** compatible with the de500-xa and de500-aa. -** Whether it's BNC, AUI, SYM or MII is sorted out in the infoblock -** functions and set during de4x5_mac_port() and/or de4x5_reset_phy(). -** This routine just has to figure out whether 10Mb/s or 100Mb/s is -** active. -** When autonegotiation is working, the ANS part searches the SROM for -** the highest common speed (TP) link that both can run and if that can -** be full duplex. That infoblock is executed and then the link speed set. -** -** Only _10Mb and _100Mb are tested here. -*/ -static int -dc2114x_autoconf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 cr, anlpa, ana, cap, irqs, irq_mask, imr, omr, slnk, sr, sts; - int next_tick = DE4X5_AUTOSENSE_MS; - - switch (lp->media) { - case INIT: - if (lp->timeout < 0) { - DISABLE_IRQs; - lp->tx_enable = FALSE; - lp->linkOK = 0; - lp->timeout = -1; - de4x5_save_skbs(dev); /* Save non transmitted skb's */ - if (lp->params.autosense & ~AUTO) { - srom_map_media(dev); /* Fixed media requested */ - if (lp->media != lp->params.autosense) { - lp->tcount++; - lp->media = INIT; - return next_tick; - } - lp->media = INIT; - } - } - if ((next_tick = de4x5_reset_phy(dev)) < 0) { - next_tick &= ~TIMER_CB; - } else { - if (lp->autosense == _100Mb) { - lp->media = _100Mb; - } else if (lp->autosense == _10Mb) { - lp->media = _10Mb; - } else if (lp->autosense == TP) { - lp->media = TP; - } else if (lp->autosense == BNC) { - lp->media = BNC; - } else if (lp->autosense == AUI) { - lp->media = AUI; - } else { - lp->media = SPD_DET; - if ((lp->infoblock_media == ANS) && - ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { - ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); - ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); - mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); - lp->media = ANS; - } - } - lp->local_state = 0; - next_tick = dc2114x_autoconf(dev); - } - break; - - case ANS: - switch (lp->local_state) { - case 0: - if (lp->timeout < 0) { - mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); - } - cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500); - if (cr < 0) { - next_tick = cr & ~TIMER_CB; - } else { - if (cr) { - lp->local_state = 0; - lp->media = SPD_DET; - } else { - lp->local_state++; - } - next_tick = dc2114x_autoconf(dev); - } - break; - - case 1: - if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) { - next_tick = sr & ~TIMER_CB; - } else { - lp->media = SPD_DET; - lp->local_state = 0; - if (sr) { /* Success! */ - lp->tmp = MII_SR_ASSC; - anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII); - ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); - if (!(anlpa & MII_ANLPA_RF) && - (cap = anlpa & MII_ANLPA_TAF & ana)) { - if (cap & MII_ANA_100M) { - lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); - lp->media = _100Mb; - } else if (cap & MII_ANA_10M) { - lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); - lp->media = _10Mb; - } - } - } /* Auto Negotiation failed to finish */ - next_tick = dc2114x_autoconf(dev); - } /* Auto Negotiation failed to start */ - break; - } - break; - - case AUI: - if (!lp->tx_enable) { - if (lp->timeout < 0) { - omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ - outl(omr & ~OMR_FDX, DE4X5_OMR); - } - irqs = 0; - irq_mask = 0; - sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); - if (sts < 0) { - next_tick = sts & ~TIMER_CB; - } else { - if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { - lp->media = BNC; - next_tick = dc2114x_autoconf(dev); - } else { - lp->local_state = 1; - de4x5_init_connection(dev); - } - } - } else if (!lp->linkOK && (lp->autosense == AUTO)) { - lp->media = AUI_SUSPECT; - next_tick = 3000; - } - break; - - case AUI_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc2114x_autoconf); - break; - - case BNC: - switch (lp->local_state) { - case 0: - if (lp->timeout < 0) { - omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ - outl(omr & ~OMR_FDX, DE4X5_OMR); - } - irqs = 0; - irq_mask = 0; - sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); - if (sts < 0) { - next_tick = sts & ~TIMER_CB; - } else { - lp->local_state++; /* Ensure media connected */ - next_tick = dc2114x_autoconf(dev); - } - break; - - case 1: - if (!lp->tx_enable) { - if ((sts = ping_media(dev, 3000)) < 0) { - next_tick = sts & ~TIMER_CB; - } else { - if (sts) { - lp->local_state = 0; - lp->tcount++; - lp->media = INIT; - } else { - de4x5_init_connection(dev); - } - } - } else if (!lp->linkOK && (lp->autosense == AUTO)) { - lp->media = BNC_SUSPECT; - next_tick = 3000; - } - break; - } - break; - - case BNC_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc2114x_autoconf); - break; - - case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ - if (srom_map_media(dev) < 0) { - lp->tcount++; - lp->media = INIT; - return next_tick; - } - if (lp->media == _100Mb) { - if ((slnk = test_for_100Mb(dev, 6500)) < 0) { - lp->media = SPD_DET; - return (slnk & ~TIMER_CB); - } - } else { - if (wait_for_link(dev) < 0) { - lp->media = SPD_DET; - return PDET_LINK_WAIT; - } - } - if (lp->media == ANS) { /* Do MII parallel detection */ - if (is_spd_100(dev)) { - lp->media = _100Mb; - } else { - lp->media = _10Mb; - } - next_tick = dc2114x_autoconf(dev); - } else if (((lp->media == _100Mb) && is_100_up(dev)) || - (((lp->media == _10Mb) || (lp->media == TP) || - (lp->media == BNC) || (lp->media == AUI)) && - is_10_up(dev))) { - next_tick = dc2114x_autoconf(dev); - } else { - lp->tcount++; - lp->media = INIT; - } - break; - - case _10Mb: - next_tick = 3000; - if (!lp->tx_enable) { - SET_10Mb; - de4x5_init_connection(dev); - } else { - if (!lp->linkOK && (lp->autosense == AUTO)) { - if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { - lp->media = INIT; - lp->tcount++; - next_tick = DE4X5_AUTOSENSE_MS; - } - } - } - break; - - case _100Mb: - next_tick = 3000; - if (!lp->tx_enable) { - SET_100Mb; - de4x5_init_connection(dev); - } else { - if (!lp->linkOK && (lp->autosense == AUTO)) { - if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { - lp->media = INIT; - lp->tcount++; - next_tick = DE4X5_AUTOSENSE_MS; - } - } - } - break; - - default: - lp->tcount++; -printk("Huh?: media:%02x\n", lp->media); - lp->media = INIT; - break; - } - - return next_tick; -} - -static int -srom_autoconf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - - return lp->infoleaf_fn(dev); -} - -/* -** This mapping keeps the original media codes and FDX flag unchanged. -** While it isn't strictly necessary, it helps me for the moment... -** The early return avoids a media state / SROM media space clash. -*/ -static int -srom_map_media(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - - lp->fdx = 0; - if (lp->infoblock_media == lp->media) - return 0; - - switch(lp->infoblock_media) { - case SROM_10BASETF: - if (!lp->params.fdx) return -1; - lp->fdx = TRUE; - case SROM_10BASET: - if (lp->params.fdx && !lp->fdx) return -1; - if ((lp->chipset == DC21140) || ((lp->chipset & ~0x00ff) == DC2114x)) { - lp->media = _10Mb; - } else { - lp->media = TP; - } - break; - - case SROM_10BASE2: - lp->media = BNC; - break; - - case SROM_10BASE5: - lp->media = AUI; - break; - - case SROM_100BASETF: - if (!lp->params.fdx) return -1; - lp->fdx = TRUE; - case SROM_100BASET: - if (lp->params.fdx && !lp->fdx) return -1; - lp->media = _100Mb; - break; - - case SROM_100BASET4: - lp->media = _100Mb; - break; - - case SROM_100BASEFF: - if (!lp->params.fdx) return -1; - lp->fdx = TRUE; - case SROM_100BASEF: - if (lp->params.fdx && !lp->fdx) return -1; - lp->media = _100Mb; - break; - - case ANS: - lp->media = ANS; - lp->fdx = lp->params.fdx; - break; - - default: - printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, - lp->infoblock_media); - return -1; - break; - } - - return 0; -} - -static void -de4x5_init_connection(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - u_long flags = 0; - - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; /* Stop scrolling media messages */ - } - - spin_lock_irqsave(&lp->lock, flags); - de4x5_rst_desc_ring(dev); - de4x5_setup_intr(dev); - lp->tx_enable = YES; - spin_unlock_irqrestore(&lp->lock, flags); - outl(POLL_DEMAND, DE4X5_TPD); - - netif_wake_queue(dev); - - return; -} - -/* -** General PHY reset function. Some MII devices don't reset correctly -** since their MII address pins can float at voltages that are dependent -** on the signal pin use. Do a double reset to ensure a reset. -*/ -static int -de4x5_reset_phy(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int next_tick = 0; - - if ((lp->useSROM) || (lp->phy[lp->active].id)) { - if (lp->timeout < 0) { - if (lp->useSROM) { - if (lp->phy[lp->active].rst) { - srom_exec(dev, lp->phy[lp->active].rst); - srom_exec(dev, lp->phy[lp->active].rst); - } else if (lp->rst) { /* Type 5 infoblock reset */ - srom_exec(dev, lp->rst); - srom_exec(dev, lp->rst); - } - } else { - PHY_HARD_RESET; - } - if (lp->useMII) { - mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); - } - } - if (lp->useMII) { - next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500); - } - } else if (lp->chipset == DC21140) { - PHY_HARD_RESET; - } - - return next_tick; -} - -static int -test_media(struct net_device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 sts, csr12; - - if (lp->timeout < 0) { - lp->timeout = msec/100; - if (!lp->useSROM) { /* Already done if by SROM, else dc2104[01] */ - reset_init_sia(dev, csr13, csr14, csr15); - } - - /* set up the interrupt mask */ - outl(irq_mask, DE4X5_IMR); - - /* clear all pending interrupts */ - sts = inl(DE4X5_STS); - outl(sts, DE4X5_STS); - - /* clear csr12 NRA and SRA bits */ - if ((lp->chipset == DC21041) || lp->useSROM) { - csr12 = inl(DE4X5_SISR); - outl(csr12, DE4X5_SISR); - } - } - - sts = inl(DE4X5_STS) & ~TIMER_CB; - - if (!(sts & irqs) && --lp->timeout) { - sts = 100 | TIMER_CB; - } else { - lp->timeout = -1; - } - - return sts; -} - -static int -test_tp(struct net_device *dev, s32 msec) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int sisr; - - if (lp->timeout < 0) { - lp->timeout = msec/100; - } - - sisr = (inl(DE4X5_SISR) & ~TIMER_CB) & (SISR_LKF | SISR_NCR); - - if (sisr && --lp->timeout) { - sisr = 100 | TIMER_CB; - } else { - lp->timeout = -1; - } - - return sisr; -} - -/* -** Samples the 100Mb Link State Signal. The sample interval is important -** because too fast a rate can give erroneous results and confuse the -** speed sense algorithm. -*/ -#define SAMPLE_INTERVAL 500 /* ms */ -#define SAMPLE_DELAY 2000 /* ms */ -static int -test_for_100Mb(struct net_device *dev, int msec) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int gep = 0, ret = ((lp->chipset & ~0x00ff)==DC2114x? -1 :GEP_SLNK); - - if (lp->timeout < 0) { - if ((msec/SAMPLE_INTERVAL) <= 0) return 0; - if (msec > SAMPLE_DELAY) { - lp->timeout = (msec - SAMPLE_DELAY)/SAMPLE_INTERVAL; - gep = SAMPLE_DELAY | TIMER_CB; - return gep; - } else { - lp->timeout = msec/SAMPLE_INTERVAL; - } - } - - if (lp->phy[lp->active].id || lp->useSROM) { - gep = is_100_up(dev) | is_spd_100(dev); - } else { - gep = (~gep_rd(dev) & (GEP_SLNK | GEP_LNP)); - } - if (!(gep & ret) && --lp->timeout) { - gep = SAMPLE_INTERVAL | TIMER_CB; - } else { - lp->timeout = -1; - } - - return gep; -} - -static int -wait_for_link(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - - if (lp->timeout < 0) { - lp->timeout = 1; - } - - if (lp->timeout--) { - return TIMER_CB; - } else { - lp->timeout = -1; - } - - return 0; -} - -/* -** -** -*/ -static int -test_mii_reg(struct net_device *dev, int reg, int mask, int pol, long msec) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int test; - u_long iobase = dev->base_addr; - - if (lp->timeout < 0) { - lp->timeout = msec/100; - } - - if (pol) pol = ~0; - reg = mii_rd((u_char)reg, lp->phy[lp->active].addr, DE4X5_MII) & mask; - test = (reg ^ pol) & mask; - - if (test && --lp->timeout) { - reg = 100 | TIMER_CB; - } else { - lp->timeout = -1; - } - - return reg; -} - -static int -is_spd_100(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int spd; - - if (lp->useMII) { - spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII); - spd = ~(spd ^ lp->phy[lp->active].spd.value); - spd &= lp->phy[lp->active].spd.mask; - } else if (!lp->useSROM) { /* de500-xa */ - spd = ((~gep_rd(dev)) & GEP_SLNK); - } else { - if ((lp->ibn == 2) || !lp->asBitValid) - return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0); - - spd = (lp->asBitValid & (lp->asPolarity ^ (gep_rd(dev) & lp->asBit))) | - (lp->linkOK & ~lp->asBitValid); - } - - return spd; -} - -static int -is_100_up(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if (lp->useMII) { - /* Double read for sticky bits & temporary drops */ - mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); - return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); - } else if (!lp->useSROM) { /* de500-xa */ - return ((~gep_rd(dev)) & GEP_SLNK); - } else { - if ((lp->ibn == 2) || !lp->asBitValid) - return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0); - - return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | - (lp->linkOK & ~lp->asBitValid)); - } -} - -static int -is_10_up(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if (lp->useMII) { - /* Double read for sticky bits & temporary drops */ - mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); - return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); - } else if (!lp->useSROM) { /* de500-xa */ - return ((~gep_rd(dev)) & GEP_LNP); - } else { - if ((lp->ibn == 2) || !lp->asBitValid) - return (((lp->chipset & ~0x00ff) == DC2114x) ? - (~inl(DE4X5_SISR)&SISR_LS10): - 0); - - return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | - (lp->linkOK & ~lp->asBitValid)); - } -} - -static int -is_anc_capable(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { - return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII)); - } else if ((lp->chipset & ~0x00ff) == DC2114x) { - return (inl(DE4X5_SISR) & SISR_LPN) >> 12; - } else { - return 0; - } -} - -/* -** Send a packet onto the media and watch for send errors that indicate the -** media is bad or unconnected. -*/ -static int -ping_media(struct net_device *dev, int msec) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int sisr; - - if (lp->timeout < 0) { - lp->timeout = msec/100; - - lp->tmp = lp->tx_new; /* Remember the ring position */ - load_packet(dev, lp->frame, TD_LS | TD_FS | sizeof(lp->frame), (struct sk_buff *)1); - lp->tx_new = (++lp->tx_new) % lp->txRingSize; - outl(POLL_DEMAND, DE4X5_TPD); - } - - sisr = inl(DE4X5_SISR); - - if ((!(sisr & SISR_NCR)) && - ((s32)le32_to_cpu(lp->tx_ring[lp->tmp].status) < 0) && - (--lp->timeout)) { - sisr = 100 | TIMER_CB; - } else { - if ((!(sisr & SISR_NCR)) && - !(le32_to_cpu(lp->tx_ring[lp->tmp].status) & (T_OWN | TD_ES)) && - lp->timeout) { - sisr = 0; - } else { - sisr = 1; - } - lp->timeout = -1; - } - - return sisr; -} - -/* -** This function does 2 things: on Intels it kmalloc's another buffer to -** replace the one about to be passed up. On Alpha's it kmallocs a buffer -** into which the packet is copied. -*/ -static struct sk_buff * -de4x5_alloc_rx_buff(struct net_device *dev, int index, int len) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - struct sk_buff *p; - -#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) - struct sk_buff *ret; - u_long i=0, tmp; - - p = dev_alloc_skb(IEEE802_3_SZ + ALIGN + 2); - if (!p) return NULL; - - p->dev = dev; - tmp = virt_to_bus(p->data); - i = ((tmp + ALIGN) & ~ALIGN) - tmp; - skb_reserve(p, i); - lp->rx_ring[index].buf = cpu_to_le32(tmp + i); - - ret = lp->rx_skb[index]; - lp->rx_skb[index] = p; - - if ((u_long) ret > 1) { - skb_put(ret, len); - } - - return ret; - -#else - if (lp->state != OPEN) return (struct sk_buff *)1; /* Fake out the open */ - - p = dev_alloc_skb(len + 2); - if (!p) return NULL; - - p->dev = dev; - skb_reserve(p, 2); /* Align */ - if (index < lp->rx_old) { /* Wrapped buffer */ - short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ; - memcpy(skb_put(p,tlen),lp->rx_bufs + lp->rx_old * RX_BUFF_SZ,tlen); - memcpy(skb_put(p,len-tlen),lp->rx_bufs,len-tlen); - } else { /* Linear buffer */ - memcpy(skb_put(p,len),lp->rx_bufs + lp->rx_old * RX_BUFF_SZ,len); - } - - return p; -#endif -} - -static void -de4x5_free_rx_buffs(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int i; - - for (i=0; irxRingSize; i++) { - if ((u_long) lp->rx_skb[i] > 1) { - dev_kfree_skb(lp->rx_skb[i]); - } - lp->rx_ring[i].status = 0; - lp->rx_skb[i] = (struct sk_buff *)1; /* Dummy entry */ - } - - return; -} - -static void -de4x5_free_tx_buffs(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int i; - - for (i=0; itxRingSize; i++) { - if (lp->tx_skb[i]) - de4x5_free_tx_buff(lp, i); - lp->tx_ring[i].status = 0; - } - - /* Unload the locally queued packets */ - while (lp->cache.skb) { - dev_kfree_skb(de4x5_get_cache(dev)); - } - - return; -} - -/* -** When a user pulls a connection, the DECchip can end up in a -** 'running - waiting for end of transmission' state. This means that we -** have to perform a chip soft reset to ensure that we can synchronize -** the hardware and software and make any media probes using a loopback -** packet meaningful. -*/ -static void -de4x5_save_skbs(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 omr; - - if (!lp->cache.save_cnt) { - STOP_DE4X5; - de4x5_tx(dev); /* Flush any sent skb's */ - de4x5_free_tx_buffs(dev); - de4x5_cache_state(dev, DE4X5_SAVE_STATE); - de4x5_sw_reset(dev); - de4x5_cache_state(dev, DE4X5_RESTORE_STATE); - lp->cache.save_cnt++; - START_DE4X5; - } - - return; -} - -static void -de4x5_rst_desc_ring(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int i; - s32 omr; - - if (lp->cache.save_cnt) { - STOP_DE4X5; - outl(lp->dma_rings, DE4X5_RRBA); - outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), - DE4X5_TRBA); - - lp->rx_new = lp->rx_old = 0; - lp->tx_new = lp->tx_old = 0; - - for (i = 0; i < lp->rxRingSize; i++) { - lp->rx_ring[i].status = cpu_to_le32(R_OWN); - } - - for (i = 0; i < lp->txRingSize; i++) { - lp->tx_ring[i].status = cpu_to_le32(0); - } - - barrier(); - lp->cache.save_cnt--; - START_DE4X5; - } - - return; -} - -static void -de4x5_cache_state(struct net_device *dev, int flag) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - switch(flag) { - case DE4X5_SAVE_STATE: - lp->cache.csr0 = inl(DE4X5_BMR); - lp->cache.csr6 = (inl(DE4X5_OMR) & ~(OMR_ST | OMR_SR)); - lp->cache.csr7 = inl(DE4X5_IMR); - break; - - case DE4X5_RESTORE_STATE: - outl(lp->cache.csr0, DE4X5_BMR); - outl(lp->cache.csr6, DE4X5_OMR); - outl(lp->cache.csr7, DE4X5_IMR); - if (lp->chipset == DC21140) { - gep_wr(lp->cache.gepc, dev); - gep_wr(lp->cache.gep, dev); - } else { - reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, - lp->cache.csr15); - } - break; - } - - return; -} - -static void -de4x5_put_cache(struct net_device *dev, struct sk_buff *skb) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - struct sk_buff *p; - - if (lp->cache.skb) { - for (p=lp->cache.skb; p->next; p=p->next); - p->next = skb; - } else { - lp->cache.skb = skb; - } - skb->next = NULL; - - return; -} - -static void -de4x5_putb_cache(struct net_device *dev, struct sk_buff *skb) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - struct sk_buff *p = lp->cache.skb; - - lp->cache.skb = skb; - skb->next = p; - - return; -} - -static struct sk_buff * -de4x5_get_cache(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - struct sk_buff *p = lp->cache.skb; - - if (p) { - lp->cache.skb = p->next; - p->next = NULL; - } - - return p; -} - -/* -** Check the Auto Negotiation State. Return OK when a link pass interrupt -** is received and the auto-negotiation status is NWAY OK. -*/ -static int -test_ans(struct net_device *dev, s32 irqs, s32 irq_mask, s32 msec) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 sts, ans; - - if (lp->timeout < 0) { - lp->timeout = msec/100; - outl(irq_mask, DE4X5_IMR); - - /* clear all pending interrupts */ - sts = inl(DE4X5_STS); - outl(sts, DE4X5_STS); - } - - ans = inl(DE4X5_SISR) & SISR_ANS; - sts = inl(DE4X5_STS) & ~TIMER_CB; - - if (!(sts & irqs) && (ans ^ ANS_NWOK) && --lp->timeout) { - sts = 100 | TIMER_CB; - } else { - lp->timeout = -1; - } - - return sts; -} - -static void -de4x5_setup_intr(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 imr, sts; - - if (inl(DE4X5_OMR) & OMR_SR) { /* Only unmask if TX/RX is enabled */ - imr = 0; - UNMASK_IRQs; - sts = inl(DE4X5_STS); /* Reset any pending (stale) interrupts */ - outl(sts, DE4X5_STS); - ENABLE_IRQs; - } - - return; -} - -/* -** -*/ -static void -reset_init_sia(struct net_device *dev, s32 csr13, s32 csr14, s32 csr15) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - RESET_SIA; - if (lp->useSROM) { - if (lp->ibn == 3) { - srom_exec(dev, lp->phy[lp->active].rst); - srom_exec(dev, lp->phy[lp->active].gep); - outl(1, DE4X5_SICR); - return; - } else { - csr15 = lp->cache.csr15; - csr14 = lp->cache.csr14; - csr13 = lp->cache.csr13; - outl(csr15 | lp->cache.gepc, DE4X5_SIGR); - outl(csr15 | lp->cache.gep, DE4X5_SIGR); - } - } else { - outl(csr15, DE4X5_SIGR); - } - outl(csr14, DE4X5_STRR); - outl(csr13, DE4X5_SICR); - - mdelay(10); - - return; -} - -/* -** Create a loopback ethernet packet -*/ -static void -create_packet(struct net_device *dev, char *frame, int len) -{ - int i; - char *buf = frame; - - for (i=0; idev_addr[i]; - } - for (i=0; idev_addr[i]; - } - - *buf++ = 0; /* Packet length (2 bytes) */ - *buf++ = 1; - - return; -} - -/* -** Look for a particular board name in the EISA configuration space -*/ -static int -EISA_signature(char *name, s32 eisa_id) -{ - static c_char *signatures[] = DE4X5_SIGNATURE; - char ManCode[DE4X5_STRLEN]; - union { - s32 ID; - char Id[4]; - } Eisa; - int i, status = 0, siglen = sizeof(signatures)/sizeof(c_char *); - - *name = '\0'; - Eisa.ID = inl(eisa_id); - - ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40); - ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40); - ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30); - ManCode[3]=((Eisa.Id[2]&0x0f)+0x30); - ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30); - ManCode[5]='\0'; - - for (i=0;ichipset == DC21040) { - strcpy(name, "DE434/5"); - return status; - } else { /* Search for a DEC name in the SROM */ - int i = *((char *)&lp->srom + 19) * 3; - strncpy(name, (char *)&lp->srom + 26 + i, 8); - } - name[8] = '\0'; - for (i=0; ichipset == DC21040) ? "DC21040" : - ((lp->chipset == DC21041) ? "DC21041" : - ((lp->chipset == DC21140) ? "DC21140" : - ((lp->chipset == DC21142) ? "DC21142" : - ((lp->chipset == DC21143) ? "DC21143" : "UNKNOWN" - ))))))); - } - if (lp->chipset != DC21041) { - useSROM = TRUE; /* card is not recognisably DEC */ - } - } else if ((lp->chipset & ~0x00ff) == DC2114x) { - useSROM = TRUE; - } - - return status; -} - -/* -** Set up the Ethernet PROM counter to the start of the Ethernet address on -** the DC21040, else read the SROM for the other chips. -** The SROM may not be present in a multi-MAC card, so first read the -** MAC address and check for a bad address. If there is a bad one then exit -** immediately with the prior srom contents intact (the h/w address will -** be fixed up later). -*/ -static void -DevicePresent(u_long aprom_addr) -{ - int i, j=0; - struct bus_type *lp = &bus; - - if (lp->chipset == DC21040) { - if (lp->bus == EISA) { - enet_addr_rst(aprom_addr); /* Reset Ethernet Address ROM Pointer */ - } else { - outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */ - } - } else { /* Read new srom */ - u_short tmp, *p = (short *)((char *)&lp->srom + SROM_HWADD); - for (i=0; i<(ETH_ALEN>>1); i++) { - tmp = srom_rd(aprom_addr, (SROM_HWADD>>1) + i); - *p = le16_to_cpu(tmp); - j += *p++; - } - if ((j == 0) || (j == 0x2fffd)) { - return; - } - - p=(short *)&lp->srom; - for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) { - tmp = srom_rd(aprom_addr, i); - *p++ = le16_to_cpu(tmp); - } - de4x5_dbg_srom((struct de4x5_srom *)&lp->srom); - } - - return; -} - -/* -** Since the write on the Enet PROM register doesn't seem to reset the PROM -** pointer correctly (at least on my DE425 EISA card), this routine should do -** it...from depca.c. -*/ -static void -enet_addr_rst(u_long aprom_addr) -{ - union { - struct { - u32 a; - u32 b; - } llsig; - char Sig[sizeof(u32) << 1]; - } dev; - short sigLength=0; - s8 data; - int i, j; - - dev.llsig.a = ETH_PROM_SIG; - dev.llsig.b = ETH_PROM_SIG; - sigLength = sizeof(u32) << 1; - - for (i=0,j=0;jbase_addr; - int broken, i, k, tmp, status = 0; - u_short j,chksum; - struct bus_type *lp = &bus; - - broken = de4x5_bad_srom(lp); - - for (i=0,k=0,j=0;j<3;j++) { - k <<= 1; - if (k > 0xffff) k-=0xffff; - - if (lp->bus == PCI) { - if (lp->chipset == DC21040) { - while ((tmp = inl(DE4X5_APROM)) < 0); - k += (u_char) tmp; - dev->dev_addr[i++] = (u_char) tmp; - while ((tmp = inl(DE4X5_APROM)) < 0); - k += (u_short) (tmp << 8); - dev->dev_addr[i++] = (u_char) tmp; - } else if (!broken) { - dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++; - dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++; - } else if ((broken == SMC) || (broken == ACCTON)) { - dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++; - dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++; - } - } else { - k += (u_char) (tmp = inb(EISA_APROM)); - dev->dev_addr[i++] = (u_char) tmp; - k += (u_short) ((tmp = inb(EISA_APROM)) << 8); - dev->dev_addr[i++] = (u_char) tmp; - } - - if (k > 0xffff) k-=0xffff; - } - if (k == 0xffff) k=0; - - if (lp->bus == PCI) { - if (lp->chipset == DC21040) { - while ((tmp = inl(DE4X5_APROM)) < 0); - chksum = (u_char) tmp; - while ((tmp = inl(DE4X5_APROM)) < 0); - chksum |= (u_short) (tmp << 8); - if ((k != chksum) && (dec_only)) status = -1; - } - } else { - chksum = (u_char) inb(EISA_APROM); - chksum |= (u_short) (inb(EISA_APROM) << 8); - if ((k != chksum) && (dec_only)) status = -1; - } - - /* If possible, try to fix a broken card - SMC only so far */ - srom_repair(dev, broken); - -#ifdef CONFIG_PPC - /* - ** If the address starts with 00 a0, we have to bit-reverse - ** each byte of the address. - */ - if ( (ppc_md.ppc_machine & _MACH_Pmac) && - (dev->dev_addr[0] == 0) && - (dev->dev_addr[1] == 0xa0) ) - { - for (i = 0; i < ETH_ALEN; ++i) - { - int x = dev->dev_addr[i]; - x = ((x & 0xf) << 4) + ((x & 0xf0) >> 4); - x = ((x & 0x33) << 2) + ((x & 0xcc) >> 2); - dev->dev_addr[i] = ((x & 0x55) << 1) + ((x & 0xaa) >> 1); - } - } -#endif /* CONFIG_PPC */ - - /* Test for a bad enet address */ - status = test_bad_enet(dev, status); - - return status; -} - -/* -** Test for enet addresses in the first 32 bytes. The built-in strncmp -** didn't seem to work here...? -*/ -static int -de4x5_bad_srom(struct bus_type *lp) -{ - int i, status = 0; - - for (i=0; isrom, (char *)&enet_det[i], 3) && - !de4x5_strncmp((char *)&lp->srom+0x10, (char *)&enet_det[i], 3)) { - if (i == 0) { - status = SMC; - } else if (i == 1) { - status = ACCTON; - } - break; - } - } - - return status; -} - -static int -de4x5_strncmp(char *a, char *b, int n) -{ - int ret=0; - - for (;n && !ret;n--) { - ret = *a++ - *b++; - } - - return ret; -} - -static void -srom_repair(struct net_device *dev, int card) -{ - struct bus_type *lp = &bus; - - switch(card) { - case SMC: - memset((char *)&bus.srom, 0, sizeof(struct de4x5_srom)); - memcpy(lp->srom.ieee_addr, (char *)dev->dev_addr, ETH_ALEN); - memcpy(lp->srom.info, (char *)&srom_repair_info[SMC-1], 100); - useSROM = TRUE; - break; - } - - return; -} - -/* -** Assume that the irq's do not follow the PCI spec - this is seems -** to be true so far (2 for 2). -*/ -static int -test_bad_enet(struct net_device *dev, int status) -{ - struct bus_type *lp = &bus; - int i, tmp; - - for (tmp=0,i=0; idev_addr[i]; - if ((tmp == 0) || (tmp == 0x5fa)) { - if ((lp->chipset == last.chipset) && - (lp->bus_num == last.bus) && (lp->bus_num > 0)) { - for (i=0; idev_addr[i] = last.addr[i]; - for (i=ETH_ALEN-1; i>2; --i) { - dev->dev_addr[i] += 1; - if (dev->dev_addr[i] != 0) break; - } - for (i=0; idev_addr[i]; - if (!an_exception(lp)) { - dev->irq = last.irq; - } - - status = 0; - } - } else if (!status) { - last.chipset = lp->chipset; - last.bus = lp->bus_num; - last.irq = dev->irq; - for (i=0; idev_addr[i]; - } - - return status; -} - -/* -** List of board exceptions with correctly wired IRQs -*/ -static int -an_exception(struct bus_type *lp) -{ - if ((*(u_short *)lp->srom.sub_vendor_id == 0x00c0) && - (*(u_short *)lp->srom.sub_system_id == 0x95e0)) { - return -1; - } - - return 0; -} - -/* -** SROM Read -*/ -static short -srom_rd(u_long addr, u_char offset) -{ - sendto_srom(SROM_RD | SROM_SR, addr); - - srom_latch(SROM_RD | SROM_SR | DT_CS, addr); - srom_command(SROM_RD | SROM_SR | DT_IN | DT_CS, addr); - srom_address(SROM_RD | SROM_SR | DT_CS, addr, offset); - - return srom_data(SROM_RD | SROM_SR | DT_CS, addr); -} - -static void -srom_latch(u_int command, u_long addr) -{ - sendto_srom(command, addr); - sendto_srom(command | DT_CLK, addr); - sendto_srom(command, addr); - - return; -} - -static void -srom_command(u_int command, u_long addr) -{ - srom_latch(command, addr); - srom_latch(command, addr); - srom_latch((command & 0x0000ff00) | DT_CS, addr); - - return; -} - -static void -srom_address(u_int command, u_long addr, u_char offset) -{ - int i, a; - - a = offset << 2; - for (i=0; i<6; i++, a <<= 1) { - srom_latch(command | ((a & 0x80) ? DT_IN : 0), addr); - } - udelay(1); - - i = (getfrom_srom(addr) >> 3) & 0x01; - - return; -} - -static short -srom_data(u_int command, u_long addr) -{ - int i; - short word = 0; - s32 tmp; - - for (i=0; i<16; i++) { - sendto_srom(command | DT_CLK, addr); - tmp = getfrom_srom(addr); - sendto_srom(command, addr); - - word = (word << 1) | ((tmp >> 3) & 0x01); - } - - sendto_srom(command & 0x0000ff00, addr); - - return word; -} - -/* -static void -srom_busy(u_int command, u_long addr) -{ - sendto_srom((command & 0x0000ff00) | DT_CS, addr); - - while (!((getfrom_srom(addr) >> 3) & 0x01)) { - mdelay(1); - } - - sendto_srom(command & 0x0000ff00, addr); - - return; -} -*/ - -static void -sendto_srom(u_int command, u_long addr) -{ - outl(command, addr); - udelay(1); - - return; -} - -static int -getfrom_srom(u_long addr) -{ - s32 tmp; - - tmp = inl(addr); - udelay(1); - - return tmp; -} - -static int -srom_infoleaf_info(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int i, count; - u_char *p; - - /* Find the infoleaf decoder function that matches this chipset */ - for (i=0; ichipset == infoleaf_array[i].chipset) break; - } - if (i == INFOLEAF_SIZE) { - lp->useSROM = FALSE; - printk("%s: Cannot find correct chipset for SROM decoding!\n", - dev->name); - return -ENXIO; - } - - lp->infoleaf_fn = infoleaf_array[i].fn; - - /* Find the information offset that this function should use */ - count = *((u_char *)&lp->srom + 19); - p = (u_char *)&lp->srom + 26; - - if (count > 1) { - for (i=count; i; --i, p+=3) { - if (lp->device == *p) break; - } - if (i == 0) { - lp->useSROM = FALSE; - printk("%s: Cannot find correct PCI device [%d] for SROM decoding!\n", - dev->name, lp->device); - return -ENXIO; - } - } - - lp->infoleaf_offset = TWIDDLE(p+1); - - return 0; -} - -/* -** This routine loads any type 1 or 3 MII info into the mii device -** struct and executes any type 5 code to reset PHY devices for this -** controller. -** The info for the MII devices will be valid since the index used -** will follow the discovery process from MII address 1-31 then 0. -*/ -static void -srom_init(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; - u_char count; - - p+=2; - if (lp->chipset == DC21140) { - lp->cache.gepc = (*p++ | GEP_CTRL); - gep_wr(lp->cache.gepc, dev); - } - - /* Block count */ - count = *p++; - - /* Jump the infoblocks to find types */ - for (;count; --count) { - if (*p < 128) { - p += COMPACT_LEN; - } else if (*(p+1) == 5) { - type5_infoblock(dev, 1, p); - p += ((*p & BLOCK_LEN) + 1); - } else if (*(p+1) == 4) { - p += ((*p & BLOCK_LEN) + 1); - } else if (*(p+1) == 3) { - type3_infoblock(dev, 1, p); - p += ((*p & BLOCK_LEN) + 1); - } else if (*(p+1) == 2) { - p += ((*p & BLOCK_LEN) + 1); - } else if (*(p+1) == 1) { - type1_infoblock(dev, 1, p); - p += ((*p & BLOCK_LEN) + 1); - } else { - p += ((*p & BLOCK_LEN) + 1); - } - } - - return; -} - -/* -** A generic routine that writes GEP control, data and reset information -** to the GEP register (21140) or csr15 GEP portion (2114[23]). -*/ -static void -srom_exec(struct net_device *dev, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - u_char count = (p ? *p++ : 0); - u_short *w = (u_short *)p; - - if (((lp->ibn != 1) && (lp->ibn != 3) && (lp->ibn != 5)) || !count) return; - - if (lp->chipset != DC21140) RESET_SIA; - - while (count--) { - gep_wr(((lp->chipset==DC21140) && (lp->ibn!=5) ? - *p++ : TWIDDLE(w++)), dev); - mdelay(2); /* 2ms per action */ - } - - if (lp->chipset != DC21140) { - outl(lp->cache.csr14, DE4X5_STRR); - outl(lp->cache.csr13, DE4X5_SICR); - } - - return; -} - -/* -** Basically this function is a NOP since it will never be called, -** unless I implement the DC21041 SROM functions. There's no need -** since the existing code will be satisfactory for all boards. -*/ -static int -dc21041_infoleaf(struct net_device *dev) -{ - return DE4X5_AUTOSENSE_MS; -} - -static int -dc21140_infoleaf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char count = 0; - u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; - int next_tick = DE4X5_AUTOSENSE_MS; - - /* Read the connection type */ - p+=2; - - /* GEP control */ - lp->cache.gepc = (*p++ | GEP_CTRL); - - /* Block count */ - count = *p++; - - /* Recursively figure out the info blocks */ - if (*p < 128) { - next_tick = dc_infoblock[COMPACT](dev, count, p); - } else { - next_tick = dc_infoblock[*(p+1)](dev, count, p); - } - - if (lp->tcount == count) { - lp->media = NC; - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; - } - lp->media = INIT; - lp->tcount = 0; - lp->tx_enable = FALSE; - } - - return next_tick & ~TIMER_CB; -} - -static int -dc21142_infoleaf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char count = 0; - u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; - int next_tick = DE4X5_AUTOSENSE_MS; - - /* Read the connection type */ - p+=2; - - /* Block count */ - count = *p++; - - /* Recursively figure out the info blocks */ - if (*p < 128) { - next_tick = dc_infoblock[COMPACT](dev, count, p); - } else { - next_tick = dc_infoblock[*(p+1)](dev, count, p); - } - - if (lp->tcount == count) { - lp->media = NC; - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; - } - lp->media = INIT; - lp->tcount = 0; - lp->tx_enable = FALSE; - } - - return next_tick & ~TIMER_CB; -} - -static int -dc21143_infoleaf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char count = 0; - u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; - int next_tick = DE4X5_AUTOSENSE_MS; - - /* Read the connection type */ - p+=2; - - /* Block count */ - count = *p++; - - /* Recursively figure out the info blocks */ - if (*p < 128) { - next_tick = dc_infoblock[COMPACT](dev, count, p); - } else { - next_tick = dc_infoblock[*(p+1)](dev, count, p); - } - if (lp->tcount == count) { - lp->media = NC; - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; - } - lp->media = INIT; - lp->tcount = 0; - lp->tx_enable = FALSE; - } - - return next_tick & ~TIMER_CB; -} - -/* -** The compact infoblock is only designed for DC21140[A] chips, so -** we'll reuse the dc21140m_autoconf function. Non MII media only. -*/ -static int -compact_infoblock(struct net_device *dev, u_char count, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char flags, csr6; - - /* Recursively figure out the info blocks */ - if (--count > lp->tcount) { - if (*(p+COMPACT_LEN) < 128) { - return dc_infoblock[COMPACT](dev, count, p+COMPACT_LEN); - } else { - return dc_infoblock[*(p+COMPACT_LEN+1)](dev, count, p+COMPACT_LEN); - } - } - - if ((lp->media == INIT) && (lp->timeout < 0)) { - lp->ibn = COMPACT; - lp->active = 0; - gep_wr(lp->cache.gepc, dev); - lp->infoblock_media = (*p++) & COMPACT_MC; - lp->cache.gep = *p++; - csr6 = *p++; - flags = *p++; - - lp->asBitValid = (flags & 0x80) ? 0 : -1; - lp->defMedium = (flags & 0x40) ? -1 : 0; - lp->asBit = 1 << ((csr6 >> 1) & 0x07); - lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; - lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); - lp->useMII = FALSE; - - de4x5_switch_mac_port(dev); - } - - return dc21140m_autoconf(dev); -} - -/* -** This block describes non MII media for the DC21140[A] only. -*/ -static int -type0_infoblock(struct net_device *dev, u_char count, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char flags, csr6, len = (*p & BLOCK_LEN)+1; - - /* Recursively figure out the info blocks */ - if (--count > lp->tcount) { - if (*(p+len) < 128) { - return dc_infoblock[COMPACT](dev, count, p+len); - } else { - return dc_infoblock[*(p+len+1)](dev, count, p+len); - } - } - - if ((lp->media == INIT) && (lp->timeout < 0)) { - lp->ibn = 0; - lp->active = 0; - gep_wr(lp->cache.gepc, dev); - p+=2; - lp->infoblock_media = (*p++) & BLOCK0_MC; - lp->cache.gep = *p++; - csr6 = *p++; - flags = *p++; - - lp->asBitValid = (flags & 0x80) ? 0 : -1; - lp->defMedium = (flags & 0x40) ? -1 : 0; - lp->asBit = 1 << ((csr6 >> 1) & 0x07); - lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; - lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); - lp->useMII = FALSE; - - de4x5_switch_mac_port(dev); - } - - return dc21140m_autoconf(dev); -} - -/* These functions are under construction! */ - -static int -type1_infoblock(struct net_device *dev, u_char count, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char len = (*p & BLOCK_LEN)+1; - - /* Recursively figure out the info blocks */ - if (--count > lp->tcount) { - if (*(p+len) < 128) { - return dc_infoblock[COMPACT](dev, count, p+len); - } else { - return dc_infoblock[*(p+len+1)](dev, count, p+len); - } - } - - p += 2; - if (lp->state == INITIALISED) { - lp->ibn = 1; - lp->active = *p++; - lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1); - lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1); - lp->phy[lp->active].mc = TWIDDLE(p); p += 2; - lp->phy[lp->active].ana = TWIDDLE(p); p += 2; - lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; - lp->phy[lp->active].ttm = TWIDDLE(p); - return 0; - } else if ((lp->media == INIT) && (lp->timeout < 0)) { - lp->ibn = 1; - lp->active = *p; - lp->infoblock_csr6 = OMR_MII_100; - lp->useMII = TRUE; - lp->infoblock_media = ANS; - - de4x5_switch_mac_port(dev); - } - - return dc21140m_autoconf(dev); -} - -static int -type2_infoblock(struct net_device *dev, u_char count, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char len = (*p & BLOCK_LEN)+1; - - /* Recursively figure out the info blocks */ - if (--count > lp->tcount) { - if (*(p+len) < 128) { - return dc_infoblock[COMPACT](dev, count, p+len); - } else { - return dc_infoblock[*(p+len+1)](dev, count, p+len); - } - } - - if ((lp->media == INIT) && (lp->timeout < 0)) { - lp->ibn = 2; - lp->active = 0; - p += 2; - lp->infoblock_media = (*p) & MEDIA_CODE; - - if ((*p++) & EXT_FIELD) { - lp->cache.csr13 = TWIDDLE(p); p += 2; - lp->cache.csr14 = TWIDDLE(p); p += 2; - lp->cache.csr15 = TWIDDLE(p); p += 2; - } else { - lp->cache.csr13 = CSR13; - lp->cache.csr14 = CSR14; - lp->cache.csr15 = CSR15; - } - lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2; - lp->cache.gep = ((s32)(TWIDDLE(p)) << 16); - lp->infoblock_csr6 = OMR_SIA; - lp->useMII = FALSE; - - de4x5_switch_mac_port(dev); - } - - return dc2114x_autoconf(dev); -} - -static int -type3_infoblock(struct net_device *dev, u_char count, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char len = (*p & BLOCK_LEN)+1; - - /* Recursively figure out the info blocks */ - if (--count > lp->tcount) { - if (*(p+len) < 128) { - return dc_infoblock[COMPACT](dev, count, p+len); - } else { - return dc_infoblock[*(p+len+1)](dev, count, p+len); - } - } - - p += 2; - if (lp->state == INITIALISED) { - lp->ibn = 3; - lp->active = *p++; - if (MOTO_SROM_BUG) lp->active = 0; - lp->phy[lp->active].gep = (*p ? p : 0); p += (2 * (*p) + 1); - lp->phy[lp->active].rst = (*p ? p : 0); p += (2 * (*p) + 1); - lp->phy[lp->active].mc = TWIDDLE(p); p += 2; - lp->phy[lp->active].ana = TWIDDLE(p); p += 2; - lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; - lp->phy[lp->active].ttm = TWIDDLE(p); p += 2; - lp->phy[lp->active].mci = *p; - return 0; - } else if ((lp->media == INIT) && (lp->timeout < 0)) { - lp->ibn = 3; - lp->active = *p; - if (MOTO_SROM_BUG) lp->active = 0; - lp->infoblock_csr6 = OMR_MII_100; - lp->useMII = TRUE; - lp->infoblock_media = ANS; - - de4x5_switch_mac_port(dev); - } - - return dc2114x_autoconf(dev); -} - -static int -type4_infoblock(struct net_device *dev, u_char count, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char flags, csr6, len = (*p & BLOCK_LEN)+1; - - /* Recursively figure out the info blocks */ - if (--count > lp->tcount) { - if (*(p+len) < 128) { - return dc_infoblock[COMPACT](dev, count, p+len); - } else { - return dc_infoblock[*(p+len+1)](dev, count, p+len); - } - } - - if ((lp->media == INIT) && (lp->timeout < 0)) { - lp->ibn = 4; - lp->active = 0; - p+=2; - lp->infoblock_media = (*p++) & MEDIA_CODE; - lp->cache.csr13 = CSR13; /* Hard coded defaults */ - lp->cache.csr14 = CSR14; - lp->cache.csr15 = CSR15; - lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2; - lp->cache.gep = ((s32)(TWIDDLE(p)) << 16); p += 2; - csr6 = *p++; - flags = *p++; - - lp->asBitValid = (flags & 0x80) ? 0 : -1; - lp->defMedium = (flags & 0x40) ? -1 : 0; - lp->asBit = 1 << ((csr6 >> 1) & 0x07); - lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; - lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); - lp->useMII = FALSE; - - de4x5_switch_mac_port(dev); - } - - return dc2114x_autoconf(dev); -} - -/* -** This block type provides information for resetting external devices -** (chips) through the General Purpose Register. -*/ -static int -type5_infoblock(struct net_device *dev, u_char count, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char len = (*p & BLOCK_LEN)+1; - - /* Recursively figure out the info blocks */ - if (--count > lp->tcount) { - if (*(p+len) < 128) { - return dc_infoblock[COMPACT](dev, count, p+len); - } else { - return dc_infoblock[*(p+len+1)](dev, count, p+len); - } - } - - /* Must be initializing to run this code */ - if ((lp->state == INITIALISED) || (lp->media == INIT)) { - p+=2; - lp->rst = p; - srom_exec(dev, lp->rst); - } - - return DE4X5_AUTOSENSE_MS; -} - -/* -** MII Read/Write -*/ - -static int -mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr) -{ - mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */ - mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */ - mii_wdata(MII_STRD, 4, ioaddr); /* SFD and Read operation */ - mii_address(phyaddr, ioaddr); /* PHY address to be accessed */ - mii_address(phyreg, ioaddr); /* PHY Register to read */ - mii_ta(MII_STRD, ioaddr); /* Turn around time - 2 MDC */ - - return mii_rdata(ioaddr); /* Read data */ -} - -static void -mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr) -{ - mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */ - mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */ - mii_wdata(MII_STWR, 4, ioaddr); /* SFD and Write operation */ - mii_address(phyaddr, ioaddr); /* PHY address to be accessed */ - mii_address(phyreg, ioaddr); /* PHY Register to write */ - mii_ta(MII_STWR, ioaddr); /* Turn around time - 2 MDC */ - data = mii_swap(data, 16); /* Swap data bit ordering */ - mii_wdata(data, 16, ioaddr); /* Write data */ - - return; -} - -static int -mii_rdata(u_long ioaddr) -{ - int i; - s32 tmp = 0; - - for (i=0; i<16; i++) { - tmp <<= 1; - tmp |= getfrom_mii(MII_MRD | MII_RD, ioaddr); - } - - return tmp; -} - -static void -mii_wdata(int data, int len, u_long ioaddr) -{ - int i; - - for (i=0; i>= 1; - } - - return; -} - -static void -mii_address(u_char addr, u_long ioaddr) -{ - int i; - - addr = mii_swap(addr, 5); - for (i=0; i<5; i++) { - sendto_mii(MII_MWR | MII_WR, addr, ioaddr); - addr >>= 1; - } - - return; -} - -static void -mii_ta(u_long rw, u_long ioaddr) -{ - if (rw == MII_STWR) { - sendto_mii(MII_MWR | MII_WR, 1, ioaddr); - sendto_mii(MII_MWR | MII_WR, 0, ioaddr); - } else { - getfrom_mii(MII_MRD | MII_RD, ioaddr); /* Tri-state MDIO */ - } - - return; -} - -static int -mii_swap(int data, int len) -{ - int i, tmp = 0; - - for (i=0; i>= 1; - } - - return tmp; -} - -static void -sendto_mii(u32 command, int data, u_long ioaddr) -{ - u32 j; - - j = (data & 1) << 17; - outl(command | j, ioaddr); - udelay(1); - outl(command | MII_MDC | j, ioaddr); - udelay(1); - - return; -} - -static int -getfrom_mii(u32 command, u_long ioaddr) -{ - outl(command, ioaddr); - udelay(1); - outl(command | MII_MDC, ioaddr); - udelay(1); - - return ((inl(ioaddr) >> 19) & 1); -} - -/* -** Here's 3 ways to calculate the OUI from the ID registers. -*/ -static int -mii_get_oui(u_char phyaddr, u_long ioaddr) -{ -/* - union { - u_short reg; - u_char breg[2]; - } a; - int i, r2, r3, ret=0;*/ - int r2, r3; - - /* Read r2 and r3 */ - r2 = mii_rd(MII_ID0, phyaddr, ioaddr); - r3 = mii_rd(MII_ID1, phyaddr, ioaddr); - /* SEEQ and Cypress way * / - / * Shuffle r2 and r3 * / - a.reg=0; - r3 = ((r3>>10)|(r2<<6))&0x0ff; - r2 = ((r2>>2)&0x3fff); - - / * Bit reverse r3 * / - for (i=0;i<8;i++) { - ret<<=1; - ret |= (r3&1); - r3>>=1; - } - - / * Bit reverse r2 * / - for (i=0;i<16;i++) { - a.reg<<=1; - a.reg |= (r2&1); - r2>>=1; - } - - / * Swap r2 bytes * / - i=a.breg[0]; - a.breg[0]=a.breg[1]; - a.breg[1]=i; - - return ((a.reg<<8)|ret); */ /* SEEQ and Cypress way */ -/* return ((r2<<6)|(u_int)(r3>>10)); */ /* NATIONAL and BROADCOM way */ - return r2; /* (I did it) My way */ -} - -/* -** The SROM spec forces us to search addresses [1-31 0]. Bummer. -*/ -static int -mii_get_phy(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table); - int id; - - lp->active = 0; - lp->useMII = TRUE; - - /* Search the MII address space for possible PHY devices */ - for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(++i)%DE4X5_MAX_MII) { - lp->phy[lp->active].addr = i; - if (i==0) n++; /* Count cycles */ - while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */ - id = mii_get_oui(i, DE4X5_MII); - if ((id == 0) || (id == 65535)) continue; /* Valid ID? */ - for (j=0; jphy[k].id && (k < DE4X5_MAX_PHY); k++); - if (k < DE4X5_MAX_PHY) { - memcpy((char *)&lp->phy[k], - (char *)&phy_info[j], sizeof(struct phy_table)); - lp->phy[k].addr = i; - lp->mii_cnt++; - lp->active++; - } else { - goto purgatory; /* Stop the search */ - } - break; - } - if ((j == limit) && (i < DE4X5_MAX_MII)) { - for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++); - lp->phy[k].addr = i; - lp->phy[k].id = id; - lp->phy[k].spd.reg = GENERIC_REG; /* ANLPA register */ - lp->phy[k].spd.mask = GENERIC_MASK; /* 100Mb/s technologies */ - lp->phy[k].spd.value = GENERIC_VALUE; /* TX & T4, H/F Duplex */ - lp->mii_cnt++; - lp->active++; - printk("%s: Using generic MII device control. If the board doesn't operate, \nplease mail the following dump to the author:\n", dev->name); - j = de4x5_debug; - de4x5_debug |= DEBUG_MII; - de4x5_dbg_mii(dev, k); - de4x5_debug = j; - printk("\n"); - } - } - purgatory: - lp->active = 0; - if (lp->phy[0].id) { /* Reset the PHY devices */ - for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/ - mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII); - while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST); - - de4x5_dbg_mii(dev, k); - } - } - if (!lp->mii_cnt) lp->useMII = FALSE; - - return lp->mii_cnt; -} - -static char * -build_setup_frame(struct net_device *dev, int mode) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int i; - char *pa = lp->setup_frame; - - /* Initialise the setup frame */ - if (mode == ALL) { - memset(lp->setup_frame, 0, SETUP_FRAME_LEN); - } - - if (lp->setup_f == HASH_PERF) { - for (pa=lp->setup_frame+IMPERF_PA_OFFSET, i=0; idev_addr[i]; /* Host address */ - if (i & 0x01) pa += 2; - } - *(lp->setup_frame + (HASH_TABLE_LEN >> 3) - 3) = 0x80; - } else { - for (i=0; idev_addr[i]; - if (i & 0x01) pa += 4; - } - for (i=0; ipriv; - - del_timer(&lp->timer); - - return; -} - -static long -de4x5_switch_mac_port(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 omr; - - STOP_DE4X5; - - /* Assert the OMR_PS bit in CSR6 */ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | - OMR_FDX)); - omr |= lp->infoblock_csr6; - if (omr & OMR_PS) omr |= OMR_HBD; - outl(omr, DE4X5_OMR); - - /* Soft Reset */ - RESET_DE4X5; - - /* Restore the GEP - especially for COMPACT and Type 0 Infoblocks */ - if (lp->chipset == DC21140) { - gep_wr(lp->cache.gepc, dev); - gep_wr(lp->cache.gep, dev); - } else if ((lp->chipset & ~0x0ff) == DC2114x) { - reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15); - } - - /* Restore CSR6 */ - outl(omr, DE4X5_OMR); - - /* Reset CSR8 */ - inl(DE4X5_MFC); - - return omr; -} - -static void -gep_wr(s32 data, struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if (lp->chipset == DC21140) { - outl(data, DE4X5_GEP); - } else if ((lp->chipset & ~0x00ff) == DC2114x) { - outl((data<<16) | lp->cache.csr15, DE4X5_SIGR); - } - - return; -} - -static int -gep_rd(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if (lp->chipset == DC21140) { - return inl(DE4X5_GEP); - } else if ((lp->chipset & ~0x00ff) == DC2114x) { - return (inl(DE4X5_SIGR) & 0x000fffff); - } - - return 0; -} - -static void -timeout(struct net_device *dev, void (*fn)(u_long data), u_long data, u_long msec) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int dt; - - /* First, cancel any pending timer events */ - del_timer(&lp->timer); - - /* Convert msec to ticks */ - dt = (msec * HZ) / 1000; - if (dt==0) dt=1; - - /* Set up timer */ - lp->timer.expires = jiffies + dt; - lp->timer.function = fn; - lp->timer.data = data; - add_timer(&lp->timer); - - return; -} - -static void -yawn(struct net_device *dev, int state) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return; - - if(lp->bus == EISA) { - switch(state) { - case WAKEUP: - outb(WAKEUP, PCI_CFPM); - mdelay(10); - break; - - case SNOOZE: - outb(SNOOZE, PCI_CFPM); - break; - - case SLEEP: - outl(0, DE4X5_SICR); - outb(SLEEP, PCI_CFPM); - break; - } - } else { - switch(state) { - case WAKEUP: - pcibios_write_config_byte(lp->bus_num, lp->device << 3, - PCI_CFDA_PSM, WAKEUP); - mdelay(10); - break; - - case SNOOZE: - pcibios_write_config_byte(lp->bus_num, lp->device << 3, - PCI_CFDA_PSM, SNOOZE); - break; - - case SLEEP: - outl(0, DE4X5_SICR); - pcibios_write_config_byte(lp->bus_num, lp->device << 3, - PCI_CFDA_PSM, SLEEP); - break; - } - } - - return; -} - -static void -de4x5_parse_params(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - char *p, *q, t; - - lp->params.fdx = 0; - lp->params.autosense = AUTO; - - if (args == NULL) return; - - if ((p = strstr(args, dev->name))) { - if (!(q = strstr(p+strlen(dev->name), "eth"))) q = p + strlen(p); - t = *q; - *q = '\0'; - -#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) - if (strstr(p, "force_eisa") || strstr(p, "FORCE_EISA")) forceEISA = 1; -#endif - if (strstr(p, "fdx") || strstr(p, "FDX")) lp->params.fdx = 1; - - if (strstr(p, "autosense") || strstr(p, "AUTOSENSE")) { - if (strstr(p, "TP")) { - lp->params.autosense = TP; - } else if (strstr(p, "TP_NW")) { - lp->params.autosense = TP_NW; - } else if (strstr(p, "BNC")) { - lp->params.autosense = BNC; - } else if (strstr(p, "AUI")) { - lp->params.autosense = AUI; - } else if (strstr(p, "BNC_AUI")) { - lp->params.autosense = BNC; - } else if (strstr(p, "10Mb")) { - lp->params.autosense = _10Mb; - } else if (strstr(p, "100Mb")) { - lp->params.autosense = _100Mb; - } else if (strstr(p, "AUTO")) { - lp->params.autosense = AUTO; - } - } - *q = t; - } - - return; -} - -static void -de4x5_dbg_open(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int i; - - if (de4x5_debug & DEBUG_OPEN) { - printk("%s: de4x5 opening with irq %d\n",dev->name,dev->irq); - printk("\tphysical address: "); - for (i=0;i<6;i++) { - printk("%2.2x:",(short)dev->dev_addr[i]); - } - printk("\n"); - printk("Descriptor head addresses:\n"); - printk("\t0x%8.8lx 0x%8.8lx\n",(u_long)lp->rx_ring,(u_long)lp->tx_ring); - printk("Descriptor addresses:\nRX: "); - for (i=0;irxRingSize-1;i++){ - if (i < 3) { - printk("0x%8.8lx ",(u_long)&lp->rx_ring[i].status); - } - } - printk("...0x%8.8lx\n",(u_long)&lp->rx_ring[i].status); - printk("TX: "); - for (i=0;itxRingSize-1;i++){ - if (i < 3) { - printk("0x%8.8lx ", (u_long)&lp->tx_ring[i].status); - } - } - printk("...0x%8.8lx\n", (u_long)&lp->tx_ring[i].status); - printk("Descriptor buffers:\nRX: "); - for (i=0;irxRingSize-1;i++){ - if (i < 3) { - printk("0x%8.8x ",le32_to_cpu(lp->rx_ring[i].buf)); - } - } - printk("...0x%8.8x\n",le32_to_cpu(lp->rx_ring[i].buf)); - printk("TX: "); - for (i=0;itxRingSize-1;i++){ - if (i < 3) { - printk("0x%8.8x ", le32_to_cpu(lp->tx_ring[i].buf)); - } - } - printk("...0x%8.8x\n", le32_to_cpu(lp->tx_ring[i].buf)); - printk("Ring size: \nRX: %d\nTX: %d\n", - (short)lp->rxRingSize, - (short)lp->txRingSize); - } - - return; -} - -static void -de4x5_dbg_mii(struct net_device *dev, int k) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if (de4x5_debug & DEBUG_MII) { - printk("\nMII device address: %d\n", lp->phy[k].addr); - printk("MII CR: %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII)); - printk("MII SR: %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII)); - printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII)); - printk("MII ID1: %x\n",mii_rd(MII_ID1,lp->phy[k].addr,DE4X5_MII)); - if (lp->phy[k].id != BROADCOM_T4) { - printk("MII ANA: %x\n",mii_rd(0x04,lp->phy[k].addr,DE4X5_MII)); - printk("MII ANC: %x\n",mii_rd(0x05,lp->phy[k].addr,DE4X5_MII)); - } - printk("MII 16: %x\n",mii_rd(0x10,lp->phy[k].addr,DE4X5_MII)); - if (lp->phy[k].id != BROADCOM_T4) { - printk("MII 17: %x\n",mii_rd(0x11,lp->phy[k].addr,DE4X5_MII)); - printk("MII 18: %x\n",mii_rd(0x12,lp->phy[k].addr,DE4X5_MII)); - } else { - printk("MII 20: %x\n",mii_rd(0x14,lp->phy[k].addr,DE4X5_MII)); - } - } - - return; -} - -static void -de4x5_dbg_media(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - - if (lp->media != lp->c_media) { - if (de4x5_debug & DEBUG_MEDIA) { - printk("%s: media is %s%s\n", dev->name, - (lp->media == NC ? "unconnected, link down or incompatible connection" : - (lp->media == TP ? "TP" : - (lp->media == ANS ? "TP/Nway" : - (lp->media == BNC ? "BNC" : - (lp->media == AUI ? "AUI" : - (lp->media == BNC_AUI ? "BNC/AUI" : - (lp->media == EXT_SIA ? "EXT SIA" : - (lp->media == _100Mb ? "100Mb/s" : - (lp->media == _10Mb ? "10Mb/s" : - "???" - ))))))))), (lp->fdx?" full duplex.":".")); - } - lp->c_media = lp->media; - } - - return; -} - -static void -de4x5_dbg_srom(struct de4x5_srom *p) -{ - int i; - - if (de4x5_debug & DEBUG_SROM) { - printk("Sub-system Vendor ID: %04x\n", *((u_short *)p->sub_vendor_id)); - printk("Sub-system ID: %04x\n", *((u_short *)p->sub_system_id)); - printk("ID Block CRC: %02x\n", (u_char)(p->id_block_crc)); - printk("SROM version: %02x\n", (u_char)(p->version)); - printk("# controllers: %02x\n", (u_char)(p->num_controllers)); - - printk("Hardware Address: "); - for (i=0;iieee_addr+i)); - } - printk("%02x\n", (u_char)*(p->ieee_addr+i)); - printk("CRC checksum: %04x\n", (u_short)(p->chksum)); - for (i=0; i<64; i++) { - printk("%3d %04x\n", i<<1, (u_short)*((u_short *)p+i)); - } - } - - return; -} - -static void -de4x5_dbg_rx(struct sk_buff *skb, int len) -{ - int i, j; - - if (de4x5_debug & DEBUG_RX) { - printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n", - (u_char)skb->data[0], - (u_char)skb->data[1], - (u_char)skb->data[2], - (u_char)skb->data[3], - (u_char)skb->data[4], - (u_char)skb->data[5], - (u_char)skb->data[6], - (u_char)skb->data[7], - (u_char)skb->data[8], - (u_char)skb->data[9], - (u_char)skb->data[10], - (u_char)skb->data[11], - (u_char)skb->data[12], - (u_char)skb->data[13], - len); - for (j=0; len>0;j+=16, len-=16) { - printk(" %03x: ",j); - for (i=0; i<16 && idata[i+j]); - } - printk("\n"); - } - } - - return; -} - -/* -** Perform IOCTL call functions here. Some are privileged operations and the -** effective uid is checked in those cases. In the normal course of events -** this function is only used for my testing. -*/ -static int -de4x5_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - struct de4x5_ioctl *ioc = (struct de4x5_ioctl *) &rq->ifr_data; - u_long iobase = dev->base_addr; - int i, j, status = 0; - s32 omr; - union { - u8 addr[144]; - u16 sval[72]; - u32 lval[36]; - } tmp; - u_long flags = 0; - - switch(ioc->cmd) { - case DE4X5_GET_HWADDR: /* Get the hardware address */ - ioc->len = ETH_ALEN; - for (i=0; idev_addr[i]; - } - if (copy_to_user(ioc->data, tmp.addr, ioc->len)) return -EFAULT; - break; - - case DE4X5_SET_HWADDR: /* Set the hardware address */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (copy_from_user(tmp.addr, ioc->data, ETH_ALEN)) return -EFAULT; - if (netif_queue_stopped(dev)) - return -EBUSY; - netif_stop_queue(dev); - for (i=0; idev_addr[i] = tmp.addr[i]; - } - build_setup_frame(dev, PHYS_ADDR_ONLY); - /* Set up the descriptor and give ownership to the card */ - load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | - SETUP_FRAME_LEN, (struct sk_buff *)1); - lp->tx_new = (++lp->tx_new) % lp->txRingSize; - outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */ - netif_wake_queue(dev); /* Unlock the TX ring */ - break; - - case DE4X5_SET_PROM: /* Set Promiscuous Mode */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - omr = inl(DE4X5_OMR); - omr |= OMR_PR; - outl(omr, DE4X5_OMR); - dev->flags |= IFF_PROMISC; - break; - - case DE4X5_CLR_PROM: /* Clear Promiscuous Mode */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - omr = inl(DE4X5_OMR); - omr &= ~OMR_PR; - outl(omr, DE4X5_OMR); - dev->flags &= ~IFF_PROMISC; - break; - - case DE4X5_SAY_BOO: /* Say "Boo!" to the kernel log file */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - printk("%s: Boo!\n", dev->name); - break; - - case DE4X5_MCA_EN: /* Enable pass all multicast addressing */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - omr = inl(DE4X5_OMR); - omr |= OMR_PM; - outl(omr, DE4X5_OMR); - break; - - case DE4X5_GET_STATS: /* Get the driver statistics */ - { - struct pkt_stats statbuf; - ioc->len = sizeof(statbuf); - spin_lock_irqsave(&lp->lock, flags); - memcpy(&statbuf, &lp->pktStats, ioc->len); - spin_unlock_irqrestore(&lp->lock, flags); - if (copy_to_user(ioc->data, &statbuf, ioc->len)) - return -EFAULT; - break; - } - case DE4X5_CLR_STATS: /* Zero out the driver statistics */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - spin_lock_irqsave(&lp->lock, flags); - memset(&lp->pktStats, 0, sizeof(lp->pktStats)); - spin_unlock_irqrestore(&lp->lock, flags); - break; - - case DE4X5_GET_OMR: /* Get the OMR Register contents */ - tmp.addr[0] = inl(DE4X5_OMR); - if (copy_to_user(ioc->data, tmp.addr, 1)) return -EFAULT; - break; - - case DE4X5_SET_OMR: /* Set the OMR Register contents */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (copy_from_user(tmp.addr, ioc->data, 1)) return -EFAULT; - outl(tmp.addr[0], DE4X5_OMR); - break; - - case DE4X5_GET_REG: /* Get the DE4X5 Registers */ - j = 0; - tmp.lval[0] = inl(DE4X5_STS); j+=4; - tmp.lval[1] = inl(DE4X5_BMR); j+=4; - tmp.lval[2] = inl(DE4X5_IMR); j+=4; - tmp.lval[3] = inl(DE4X5_OMR); j+=4; - tmp.lval[4] = inl(DE4X5_SISR); j+=4; - tmp.lval[5] = inl(DE4X5_SICR); j+=4; - tmp.lval[6] = inl(DE4X5_STRR); j+=4; - tmp.lval[7] = inl(DE4X5_SIGR); j+=4; - ioc->len = j; - if (copy_to_user(ioc->data, tmp.addr, ioc->len)) return -EFAULT; - break; - -#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */ -/* - case DE4X5_DUMP: - j = 0; - tmp.addr[j++] = dev->irq; - for (i=0; idev_addr[i]; - } - tmp.addr[j++] = lp->rxRingSize; - tmp.lval[j>>2] = (long)lp->rx_ring; j+=4; - tmp.lval[j>>2] = (long)lp->tx_ring; j+=4; - - for (i=0;irxRingSize-1;i++){ - if (i < 3) { - tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4; - } - } - tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4; - for (i=0;itxRingSize-1;i++){ - if (i < 3) { - tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4; - } - } - tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4; - - for (i=0;irxRingSize-1;i++){ - if (i < 3) { - tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; - } - } - tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; - for (i=0;itxRingSize-1;i++){ - if (i < 3) { - tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; - } - } - tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; - - for (i=0;irxRingSize;i++){ - tmp.lval[j>>2] = le32_to_cpu(lp->rx_ring[i].status); j+=4; - } - for (i=0;itxRingSize;i++){ - tmp.lval[j>>2] = le32_to_cpu(lp->tx_ring[i].status); j+=4; - } - - tmp.lval[j>>2] = inl(DE4X5_BMR); j+=4; - tmp.lval[j>>2] = inl(DE4X5_TPD); j+=4; - tmp.lval[j>>2] = inl(DE4X5_RPD); j+=4; - tmp.lval[j>>2] = inl(DE4X5_RRBA); j+=4; - tmp.lval[j>>2] = inl(DE4X5_TRBA); j+=4; - tmp.lval[j>>2] = inl(DE4X5_STS); j+=4; - tmp.lval[j>>2] = inl(DE4X5_OMR); j+=4; - tmp.lval[j>>2] = inl(DE4X5_IMR); j+=4; - tmp.lval[j>>2] = lp->chipset; j+=4; - if (lp->chipset == DC21140) { - tmp.lval[j>>2] = gep_rd(dev); j+=4; - } else { - tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4; - tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4; - tmp.lval[j>>2] = inl(DE4X5_STRR); j+=4; - tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4; - } - tmp.lval[j>>2] = lp->phy[lp->active].id; j+=4; - if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { - tmp.lval[j>>2] = lp->active; j+=4; - tmp.lval[j>>2]=mii_rd(MII_CR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - tmp.lval[j>>2]=mii_rd(MII_SR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - tmp.lval[j>>2]=mii_rd(MII_ID0,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - tmp.lval[j>>2]=mii_rd(MII_ID1,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - if (lp->phy[lp->active].id != BROADCOM_T4) { - tmp.lval[j>>2]=mii_rd(MII_ANA,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - tmp.lval[j>>2]=mii_rd(MII_ANLPA,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - } - tmp.lval[j>>2]=mii_rd(0x10,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - if (lp->phy[lp->active].id != BROADCOM_T4) { - tmp.lval[j>>2]=mii_rd(0x11,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - tmp.lval[j>>2]=mii_rd(0x12,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - } else { - tmp.lval[j>>2]=mii_rd(0x14,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - } - } - - tmp.addr[j++] = lp->txRingSize; - tmp.addr[j++] = netif_queue_stopped(dev); - - ioc->len = j; - if (copy_to_user(ioc->data, tmp.addr, ioc->len)) return -EFAULT; - break; - -*/ - default: - return -EOPNOTSUPP; - } - - return status; -} - -#ifdef MODULE -/* -** Note now that module autoprobing is allowed under EISA and PCI. The -** IRQ lines will not be auto-detected; instead I'll rely on the BIOSes -** to "do the right thing". -*/ -#define LP(a) ((struct de4x5_private *)(a)) -static struct net_device *mdev = NULL; -static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED */ -MODULE_PARM(io, "i"); -MODULE_PARM_DESC(io, "de4x5 I/O base address"); - -int -init_module(void) -{ - int i, num, status = -EIO; - struct net_device *p; - - num = count_adapters(); - - for (i=0; ipriv; - if (lp) { - release_region(p->base_addr, (lp->bus == PCI ? - DE4X5_PCI_TOTAL_SIZE : - DE4X5_EISA_TOTAL_SIZE)); - if (lp->cache.priv) { /* Private area allocated? */ - kfree(lp->cache.priv); /* Free the private area */ - } - if (lp->rx_ring) { - pci_free_consistent(lp->pdev, lp->dma_size, lp->rx_ring, - lp->dma_rings); - } - } - kfree(p); - } else { - status = 0; /* At least one adapter will work */ - lastModule = p; - } - } - - return status; -} - -void -cleanup_module(void) -{ - while (mdev != NULL) { - mdev = unlink_modules(mdev); - } - - return; -} - -static struct net_device * -unlink_modules(struct net_device *p) -{ - struct net_device *next = NULL; - - if (p->priv) { /* Private areas allocated? */ - struct de4x5_private *lp = (struct de4x5_private *)p->priv; - - next = lp->next_module; - if (lp->rx_ring) { - pci_free_consistent(lp->pdev, lp->dma_size, lp->rx_ring, - lp->dma_rings); - } - release_region(p->base_addr, (lp->bus == PCI ? - DE4X5_PCI_TOTAL_SIZE : - DE4X5_EISA_TOTAL_SIZE)); - kfree(lp->cache.priv); /* Free the private area */ - } - unregister_netdev(p); - kfree(p); /* Free the device structure */ - - return next; -} - -static int -count_adapters(void) -{ - int i, j=0; - u_short vendor; - u_int class = DE4X5_CLASS_CODE; - u_int device; - -#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) - char name[DE4X5_STRLEN]; - u_long iobase = 0x1000; - - for (i=1; ivendor; - device = pdev->device << 8; - if (is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x) j++; - } - - return j; -} - -/* -** If at end of eth device list and can't use current entry, malloc -** one up. If memory could not be allocated, print an error message. -*/ -static struct net_device * __init -insert_device(struct net_device *dev, u_long iobase, int (*init)(struct net_device *)) -{ - struct net_device *new; - - new = (struct net_device *)kmalloc(sizeof(struct net_device), GFP_KERNEL); - if (new == NULL) { - printk("de4x5.c: Device not initialised, insufficient memory\n"); - return NULL; - } else { - memset((char *)new, 0, sizeof(struct net_device)); - new->base_addr = iobase; /* assign the io address */ - new->init = init; /* initialisation routine */ - } - - return new; -} - -#endif /* MODULE */ - - -/* - * Local variables: - * - * Delete -DMODVERSIONS below if you didn't define this in your kernel - * - * compile-command: "gcc -D__KERNEL__ -DMODULE -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -DMODVERSIONS -include /linux/include/linux/modversions.h -c de4x5.c" - * End: - */ diff -Nru a/drivers/net/de4x5.h b/drivers/net/de4x5.h --- a/drivers/net/de4x5.h Thu Mar 7 18:17:45 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1031 +0,0 @@ -/* - Copyright 1994 Digital Equipment Corporation. - - This software may be used and distributed according to the terms of the - GNU General Public License, incorporated herein by reference. - - The author may be reached as davies@wanton.lkg.dec.com or Digital - Equipment Corporation, 550 King Street, Littleton MA 01460. - - ========================================================================= -*/ - -/* -** DC21040 CSR<1..15> Register Address Map -*/ -#define DE4X5_BMR iobase+(0x000 << lp->bus) /* Bus Mode Register */ -#define DE4X5_TPD iobase+(0x008 << lp->bus) /* Transmit Poll Demand Reg */ -#define DE4X5_RPD iobase+(0x010 << lp->bus) /* Receive Poll Demand Reg */ -#define DE4X5_RRBA iobase+(0x018 << lp->bus) /* RX Ring Base Address Reg */ -#define DE4X5_TRBA iobase+(0x020 << lp->bus) /* TX Ring Base Address Reg */ -#define DE4X5_STS iobase+(0x028 << lp->bus) /* Status Register */ -#define DE4X5_OMR iobase+(0x030 << lp->bus) /* Operation Mode Register */ -#define DE4X5_IMR iobase+(0x038 << lp->bus) /* Interrupt Mask Register */ -#define DE4X5_MFC iobase+(0x040 << lp->bus) /* Missed Frame Counter */ -#define DE4X5_APROM iobase+(0x048 << lp->bus) /* Ethernet Address PROM */ -#define DE4X5_BROM iobase+(0x048 << lp->bus) /* Boot ROM Register */ -#define DE4X5_SROM iobase+(0x048 << lp->bus) /* Serial ROM Register */ -#define DE4X5_MII iobase+(0x048 << lp->bus) /* MII Interface Register */ -#define DE4X5_DDR iobase+(0x050 << lp->bus) /* Data Diagnostic Register */ -#define DE4X5_FDR iobase+(0x058 << lp->bus) /* Full Duplex Register */ -#define DE4X5_GPT iobase+(0x058 << lp->bus) /* General Purpose Timer Reg.*/ -#define DE4X5_GEP iobase+(0x060 << lp->bus) /* General Purpose Register */ -#define DE4X5_SISR iobase+(0x060 << lp->bus) /* SIA Status Register */ -#define DE4X5_SICR iobase+(0x068 << lp->bus) /* SIA Connectivity Register */ -#define DE4X5_STRR iobase+(0x070 << lp->bus) /* SIA TX/RX Register */ -#define DE4X5_SIGR iobase+(0x078 << lp->bus) /* SIA General Register */ - -/* -** EISA Register Address Map -*/ -#define EISA_ID iobase+0x0c80 /* EISA ID Registers */ -#define EISA_ID0 iobase+0x0c80 /* EISA ID Register 0 */ -#define EISA_ID1 iobase+0x0c81 /* EISA ID Register 1 */ -#define EISA_ID2 iobase+0x0c82 /* EISA ID Register 2 */ -#define EISA_ID3 iobase+0x0c83 /* EISA ID Register 3 */ -#define EISA_CR iobase+0x0c84 /* EISA Control Register */ -#define EISA_REG0 iobase+0x0c88 /* EISA Configuration Register 0 */ -#define EISA_REG1 iobase+0x0c89 /* EISA Configuration Register 1 */ -#define EISA_REG2 iobase+0x0c8a /* EISA Configuration Register 2 */ -#define EISA_REG3 iobase+0x0c8f /* EISA Configuration Register 3 */ -#define EISA_APROM iobase+0x0c90 /* Ethernet Address PROM */ - -/* -** PCI/EISA Configuration Registers Address Map -*/ -#define PCI_CFID iobase+0x0008 /* PCI Configuration ID Register */ -#define PCI_CFCS iobase+0x000c /* PCI Command/Status Register */ -#define PCI_CFRV iobase+0x0018 /* PCI Revision Register */ -#define PCI_CFLT iobase+0x001c /* PCI Latency Timer Register */ -#define PCI_CBIO iobase+0x0028 /* PCI Base I/O Register */ -#define PCI_CBMA iobase+0x002c /* PCI Base Memory Address Register */ -#define PCI_CBER iobase+0x0030 /* PCI Expansion ROM Base Address Reg. */ -#define PCI_CFIT iobase+0x003c /* PCI Configuration Interrupt Register */ -#define PCI_CFDA iobase+0x0040 /* PCI Driver Area Register */ -#define PCI_CFDD iobase+0x0041 /* PCI Driver Dependent Area Register */ -#define PCI_CFPM iobase+0x0043 /* PCI Power Management Area Register */ - -/* -** EISA Configuration Register 0 bit definitions -*/ -#define ER0_BSW 0x80 /* EISA Bus Slave Width, 1: 32 bits */ -#define ER0_BMW 0x40 /* EISA Bus Master Width, 1: 32 bits */ -#define ER0_EPT 0x20 /* EISA PREEMPT Time, 0: 23 BCLKs */ -#define ER0_ISTS 0x10 /* Interrupt Status (X) */ -#define ER0_LI 0x08 /* Latch Interrupts */ -#define ER0_INTL 0x06 /* INTerrupt Level */ -#define ER0_INTT 0x01 /* INTerrupt Type, 0: Level, 1: Edge */ - -/* -** EISA Configuration Register 1 bit definitions -*/ -#define ER1_IAM 0xe0 /* ISA Address Mode */ -#define ER1_IAE 0x10 /* ISA Addressing Enable */ -#define ER1_UPIN 0x0f /* User Pins */ - -/* -** EISA Configuration Register 2 bit definitions -*/ -#define ER2_BRS 0xc0 /* Boot ROM Size */ -#define ER2_BRA 0x3c /* Boot ROM Address <16:13> */ - -/* -** EISA Configuration Register 3 bit definitions -*/ -#define ER3_BWE 0x40 /* Burst Write Enable */ -#define ER3_BRE 0x04 /* Burst Read Enable */ -#define ER3_LSR 0x02 /* Local Software Reset */ - -/* -** PCI Configuration ID Register (PCI_CFID). The Device IDs are left -** shifted 8 bits to allow detection of DC21142 and DC21143 variants with -** the configuration revision register step number. -*/ -#define CFID_DID 0xff00 /* Device ID */ -#define CFID_VID 0x00ff /* Vendor ID */ -#define DC21040_DID 0x0200 /* Unique Device ID # */ -#define DC21040_VID 0x1011 /* DC21040 Manufacturer */ -#define DC21041_DID 0x1400 /* Unique Device ID # */ -#define DC21041_VID 0x1011 /* DC21041 Manufacturer */ -#define DC21140_DID 0x0900 /* Unique Device ID # */ -#define DC21140_VID 0x1011 /* DC21140 Manufacturer */ -#define DC2114x_DID 0x1900 /* Unique Device ID # */ -#define DC2114x_VID 0x1011 /* DC2114[23] Manufacturer */ - -/* -** Chipset defines -*/ -#define DC21040 DC21040_DID -#define DC21041 DC21041_DID -#define DC21140 DC21140_DID -#define DC2114x DC2114x_DID -#define DC21142 (DC2114x_DID | 0x0010) -#define DC21143 (DC2114x_DID | 0x0030) -#define DC2114x_BRK 0x0020 /* CFRV break between DC21142 & DC21143 */ - -#define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID)) -#define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID)) -#define is_DC21140 ((vendor == DC21140_VID) && (device == DC21140_DID)) -#define is_DC2114x ((vendor == DC2114x_VID) && (device == DC2114x_DID)) -#define is_DC21142 ((vendor == DC2114x_VID) && (device == DC21142)) -#define is_DC21143 ((vendor == DC2114x_VID) && (device == DC21143)) - -/* -** PCI Configuration Command/Status Register (PCI_CFCS) -*/ -#define CFCS_DPE 0x80000000 /* Detected Parity Error (S) */ -#define CFCS_SSE 0x40000000 /* Signal System Error (S) */ -#define CFCS_RMA 0x20000000 /* Receive Master Abort (S) */ -#define CFCS_RTA 0x10000000 /* Receive Target Abort (S) */ -#define CFCS_DST 0x06000000 /* DEVSEL Timing (S) */ -#define CFCS_DPR 0x01000000 /* Data Parity Report (S) */ -#define CFCS_FBB 0x00800000 /* Fast Back-To-Back (S) */ -#define CFCS_SEE 0x00000100 /* System Error Enable (C) */ -#define CFCS_PER 0x00000040 /* Parity Error Response (C) */ -#define CFCS_MO 0x00000004 /* Master Operation (C) */ -#define CFCS_MSA 0x00000002 /* Memory Space Access (C) */ -#define CFCS_IOSA 0x00000001 /* I/O Space Access (C) */ - -/* -** PCI Configuration Revision Register (PCI_CFRV) -*/ -#define CFRV_BC 0xff000000 /* Base Class */ -#define CFRV_SC 0x00ff0000 /* Subclass */ -#define CFRV_RN 0x000000f0 /* Revision Number */ -#define CFRV_SN 0x0000000f /* Step Number */ -#define BASE_CLASS 0x02000000 /* Indicates Network Controller */ -#define SUB_CLASS 0x00000000 /* Indicates Ethernet Controller */ -#define STEP_NUMBER 0x00000020 /* Increments for future chips */ -#define REV_NUMBER 0x00000003 /* 0x00, 0x01, 0x02, 0x03: Rev in Step */ -#define CFRV_MASK 0xffff0000 /* Register mask */ - -/* -** PCI Configuration Latency Timer Register (PCI_CFLT) -*/ -#define CFLT_BC 0x0000ff00 /* Latency Timer bits */ - -/* -** PCI Configuration Base I/O Address Register (PCI_CBIO) -*/ -#define CBIO_MASK -128 /* Base I/O Address Mask */ -#define CBIO_IOSI 0x00000001 /* I/O Space Indicator (RO, value is 1) */ - -/* -** PCI Configuration Card Information Structure Register (PCI_CCIS) -*/ -#define CCIS_ROMI 0xf0000000 /* ROM Image */ -#define CCIS_ASO 0x0ffffff8 /* Address Space Offset */ -#define CCIS_ASI 0x00000007 /* Address Space Indicator */ - -/* -** PCI Configuration Subsystem ID Register (PCI_SSID) -*/ -#define SSID_SSID 0xffff0000 /* Subsystem ID */ -#define SSID_SVID 0x0000ffff /* Subsystem Vendor ID */ - -/* -** PCI Configuration Expansion ROM Base Address Register (PCI_CBER) -*/ -#define CBER_MASK 0xfffffc00 /* Expansion ROM Base Address Mask */ -#define CBER_ROME 0x00000001 /* ROM Enable */ - -/* -** PCI Configuration Interrupt Register (PCI_CFIT) -*/ -#define CFIT_MXLT 0xff000000 /* MAX_LAT Value (0.25us periods) */ -#define CFIT_MNGT 0x00ff0000 /* MIN_GNT Value (0.25us periods) */ -#define CFIT_IRQP 0x0000ff00 /* Interrupt Pin */ -#define CFIT_IRQL 0x000000ff /* Interrupt Line */ - -/* -** PCI Configuration Power Management Area Register (PCI_CFPM) -*/ -#define SLEEP 0x80 /* Power Saving Sleep Mode */ -#define SNOOZE 0x40 /* Power Saving Snooze Mode */ -#define WAKEUP 0x00 /* Power Saving Wakeup */ - -#define PCI_CFDA_DSU 0x41 /* 8 bit Configuration Space Address */ -#define PCI_CFDA_PSM 0x43 /* 8 bit Configuration Space Address */ - -/* -** DC21040 Bus Mode Register (DE4X5_BMR) -*/ -#define BMR_RML 0x00200000 /* [Memory] Read Multiple */ -#define BMR_DBO 0x00100000 /* Descriptor Byte Ordering (Endian) */ -#define BMR_TAP 0x000e0000 /* Transmit Automatic Polling */ -#define BMR_DAS 0x00010000 /* Diagnostic Address Space */ -#define BMR_CAL 0x0000c000 /* Cache Alignment */ -#define BMR_PBL 0x00003f00 /* Programmable Burst Length */ -#define BMR_BLE 0x00000080 /* Big/Little Endian */ -#define BMR_DSL 0x0000007c /* Descriptor Skip Length */ -#define BMR_BAR 0x00000002 /* Bus ARbitration */ -#define BMR_SWR 0x00000001 /* Software Reset */ - - /* Timings here are for 10BASE-T/AUI only*/ -#define TAP_NOPOLL 0x00000000 /* No automatic polling */ -#define TAP_200US 0x00020000 /* TX automatic polling every 200us */ -#define TAP_800US 0x00040000 /* TX automatic polling every 800us */ -#define TAP_1_6MS 0x00060000 /* TX automatic polling every 1.6ms */ -#define TAP_12_8US 0x00080000 /* TX automatic polling every 12.8us */ -#define TAP_25_6US 0x000a0000 /* TX automatic polling every 25.6us */ -#define TAP_51_2US 0x000c0000 /* TX automatic polling every 51.2us */ -#define TAP_102_4US 0x000e0000 /* TX automatic polling every 102.4us */ - -#define CAL_NOUSE 0x00000000 /* Not used */ -#define CAL_8LONG 0x00004000 /* 8-longword alignment */ -#define CAL_16LONG 0x00008000 /* 16-longword alignment */ -#define CAL_32LONG 0x0000c000 /* 32-longword alignment */ - -#define PBL_0 0x00000000 /* DMA burst length = amount in RX FIFO */ -#define PBL_1 0x00000100 /* 1 longword DMA burst length */ -#define PBL_2 0x00000200 /* 2 longwords DMA burst length */ -#define PBL_4 0x00000400 /* 4 longwords DMA burst length */ -#define PBL_8 0x00000800 /* 8 longwords DMA burst length */ -#define PBL_16 0x00001000 /* 16 longwords DMA burst length */ -#define PBL_32 0x00002000 /* 32 longwords DMA burst length */ - -#define DSL_0 0x00000000 /* 0 longword / descriptor */ -#define DSL_1 0x00000004 /* 1 longword / descriptor */ -#define DSL_2 0x00000008 /* 2 longwords / descriptor */ -#define DSL_4 0x00000010 /* 4 longwords / descriptor */ -#define DSL_8 0x00000020 /* 8 longwords / descriptor */ -#define DSL_16 0x00000040 /* 16 longwords / descriptor */ -#define DSL_32 0x00000080 /* 32 longwords / descriptor */ - -/* -** DC21040 Transmit Poll Demand Register (DE4X5_TPD) -*/ -#define TPD 0x00000001 /* Transmit Poll Demand */ - -/* -** DC21040 Receive Poll Demand Register (DE4X5_RPD) -*/ -#define RPD 0x00000001 /* Receive Poll Demand */ - -/* -** DC21040 Receive Ring Base Address Register (DE4X5_RRBA) -*/ -#define RRBA 0xfffffffc /* RX Descriptor List Start Address */ - -/* -** DC21040 Transmit Ring Base Address Register (DE4X5_TRBA) -*/ -#define TRBA 0xfffffffc /* TX Descriptor List Start Address */ - -/* -** Status Register (DE4X5_STS) -*/ -#define STS_GPI 0x04000000 /* General Purpose Port Interrupt */ -#define STS_BE 0x03800000 /* Bus Error Bits */ -#define STS_TS 0x00700000 /* Transmit Process State */ -#define STS_RS 0x000e0000 /* Receive Process State */ -#define STS_NIS 0x00010000 /* Normal Interrupt Summary */ -#define STS_AIS 0x00008000 /* Abnormal Interrupt Summary */ -#define STS_ER 0x00004000 /* Early Receive */ -#define STS_FBE 0x00002000 /* Fatal Bus Error */ -#define STS_SE 0x00002000 /* System Error */ -#define STS_LNF 0x00001000 /* Link Fail */ -#define STS_FD 0x00000800 /* Full-Duplex Short Frame Received */ -#define STS_TM 0x00000800 /* Timer Expired (DC21041) */ -#define STS_ETI 0x00000400 /* Early Transmit Interrupt */ -#define STS_AT 0x00000400 /* AUI/TP Pin */ -#define STS_RWT 0x00000200 /* Receive Watchdog Time-Out */ -#define STS_RPS 0x00000100 /* Receive Process Stopped */ -#define STS_RU 0x00000080 /* Receive Buffer Unavailable */ -#define STS_RI 0x00000040 /* Receive Interrupt */ -#define STS_UNF 0x00000020 /* Transmit Underflow */ -#define STS_LNP 0x00000010 /* Link Pass */ -#define STS_ANC 0x00000010 /* Autonegotiation Complete */ -#define STS_TJT 0x00000008 /* Transmit Jabber Time-Out */ -#define STS_TU 0x00000004 /* Transmit Buffer Unavailable */ -#define STS_TPS 0x00000002 /* Transmit Process Stopped */ -#define STS_TI 0x00000001 /* Transmit Interrupt */ - -#define EB_PAR 0x00000000 /* Parity Error */ -#define EB_MA 0x00800000 /* Master Abort */ -#define EB_TA 0x01000000 /* Target Abort */ -#define EB_RES0 0x01800000 /* Reserved */ -#define EB_RES1 0x02000000 /* Reserved */ - -#define TS_STOP 0x00000000 /* Stopped */ -#define TS_FTD 0x00100000 /* Fetch Transmit Descriptor */ -#define TS_WEOT 0x00200000 /* Wait for End Of Transmission */ -#define TS_QDAT 0x00300000 /* Queue skb data into TX FIFO */ -#define TS_RES 0x00400000 /* Reserved */ -#define TS_SPKT 0x00500000 /* Setup Packet */ -#define TS_SUSP 0x00600000 /* Suspended */ -#define TS_CLTD 0x00700000 /* Close Transmit Descriptor */ - -#define RS_STOP 0x00000000 /* Stopped */ -#define RS_FRD 0x00020000 /* Fetch Receive Descriptor */ -#define RS_CEOR 0x00040000 /* Check for End of Receive Packet */ -#define RS_WFRP 0x00060000 /* Wait for Receive Packet */ -#define RS_SUSP 0x00080000 /* Suspended */ -#define RS_CLRD 0x000a0000 /* Close Receive Descriptor */ -#define RS_FLUSH 0x000c0000 /* Flush RX FIFO */ -#define RS_QRFS 0x000e0000 /* Queue RX FIFO into RX Skb */ - -#define INT_CANCEL 0x0001ffff /* For zeroing all interrupt sources */ - -/* -** Operation Mode Register (DE4X5_OMR) -*/ -#define OMR_SC 0x80000000 /* Special Capture Effect Enable */ -#define OMR_RA 0x40000000 /* Receive All */ -#define OMR_SDP 0x02000000 /* SD Polarity - MUST BE ASSERTED */ -#define OMR_SCR 0x01000000 /* Scrambler Mode */ -#define OMR_PCS 0x00800000 /* PCS Function */ -#define OMR_TTM 0x00400000 /* Transmit Threshold Mode */ -#define OMR_SF 0x00200000 /* Store and Forward */ -#define OMR_HBD 0x00080000 /* HeartBeat Disable */ -#define OMR_PS 0x00040000 /* Port Select */ -#define OMR_CA 0x00020000 /* Capture Effect Enable */ -#define OMR_BP 0x00010000 /* Back Pressure */ -#define OMR_TR 0x0000c000 /* Threshold Control Bits */ -#define OMR_ST 0x00002000 /* Start/Stop Transmission Command */ -#define OMR_FC 0x00001000 /* Force Collision Mode */ -#define OMR_OM 0x00000c00 /* Operating Mode */ -#define OMR_FDX 0x00000200 /* Full Duplex Mode */ -#define OMR_FKD 0x00000100 /* Flaky Oscillator Disable */ -#define OMR_PM 0x00000080 /* Pass All Multicast */ -#define OMR_PR 0x00000040 /* Promiscuous Mode */ -#define OMR_SB 0x00000020 /* Start/Stop Backoff Counter */ -#define OMR_IF 0x00000010 /* Inverse Filtering */ -#define OMR_PB 0x00000008 /* Pass Bad Frames */ -#define OMR_HO 0x00000004 /* Hash Only Filtering Mode */ -#define OMR_SR 0x00000002 /* Start/Stop Receive */ -#define OMR_HP 0x00000001 /* Hash/Perfect Receive Filtering Mode */ - -#define TR_72 0x00000000 /* Threshold set to 72 (128) bytes */ -#define TR_96 0x00004000 /* Threshold set to 96 (256) bytes */ -#define TR_128 0x00008000 /* Threshold set to 128 (512) bytes */ -#define TR_160 0x0000c000 /* Threshold set to 160 (1024) bytes */ - -#define OMR_DEF (OMR_SDP) -#define OMR_SIA (OMR_SDP | OMR_TTM) -#define OMR_SYM (OMR_SDP | OMR_SCR | OMR_PCS | OMR_HBD | OMR_PS) -#define OMR_MII_10 (OMR_SDP | OMR_TTM | OMR_PS) -#define OMR_MII_100 (OMR_SDP | OMR_HBD | OMR_PS) - -/* -** DC21040 Interrupt Mask Register (DE4X5_IMR) -*/ -#define IMR_GPM 0x04000000 /* General Purpose Port Mask */ -#define IMR_NIM 0x00010000 /* Normal Interrupt Summary Mask */ -#define IMR_AIM 0x00008000 /* Abnormal Interrupt Summary Mask */ -#define IMR_ERM 0x00004000 /* Early Receive Mask */ -#define IMR_FBM 0x00002000 /* Fatal Bus Error Mask */ -#define IMR_SEM 0x00002000 /* System Error Mask */ -#define IMR_LFM 0x00001000 /* Link Fail Mask */ -#define IMR_FDM 0x00000800 /* Full-Duplex (Short Frame) Mask */ -#define IMR_TMM 0x00000800 /* Timer Expired Mask (DC21041) */ -#define IMR_ETM 0x00000400 /* Early Transmit Interrupt Mask */ -#define IMR_ATM 0x00000400 /* AUI/TP Switch Mask */ -#define IMR_RWM 0x00000200 /* Receive Watchdog Time-Out Mask */ -#define IMR_RSM 0x00000100 /* Receive Stopped Mask */ -#define IMR_RUM 0x00000080 /* Receive Buffer Unavailable Mask */ -#define IMR_RIM 0x00000040 /* Receive Interrupt Mask */ -#define IMR_UNM 0x00000020 /* Underflow Interrupt Mask */ -#define IMR_ANM 0x00000010 /* Autonegotiation Complete Mask */ -#define IMR_LPM 0x00000010 /* Link Pass */ -#define IMR_TJM 0x00000008 /* Transmit Time-Out Jabber Mask */ -#define IMR_TUM 0x00000004 /* Transmit Buffer Unavailable Mask */ -#define IMR_TSM 0x00000002 /* Transmission Stopped Mask */ -#define IMR_TIM 0x00000001 /* Transmit Interrupt Mask */ - -/* -** Missed Frames and FIFO Overflow Counters (DE4X5_MFC) -*/ -#define MFC_FOCO 0x10000000 /* FIFO Overflow Counter Overflow Bit */ -#define MFC_FOC 0x0ffe0000 /* FIFO Overflow Counter Bits */ -#define MFC_OVFL 0x00010000 /* Missed Frames Counter Overflow Bit */ -#define MFC_CNTR 0x0000ffff /* Missed Frames Counter Bits */ -#define MFC_FOCM 0x1ffe0000 /* FIFO Overflow Counter Mask */ - -/* -** DC21040 Ethernet Address PROM (DE4X5_APROM) -*/ -#define APROM_DN 0x80000000 /* Data Not Valid */ -#define APROM_DT 0x000000ff /* Address Byte */ - -/* -** DC21041 Boot/Ethernet Address ROM (DE4X5_BROM) -*/ -#define BROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */ -#define BROM_RD 0x00004000 /* Read from Boot ROM */ -#define BROM_WR 0x00002000 /* Write to Boot ROM */ -#define BROM_BR 0x00001000 /* Select Boot ROM when set */ -#define BROM_SR 0x00000800 /* Select Serial ROM when set */ -#define BROM_REG 0x00000400 /* External Register Select */ -#define BROM_DT 0x000000ff /* Data Byte */ - -/* -** DC21041 Serial/Ethernet Address ROM (DE4X5_SROM, DE4X5_MII) -*/ -#define MII_MDI 0x00080000 /* MII Management Data In */ -#define MII_MDO 0x00060000 /* MII Management Mode/Data Out */ -#define MII_MRD 0x00040000 /* MII Management Define Read Mode */ -#define MII_MWR 0x00000000 /* MII Management Define Write Mode */ -#define MII_MDT 0x00020000 /* MII Management Data Out */ -#define MII_MDC 0x00010000 /* MII Management Clock */ -#define MII_RD 0x00004000 /* Read from MII */ -#define MII_WR 0x00002000 /* Write to MII */ -#define MII_SEL 0x00000800 /* Select MII when RESET */ - -#define SROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */ -#define SROM_RD 0x00004000 /* Read from Boot ROM */ -#define SROM_WR 0x00002000 /* Write to Boot ROM */ -#define SROM_BR 0x00001000 /* Select Boot ROM when set */ -#define SROM_SR 0x00000800 /* Select Serial ROM when set */ -#define SROM_REG 0x00000400 /* External Register Select */ -#define SROM_DT 0x000000ff /* Data Byte */ - -#define DT_OUT 0x00000008 /* Serial Data Out */ -#define DT_IN 0x00000004 /* Serial Data In */ -#define DT_CLK 0x00000002 /* Serial ROM Clock */ -#define DT_CS 0x00000001 /* Serial ROM Chip Select */ - -#define MII_PREAMBLE 0xffffffff /* MII Management Preamble */ -#define MII_TEST 0xaaaaaaaa /* MII Test Signal */ -#define MII_STRD 0x06 /* Start of Frame+Op Code: use low nibble */ -#define MII_STWR 0x0a /* Start of Frame+Op Code: use low nibble */ - -#define MII_CR 0x00 /* MII Management Control Register */ -#define MII_SR 0x01 /* MII Management Status Register */ -#define MII_ID0 0x02 /* PHY Identifier Register 0 */ -#define MII_ID1 0x03 /* PHY Identifier Register 1 */ -#define MII_ANA 0x04 /* Auto Negotiation Advertisement */ -#define MII_ANLPA 0x05 /* Auto Negotiation Link Partner Ability */ -#define MII_ANE 0x06 /* Auto Negotiation Expansion */ -#define MII_ANP 0x07 /* Auto Negotiation Next Page TX */ - -#define DE4X5_MAX_MII 32 /* Maximum address of MII PHY devices */ - -/* -** MII Management Control Register -*/ -#define MII_CR_RST 0x8000 /* RESET the PHY chip */ -#define MII_CR_LPBK 0x4000 /* Loopback enable */ -#define MII_CR_SPD 0x2000 /* 0: 10Mb/s; 1: 100Mb/s */ -#define MII_CR_10 0x0000 /* Set 10Mb/s */ -#define MII_CR_100 0x2000 /* Set 100Mb/s */ -#define MII_CR_ASSE 0x1000 /* Auto Speed Select Enable */ -#define MII_CR_PD 0x0800 /* Power Down */ -#define MII_CR_ISOL 0x0400 /* Isolate Mode */ -#define MII_CR_RAN 0x0200 /* Restart Auto Negotiation */ -#define MII_CR_FDM 0x0100 /* Full Duplex Mode */ -#define MII_CR_CTE 0x0080 /* Collision Test Enable */ - -/* -** MII Management Status Register -*/ -#define MII_SR_T4C 0x8000 /* 100BASE-T4 capable */ -#define MII_SR_TXFD 0x4000 /* 100BASE-TX Full Duplex capable */ -#define MII_SR_TXHD 0x2000 /* 100BASE-TX Half Duplex capable */ -#define MII_SR_TFD 0x1000 /* 10BASE-T Full Duplex capable */ -#define MII_SR_THD 0x0800 /* 10BASE-T Half Duplex capable */ -#define MII_SR_ASSC 0x0020 /* Auto Speed Selection Complete*/ -#define MII_SR_RFD 0x0010 /* Remote Fault Detected */ -#define MII_SR_ANC 0x0008 /* Auto Negotiation capable */ -#define MII_SR_LKS 0x0004 /* Link Status */ -#define MII_SR_JABD 0x0002 /* Jabber Detect */ -#define MII_SR_XC 0x0001 /* Extended Capabilities */ - -/* -** MII Management Auto Negotiation Advertisement Register -*/ -#define MII_ANA_TAF 0x03e0 /* Technology Ability Field */ -#define MII_ANA_T4AM 0x0200 /* T4 Technology Ability Mask */ -#define MII_ANA_TXAM 0x0180 /* TX Technology Ability Mask */ -#define MII_ANA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ -#define MII_ANA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ -#define MII_ANA_100M 0x0380 /* 100Mb Technology Ability Mask */ -#define MII_ANA_10M 0x0060 /* 10Mb Technology Ability Mask */ -#define MII_ANA_CSMA 0x0001 /* CSMA-CD Capable */ - -/* -** MII Management Auto Negotiation Remote End Register -*/ -#define MII_ANLPA_NP 0x8000 /* Next Page (Enable) */ -#define MII_ANLPA_ACK 0x4000 /* Remote Acknowledge */ -#define MII_ANLPA_RF 0x2000 /* Remote Fault */ -#define MII_ANLPA_TAF 0x03e0 /* Technology Ability Field */ -#define MII_ANLPA_T4AM 0x0200 /* T4 Technology Ability Mask */ -#define MII_ANLPA_TXAM 0x0180 /* TX Technology Ability Mask */ -#define MII_ANLPA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ -#define MII_ANLPA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ -#define MII_ANLPA_100M 0x0380 /* 100Mb Technology Ability Mask */ -#define MII_ANLPA_10M 0x0060 /* 10Mb Technology Ability Mask */ -#define MII_ANLPA_CSMA 0x0001 /* CSMA-CD Capable */ - -/* -** SROM Media Definitions (ABG SROM Section) -*/ -#define MEDIA_NWAY 0x0080 /* Nway (Auto Negotiation) on PHY */ -#define MEDIA_MII 0x0040 /* MII Present on the adapter */ -#define MEDIA_FIBRE 0x0008 /* Fibre Media present */ -#define MEDIA_AUI 0x0004 /* AUI Media present */ -#define MEDIA_TP 0x0002 /* TP Media present */ -#define MEDIA_BNC 0x0001 /* BNC Media present */ - -/* -** SROM Definitions (Digital Semiconductor Format) -*/ -#define SROM_SSVID 0x0000 /* Sub-system Vendor ID offset */ -#define SROM_SSID 0x0002 /* Sub-system ID offset */ -#define SROM_CISPL 0x0004 /* CardBus CIS Pointer low offset */ -#define SROM_CISPH 0x0006 /* CardBus CIS Pointer high offset */ -#define SROM_IDCRC 0x0010 /* ID Block CRC offset*/ -#define SROM_RSVD2 0x0011 /* ID Reserved 2 offset */ -#define SROM_SFV 0x0012 /* SROM Format Version offset */ -#define SROM_CCNT 0x0013 /* Controller Count offset */ -#define SROM_HWADD 0x0014 /* Hardware Address offset */ -#define SROM_MRSVD 0x007c /* Manufacturer Reserved offset*/ -#define SROM_CRC 0x007e /* SROM CRC offset */ - -/* -** SROM Media Connection Definitions -*/ -#define SROM_10BT 0x0000 /* 10BASE-T half duplex */ -#define SROM_10BTN 0x0100 /* 10BASE-T with Nway */ -#define SROM_10BTF 0x0204 /* 10BASE-T full duplex */ -#define SROM_10BTNLP 0x0400 /* 10BASE-T without Link Pass test */ -#define SROM_10B2 0x0001 /* 10BASE-2 (BNC) */ -#define SROM_10B5 0x0002 /* 10BASE-5 (AUI) */ -#define SROM_100BTH 0x0003 /* 100BASE-T half duplex */ -#define SROM_100BTF 0x0205 /* 100BASE-T full duplex */ -#define SROM_100BT4 0x0006 /* 100BASE-T4 */ -#define SROM_100BFX 0x0007 /* 100BASE-FX half duplex (Fiber) */ -#define SROM_M10BT 0x0009 /* MII 10BASE-T half duplex */ -#define SROM_M10BTF 0x020a /* MII 10BASE-T full duplex */ -#define SROM_M100BT 0x000d /* MII 100BASE-T half duplex */ -#define SROM_M100BTF 0x020e /* MII 100BASE-T full duplex */ -#define SROM_M100BT4 0x000f /* MII 100BASE-T4 */ -#define SROM_M100BF 0x0010 /* MII 100BASE-FX half duplex */ -#define SROM_M100BFF 0x0211 /* MII 100BASE-FX full duplex */ -#define SROM_PDA 0x0800 /* Powerup & Dynamic Autosense */ -#define SROM_PAO 0x8800 /* Powerup Autosense Only */ -#define SROM_NSMI 0xffff /* No Selected Media Information */ - -/* -** SROM Media Definitions -*/ -#define SROM_10BASET 0x0000 /* 10BASE-T half duplex */ -#define SROM_10BASE2 0x0001 /* 10BASE-2 (BNC) */ -#define SROM_10BASE5 0x0002 /* 10BASE-5 (AUI) */ -#define SROM_100BASET 0x0003 /* 100BASE-T half duplex */ -#define SROM_10BASETF 0x0004 /* 10BASE-T full duplex */ -#define SROM_100BASETF 0x0005 /* 100BASE-T full duplex */ -#define SROM_100BASET4 0x0006 /* 100BASE-T4 */ -#define SROM_100BASEF 0x0007 /* 100BASE-FX half duplex */ -#define SROM_100BASEFF 0x0008 /* 100BASE-FX full duplex */ - -#define BLOCK_LEN 0x7f /* Extended blocks length mask */ -#define EXT_FIELD 0x40 /* Extended blocks extension field bit */ -#define MEDIA_CODE 0x3f /* Extended blocks media code mask */ - -/* -** SROM Compact Format Block Masks -*/ -#define COMPACT_FI 0x80 /* Format Indicator */ -#define COMPACT_LEN 0x04 /* Length */ -#define COMPACT_MC 0x3f /* Media Code */ - -/* -** SROM Extended Format Block Type 0 Masks -*/ -#define BLOCK0_FI 0x80 /* Format Indicator */ -#define BLOCK0_MCS 0x80 /* Media Code byte Sign */ -#define BLOCK0_MC 0x3f /* Media Code */ - -/* -** DC21040 Full Duplex Register (DE4X5_FDR) -*/ -#define FDR_FDACV 0x0000ffff /* Full Duplex Auto Configuration Value */ - -/* -** DC21041 General Purpose Timer Register (DE4X5_GPT) -*/ -#define GPT_CON 0x00010000 /* One shot: 0, Continuous: 1 */ -#define GPT_VAL 0x0000ffff /* Timer Value */ - -/* -** DC21140 General Purpose Register (DE4X5_GEP) (hardware dependent bits) -*/ -/* Valid ONLY for DE500 hardware */ -#define GEP_LNP 0x00000080 /* Link Pass (input) */ -#define GEP_SLNK 0x00000040 /* SYM LINK (input) */ -#define GEP_SDET 0x00000020 /* Signal Detect (input) */ -#define GEP_HRST 0x00000010 /* Hard RESET (to PHY) (output) */ -#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */ -#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */ -#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */ -#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */ -#define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */ -#define GEP_CTRL 0x00000100 /* GEP control bit */ - -/* -** SIA Register Defaults -*/ -#define CSR13 0x00000001 -#define CSR14 0x0003ff7f /* Autonegotiation disabled */ -#define CSR15 0x00000008 - -/* -** SIA Status Register (DE4X5_SISR) -*/ -#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */ -#define SISR_LPN 0x00008000 /* Link Partner Negotiable */ -#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */ -#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */ -#define SISR_TRF 0x00000800 /* Transmit Remote Fault */ -#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */ -#define SISR_ANR_FDS 0x00000400 /* Auto Negotiate Restart/Full Duplex Sel.*/ -#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */ -#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */ -#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */ -#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */ -#define SISR_DAO 0x00000080 /* PLL All One */ -#define SISR_DAZ 0x00000040 /* PLL All Zero */ -#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */ -#define SISR_DSD 0x00000010 /* PLL Self-Test Done */ -#define SISR_APS 0x00000008 /* Auto Polarity State */ -#define SISR_LKF 0x00000004 /* Link Fail Status */ -#define SISR_LS10 0x00000004 /* 10Mb/s Link Fail Status */ -#define SISR_NCR 0x00000002 /* Network Connection Error */ -#define SISR_LS100 0x00000002 /* 100Mb/s Link Fail Status */ -#define SISR_PAUI 0x00000001 /* AUI_TP Indication */ -#define SISR_MRA 0x00000001 /* MII Receive Port Activity */ - -#define ANS_NDIS 0x00000000 /* Nway disable */ -#define ANS_TDIS 0x00001000 /* Transmit Disable */ -#define ANS_ADET 0x00002000 /* Ability Detect */ -#define ANS_ACK 0x00003000 /* Acknowledge */ -#define ANS_CACK 0x00004000 /* Complete Acknowledge */ -#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */ -#define ANS_LCHK 0x00006000 /* Link Check */ - -#define SISR_RST 0x00000301 /* CSR12 reset */ -#define SISR_ANR 0x00001301 /* Autonegotiation restart */ - -/* -** SIA Connectivity Register (DE4X5_SICR) -*/ -#define SICR_SDM 0xffff0000 /* SIA Diagnostics Mode */ -#define SICR_OE57 0x00008000 /* Output Enable 5 6 7 */ -#define SICR_OE24 0x00004000 /* Output Enable 2 4 */ -#define SICR_OE13 0x00002000 /* Output Enable 1 3 */ -#define SICR_IE 0x00001000 /* Input Enable */ -#define SICR_EXT 0x00000000 /* SIA MUX Select External SIA Mode */ -#define SICR_D_SIA 0x00000400 /* SIA MUX Select Diagnostics - SIA Sigs */ -#define SICR_DPLL 0x00000800 /* SIA MUX Select Diagnostics - DPLL Sigs*/ -#define SICR_APLL 0x00000a00 /* SIA MUX Select Diagnostics - DPLL Sigs*/ -#define SICR_D_RxM 0x00000c00 /* SIA MUX Select Diagnostics - RxM Sigs */ -#define SICR_M_RxM 0x00000d00 /* SIA MUX Select Diagnostics - RxM Sigs */ -#define SICR_LNKT 0x00000e00 /* SIA MUX Select Diagnostics - Link Test*/ -#define SICR_SEL 0x00000f00 /* SIA MUX Select AUI or TP with LEDs */ -#define SICR_ASE 0x00000080 /* APLL Start Enable*/ -#define SICR_SIM 0x00000040 /* Serial Interface Input Multiplexer */ -#define SICR_ENI 0x00000020 /* Encoder Input Multiplexer */ -#define SICR_EDP 0x00000010 /* SIA PLL External Input Enable */ -#define SICR_AUI 0x00000008 /* 10Base-T (0) or AUI (1) */ -#define SICR_CAC 0x00000004 /* CSR Auto Configuration */ -#define SICR_PS 0x00000002 /* Pin AUI/TP Selection */ -#define SICR_SRL 0x00000001 /* SIA Reset */ -#define SIA_RESET 0x00000000 /* SIA Reset Value */ - -/* -** SIA Transmit and Receive Register (DE4X5_STRR) -*/ -#define STRR_TAS 0x00008000 /* 10Base-T/AUI Autosensing Enable */ -#define STRR_SPP 0x00004000 /* Set Polarity Plus */ -#define STRR_APE 0x00002000 /* Auto Polarity Enable */ -#define STRR_LTE 0x00001000 /* Link Test Enable */ -#define STRR_SQE 0x00000800 /* Signal Quality Enable */ -#define STRR_CLD 0x00000400 /* Collision Detect Enable */ -#define STRR_CSQ 0x00000200 /* Collision Squelch Enable */ -#define STRR_RSQ 0x00000100 /* Receive Squelch Enable */ -#define STRR_ANE 0x00000080 /* Auto Negotiate Enable */ -#define STRR_HDE 0x00000040 /* Half Duplex Enable */ -#define STRR_CPEN 0x00000030 /* Compensation Enable */ -#define STRR_LSE 0x00000008 /* Link Pulse Send Enable */ -#define STRR_DREN 0x00000004 /* Driver Enable */ -#define STRR_LBK 0x00000002 /* Loopback Enable */ -#define STRR_ECEN 0x00000001 /* Encoder Enable */ -#define STRR_RESET 0xffffffff /* Reset value for STRR */ - -/* -** SIA General Register (DE4X5_SIGR) -*/ -#define SIGR_RMI 0x40000000 /* Receive Match Interrupt */ -#define SIGR_GI1 0x20000000 /* General Port Interrupt 1 */ -#define SIGR_GI0 0x10000000 /* General Port Interrupt 0 */ -#define SIGR_CWE 0x08000000 /* Control Write Enable */ -#define SIGR_RME 0x04000000 /* Receive Match Enable */ -#define SIGR_GEI1 0x02000000 /* GEP Interrupt Enable on Port 1 */ -#define SIGR_GEI0 0x01000000 /* GEP Interrupt Enable on Port 0 */ -#define SIGR_LGS3 0x00800000 /* LED/GEP3 Select */ -#define SIGR_LGS2 0x00400000 /* LED/GEP2 Select */ -#define SIGR_LGS1 0x00200000 /* LED/GEP1 Select */ -#define SIGR_LGS0 0x00100000 /* LED/GEP0 Select */ -#define SIGR_MD 0x000f0000 /* General Purpose Mode and Data */ -#define SIGR_LV2 0x00008000 /* General Purpose LED2 value */ -#define SIGR_LE2 0x00004000 /* General Purpose LED2 enable */ -#define SIGR_FRL 0x00002000 /* Force Receiver Low */ -#define SIGR_DPST 0x00001000 /* PLL Self Test Start */ -#define SIGR_LSD 0x00000800 /* LED Stretch Disable */ -#define SIGR_FLF 0x00000400 /* Force Link Fail */ -#define SIGR_FUSQ 0x00000200 /* Force Unsquelch */ -#define SIGR_TSCK 0x00000100 /* Test Clock */ -#define SIGR_LV1 0x00000080 /* General Purpose LED1 value */ -#define SIGR_LE1 0x00000040 /* General Purpose LED1 enable */ -#define SIGR_RWR 0x00000020 /* Receive Watchdog Release */ -#define SIGR_RWD 0x00000010 /* Receive Watchdog Disable */ -#define SIGR_ABM 0x00000008 /* BNC: 0, AUI:1 */ -#define SIGR_JCK 0x00000004 /* Jabber Clock */ -#define SIGR_HUJ 0x00000002 /* Host Unjab */ -#define SIGR_JBD 0x00000001 /* Jabber Disable */ -#define SIGR_RESET 0xffff0000 /* Reset value for SIGR */ - -/* -** Receive Descriptor Bit Summary -*/ -#define R_OWN 0x80000000 /* Own Bit */ -#define RD_FF 0x40000000 /* Filtering Fail */ -#define RD_FL 0x3fff0000 /* Frame Length */ -#define RD_ES 0x00008000 /* Error Summary */ -#define RD_LE 0x00004000 /* Length Error */ -#define RD_DT 0x00003000 /* Data Type */ -#define RD_RF 0x00000800 /* Runt Frame */ -#define RD_MF 0x00000400 /* Multicast Frame */ -#define RD_FS 0x00000200 /* First Descriptor */ -#define RD_LS 0x00000100 /* Last Descriptor */ -#define RD_TL 0x00000080 /* Frame Too Long */ -#define RD_CS 0x00000040 /* Collision Seen */ -#define RD_FT 0x00000020 /* Frame Type */ -#define RD_RJ 0x00000010 /* Receive Watchdog */ -#define RD_RE 0x00000008 /* Report on MII Error */ -#define RD_DB 0x00000004 /* Dribbling Bit */ -#define RD_CE 0x00000002 /* CRC Error */ -#define RD_OF 0x00000001 /* Overflow */ - -#define RD_RER 0x02000000 /* Receive End Of Ring */ -#define RD_RCH 0x01000000 /* Second Address Chained */ -#define RD_RBS2 0x003ff800 /* Buffer 2 Size */ -#define RD_RBS1 0x000007ff /* Buffer 1 Size */ - -/* -** Transmit Descriptor Bit Summary -*/ -#define T_OWN 0x80000000 /* Own Bit */ -#define TD_ES 0x00008000 /* Error Summary */ -#define TD_TO 0x00004000 /* Transmit Jabber Time-Out */ -#define TD_LO 0x00000800 /* Loss Of Carrier */ -#define TD_NC 0x00000400 /* No Carrier */ -#define TD_LC 0x00000200 /* Late Collision */ -#define TD_EC 0x00000100 /* Excessive Collisions */ -#define TD_HF 0x00000080 /* Heartbeat Fail */ -#define TD_CC 0x00000078 /* Collision Counter */ -#define TD_LF 0x00000004 /* Link Fail */ -#define TD_UF 0x00000002 /* Underflow Error */ -#define TD_DE 0x00000001 /* Deferred */ - -#define TD_IC 0x80000000 /* Interrupt On Completion */ -#define TD_LS 0x40000000 /* Last Segment */ -#define TD_FS 0x20000000 /* First Segment */ -#define TD_FT1 0x10000000 /* Filtering Type */ -#define TD_SET 0x08000000 /* Setup Packet */ -#define TD_AC 0x04000000 /* Add CRC Disable */ -#define TD_TER 0x02000000 /* Transmit End Of Ring */ -#define TD_TCH 0x01000000 /* Second Address Chained */ -#define TD_DPD 0x00800000 /* Disabled Padding */ -#define TD_FT0 0x00400000 /* Filtering Type */ -#define TD_TBS2 0x003ff800 /* Buffer 2 Size */ -#define TD_TBS1 0x000007ff /* Buffer 1 Size */ - -#define PERFECT_F 0x00000000 -#define HASH_F TD_FT0 -#define INVERSE_F TD_FT1 -#define HASH_O_F (TD_FT1 | TD_F0) - -/* -** Media / mode state machine definitions -** User selectable: -*/ -#define TP 0x0040 /* 10Base-T (now equiv to _10Mb) */ -#define TP_NW 0x0002 /* 10Base-T with Nway */ -#define BNC 0x0004 /* Thinwire */ -#define AUI 0x0008 /* Thickwire */ -#define BNC_AUI 0x0010 /* BNC/AUI on DC21040 indistinguishable */ -#define _10Mb 0x0040 /* 10Mb/s Ethernet */ -#define _100Mb 0x0080 /* 100Mb/s Ethernet */ -#define AUTO 0x4000 /* Auto sense the media or speed */ - -/* -** Internal states -*/ -#define NC 0x0000 /* No Connection */ -#define ANS 0x0020 /* Intermediate AutoNegotiation State */ -#define SPD_DET 0x0100 /* Parallel speed detection */ -#define INIT 0x0200 /* Initial state */ -#define EXT_SIA 0x0400 /* External SIA for motherboard chip */ -#define ANS_SUSPECT 0x0802 /* Suspect the ANS (TP) port is down */ -#define TP_SUSPECT 0x0803 /* Suspect the TP port is down */ -#define BNC_AUI_SUSPECT 0x0804 /* Suspect the BNC or AUI port is down */ -#define EXT_SIA_SUSPECT 0x0805 /* Suspect the EXT SIA port is down */ -#define BNC_SUSPECT 0x0806 /* Suspect the BNC port is down */ -#define AUI_SUSPECT 0x0807 /* Suspect the AUI port is down */ -#define MII 0x1000 /* MII on the 21143 */ - -#define TIMER_CB 0x80000000 /* Timer callback detection */ - -/* -** DE4X5 DEBUG Options -*/ -#define DEBUG_NONE 0x0000 /* No DEBUG messages */ -#define DEBUG_VERSION 0x0001 /* Print version message */ -#define DEBUG_MEDIA 0x0002 /* Print media messages */ -#define DEBUG_TX 0x0004 /* Print TX (queue_pkt) messages */ -#define DEBUG_RX 0x0008 /* Print RX (de4x5_rx) messages */ -#define DEBUG_SROM 0x0010 /* Print SROM messages */ -#define DEBUG_MII 0x0020 /* Print MII messages */ -#define DEBUG_OPEN 0x0040 /* Print de4x5_open() messages */ -#define DEBUG_CLOSE 0x0080 /* Print de4x5_close() messages */ -#define DEBUG_PCICFG 0x0100 -#define DEBUG_ALL 0x01ff - -/* -** Miscellaneous -*/ -#define PCI 0 -#define EISA 1 - -#define HASH_TABLE_LEN 512 /* Bits */ -#define HASH_BITS 0x01ff /* 9 LS bits */ - -#define SETUP_FRAME_LEN 192 /* Bytes */ -#define IMPERF_PA_OFFSET 156 /* Bytes */ - -#define POLL_DEMAND 1 - -#define LOST_MEDIA_THRESHOLD 3 - -#define MASK_INTERRUPTS 1 -#define UNMASK_INTERRUPTS 0 - -#define DE4X5_STRLEN 8 - -#define DE4X5_INIT 0 /* Initialisation time */ -#define DE4X5_RUN 1 /* Run time */ - -#define DE4X5_SAVE_STATE 0 -#define DE4X5_RESTORE_STATE 1 - -/* -** Address Filtering Modes -*/ -#define PERFECT 0 /* 16 perfect physical addresses */ -#define HASH_PERF 1 /* 1 perfect, 512 multicast addresses */ -#define PERFECT_REJ 2 /* Reject 16 perfect physical addresses */ -#define ALL_HASH 3 /* Hashes all physical & multicast addrs */ - -#define ALL 0 /* Clear out all the setup frame */ -#define PHYS_ADDR_ONLY 1 /* Update the physical address only */ - -/* -** Booleans -*/ -#define NO 0 -#define FALSE 0 - -#define YES ~0 -#define TRUE ~0 - -/* -** Adapter state -*/ -#define INITIALISED 0 /* After h/w initialised and mem alloc'd */ -#define CLOSED 1 /* Ready for opening */ -#define OPEN 2 /* Running */ - -/* -** Various wait times -*/ -#define PDET_LINK_WAIT 1200 /* msecs to wait for link detect bits */ -#define ANS_FINISH_WAIT 1000 /* msecs to wait for link detect bits */ - -/* -** IEEE OUIs for various PHY vendor/chip combos - Reg 2 values only. Since -** the vendors seem split 50-50 on how to calculate the OUI register values -** anyway, just reading Reg2 seems reasonable for now [see de4x5_get_oui()]. -*/ -#define NATIONAL_TX 0x2000 -#define BROADCOM_T4 0x03e0 -#define SEEQ_T4 0x0016 -#define CYPRESS_T4 0x0014 - -/* -** Speed Selection stuff -*/ -#define SET_10Mb {\ - if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ - omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ - if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ - mii_wr(MII_CR_10|(lp->fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ - }\ - omr |= ((lp->fdx ? OMR_FDX : 0) | OMR_TTM);\ - outl(omr, DE4X5_OMR);\ - if (!lp->useSROM) lp->cache.gep = 0;\ - } else if (lp->useSROM && !lp->useMII) {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | (lp->infoblock_csr6 & ~(OMR_SCR | OMR_HBD)), DE4X5_OMR);\ - } else {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | OMR_SDP | OMR_TTM, DE4X5_OMR);\ - lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD);\ - gep_wr(lp->cache.gep, dev);\ - }\ -} - -#define SET_100Mb {\ - if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ - int fdx=0;\ - if (lp->phy[lp->active].id == NATIONAL_TX) {\ - mii_wr(mii_rd(0x18, lp->phy[lp->active].addr, DE4X5_MII) & ~0x2000,\ - 0x18, lp->phy[lp->active].addr, DE4X5_MII);\ - }\ - omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ - sr = mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);\ - if (!(sr & MII_ANA_T4AM) && lp->fdx) fdx=1;\ - if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ - mii_wr(MII_CR_100|(fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ - }\ - if (fdx) omr |= OMR_FDX;\ - outl(omr, DE4X5_OMR);\ - if (!lp->useSROM) lp->cache.gep = 0;\ - } else if (lp->useSROM && !lp->useMII) {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | lp->infoblock_csr6, DE4X5_OMR);\ - } else {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ - lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD) | GEP_MODE;\ - gep_wr(lp->cache.gep, dev);\ - }\ -} - -/* FIX ME so I don't jam 10Mb networks */ -#define SET_100Mb_PDET {\ - if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ - mii_wr(MII_CR_100|MII_CR_ASSE, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ - omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - outl(omr, DE4X5_OMR);\ - } else if (lp->useSROM && !lp->useMII) {\ - omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - outl(omr, DE4X5_OMR);\ - } else {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS, DE4X5_OMR);\ - lp->cache.gep = (GEP_FDXD | GEP_MODE);\ - gep_wr(lp->cache.gep, dev);\ - }\ -} - -/* -** Include the IOCTL stuff -*/ -#include - -#define DE4X5IOCTL SIOCDEVPRIVATE - -struct de4x5_ioctl { - unsigned short cmd; /* Command to run */ - unsigned short len; /* Length of the data buffer */ - unsigned char *data; /* Pointer to the data buffer */ -}; - -/* -** Recognised commands for the driver -*/ -#define DE4X5_GET_HWADDR 0x01 /* Get the hardware address */ -#define DE4X5_SET_HWADDR 0x02 /* Set the hardware address */ -#define DE4X5_SET_PROM 0x03 /* Set Promiscuous Mode */ -#define DE4X5_CLR_PROM 0x04 /* Clear Promiscuous Mode */ -#define DE4X5_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */ -#define DE4X5_GET_MCA 0x06 /* Get a multicast address */ -#define DE4X5_SET_MCA 0x07 /* Set a multicast address */ -#define DE4X5_CLR_MCA 0x08 /* Clear a multicast address */ -#define DE4X5_MCA_EN 0x09 /* Enable a multicast address group */ -#define DE4X5_GET_STATS 0x0a /* Get the driver statistics */ -#define DE4X5_CLR_STATS 0x0b /* Zero out the driver statistics */ -#define DE4X5_GET_OMR 0x0c /* Get the OMR Register contents */ -#define DE4X5_SET_OMR 0x0d /* Set the OMR Register contents */ -#define DE4X5_GET_REG 0x0e /* Get the DE4X5 Registers */ - -#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) - -#define MOTO_SROM_BUG ((lp->active == 8) && (((le32_to_cpu(get_unaligned(((s32 *)dev->dev_addr))))&0x00ffffff)==0x3e0008)) diff -Nru a/drivers/net/defxx.c b/drivers/net/defxx.c --- a/drivers/net/defxx.c Thu Mar 7 18:17:36 2002 +++ b/drivers/net/defxx.c Thu Mar 7 18:17:36 2002 @@ -3362,7 +3362,7 @@ static struct pci_driver dfx_driver = { name: "defxx", probe: dfx_init_one, - remove: dfx_remove_one, + remove: __devexit_p(dfx_remove_one), id_table: dfx_pci_tbl, }; diff -Nru a/drivers/net/dl2k.c b/drivers/net/dl2k.c --- a/drivers/net/dl2k.c Thu Mar 7 18:17:43 2002 +++ b/drivers/net/dl2k.c Thu Mar 7 18:17:43 2002 @@ -1682,7 +1682,7 @@ name:"dl2k", id_table:rio_pci_tbl, probe:rio_probe1, - remove:rio_remove1, + remove: __devexit_p(rio_remove1), }; static int __init diff -Nru a/drivers/net/dmfe.c b/drivers/net/dmfe.c --- a/drivers/net/dmfe.c Thu Mar 7 18:17:46 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2070 +0,0 @@ -/* - A Davicom DM9102/DM9102A/DM9102A+DM9801/DM9102A+DM9802 NIC fast - ethernet driver for Linux. - Copyright (C) 1997 Sten Wang - - 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. - - DAVICOM Web-Site: www.davicom.com.tw - - Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw - Maintainer: Tobias Ringstrom - - (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved. - - Marcelo Tosatti : - Made it compile in 2.3 (device to net_device) - - Alan Cox : - Cleaned up for kernel merge. - Removed the back compatibility support - Reformatted, fixing spelling etc as I went - Removed IRQ 0-15 assumption - - Jeff Garzik : - Updated to use new PCI driver API. - Resource usage cleanups. - Report driver version to user. - - Tobias Ringstrom : - Cleaned up and added SMP safety. Thanks go to Jeff Garzik, - Andrew Morton and Frank Davis for the SMP safety fixes. - - Vojtech Pavlik : - Cleaned up pointer arithmetics. - Fixed a lot of 64bit issues. - Cleaned up printk()s a bit. - Fixed some obvious big endian problems. - - Tobias Ringstrom : - Use time_after for jiffies calculation. Added ethtool - support. Updated PCI resource allocation. Do not - forget to unmap PCI mapped skbs. - - TODO - - Implement pci_driver::suspend() and pci_driver::resume() - power management methods. - - Check on 64 bit boxes. - Check and fix on big endian boxes. - - Test and make sure PCI latency is now correct for all cases. -*/ - -#define DRV_NAME "dmfe" -#define DRV_VERSION "1.36.4" -#define DRV_RELDATE "2002-01-17" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -/* Board/System/Debug information/definition ---------------- */ -#define PCI_DM9132_ID 0x91321282 /* Davicom DM9132 ID */ -#define PCI_DM9102_ID 0x91021282 /* Davicom DM9102 ID */ -#define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */ -#define PCI_DM9009_ID 0x90091282 /* Davicom DM9009 ID */ - -#define DM9102_IO_SIZE 0x80 -#define DM9102A_IO_SIZE 0x100 -#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */ -#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */ -#define RX_DESC_CNT 0x20 /* Allocated Rx descriptors */ -#define TX_FREE_DESC_CNT (TX_DESC_CNT - 2) /* Max TX packet count */ -#define TX_WAKE_DESC_CNT (TX_DESC_CNT - 3) /* TX wakeup count */ -#define DESC_ALL_CNT (TX_DESC_CNT + RX_DESC_CNT) -#define TX_BUF_ALLOC 0x600 -#define RX_ALLOC_SIZE 0x620 -#define DM910X_RESET 1 -#define CR0_DEFAULT 0x00E00000 /* TX & RX burst mode */ -#define CR6_DEFAULT 0x00080000 /* HD */ -#define CR7_DEFAULT 0x180c1 -#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */ -#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */ -#define MAX_PACKET_SIZE 1514 -#define DMFE_MAX_MULTICAST 14 -#define RX_COPY_SIZE 100 -#define MAX_CHECK_PACKET 0x8000 -#define DM9801_NOISE_FLOOR 8 -#define DM9802_NOISE_FLOOR 5 - -#define DMFE_10MHF 0 -#define DMFE_100MHF 1 -#define DMFE_10MFD 4 -#define DMFE_100MFD 5 -#define DMFE_AUTO 8 -#define DMFE_1M_HPNA 0x10 - -#define DMFE_TXTH_72 0x400000 /* TX TH 72 byte */ -#define DMFE_TXTH_96 0x404000 /* TX TH 96 byte */ -#define DMFE_TXTH_128 0x0000 /* TX TH 128 byte */ -#define DMFE_TXTH_256 0x4000 /* TX TH 256 byte */ -#define DMFE_TXTH_512 0x8000 /* TX TH 512 byte */ -#define DMFE_TXTH_1K 0xC000 /* TX TH 1K byte */ - -#define DMFE_TIMER_WUT (jiffies + HZ * 1)/* timer wakeup time : 1 second */ -#define DMFE_TX_TIMEOUT ((3*HZ)/2) /* tx packet time-out time 1.5 s" */ -#define DMFE_TX_KICK (HZ/2) /* tx packet Kick-out time 0.5 s" */ - -#define DMFE_DBUG(dbug_now, msg, value) if (dmfe_debug || (dbug_now)) printk(KERN_ERR DRV_NAME ": %s %lx\n", (msg), (long) (value)) - -#define SHOW_MEDIA_TYPE(mode) printk(KERN_ERR DRV_NAME ": Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half"); - - -/* CR9 definition: SROM/MII */ -#define CR9_SROM_READ 0x4800 -#define CR9_SRCS 0x1 -#define CR9_SRCLK 0x2 -#define CR9_CRDOUT 0x8 -#define SROM_DATA_0 0x0 -#define SROM_DATA_1 0x4 -#define PHY_DATA_1 0x20000 -#define PHY_DATA_0 0x00000 -#define MDCLKH 0x10000 - -#define PHY_POWER_DOWN 0x800 - -#define SROM_V41_CODE 0x14 - -#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5); - -#define __CHK_IO_SIZE(pci_id, dev_rev) ( ((pci_id)==PCI_DM9132_ID) || ((dev_rev) >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE -#define CHK_IO_SIZE(pci_dev, dev_rev) __CHK_IO_SIZE(((pci_dev)->device << 16) | (pci_dev)->vendor, dev_rev) - -/* Sten Check */ -#define DEVICE net_device - -/* Structure/enum declaration ------------------------------- */ -struct tx_desc { - u32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */ - char *tx_buf_ptr; /* Data for us */ - struct tx_desc *next_tx_desc; -} __attribute__(( aligned(32) )); - -struct rx_desc { - u32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */ - struct sk_buff *rx_skb_ptr; /* Data for us */ - struct rx_desc *next_rx_desc; -} __attribute__(( aligned(32) )); - -struct dmfe_board_info { - u32 chip_id; /* Chip vendor/Device ID */ - u32 chip_revision; /* Chip revision */ - struct DEVICE *next_dev; /* next device */ - struct pci_dev *pdev; /* PCI device */ - spinlock_t lock; - - long ioaddr; /* I/O base address */ - u32 cr0_data; - u32 cr5_data; - u32 cr6_data; - u32 cr7_data; - u32 cr15_data; - - /* pointer for memory physical address */ - dma_addr_t buf_pool_dma_ptr; /* Tx buffer pool memory */ - dma_addr_t buf_pool_dma_start; /* Tx buffer pool align dword */ - dma_addr_t desc_pool_dma_ptr; /* descriptor pool memory */ - dma_addr_t first_tx_desc_dma; - dma_addr_t first_rx_desc_dma; - - /* descriptor pointer */ - unsigned char *buf_pool_ptr; /* Tx buffer pool memory */ - unsigned char *buf_pool_start; /* Tx buffer pool align dword */ - unsigned char *desc_pool_ptr; /* descriptor pool memory */ - struct tx_desc *first_tx_desc; - struct tx_desc *tx_insert_ptr; - struct tx_desc *tx_remove_ptr; - struct rx_desc *first_rx_desc; - struct rx_desc *rx_insert_ptr; - struct rx_desc *rx_ready_ptr; /* packet come pointer */ - unsigned long tx_packet_cnt; /* transmitted packet count */ - unsigned long tx_queue_cnt; /* wait to send packet count */ - unsigned long rx_avail_cnt; /* available rx descriptor count */ - unsigned long interval_rx_cnt; /* rx packet count a callback time */ - - u16 HPNA_command; /* For HPNA register 16 */ - u16 HPNA_timer; /* For HPNA remote device check */ - u16 dbug_cnt; - u16 NIC_capability; /* NIC media capability */ - u16 PHY_reg4; /* Saved Phyxcer register 4 value */ - - u8 HPNA_present; /* 0:none, 1:DM9801, 2:DM9802 */ - u8 chip_type; /* Keep DM9102A chip type */ - u8 media_mode; /* user specify media mode */ - u8 op_mode; /* real work media mode */ - u8 phy_addr; - u8 link_failed; /* Ever link failed */ - u8 wait_reset; /* Hardware failed, need to reset */ - u8 dm910x_chk_mode; /* Operating mode check */ - u8 first_in_callback; /* Flag to record state */ - struct timer_list timer; - - /* System defined statistic counter */ - struct net_device_stats stats; - - /* Driver defined statistic counter */ - unsigned long tx_fifo_underrun; - unsigned long tx_loss_carrier; - unsigned long tx_no_carrier; - unsigned long tx_late_collision; - unsigned long tx_excessive_collision; - unsigned long tx_jabber_timeout; - unsigned long reset_count; - unsigned long reset_cr8; - unsigned long reset_fatal; - unsigned long reset_TXtimeout; - - /* NIC SROM data */ - unsigned char srom[128]; -}; - -enum dmfe_offsets { - DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20, - DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48, - DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70, - DCR15 = 0x78 -}; - -enum dmfe_CR6_bits { - CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, - CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000, - CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000 -}; - -/* Global variable declaration ----------------------------- */ -static int __devinitdata printed_version; -static char version[] __devinitdata = - KERN_INFO DRV_NAME ": Davicom DM9xxx net driver, version " - DRV_VERSION " (" DRV_RELDATE ")\n"; - -static int dmfe_debug; -static unsigned char dmfe_media_mode = DMFE_AUTO; -static u32 dmfe_cr6_user_set; - -/* For module input parameter */ -static int debug; -static u32 cr6set; -static unsigned char mode = 8; -static u8 chkmode = 1; -static u8 HPNA_mode; /* Default: Low Power/High Speed */ -static u8 HPNA_rx_cmd; /* Default: Disable Rx remote command */ -static u8 HPNA_tx_cmd; /* Default: Don't issue remote command */ -static u8 HPNA_NoiseFloor; /* Default: HPNA NoiseFloor */ -static u8 SF_mode; /* Special Function: 1:VLAN, 2:RX Flow Control - 4: TX pause packet */ - - -/* function declaration ------------------------------------- */ -static int dmfe_open(struct DEVICE *); -static int dmfe_start_xmit(struct sk_buff *, struct DEVICE *); -static int dmfe_stop(struct DEVICE *); -static struct net_device_stats * dmfe_get_stats(struct DEVICE *); -static void dmfe_set_filter_mode(struct DEVICE *); -static int dmfe_do_ioctl(struct DEVICE *, struct ifreq *, int); -static u16 read_srom_word(long ,int); -static void dmfe_interrupt(int , void *, struct pt_regs *); -static void dmfe_descriptor_init(struct dmfe_board_info *, unsigned long); -static void allocate_rx_buffer(struct dmfe_board_info *); -static void update_cr6(u32, unsigned long); -static void send_filter_frame(struct DEVICE * ,int); -static void dm9132_id_table(struct DEVICE * ,int); -static u16 phy_read(unsigned long, u8, u8, u32); -static void phy_write(unsigned long, u8, u8, u16, u32); -static void phy_write_1bit(unsigned long, u32); -static u16 phy_read_1bit(unsigned long); -static u8 dmfe_sense_speed(struct dmfe_board_info *); -static void dmfe_process_mode(struct dmfe_board_info *); -static void dmfe_timer(unsigned long); -static void dmfe_rx_packet(struct DEVICE *, struct dmfe_board_info *); -static void dmfe_free_tx_pkt(struct DEVICE *, struct dmfe_board_info *); -static void dmfe_reuse_skb(struct dmfe_board_info *, struct sk_buff *); -static void dmfe_dynamic_reset(struct DEVICE *); -static void dmfe_free_rxbuffer(struct dmfe_board_info *); -static void dmfe_init_dm910x(struct DEVICE *); -static inline u32 cal_CRC(unsigned char *, unsigned int, u8); -static void dmfe_parse_srom(struct dmfe_board_info *); -static void dmfe_program_DM9801(struct dmfe_board_info *, int); -static void dmfe_program_DM9802(struct dmfe_board_info *); -static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info * ); -static void dmfe_set_phyxcer(struct dmfe_board_info *); - -/* DM910X network baord routine ---------------------------- */ - -/* - * Search DM910X board ,allocate space and register it - */ - -static int __devinit dmfe_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct dmfe_board_info *db; /* board information structure */ - struct net_device *dev; - u32 dev_rev, pci_pmr; - int i, err; - - DMFE_DBUG(0, "dmfe_init_one()", 0); - - if (!printed_version++) - printk(version); - - /* Init network device */ - dev = alloc_etherdev(sizeof(*db)); - if (dev == NULL) - return -ENOMEM; - SET_MODULE_OWNER(dev); - - if (pci_set_dma_mask(pdev, 0xffffffff)) { - printk(KERN_WARNING DRV_NAME ": 32-bit PCI DMA not available.\n"); - err = -ENODEV; - goto err_out_free; - } - - /* Enable Master/IO access, Disable memory access */ - err = pci_enable_device(pdev); - if (err) - goto err_out_free; - - if (!pci_resource_start(pdev, 0)) { - printk(KERN_ERR DRV_NAME ": I/O base is zero\n"); - err = -ENODEV; - goto err_out_disable; - } - - /* Read Chip revision */ - pci_read_config_dword(pdev, PCI_REVISION_ID, &dev_rev); - - if (pci_resource_len(pdev, 0) < (CHK_IO_SIZE(pdev, dev_rev)) ) { - printk(KERN_ERR DRV_NAME ": Allocated I/O size too small\n"); - err = -ENODEV; - goto err_out_disable; - } - -#if 0 /* pci_{enable_device,set_master} sets minimum latency for us now */ - - /* Set Latency Timer 80h */ - /* FIXME: setting values > 32 breaks some SiS 559x stuff. - Need a PCI quirk.. */ - - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x80); -#endif - - if (pci_request_regions(pdev, DRV_NAME)) { - printk(KERN_ERR DRV_NAME ": Failed to request PCI regions\n"); - err = -ENODEV; - goto err_out_disable; - } - - /* Init system & device */ - db = dev->priv; - - /* Allocate Tx/Rx descriptor memory */ - db->desc_pool_ptr = pci_alloc_consistent(pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, &db->desc_pool_dma_ptr); - db->buf_pool_ptr = pci_alloc_consistent(pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, &db->buf_pool_dma_ptr); - - db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr; - db->first_tx_desc_dma = db->desc_pool_dma_ptr; - db->buf_pool_start = db->buf_pool_ptr; - db->buf_pool_dma_start = db->buf_pool_dma_ptr; - - db->chip_id = ent->driver_data; - db->ioaddr = pci_resource_start(pdev, 0); - db->chip_revision = dev_rev; - - db->pdev = pdev; - - dev->base_addr = db->ioaddr; - dev->irq = pdev->irq; - pci_set_drvdata(pdev, dev); - dev->open = &dmfe_open; - dev->hard_start_xmit = &dmfe_start_xmit; - dev->stop = &dmfe_stop; - dev->get_stats = &dmfe_get_stats; - dev->set_multicast_list = &dmfe_set_filter_mode; - dev->do_ioctl = &dmfe_do_ioctl; - spin_lock_init(&db->lock); - - pci_read_config_dword(pdev, 0x50, &pci_pmr); - pci_pmr &= 0x70000; - if ( (pci_pmr == 0x10000) && (dev_rev == 0x02000031) ) - db->chip_type = 1; /* DM9102A E3 */ - else - db->chip_type = 0; - - /* read 64 word srom data */ - for (i = 0; i < 64; i++) - ((u16 *) db->srom)[i] = cpu_to_le16(read_srom_word(db->ioaddr, i)); - - /* Set Node address */ - for (i = 0; i < 6; i++) - dev->dev_addr[i] = db->srom[20 + i]; - - err = register_netdev (dev); - if (err) - goto err_out_res; - - printk(KERN_INFO "%s: Davicom DM%04lx at pci%s,", - dev->name, - ent->driver_data >> 16, - pdev->slot_name); - for (i = 0; i < 6; i++) - printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]); - printk(", irq %d.\n", dev->irq); - - pci_set_master(pdev); - - return 0; - -err_out_res: - pci_release_regions(pdev); -err_out_disable: - pci_disable_device(pdev); -err_out_free: - pci_set_drvdata(pdev, NULL); - kfree(dev); - - return err; -} - - -static void __exit dmfe_remove_one (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct dmfe_board_info *db = dev->priv; - - DMFE_DBUG(0, "dmfe_remove_one()", 0); - - if (dev) { - pci_free_consistent(db->pdev, sizeof(struct tx_desc) * - DESC_ALL_CNT + 0x20, db->desc_pool_ptr, - db->desc_pool_dma_ptr); - pci_free_consistent(db->pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, - db->buf_pool_ptr, db->buf_pool_dma_ptr); - unregister_netdev(dev); - pci_release_regions(pdev); - kfree(dev); /* free board information */ - pci_set_drvdata(pdev, NULL); - } - - DMFE_DBUG(0, "dmfe_remove_one() exit", 0); -} - - -/* - * Open the interface. - * The interface is opened whenever "ifconfig" actives it. - */ - -static int dmfe_open(struct DEVICE *dev) -{ - int ret; - struct dmfe_board_info *db = dev->priv; - - DMFE_DBUG(0, "dmfe_open", 0); - - ret = request_irq(dev->irq, &dmfe_interrupt, SA_SHIRQ, dev->name, dev); - if (ret) - return ret; - - /* system variable init */ - db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set; - db->tx_packet_cnt = 0; - db->tx_queue_cnt = 0; - db->rx_avail_cnt = 0; - db->link_failed = 1; - db->wait_reset = 0; - - db->first_in_callback = 0; - db->NIC_capability = 0xf; /* All capability*/ - db->PHY_reg4 = 0x1e0; - - /* CR6 operation mode decision */ - if ( !chkmode || (db->chip_id == PCI_DM9132_ID) || - (db->chip_revision >= 0x02000030) ) { - db->cr6_data |= DMFE_TXTH_256; - db->cr0_data = CR0_DEFAULT; - db->dm910x_chk_mode=4; /* Enter the normal mode */ - } else { - db->cr6_data |= CR6_SFT; /* Store & Forward mode */ - db->cr0_data = 0; - db->dm910x_chk_mode = 1; /* Enter the check mode */ - } - - /* Initilize DM910X board */ - dmfe_init_dm910x(dev); - - /* Active System Interface */ - netif_wake_queue(dev); - - /* set and active a timer process */ - init_timer(&db->timer); - db->timer.expires = DMFE_TIMER_WUT + HZ * 2; - db->timer.data = (unsigned long)dev; - db->timer.function = &dmfe_timer; - add_timer(&db->timer); - - return 0; -} - - -/* Initilize DM910X board - * Reset DM910X board - * Initilize TX/Rx descriptor chain structure - * Send the set-up frame - * Enable Tx/Rx machine - */ - -static void dmfe_init_dm910x(struct DEVICE *dev) -{ - struct dmfe_board_info *db = dev->priv; - unsigned long ioaddr = db->ioaddr; - - DMFE_DBUG(0, "dmfe_init_dm910x()", 0); - - /* Reset DM910x MAC controller */ - outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */ - udelay(100); - outl(db->cr0_data, ioaddr + DCR0); - udelay(5); - - /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */ - db->phy_addr = 1; - - /* Parser SROM and media mode */ - dmfe_parse_srom(db); - db->media_mode = dmfe_media_mode; - - /* RESET Phyxcer Chip by GPR port bit 7 */ - outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */ - if (db->chip_id == PCI_DM9009_ID) { - outl(0x80, ioaddr + DCR12); /* Issue RESET signal */ - mdelay(300); /* Delay 300 ms */ - } - outl(0x0, ioaddr + DCR12); /* Clear RESET signal */ - - /* Process Phyxcer Media Mode */ - if ( !(db->media_mode & 0x10) ) /* Force 1M mode */ - dmfe_set_phyxcer(db); - - /* Media Mode Process */ - if ( !(db->media_mode & DMFE_AUTO) ) - db->op_mode = db->media_mode; /* Force Mode */ - - /* Initiliaze Transmit/Receive decriptor and CR3/4 */ - dmfe_descriptor_init(db, ioaddr); - - /* Init CR6 to program DM910x operation */ - update_cr6(db->cr6_data, ioaddr); - - /* Send setup frame */ - if (db->chip_id == PCI_DM9132_ID) - dm9132_id_table(dev, dev->mc_count); /* DM9132 */ - else - send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */ - - /* Init CR7, interrupt active bit */ - db->cr7_data = CR7_DEFAULT; - outl(db->cr7_data, ioaddr + DCR7); - - /* Init CR15, Tx jabber and Rx watchdog timer */ - outl(db->cr15_data, ioaddr + DCR15); - - /* Enable DM910X Tx/Rx function */ - db->cr6_data |= CR6_RXSC | CR6_TXSC | 0x40000; - update_cr6(db->cr6_data, ioaddr); -} - - -/* - * Hardware start transmission. - * Send a packet to media from the upper layer. - */ - -static int dmfe_start_xmit(struct sk_buff *skb, struct DEVICE *dev) -{ - struct dmfe_board_info *db = dev->priv; - struct tx_desc *txptr; - unsigned long flags; - - DMFE_DBUG(0, "dmfe_start_xmit", 0); - - /* Resource flag check */ - netif_stop_queue(dev); - - /* Too large packet check */ - if (skb->len > MAX_PACKET_SIZE) { - printk(KERN_ERR DRV_NAME ": big packet = %d\n", (u16)skb->len); - dev_kfree_skb(skb); - return 0; - } - - spin_lock_irqsave(&db->lock, flags); - - /* No Tx resource check, it never happen nromally */ - if (db->tx_queue_cnt >= TX_FREE_DESC_CNT) { - spin_unlock_irqrestore(&db->lock, flags); - printk(KERN_ERR DRV_NAME ": No Tx resource %ld\n", db->tx_queue_cnt); - return 1; - } - - /* Disable NIC interrupt */ - outl(0, dev->base_addr + DCR7); - - /* transmit this packet */ - txptr = db->tx_insert_ptr; - memcpy(txptr->tx_buf_ptr, skb->data, skb->len); - txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len); - - /* Point to next transmit free descriptor */ - db->tx_insert_ptr = txptr->next_tx_desc; - - /* Transmit Packet Process */ - if ( (!db->tx_queue_cnt) && (db->tx_packet_cnt < TX_MAX_SEND_CNT) ) { - txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ - db->tx_packet_cnt++; /* Ready to send */ - outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ - dev->trans_start = jiffies; /* saved time stamp */ - } else { - db->tx_queue_cnt++; /* queue TX packet */ - outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ - } - - /* Tx resource check */ - if ( db->tx_queue_cnt < TX_FREE_DESC_CNT ) - netif_wake_queue(dev); - - /* free this SKB */ - dev_kfree_skb(skb); - - /* Restore CR7 to enable interrupt */ - spin_unlock_irqrestore(&db->lock, flags); - outl(db->cr7_data, dev->base_addr + DCR7); - - return 0; -} - - -/* - * Stop the interface. - * The interface is stopped when it is brought. - */ - -static int dmfe_stop(struct DEVICE *dev) -{ - struct dmfe_board_info *db = dev->priv; - unsigned long ioaddr = dev->base_addr; - - DMFE_DBUG(0, "dmfe_stop", 0); - - /* disable system */ - netif_stop_queue(dev); - - /* deleted timer */ - del_timer_sync(&db->timer); - - /* Reset & stop DM910X board */ - outl(DM910X_RESET, ioaddr + DCR0); - udelay(5); - phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); - - /* free interrupt */ - free_irq(dev->irq, dev); - - /* free allocated rx buffer */ - dmfe_free_rxbuffer(db); - -#if 0 - /* show statistic counter */ - printk(DRV_NAME ": FU:%lx EC:%lx LC:%lx NC:%lx LOC:%lx TXJT:%lx RESET:%lx RCR8:%lx FAL:%lx TT:%lx\n", - db->tx_fifo_underrun, db->tx_excessive_collision, - db->tx_late_collision, db->tx_no_carrier, db->tx_loss_carrier, - db->tx_jabber_timeout, db->reset_count, db->reset_cr8, - db->reset_fatal, db->reset_TXtimeout); -#endif - - return 0; -} - - -/* - * DM9102 insterrupt handler - * receive the packet to upper layer, free the transmitted packet - */ - -static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct DEVICE *dev = dev_id; - struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv; - unsigned long ioaddr = dev->base_addr; - unsigned long flags; - - DMFE_DBUG(0, "dmfe_interrupt()", 0); - - if (!dev) { - DMFE_DBUG(1, "dmfe_interrupt() without DEVICE arg", 0); - return; - } - - spin_lock_irqsave(&db->lock, flags); - - /* Got DM910X status */ - db->cr5_data = inl(ioaddr + DCR5); - outl(db->cr5_data, ioaddr + DCR5); - if ( !(db->cr5_data & 0xc1) ) { - spin_unlock_irqrestore(&db->lock, flags); - return; - } - - /* Disable all interrupt in CR7 to solve the interrupt edge problem */ - outl(0, ioaddr + DCR7); - - /* Check system status */ - if (db->cr5_data & 0x2000) { - /* system bus error happen */ - DMFE_DBUG(1, "System bus error happen. CR5=", db->cr5_data); - db->reset_fatal++; - db->wait_reset = 1; /* Need to RESET */ - spin_unlock_irqrestore(&db->lock, flags); - return; - } - - /* Received the coming packet */ - if ( (db->cr5_data & 0x40) && db->rx_avail_cnt ) - dmfe_rx_packet(dev, db); - - /* reallocate rx descriptor buffer */ - if (db->rx_avail_cntcr5_data & 0x01) - dmfe_free_tx_pkt(dev, db); - - /* Mode Check */ - if (db->dm910x_chk_mode & 0x2) { - db->dm910x_chk_mode = 0x4; - db->cr6_data |= 0x100; - update_cr6(db->cr6_data, db->ioaddr); - } - - /* Restore CR7 to enable interrupt mask */ - outl(db->cr7_data, ioaddr + DCR7); - - spin_unlock_irqrestore(&db->lock, flags); -} - - -/* - * Free TX resource after TX complete - */ - -static void dmfe_free_tx_pkt(struct DEVICE *dev, struct dmfe_board_info * db) -{ - struct tx_desc *txptr; - unsigned long ioaddr = dev->base_addr; - u32 tdes0; - - txptr = db->tx_remove_ptr; - while(db->tx_packet_cnt) { - tdes0 = le32_to_cpu(txptr->tdes0); - /* printk(DRV_NAME ": tdes0=%x\n", tdes0); */ - if (tdes0 & 0x80000000) - break; - - /* A packet sent completed */ - db->tx_packet_cnt--; - db->stats.tx_packets++; - - /* Transmit statistic counter */ - if ( tdes0 != 0x7fffffff ) { - /* printk(DRV_NAME ": tdes0=%x\n", tdes0); */ - db->stats.collisions += (tdes0 >> 3) & 0xf; - db->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff; - if (tdes0 & TDES0_ERR_MASK) { - db->stats.tx_errors++; - - if (tdes0 & 0x0002) { /* UnderRun */ - db->tx_fifo_underrun++; - if ( !(db->cr6_data & CR6_SFT) ) { - db->cr6_data = db->cr6_data | CR6_SFT; - update_cr6(db->cr6_data, db->ioaddr); - } - } - if (tdes0 & 0x0100) - db->tx_excessive_collision++; - if (tdes0 & 0x0200) - db->tx_late_collision++; - if (tdes0 & 0x0400) - db->tx_no_carrier++; - if (tdes0 & 0x0800) - db->tx_loss_carrier++; - if (tdes0 & 0x4000) - db->tx_jabber_timeout++; - } - } - - txptr = txptr->next_tx_desc; - }/* End of while */ - - /* Update TX remove pointer to next */ - db->tx_remove_ptr = txptr; - - /* Send the Tx packet in queue */ - if ( (db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt ) { - txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ - db->tx_packet_cnt++; /* Ready to send */ - db->tx_queue_cnt--; - outl(0x1, ioaddr + DCR1); /* Issue Tx polling */ - dev->trans_start = jiffies; /* saved time stamp */ - } - - /* Resource available check */ - if ( db->tx_queue_cnt < TX_WAKE_DESC_CNT ) - netif_wake_queue(dev); /* Active upper layer, send again */ -} - - -/* - * Receive the come packet and pass to upper layer - */ - -static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db) -{ - struct rx_desc *rxptr; - struct sk_buff *skb; - int rxlen; - u32 rdes0; - - rxptr = db->rx_ready_ptr; - - while(db->rx_avail_cnt) { - rdes0 = le32_to_cpu(rxptr->rdes0); - if (rdes0 & 0x80000000) /* packet owner check */ - break; - - db->rx_avail_cnt--; - db->interval_rx_cnt++; - - pci_unmap_single(db->pdev, le32_to_cpu(rxptr->rdes2), RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE); - if ( (rdes0 & 0x300) != 0x300) { - /* A packet without First/Last flag */ - /* reuse this SKB */ - DMFE_DBUG(0, "Reuse SK buffer, rdes0", rdes0); - dmfe_reuse_skb(db, rxptr->rx_skb_ptr); - } else { - /* A packet with First/Last flag */ - rxlen = ( (rdes0 >> 16) & 0x3fff) - 4; - - /* error summary bit check */ - if (rdes0 & 0x8000) { - /* This is a error packet */ - //printk(DRV_NAME ": rdes0: %lx\n", rdes0); - db->stats.rx_errors++; - if (rdes0 & 1) - db->stats.rx_fifo_errors++; - if (rdes0 & 2) - db->stats.rx_crc_errors++; - if (rdes0 & 0x80) - db->stats.rx_length_errors++; - } - - if ( !(rdes0 & 0x8000) || - ((db->cr6_data & CR6_PM) && (rxlen>6)) ) { - skb = rxptr->rx_skb_ptr; - - /* Received Packet CRC check need or not */ - if ( (db->dm910x_chk_mode & 1) && - (cal_CRC(skb->tail, rxlen, 1) != - (*(u32 *) (skb->tail+rxlen) ))) { /* FIXME (?) */ - /* Found a error received packet */ - dmfe_reuse_skb(db, rxptr->rx_skb_ptr); - db->dm910x_chk_mode = 3; - } else { - /* Good packet, send to upper layer */ - /* Shorst packet used new SKB */ - if ( (rxlen < RX_COPY_SIZE) && - ( (skb = dev_alloc_skb(rxlen + 2) ) - != NULL) ) { - /* size less than COPY_SIZE, allocate a rxlen SKB */ - skb->dev = dev; - skb_reserve(skb, 2); /* 16byte align */ - memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->tail, rxlen); - dmfe_reuse_skb(db, rxptr->rx_skb_ptr); - } else { - skb->dev = dev; - skb_put(skb, rxlen); - } - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->last_rx = jiffies; - db->stats.rx_packets++; - db->stats.rx_bytes += rxlen; - } - } else { - /* Reuse SKB buffer when the packet is error */ - DMFE_DBUG(0, "Reuse SK buffer, rdes0", rdes0); - dmfe_reuse_skb(db, rxptr->rx_skb_ptr); - } - } - - rxptr = rxptr->next_rx_desc; - } - - db->rx_ready_ptr = rxptr; -} - - -/* - * Get statistics from driver. - */ - -static struct net_device_stats * dmfe_get_stats(struct DEVICE *dev) -{ - struct dmfe_board_info *db = (struct dmfe_board_info *)dev->priv; - - DMFE_DBUG(0, "dmfe_get_stats", 0); - return &db->stats; -} - - -/* - * Set DM910X multicast address - */ - -static void dmfe_set_filter_mode(struct DEVICE * dev) -{ - struct dmfe_board_info *db = dev->priv; - unsigned long flags; - - DMFE_DBUG(0, "dmfe_set_filter_mode()", 0); - spin_lock_irqsave(&db->lock, flags); - - if (dev->flags & IFF_PROMISC) { - DMFE_DBUG(0, "Enable PROM Mode", 0); - db->cr6_data |= CR6_PM | CR6_PBF; - update_cr6(db->cr6_data, db->ioaddr); - spin_unlock_irqrestore(&db->lock, flags); - return; - } - - if (dev->flags & IFF_ALLMULTI || dev->mc_count > DMFE_MAX_MULTICAST) { - DMFE_DBUG(0, "Pass all multicast address", dev->mc_count); - db->cr6_data &= ~(CR6_PM | CR6_PBF); - db->cr6_data |= CR6_PAM; - spin_unlock_irqrestore(&db->lock, flags); - return; - } - - DMFE_DBUG(0, "Set multicast address", dev->mc_count); - if (db->chip_id == PCI_DM9132_ID) - dm9132_id_table(dev, dev->mc_count); /* DM9132 */ - else - send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */ - spin_unlock_irqrestore(&db->lock, flags); -} - - -/* - * Process the ethtool ioctl command - */ - -static int dmfe_ethtool_ioctl(struct net_device *dev, void *useraddr) -{ - struct dmfe_board_info *db = dev->priv; - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - if (db->pdev) - strcpy(info.bus_info, db->pdev->slot_name); - else - sprintf(info.bus_info, "EISA 0x%lx %d", - dev->base_addr, dev->irq); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - return -EOPNOTSUPP; -} - - -/* - * Process the upper socket ioctl command - */ - -static int dmfe_do_ioctl(struct DEVICE *dev, struct ifreq *ifr, int cmd) -{ - int retval = -EOPNOTSUPP; - DMFE_DBUG(0, "dmfe_do_ioctl()", 0); - - switch(cmd) { - case SIOCETHTOOL: - return dmfe_ethtool_ioctl(dev, (void*)ifr->ifr_data); - } - - return retval; -} - - -/* - * A periodic timer routine - * Dynamic media sense, allocate Rx buffer... - */ - -static void dmfe_timer(unsigned long data) -{ - u32 tmp_cr8; - unsigned char tmp_cr12; - struct DEVICE *dev = (struct DEVICE *) data; - struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv; - unsigned long flags; - - DMFE_DBUG(0, "dmfe_timer()", 0); - spin_lock_irqsave(&db->lock, flags); - - /* Media mode process when Link OK before enter this route */ - if (db->first_in_callback == 0) { - db->first_in_callback = 1; - if (db->chip_type && (db->chip_id==PCI_DM9102_ID)) { - db->cr6_data &= ~0x40000; - update_cr6(db->cr6_data, db->ioaddr); - phy_write(db->ioaddr, db->phy_addr, 0, 0x1000, db->chip_id); - db->cr6_data |= 0x40000; - update_cr6(db->cr6_data, db->ioaddr); - db->timer.expires = DMFE_TIMER_WUT + HZ * 2; - add_timer(&db->timer); - spin_unlock_irqrestore(&db->lock, flags); - return; - } - } - - - /* Operating Mode Check */ - if ( (db->dm910x_chk_mode & 0x1) && - (db->stats.rx_packets > MAX_CHECK_PACKET) ) - db->dm910x_chk_mode = 0x4; - - /* Dynamic reset DM910X : system error or transmit time-out */ - tmp_cr8 = inl(db->ioaddr + DCR8); - if ( (db->interval_rx_cnt==0) && (tmp_cr8) ) { - db->reset_cr8++; - db->wait_reset = 1; - } - db->interval_rx_cnt = 0; - - /* TX polling kick monitor */ - if ( db->tx_packet_cnt && - time_after(jiffies, dev->trans_start + DMFE_TX_KICK) ) { - outl(0x1, dev->base_addr + DCR1); /* Tx polling again */ - - /* TX Timeout */ - if ( time_after(jiffies, dev->trans_start + DMFE_TX_TIMEOUT) ) { - db->reset_TXtimeout++; - db->wait_reset = 1; - printk(KERN_WARNING "%s: Tx timeout - resetting\n", - dev->name); - } - } - - if (db->wait_reset) { - DMFE_DBUG(0, "Dynamic Reset device", db->tx_packet_cnt); - db->reset_count++; - dmfe_dynamic_reset(dev); - db->first_in_callback = 0; - db->timer.expires = DMFE_TIMER_WUT; - add_timer(&db->timer); - spin_unlock_irqrestore(&db->lock, flags); - return; - } - - /* Link status check, Dynamic media type change */ - if (db->chip_id == PCI_DM9132_ID) - tmp_cr12 = inb(db->ioaddr + DCR9 + 3); /* DM9132 */ - else - tmp_cr12 = inb(db->ioaddr + DCR12); /* DM9102/DM9102A */ - - if ( ((db->chip_id == PCI_DM9102_ID) && - (db->chip_revision == 0x02000030)) || - ((db->chip_id == PCI_DM9132_ID) && - (db->chip_revision == 0x02000010)) ) { - /* DM9102A Chip */ - if (tmp_cr12 & 2) - tmp_cr12 = 0x0; /* Link failed */ - else - tmp_cr12 = 0x3; /* Link OK */ - } - - if ( !(tmp_cr12 & 0x3) && !db->link_failed ) { - /* Link Failed */ - DMFE_DBUG(0, "Link Failed", tmp_cr12); - db->link_failed = 1; - - /* For Force 10/100M Half/Full mode: Enable Auto-Nego mode */ - /* AUTO or force 1M Homerun/Longrun don't need */ - if ( !(db->media_mode & 0x38) ) - phy_write(db->ioaddr, db->phy_addr, 0, 0x1000, db->chip_id); - - /* AUTO mode, if INT phyxcer link failed, select EXT device */ - if (db->media_mode & DMFE_AUTO) { - /* 10/100M link failed, used 1M Home-Net */ - db->cr6_data|=0x00040000; /* bit18=1, MII */ - db->cr6_data&=~0x00000200; /* bit9=0, HD mode */ - update_cr6(db->cr6_data, db->ioaddr); - } - } else - if ((tmp_cr12 & 0x3) && db->link_failed) { - DMFE_DBUG(0, "Link link OK", tmp_cr12); - db->link_failed = 0; - - /* Auto Sense Speed */ - if ( (db->media_mode & DMFE_AUTO) && - dmfe_sense_speed(db) ) - db->link_failed = 1; - dmfe_process_mode(db); - /* SHOW_MEDIA_TYPE(db->op_mode); */ - } - - /* HPNA remote command check */ - if (db->HPNA_command & 0xf00) { - db->HPNA_timer--; - if (!db->HPNA_timer) - dmfe_HPNA_remote_cmd_chk(db); - } - - /* Timer active again */ - db->timer.expires = DMFE_TIMER_WUT; - add_timer(&db->timer); - spin_unlock_irqrestore(&db->lock, flags); -} - - -/* - * Dynamic reset the DM910X board - * Stop DM910X board - * Free Tx/Rx allocated memory - * Reset DM910X board - * Re-initilize DM910X board - */ - -static void dmfe_dynamic_reset(struct DEVICE *dev) -{ - struct dmfe_board_info *db = dev->priv; - - DMFE_DBUG(0, "dmfe_dynamic_reset()", 0); - - /* Sopt MAC controller */ - db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); /* Disable Tx/Rx */ - update_cr6(db->cr6_data, dev->base_addr); - outl(0, dev->base_addr + DCR7); /* Disable Interrupt */ - outl(inl(dev->base_addr + DCR5), dev->base_addr + DCR5); - - /* Disable upper layer interface */ - netif_stop_queue(dev); - - /* Free Rx Allocate buffer */ - dmfe_free_rxbuffer(db); - - /* system variable init */ - db->tx_packet_cnt = 0; - db->tx_queue_cnt = 0; - db->rx_avail_cnt = 0; - db->link_failed = 1; - db->wait_reset = 0; - - /* Re-initilize DM910X board */ - dmfe_init_dm910x(dev); - - /* Restart upper layer interface */ - netif_wake_queue(dev); -} - - -/* - * free all allocated rx buffer - */ - -static void dmfe_free_rxbuffer(struct dmfe_board_info * db) -{ - DMFE_DBUG(0, "dmfe_free_rxbuffer()", 0); - - /* free allocated rx buffer */ - while (db->rx_avail_cnt) { - dev_kfree_skb(db->rx_ready_ptr->rx_skb_ptr); - db->rx_ready_ptr = db->rx_ready_ptr->next_rx_desc; - db->rx_avail_cnt--; - } -} - - -/* - * Reuse the SK buffer - */ - -static void dmfe_reuse_skb(struct dmfe_board_info *db, struct sk_buff * skb) -{ - struct rx_desc *rxptr = db->rx_insert_ptr; - - if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) { - rxptr->rx_skb_ptr = skb; - rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); - wmb(); - rxptr->rdes0 = cpu_to_le32(0x80000000); - db->rx_avail_cnt++; - db->rx_insert_ptr = rxptr->next_rx_desc; - } else - DMFE_DBUG(0, "SK Buffer reuse method error", db->rx_avail_cnt); -} - - -/* - * Initialize transmit/Receive descriptor - * Using Chain structure, and allocate Tx/Rx buffer - */ - -static void dmfe_descriptor_init(struct dmfe_board_info *db, unsigned long ioaddr) -{ - struct tx_desc *tmp_tx; - struct rx_desc *tmp_rx; - unsigned char *tmp_buf; - dma_addr_t tmp_tx_dma, tmp_rx_dma; - dma_addr_t tmp_buf_dma; - int i; - - DMFE_DBUG(0, "dmfe_descriptor_init()", 0); - - /* tx descriptor start pointer */ - db->tx_insert_ptr = db->first_tx_desc; - db->tx_remove_ptr = db->first_tx_desc; - outl(db->first_tx_desc_dma, ioaddr + DCR4); /* TX DESC address */ - - /* rx descriptor start pointer */ - db->first_rx_desc = (void *)db->first_tx_desc + sizeof(struct tx_desc) * TX_DESC_CNT; - db->first_rx_desc_dma = db->first_tx_desc_dma + sizeof(struct tx_desc) * TX_DESC_CNT; - db->rx_insert_ptr = db->first_rx_desc; - db->rx_ready_ptr = db->first_rx_desc; - outl(db->first_rx_desc_dma, ioaddr + DCR3); /* RX DESC address */ - - /* Init Transmit chain */ - tmp_buf = db->buf_pool_start; - tmp_buf_dma = db->buf_pool_dma_start; - tmp_tx_dma = db->first_tx_desc_dma; - for (tmp_tx = db->first_tx_desc, i = 0; i < TX_DESC_CNT; i++, tmp_tx++) { - tmp_tx->tx_buf_ptr = tmp_buf; - tmp_tx->tdes0 = cpu_to_le32(0); - tmp_tx->tdes1 = cpu_to_le32(0x81000000); /* IC, chain */ - tmp_tx->tdes2 = cpu_to_le32(tmp_buf_dma); - tmp_tx_dma += sizeof(struct tx_desc); - tmp_tx->tdes3 = cpu_to_le32(tmp_tx_dma); - tmp_tx->next_tx_desc = tmp_tx + 1; - tmp_buf = tmp_buf + TX_BUF_ALLOC; - tmp_buf_dma = tmp_buf_dma + TX_BUF_ALLOC; - } - (--tmp_tx)->tdes3 = cpu_to_le32(db->first_tx_desc_dma); - tmp_tx->next_tx_desc = db->first_tx_desc; - - /* Init Receive descriptor chain */ - tmp_rx_dma=db->first_rx_desc_dma; - for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT; i++, tmp_rx++) { - tmp_rx->rdes0 = cpu_to_le32(0); - tmp_rx->rdes1 = cpu_to_le32(0x01000600); - tmp_rx_dma += sizeof(struct rx_desc); - tmp_rx->rdes3 = cpu_to_le32(tmp_rx_dma); - tmp_rx->next_rx_desc = tmp_rx + 1; - } - (--tmp_rx)->rdes3 = cpu_to_le32(db->first_rx_desc_dma); - tmp_rx->next_rx_desc = db->first_rx_desc; - - /* pre-allocate Rx buffer */ - allocate_rx_buffer(db); -} - - -/* - * Update CR6 value - * Firstly stop DM910X , then written value and start - */ - -static void update_cr6(u32 cr6_data, unsigned long ioaddr) -{ - u32 cr6_tmp; - - cr6_tmp = cr6_data & ~0x2002; /* stop Tx/Rx */ - outl(cr6_tmp, ioaddr + DCR6); - udelay(5); - outl(cr6_data, ioaddr + DCR6); - udelay(5); -} - - -/* - * Send a setup frame for DM9132 - * This setup frame initilize DM910X addres filter mode -*/ - -static void dm9132_id_table(struct DEVICE *dev, int mc_cnt) -{ - struct dev_mc_list *mcptr; - u16 * addrptr; - unsigned long ioaddr = dev->base_addr+0xc0; /* ID Table */ - u32 hash_val; - u16 i, hash_table[4]; - - DMFE_DBUG(0, "dm9132_id_table()", 0); - - /* Node address */ - addrptr = (u16 *) dev->dev_addr; - outw(addrptr[0], ioaddr); - ioaddr += 4; - outw(addrptr[1], ioaddr); - ioaddr += 4; - outw(addrptr[2], ioaddr); - ioaddr += 4; - - /* Clear Hash Table */ - for (i = 0; i < 4; i++) - hash_table[i] = 0x0; - - /* broadcast address */ - hash_table[3] = 0x8000; - - /* the multicast address in Hash Table : 64 bits */ - for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) { - hash_val = cal_CRC( (char *) mcptr->dmi_addr, 6, 0) & 0x3f; - hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16); - } - - /* Write the hash table to MAC MD table */ - for (i = 0; i < 4; i++, ioaddr += 4) - outw(hash_table[i], ioaddr); -} - - -/* - * Send a setup frame for DM9102/DM9102A - * This setup frame initilize DM910X addres filter mode - */ - -static void send_filter_frame(struct DEVICE *dev, int mc_cnt) -{ - struct dmfe_board_info *db = dev->priv; - struct dev_mc_list *mcptr; - struct tx_desc *txptr; - u16 * addrptr; - u32 * suptr; - int i; - - DMFE_DBUG(0, "send_filter_frame()", 0); - - txptr = db->tx_insert_ptr; - suptr = (u32 *) txptr->tx_buf_ptr; - - /* Node address */ - addrptr = (u16 *) dev->dev_addr; - *suptr++ = addrptr[0]; - *suptr++ = addrptr[1]; - *suptr++ = addrptr[2]; - - /* broadcast address */ - *suptr++ = 0xffff; - *suptr++ = 0xffff; - *suptr++ = 0xffff; - - /* fit the multicast address */ - for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) { - addrptr = (u16 *) mcptr->dmi_addr; - *suptr++ = addrptr[0]; - *suptr++ = addrptr[1]; - *suptr++ = addrptr[2]; - } - - for (; i<14; i++) { - *suptr++ = 0xffff; - *suptr++ = 0xffff; - *suptr++ = 0xffff; - } - - /* prepare the setup frame */ - db->tx_insert_ptr = txptr->next_tx_desc; - txptr->tdes1 = cpu_to_le32(0x890000c0); - - /* Resource Check and Send the setup packet */ - if (!db->tx_packet_cnt) { - /* Resource Empty */ - db->tx_packet_cnt++; - txptr->tdes0 = cpu_to_le32(0x80000000); - update_cr6(db->cr6_data | 0x2000, dev->base_addr); - outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ - update_cr6(db->cr6_data, dev->base_addr); - dev->trans_start = jiffies; - } else - db->tx_queue_cnt++; /* Put in TX queue */ -} - - -/* - * Allocate rx buffer, - * As possible as allocate maxiumn Rx buffer - */ - -static void allocate_rx_buffer(struct dmfe_board_info *db) -{ - struct rx_desc *rxptr; - struct sk_buff *skb; - - rxptr = db->rx_insert_ptr; - - while(db->rx_avail_cnt < RX_DESC_CNT) { - if ( ( skb = dev_alloc_skb(RX_ALLOC_SIZE) ) == NULL ) - break; - rxptr->rx_skb_ptr = skb; /* FIXME (?) */ - rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); - wmb(); - rxptr->rdes0 = cpu_to_le32(0x80000000); - rxptr = rxptr->next_rx_desc; - db->rx_avail_cnt++; - } - - db->rx_insert_ptr = rxptr; -} - - -/* - * Read one word data from the serial ROM - */ - -static u16 read_srom_word(long ioaddr, int offset) -{ - int i; - u16 srom_data = 0; - long cr9_ioaddr = ioaddr + DCR9; - - outl(CR9_SROM_READ, cr9_ioaddr); - outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); - - /* Send the Read Command 110b */ - SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); - SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); - SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr); - - /* Send the offset */ - for (i = 5; i >= 0; i--) { - srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0; - SROM_CLK_WRITE(srom_data, cr9_ioaddr); - } - - outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); - - for (i = 16; i > 0; i--) { - outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr); - udelay(5); - srom_data = (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1 : 0); - outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); - udelay(5); - } - - outl(CR9_SROM_READ, cr9_ioaddr); - return srom_data; -} - - -/* - * Auto sense the media mode - */ - -static u8 dmfe_sense_speed(struct dmfe_board_info * db) -{ - u8 ErrFlag = 0; - u16 phy_mode; - - /* CR6 bit18=0, select 10/100M */ - update_cr6( (db->cr6_data & ~0x40000), db->ioaddr); - - phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id); - phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id); - - if ( (phy_mode & 0x24) == 0x24 ) { - if (db->chip_id == PCI_DM9132_ID) /* DM9132 */ - phy_mode = phy_read(db->ioaddr, db->phy_addr, 7, db->chip_id) & 0xf000; - else /* DM9102/DM9102A */ - phy_mode = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0xf000; - /* printk(DRV_NAME ": Phy_mode %x ",phy_mode); */ - switch (phy_mode) { - case 0x1000: db->op_mode = DMFE_10MHF; break; - case 0x2000: db->op_mode = DMFE_10MFD; break; - case 0x4000: db->op_mode = DMFE_100MHF; break; - case 0x8000: db->op_mode = DMFE_100MFD; break; - default: db->op_mode = DMFE_10MHF; - ErrFlag = 1; - break; - } - } else { - db->op_mode = DMFE_10MHF; - DMFE_DBUG(0, "Link Failed :", phy_mode); - ErrFlag = 1; - } - - return ErrFlag; -} - - -/* - * Set 10/100 phyxcer capability - * AUTO mode : phyxcer register4 is NIC capability - * Force mode: phyxcer register4 is the force media - */ - -static void dmfe_set_phyxcer(struct dmfe_board_info *db) -{ - u16 phy_reg; - - /* Select 10/100M phyxcer */ - db->cr6_data &= ~0x40000; - update_cr6(db->cr6_data, db->ioaddr); - - /* DM9009 Chip: Phyxcer reg18 bit12=0 */ - if (db->chip_id == PCI_DM9009_ID) { - phy_reg = phy_read(db->ioaddr, db->phy_addr, 18, db->chip_id) & ~0x1000; - phy_write(db->ioaddr, db->phy_addr, 18, phy_reg, db->chip_id); - } - - /* Phyxcer capability setting */ - phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x01e0; - - if (db->media_mode & DMFE_AUTO) { - /* AUTO Mode */ - phy_reg |= db->PHY_reg4; - } else { - /* Force Mode */ - switch(db->media_mode) { - case DMFE_10MHF: phy_reg |= 0x20; break; - case DMFE_10MFD: phy_reg |= 0x40; break; - case DMFE_100MHF: phy_reg |= 0x80; break; - case DMFE_100MFD: phy_reg |= 0x100; break; - } - if (db->chip_id == PCI_DM9009_ID) phy_reg &= 0x61; - } - - /* Write new capability to Phyxcer Reg4 */ - if ( !(phy_reg & 0x01e0)) { - phy_reg|=db->PHY_reg4; - db->media_mode|=DMFE_AUTO; - } - phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id); - - /* Restart Auto-Negotiation */ - if ( db->chip_type && (db->chip_id == PCI_DM9102_ID) ) - phy_write(db->ioaddr, db->phy_addr, 0, 0x1800, db->chip_id); - if ( !db->chip_type ) - phy_write(db->ioaddr, db->phy_addr, 0, 0x1200, db->chip_id); -} - - -/* - * Process op-mode - * AUTO mode : PHY controller in Auto-negotiation Mode - * Force mode: PHY controller in force mode with HUB - * N-way force capability with SWITCH - */ - -static void dmfe_process_mode(struct dmfe_board_info *db) -{ - u16 phy_reg; - - /* Full Duplex Mode Check */ - if (db->op_mode & 0x4) - db->cr6_data |= CR6_FDM; /* Set Full Duplex Bit */ - else - db->cr6_data &= ~CR6_FDM; /* Clear Full Duplex Bit */ - - /* Transciver Selection */ - if (db->op_mode & 0x10) /* 1M HomePNA */ - db->cr6_data |= 0x40000;/* External MII select */ - else - db->cr6_data &= ~0x40000;/* Internal 10/100 transciver */ - - update_cr6(db->cr6_data, db->ioaddr); - - /* 10/100M phyxcer force mode need */ - if ( !(db->media_mode & 0x18)) { - /* Forece Mode */ - phy_reg = phy_read(db->ioaddr, db->phy_addr, 6, db->chip_id); - if ( !(phy_reg & 0x1) ) { - /* parter without N-Way capability */ - phy_reg = 0x0; - switch(db->op_mode) { - case DMFE_10MHF: phy_reg = 0x0; break; - case DMFE_10MFD: phy_reg = 0x100; break; - case DMFE_100MHF: phy_reg = 0x2000; break; - case DMFE_100MFD: phy_reg = 0x2100; break; - } - phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id); - if ( db->chip_type && (db->chip_id == PCI_DM9102_ID) ) - mdelay(20); - phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id); - } - } -} - - -/* - * Write a word to Phy register - */ - -static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, u16 phy_data, u32 chip_id) -{ - u16 i; - unsigned long ioaddr; - - if (chip_id == PCI_DM9132_ID) { - ioaddr = iobase + 0x80 + offset * 4; - outw(phy_data, ioaddr); - } else { - /* DM9102/DM9102A Chip */ - ioaddr = iobase + DCR9; - - /* Send 33 synchronization clock to Phy controller */ - for (i = 0; i < 35; i++) - phy_write_1bit(ioaddr, PHY_DATA_1); - - /* Send start command(01) to Phy */ - phy_write_1bit(ioaddr, PHY_DATA_0); - phy_write_1bit(ioaddr, PHY_DATA_1); - - /* Send write command(01) to Phy */ - phy_write_1bit(ioaddr, PHY_DATA_0); - phy_write_1bit(ioaddr, PHY_DATA_1); - - /* Send Phy addres */ - for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); - - /* Send register addres */ - for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); - - /* written trasnition */ - phy_write_1bit(ioaddr, PHY_DATA_1); - phy_write_1bit(ioaddr, PHY_DATA_0); - - /* Write a word data to PHY controller */ - for ( i = 0x8000; i > 0; i >>= 1) - phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0); - } -} - - -/* - * Read a word data from phy register - */ - -static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset, u32 chip_id) -{ - int i; - u16 phy_data; - unsigned long ioaddr; - - if (chip_id == PCI_DM9132_ID) { - /* DM9132 Chip */ - ioaddr = iobase + 0x80 + offset * 4; - phy_data = inw(ioaddr); - } else { - /* DM9102/DM9102A Chip */ - ioaddr = iobase + DCR9; - - /* Send 33 synchronization clock to Phy controller */ - for (i = 0; i < 35; i++) - phy_write_1bit(ioaddr, PHY_DATA_1); - - /* Send start command(01) to Phy */ - phy_write_1bit(ioaddr, PHY_DATA_0); - phy_write_1bit(ioaddr, PHY_DATA_1); - - /* Send read command(10) to Phy */ - phy_write_1bit(ioaddr, PHY_DATA_1); - phy_write_1bit(ioaddr, PHY_DATA_0); - - /* Send Phy addres */ - for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); - - /* Send register addres */ - for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); - - /* Skip transition state */ - phy_read_1bit(ioaddr); - - /* read 16bit data */ - for (phy_data = 0, i = 0; i < 16; i++) { - phy_data <<= 1; - phy_data |= phy_read_1bit(ioaddr); - } - } - - return phy_data; -} - - -/* - * Write one bit data to Phy Controller - */ - -static void phy_write_1bit(unsigned long ioaddr, u32 phy_data) -{ - outl(phy_data, ioaddr); /* MII Clock Low */ - udelay(1); - outl(phy_data | MDCLKH, ioaddr); /* MII Clock High */ - udelay(1); - outl(phy_data, ioaddr); /* MII Clock Low */ - udelay(1); -} - - -/* - * Read one bit phy data from PHY controller - */ - -static u16 phy_read_1bit(unsigned long ioaddr) -{ - u16 phy_data; - - outl(0x50000, ioaddr); - udelay(1); - phy_data = ( inl(ioaddr) >> 19 ) & 0x1; - outl(0x40000, ioaddr); - udelay(1); - - return phy_data; -} - - -/* - * Calculate the CRC valude of the Rx packet - * flag = 1 : return the reverse CRC (for the received packet CRC) - * 0 : return the normal CRC (for Hash Table index) - */ - -static inline u32 cal_CRC(unsigned char * Data, unsigned int Len, u8 flag) -{ - u32 crc = crc32(~0, Data, Len); - if (flag) crc = ~crc; - return crc; -} - - -/* - * Parser SROM and media mode - */ - -static void dmfe_parse_srom(struct dmfe_board_info * db) -{ - char * srom = db->srom; - int dmfe_mode, tmp_reg; - - DMFE_DBUG(0, "dmfe_parse_srom() ", 0); - - /* Init CR15 */ - db->cr15_data = CR15_DEFAULT; - - /* Check SROM Version */ - if ( ( (int) srom[18] & 0xff) == SROM_V41_CODE) { - /* SROM V4.01 */ - /* Get NIC support media mode */ - db->NIC_capability = le16_to_cpup(srom + 34); - db->PHY_reg4 = 0; - for (tmp_reg = 1; tmp_reg < 0x10; tmp_reg <<= 1) { - switch( db->NIC_capability & tmp_reg ) { - case 0x1: db->PHY_reg4 |= 0x0020; break; - case 0x2: db->PHY_reg4 |= 0x0040; break; - case 0x4: db->PHY_reg4 |= 0x0080; break; - case 0x8: db->PHY_reg4 |= 0x0100; break; - } - } - - /* Media Mode Force or not check */ - dmfe_mode = le32_to_cpup(srom + 34) & le32_to_cpup(srom + 36); - switch(dmfe_mode) { - case 0x4: dmfe_media_mode = DMFE_100MHF; break; /* 100MHF */ - case 0x2: dmfe_media_mode = DMFE_10MFD; break; /* 10MFD */ - case 0x8: dmfe_media_mode = DMFE_100MFD; break; /* 100MFD */ - case 0x100: - case 0x200: dmfe_media_mode = DMFE_1M_HPNA; break;/* HomePNA */ - } - - /* Special Function setting */ - /* VLAN function */ - if ( (SF_mode & 0x1) || (srom[43] & 0x80) ) - db->cr15_data |= 0x40; - - /* Flow Control */ - if ( (SF_mode & 0x2) || (srom[40] & 0x1) ) - db->cr15_data |= 0x400; - - /* TX pause packet */ - if ( (SF_mode & 0x4) || (srom[40] & 0xe) ) - db->cr15_data |= 0x9800; - } - - /* Parse HPNA parameter */ - db->HPNA_command = 1; - - /* Accept remote command or not */ - if (HPNA_rx_cmd == 0) - db->HPNA_command |= 0x8000; - - /* Issue remote command & operation mode */ - if (HPNA_tx_cmd == 1) - switch(HPNA_mode) { /* Issue Remote Command */ - case 0: db->HPNA_command |= 0x0904; break; - case 1: db->HPNA_command |= 0x0a00; break; - case 2: db->HPNA_command |= 0x0506; break; - case 3: db->HPNA_command |= 0x0602; break; - } - else - switch(HPNA_mode) { /* Don't Issue */ - case 0: db->HPNA_command |= 0x0004; break; - case 1: db->HPNA_command |= 0x0000; break; - case 2: db->HPNA_command |= 0x0006; break; - case 3: db->HPNA_command |= 0x0002; break; - } - - /* Check DM9801 or DM9802 present or not */ - db->HPNA_present = 0; - update_cr6(db->cr6_data|0x40000, db->ioaddr); - tmp_reg = phy_read(db->ioaddr, db->phy_addr, 3, db->chip_id); - if ( ( tmp_reg & 0xfff0 ) == 0xb900 ) { - /* DM9801 or DM9802 present */ - db->HPNA_timer = 8; - if ( phy_read(db->ioaddr, db->phy_addr, 31, db->chip_id) == 0x4404) { - /* DM9801 HomeRun */ - db->HPNA_present = 1; - dmfe_program_DM9801(db, tmp_reg); - } else { - /* DM9802 LongRun */ - db->HPNA_present = 2; - dmfe_program_DM9802(db); - } - } - -} - - -/* - * Init HomeRun DM9801 - */ - -static void dmfe_program_DM9801(struct dmfe_board_info * db, int HPNA_rev) -{ - uint reg17, reg25; - - if ( !HPNA_NoiseFloor ) HPNA_NoiseFloor = DM9801_NOISE_FLOOR; - switch(HPNA_rev) { - case 0xb900: /* DM9801 E3 */ - db->HPNA_command |= 0x1000; - reg25 = phy_read(db->ioaddr, db->phy_addr, 24, db->chip_id); - reg25 = ( (reg25 + HPNA_NoiseFloor) & 0xff) | 0xf000; - reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); - break; - case 0xb901: /* DM9801 E4 */ - reg25 = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); - reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor; - reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); - reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor + 3; - break; - case 0xb902: /* DM9801 E5 */ - case 0xb903: /* DM9801 E6 */ - default: - db->HPNA_command |= 0x1000; - reg25 = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); - reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor - 5; - reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); - reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor; - break; - } - phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); - phy_write(db->ioaddr, db->phy_addr, 17, reg17, db->chip_id); - phy_write(db->ioaddr, db->phy_addr, 25, reg25, db->chip_id); -} - - -/* - * Init HomeRun DM9802 - */ - -static void dmfe_program_DM9802(struct dmfe_board_info * db) -{ - uint phy_reg; - - if ( !HPNA_NoiseFloor ) HPNA_NoiseFloor = DM9802_NOISE_FLOOR; - phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); - phy_reg = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); - phy_reg = ( phy_reg & 0xff00) + HPNA_NoiseFloor; - phy_write(db->ioaddr, db->phy_addr, 25, phy_reg, db->chip_id); -} - - -/* - * Check remote HPNA power and speed status. If not correct, - * issue command again. -*/ - -static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info * db) -{ - uint phy_reg; - - /* Got remote device status */ - phy_reg = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0x60; - switch(phy_reg) { - case 0x00: phy_reg = 0x0a00;break; /* LP/LS */ - case 0x20: phy_reg = 0x0900;break; /* LP/HS */ - case 0x40: phy_reg = 0x0600;break; /* HP/LS */ - case 0x60: phy_reg = 0x0500;break; /* HP/HS */ - } - - /* Check remote device status match our setting ot not */ - if ( phy_reg != (db->HPNA_command & 0x0f00) ) { - phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); - db->HPNA_timer=8; - } else - db->HPNA_timer=600; /* Match, every 10 minutes, check */ -} - - - -static struct pci_device_id dmfe_pci_tbl[] __devinitdata = { - { 0x1282, 0x9132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9132_ID }, - { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9102_ID }, - { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9100_ID }, - { 0x1282, 0x9009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9009_ID }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, dmfe_pci_tbl); - - -static struct pci_driver dmfe_driver = { - name: "dmfe", - id_table: dmfe_pci_tbl, - probe: dmfe_init_one, - remove: dmfe_remove_one, -}; - -MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw"); -MODULE_DESCRIPTION("Davicom DM910X fast ethernet driver"); -MODULE_LICENSE("GPL"); - -MODULE_PARM(debug, "i"); -MODULE_PARM(mode, "i"); -MODULE_PARM(cr6set, "i"); -MODULE_PARM(chkmode, "i"); -MODULE_PARM(HPNA_mode, "i"); -MODULE_PARM(HPNA_rx_cmd, "i"); -MODULE_PARM(HPNA_tx_cmd, "i"); -MODULE_PARM(HPNA_NoiseFloor, "i"); -MODULE_PARM(SF_mode, "i"); -MODULE_PARM_DESC(debug, "Davicom DM9xxx enable debugging (0-1)"); -MODULE_PARM_DESC(mode, "Davicom DM9xxx: Bit 0: 10/100Mbps, bit 2: duplex, bit 8: HomePNA"); -MODULE_PARM_DESC(SF_mode, "Davicom DM9xxx special function (bit 0: VLAN, bit 1 Flow Control, bit 2: TX pause packet)"); - -/* Description: - * when user used insmod to add module, system invoked init_module() - * to initilize and register. - */ - -static int __init dmfe_init_module(void) -{ - int rc; - - printk(version); - printed_version = 1; - - DMFE_DBUG(0, "init_module() ", debug); - - if (debug) - dmfe_debug = debug; /* set debug flag */ - if (cr6set) - dmfe_cr6_user_set = cr6set; - - switch(mode) { - case DMFE_10MHF: - case DMFE_100MHF: - case DMFE_10MFD: - case DMFE_100MFD: - case DMFE_1M_HPNA: - dmfe_media_mode = mode; - break; - default:dmfe_media_mode = DMFE_AUTO; - break; - } - - if (HPNA_mode > 4) - HPNA_mode = 0; /* Default: LP/HS */ - if (HPNA_rx_cmd > 1) - HPNA_rx_cmd = 0; /* Default: Ignored remote cmd */ - if (HPNA_tx_cmd > 1) - HPNA_tx_cmd = 0; /* Default: Don't issue remote cmd */ - if (HPNA_NoiseFloor > 15) - HPNA_NoiseFloor = 0; - - rc = pci_module_init(&dmfe_driver); - if (rc < 0) - return rc; - - return 0; -} - - -/* - * Description: - * when user used rmmod to delete module, system invoked clean_module() - * to un-register all registered services. - */ - -static void __exit dmfe_cleanup_module(void) -{ - DMFE_DBUG(0, "dmfe_clean_module() ", debug); - pci_unregister_driver(&dmfe_driver); -} - -module_init(dmfe_init_module); -module_exit(dmfe_cleanup_module); diff -Nru a/drivers/net/e100/Makefile b/drivers/net/e100/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e100/Makefile Thu Mar 7 18:17:47 2002 @@ -0,0 +1,16 @@ +# +# Makefile for the Intel's E100 ethernet driver +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := e100.o + +obj-y := e100_main.o e100_config.o e100_proc.o e100_phy.o \ + e100_eeprom.o +obj-m := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff -Nru a/drivers/net/e100/e100.h b/drivers/net/e100/e100.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e100/e100.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,1033 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. +*******************************************************************************/ + +#ifndef _E100_INC_ +#define _E100_INC_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SIOCETHTOOL +#include +#include +#endif + +#include +#include +#include +#include + +/* + * Configure parameters for buffers per controller. + * If the machine this is being used on is a faster machine (i.e. > 150MHz) + * and running on a 10MBS network then more queueing of data occurs. This + * may indicate the some of the numbers below should be adjusted. Here are + * some typical numbers: + * MAX_TCB 64 + * MAX_RFD 64 + * The default numbers give work well on most systems tests so no real + * adjustments really need to take place. Also, if the machine is connected + * to a 100MBS network the numbers described above can be lowered from the + * defaults as considerably less data will be queued. + */ + +#define MAX_TCB 64 /* number of transmit control blocks */ +#define MAX_TBD MAX_TCB +#define TX_FRAME_CNT 8 /* consecutive transmit frames per interrupt */ +/* TX_FRAME_CNT must be less than MAX_TCB */ +#define MAX_RFD 64 + +#define E100_DEFAULT_TCB MAX_TCB +#define E100_MIN_TCB 2*TX_FRAME_CNT + 3 /* make room for at least 2 interrupts */ + +#ifdef __ia64__ + /* We can't use too many DMAble buffers on IA64 machines with >4 GB mem */ +#define E100_MAX_TCB 64 +#else +#define E100_MAX_TCB 1024 +#endif /* __ia64__ */ + +#define E100_DEFAULT_RFD MAX_RFD +#define E100_MIN_RFD 8 + +#ifdef __ia64__ + /* We can't use too many DMAble buffers on IA64 machines with >4 GB mem */ +#define E100_MAX_RFD 64 +#else +#define E100_MAX_RFD 1024 +#endif /* __ia64__ */ + +#define E100_DEFAULT_XSUM true +#define E100_DEFAULT_BER ZLOCK_MAX_ERRORS +#define E100_DEFAULT_SPEED_DUPLEX 0 +#define E100_DEFAULT_FC 0 +#define E100_DEFAULT_IFS true +#define E100_DEFAULT_UCODE true + +#define TX_THRSHLD 8 + +/* sleep time is at least 50 ms, in jiffies */ +#define SLEEP_TIME ((HZ / 20) + 1) +#define CUS_TIMEOUT 1000 + +/* IFS parameters */ +#define MIN_NUMBER_OF_TRANSMITS_100 1000 +#define MIN_NUMBER_OF_TRANSMITS_10 100 + +#define E100_MAX_NIC 16 + +#define E100_MAX_BUSY_WAIT 50 /*Max udelays in wait_scb and wait_cus_idle */ + +/* CPUSAVER_BUNDLE_MAX: Sets the maximum number of frames that will be bundled. + * In some situations, such as the TCP windowing algorithm, it may be + * better to limit the growth of the bundle size than let it go as + * high as it can, because that could cause too much added latency. + * The default is six, because this is the number of packets in the + * default TCP window size. A value of 1 would make CPUSaver indicate + * an interrupt for every frame received. If you do not want to put + * a limit on the bundle size, set this value to xFFFF. + */ +#define E100_DEFAULT_CPUSAVER_BUNDLE_MAX 6 +#define E100_DEFAULT_CPUSAVER_INTERRUPT_DELAY 0x600 +#define E100_DEFAULT_BUNDLE_SMALL_FR false +#define E100_DEFAULT_RX_CONGESTION_CONTROL true + +/* end of configurables */ + +/* ====================================================================== */ +/* hw */ +/* ====================================================================== */ + +/* timeout for command completion */ +#define E100_CMD_WAIT 100 /* iterations */ + +struct driver_stats { + struct net_device_stats net_stats; + + unsigned long tx_late_col; + unsigned long tx_ok_defrd; + unsigned long tx_one_retry; + unsigned long tx_mt_one_retry; + unsigned long rcv_cdt_frames; + unsigned long xmt_fc_pkts; + unsigned long rcv_fc_pkts; + unsigned long rcv_fc_unsupported; + unsigned long xmt_tco_pkts; + unsigned long rcv_tco_pkts; + unsigned long rx_intr_pkts; + unsigned long rx_tasklet_pkts; + unsigned long poll_intr_switch; +}; + +/* TODO: kill me when we can do C99 */ +#define false (0) +#define true (1) + +/* Changed for 82558 and 82559 enhancements */ +/* defines for 82558/9 flow control CSR values */ +#define DFLT_FC_THLD 0x00 /* Rx FIFO threshold of 0.5KB free */ +#define DFLT_FC_CMD 0x00 /* FC Command in CSR */ + +/* ====================================================================== */ +/* equates */ +/* ====================================================================== */ + +/* + * These are general purpose defines + */ + +/* Bit Mask definitions */ +#define BIT_0 0x0001 +#define BIT_1 0x0002 +#define BIT_2 0x0004 +#define BIT_3 0x0008 +#define BIT_4 0x0010 +#define BIT_5 0x0020 +#define BIT_6 0x0040 +#define BIT_7 0x0080 +#define BIT_8 0x0100 +#define BIT_9 0x0200 +#define BIT_10 0x0400 +#define BIT_11 0x0800 +#define BIT_12 0x1000 +#define BIT_13 0x2000 +#define BIT_14 0x4000 +#define BIT_15 0x8000 +#define BIT_28 0x10000000 + +#define BIT_0_2 0x0007 +#define BIT_0_3 0x000F +#define BIT_0_4 0x001F +#define BIT_0_5 0x003F +#define BIT_0_6 0x007F +#define BIT_0_7 0x00FF +#define BIT_0_8 0x01FF +#define BIT_0_13 0x3FFF +#define BIT_0_15 0xFFFF +#define BIT_1_2 0x0006 +#define BIT_1_3 0x000E +#define BIT_2_5 0x003C +#define BIT_3_4 0x0018 +#define BIT_4_5 0x0030 +#define BIT_4_6 0x0070 +#define BIT_4_7 0x00F0 +#define BIT_5_7 0x00E0 +#define BIT_5_12 0x1FE0 +#define BIT_5_15 0xFFE0 +#define BIT_6_7 0x00c0 +#define BIT_7_11 0x0F80 +#define BIT_8_10 0x0700 +#define BIT_9_13 0x3E00 +#define BIT_12_15 0xF000 +#define BIT_8_15 0xFF00 + +#define BIT_16_20 0x001F0000 +#define BIT_21_25 0x03E00000 +#define BIT_26_27 0x0C000000 + +/* Transmit Threshold related constants */ +#define DEFAULT_TX_PER_UNDERRUN 20000 + +#define MAX_MULTICAST_ADDRS 64 +#define MAX_FILTER 16 + +#define FULL_DUPLEX 2 +#define HALF_DUPLEX 1 + +/* + * These defines are specific to the 82557 + */ + +/* E100 PORT functions -- lower 4 bits */ +#define PORT_SOFTWARE_RESET 0 +#define PORT_SELFTEST 1 +#define PORT_SELECTIVE_RESET 2 +#define PORT_DUMP 3 + +/* SCB Status Word bit definitions */ +/* Interrupt status/ack fields */ +/* ER and FCP interrupts for 82558 masks */ +#define SCB_STATUS_ACK_MASK BIT_8_15 /* Status Mask */ +#define SCB_STATUS_ACK_CX BIT_15 /* CU Completed Action Cmd */ +#define SCB_STATUS_ACK_FR BIT_14 /* RU Received A Frame */ +#define SCB_STATUS_ACK_CNA BIT_13 /* CU Became Inactive (IDLE) */ +#define SCB_STATUS_ACK_RNR BIT_12 /* RU Became Not Ready */ +#define SCB_STATUS_ACK_MDI BIT_11 /* MDI read or write done */ +#define SCB_STATUS_ACK_SWI BIT_10 /* S/W generated interrupt */ +#define SCB_STATUS_ACK_ER BIT_9 /* Early Receive */ +#define SCB_STATUS_ACK_FCP BIT_8 /* Flow Control Pause */ + +/*- CUS Fields */ +#define SCB_CUS_MASK (BIT_6 | BIT_7) /* CUS 2-bit Mask */ +#define SCB_CUS_IDLE 0 /* CU Idle */ +#define SCB_CUS_SUSPEND BIT_6 /* CU Suspended */ +#define SCB_CUS_ACTIVE BIT_7 /* CU Active */ + +/*- RUS Fields */ +#define SCB_RUS_IDLE 0 /* RU Idle */ +#define SCB_RUS_MASK BIT_2_5 /* RUS 3-bit Mask */ +#define SCB_RUS_SUSPEND BIT_2 /* RU Suspended */ +#define SCB_RUS_NO_RESOURCES BIT_3 /* RU Out Of Resources */ +#define SCB_RUS_READY BIT_4 /* RU Ready */ +#define SCB_RUS_SUSP_NO_RBDS (BIT_2 | BIT_5) /* RU No More RBDs */ +#define SCB_RUS_NO_RBDS (BIT_3 | BIT_5) /* RU No More RBDs */ +#define SCB_RUS_READY_NO_RBDS (BIT_4 | BIT_5) /* RU Ready, No RBDs */ + +/* SCB Command Word bit definitions */ +/*- CUC fields */ +/* Changing mask to 4 bits */ +#define SCB_CUC_MASK BIT_4_7 /* CUC 4-bit Mask */ +#define SCB_CUC_NOOP 0 +#define SCB_CUC_START BIT_4 /* CU Start */ +#define SCB_CUC_RESUME BIT_5 /* CU Resume */ +/* Changed for 82558 enhancements */ +#define SCB_CUC_STATIC_RESUME (BIT_5 | BIT_7) /* 82558/9 Static Resume */ +#define SCB_CUC_DUMP_ADDR BIT_6 /* CU Dump Counters Address */ +#define SCB_CUC_DUMP_STAT (BIT_4 | BIT_6) /* CU Dump stat. counters */ +#define SCB_CUC_LOAD_BASE (BIT_5 | BIT_6) /* Load the CU base */ +/* Below was defined as BIT_4_7 */ +#define SCB_CUC_DUMP_RST_STAT BIT_4_6 /* CU Dump & reset statistics cntrs */ + +/*- RUC fields */ +#define SCB_RUC_MASK BIT_0_2 /* RUC 3-bit Mask */ +#define SCB_RUC_START BIT_0 /* RU Start */ +#define SCB_RUC_RESUME BIT_1 /* RU Resume */ +#define SCB_RUC_ABORT BIT_2 /* RU Abort */ +#define SCB_RUC_LOAD_HDS (BIT_0 | BIT_2) /* Load RFD Header Data Size */ +#define SCB_RUC_LOAD_BASE (BIT_1 | BIT_2) /* Load the RU base */ +#define SCB_RUC_RBD_RESUME BIT_0_2 /* RBD resume */ + +/* Interrupt fields (assuming byte addressing) */ +#define SCB_INT_MASK BIT_0 /* Mask interrupts */ +#define SCB_SOFT_INT BIT_1 /* Generate a S/W interrupt */ +/* Specific Interrupt Mask Bits (upper byte of SCB Command word) */ +#define SCB_FCP_INT_MASK BIT_2 /* Flow Control Pause */ +#define SCB_ER_INT_MASK BIT_3 /* Early Receive */ +#define SCB_RNR_INT_MASK BIT_4 /* RU Not Ready */ +#define SCB_CNA_INT_MASK BIT_5 /* CU Not Active */ +#define SCB_FR_INT_MASK BIT_6 /* Frame Received */ +#define SCB_CX_INT_MASK BIT_7 /* CU eXecution w/ I-bit done */ +#define SCB_BACHELOR_INT_MASK BIT_2_7 /* 82558 interrupt mask bits */ + +#define SCB_GCR2_EEPROM_ACCESS_SEMAPHORE BIT_7 + +/* EEPROM bit definitions */ +/*- EEPROM control register bits */ +#define EN_TRNF 0x10 /* Enable turnoff */ +#define EEDO 0x08 /* EEPROM data out */ +#define EEDI 0x04 /* EEPROM data in (set for writing data) */ +#define EECS 0x02 /* EEPROM chip select (1=hi, 0=lo) */ +#define EESK 0x01 /* EEPROM shift clock (1=hi, 0=lo) */ + +/*- EEPROM opcodes */ +#define EEPROM_READ_OPCODE 06 +#define EEPROM_WRITE_OPCODE 05 +#define EEPROM_ERASE_OPCODE 07 +#define EEPROM_EWEN_OPCODE 19 /* Erase/write enable */ +#define EEPROM_EWDS_OPCODE 16 /* Erase/write disable */ + +/*- EEPROM data locations */ +#define EEPROM_NODE_ADDRESS_BYTE_0 0 +#define EEPROM_COMPATIBILITY_WORD 3 +#define EEPROM_PWA_NO 8 +#define EEPROM_ID_WORD 0x0A + +#define EEPROM_SUM 0xbaba + +// Zero Locking Algorithm definitions: +#define ZLOCK_ZERO_MASK 0x00F0 +#define ZLOCK_MAX_READS 50 +#define ZLOCK_SET_ZERO 0x2010 +#define ZLOCK_MAX_SLEEP 300 * HZ +#define ZLOCK_MAX_ERRORS 300 + +/* E100 Action Commands */ +#define CB_IA_ADDRESS 1 +#define CB_CONFIGURE 2 +#define CB_MULTICAST 3 +#define CB_TRANSMIT 4 +#define CB_LOAD_MICROCODE 5 +#define CB_LOAD_FILTER 8 +#define CB_MAX_NONTX_CMD 9 +#define CB_IPCB_TRANSMIT 9 + +/* Pre-defined Filter Bits */ +#define CB_FILTER_EL 0x80000000 +#define CB_FILTER_FIX 0x40000000 +#define CB_FILTER_ARP 0x08000000 +#define CB_FILTER_IA_MATCH 0x02000000 + +/* Command Block (CB) Field Definitions */ +/*- CB Command Word */ +#define CB_EL_BIT BIT_15 /* CB EL Bit */ +#define CB_S_BIT BIT_14 /* CB Suspend Bit */ +#define CB_I_BIT BIT_13 /* CB Interrupt Bit */ +#define CB_TX_SF_BIT BIT_3 /* TX CB Flexible Mode */ +#define CB_CMD_MASK BIT_0_3 /* CB 4-bit CMD Mask */ +#define CB_CID_DEFAULT (0x1f << 8) /* CB 5-bit CID (max value) */ + +/*- CB Status Word */ +#define CB_STATUS_MASK BIT_12_15 /* CB Status Mask (4-bits) */ +#define CB_STATUS_COMPLETE BIT_15 /* CB Complete Bit */ +#define CB_STATUS_OK BIT_13 /* CB OK Bit */ +#define CB_STATUS_UNDERRUN BIT_12 /* CB A Bit */ +#define CB_STATUS_FAIL BIT_11 /* CB Fail (F) Bit */ + +/*misc command bits */ +#define CB_TX_EOF_BIT BIT_15 /* TX CB/TBD EOF Bit */ + +/* Config params */ +#define CB_CFIG_BYTE_COUNT 22 /* 22 config bytes */ +#define CB_CFIG_D102_BYTE_COUNT 10 + +/* Receive Frame Descriptor Fields */ + +/*- RFD Status Bits */ +#define RFD_RECEIVE_COLLISION BIT_0 /* Collision detected on Receive */ +#define RFD_IA_MATCH BIT_1 /* Indv Address Match Bit */ +#define RFD_RX_ERR BIT_4 /* RX_ERR pin on Phy was set */ +#define RFD_FRAME_TOO_SHORT BIT_7 /* Receive Frame Short */ +#define RFD_DMA_OVERRUN BIT_8 /* Receive DMA Overrun */ +#define RFD_NO_RESOURCES BIT_9 /* No Buffer Space */ +#define RFD_ALIGNMENT_ERROR BIT_10 /* Alignment Error */ +#define RFD_CRC_ERROR BIT_11 /* CRC Error */ +#define RFD_STATUS_OK BIT_13 /* RFD OK Bit */ +#define RFD_STATUS_COMPLETE BIT_15 /* RFD Complete Bit */ + +/*- RFD Command Bits*/ +#define RFD_EL_BIT BIT_15 /* RFD EL Bit */ +#define RFD_S_BIT BIT_14 /* RFD Suspend Bit */ +#define RFD_H_BIT BIT_4 /* Header RFD Bit */ +#define RFD_SF_BIT BIT_3 /* RFD Flexible Mode */ + +/*- RFD misc bits*/ +#define RFD_EOF_BIT BIT_15 /* RFD End-Of-Frame Bit */ +#define RFD_F_BIT BIT_14 /* RFD Buffer Fetch Bit */ +#define RFD_ACT_COUNT_MASK BIT_0_13 /* RFD Actual Count Mask */ + +/* Receive Buffer Descriptor Fields*/ +#define RBD_EOF_BIT BIT_15 /* RBD End-Of-Frame Bit */ +#define RBD_F_BIT BIT_14 /* RBD Buffer Fetch Bit */ +#define RBD_ACT_COUNT_MASK BIT_0_13 /* RBD Actual Count Mask */ + +#define SIZE_FIELD_MASK BIT_0_13 /* Size of the associated buffer */ +#define RBD_EL_BIT BIT_15 /* RBD EL Bit */ + +/* Self Test Results*/ +#define CB_SELFTEST_FAIL_BIT BIT_12 +#define CB_SELFTEST_DIAG_BIT BIT_5 +#define CB_SELFTEST_REGISTER_BIT BIT_3 +#define CB_SELFTEST_ROM_BIT BIT_2 + +#define CB_SELFTEST_ERROR_MASK ( \ + CB_SELFTEST_FAIL_BIT | CB_SELFTEST_DIAG_BIT | \ + CB_SELFTEST_REGISTER_BIT | CB_SELFTEST_ROM_BIT) + +/* adapter vendor & device ids */ +#define PCI_OHIO_BOARD 0x10f0 /* subdevice ID, Ohio dual port nic */ + +/* Values for PCI_REV_ID_REGISTER values */ +#define D101A4_REV_ID 4 /* 82558 A4 stepping */ +#define D101B0_REV_ID 5 /* 82558 B0 stepping */ +#define D101MA_REV_ID 8 /* 82559 A0 stepping */ +#define D101S_REV_ID 9 /* 82559S A-step */ +#define D102_REV_ID 12 +#define D102C_REV_ID 13 /* 82550 step C */ +#define D102E_REV_ID 15 + +/* ############Start of 82555 specific defines################## */ + +#define PHY_82555_LED_SWITCH_CONTROL 0x1b /* 82555 led switch control register */ + +/* 82555 led switch control reg. opcodes */ +#define PHY_82555_LED_NORMAL_CONTROL 0 // control back to the 8255X +#define PHY_82555_LED_DRIVER_CONTROL BIT_2 // the driver is in control +#define PHY_82555_LED_OFF BIT_2 // activity LED is off +#define PHY_82555_LED_ON_559 (BIT_0 | BIT_2) // activity LED is on for 559 and later +#define PHY_82555_LED_ON_PRE_559 (BIT_0 | BIT_1 | BIT_2) // activity LED is on for 558 and before + +// Describe the state of the phy led. +// needed for the function : 'e100_blink_timer' +enum led_state_e { + LED_OFF = 0, + LED_ON, +}; + +/* ############End of 82555 specific defines##################### */ + +#define RFD_PARSE_BIT BIT_3 +#define RFD_TCP_PACKET 0x00 +#define RFD_UDP_PACKET 0x01 +#define TCPUDP_CHECKSUM_BIT_VALID BIT_4 +#define TCPUDP_CHECKSUM_VALID BIT_5 +#define CHECKSUM_PROTOCOL_MASK 0x03 + +#define VLAN_SIZE 4 +#define CHKSUM_SIZE 2 +#define RFD_DATA_SIZE (ETH_FRAME_LEN + CHKSUM_SIZE + VLAN_SIZE) + +/* Bits for bdp->flags */ +#define DF_LINK_FC_CAP 0x00000001 /* Link is flow control capable */ +#define DF_CSUM_OFFLOAD 0x00000002 +#define DF_UCODE_LOADED 0x00000004 +#define USE_IPCB 0x00000008 /* set if using ipcb for transmits */ +#define IS_BACHELOR 0x00000010 /* set if 82558 or newer board */ +#define IS_ICH 0x00000020 +#define DF_SPEED_FORCED 0x00000040 /* set if speed is forced */ + +typedef struct net_device_stats net_dev_stats_t; + +/* needed macros */ +/* These macros use the bdp pointer. If you use them it better be defined */ +#define PREV_TCB_USED(X) ((X).tail ? (X).tail - 1 : bdp->params.TxDescriptors - 1) +#define NEXT_TCB_TOUSE(X) ((((X) + 1) >= bdp->params.TxDescriptors) ? 0 : (X) + 1) +#define TCB_TO_USE(X) ((X).tail) +#define TCBS_AVAIL(X) (NEXT_TCB_TOUSE( NEXT_TCB_TOUSE((X).tail)) != (X).head) + +#define RFD_POINTER(skb,bdp) ((rfd_t *) (((unsigned char *)((skb)->data))-((bdp)->rfd_size))) +#define SKB_RFD_STATUS(skb,bdp) ((RFD_POINTER((skb),(bdp)))->rfd_header.cb_status) +#define GET_SKB_DMA_ADDR(skb) ( *(dma_addr_t *)( (skb)->cb) ) +#define SET_SKB_DMA_ADDR(skb,dma_addr) ( *(dma_addr_t *)( (skb)->cb) = (dma_addr) ) + +/* ====================================================================== */ +/* 82557 */ +/* ====================================================================== */ + +/* Changed for 82558 enhancement */ +typedef struct _d101_scb_ext_t { + u32 scb_rx_dma_cnt; /* Rx DMA byte count */ + u8 scb_early_rx_int; /* Early Rx DMA byte count */ + u8 scb_fc_thld; /* Flow Control threshold */ + u8 scb_fc_xon_xoff; /* Flow Control XON/XOFF values */ + u8 scb_pmdr; /* Power Mgmt. Driver Reg */ +} d101_scb_ext __attribute__ ((__packed__)); + +/* Changed for 82559 enhancement */ +typedef struct _d101m_scb_ext_t { + u32 scb_rx_dma_cnt; /* Rx DMA byte count */ + u8 scb_early_rx_int; /* Early Rx DMA byte count */ + u8 scb_fc_thld; /* Flow Control threshold */ + u8 scb_fc_xon_xoff; /* Flow Control XON/XOFF values */ + u8 scb_pmdr; /* Power Mgmt. Driver Reg */ + u8 scb_gen_ctrl; /* General Control */ + u8 scb_gen_stat; /* General Status */ + u16 scb_reserved; /* Reserved */ + u32 scb_function_event; /* Cardbus Function Event */ + u32 scb_function_event_mask; /* Cardbus Function Mask */ + u32 scb_function_present_state; /* Cardbus Function state */ + u32 scb_force_event; /* Cardbus Force Event */ +} d101m_scb_ext __attribute__ ((__packed__)); + +/* Changed for 82550 enhancement */ +typedef struct _d102_scb_ext_t { + u32 scb_rx_dma_cnt; /* Rx DMA byte count */ + u8 scb_early_rx_int; /* Early Rx DMA byte count */ + u8 scb_fc_thld; /* Flow Control threshold */ + u8 scb_fc_xon_xoff; /* Flow Control XON/XOFF values */ + u8 scb_pmdr; /* Power Mgmt. Driver Reg */ + u8 scb_gen_ctrl; /* General Control */ + u8 scb_gen_stat; /* General Status */ + u8 scb_gen_ctrl2; + u8 scb_reserved; /* Reserved */ + u32 scb_scheduling_reg; + u32 scb_reserved2; + u32 scb_function_event; /* Cardbus Function Event */ + u32 scb_function_event_mask; /* Cardbus Function Mask */ + u32 scb_function_present_state; /* Cardbus Function state */ + u32 scb_force_event; /* Cardbus Force Event */ +} d102_scb_ext __attribute__ ((__packed__)); + +/* + * 82557 status control block. this will be memory mapped & will hang of the + * the bdp, which hangs of the bdp. This is the brain of it. + */ +typedef struct _scb_t { + u16 scb_status; /* SCB Status register */ + u8 scb_cmd_low; /* SCB Command register (low byte) */ + u8 scb_cmd_hi; /* SCB Command register (high byte) */ + u32 scb_gen_ptr; /* SCB General pointer */ + u32 scb_port; /* PORT register */ + u16 scb_flsh_cntrl; /* Flash Control register */ + u16 scb_eprm_cntrl; /* EEPROM control register */ + u32 scb_mdi_cntrl; /* MDI Control Register */ + /* Changed for 82558 enhancement */ + union { + u32 scb_rx_dma_cnt; /* Rx DMA byte count */ + d101_scb_ext d101_scb; /* 82558/9 specific fields */ + d101m_scb_ext d101m_scb; /* 82559 specific fields */ + d102_scb_ext d102_scb; + } scb_ext; +} scb_t __attribute__ ((__packed__)); + +/* Self test + * This is used to dump results of the self test + */ +typedef struct _self_test_t { + u32 st_sign; /* Self Test Signature */ + u32 st_result; /* Self Test Results */ +} self_test_t __attribute__ ((__packed__)); + +/* + * Statistical Counters + */ +/* 82557 counters */ +typedef struct _basic_cntr_t { + u32 xmt_gd_frames; /* Good frames transmitted */ + u32 xmt_max_coll; /* Fatal frames -- had max collisions */ + u32 xmt_late_coll; /* Fatal frames -- had a late coll. */ + u32 xmt_uruns; /* Xmit underruns (fatal or re-transmit) */ + u32 xmt_lost_crs; /* Frames transmitted without CRS */ + u32 xmt_deferred; /* Deferred transmits */ + u32 xmt_sngl_coll; /* Transmits that had 1 and only 1 coll. */ + u32 xmt_mlt_coll; /* Transmits that had multiple coll. */ + u32 xmt_ttl_coll; /* Transmits that had 1+ collisions. */ + u32 rcv_gd_frames; /* Good frames received */ + u32 rcv_crc_errs; /* Aligned frames that had a CRC error */ + u32 rcv_algn_errs; /* Receives that had alignment errors */ + u32 rcv_rsrc_err; /* Good frame dropped cuz no resources */ + u32 rcv_oruns; /* Overrun errors - bus was busy */ + u32 rcv_err_coll; /* Received frms. that encountered coll. */ + u32 rcv_shrt_frames; /* Received frames that were to short */ +} basic_cntr_t; + +/* 82558 extended statistic counters */ +typedef struct _ext_cntr_t { + u32 xmt_fc_frames; + u32 rcv_fc_frames; + u32 rcv_fc_unsupported; +} ext_cntr_t; + +/* 82559 TCO statistic counters */ +typedef struct _tco_cntr_t { + u16 xmt_tco_frames; + u16 rcv_tco_frames; +} tco_cntr_t; + +/* Structures to access thet physical dump area */ +/* Use one of these types, according to the statisitcal counters mode, + to cast the pointer to the physical dump area and access the cmd_complete + DWORD. */ + +/* 557-mode : only basic counters + cmd_complete */ +typedef struct _err_cntr_557_t { + basic_cntr_t basic_stats; + u32 cmd_complete; +} err_cntr_557_t; + +/* 558-mode : basic + extended counters + cmd_complete */ +typedef struct _err_cntr_558_t { + basic_cntr_t basic_stats; + ext_cntr_t extended_stats; + u32 cmd_complete; +} err_cntr_558_t; + +/* 559-mode : basic + extended + TCO counters + cmd_complete */ +typedef struct _err_cntr_559_t { + basic_cntr_t basic_stats; + ext_cntr_t extended_stats; + tco_cntr_t tco_stats; + u32 cmd_complete; +} err_cntr_559_t; + +/* This typedef defines the struct needed to hold the largest number of counters */ +typedef err_cntr_559_t max_counters_t; + +/* Different statistical-counters mode the controller may be in */ +typedef enum _stat_mode_t { + E100_BASIC_STATS = 0, /* 82557 stats : 16 counters / 16 dw */ + E100_EXTENDED_STATS, /* 82558 stats : 19 counters / 19 dw */ + E100_TCO_STATS /* 82559 stats : 21 counters / 20 dw */ +} stat_mode_t; + +/* dump statistical counters complete codes */ +#define DUMP_STAT_COMPLETED 0xA005 +#define DUMP_RST_STAT_COMPLETED 0xA007 + +/* Command Block (CB) Generic Header Structure*/ +typedef struct _cb_header_t { + u16 cb_status; /* Command Block Status */ + u16 cb_cmd; /* Command Block Command */ + u32 cb_lnk_ptr; /* Link To Next CB */ +} cb_header_t __attribute__ ((__packed__)); + +//* Individual Address Command Block (IA_CB)*/ +typedef struct _ia_cb_t { + cb_header_t ia_cb_hdr; + u8 ia_addr[ETH_ALEN]; +} ia_cb_t __attribute__ ((__packed__)); + +/* Configure Command Block (CONFIG_CB)*/ +typedef struct _config_cb_t { + cb_header_t cfg_cbhdr; + u8 cfg_byte[CB_CFIG_BYTE_COUNT + CB_CFIG_D102_BYTE_COUNT]; +} config_cb_t __attribute__ ((__packed__)); + +/* MultiCast Command Block (MULTICAST_CB)*/ +typedef struct _multicast_cb_t { + cb_header_t mc_cbhdr; + u16 mc_count; /* Number of multicast addresses */ + u8 mc_addr[(ETH_ALEN * MAX_MULTICAST_ADDRS)]; +} mltcst_cb_t __attribute__ ((__packed__)); + +#define UCODE_MAX_DWORDS 134 +/* Load Microcode Command Block (LOAD_UCODE_CB)*/ +typedef struct _load_ucode_cb_t { + cb_header_t load_ucode_cbhdr; + u32 ucode_dword[UCODE_MAX_DWORDS]; +} load_ucode_cb_t __attribute__ ((__packed__)); + +/* Load Programmable Filter Data*/ +typedef struct _filter_cb_t { + cb_header_t filter_cb_hdr; + u32 filter_data[MAX_FILTER]; +} filter_cb_t __attribute__ ((__packed__)); + +/* NON_TRANSMIT_CB -- Generic Non-Transmit Command Block + */ +typedef struct _nxmit_cb_t { + union { + config_cb_t config; + ia_cb_t setup; + load_ucode_cb_t load_ucode; + mltcst_cb_t multicast; + filter_cb_t filter; + } ntcb; +} nxmit_cb_t __attribute__ ((__packed__)); + +/*Block for queuing for postponed execution of the non-transmit commands*/ +typedef struct _nxmit_cb_entry_t { + struct list_head list_elem; + nxmit_cb_t *non_tx_cmd; + dma_addr_t dma_addr; + unsigned long expiration_time; +} nxmit_cb_entry_t; + +/* States for postponed non tx commands execution */ +typedef enum _non_tx_cmd_state_t { + E100_NON_TX_IDLE = 0, /* No queued NON-TX commands */ + E100_WAIT_TX_FINISH, /* Wait for completion of the TX activities */ + E100_WAIT_NON_TX_FINISH /* Wait for completion of the non TX command */ +} non_tx_cmd_state_t; + +/* some defines for the ipcb */ +#define IPCB_IP_CHECKSUM_ENABLE BIT_4 +#define IPCB_TCPUDP_CHECKSUM_ENABLE BIT_5 +#define IPCB_TCP_PACKET BIT_6 +#define IPCB_LARGESEND_ENABLE BIT_7 +#define IPCB_HARDWAREPARSING_ENABLE BIT_0 +#define IPCB_INSERTVLAN_ENABLE BIT_1 +#define IPCB_IP_ACTIVATION_DEFAULT IPCB_HARDWAREPARSING_ENABLE + +/* Transmit Buffer Descriptor (TBD)*/ +typedef struct _tbd_t { + u32 tbd_buf_addr; /* Physical Transmit Buffer Address */ + u16 tbd_buf_cnt; /* Actual Count Of Bytes */ + u16 padd; +} tbd_t __attribute__ ((__packed__)); + +/* d102 specific fields */ +typedef struct _tcb_ipcb_t { + u16 schedule_low; + u8 ip_schedule; + u8 ip_activation_high; + u16 vlan; + u8 ip_header_offset; + u8 tcp_header_offset; + union { + u32 sec_rec_phys_addr; + u32 tbd_zero_address; + } tbd_sec_addr; + union { + u16 sec_rec_size; + u16 tbd_zero_size; + } tbd_sec_size; + u16 total_tcp_payload; +} tcb_ipcb_t __attribute__ ((__packed__)); + +#ifdef MAX_SKB_FRAGS +#define E100_ZEROCOPY +#endif + +#ifdef E100_ZEROCOPY +#define E100_TBD_ARRAY_SIZE (2+MAX_SKB_FRAGS) +#else +#define E100_TBD_ARRAY_SIZE 2 +#endif /*E100_ZEROCOPY */ + +/* Transmit Command Block (TCB)*/ +struct _tcb_t { + cb_header_t tcb_hdr; + u32 tcb_tbd_ptr; /* TBD address */ + u16 tcb_cnt; /* Data Bytes In TCB past header */ + u8 tcb_thrshld; /* TX Threshold for FIFO Extender */ + u8 tcb_tbd_num; + + union { + tcb_ipcb_t ipcb; /* d102 ipcb fields */ + tbd_t tbd_array[E100_TBD_ARRAY_SIZE]; + } tcbu; + + /* From here onward we can dump anything we want as long as the + * size of the total structure is a multiple of a paragraph + * boundary ( i.e. -16 bit aligned ). + */ + tbd_t *tbd_ptr; + +#ifdef E100_ZEROCOPY + u32 tcb_tbd_dflt_ptr; /* TBD address for non-segmented packet */ + u32 tcb_tbd_expand_ptr; /* TBD address for segmented packet */ +#endif /*E100_ZEROCOPY */ + + struct sk_buff *tcb_skb; /* the associated socket buffer */ + dma_addr_t tcb_phys; /* phys addr of the TCB */ +} __attribute__ ((__packed__)); + +#ifndef _TCB_T_ +#define _TCB_T_ +typedef struct _tcb_t tcb_t; +#endif + +/* Receive Frame Descriptor (RFD) - will be using the simple model*/ +struct _rfd_t { + /* 8255x */ + cb_header_t rfd_header; + u32 rfd_rbd_ptr; /* Receive Buffer Descriptor Addr */ + u16 rfd_act_cnt; /* Number Of Bytes Received */ + u16 rfd_sz; /* Number Of Bytes In RFD */ + /* D102 aka Gamla */ + u16 vlanid; + u8 rcvparserstatus; + u8 reserved; + u16 securitystatus; + u8 checksumstatus; + u8 zerocopystatus; + u8 pad[8]; /* data should be 16 byte aligned */ + u8 data[RFD_DATA_SIZE]; + +} __attribute__ ((__packed__)); + +#ifndef _RFD_T_ +#define _RFD_T_ +typedef struct _rfd_t rfd_t; +#endif + +/* Receive Buffer Descriptor (RBD)*/ +typedef struct _rbd_t { + u16 rbd_act_cnt; /* Number Of Bytes Received */ + u16 rbd_filler; + u32 rbd_lnk_addr; /* Link To Next RBD */ + u32 rbd_rcb_addr; /* Receive Buffer Address */ + u16 rbd_sz; /* Receive Buffer Size */ + u16 rbd_filler1; +} rbd_t __attribute__ ((__packed__)); + +/* + * This structure is used to maintain a FIFO access to a resource that is + * maintained as a circular queue. The resource to be maintained is pointed + * to by the "data" field in the structure below. In this driver the TCBs', + * TBDs' & RFDs' are maintained as a circular queue & are managed thru this + * structure. + */ +typedef struct _buf_pool_t { + unsigned int head; /* index to first used resource */ + unsigned int tail; /* index to last used resource */ + void *data; /* points to resource pool */ +} buf_pool_t; + +/*Rx skb holding structure*/ +struct rx_list_elem { + struct list_head list_elem; + dma_addr_t dma_addr; + struct sk_buff *skb; +}; + +enum next_cu_cmd_e { RESUME_NO_WAIT = 0, RESUME_WAIT, START_WAIT }; +enum zlock_state_e { ZLOCK_INITIAL, ZLOCK_READING, ZLOCK_SLEEPING }; +enum tx_queue_stop_type { LONG_STOP = 0, SHORT_STOP }; + +/* 64 bit aligned size */ +#define E100_SIZE_64A(X) ((sizeof(X) + 7) & ~0x7) + +typedef struct _bd_dma_able_t { + char selftest[E100_SIZE_64A(self_test_t)]; + char stats_counters[E100_SIZE_64A(max_counters_t)]; +} bd_dma_able_t; + +/* bit masks for bool parameters */ +#define PRM_XSUMRX 0x00000001 +#define PRM_UCODE 0x00000002 +#define PRM_FC 0x00000004 +#define PRM_IFS 0x00000008 +#define PRM_BUNDLE_SMALL 0x00000010 +#define PRM_RX_CONG 0x00000020 + +struct cfg_params { + int e100_speed_duplex; + int RxDescriptors; + int TxDescriptors; + int IntDelay; + int BundleMax; + int ber; + int PollingMaxWork; + u32 b_params; +}; + +struct e100_private { + u32 flags; /* board management flags */ + u32 tx_per_underrun; /* number of good tx frames per underrun */ + unsigned int tx_count; /* count of tx frames, so we can request an interrupt */ + u8 tx_thld; /* stores transmit threshold */ + u16 eeprom_size; + u32 pwa_no; /* PWA: xxxxxx-0xx */ + u8 perm_node_address[ETH_ALEN]; + struct list_head active_rx_list; /* list of rx buffers */ + struct list_head rx_struct_pool; /* pool of rx buffer struct headers */ + u16 rfd_size; /* size of the adapter's RFD struct */ + int skb_req; /* number of skbs neede by the adapter */ + u8 intr_mask; /* mask for interrupt status */ + + void *dma_able; /* dma allocated structs */ + dma_addr_t dma_able_phys; + self_test_t *selftest; /* pointer to self test area */ + dma_addr_t selftest_phys; /* phys addr of selftest */ + max_counters_t *stats_counters; /* pointer to stats table */ + dma_addr_t stat_cnt_phys; /* phys addr of stat counter area */ + + stat_mode_t stat_mode; /* statistics mode: extended, TCO, basic */ + scb_t *scb; /* memory mapped ptr to 82557 scb */ + + tcb_t *last_tcb; /* pointer to last tcb sent */ + buf_pool_t tcb_pool; /* adapter's TCB array */ + dma_addr_t tcb_phys; /* phys addr of start of TCBs */ + + u16 cur_line_speed; + u16 cur_dplx_mode; + + struct net_device *device; + struct pci_dev *pdev; + struct driver_stats drv_stats; + + u8 rev_id; /* adapter PCI revision ID */ + unsigned long device_type; /* device type from e100_vendor.h */ + + unsigned int phy_addr; /* address of PHY component */ + unsigned int PhyId; /* ID of PHY component */ + unsigned int PhyState; /* state for the fix squelch algorithm */ + unsigned int PhyDelay; /* delay for the fix squelch algorithm */ + + /* Lock defintions for the driver */ + spinlock_t bd_lock; /* board lock */ + spinlock_t bd_non_tx_lock; /* Non transmit command lock */ + spinlock_t config_lock; /* config block lock */ + spinlock_t mdi_access_lock; /* mdi lock */ + + struct timer_list watchdog_timer; /* watchdog timer id */ + + /* non-tx commands parameters */ + struct timer_list nontx_timer_id; /* non-tx timer id */ + struct list_head non_tx_cmd_list; + non_tx_cmd_state_t non_tx_command_state; + nxmit_cb_entry_t *same_cmd_entry[CB_MAX_NONTX_CMD]; + + enum next_cu_cmd_e next_cu_cmd; + + /* Zero Locking Algorithm data members */ + enum zlock_state_e zlock_state; + u8 zlock_read_data[16]; /* number of times each value 0-15 was read */ + u16 zlock_read_cnt; /* counts number of reads */ + ulong zlock_sleep_cnt; /* keeps track of "sleep" time */ + + u8 config[CB_CFIG_BYTE_COUNT + CB_CFIG_D102_BYTE_COUNT]; + + /* IFS params */ + u8 ifs_state; + u8 ifs_value; + + struct cfg_params params; /* adapter's command line parameters */ + + struct proc_dir_entry *proc_parent; + + rwlock_t isolate_lock; + int driver_isolated; + + u32 speed_duplex_caps; /* adapter's speed/duplex capabilities */ + + struct tasklet_struct polling_tasklet; + +#ifdef ETHTOOL_GWOL + /* WOL params for ethtool */ + u32 wolsupported; + u32 wolopts; + u16 ip_lbytes; +#endif +}; + +#define E100_AUTONEG 0 +#define E100_SPEED_10_HALF 1 +#define E100_SPEED_10_FULL 2 +#define E100_SPEED_100_HALF 3 +#define E100_SPEED_100_FULL 4 + +/********* function prototypes *************/ +extern void e100_isolate_driver(struct e100_private *bdp); +extern void e100_sw_reset(struct e100_private *bdp, u32 reset_cmd); +extern void e100_start_cu(struct e100_private *bdp, tcb_t *tcb); +extern void e100_free_non_tx_cmd(struct e100_private *bdp, + nxmit_cb_entry_t *non_tx_cmd); +extern nxmit_cb_entry_t *e100_alloc_non_tx_cmd(struct e100_private *bdp); +extern unsigned char e100_exec_non_cu_cmd(struct e100_private *bdp, + nxmit_cb_entry_t *cmd); +extern unsigned char e100_selftest(struct e100_private *bdp, u32 *st_timeout, + u32 *st_result); +extern unsigned char e100_get_link_state(struct e100_private *bdp); +extern unsigned char e100_wait_scb(struct e100_private *bdp); + +#endif diff -Nru a/drivers/net/e100/e100_config.c b/drivers/net/e100/e100_config.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e100/e100_config.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,596 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. +*******************************************************************************/ + +/********************************************************************** +* * +* INTEL CORPORATION * +* * +* This software is supplied under the terms of the license included * +* above. All use of this driver must be in accordance with the terms * +* of that license. * +* * +* Module Name: e100_config.c * +* * +* Abstract: Functions for configuring the network adapter. * +* * +* Environment: This file is intended to be specific to the Linux * +* operating system. * +* * +**********************************************************************/ +#ifdef SIOCETHTOOL +#include +#endif + +#include "e100_config.h" + +static void e100_config_long_rx(struct e100_private *bdp, unsigned char enable); + +static const u8 def_config[] = { + CB_CFIG_BYTE_COUNT, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x32, 0x07, 0x01, + 0x00, 0x2e, 0x00, 0x60, 0x00, 0xf2, 0xc8, 0x00, + 0x40, 0xf2, 0x80, 0x3f, 0x05 +}; + +/** + * e100_config_init_82557 - config the 82557 adapter + * @bdp: atapter's private data struct + * + * This routine will initialize the 82557 configure block. + * All other init functions will only set values that are + * different from the 82557 default. + */ +static void __devinit +e100_config_init_82557(struct e100_private *bdp) +{ + /* initialize config block */ + memcpy(bdp->config, def_config, sizeof (def_config)); + bdp->config[0] = CB_CFIG_BYTE_COUNT; /* just in case */ + + e100_config_ifs(bdp); + + /* + * Enable extended statistical counters (82558 and up) and TCO counters + * (82559 and up) and set the statistical counters' mode in bdp + * + * stat. mode | TCO stat. bit (2) | Extended stat. bit (5) + * ------------------------------------------------------------------ + * Basic (557) | 0 | 1 + * ------------------------------------------------------------------ + * Extended (558) | 0 | 0 + * ------------------------------------------------------------------ + * TCO (559) | 1 | 1 + * ------------------------------------------------------------------ + * Reserved | 1 | 0 + * ------------------------------------------------------------------ + */ + bdp->config[6] &= ~CB_CFIG_TCO_STAT; + bdp->config[6] |= CB_CFIG_EXT_STAT_DIS; + bdp->stat_mode = E100_BASIC_STATS; + + /* Setup for MII or 503 operation. The CRS+CDT bit should only be set */ + /* when operating in 503 mode. */ + if (bdp->phy_addr == 32) { + bdp->config[8] &= ~CB_CFIG_503_MII; + bdp->config[15] |= CB_CFIG_CRS_OR_CDT; + } else { + bdp->config[8] |= CB_CFIG_503_MII; + bdp->config[15] &= ~CB_CFIG_CRS_OR_CDT; + } + + e100_config_fc(bdp); + e100_config_force_dplx(bdp); + e100_config_promisc(bdp, false); + e100_config_mulcast_enbl(bdp, false); +} + +static void __devinit +e100_config_init_82558(struct e100_private *bdp) +{ + /* MWI enable. This should be turned on only if the adapter is a 82558/9 + * and if the PCI command reg. has enabled the MWI bit. */ + bdp->config[3] |= CB_CFIG_MWI_EN; + + bdp->config[6] &= ~CB_CFIG_EXT_TCB_DIS; + + if (bdp->rev_id >= D101MA_REV_ID) { + /* this is 82559 and up - enable TCO counters */ + bdp->config[6] |= CB_CFIG_TCO_STAT; + bdp->config[6] |= CB_CFIG_EXT_STAT_DIS; + bdp->stat_mode = E100_TCO_STATS; + + if ((bdp->rev_id < D102_REV_ID) && + (bdp->params.b_params & PRM_XSUMRX) && + (bdp->pdev->device != 0x1209)) { + + bdp->flags |= DF_CSUM_OFFLOAD; + bdp->config[9] |= 1; + } + } else { + /* this is 82558 */ + bdp->config[6] &= ~CB_CFIG_TCO_STAT; + bdp->config[6] &= ~CB_CFIG_EXT_STAT_DIS; + bdp->stat_mode = E100_EXTENDED_STATS; + } + + e100_config_long_rx(bdp, true); +} + +static void __devinit +e100_config_init_82550(struct e100_private *bdp) +{ + /* The D102 chip allows for 32 config bytes. This value is + * supposed to be in Byte 0. Just add the extra bytes to + * what was already setup in the block. */ + bdp->config[0] += CB_CFIG_D102_BYTE_COUNT; + + /* now we need to enable the extended RFD. When this is + * enabled, the immediated receive data buffer starts at offset + * 32 from the RFD base address, instead of at offset 16. */ + bdp->config[7] |= CB_CFIG_EXTENDED_RFD; + + /* put the chip into D102 receive mode. This is neccessary + * for any parsing and offloading features. */ + bdp->config[22] = CB_CFIG_RECEIVE_GAMLA_MODE; + + /* set the flag if checksum offloading was enabled */ + if (bdp->params.b_params & PRM_XSUMRX) { + bdp->flags |= DF_CSUM_OFFLOAD; + } +} + +/* Initialize the adapter's configure block */ +void __devinit +e100_config_init(struct e100_private *bdp) +{ + e100_config_init_82557(bdp); + + if (bdp->flags & IS_BACHELOR) + e100_config_init_82558(bdp); + + if (bdp->rev_id >= D102_REV_ID) + e100_config_init_82550(bdp); +} + +/** + * e100_force_config - force a configure command + * @bdp: atapter's private data struct + * + * This routine will force a configure command to the adapter. + * The command will be executed in polled mode as interrupts + * are _disabled_ at this time. + * + * Returns: + * true: if the configure command was successfully issued and completed + * false: otherwise + */ +unsigned char +e100_force_config(struct e100_private *bdp) +{ + spin_lock_bh(&(bdp->config_lock)); + + bdp->config[0] = CB_CFIG_BYTE_COUNT; + if (bdp->rev_id >= D102_REV_ID) { + /* The D102 chip allows for 32 config bytes. This value is + supposed to be in Byte 0. Just add the extra bytes to + what was already setup in the block. */ + bdp->config[0] += CB_CFIG_D102_BYTE_COUNT; + } + + spin_unlock_bh(&(bdp->config_lock)); + + // although we call config outside the lock, there is no + // race condition because config byte count has maximum value + return e100_config(bdp); +} + +/** + * e100_config - issue a configure command + * @bdp: atapter's private data struct + * + * This routine will issue a configure command to the 82557. + * This command will be executed in polled mode as interrupts + * are _disabled_ at this time. + * + * Returns: + * true: if the configure command was successfully issued and completed + * false: otherwise + */ +unsigned char +e100_config(struct e100_private *bdp) +{ + cb_header_t *pntcb_hdr; + unsigned char res = true; + nxmit_cb_entry_t *cmd; + + if (bdp->config[0] == 0) { + goto exit; + } + + if ((cmd = e100_alloc_non_tx_cmd(bdp)) == NULL) { + res = false; + goto exit; + } + + pntcb_hdr = (cb_header_t *) cmd->non_tx_cmd; + pntcb_hdr->cb_cmd = __constant_cpu_to_le16(CB_CONFIGURE); + + spin_lock_bh(&bdp->config_lock); + + if (bdp->config[0] < CB_CFIG_MIN_PARAMS) { + bdp->config[0] = CB_CFIG_MIN_PARAMS; + } + + /* Copy the device's config block to the device's memory */ + memcpy(cmd->non_tx_cmd->ntcb.config.cfg_byte, bdp->config, + bdp->config[0]); + /* reset number of bytes to config next time */ + bdp->config[0] = 0; + + spin_unlock_bh(&bdp->config_lock); + + res = e100_exec_non_cu_cmd(bdp, cmd); + +exit: + if (netif_running(bdp->device)) + netif_wake_queue(bdp->device); + return res; +} + +/** + * e100_config_fc - config flow-control state + * @bdp: atapter's private data struct + * + * This routine will enable or disable flow control support in the adapter's + * config block. Flow control will be enable only if requested using the command + * line option, and if the link is flow-contorl capable (both us and the link + * partner). + * + * Returns: + * true: if then option was indeed changed + * false: if no change was needed + */ +unsigned char +e100_config_fc(struct e100_private *bdp) +{ + unsigned char enable = false; + unsigned char changed = false; + + /* 82557 doesn't support fc. Don't touch this option */ + if (!(bdp->flags & IS_BACHELOR)) + return false; + + /* Enable fc if requested and if the link supports it */ + if ((bdp->params.b_params & PRM_FC) && (bdp->flags & DF_LINK_FC_CAP)) { + enable = true; + } + + spin_lock_bh(&(bdp->config_lock)); + + if (enable) { + + if (bdp->config[16] != DFLT_FC_DELAY_LSB) { + bdp->config[16] = DFLT_FC_DELAY_LSB; + E100_CONFIG(bdp, 16); + changed = true; + } + + if (bdp->config[17] != DFLT_FC_DELAY_LSB) { + bdp->config[17] = DFLT_FC_DELAY_MSB; + E100_CONFIG(bdp, 17); + changed = true; + } + + /* check if *all* fc config options were already set */ + if (((bdp->config[19] & CB_CFIG_FC_OPTS) != CB_CFIG_FC_OPTS) || + (bdp->config[19] & CB_CFIG_TX_FC_DIS)) { + + bdp->config[19] |= CB_CFIG_FC_OPTS; + bdp->config[19] &= ~CB_CFIG_TX_FC_DIS; + E100_CONFIG(bdp, 19); + changed = true; + } + + } else { + if (bdp->config[16] != DFLT_NO_FC_DELAY_LSB) { + bdp->config[16] = DFLT_NO_FC_DELAY_LSB; + E100_CONFIG(bdp, 16); + changed = true; + } + + if (bdp->config[17] != DFLT_NO_FC_DELAY_MSB) { + bdp->config[17] = DFLT_NO_FC_DELAY_MSB; + E100_CONFIG(bdp, 17); + changed = true; + } + + /* check if *any* fc config options was already set */ + if ((bdp->config[19] & CB_CFIG_FC_OPTS) || + !(bdp->config[19] & CB_CFIG_TX_FC_DIS)) { + + bdp->config[19] &= ~CB_CFIG_FC_OPTS; + bdp->config[19] |= CB_CFIG_TX_FC_DIS; + E100_CONFIG(bdp, 19); + changed = true; + } + } + + spin_unlock_bh(&(bdp->config_lock)); + + return changed; +} + +/** + * e100_config_promisc - configure promiscuous mode + * @bdp: atapter's private data struct + * @enable: should we enable this option or not + * + * This routine will enable or disable promiscuous mode + * in the adapter's config block. + */ +void +e100_config_promisc(struct e100_private *bdp, unsigned char enable) +{ + spin_lock_bh(&(bdp->config_lock)); + + /* if in promiscuous mode, save bad frames */ + if (enable) { + + if (!(bdp->config[6] & CB_CFIG_SAVE_BAD_FRAMES)) { + bdp->config[6] |= CB_CFIG_SAVE_BAD_FRAMES; + E100_CONFIG(bdp, 6); + } + + if (bdp->config[7] & (u8) BIT_0) { + bdp->config[7] &= (u8) (~BIT_0); + E100_CONFIG(bdp, 7); + } + + if (!(bdp->config[15] & CB_CFIG_PROMISCUOUS)) { + bdp->config[15] |= CB_CFIG_PROMISCUOUS; + E100_CONFIG(bdp, 15); + } + + } else { /* not in promiscuous mode */ + + if (bdp->config[6] & CB_CFIG_SAVE_BAD_FRAMES) { + bdp->config[6] &= ~CB_CFIG_SAVE_BAD_FRAMES; + E100_CONFIG(bdp, 6); + } + + if (!(bdp->config[7] & (u8) BIT_0)) { + bdp->config[7] |= (u8) (BIT_0); + E100_CONFIG(bdp, 7); + } + + if (bdp->config[15] & CB_CFIG_PROMISCUOUS) { + bdp->config[15] &= ~CB_CFIG_PROMISCUOUS; + E100_CONFIG(bdp, 15); + } + } + + spin_unlock_bh(&(bdp->config_lock)); +} + +/** + * e100_config_mulcast_enbl - configure allmulti mode + * @bdp: atapter's private data struct + * @enable: should we enable this option or not + * + * This routine will enable or disable reception of all multicast packets + * in the adapter's config block. + */ +void +e100_config_mulcast_enbl(struct e100_private *bdp, unsigned char enable) +{ + spin_lock_bh(&(bdp->config_lock)); + + /* this flag is used to enable receiving all multicast packet */ + if (enable) { + if (!(bdp->config[21] & CB_CFIG_MULTICAST_ALL)) { + bdp->config[21] |= CB_CFIG_MULTICAST_ALL; + E100_CONFIG(bdp, 21); + } + + } else { + if (bdp->config[21] & CB_CFIG_MULTICAST_ALL) { + bdp->config[21] &= ~CB_CFIG_MULTICAST_ALL; + E100_CONFIG(bdp, 21); + } + } + + spin_unlock_bh(&(bdp->config_lock)); +} + +/** + * e100_config_ifs - configure the IFS parameter + * @bdp: atapter's private data struct + * + * This routine will configure the adaptive IFS value + * in the adapter's config block. IFS values are only + * relevant in half duplex, so set to 0 in full duplex. + */ +void +e100_config_ifs(struct e100_private *bdp) +{ + u8 value = 0; + + spin_lock_bh(&(bdp->config_lock)); + + /* IFS value is only needed to be specified at half-duplex mode */ + if (bdp->cur_dplx_mode == HALF_DUPLEX) { + value = (u8) bdp->ifs_value; + } + + if (bdp->config[2] != value) { + bdp->config[2] = value; + E100_CONFIG(bdp, 2); + } + + spin_unlock_bh(&(bdp->config_lock)); +} + +/** + * e100_config_force_dplx - configure the forced full duplex mode + * @bdp: atapter's private data struct + * + * This routine will enable or disable force full duplex + * in the adapter's config block. If the PHY is 503, and + * the duplex is full, consider the adapter forced. + */ +void +e100_config_force_dplx(struct e100_private *bdp) +{ + spin_lock_bh(&(bdp->config_lock)); + + /* We must force full duplex on if we are using PHY 0, and we are */ + /* supposed to run in FDX mode. We do this because the e100 has only */ + /* one FDX# input pin, and that pin will be connected to PHY 1. */ + /* Changed the 'if' condition below to fix performance problem * at 10 + * full. The Phy was getting forced to full duplex while the MAC * was + * not, because the cur_dplx_mode was not being set to 2 by SetupPhy. * + * This is how the condition was, initially. * This has been changed so + * that the MAC gets forced to full duplex * simply if the user has + * forced full duplex. * * if (( bdp->phy_addr == 0 ) && ( + * bdp->cur_dplx_mode == 2 )) */ + /* The rest of the fix is in the PhyDetect code. */ + if ((bdp->params.e100_speed_duplex == E100_SPEED_10_FULL) || + (bdp->params.e100_speed_duplex == E100_SPEED_100_FULL) || + ((bdp->phy_addr == 32) && (bdp->cur_dplx_mode == FULL_DUPLEX))) { + if (!(bdp->config[19] & (u8) CB_CFIG_FORCE_FDX)) { + bdp->config[19] |= (u8) CB_CFIG_FORCE_FDX; + E100_CONFIG(bdp, 19); + } + + } else { + if (bdp->config[19] & (u8) CB_CFIG_FORCE_FDX) { + bdp->config[19] &= (u8) (~CB_CFIG_FORCE_FDX); + E100_CONFIG(bdp, 19); + } + } + + spin_unlock_bh(&(bdp->config_lock)); +} + +/** + * e100_config_long_rx + * @bdp: atapter's private data struct + * @enable: should we enable this option or not + * + * This routine will enable or disable reception of larger packets. + * This is needed by VLAN implementations. + */ +static void +e100_config_long_rx(struct e100_private *bdp, unsigned char enable) +{ + if (enable) { + if (!(bdp->config[18] & CB_CFIG_LONG_RX_OK)) { + bdp->config[18] |= CB_CFIG_LONG_RX_OK; + E100_CONFIG(bdp, 18); + } + + } else { + if ((bdp->config[18] & CB_CFIG_LONG_RX_OK)) { + bdp->config[18] &= ~CB_CFIG_LONG_RX_OK; + E100_CONFIG(bdp, 18); + } + } +} + +#ifdef ETHTOOL_GWOL +/** + * e100_config_wol + * @bdp: atapter's private data struct + * + * This sets configuration options for Wake On LAN functionality (WOL) in the + * config record. WOL options are retrieved from wolinfo_wolopts in @bdp + */ +void +e100_config_wol(struct e100_private *bdp) +{ + spin_lock_bh(&(bdp->config_lock)); + + if (bdp->wolopts & WAKE_PHY) { + bdp->config[9] |= CB_LINK_STATUS_WOL; + E100_CONFIG(bdp, 9); + } + + if (!(bdp->wolopts & WAKE_MAGIC)) { + bdp->config[19] |= CB_DISABLE_MAGPAK_WAKE; + E100_CONFIG(bdp, 19); + } + + spin_unlock_bh(&(bdp->config_lock)); +} +#endif + diff -Nru a/drivers/net/e100/e100_config.h b/drivers/net/e100/e100_config.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e100/e100_config.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,206 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. +*******************************************************************************/ + +#ifndef _E100_CONFIG_INC_ +#define _E100_CONFIG_INC_ + +#include "e100.h" + +#define E100_CONFIG(bdp, X) ((bdp)->config[0] = max_t(u8, (bdp)->config[0], (X)+1)) + +#define CB_CFIG_MIN_PARAMS 8 + +/* byte 0 bit definitions*/ +#define CB_CFIG_BYTE_COUNT_MASK BIT_0_5 /* Byte count occupies bit 5-0 */ + +/* byte 1 bit definitions*/ +#define CB_CFIG_RXFIFO_LIMIT_MASK BIT_0_4 /* RxFifo limit mask */ +#define CB_CFIG_TXFIFO_LIMIT_MASK BIT_4_7 /* TxFifo limit mask */ + +/* byte 2 bit definitions -- ADAPTIVE_IFS*/ + +/* word 3 bit definitions -- RESERVED*/ +/* Changed for 82558 enhancements */ +/* byte 3 bit definitions */ +#define CB_CFIG_MWI_EN BIT_0 /* Enable MWI on PCI bus */ +#define CB_CFIG_TYPE_EN BIT_1 /* Type Enable */ +#define CB_CFIG_READAL_EN BIT_2 /* Enable Read Align */ +#define CB_CFIG_TERMCL_EN BIT_3 /* Cache line write */ + +/* byte 4 bit definitions*/ +#define CB_CFIG_RX_MIN_DMA_MASK BIT_0_6 /* Rx minimum DMA count mask */ + +/* byte 5 bit definitions*/ +#define CB_CFIG_TX_MIN_DMA_MASK BIT_0_6 /* Tx minimum DMA count mask */ +#define CB_CFIG_DMBC_EN BIT_7 /* Enable Tx/Rx min. DMA counts */ + +/* Changed for 82558 enhancements */ +/* byte 6 bit definitions*/ +#define CB_CFIG_LATE_SCB BIT_0 /* Update SCB After New Tx Start */ +#define CB_CFIG_DIRECT_DMA_DIS BIT_1 /* Direct DMA mode */ +#define CB_CFIG_TNO_INT BIT_2 /* Tx Not OK Interrupt */ +#define CB_CFIG_TCO_STAT BIT_2 /* TCO statistics in 559 and above */ +#define CB_CFIG_CI_INT BIT_3 /* Command Complete Interrupt */ +#define CB_CFIG_EXT_TCB_DIS BIT_4 /* Extended TCB */ +#define CB_CFIG_EXT_STAT_DIS BIT_5 /* Extended Stats */ +#define CB_CFIG_SAVE_BAD_FRAMES BIT_7 /* Save Bad Frames Enabled */ + +/* byte 7 bit definitions*/ +#define CB_CFIG_DISC_SHORT_FRAMES BIT_0 /* Discard Short Frames */ +#define CB_CFIG_DYNTBD_EN BIT_7 /* Enable dynamic TBD */ +/* Enable extended RFD's on D102 */ +#define CB_CFIG_EXTENDED_RFD BIT_5 + +/* byte 8 bit definitions*/ +#define CB_CFIG_503_MII BIT_0 /* 503 vs. MII mode */ + +/* byte 9 bit definitions -- pre-defined all zeros*/ +#define CB_LINK_STATUS_WOL BIT_5 + +/* byte 10 bit definitions*/ +#define CB_CFIG_NO_SRCADR BIT_3 /* No Source Address Insertion */ +#define CB_CFIG_PREAMBLE_LEN BIT_4_5 /* Preamble Length */ +#define CB_CFIG_LOOPBACK_MODE BIT_6_7 /* Loopback Mode */ +#define CB_CFIG_LOOPBACK_NORMAL 0 +#define CB_CFIG_LOOPBACK_INTERNAL BIT_6 +#define CB_CFIG_LOOPBACK_EXTERNAL BIT_6_7 + +/* byte 11 bit definitions*/ +#define CB_CFIG_LINEAR_PRIORITY BIT_0_2 /* Linear Priority */ + +/* byte 12 bit definitions*/ +#define CB_CFIG_LINEAR_PRI_MODE BIT_0 /* Linear Priority mode */ +#define CB_CFIG_IFS_MASK BIT_4_7 /* Interframe Spacing mask */ + +/* byte 13 bit definitions -- pre-defined all zeros*/ + +/* byte 14 bit definitions -- pre-defined 0xf2*/ + +/* byte 15 bit definitions*/ +#define CB_CFIG_PROMISCUOUS BIT_0 /* Promiscuous Mode Enable */ +#define CB_CFIG_BROADCAST_DIS BIT_1 /* Broadcast Mode Disable */ +#define CB_CFIG_CRS_OR_CDT BIT_7 /* CRS Or CDT */ + +/* byte 16 bit definitions -- pre-defined all zeros*/ +#define DFLT_FC_DELAY_LSB 0x1f /* Delay for outgoing Pause frames */ +#define DFLT_NO_FC_DELAY_LSB 0x00 /* no flow control default value */ + +/* byte 17 bit definitions -- pre-defined 0x40*/ +#define DFLT_FC_DELAY_MSB 0x01 /* Delay for outgoing Pause frames */ +#define DFLT_NO_FC_DELAY_MSB 0x40 /* no flow control default value */ + +/* byte 18 bit definitions*/ +#define CB_CFIG_STRIPPING BIT_0 /* Padding Disabled */ +#define CB_CFIG_PADDING BIT_1 /* Padding Disabled */ +#define CB_CFIG_CRC_IN_MEM BIT_2 /* Transfer CRC To Memory */ + +/* byte 19 bit definitions*/ +#define CB_CFIG_TX_ADDR_WAKE BIT_0 /* Address Wakeup */ +#define CB_DISABLE_MAGPAK_WAKE BIT_1 /* Magic Packet Wakeup disable */ +/* Changed TX_FC_EN to TX_FC_DIS because 0 enables, 1 disables. Jul 8, 1999 */ +#define CB_CFIG_TX_FC_DIS BIT_2 /* Tx Flow Control Disable */ +#define CB_CFIG_FC_RESTOP BIT_3 /* Rx Flow Control Restop */ +#define CB_CFIG_FC_RESTART BIT_4 /* Rx Flow Control Restart */ +#define CB_CFIG_FC_REJECT BIT_5 /* Rx Flow Control Restart */ +#define CB_CFIG_FC_OPTS (CB_CFIG_FC_RESTOP | CB_CFIG_FC_RESTART | CB_CFIG_FC_REJECT) + +/* end 82558/9 specifics */ + +#define CB_CFIG_FORCE_FDX BIT_6 /* Force Full Duplex */ +#define CB_CFIG_FDX_ENABLE BIT_7 /* Full Duplex Enabled */ + +/* byte 20 bit definitions*/ +#define CB_CFIG_MULTI_IA BIT_6 /* Multiple IA Addr */ + +/* byte 21 bit definitions*/ +#define CB_CFIG_MULTICAST_ALL BIT_3 /* Multicast All */ + +/* byte 22 bit defines */ +#define CB_CFIG_RECEIVE_GAMLA_MODE BIT_0 /* D102 receive mode */ +#define CB_CFIG_VLAN_DROP_ENABLE BIT_1 /* vlan stripping */ + +#define CB_CFIG_LONG_RX_OK BIT_3 + +/* function prototypes */ +extern void e100_config_init(struct e100_private *bdp); +extern unsigned char e100_force_config(struct e100_private *bdp); +extern unsigned char e100_config(struct e100_private *bdp); +extern unsigned char e100_config_fc(struct e100_private *bdp); +extern void e100_config_promisc(struct e100_private *bdp, unsigned char enable); +extern void e100_config_brdcast_dsbl(struct e100_private *bdp); +extern void e100_config_mulcast_enbl(struct e100_private *bdp, + unsigned char enable); +extern void e100_config_ifs(struct e100_private *bdp); +extern void e100_config_force_dplx(struct e100_private *bdp); + +#endif /* _E100_CONFIG_INC_ */ diff -Nru a/drivers/net/e100/e100_eeprom.c b/drivers/net/e100/e100_eeprom.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e100/e100_eeprom.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,614 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. +*******************************************************************************/ + +/********************************************************************** +* * +* INTEL CORPORATION * +* * +* This software is supplied under the terms of the license included * +* above. All use of this driver must be in accordance with the terms * +* of that license. * +* * +* Module Name: e100_eeprom.c * +* * +* Abstract: This module contains routines to read and write to a * +* serial EEPROM * +* * +* Environment: This file is intended to be specific to the Linux * +* operating system. * +* * +**********************************************************************/ +#include "e100.h" + +#define CSR_EEPROM_CONTROL_FIELD(bdp) ((bdp)->scb->scb_eprm_cntrl) + +#define CSR_GENERAL_CONTROL2_FIELD(bdp) \ + ((bdp)->scb->scb_ext.d102_scb.scb_gen_ctrl2) + +#define EEPROM_STALL_TIME 4 +#define EEPROM_CHECKSUM ((u16) 0xBABA) +#define EEPROM_MAX_WORD_SIZE 256 + +void e100_eeprom_cleanup(struct e100_private *adapter); +u16 e100_eeprom_calculate_chksum(struct e100_private *adapter); +static void e100_eeprom_write_word(struct e100_private *adapter, u16 reg, + u16 data); +void e100_eeprom_write_block(struct e100_private *adapter, u16 start, u16 *data, + u16 size); +u16 e100_eeprom_size(struct e100_private *adapter); +u16 e100_eeprom_read(struct e100_private *adapter, u16 reg); + +static void shift_out_bits(struct e100_private *adapter, u16 data, u16 count); +static u16 shift_in_bits(struct e100_private *adapter); +static void raise_clock(struct e100_private *adapter, u16 *x); +static void lower_clock(struct e100_private *adapter, u16 *x); +static u16 eeprom_wait_cmd_done(struct e100_private *adapter); +static void eeprom_stand_by(struct e100_private *adapter); + +//---------------------------------------------------------------------------------------- +// Procedure: eeprom_set_semaphore +// +// Description: This function set (write 1) Gamla EEPROM semaphore bit (bit 23 word 0x1C in the CSR). +// +// Arguments: +// Adapter - Adapter context +// +// Returns: true if success +// else return false +// +//---------------------------------------------------------------------------------------- + +inline u8 +eeprom_set_semaphore(struct e100_private *adapter) +{ + u16 data = 0; + unsigned long expiration_time = jiffies + HZ / 100 + 1; + + while (time_before(jiffies, expiration_time)) { + // Get current value of General Control 2 + data = readb(&CSR_GENERAL_CONTROL2_FIELD(adapter)); + + // Set bit 23 word 0x1C in the CSR. + data |= SCB_GCR2_EEPROM_ACCESS_SEMAPHORE; + writeb(data, &CSR_GENERAL_CONTROL2_FIELD(adapter)); + + barrier(); + + // Check to see if this bit set or not. + data = readb(&CSR_GENERAL_CONTROL2_FIELD(adapter)); + + if (data & SCB_GCR2_EEPROM_ACCESS_SEMAPHORE) { + return true; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + return false; +} + +//---------------------------------------------------------------------------------------- +// Procedure: eeprom_reset_semaphore +// +// Description: This function reset (write 0) Gamla EEPROM semaphore bit +// (bit 23 word 0x1C in the CSR). +// +// Arguments: struct e100_private * adapter - Adapter context +//---------------------------------------------------------------------------------------- + +inline void +eeprom_reset_semaphore(struct e100_private *adapter) +{ + u16 data = 0; + + data = readb(&CSR_GENERAL_CONTROL2_FIELD(adapter)); + data &= ~(SCB_GCR2_EEPROM_ACCESS_SEMAPHORE); + writeb(data, &CSR_GENERAL_CONTROL2_FIELD(adapter)); +} + +//---------------------------------------------------------------------------------------- +// Procedure: e100_eeprom_size +// +// Description: This routine determines the size of the EEPROM. This value should be +// checked for validity - ie. is it too big or too small. The size returned +// is then passed to the read/write functions. +// +// Returns: +// Size of the eeprom, or zero if an error occured +//---------------------------------------------------------------------------------------- +u16 +e100_eeprom_size(struct e100_private *adapter) +{ + u16 x, size = 1; // must be one to accumulate a product + + // if we've already stored this data, read from memory + if (adapter->eeprom_size) { + return adapter->eeprom_size; + } + // otherwise, read from the eeprom + // Set EEPROM semaphore. + if (adapter->rev_id >= D102_REV_ID) { + if (!eeprom_set_semaphore(adapter)) + return 0; + } + // enable the eeprom by setting EECS. + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + x &= ~(EEDI | EEDO | EESK); + x |= EECS; + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + + // write the read opcode + shift_out_bits(adapter, EEPROM_READ_OPCODE, 3); + + // experiment to discover the size of the eeprom. request register zero + // and wait for the eeprom to tell us it has accepted the entire address. + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + do { + size *= 2; // each bit of address doubles eeprom size + x |= EEDO; // set bit to detect "dummy zero" + x &= ~EEDI; // address consists of all zeros + + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + udelay(EEPROM_STALL_TIME); + raise_clock(adapter, &x); + lower_clock(adapter, &x); + + // check for "dummy zero" + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + if (size > EEPROM_MAX_WORD_SIZE) { + size = 0; + break; + } + } while (x & EEDO); + + // read in the value requested + (void) shift_in_bits(adapter); + e100_eeprom_cleanup(adapter); + + // Clear EEPROM Semaphore. + if (adapter->rev_id >= D102_REV_ID) { + eeprom_reset_semaphore(adapter); + } + + return size; +} + +//---------------------------------------------------------------------------------------- +// Procedure: eeprom_address_size +// +// Description: determines the number of bits in an address for the eeprom acceptable +// values are 64, 128, and 256 +// Arguments: size of the eeprom +// Returns: bits in an address for that size eeprom +//---------------------------------------------------------------------------------------- + +static u16 +eeprom_address_size(u16 size) +{ + switch (size) { + case 64: + return 6; + case 128: + return 7; + case 256: + return 8; + } + + return 0; //fix compiler warning or error! +} + +//---------------------------------------------------------------------------------------- +// Procedure: e100_eeprom_read +// +// Description: This routine serially reads one word out of the EEPROM. +// +// Arguments: +// adapter - our adapter context +// reg - EEPROM word to read. +// +// Returns: +// Contents of EEPROM word (reg). +//---------------------------------------------------------------------------------------- + +u16 +e100_eeprom_read(struct e100_private *adapter, u16 reg) +{ + u16 x, data, bits; + + // Set EEPROM semaphore. + if (adapter->rev_id >= D102_REV_ID) { + if (!eeprom_set_semaphore(adapter)) + return 0; + } + // eeprom size is initialized to zero + if (!adapter->eeprom_size) + adapter->eeprom_size = e100_eeprom_size(adapter); + + bits = eeprom_address_size(adapter->eeprom_size); + + // select EEPROM, reset bits, set EECS + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + + x &= ~(EEDI | EEDO | EESK); + x |= EECS; + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + + // write the read opcode and register number in that order + // The opcode is 3bits in length, reg is 'bits' bits long + shift_out_bits(adapter, EEPROM_READ_OPCODE, 3); + shift_out_bits(adapter, reg, bits); + + // Now read the data (16 bits) in from the selected EEPROM word + data = shift_in_bits(adapter); + + e100_eeprom_cleanup(adapter); + + // Clear EEPROM Semaphore. + if (adapter->rev_id >= D102_REV_ID) { + eeprom_reset_semaphore(adapter); + } + + return data; +} + +//---------------------------------------------------------------------------------------- +// Procedure: shift_out_bits +// +// Description: This routine shifts data bits out to the EEPROM. +// +// Arguments: +// data - data to send to the EEPROM. +// count - number of data bits to shift out. +// +// Returns: (none) +//---------------------------------------------------------------------------------------- + +static void +shift_out_bits(struct e100_private *adapter, u16 data, u16 count) +{ + u16 x, mask; + + mask = 1 << (count - 1); + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + x &= ~(EEDO | EEDI); + + do { + x &= ~EEDI; + if (data & mask) + x |= EEDI; + + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + udelay(EEPROM_STALL_TIME); + raise_clock(adapter, &x); + lower_clock(adapter, &x); + mask = mask >> 1; + } while (mask); + + x &= ~EEDI; + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); +} + +//---------------------------------------------------------------------------------------- +// Procedure: raise_clock +// +// Description: This routine raises the EEPROM's clock input (EESK) +// +// Arguments: +// x - Ptr to the EEPROM control register's current value +// +// Returns: (none) +//---------------------------------------------------------------------------------------- + +void +raise_clock(struct e100_private *adapter, u16 *x) +{ + *x = *x | EESK; + writew(*x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + udelay(EEPROM_STALL_TIME); +} + +//---------------------------------------------------------------------------------------- +// Procedure: lower_clock +// +// Description: This routine lower's the EEPROM's clock input (EESK) +// +// Arguments: +// x - Ptr to the EEPROM control register's current value +// +// Returns: (none) +//---------------------------------------------------------------------------------------- + +void +lower_clock(struct e100_private *adapter, u16 *x) +{ + *x = *x & ~EESK; + writew(*x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + udelay(EEPROM_STALL_TIME); +} + +//---------------------------------------------------------------------------------------- +// Procedure: shift_in_bits +// +// Description: This routine shifts data bits in from the EEPROM. +// +// Arguments: +// +// Returns: +// The contents of that particular EEPROM word +//---------------------------------------------------------------------------------------- + +static u16 +shift_in_bits(struct e100_private *adapter) +{ + u16 x, d, i; + + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + x &= ~(EEDO | EEDI); + d = 0; + + for (i = 0; i < 16; i++) { + d <<= 1; + raise_clock(adapter, &x); + + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + + x &= ~EEDI; + if (x & EEDO) + d |= 1; + + lower_clock(adapter, &x); + } + + return d; +} + +//---------------------------------------------------------------------------------------- +// Procedure: e100_eeprom_cleanup +// +// Description: This routine returns the EEPROM to an idle state +//---------------------------------------------------------------------------------------- + +void +e100_eeprom_cleanup(struct e100_private *adapter) +{ + u16 x; + + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + + x &= ~(EECS | EEDI); + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + + raise_clock(adapter, &x); + lower_clock(adapter, &x); +} + +//********************************************************************************** +// Procedure: e100_eeprom_update_chksum +// +// Description: Calculates the checksum and writes it to the EEProm. +// It calculates the checksum accroding to the formula: +// Checksum = 0xBABA - (sum of first 63 words). +// +//----------------------------------------------------------------------------------- +u16 +e100_eeprom_calculate_chksum(struct e100_private *adapter) +{ + u16 idx, xsum_index, checksum = 0; + + // eeprom size is initialized to zero + if (!adapter->eeprom_size) + adapter->eeprom_size = e100_eeprom_size(adapter); + + xsum_index = adapter->eeprom_size - 1; + for (idx = 0; idx < xsum_index; idx++) + checksum += e100_eeprom_read(adapter, idx); + + checksum = EEPROM_CHECKSUM - checksum; + return checksum; +} + +//---------------------------------------------------------------------------------------- +// Procedure: e100_eeprom_write_word +// +// Description: This routine writes a word to a specific EEPROM location without. +// taking EEPROM semaphore and updating checksum. +// Use e100_eeprom_write_block for the EEPROM update +// Arguments: reg - The EEPROM word that we are going to write to. +// data - The data (word) that we are going to write to the EEPROM. +//---------------------------------------------------------------------------------------- +static void +e100_eeprom_write_word(struct e100_private *adapter, u16 reg, u16 data) +{ + u16 x; + u16 bits; + + bits = eeprom_address_size(adapter->eeprom_size); + + /* select EEPROM, mask off ASIC and reset bits, set EECS */ + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + x &= ~(EEDI | EEDO | EESK); + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + wmb(); + udelay(EEPROM_STALL_TIME); + x |= EECS; + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + + shift_out_bits(adapter, EEPROM_EWEN_OPCODE, 5); + shift_out_bits(adapter, reg, (u16) (bits - 2)); + if (!eeprom_wait_cmd_done(adapter)) + return; + + /* write the new word to the EEPROM & send the write opcode the EEPORM */ + shift_out_bits(adapter, EEPROM_WRITE_OPCODE, 3); + + /* select which word in the EEPROM that we are writing to */ + shift_out_bits(adapter, reg, bits); + + /* write the data to the selected EEPROM word */ + shift_out_bits(adapter, data, 16); + if (!eeprom_wait_cmd_done(adapter)) + return; + + shift_out_bits(adapter, EEPROM_EWDS_OPCODE, 5); + shift_out_bits(adapter, reg, (u16) (bits - 2)); + if (!eeprom_wait_cmd_done(adapter)) + return; + + e100_eeprom_cleanup(adapter); +} + +//---------------------------------------------------------------------------------------- +// Procedure: e100_eeprom_write_block +// +// Description: This routine writes a block of words starting from specified EEPROM +// location and updates checksum +// Arguments: reg - The EEPROM word that we are going to write to. +// data - The data (word) that we are going to write to the EEPROM. +//---------------------------------------------------------------------------------------- +void +e100_eeprom_write_block(struct e100_private *adapter, u16 start, u16 *data, + u16 size) +{ + u16 checksum; + u16 i; + + if (!adapter->eeprom_size) + adapter->eeprom_size = e100_eeprom_size(adapter); + + // Set EEPROM semaphore. + if (adapter->rev_id >= D102_REV_ID) { + if (!eeprom_set_semaphore(adapter)) + return; + } + + for (i = 0; i < size; i++) { + e100_eeprom_write_word(adapter, start + i, data[i]); + } + //Update checksum + checksum = e100_eeprom_calculate_chksum(adapter); + e100_eeprom_write_word(adapter, (adapter->eeprom_size - 1), checksum); + + // Clear EEPROM Semaphore. + if (adapter->rev_id >= D102_REV_ID) { + eeprom_reset_semaphore(adapter); + } +} + +//---------------------------------------------------------------------------------------- +// Procedure: eeprom_wait_cmd_done +// +// Description: This routine waits for the the EEPROM to finish its command. +// Specifically, it waits for EEDO (data out) to go high. +// Returns: true - If the command finished +// false - If the command never finished (EEDO stayed low) +//---------------------------------------------------------------------------------------- +static u16 +eeprom_wait_cmd_done(struct e100_private *adapter) +{ + u16 x; + unsigned long expiration_time = jiffies + HZ / 100 + 1; + + eeprom_stand_by(adapter); + + while (time_before(jiffies, expiration_time)) { + rmb(); + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + if (x & EEDO) + return true; + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + + return false; +} + +//---------------------------------------------------------------------------------------- +// Procedure: eeprom_stand_by +// +// Description: This routine lowers the EEPROM chip select (EECS) for a few microseconds. +//---------------------------------------------------------------------------------------- +static void +eeprom_stand_by(struct e100_private *adapter) +{ + u16 x; + + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + x &= ~(EECS | EESK); + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + wmb(); + udelay(EEPROM_STALL_TIME); + x |= EECS; + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + udelay(EEPROM_STALL_TIME); +} diff -Nru a/drivers/net/e100/e100_main.c b/drivers/net/e100/e100_main.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e100/e100_main.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,3797 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. +*******************************************************************************/ + +/********************************************************************** +* * +* INTEL CORPORATION * +* * +* This software is supplied under the terms of the license included * +* above. All use of this driver must be in accordance with the terms * +* of that license. * +* * +* Module Name: e100_main.c * +* * +* Abstract: Functions for the driver entry points like load, * +* unload, open and close. All board specific calls made * +* by the network interface section of the driver. * +* * +* Environment: This file is intended to be specific to the Linux * +* operating system. * +* * +**********************************************************************/ + +#undef __NO_VERSION__ + +#include +#include +#include +#include +#include "e100.h" +#include "e100_ucode.h" +#include "e100_config.h" +#include "e100_phy.h" +#include "e100_vendor.h" + +#ifndef CONFIG_PROC_FS +#undef E100_CONFIG_PROC_FS +#endif + +#ifdef E100_CONFIG_PROC_FS +extern int e100_create_proc_subdir(struct e100_private *); +extern void e100_remove_proc_subdir(struct e100_private *); +#else +#define e100_create_proc_subdir(X) 0 +#define e100_remove_proc_subdir(X) do {} while(0) +#endif + +#ifdef SIOCETHTOOL +#define E100_ETHTOOL_IOCTL +#endif +#ifdef E100_ETHTOOL_IOCTL +static int e100_do_ethtool_ioctl(struct net_device *, struct ifreq *); +static void e100_get_speed_duplex_caps(struct e100_private *); +static int e100_ethtool_get_settings(struct net_device *, struct ifreq *); +static int e100_ethtool_set_settings(struct net_device *, struct ifreq *); +static void e100_set_speed_duplex(struct e100_private *); + +#ifdef ETHTOOL_GDRVINFO +static int e100_ethtool_get_drvinfo(struct net_device *, struct ifreq *); +#endif +#ifdef ETHTOOL_GEEPROM +static int e100_ethtool_eeprom(struct net_device *, struct ifreq *); + +#define E100_EEPROM_MAGIC 0x1234 +#endif +#ifdef ETHTOOL_GLINK +static int e100_ethtool_glink(struct net_device *, struct ifreq *); +#endif +#ifdef ETHTOOL_NWAY_RST +static int e100_ethtool_nway_rst(struct net_device *, struct ifreq *); +#endif +#ifdef ETHTOOL_GWOL +static int e100_ethtool_wol(struct net_device *, struct ifreq *); +static unsigned char e100_setup_filter(struct e100_private *bdp); +static void e100_do_wol(struct pci_dev *pcid, struct e100_private *bdp); +static u16 e100_get_ip_lbytes(struct net_device *dev); +extern void e100_config_wol(struct e100_private *bdp); +#endif +#endif /*E100_ETHTOOL_IOCTL */ + +#ifdef SIOCGMIIPHY +#define E100_MII_IOCTL +#endif +#ifdef E100_MII_IOCTL +#include +static int e100_mii_ioctl(struct net_device *, struct ifreq *, int); +#endif /*E100_MII_IOCTL */ + +static unsigned char e100_delayed_exec_non_cu_cmd(struct e100_private *, + nxmit_cb_entry_t *); +static void e100_free_nontx_list(struct e100_private *); +static void e100_non_tx_background(unsigned long); + +/* Global Data structures and variables */ +char e100_copyright[] __devinitdata = "Copyright (c) 2002 Intel Corporation"; + +#define E100_VERSION "2.0.20-pre1" +#define E100_FULL_DRIVER_NAME "Intel(R) PRO/100 Fast Ethernet Adapter - Loadable driver, ver " + +const char *e100_version = E100_VERSION; +const char *e100_full_driver_name = E100_FULL_DRIVER_NAME E100_VERSION; +char *e100_short_driver_name = "e100"; +static int e100nics = 0; + +/*********************************************************************/ +/*! This is a GCC extension to ANSI C. + * See the item "Labeled Elements in Initializers" in the section + * "Extensions to the C Language Family" of the GCC documentation. + *********************************************************************/ + +#define E100_PARAM_INIT { [0 ... E100_MAX_NIC-1] = -1 } + +/* All parameters are treated the same, as an integer array of values. + * This macro just reduces the need to repeat the same declaration code + * over and over (plus this helps to avoid typo bugs). + */ +#define E100_PARAM(X, S) \ + static const int X[E100_MAX_NIC + 1] = E100_PARAM_INIT; \ + MODULE_PARM(X, "1-" __MODULE_STRING(E100_MAX_NIC) "i"); \ + MODULE_PARM_DESC(X, S); + +/* ====================================================================== */ +static u8 e100_D101M_checksum(struct e100_private *, struct sk_buff *); +static u8 e100_D102_check_checksum(rfd_t *); +static int e100_ioctl(struct net_device *, struct ifreq *, int); +static int e100_open(struct net_device *); +static int e100_close(struct net_device *); +static int e100_change_mtu(struct net_device *, int); +static int e100_xmit_frame(struct sk_buff *, struct net_device *); +static unsigned char e100_init(struct e100_private *); +static int e100_set_mac(struct net_device *, void *); +struct net_device_stats *e100_get_stats(struct net_device *); + +static void e100intr(int, void *, struct pt_regs *); +static void e100_print_brd_conf(struct e100_private *); +static void e100_set_multi(struct net_device *); + +char *e100_get_brand_msg(struct e100_private *); +static u8 e100_pci_setup(struct pci_dev *, struct e100_private *); +static u8 e100_sw_init(struct e100_private *); +static unsigned char e100_alloc_space(struct e100_private *); +static void e100_dealloc_space(struct e100_private *); +static int e100_alloc_tcb_pool(struct e100_private *); +static void e100_setup_tcb_pool(tcb_t *, unsigned int, struct e100_private *); +static void e100_free_tcb_pool(struct e100_private *); +static int e100_alloc_rfd_pool(struct e100_private *); +static void e100_free_rfd_pool(struct e100_private *); + +static void e100_rd_eaddr(struct e100_private *); +static void e100_rd_pwa_no(struct e100_private *); +extern u16 e100_eeprom_read(struct e100_private *, u16); +extern void e100_eeprom_write_block(struct e100_private *, u16, u16 *, u16); +extern u16 e100_eeprom_size(struct e100_private *); + +static unsigned char e100_clr_cntrs(struct e100_private *); +static unsigned char e100_load_microcode(struct e100_private *); +static unsigned char e100_hw_init(struct e100_private *, u32); +static unsigned char e100_setup_iaaddr(struct e100_private *, u8 *); +static unsigned char e100_update_stats(struct e100_private *bdp); + +static void e100_start_ru(struct e100_private *); +static void e100_dump_stats_cntrs(struct e100_private *); + +static void e100_check_options(int board, struct e100_private *bdp); +static void e100_set_int_option(int *, int, int, int, int, char *); +static void e100_set_bool_option(struct e100_private *bdp, int, u32, int, + char *); +unsigned char e100_wait_exec_cmplx(struct e100_private *, u32, u8); +void e100_exec_cmplx(struct e100_private *, u32, u8); + +/** + * e100_get_rx_struct - retrieve cell to hold skb buff from the pool + * @bdp: atapter's private data struct + * + * Returns the new cell to hold sk_buff or %NULL. + */ +static inline struct rx_list_elem * +e100_get_rx_struct(struct e100_private *bdp) +{ + struct rx_list_elem *rx_struct = NULL; + + if (!list_empty(&(bdp->rx_struct_pool))) { + rx_struct = list_entry(bdp->rx_struct_pool.next, + struct rx_list_elem, list_elem); + list_del(&(rx_struct->list_elem)); + } + + return rx_struct; +} + +/** + * e100_alloc_skb - allocate an skb for the adapter + * @bdp: atapter's private data struct + * + * Allocates skb with enough room for rfd, and data, and reserve non-data space. + * Returns the new cell with sk_buff or %NULL. + */ +static inline struct rx_list_elem * +e100_alloc_skb(struct e100_private *bdp) +{ + struct sk_buff *new_skb; + u32 skb_size = sizeof (rfd_t); + struct rx_list_elem *rx_struct; + + new_skb = (struct sk_buff *) dev_alloc_skb(skb_size); + if (new_skb) { + /* The IP data should be + DWORD aligned. since the ethernet header is 14 bytes long, + we need to reserve 2 extra bytes so that the TCP/IP headers + will be DWORD aligned. */ + skb_reserve(new_skb, 2); + if ((rx_struct = e100_get_rx_struct(bdp)) == NULL) + goto err; + rx_struct->skb = new_skb; + rx_struct->dma_addr = pci_map_single(bdp->pdev, new_skb->data, + sizeof (rfd_t), + PCI_DMA_FROMDEVICE); + if (!rx_struct->dma_addr) + goto err; + skb_reserve(new_skb, bdp->rfd_size); + return rx_struct; + } else { + return NULL; + } + +err: + dev_kfree_skb_irq(new_skb); + return NULL; +} + +/** + * e100_add_skb_to_end - add an skb to the end of our rfd list + * @bdp: atapter's private data struct + * @rx_struct: rx_list_elem with the new skb + * + * Adds a newly allocated skb to the end of our rfd list. + */ +inline void +e100_add_skb_to_end(struct e100_private *bdp, struct rx_list_elem *rx_struct) +{ + rfd_t *rfdn; /* The new rfd */ + rfd_t *rfd; /* The old rfd */ + struct rx_list_elem *rx_struct_last; + + (rx_struct->skb)->dev = bdp->device; + rfdn = RFD_POINTER(rx_struct->skb, bdp); + rfdn->rfd_header.cb_status = 0; + rfdn->rfd_header.cb_cmd = __constant_cpu_to_le16(RFD_EL_BIT); + rfdn->rfd_act_cnt = 0; + rfdn->rfd_sz = __constant_cpu_to_le16(RFD_DATA_SIZE); + + pci_dma_sync_single(bdp->pdev, rx_struct->dma_addr, bdp->rfd_size, + PCI_DMA_TODEVICE); + + if (!list_empty(&(bdp->active_rx_list))) { + rx_struct_last = list_entry(bdp->active_rx_list.prev, + struct rx_list_elem, list_elem); + rfd = RFD_POINTER(rx_struct_last->skb, bdp); + pci_dma_sync_single(bdp->pdev, rx_struct_last->dma_addr, + 4, PCI_DMA_FROMDEVICE); + put_unaligned(cpu_to_le32(rx_struct->dma_addr), + ((u32 *) (&(rfd->rfd_header.cb_lnk_ptr)))); + + pci_dma_sync_single(bdp->pdev, rx_struct_last->dma_addr, + 8, PCI_DMA_TODEVICE); + rfd->rfd_header.cb_cmd &= + __constant_cpu_to_le16((u16) ~RFD_EL_BIT); + + pci_dma_sync_single(bdp->pdev, rx_struct_last->dma_addr, + 4, PCI_DMA_TODEVICE); + } + + list_add_tail(&(rx_struct->list_elem), &(bdp->active_rx_list)); +} + +static inline void +e100_alloc_skbs(struct e100_private *bdp) +{ + for (; bdp->skb_req > 0; bdp->skb_req--) { + struct rx_list_elem *rx_struct; + + if ((rx_struct = e100_alloc_skb(bdp)) == NULL) + return; + + e100_add_skb_to_end(bdp, rx_struct); + } +} + +void e100_tx_srv(struct e100_private *); +u32 e100_rx_srv(struct e100_private *, u32, int *); + +void e100_polling_tasklet(unsigned long); + +void e100_watchdog(struct net_device *); +void e100_refresh_txthld(struct e100_private *); +void e100_manage_adaptive_ifs(struct e100_private *); +void e100_clear_pools(struct e100_private *); +static void e100_clear_structs(struct net_device *); +static inline tcb_t *e100_prepare_xmit_buff(struct e100_private *, + struct sk_buff *); +static void e100_set_multi_exec(struct net_device *dev); + +MODULE_AUTHOR("Intel Corporation, "); +MODULE_DESCRIPTION(E100_FULL_DRIVER_NAME E100_VERSION); +MODULE_LICENSE("Dual BSD/GPL"); + +E100_PARAM(TxDescriptors, "Number of transmit descriptors"); +E100_PARAM(RxDescriptors, "Number of receive descriptors"); +E100_PARAM(XsumRX, "Disable or enable Receive Checksum offload"); +E100_PARAM(e100_speed_duplex, "Speed and Duplex settings"); +E100_PARAM(ucode, "Disable or enable microcode loading"); +E100_PARAM(ber, "Value for the BER correction algorithm"); +E100_PARAM(flow_control, "Disable or enable Ethernet PAUSE frames processing"); +E100_PARAM(IntDelay, "Value for CPU saver's interrupt delay"); +E100_PARAM(BundleSmallFr, "Disable or enable interrupt bundling of small frames"); +E100_PARAM(BundleMax, "Maximum number for CPU saver's packet bundling"); +E100_PARAM(IFS, "Disable or enable the adaptive IFS algorithm"); +E100_PARAM(RxCongestionControl, "Disable or enable switch to polling mode"); +E100_PARAM(PollingMaxWork, "Max number of receive packets processed on single " + "polling call"); + +/** + * e100_exec_cmd - issue a comand + * @bdp: atapter's private data struct + * @scb_cmd_low: the command that is to be issued + * + * This general routine will issue a command to the e100. + */ +static inline void +e100_exec_cmd(struct e100_private *bdp, u8 cmd_low) +{ + writeb(cmd_low, &(bdp->scb->scb_cmd_low)); +} + +/** + * e100_wait_scb - wait for SCB to clear + * @bdp: atapter's private data struct + * + * This routine checks to see if the e100 has accepted a command. + * It does so by checking the command field in the SCB, which will + * be zeroed by the e100 upon accepting a command. The loop waits + * for up to 1 millisecond for command acceptance. + * + * Returns: + * true if the SCB cleared within 1 millisecond. + * false if it didn't clear within 1 millisecond + */ +unsigned char +e100_wait_scb(struct e100_private *bdp) +{ + int i; + + /* loop on the scb for a few times */ + for (i = 0; i < 100; i++) { + if (!readb(&bdp->scb->scb_cmd_low)) + return true; + cpu_relax(); + } + + /* it didn't work. do it the slow way using udelay()s */ + for (i = 0; i < E100_MAX_BUSY_WAIT; i++) { + if (!readb(&bdp->scb->scb_cmd_low)) + return true; + cpu_relax(); + udelay(1); + } + + return false; +} + +/** + * e100_wait_exec_simple - issue a command + * @bdp: atapter's private data struct + * @scb_cmd_low: the command that is to be issued + * + * This general routine will issue a command to the e100 after waiting for + * the previous command to finish. + * + * Returns: + * true if the command was issued to the chip successfully + * false if the command was not issued to the chip + */ +inline unsigned char +e100_wait_exec_simple(struct e100_private *bdp, u8 scb_cmd_low) +{ + if (!e100_wait_scb(bdp)) { + printk(KERN_ERR "%s e100_wait_exec_simple: Wait failed\n", + bdp->device->name); + return false; + } + e100_exec_cmd(bdp, scb_cmd_low); + return true; +} + +void +e100_exec_cmplx(struct e100_private *bdp, u32 phys_addr, u8 cmd) +{ + writel(phys_addr, &(bdp->scb->scb_gen_ptr)); + readw(&(bdp->scb->scb_status)); /* flashes last write, read-safe */ + e100_exec_cmd(bdp, cmd); +} + +unsigned char +e100_wait_exec_cmplx(struct e100_private *bdp, u32 phys_addr, u8 cmd) +{ + if (!e100_wait_scb(bdp)) { + return false; + } + e100_exec_cmplx(bdp, phys_addr, cmd); + return true; +} + +inline u8 +e100_wait_cus_idle(struct e100_private *bdp) +{ + int i; + + /* loop on the scb for a few times */ + for (i = 0; i < 100; i++) { + if (((readw(&(bdp->scb->scb_status)) & SCB_CUS_MASK) != + SCB_CUS_ACTIVE)) { + return true; + } + cpu_relax(); + } + + for (i = 0; i < E100_MAX_BUSY_WAIT; i++) { + if (((readw(&(bdp->scb->scb_status)) & SCB_CUS_MASK) != + SCB_CUS_ACTIVE)) { + return true; + } + cpu_relax(); + udelay(1); + } + + return false; +} + +/** + * e100_dis_intr - disable interrupts + * @bdp: atapter's private data struct + * + * This routine disables interrupts at the hardware, by setting + * the M (mask) bit in the adapter's CSR SCB command word. + */ +static inline void +e100_dis_intr(struct e100_private *bdp) +{ + /* Disable interrupts on our PCI board by setting the mask bit */ + writeb(SCB_INT_MASK, &bdp->scb->scb_cmd_hi); +} + +/** + * e100_set_intr_mask - set interrupts + * @bdp: atapter's private data struct + * + * This routine sets interrupts at the hardware, by resetting + * the M (mask) bit in the adapter's CSR SCB command word + */ +static inline void +e100_set_intr_mask(struct e100_private *bdp) +{ + writeb(bdp->intr_mask, &bdp->scb->scb_cmd_hi); +} + +static inline void +e100_trigger_SWI(struct e100_private *bdp) +{ + /* Trigger interrupt on our PCI board by asserting SWI bit */ + writeb(SCB_SOFT_INT, &bdp->scb->scb_cmd_hi); +} + +static int __devinit +e100_found1(struct pci_dev *pcid, const struct pci_device_id *ent) +{ + static int first_time = true; + struct net_device *dev = NULL; + struct e100_private *bdp = NULL; + int rc = 0; + + dev = alloc_etherdev(sizeof (struct e100_private)); + if (dev == NULL) { + printk(KERN_ERR "Not able to alloc etherdev struct\n"); + rc = -ENODEV; + goto out; + } + + SET_MODULE_OWNER(dev); + + if (first_time) { + first_time = false; + printk(KERN_NOTICE "%s\n", e100_full_driver_name); + printk(KERN_NOTICE "%s\n", e100_copyright); + printk(KERN_NOTICE "\n"); + } + + bdp = dev->priv; + bdp->pdev = pcid; + bdp->device = dev; + + pci_set_drvdata(pcid, dev); + + if ((rc = e100_alloc_space(bdp)) != 0) { + goto err_dev; + } + + bdp->flags = 0; + bdp->ifs_state = 0; + bdp->ifs_value = 0; + bdp->scb = 0; + + init_timer(&bdp->nontx_timer_id); + bdp->nontx_timer_id.data = (unsigned long) bdp; + bdp->nontx_timer_id.function = (void *) &e100_non_tx_background; + INIT_LIST_HEAD(&(bdp->non_tx_cmd_list)); + bdp->non_tx_command_state = E100_NON_TX_IDLE; + + init_timer(&bdp->watchdog_timer); + bdp->watchdog_timer.data = (unsigned long) dev; + bdp->watchdog_timer.function = (void *) &e100_watchdog; + + if ((rc = e100_pci_setup(pcid, bdp)) != 0) { + goto err_dealloc; + } + + if (((bdp->pdev->device > 0x1030) + && (bdp->pdev->device < 0x103F)) + || (bdp->pdev->device == 0x2449) + || (bdp->pdev->device == 0x2459) + || (bdp->pdev->device == 0x245D)) { + bdp->rev_id = D101MA_REV_ID; /* workaround for ICH3 */ + bdp->flags |= IS_ICH; + } + + if (bdp->rev_id == 0xff) + bdp->rev_id = 1; + + if ((u8) bdp->rev_id >= D101A4_REV_ID) + bdp->flags |= IS_BACHELOR; + + if ((u8) bdp->rev_id >= D102_REV_ID) { + bdp->flags |= USE_IPCB; + bdp->rfd_size = 32; + } else { + bdp->rfd_size = 16; + } + + e100_check_options(e100nics, bdp); + + if (!e100_init(bdp)) { + printk(KERN_ERR "Failed to initialize e100, instance #%d\n", + e100nics); + rc = -ENODEV; + goto err_pci; + } + + dev->irq = pcid->irq; + dev->open = &e100_open; + dev->hard_start_xmit = &e100_xmit_frame; + dev->stop = &e100_close; + dev->change_mtu = &e100_change_mtu; + dev->get_stats = &e100_get_stats; + dev->set_multicast_list = &e100_set_multi; + dev->set_mac_address = &e100_set_mac; + dev->do_ioctl = &e100_ioctl; +#ifdef E100_ZEROCOPY + if (bdp->flags & USE_IPCB) { + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM; + } +#endif + e100nics++; + +#ifdef E100_ETHTOOL_IOCTL + e100_get_speed_duplex_caps(bdp); +#endif /*E100_ETHTOOL_IOCTL */ + + if ((rc = register_netdev(dev)) != 0) { + goto err_pci; + } + + bdp->device_type = ent->driver_data; + printk(KERN_NOTICE + "%s: %s\n", bdp->device->name, e100_get_brand_msg(bdp)); + e100_print_brd_conf(bdp); + + if (e100_create_proc_subdir(bdp) < 0) { + printk(KERN_ERR "Failed to create proc directory for %s\n", + bdp->device->name); + } + +#ifdef ETHTOOL_GWOL + /* Disabling all WOLs as initialization */ + bdp->wolsupported = bdp->wolopts = 0; + if (bdp->rev_id >= D101MA_REV_ID) { + bdp->wolsupported = + WAKE_PHY | WAKE_UCAST | WAKE_ARP | WAKE_MAGIC; + bdp->wolopts = WAKE_MAGIC; + } +#endif + + printk(KERN_NOTICE "\n"); + + goto out; + +err_pci: + iounmap(bdp->scb); + pci_release_regions(pcid); + pci_disable_device(pcid); +err_dealloc: + e100_dealloc_space(bdp); +err_dev: + pci_set_drvdata(pcid, NULL); + kfree(dev); +out: + return rc; +} + +/** + * e100_clear_structs - free resources + * @dev: adapter's net_device struct + * + * Free all device specific structs, unmap i/o address, etc. + */ +static void __devexit +e100_clear_structs(struct net_device *dev) +{ + struct e100_private *bdp = dev->priv; + + iounmap(bdp->scb); + pci_release_regions(bdp->pdev); + pci_disable_device(bdp->pdev); + + e100_dealloc_space(bdp); + pci_set_drvdata(bdp->pdev, NULL); + kfree(dev); +} + +static void __devexit +e100_remove1(struct pci_dev *pcid) +{ + struct net_device *dev; + struct e100_private *bdp; + + if (!(dev = (struct net_device *) pci_get_drvdata(pcid))) + return; + + bdp = dev->priv; + + unregister_netdev(dev); + + e100_remove_proc_subdir(bdp); + + e100_sw_reset(bdp, PORT_SELECTIVE_RESET); + + if (bdp->non_tx_command_state != E100_NON_TX_IDLE) { + del_timer_sync(&bdp->nontx_timer_id); + e100_free_nontx_list(bdp); + bdp->non_tx_command_state = E100_NON_TX_IDLE; + } + +#ifdef ETHTOOL_GWOL + /* Set up wol options and enable PME */ + e100_do_wol(pcid, bdp); +#endif + + e100_clear_structs(dev); + + --e100nics; +} + +MODULE_DEVICE_TABLE(pci, e100_id_table); + +static struct pci_driver e100_driver = { + name: "e100", + id_table: e100_id_table, + probe: e100_found1, + remove: __devexit_p(e100_remove1), + suspend: NULL, + resume: NULL, +}; + +static int __init +e100_init_module(void) +{ + return pci_module_init(&e100_driver); + +} + +static void __exit +e100_cleanup_module(void) +{ + pci_unregister_driver(&e100_driver); +} + +module_init(e100_init_module); +module_exit(e100_cleanup_module); + +/** + * e100_check_options - check command line options + * @board: board number + * @bdp: atapter's private data struct + * + * This routine does range checking on command-line options + */ +void __devinit +e100_check_options(int board, struct e100_private *bdp) +{ + int val; + + if (board >= E100_MAX_NIC) { + printk(KERN_NOTICE "No configuration available for board #%d\n", + board); + printk(KERN_NOTICE "Using defaults for all values\n"); + } + + val = (board < E100_MAX_NIC) ? TxDescriptors[board] : -1; + e100_set_int_option(&(bdp->params.TxDescriptors), val, E100_MIN_TCB, + E100_MAX_TCB, E100_DEFAULT_TCB, + "TxDescriptor count"); + + val = (board < E100_MAX_NIC) ? RxDescriptors[board] : -1; + e100_set_int_option(&(bdp->params.RxDescriptors), val, E100_MIN_RFD, + E100_MAX_RFD, E100_DEFAULT_RFD, + "RxDescriptor count"); + + val = (board < E100_MAX_NIC) ? e100_speed_duplex[board] : -1; + e100_set_int_option(&(bdp->params.e100_speed_duplex), val, 0, 4, + E100_DEFAULT_SPEED_DUPLEX, "speed/duplex mode"); + + val = (board < E100_MAX_NIC) ? ber[board] : -1; + e100_set_int_option(&(bdp->params.ber), val, 0, ZLOCK_MAX_ERRORS, + E100_DEFAULT_BER, "Bit Error Rate count"); + + val = (board < E100_MAX_NIC) ? XsumRX[board] : -1; + e100_set_bool_option(bdp, val, PRM_XSUMRX, E100_DEFAULT_XSUM, + "XsumRX value"); + + /* Default ucode value depended on controller revision */ + val = (board < E100_MAX_NIC) ? ucode[board] : -1; + if (bdp->rev_id >= D101MA_REV_ID) { + e100_set_bool_option(bdp, val, PRM_UCODE, E100_DEFAULT_UCODE, + "ucode value"); + } else { + e100_set_bool_option(bdp, val, PRM_UCODE, false, "ucode value"); + } + + val = (board < E100_MAX_NIC) ? flow_control[board] : -1; + e100_set_bool_option(bdp, val, PRM_FC, E100_DEFAULT_FC, + "flow control value"); + + val = (board < E100_MAX_NIC) ? IFS[board] : -1; + e100_set_bool_option(bdp, val, PRM_IFS, E100_DEFAULT_IFS, "IFS value"); + + val = (board < E100_MAX_NIC) ? BundleSmallFr[board] : -1; + e100_set_bool_option(bdp, val, PRM_BUNDLE_SMALL, + E100_DEFAULT_BUNDLE_SMALL_FR, + "CPU saver bundle small frames value"); + + val = (board < E100_MAX_NIC) ? IntDelay[board] : -1; + e100_set_int_option(&(bdp->params.IntDelay), val, 0x0, 0xFFFF, + E100_DEFAULT_CPUSAVER_INTERRUPT_DELAY, + "CPU saver interrupt delay value"); + + val = (board < E100_MAX_NIC) ? BundleMax[board] : -1; + e100_set_int_option(&(bdp->params.BundleMax), val, 0x1, 0xFFFF, + E100_DEFAULT_CPUSAVER_BUNDLE_MAX, + "CPU saver bundle max value"); + + val = (board < E100_MAX_NIC) ? RxCongestionControl[board] : -1; + e100_set_bool_option(bdp, val, PRM_RX_CONG, + E100_DEFAULT_RX_CONGESTION_CONTROL, + "Rx Congestion Control value"); + + val = (board < E100_MAX_NIC) ? PollingMaxWork[board] : -1; + e100_set_int_option(&(bdp->params.PollingMaxWork), val, 1, E100_MAX_RFD, + RxDescriptors[board], "Polling Max Work value"); + + if (val <= 0) { + bdp->params.b_params &= ~PRM_RX_CONG; + } +} + +/** + * e100_set_int_option - check and set an integer option + * @option: a pointer to the relevant option field + * @val: the value specified + * @min: the minimum valid value + * @max: the maximum valid value + * @default_val: the default value + * @name: the name of the option + * + * This routine does range checking on a command-line option. + * If the option's value is '-1' use the specified default. + * Otherwise, if the value is invalid, change it to the default. + */ +void __devinit +e100_set_int_option(int *option, int val, int min, int max, int default_val, + char *name) +{ + if (val == -1) { /* no value specified. use default */ + *option = default_val; + + } else if ((val < min) || (val > max)) { + printk(KERN_NOTICE + "Invalid %s specified (%i). Valid range is %i-%i\n", + name, val, min, max); + printk(KERN_NOTICE "Using default %s of %i\n", name, + default_val); + *option = default_val; + } else { + printk(KERN_INFO "Using specified %s of %i\n", name, val); + *option = val; + } +} + +/** + * e100_set_bool_option - check and set a boolean option + * @bdp: atapter's private data struct + * @val: the value specified + * @mask: the mask for the relevant option + * @default_val: the default value + * @name: the name of the option + * + * This routine checks a boolean command-line option. + * If the option's value is '-1' use the specified default. + * Otherwise, if the value is invalid (not 0 or 1), + * change it to the default. + */ +void __devinit +e100_set_bool_option(struct e100_private *bdp, int val, u32 mask, + int default_val, char *name) +{ + if (val == -1) { + if (default_val) + bdp->params.b_params |= mask; + + } else if ((val != true) && (val != false)) { + printk(KERN_NOTICE + "Invalid %s specified (%i). Valid values are %i/%i\n", + name, val, false, true); + printk(KERN_NOTICE "Using default %s of %i\n", name, + default_val); + + if (default_val) + bdp->params.b_params |= mask; + } else { + printk(KERN_INFO "Using specified %s of %i\n", name, val); + if (val) + bdp->params.b_params |= mask; + } +} + +static int +e100_open(struct net_device *dev) +{ + struct e100_private *bdp; + int rc = 0; + + bdp = dev->priv; + + read_lock(&(bdp->isolate_lock)); + + if (bdp->driver_isolated) { + rc = -EBUSY; + goto exit; + } + + /* setup the tcb pool */ + if (!e100_alloc_tcb_pool(bdp)) { + rc = -ENOMEM; + goto err_exit; + } + bdp->last_tcb = NULL; + + bdp->tcb_pool.head = 0; + bdp->tcb_pool.tail = 1; + + e100_setup_tcb_pool((tcb_t *) bdp->tcb_pool.data, + bdp->params.TxDescriptors, bdp); + + if (!e100_alloc_rfd_pool(bdp)) { + rc = -ENOMEM; + goto err_exit; + } + + if (!e100_wait_exec_cmplx(bdp, 0, SCB_CUC_LOAD_BASE)) { + rc = -EAGAIN; + goto err_exit; + } + + if (!e100_wait_exec_cmplx(bdp, 0, SCB_RUC_LOAD_BASE)) { + rc = -EAGAIN; + goto err_exit; + } + + mod_timer(&(bdp->watchdog_timer), jiffies + (2 * HZ)); + + netif_start_queue(dev); + + e100_start_ru(bdp); + if ((rc = request_irq(dev->irq, &e100intr, SA_SHIRQ, + e100_short_driver_name, dev)) != 0) { + del_timer_sync(&bdp->watchdog_timer); + goto err_exit; + } + if (bdp->params.b_params & PRM_RX_CONG) { + DECLARE_TASKLET(polling_tasklet, + e100_polling_tasklet, (unsigned long) bdp); + bdp->polling_tasklet = polling_tasklet; + } + bdp->intr_mask = 0; + e100_set_intr_mask(bdp); + + e100_force_config(bdp); + + goto exit; + +err_exit: + e100_clear_pools(bdp); +exit: + read_unlock(&(bdp->isolate_lock)); + return rc; +} + +static int +e100_close(struct net_device *dev) +{ + struct e100_private *bdp = dev->priv; + + bdp->intr_mask = SCB_INT_MASK; + e100_isolate_driver(bdp); + +#ifdef ETHTOOL_GWOL + bdp->ip_lbytes = e100_get_ip_lbytes(dev); +#endif + free_irq(dev->irq, dev); + e100_clear_pools(bdp); + + if (bdp->params.b_params & PRM_RX_CONG) { + tasklet_kill(&(bdp->polling_tasklet)); + } + + /* set the isolate flag to false, so e100_open can be called */ + bdp->driver_isolated = false; + + return 0; +} + +static int +e100_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > (ETH_DATA_LEN + VLAN_SIZE))) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + +static int +e100_xmit_frame(struct sk_buff *skb, struct net_device *dev) +{ + int rc = 0; + int notify_stop = false; + struct e100_private *bdp = dev->priv; + + read_lock(&(bdp->isolate_lock)); + + if (bdp->driver_isolated) { + rc = -EBUSY; + goto exit2; + } + + if (!spin_trylock(&bdp->bd_non_tx_lock)) { + notify_stop = true; + rc = 1; + goto exit2; + } + + if (!TCBS_AVAIL(bdp->tcb_pool) || + (bdp->non_tx_command_state != E100_NON_TX_IDLE)) { + notify_stop = true; + rc = 1; + goto exit1; + } + + e100_prepare_xmit_buff(bdp, skb); + + bdp->drv_stats.net_stats.tx_bytes += skb->len; + + dev->trans_start = jiffies; + +exit1: + spin_unlock(&bdp->bd_non_tx_lock); +exit2: + read_unlock(&(bdp->isolate_lock)); + if (notify_stop) { + netif_stop_queue(dev); + } + + return rc; +} + +/** + * e100_get_stats - get driver statistics + * @dev: adapter's net_device struct + * + * This routine is called when the OS wants the adapter's stats returned. + * It returns the address of the net_device_stats stucture for the device. + * If the statistics are currently being updated, then they might be incorrect + * for a short while. However, since this cannot actually cause damage, no + * locking is used. + */ +struct net_device_stats * +e100_get_stats(struct net_device *dev) +{ + struct e100_private *bdp = dev->priv; + + bdp->drv_stats.net_stats.tx_errors = + bdp->drv_stats.net_stats.tx_carrier_errors + + bdp->drv_stats.net_stats.tx_aborted_errors; + + bdp->drv_stats.net_stats.rx_errors = + bdp->drv_stats.net_stats.rx_crc_errors + + bdp->drv_stats.net_stats.rx_frame_errors + + bdp->drv_stats.net_stats.rx_length_errors + + bdp->drv_stats.rcv_cdt_frames; + + return &(bdp->drv_stats.net_stats); +} + +/** + * e100_set_mac - set the MAC address + * @dev: adapter's net_device struct + * @addr: the new address + * + * This routine sets the ethernet address of the board + * Returns: + * 0 - if successful + * -1 - otherwise + */ +static int +e100_set_mac(struct net_device *dev, void *addr) +{ + struct e100_private *bdp; + int rc = -1; + struct sockaddr *p_sockaddr = (struct sockaddr *) addr; + + bdp = dev->priv; + + read_lock(&(bdp->isolate_lock)); + + if (bdp->driver_isolated) { + goto exit; + } + if (e100_setup_iaaddr(bdp, (u8 *) (p_sockaddr->sa_data))) { + memcpy(&(dev->dev_addr[0]), p_sockaddr->sa_data, ETH_ALEN); + rc = 0; + } + +exit: + read_unlock(&(bdp->isolate_lock)); + return rc; +} + +static void +e100_set_multi_exec(struct net_device *dev) +{ + struct e100_private *bdp = dev->priv; + mltcst_cb_t *mcast_buff; + cb_header_t *cb_hdr; + struct dev_mc_list *mc_list; + unsigned int i; + nxmit_cb_entry_t *cmd = e100_alloc_non_tx_cmd(bdp); + + if (cmd != NULL) { + mcast_buff = &((cmd->non_tx_cmd)->ntcb.multicast); + cb_hdr = &((cmd->non_tx_cmd)->ntcb.multicast.mc_cbhdr); + } else { + return; + } + + /* initialize the multi cast command */ + cb_hdr->cb_cmd = __constant_cpu_to_le16(CB_MULTICAST); + + /* now fill in the rest of the multicast command */ + *(u16 *) (&(mcast_buff->mc_count)) = cpu_to_le16(dev->mc_count * 6); + for (i = 0, mc_list = dev->mc_list; + (i < dev->mc_count) && (i < MAX_MULTICAST_ADDRS); + i++, mc_list = mc_list->next) { + /* copy into the command */ + memcpy(&(mcast_buff->mc_addr[i * ETH_ALEN]), + (u8 *) &(mc_list->dmi_addr), ETH_ALEN); + } + + if (!e100_exec_non_cu_cmd(bdp, cmd)) { + printk(KERN_WARNING "%s: Multicast setup failed\n", dev->name); + } +} + +/** + * e100_set_multi - set multicast status + * @dev: adapter's net_device struct + * + * This routine is called to add or remove multicast addresses, and/or to + * change the adapter's promiscuous state. + */ +static void +e100_set_multi(struct net_device *dev) +{ + struct e100_private *bdp = dev->priv; + unsigned char promisc_enbl; + unsigned char mulcast_enbl; + + read_lock(&(bdp->isolate_lock)); + if (bdp->driver_isolated) { + goto exit; + } + promisc_enbl = (dev->flags & IFF_PROMISC); + mulcast_enbl = ((dev->flags & IFF_ALLMULTI) || + (dev->mc_count > MAX_MULTICAST_ADDRS)); + + e100_config_promisc(bdp, promisc_enbl); + e100_config_mulcast_enbl(bdp, mulcast_enbl); + + /* reconfigure the chip if something has changed in its config space */ + e100_config(bdp); + + if ((promisc_enbl) || (mulcast_enbl)) { + goto exit; /* no need for Multicast Cmd */ + } + + /* get the multicast CB */ + e100_set_multi_exec(dev); + +exit: + read_unlock(&(bdp->isolate_lock)); +} + +static int +e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + + switch (cmd) { + +#ifdef E100_ETHTOOL_IOCTL + case SIOCETHTOOL: + return e100_do_ethtool_ioctl(dev, ifr); + break; +#endif /*E100_ETHTOOL_IOCTL */ + +#ifdef E100_MII_IOCTL + case SIOCGMIIPHY: /* Get address of MII PHY in use. */ + case SIOCGMIIREG: /* Read MII PHY register. */ + case SIOCSMIIREG: /* Write to MII PHY register. */ + return e100_mii_ioctl(dev, ifr, cmd); + break; +#endif /*E100_MII_IOCTL */ + + default: + return -EOPNOTSUPP; + } + return 0; + +} + +/** + * e100init - initialize the adapter + * @bdp: atapter's private data struct + * + * This routine is called when this driver is loaded. This is the initialization + * routine which allocates memory, configures the adapter and determines the + * system resources. + * + * Returns: + * true: if successful + * false: otherwise + */ +static unsigned char __devinit +e100_init(struct e100_private *bdp) +{ + e100_sw_init(bdp); + + if (!e100_selftest(bdp, NULL, NULL)) { + printk(KERN_ERR "selftest failed\n"); + return false; + } + + /* read the MAC address from the eprom */ + e100_rd_eaddr(bdp); + /* read NIC's part number */ + e100_rd_pwa_no(bdp); + + if (!e100_hw_init(bdp, PORT_SOFTWARE_RESET)) { + printk(KERN_ERR "hw init failed\n"); + return false; + } + e100_dis_intr(bdp); + + return true; +} + +/** + * e100_sw_init - initialize software structs + * @bdp: atapter's private data struct + * + * This routine initializes all software structures. Sets up the + * circular structures for the RFD's & TCB's. Allocates the per board + * structure for storing adapter information. The CSR is also memory + * mapped in this routine. + * + * Returns : + * true: if S/W was successfully initialized + * false: otherwise + */ +static unsigned char __devinit +e100_sw_init(struct e100_private *bdp) +{ + bdp->next_cu_cmd = START_WAIT; // init the next cu state + + /* + * Set the value for # of good xmits per underrun. the value assigned + * here is an intelligent suggested default. Nothing magical about it. + */ + bdp->tx_per_underrun = DEFAULT_TX_PER_UNDERRUN; + + /* get the default transmit threshold value */ + bdp->tx_thld = TX_THRSHLD; + + /* get the EPROM size */ + bdp->eeprom_size = e100_eeprom_size(bdp); + + /* Initialize our spinlocks */ + spin_lock_init(&(bdp->bd_lock)); + spin_lock_init(&(bdp->bd_non_tx_lock)); + spin_lock_init(&(bdp->config_lock)); + spin_lock_init(&(bdp->mdi_access_lock)); + bdp->isolate_lock = RW_LOCK_UNLOCKED; + bdp->driver_isolated = false; + + return 1; +} + +/** + * e100_hw_init - initialized tthe hardware + * @bdp: atapter's private data struct + * @reset_cmd: s/w reset or selective reset + * + * This routine performs a reset on the adapter, and configures the adapter. + * This includes configuring the 82557 LAN controller, validating and setting + * the node address, detecting and configuring the Phy chip on the adapter, + * and initializing all of the on chip counters. + * + * Returns: + * true - If the adapter was initialized + * false - If the adapter failed initialization + */ +unsigned char __devinit +e100_hw_init(struct e100_private *bdp, u32 reset_cmd) +{ + if (!e100_phy_init(bdp)) + return false; + + /* Issue a software reset to the e100 */ + e100_sw_reset(bdp, reset_cmd); + + /* Load the CU BASE (set to 0, because we use linear mode) */ + if (!e100_wait_exec_cmplx(bdp, 0, SCB_CUC_LOAD_BASE)) + return false; + + if (!e100_wait_exec_cmplx(bdp, 0, SCB_RUC_LOAD_BASE)) + return false; + + /* Load interrupt microcode */ + if (e100_load_microcode(bdp)) { + bdp->flags |= DF_UCODE_LOADED; + } + + e100_config_init(bdp); + if (!e100_config(bdp)) { + return false; + } + + if (!e100_setup_iaaddr(bdp, bdp->device->dev_addr)) + return false; + + /* Clear the internal counters */ + if (!e100_clr_cntrs(bdp)) + return false; + + /* Change for 82558 enhancement */ + /* If 82558/9 and if the user has enabled flow control, set up the + * Flow Control Reg. in the CSR */ + if ((bdp->flags & IS_BACHELOR) + && (bdp->params.b_params & PRM_FC)) { + writeb(DFLT_FC_THLD, &bdp->scb->scb_ext.d101_scb.scb_fc_thld); + writeb(DFLT_FC_CMD, + &bdp->scb->scb_ext.d101_scb.scb_fc_xon_xoff); + } + + return true; +} + +/** + * e100_setup_tcb_pool - setup TCB circular list + * @head: Pointer to head of the allocated TCBs + * @qlen: Number of elements in the queue + * @bdp: atapter's private data struct + * + * This routine arranges the contigiously allocated TCB's in a circular list. + * Also does the one time initialization of the TCBs. + */ +static void +e100_setup_tcb_pool(tcb_t *head, unsigned int qlen, struct e100_private *bdp) +{ + int ele_no; + tcb_t *pcurr_tcb; /* point to current tcb */ + u32 next_phys; /* the next phys addr */ + u16 txcommand = CB_S_BIT | CB_TX_SF_BIT; + + if (bdp->flags & USE_IPCB) { + txcommand |= CB_IPCB_TRANSMIT | CB_CID_DEFAULT; + } else if (bdp->flags & IS_BACHELOR) { + txcommand |= CB_TRANSMIT | CB_CID_DEFAULT; + } else { + txcommand |= CB_TRANSMIT; + } + + for (ele_no = 0, next_phys = bdp->tcb_phys, pcurr_tcb = head; + ele_no < qlen; ele_no++, pcurr_tcb++) { + + /* set the phys addr for this TCB, next_phys has not incr. yet */ + pcurr_tcb->tcb_phys = next_phys; + next_phys += sizeof (tcb_t); + + /* set the link to next tcb */ + if (ele_no == (qlen - 1)) + pcurr_tcb->tcb_hdr.cb_lnk_ptr = + cpu_to_le32(bdp->tcb_phys); + else + pcurr_tcb->tcb_hdr.cb_lnk_ptr = cpu_to_le32(next_phys); + + pcurr_tcb->tcb_hdr.cb_status = 0; + pcurr_tcb->tcb_hdr.cb_cmd = cpu_to_le16(txcommand); + pcurr_tcb->tcb_cnt = 0; + pcurr_tcb->tcb_thrshld = bdp->tx_thld; + if (ele_no < 2) { + pcurr_tcb->tcb_hdr.cb_status = + cpu_to_le16(CB_STATUS_COMPLETE); + } + pcurr_tcb->tcb_tbd_num = 1; + + if (bdp->flags & IS_BACHELOR) { + pcurr_tcb->tcb_tbd_ptr = + __constant_cpu_to_le32(0xFFFFFFFF); + } else { + pcurr_tcb->tcb_tbd_ptr = + cpu_to_le32(pcurr_tcb->tcb_phys + 0x10); + } + +#ifdef E100_ZEROCOPY + if (bdp->flags & IS_BACHELOR) { + pcurr_tcb->tcb_tbd_expand_ptr = + cpu_to_le32(pcurr_tcb->tcb_phys + 0x20); + } else { + pcurr_tcb->tcb_tbd_expand_ptr = + cpu_to_le32(pcurr_tcb->tcb_phys + 0x10); + } + pcurr_tcb->tcb_tbd_dflt_ptr = pcurr_tcb->tcb_tbd_ptr; +#endif + + if (bdp->flags & USE_IPCB) { + pcurr_tcb->tbd_ptr = &(pcurr_tcb->tcbu.tbd_array[1]); + pcurr_tcb->tcbu.ipcb.ip_activation_high = + IPCB_IP_ACTIVATION_DEFAULT; + pcurr_tcb->tcbu.ipcb.vlan = 0; + } else { + pcurr_tcb->tbd_ptr = &(pcurr_tcb->tcbu.tbd_array[0]); + } + + pcurr_tcb->tcb_skb = NULL; + } + + wmb(); +} + +/***************************************************************************/ +/***************************************************************************/ +/* Memory Management Routines */ +/***************************************************************************/ + +/** + * e100_alloc_space - allocate private driver data + * @bdp: atapter's private data struct + * + * This routine allocates memory for the driver. Memory allocated is for the + * selftest and statistics structures. + * + * Returns: + * 0: if the operation was successful + * %-ENOMEM: if memory allocation failed + */ +unsigned char __devinit +e100_alloc_space(struct e100_private *bdp) +{ + unsigned long off; + + /* allocate all the dma-able structures in one call: + * selftest results, adapter stats, and non-tx cb commands */ + if (!(bdp->dma_able = + pci_alloc_consistent(bdp->pdev, sizeof (bd_dma_able_t), + &(bdp->dma_able_phys)))) { + goto err; + } + + /* now assign the various pointers into the struct we've just allocated */ + off = offsetof(bd_dma_able_t, selftest); + + bdp->selftest = (self_test_t *) (bdp->dma_able + off); + bdp->selftest_phys = bdp->dma_able_phys + off; + + off = offsetof(bd_dma_able_t, stats_counters); + + bdp->stats_counters = (max_counters_t *) (bdp->dma_able + off); + bdp->stat_cnt_phys = bdp->dma_able_phys + off; + + return 0; + +err: + printk(KERN_ERR + "%s - Failed to allocate memory\n", e100_short_driver_name); + return -ENOMEM; +} + +/** + * e100_alloc_tcb_pool - allocate TCB circular list + * @bdp: atapter's private data struct + * + * This routine allocates memory for the circular list of transmit descriptors. + * + * Returns: + * 0: if allocation has failed. + * 1: Otherwise. + */ +int +e100_alloc_tcb_pool(struct e100_private *bdp) +{ + int stcb = sizeof (tcb_t) * bdp->params.TxDescriptors; + + /* allocate space for the TCBs */ + if (!(bdp->tcb_pool.data = + pci_alloc_consistent(bdp->pdev, stcb, &bdp->tcb_phys))) + return 0; + + memset(bdp->tcb_pool.data, 0x00, stcb); + + return 1; +} + +void +e100_free_tcb_pool(struct e100_private *bdp) +{ + pci_free_consistent(bdp->pdev, + sizeof (tcb_t) * bdp->params.TxDescriptors, + bdp->tcb_pool.data, bdp->tcb_phys); + bdp->tcb_phys = 0; +} + +static void +e100_dealloc_space(struct e100_private *bdp) +{ + if (bdp->dma_able) { + pci_free_consistent(bdp->pdev, sizeof (bd_dma_able_t), + bdp->dma_able, bdp->dma_able_phys); + } + + bdp->selftest_phys = 0; + bdp->stat_cnt_phys = 0; + bdp->dma_able_phys = 0; + bdp->dma_able = 0; +} + +static void +e100_free_rfd_pool(struct e100_private *bdp) +{ + struct rx_list_elem *rx_struct; + + while (!list_empty(&(bdp->active_rx_list))) { + + rx_struct = list_entry(bdp->active_rx_list.next, + struct rx_list_elem, list_elem); + list_del(&(rx_struct->list_elem)); + pci_unmap_single(bdp->pdev, rx_struct->dma_addr, + sizeof (rfd_t), PCI_DMA_TODEVICE); + dev_kfree_skb(rx_struct->skb); + kfree(rx_struct); + } + + while (!list_empty(&(bdp->rx_struct_pool))) { + rx_struct = list_entry(bdp->rx_struct_pool.next, + struct rx_list_elem, list_elem); + list_del(&(rx_struct->list_elem)); + kfree(rx_struct); + } +} + +/** + * e100_alloc_rfd_pool - allocate RFDs + * @bdp: atapter's private data struct + * + * Allocates initial pool of skb which holds both rfd and data, + * and return a pointer to the head of the list + */ +static int +e100_alloc_rfd_pool(struct e100_private *bdp) +{ + struct rx_list_elem *rx_struct; + int i; + + INIT_LIST_HEAD(&(bdp->active_rx_list)); + INIT_LIST_HEAD(&(bdp->rx_struct_pool)); + bdp->skb_req = bdp->params.RxDescriptors; + for (i = 0; i < bdp->skb_req; i++) { + rx_struct = kmalloc(sizeof (struct rx_list_elem), GFP_ATOMIC); + list_add(&(rx_struct->list_elem), &(bdp->rx_struct_pool)); + } + e100_alloc_skbs(bdp); + return !list_empty(&(bdp->active_rx_list)); + +} + +void +e100_clear_pools(struct e100_private *bdp) +{ + bdp->last_tcb = NULL; + e100_free_rfd_pool(bdp); + e100_free_tcb_pool(bdp); +} + +/*****************************************************************************/ +/*****************************************************************************/ +/* Run Time Functions */ +/*****************************************************************************/ + +/** + * e100_watchdog + * @dev: adapter's net_device struct + * + * This routine runs every 2 seconds and updates our statitics and link state, + * and refreshs txthld value. + */ +void +e100_watchdog(struct net_device *dev) +{ + struct e100_private *bdp = dev->priv; + + read_lock(&(bdp->isolate_lock)); + if (bdp->driver_isolated) { + goto exit; + } + if (!netif_running(dev)) { + goto exit; + } + spin_lock_bh(&(bdp->mdi_access_lock)); + + /* check if link state has changed */ + if (e100_phy_check(bdp)) { + if (netif_carrier_ok(dev)) { + printk(KERN_ERR + "e100: %s NIC Link is Up %d Mbps %s duplex\n", + bdp->device->name, bdp->cur_line_speed, + (bdp->cur_dplx_mode == HALF_DUPLEX) ? + "Half" : "Full"); + + e100_config_fc(bdp); + e100_config(bdp); + + } else { + printk(KERN_ERR "e100: %s NIC Link is Down\n", + bdp->device->name); + } + } + + // toggle the tx queue according to link status + // this also resolves a race condition between tx & non-cu cmd flows + if (netif_carrier_ok(dev)) { + if (netif_running(dev)) + netif_wake_queue(dev); + } else { + netif_stop_queue(dev); + } + + rmb(); + + if (e100_update_stats(bdp)) { + + /* Check if a change in the IFS parameter is needed, + and configure the device accordingly */ + if (bdp->params.b_params & PRM_IFS) + e100_manage_adaptive_ifs(bdp); + + /* Now adjust our dynamic tx threshold value */ + e100_refresh_txthld(bdp); + + /* Now if we are on a 557 and we havn't received any frames then we + * should issue a multicast command to reset the RU */ + if (bdp->rev_id < D101A4_REV_ID) { + if (!(bdp->stats_counters->basic_stats.rcv_gd_frames)) { + e100_set_multi(dev); + } + } + + /* Update the statistics needed by the upper interface */ + /* This should be the last statistic related command + * as it's async. now */ + e100_dump_stats_cntrs(bdp); + } + + wmb(); + + spin_unlock_bh(&(bdp->mdi_access_lock)); + + /* relaunch watchdog timer in 2 sec */ + mod_timer(&(bdp->watchdog_timer), jiffies + (2 * HZ)); + + if (list_empty(&bdp->active_rx_list)) + e100_trigger_SWI(bdp); + +exit: + read_unlock(&(bdp->isolate_lock)); +} + +/** + * e100_manage_adaptive_ifs + * @bdp: atapter's private data struct + * + * This routine manages the adaptive Inter-Frame Spacing algorithm + * using a state machine. + */ +void +e100_manage_adaptive_ifs(struct e100_private *bdp) +{ + static u16 state_table[9][4] = { // rows are states + {2, 0, 0, 0}, // state0 // column0: next state if increasing + {2, 0, 5, 30}, // state1 // column1: next state if decreasing + {5, 1, 5, 30}, // state2 // column2: IFS value for 100 mbit + {5, 3, 0, 0}, // state3 // column3: IFS value for 10 mbit + {5, 3, 10, 60}, // state4 + {8, 4, 10, 60}, // state5 + {8, 6, 0, 0}, // state6 + {8, 6, 20, 60}, // state7 + {8, 7, 20, 60} // state8 + }; + + u32 transmits = + le32_to_cpu(bdp->stats_counters->basic_stats.xmt_gd_frames); + u32 collisions = + le32_to_cpu(bdp->stats_counters->basic_stats.xmt_ttl_coll); + u32 state = bdp->ifs_state; + u32 old_value = bdp->ifs_value; + int next_col; + u32 min_transmits; + + if (bdp->cur_dplx_mode == FULL_DUPLEX) { + bdp->ifs_state = 0; + bdp->ifs_value = 0; + + } else { /* Half Duplex */ + /* Set speed specific parameters */ + if (bdp->cur_line_speed == 100) { + next_col = 2; + min_transmits = MIN_NUMBER_OF_TRANSMITS_100; + + } else { /* 10 Mbps */ + next_col = 3; + min_transmits = MIN_NUMBER_OF_TRANSMITS_10; + } + + if ((transmits / 32 < collisions) + && (transmits > min_transmits)) { + state = state_table[state][0]; /* increment */ + + } else if (transmits < min_transmits) { + state = state_table[state][1]; /* decrement */ + } + + bdp->ifs_value = state_table[state][next_col]; + bdp->ifs_state = state; + } + + /* If the IFS value has changed, configure the device */ + if (bdp->ifs_value != old_value) { + e100_config_ifs(bdp); + e100_config(bdp); + } +} + +void +e100_polling_tasklet(unsigned long ptr) +{ + struct e100_private *bdp = (struct e100_private *) ptr; + unsigned int rx_congestion = 0; + u32 skb_cnt; + + /* the device is closed, don't continue or else bad things may happen. */ + if (!netif_running(bdp->device)) { + return; + } + + read_lock(&(bdp->isolate_lock)); + if (bdp->driver_isolated) { + tasklet_schedule(&(bdp->polling_tasklet)); + goto exit; + } + + e100_alloc_skbs(bdp); + + skb_cnt = e100_rx_srv(bdp, bdp->params.PollingMaxWork, &rx_congestion); + + bdp->drv_stats.rx_tasklet_pkts += skb_cnt; + + if (rx_congestion || skb_cnt) { + tasklet_schedule(&(bdp->polling_tasklet)); + } else { + bdp->intr_mask &= ~SCB_INT_MASK; + + bdp->drv_stats.poll_intr_switch++; + } + + bdp->tx_count = 0; /* restart tx interrupt batch count */ + e100_tx_srv(bdp); + + e100_set_intr_mask(bdp); + +exit: + read_unlock(&(bdp->isolate_lock)); +} + +/** + * e100intr - interrupt handler + * @irq: the IRQ number + * @dev_inst: the net_device struct + * @regs: registers (unused) + * + * This routine is the ISR for the e100 board. It services + * the RX & TX queues & starts the RU if it has stopped due + * to no resources. + */ +void +e100intr(int irq, void *dev_inst, struct pt_regs *regs) +{ + struct net_device *dev; + struct e100_private *bdp; + u16 intr_status; + + dev = dev_inst; + bdp = dev->priv; + + intr_status = readw(&bdp->scb->scb_status); + if (!intr_status || (intr_status == 0xffff)) { + return; + } + + /* disable intr before we ack & after identifying the intr as ours */ + e100_dis_intr(bdp); + + writew(intr_status, &bdp->scb->scb_status); /* ack intrs */ + + /* the device is closed, don't continue or else bad things may happen. */ + if (!netif_running(dev)) { + e100_set_intr_mask(bdp); + return; + } + + read_lock(&(bdp->isolate_lock)); + if (bdp->driver_isolated) { + goto exit; + } + + /* SWI intr (triggered by watchdog) is signal to allocate new skb buffers */ + if (intr_status & SCB_STATUS_ACK_SWI) { + e100_alloc_skbs(bdp); + } + + /* do recv work if any */ + if (intr_status & + (SCB_STATUS_ACK_FR | SCB_STATUS_ACK_RNR | SCB_STATUS_ACK_SWI)) { + int rx_congestion; + + bdp->drv_stats.rx_intr_pkts += + e100_rx_srv(bdp, 0, &rx_congestion); + if ((bdp->params.b_params & PRM_RX_CONG) && rx_congestion) { + bdp->intr_mask |= SCB_INT_MASK; + tasklet_schedule(&(bdp->polling_tasklet)); + + bdp->drv_stats.poll_intr_switch++; + } + } + + /* clean up after tx'ed packets */ + if (intr_status & (SCB_STATUS_ACK_CNA | SCB_STATUS_ACK_CX)) { + bdp->tx_count = 0; /* restart tx interrupt batch count */ + e100_tx_srv(bdp); + } + +exit: + e100_set_intr_mask(bdp); + read_unlock(&(bdp->isolate_lock)); +} + +/** + * e100_tx_skb_free - free TX skbs resources + * @bdp: atapter's private data struct + * @tcb: associated tcb of the freed skb + * + * This routine frees resources of TX skbs. + */ +static void inline +e100_tx_skb_free(struct e100_private *bdp, tcb_t *tcb) +{ + if (tcb->tcb_skb) { +#ifdef E100_ZEROCOPY + int i; + tbd_t *tbd_arr = tcb->tbd_ptr; + int frags = skb_shinfo(tcb->tcb_skb)->nr_frags; + + for (i = 0; i <= frags; i++, tbd_arr++) { + pci_unmap_single(bdp->pdev, + le32_to_cpu(tbd_arr->tbd_buf_addr), + le16_to_cpu(tbd_arr->tbd_buf_cnt), + PCI_DMA_TODEVICE); + } +#else + pci_unmap_single(bdp->pdev, + le32_to_cpu((tcb->tbd_ptr)->tbd_buf_addr), + tcb->tcb_skb->len, PCI_DMA_TODEVICE); +#endif + dev_kfree_skb_irq(tcb->tcb_skb); + tcb->tcb_skb = NULL; + } +} + +/** + * e100_tx_srv - service TX queues + * @bdp: atapter's private data struct + * + * This routine services the TX queues. It reclaims the TCB's & TBD's & other + * resources used during the transmit of this buffer. It is called from the ISR. + * We don't need a tx_lock since we always access buffers which were already + * prepared. + */ +void +e100_tx_srv(struct e100_private *bdp) +{ + tcb_t *tcb; + int i; + + /* go over at most TxDescriptors buffers */ + for (i = 0; i < bdp->params.TxDescriptors; i++) { + tcb = bdp->tcb_pool.data; + tcb += bdp->tcb_pool.head; + + rmb(); + + /* if the buffer at 'head' is not complete, break */ + if (!(tcb->tcb_hdr.cb_status & + __constant_cpu_to_le16(CB_STATUS_COMPLETE))) + break; + + /* service next buffer, clear the out of resource condition */ + e100_tx_skb_free(bdp, tcb); + + if (netif_running(bdp->device)) + netif_wake_queue(bdp->device); + + /* if we've caught up with 'tail', break */ + if (NEXT_TCB_TOUSE(bdp->tcb_pool.head) == bdp->tcb_pool.tail) { + break; + } + + bdp->tcb_pool.head = NEXT_TCB_TOUSE(bdp->tcb_pool.head); + } +} + +/** + * e100_rx_srv - service RX queue + * @bdp: atapter's private data struct + * @max_number_of_rfds: max number of RFDs to process + * @rx_congestion: flag pointer, to inform the calling function of congestion. + * + * This routine processes the RX interrupt & services the RX queues. + * For each successful RFD, it allocates a new msg block, links that + * into the RFD list, and sends the old msg upstream. + * The new RFD is then put at the end of the free list of RFD's. + * It returns the number of serviced RFDs. + */ +u32 +e100_rx_srv(struct e100_private *bdp, u32 max_number_of_rfds, + int *rx_congestion) +{ + rfd_t *rfd; /* new rfd, received rfd */ + int i; + u16 rfd_status; + struct sk_buff *skb; + struct net_device *dev; + unsigned int data_sz; + struct rx_list_elem *rx_struct; + u32 rfd_cnt = 0; + + if (rx_congestion) { + *rx_congestion = 0; + } + + dev = bdp->device; + + /* current design of rx is as following: + * 1. socket buffer (skb) used to pass network packet to upper layer + * 2. all HW host memory structures (like RFDs, RBDs and data buffers) + * are placed in a skb's data room + * 3. when rx process is complete, we change skb internal pointers to exclude + * from data area all unrelated things (RFD, RDB) and to leave + * just rx'ed packet netto + * 4. for each skb passed to upper layer, new one is allocated instead. + * 5. if no skb left, in 2 sec another atempt to allocate skbs will be made + * (watchdog trigger SWI intr and isr should allocate new skbs) + */ + for (i = 0; i < bdp->params.RxDescriptors; i++) { + if (max_number_of_rfds && (rfd_cnt >= max_number_of_rfds)) { + break; + } + if (list_empty(&(bdp->active_rx_list))) + break; + + rx_struct = list_entry(bdp->active_rx_list.next, + struct rx_list_elem, list_elem); + skb = rx_struct->skb; + + rfd = RFD_POINTER(skb, bdp); /* locate RFD within skb */ + + // sync only the RFD header + pci_dma_sync_single(bdp->pdev, rx_struct->dma_addr, + bdp->rfd_size, PCI_DMA_FROMDEVICE); + rfd_status = le16_to_cpu(rfd->rfd_header.cb_status); /* get RFD's status */ + if (!(rfd_status & RFD_STATUS_COMPLETE)) /* does not contains data yet - exit */ + break; + + /* to allow manipulation with current skb we need to unlink it */ + list_del(&(rx_struct->list_elem)); + + /* do not free & unmap badly recieved packet. + * move it to the end of skb list for reuse */ + if (!(rfd_status & RFD_STATUS_OK)) { + e100_add_skb_to_end(bdp, rx_struct); + continue; + } + + data_sz = min_t(u16, (le16_to_cpu(rfd->rfd_act_cnt) & 0x3fff), + (sizeof (rfd_t) - bdp->rfd_size)); + + /* now sync all the data */ + pci_dma_sync_single(bdp->pdev, rx_struct->dma_addr, + (data_sz + bdp->rfd_size), + PCI_DMA_FROMDEVICE); + + // we unmap using DMA_TODEVICE to avoid another memcpy from the + // bounce buffer + pci_unmap_single(bdp->pdev, rx_struct->dma_addr, + sizeof (rfd_t), PCI_DMA_TODEVICE); + + list_add(&(rx_struct->list_elem), &(bdp->rx_struct_pool)); + + /* end of dma access to rfd */ + bdp->skb_req++; /* incr number of requested skbs */ + e100_alloc_skbs(bdp); /* and get them */ + + /* set packet size, excluding checksum (2 last bytes) if it is present */ + if ((bdp->flags & DF_CSUM_OFFLOAD) + && (bdp->rev_id < D102_REV_ID)) + skb_put(skb, (int) data_sz - 2); + else + skb_put(skb, (int) data_sz); + + /* set the protocol */ + skb->protocol = eth_type_trans(skb, dev); + + /* set the checksum info */ + if (bdp->flags & DF_CSUM_OFFLOAD) { + if (bdp->rev_id >= D102_REV_ID) { + skb->ip_summed = e100_D102_check_checksum(rfd); + } else { + skb->ip_summed = e100_D101M_checksum(bdp, skb); + } + } else { + skb->ip_summed = CHECKSUM_NONE; + } + + switch (netif_rx(skb)) { + case NET_RX_BAD: + break; + + case NET_RX_DROP: + case NET_RX_CN_MOD: + case NET_RX_CN_HIGH: + if (bdp->params.b_params & PRM_RX_CONG) { + if (rx_congestion) { + *rx_congestion = 1; + } + } + /* FALL THROUGH TO STATISTICS UPDATE */ + default: + bdp->drv_stats.net_stats.rx_bytes += skb->len; + break; + } + + rfd_cnt++; + } /* end of rfd loop */ + + /* restart the RU if it has stopped */ + if ((readw(&bdp->scb->scb_status) & SCB_RUS_MASK) != SCB_RUS_READY) { + e100_start_ru(bdp); + } + + return rfd_cnt; +} + +void +e100_refresh_txthld(struct e100_private *bdp) +{ + basic_cntr_t *pstat = &(bdp->stats_counters->basic_stats); + + /* as long as tx_per_underrun is not 0, we can go about dynamically * + * adjusting the xmit threshold. we stop doing that & resort to defaults + * * once the adjustments become meaningless. the value is adjusted by * + * dumping the error counters & checking the # of xmit underrun errors * + * we've had. */ + if (bdp->tx_per_underrun) { + /* We are going to last values dumped from the dump statistics + * command */ + if (le32_to_cpu(pstat->xmt_gd_frames)) { + if (le32_to_cpu(pstat->xmt_uruns)) { + /* + * if we have had more than one underrun per "DEFAULT # + * OF XMITS ALLOWED PER UNDERRUN" good xmits, raise the + * THRESHOLD. + */ + if ((le32_to_cpu(pstat->xmt_gd_frames) / + le32_to_cpu(pstat->xmt_uruns)) < + bdp->tx_per_underrun) { + bdp->tx_thld += 3; + } + } + + /* + * if we've had less than one underrun per the DEFAULT number of + * of good xmits allowed, lower the THOLD but not less than 0 + */ + if (le32_to_cpu(pstat->xmt_gd_frames) > + bdp->tx_per_underrun) { + bdp->tx_thld--; + + if (bdp->tx_thld < 6) + bdp->tx_thld = 6; + + } + } + + /* end good xmits */ + /* + * * if our adjustments are becoming unresonable, stop adjusting & + * resort * to defaults & pray. A THOLD value > 190 means that the + * adapter will * wait for 190*8=1520 bytes in TX FIFO before it + * starts xmit. Since * MTU is 1514, it doesn't make any sense for + * further increase. */ + if (bdp->tx_thld >= 190) { + bdp->tx_per_underrun = 0; + bdp->tx_thld = 189; + } + } /* end underrun check */ +} + +/** + * e100_prepare_xmit_buff - prepare a buffer for transmission + * @bdp: atapter's private data struct + * @skb: skb to send + * + * This routine prepare a buffer for transmission. It checks + * the message length for the appropiate size. It picks up a + * free tcb from the TCB pool and sets up the corresponding + * TBD's. If the number of fragments are more than the number + * of TBD/TCB it copies all the fragments in a coalesce buffer. + * It returns a pointer to the prepared TCB. + */ +static inline tcb_t * +e100_prepare_xmit_buff(struct e100_private *bdp, struct sk_buff *skb) +{ + tcb_t *tcb, *prev_tcb; + + tcb = bdp->tcb_pool.data; + tcb += TCB_TO_USE(bdp->tcb_pool); + + tcb->tcb_hdr.cb_status = 0; + tcb->tcb_thrshld = bdp->tx_thld; + tcb->tcb_hdr.cb_cmd |= __constant_cpu_to_le16(CB_S_BIT); + + /* set the I bit on the modulo tcbs, so we will get an interrupt * to + * clean things up */ + if (!(++bdp->tx_count % TX_FRAME_CNT)) { + tcb->tcb_hdr.cb_cmd |= __constant_cpu_to_le16(CB_I_BIT); + } + + tcb->tcb_skb = skb; + +#ifdef E100_ZEROCOPY + if (skb->ip_summed == CHECKSUM_HW) { + const struct iphdr *ip = skb->nh.iph; + + if ((ip->protocol == IPPROTO_TCP) || + (ip->protocol == IPPROTO_UDP)) { + u16 *chksum; + + tcb->tcbu.ipcb.ip_activation_high = + IPCB_HARDWAREPARSING_ENABLE; + tcb->tcbu.ipcb.ip_schedule |= + IPCB_TCPUDP_CHECKSUM_ENABLE; + + if (ip->protocol == IPPROTO_TCP) { + struct tcphdr *tcp; + + tcp = (struct tcphdr *) ((u32 *) ip + ip->ihl); + chksum = &(tcp->check); + tcb->tcbu.ipcb.ip_schedule |= IPCB_TCP_PACKET; + } else { + struct udphdr *udp; + + udp = (struct udphdr *) ((u32 *) ip + ip->ihl); + chksum = &(udp->check); + } + + *chksum = csum_tcpudp_magic(ip->daddr, ip->saddr, + sizeof (struct tcphdr), + ip->protocol, 0); + } + } else { + if (bdp->flags & USE_IPCB) { + tcb->tcbu.ipcb.ip_activation_high = + IPCB_IP_ACTIVATION_DEFAULT; + tcb->tcbu.ipcb.ip_schedule &= ~IPCB_TCP_PACKET; + tcb->tcbu.ipcb.ip_schedule &= + ~IPCB_TCPUDP_CHECKSUM_ENABLE; + } + } + + if (!skb_shinfo(skb)->nr_frags) { + (tcb->tbd_ptr)->tbd_buf_addr = + cpu_to_le32(pci_map_single(bdp->pdev, skb->data, + skb->len, PCI_DMA_TODEVICE)); + (tcb->tbd_ptr)->tbd_buf_cnt = cpu_to_le16(skb->len); + tcb->tcb_tbd_num = 1; + tcb->tcb_tbd_ptr = tcb->tcb_tbd_dflt_ptr; + } else { + int i; + void *addr; + tbd_t *tbd_arr_ptr = &(tcb->tbd_ptr[1]); + skb_frag_t *frag = &skb_shinfo(skb)->frags[0]; + + (tcb->tbd_ptr)->tbd_buf_addr = + cpu_to_le32(pci_map_single(bdp->pdev, skb->data, + (skb->len - skb->data_len), + PCI_DMA_TODEVICE)); + (tcb->tbd_ptr)->tbd_buf_cnt = + cpu_to_le16(skb->len - skb->data_len); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; + i++, tbd_arr_ptr++, frag++) { + + addr = ((void *) page_address(frag->page) + + frag->page_offset); + + tbd_arr_ptr->tbd_buf_addr = + cpu_to_le32(pci_map_single(bdp->pdev, + addr, frag->size, + PCI_DMA_TODEVICE)); + tbd_arr_ptr->tbd_buf_cnt = cpu_to_le16(frag->size); + } + tcb->tcb_tbd_num = skb_shinfo(skb)->nr_frags + 1; + tcb->tcb_tbd_ptr = tcb->tcb_tbd_expand_ptr; + } +#else + (tcb->tbd_ptr)->tbd_buf_addr = + cpu_to_le32(pci_map_single(bdp->pdev, skb->data, + skb->len, PCI_DMA_TODEVICE)); + (tcb->tbd_ptr)->tbd_buf_cnt = cpu_to_le16(skb->len); +#endif + + /* clear the S-BIT on the previous tcb */ + prev_tcb = bdp->tcb_pool.data; + prev_tcb += PREV_TCB_USED(bdp->tcb_pool); + prev_tcb->tcb_hdr.cb_cmd &= __constant_cpu_to_le16((u16) ~CB_S_BIT); + + bdp->tcb_pool.tail = NEXT_TCB_TOUSE(bdp->tcb_pool.tail); + + wmb(); + + e100_start_cu(bdp, tcb); + + return tcb; +} + +/* Changed for 82558 enhancement */ +/** + * e100_start_cu - start the adapter's CU + * @bdp: atapter's private data struct + * @tcb: TCB to be transmitted + * + * This routine issues a CU Start or CU Resume command to the 82558/9. + * This routine was added because the prepare_ext_xmit_buff takes advantage + * of the 82558/9's Dynamic TBD chaining feature and has to start the CU as + * soon as the first TBD is ready. + * + * e100_start_cu must be called while holding the tx_lock ! + */ +void +e100_start_cu(struct e100_private *bdp, tcb_t *tcb) +{ + unsigned long lock_flag; + + spin_lock_irqsave(&(bdp->bd_lock), lock_flag); + switch (bdp->next_cu_cmd) { + case RESUME_NO_WAIT: + /*last cu command was a CU_RESMUE if this is a 558 or newer we dont need to + * wait for command word to clear, we reach here only if we are bachlor + */ + e100_exec_cmd(bdp, SCB_CUC_RESUME); + break; + + case RESUME_WAIT: + if ((bdp->flags & IS_ICH) && + (bdp->cur_line_speed == 10) && + (bdp->cur_dplx_mode == HALF_DUPLEX)) { + e100_wait_exec_simple(bdp, SCB_CUC_NOOP); + udelay(1); + } + if ((e100_wait_exec_simple(bdp, SCB_CUC_RESUME)) && + (bdp->flags & IS_BACHELOR) && (!(bdp->flags & IS_ICH))) { + bdp->next_cu_cmd = RESUME_NO_WAIT; + } + break; + + case START_WAIT: + // The last command was a non_tx CU command + if (!e100_wait_cus_idle(bdp)) + printk("%s cu_start: timeout waiting for cu\n", + bdp->device->name); + + if (!e100_wait_exec_cmplx(bdp, (u32) (tcb->tcb_phys), + SCB_CUC_START)) { + printk("%s cu_start: timeout waiting for scb\n", + bdp->device->name); + e100_exec_cmplx(bdp, (u32) (tcb->tcb_phys), + SCB_CUC_START); + } + + bdp->next_cu_cmd = RESUME_WAIT; + + break; + } + + /* save the last tcb */ + bdp->last_tcb = tcb; + + spin_unlock_irqrestore(&(bdp->bd_lock), lock_flag); +} + +/* ====================================================================== */ +/* hw */ +/* ====================================================================== */ + +/** + * e100_selftest - perform H/W self test + * @bdp: atapter's private data struct + * @st_timeout: address to return timeout value, if fails + * @st_result: address to return selftest result, if fails + * + * This routine will issue PORT Self-test command to test the e100. + * The self-test will fail if the adapter's master-enable bit is not + * set in the PCI Command Register, or if the adapter is not seated + * in a PCI master-enabled slot. we also disable interrupts when the + * command is completed. + * + * Returns: + * true: if adapter passes self_test + * false: otherwise + */ +unsigned char +e100_selftest(struct e100_private *bdp, u32 *st_timeout, u32 *st_result) +{ + u32 selftest_cmd; + + /* initialize the nic state before running test */ + e100_sw_reset(bdp, PORT_SOFTWARE_RESET); + /* Setup the address of the self_test area */ + selftest_cmd = bdp->selftest_phys; + + /* Setup SELF TEST Command Code in D3 - D0 */ + selftest_cmd |= PORT_SELFTEST; + + /* Initialize the self-test signature and results DWORDS */ + bdp->selftest->st_sign = 0; + bdp->selftest->st_result = 0xffffffff; + + /* Do the port command */ + writel(selftest_cmd, &bdp->scb->scb_port); + + /* Wait at least 10 milliseconds for the self-test to complete */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 100 + 1); + + /* disable interrupts since the're now enabled */ + e100_dis_intr(bdp); + + rmb(); + + /* if The First Self Test DWORD Still Zero, We've timed out. If the + * second DWORD is not zero then we have an error. */ + if ((bdp->selftest->st_sign == 0) || (bdp->selftest->st_result != 0)) { + + if (st_timeout) + *st_timeout = !(le32_to_cpu(bdp->selftest->st_sign)); + + if (st_result) + *st_result = le32_to_cpu(bdp->selftest->st_result); + + return false; + } + + return true; +} + +/** + * e100_setup_iaaddr - issue IA setup sommand + * @bdp: atapter's private data struct + * @eaddr: new ethernet address + * + * This routine will issue the IA setup command. This command + * will notify the 82557 (e100) of what its individual (node) + * address is. This command will be executed in polled mode. + * + * Returns: + * true: if the IA setup command was successfully issued and completed + * false: otherwise + */ +unsigned char +e100_setup_iaaddr(struct e100_private *bdp, u8 *eaddr) +{ + unsigned int i; + cb_header_t *ntcb_hdr; + unsigned char res; + nxmit_cb_entry_t *cmd; + + if ((cmd = e100_alloc_non_tx_cmd(bdp)) == NULL) { + res = false; + goto exit; + } + + ntcb_hdr = (cb_header_t *) cmd->non_tx_cmd; + ntcb_hdr->cb_cmd = __constant_cpu_to_le16(CB_IA_ADDRESS); + + for (i = 0; i < ETH_ALEN; i++) { + (cmd->non_tx_cmd)->ntcb.setup.ia_addr[i] = eaddr[i]; + } + + res = e100_exec_non_cu_cmd(bdp, cmd); + if (!res) + printk(KERN_WARNING "%s IA setup failed\n", bdp->device->name); + +exit: + return res; +} + +/** + * e100_start_ru - start the RU if needed + * @bdp: atapter's private data struct + * + * This routine checks the status of the 82557's receive unit(RU), + * and starts the RU if it was not already active. However, + * before restarting the RU, the driver gives the RU the buffers + * it freed up during the servicing of the ISR. If there are + * no free buffers to give to the RU, (i.e. we have reached a + * no resource condition) the RU will not be started till the + * next ISR. + */ +void +e100_start_ru(struct e100_private *bdp) +{ + struct rx_list_elem *rx_struct = NULL; + int buffer_found = 0; + struct list_head *entry_ptr; + + list_for_each(entry_ptr, &(bdp->active_rx_list)) { + rx_struct = + list_entry(entry_ptr, struct rx_list_elem, list_elem); + pci_dma_sync_single(bdp->pdev, rx_struct->dma_addr, + bdp->rfd_size, PCI_DMA_FROMDEVICE); + if (!((SKB_RFD_STATUS(rx_struct->skb, bdp) & + __constant_cpu_to_le16(RFD_STATUS_COMPLETE)))) { + buffer_found = 1; + break; + } + } + + /* No available buffers */ + if (!buffer_found) { + return; + } + + spin_lock(&bdp->bd_lock); + + if (!e100_wait_exec_cmplx(bdp, rx_struct->dma_addr, SCB_RUC_START)) { + printk("%s start_ru: wait_scb failed\n", bdp->device->name); + e100_exec_cmplx(bdp, rx_struct->dma_addr, SCB_RUC_START); + } + if (bdp->next_cu_cmd == RESUME_NO_WAIT) { + bdp->next_cu_cmd = RESUME_WAIT; + } + spin_unlock(&bdp->bd_lock); +} + +/** + * e100_cmd_complete_location + * @bdp: atapter's private data struct + * + * This routine returns a pointer to the location of the command-complete + * DWord in the dump statistical counters area, according to the statistical + * counters mode (557 - basic, 558 - extended, or 559 - TCO mode). + * See e100_config_init() for the setting of the statistical counters mode. + */ +static u32 * +e100_cmd_complete_location(struct e100_private *bdp) +{ + u32 *cmd_complete; + max_counters_t *stats = bdp->stats_counters; + + switch (bdp->stat_mode) { + case E100_EXTENDED_STATS: + cmd_complete = + (u32 *) &(((err_cntr_558_t *) (stats))->cmd_complete); + break; + + case E100_TCO_STATS: + cmd_complete = + (u32 *) &(((err_cntr_559_t *) (stats))->cmd_complete); + break; + + case E100_BASIC_STATS: + default: + cmd_complete = + (u32 *) &(((err_cntr_557_t *) (stats))->cmd_complete); + break; + } + + return cmd_complete; +} + +/** + * e100_clr_cntrs - clear statistics counters + * @bdp: atapter's private data struct + * + * This routine will clear the adapter error statistic counters. + * + * Returns: + * true: if successfully cleared stat counters + * false: otherwise + */ +static unsigned char __devinit +e100_clr_cntrs(struct e100_private *bdp) +{ + volatile u32 *pcmd_complete; + + /* clear the dump counter complete word */ + pcmd_complete = e100_cmd_complete_location(bdp); + *pcmd_complete = 0; + wmb(); + + if (!e100_wait_exec_cmplx(bdp, bdp->stat_cnt_phys, SCB_CUC_DUMP_ADDR)) + return false; + + /* wait 10 microseconds for the command to complete */ + udelay(10); + + if (!e100_wait_exec_simple(bdp, SCB_CUC_DUMP_RST_STAT)) + return false; + + if (bdp->next_cu_cmd == RESUME_NO_WAIT) { + bdp->next_cu_cmd = RESUME_WAIT; + } + + return true; +} + +static unsigned char +e100_update_stats(struct e100_private *bdp) +{ + u32 *pcmd_complete; + basic_cntr_t *pstat = &(bdp->stats_counters->basic_stats); + + // check if last dump command completed + pcmd_complete = e100_cmd_complete_location(bdp); + if (*pcmd_complete != le32_to_cpu(DUMP_RST_STAT_COMPLETED) && + *pcmd_complete != le32_to_cpu(DUMP_STAT_COMPLETED)) { + return false; + } + + /* increment the statistics */ + bdp->drv_stats.net_stats.rx_packets += + le32_to_cpu(pstat->rcv_gd_frames); + bdp->drv_stats.net_stats.tx_packets += + le32_to_cpu(pstat->xmt_gd_frames); + bdp->drv_stats.net_stats.rx_dropped += le32_to_cpu(pstat->rcv_rsrc_err); + bdp->drv_stats.net_stats.collisions += le32_to_cpu(pstat->xmt_ttl_coll); + bdp->drv_stats.net_stats.rx_length_errors += + le32_to_cpu(pstat->rcv_shrt_frames); + bdp->drv_stats.net_stats.rx_over_errors += + le32_to_cpu(pstat->rcv_rsrc_err); + bdp->drv_stats.net_stats.rx_crc_errors += + le32_to_cpu(pstat->rcv_crc_errs); + bdp->drv_stats.net_stats.rx_frame_errors += + le32_to_cpu(pstat->rcv_algn_errs); + bdp->drv_stats.net_stats.rx_fifo_errors += + le32_to_cpu(pstat->rcv_oruns); + bdp->drv_stats.net_stats.tx_aborted_errors += + le32_to_cpu(pstat->xmt_max_coll); + bdp->drv_stats.net_stats.tx_carrier_errors += + le32_to_cpu(pstat->xmt_lost_crs); + bdp->drv_stats.net_stats.tx_fifo_errors += + le32_to_cpu(pstat->xmt_uruns); + + bdp->drv_stats.tx_late_col += le32_to_cpu(pstat->xmt_late_coll); + bdp->drv_stats.tx_ok_defrd += le32_to_cpu(pstat->xmt_deferred); + bdp->drv_stats.tx_one_retry += le32_to_cpu(pstat->xmt_sngl_coll); + bdp->drv_stats.tx_mt_one_retry += le32_to_cpu(pstat->xmt_mlt_coll); + bdp->drv_stats.rcv_cdt_frames += le32_to_cpu(pstat->rcv_err_coll); + + if (bdp->stat_mode != E100_BASIC_STATS) { + ext_cntr_t *pex_stat = &bdp->stats_counters->extended_stats; + + bdp->drv_stats.xmt_fc_pkts += + le32_to_cpu(pex_stat->xmt_fc_frames); + bdp->drv_stats.rcv_fc_pkts += + le32_to_cpu(pex_stat->rcv_fc_frames); + bdp->drv_stats.rcv_fc_unsupported += + le32_to_cpu(pex_stat->rcv_fc_unsupported); + } + + if (bdp->stat_mode == E100_TCO_STATS) { + tco_cntr_t *ptco_stat = &bdp->stats_counters->tco_stats; + + bdp->drv_stats.xmt_tco_pkts += + le16_to_cpu(ptco_stat->xmt_tco_frames); + bdp->drv_stats.rcv_tco_pkts += + le16_to_cpu(ptco_stat->rcv_tco_frames); + } + + *pcmd_complete = 0; + return true; +} + +/** + * e100_dump_stat_cntrs + * @bdp: atapter's private data struct + * + * This routine will dump the board statistical counters without waiting + * for stat_dump to complete. Any access to this stats should verify the completion + * of the command + */ +void +e100_dump_stats_cntrs(struct e100_private *bdp) +{ + unsigned long lock_flag_bd; + + spin_lock_irqsave(&(bdp->bd_lock), lock_flag_bd); + + /* dump h/w stats counters */ + if (e100_wait_exec_simple(bdp, SCB_CUC_DUMP_RST_STAT)) { + if (bdp->next_cu_cmd == RESUME_NO_WAIT) { + bdp->next_cu_cmd = RESUME_WAIT; + } + } + + spin_unlock_irqrestore(&(bdp->bd_lock), lock_flag_bd); +} + +/** + * e100_exec_non_cu_cmd + * @bdp: atapter's private data struct + * @command: the non-cu command to execute + * + * This routine will submit a command block to be executed, + */ +unsigned char +e100_exec_non_cu_cmd(struct e100_private *bdp, nxmit_cb_entry_t *command) +{ + cb_header_t *ntcb_hdr; + unsigned long lock_flag; + unsigned long expiration_time; + unsigned char rc = false; + + ntcb_hdr = (cb_header_t *) command->non_tx_cmd; /* get hdr of non tcb cmd */ + + /* Set the Command Block to be the last command block */ + ntcb_hdr->cb_cmd |= __constant_cpu_to_le16(CB_EL_BIT); + ntcb_hdr->cb_status = 0; + ntcb_hdr->cb_lnk_ptr = 0; + + wmb(); + + if (in_interrupt()) + return e100_delayed_exec_non_cu_cmd(bdp, command); + + spin_lock_bh(&(bdp->bd_non_tx_lock)); + + if (bdp->non_tx_command_state != E100_NON_TX_IDLE) { + goto delayed_exec; + } + + if (bdp->last_tcb) { + rmb(); + if ((bdp->last_tcb->tcb_hdr.cb_status & + __constant_cpu_to_le16(CB_STATUS_COMPLETE)) == 0) + goto delayed_exec; + } + + if ((readw(&bdp->scb->scb_status) & SCB_CUS_MASK) == SCB_CUS_ACTIVE) { + goto delayed_exec; + } + + spin_lock_irqsave(&bdp->bd_lock, lock_flag); + + if (!e100_wait_exec_cmplx(bdp, command->dma_addr, SCB_CUC_START)) { + spin_unlock_irqrestore(&(bdp->bd_lock), lock_flag); + goto exit; + } + + bdp->next_cu_cmd = START_WAIT; + spin_unlock_irqrestore(&(bdp->bd_lock), lock_flag); + + /* now wait for completion of non-cu CB up to 20 msec*/ + expiration_time = jiffies + HZ / 50 + 1; + while (time_before(jiffies, expiration_time)) { + rmb(); + if ((ntcb_hdr->cb_status & + __constant_cpu_to_le16(CB_STATUS_COMPLETE))) { + rc = true; + goto exit; + } + spin_unlock_bh(&(bdp->bd_non_tx_lock)); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + spin_lock_bh(&(bdp->bd_non_tx_lock)); + } + + /* didn't get a C bit assume command failed */ +exit: + e100_free_non_tx_cmd(bdp, command); + + if (netif_running(bdp->device)) + netif_wake_queue(bdp->device); + + spin_unlock_bh(&(bdp->bd_non_tx_lock)); + return rc; + +delayed_exec: + spin_unlock_bh(&(bdp->bd_non_tx_lock)); + return e100_delayed_exec_non_cu_cmd(bdp, command); +} + +/** + * e100_sw_reset + * @bdp: atapter's private data struct + * @reset_cmd: s/w reset or selective reset + * + * This routine will issue a software reset to the adapter. It + * will also disable interrupts, as the are enabled after reset. + */ +void +e100_sw_reset(struct e100_private *bdp, u32 reset_cmd) +{ + /* Do a selective reset first to avoid a potential PCI hang */ + writel(PORT_SELECTIVE_RESET, &bdp->scb->scb_port); + + /* wait for the reset to take effect */ + udelay(20); + if (reset_cmd == PORT_SOFTWARE_RESET) { + writel(PORT_SOFTWARE_RESET, &bdp->scb->scb_port); + + /* wait 20 micro seconds for the reset to take effect */ + udelay(20); + } + + /* Mask off our interrupt line -- its unmasked after reset */ + e100_dis_intr(bdp); +} + +/** + * e100_load_microcode - Download microsocde to controller. + * @bdp: atapter's private data struct + * + * This routine downloads microcode on to the controller. This + * microcode is available for the 82558/9, 82550. Currently the + * microcode handles interrupt bundling and TCO workaround. + * + * Returns: + * true: if successfull + * false: otherwise + */ +static unsigned char +e100_load_microcode(struct e100_private *bdp) +{ + static struct { + u8 rev_id; + u32 ucode[UCODE_MAX_DWORDS + 1]; + int timer_dword; + int bundle_dword; + int min_size_dword; + } ucode_opts[] = { + { D101A4_REV_ID, + D101_A_RCVBUNDLE_UCODE, + D101_CPUSAVER_TIMER_DWORD, + D101_CPUSAVER_BUNDLE_DWORD, + D101_CPUSAVER_MIN_SIZE_DWORD }, + { D101B0_REV_ID, + D101_B0_RCVBUNDLE_UCODE, + D101_CPUSAVER_TIMER_DWORD, + D101_CPUSAVER_BUNDLE_DWORD, + D101_CPUSAVER_MIN_SIZE_DWORD }, + { D101MA_REV_ID, + D101M_B_RCVBUNDLE_UCODE, + D101M_CPUSAVER_TIMER_DWORD, + D101M_CPUSAVER_BUNDLE_DWORD, + D101M_CPUSAVER_MIN_SIZE_DWORD }, + { D101S_REV_ID, + D101S_RCVBUNDLE_UCODE, + D101S_CPUSAVER_TIMER_DWORD, + D101S_CPUSAVER_BUNDLE_DWORD, + D101S_CPUSAVER_MIN_SIZE_DWORD }, + { D102_REV_ID, + D102_B_RCVBUNDLE_UCODE, + D102_B_CPUSAVER_TIMER_DWORD, + D102_B_CPUSAVER_BUNDLE_DWORD, + D102_B_CPUSAVER_MIN_SIZE_DWORD }, + { D102C_REV_ID, + D102_C_RCVBUNDLE_UCODE, + D102_C_CPUSAVER_TIMER_DWORD, + D102_C_CPUSAVER_BUNDLE_DWORD, + D102_C_CPUSAVER_MIN_SIZE_DWORD }, + { D102E_REV_ID, + D102_E_RCVBUNDLE_UCODE, + D102_E_CPUSAVER_TIMER_DWORD, + D102_E_CPUSAVER_BUNDLE_DWORD, + D102_E_CPUSAVER_MIN_SIZE_DWORD }, + { 0, {0}, 0, 0, 0} + }, *opts; + + opts = ucode_opts; + + /* User turned ucode loading off */ + if (!(bdp->params.b_params & PRM_UCODE)) + return false; + + /* These controllers do not need ucode */ + if (bdp->flags & IS_ICH) + return false; + + /* Search for ucode match against h/w rev_id */ + while (opts->rev_id) { + if (bdp->rev_id == opts->rev_id) { + int i; + u32 *ucode_dword; + load_ucode_cb_t *ucode_cmd_ptr; + nxmit_cb_entry_t *cmd = e100_alloc_non_tx_cmd(bdp); + + if (cmd != NULL) { + ucode_cmd_ptr = + (load_ucode_cb_t *) cmd->non_tx_cmd; + ucode_dword = ucode_cmd_ptr->ucode_dword; + } else { + return false; + } + + memcpy(ucode_dword, opts->ucode, sizeof (opts->ucode)); + + /* Insert user-tunable settings */ + ucode_dword[opts->timer_dword] &= 0xFFFF0000; + ucode_dword[opts->timer_dword] |= + (u16) bdp->params.IntDelay; + ucode_dword[opts->bundle_dword] &= 0xFFFF0000; + ucode_dword[opts->bundle_dword] |= + (u16) bdp->params.BundleMax; + ucode_dword[opts->min_size_dword] &= 0xFFFF0000; + ucode_dword[opts->min_size_dword] |= + (bdp->params.b_params & PRM_BUNDLE_SMALL) ? + 0xFFFF : 0xFF80; + + for (i = 0; i < UCODE_MAX_DWORDS; i++) + cpu_to_le32s(&(ucode_dword[i])); + + ucode_cmd_ptr->load_ucode_cbhdr.cb_cmd = + __constant_cpu_to_le16(CB_LOAD_MICROCODE); + + return e100_exec_non_cu_cmd(bdp, cmd); + } + opts++; + } + + return false; +} + +/***************************************************************************/ +/***************************************************************************/ +/* EEPROM Functions */ +/***************************************************************************/ + +/* Read PWA (printed wired assembly) number */ +void __devinit +e100_rd_pwa_no(struct e100_private *bdp) +{ + bdp->pwa_no = e100_eeprom_read(bdp, EEPROM_PWA_NO); + bdp->pwa_no <<= 16; + bdp->pwa_no |= e100_eeprom_read(bdp, EEPROM_PWA_NO + 1); +} + +/* Read the permanent ethernet address from the eprom. */ +void __devinit +e100_rd_eaddr(struct e100_private *bdp) +{ + int i; + u16 eeprom_word; + + for (i = 0; i < 6; i += 2) { + eeprom_word = + e100_eeprom_read(bdp, + EEPROM_NODE_ADDRESS_BYTE_0 + (i / 2)); + + bdp->device->dev_addr[i] = + bdp->perm_node_address[i] = (u8) eeprom_word; + bdp->device->dev_addr[i + 1] = + bdp->perm_node_address[i + 1] = (u8) (eeprom_word >> 8); + } +} + +/* Check the D102 RFD flags to see if the checksum passed */ +static unsigned char +e100_D102_check_checksum(rfd_t *rfd) +{ + if (((le16_to_cpu(rfd->rfd_header.cb_status)) & RFD_PARSE_BIT) + && (((rfd->rcvparserstatus & CHECKSUM_PROTOCOL_MASK) == + RFD_TCP_PACKET) + || ((rfd->rcvparserstatus & CHECKSUM_PROTOCOL_MASK) == + RFD_UDP_PACKET)) + && (rfd->checksumstatus & TCPUDP_CHECKSUM_BIT_VALID) + && (rfd->checksumstatus & TCPUDP_CHECKSUM_VALID)) { + return CHECKSUM_UNNECESSARY; + } + return CHECKSUM_NONE; +} + +/** + * e100_D101M_checksum + * @bdp: atapter's private data struct + * @skb: skb received + * + * Sets the skb->csum value from D101 csum found at the end of the Rx frame. The + * D101M sums all words in frame excluding the ethernet II header (14 bytes) so + * in case the packet is ethernet II and the protocol is IP, all is need is to + * assign this value to skb->csum. + */ +static unsigned char +e100_D101M_checksum(struct e100_private *bdp, struct sk_buff *skb) +{ + unsigned short proto = (skb->protocol); + + if (proto == __constant_htons(ETH_P_IP)) { + + skb->csum = get_unaligned((u16 *) (skb->tail)); + return CHECKSUM_HW; + } + return CHECKSUM_NONE; +} + +/***************************************************************************/ +/***************************************************************************/ +/***************************************************************************/ +/***************************************************************************/ +/* Auxilary Functions */ +/***************************************************************************/ + +/* Print the board's configuration */ +void __devinit +e100_print_brd_conf(struct e100_private *bdp) +{ + if (netif_carrier_ok(bdp->device)) { + printk(KERN_NOTICE + " Mem:0x%08lx IRQ:%d Speed:%d Mbps Dx:%s\n", + (unsigned long) bdp->device->mem_start, + bdp->device->irq, bdp->cur_line_speed, + (bdp->cur_dplx_mode == FULL_DUPLEX) ? "Full" : "Half"); + } else { + printk(KERN_NOTICE + " Mem:0x%08lx IRQ:%d Speed:%d Mbps Dx:%s\n", + (unsigned long) bdp->device->mem_start, + bdp->device->irq, 0, "N/A"); + + /* Auto negotiation failed so we should display an error */ + printk(KERN_NOTICE " Failed to detect cable link\n"); + printk(KERN_NOTICE " Speed and duplex will be determined " + "at time of connection\n"); + } + + /* Print the string if checksum Offloading was enabled */ + if (bdp->flags & DF_CSUM_OFFLOAD) + printk(KERN_NOTICE " Hardware receive checksums enabled\n"); + else + printk(KERN_NOTICE " Hardware receive checksums disabled\n"); + + if ((bdp->flags & DF_UCODE_LOADED)) + printk(KERN_NOTICE " cpu cycle saver enabled\n"); +} + +/** + * e100_get_brand_msg + * @bdp: atapter's private data struct + * + * This routine checks if there is specified branding message for a given board + * type and returns a pointer to the string containing the branding message. + */ +char * +e100_get_brand_msg(struct e100_private *bdp) +{ + int i; + + for (i = 0; e100_vendor_info_array[i].idstr != NULL; i++) { + if (e100_vendor_info_array[i].device_type == bdp->device_type) { + return e100_vendor_info_array[i].idstr; + } + } + + return e100_vendor_info_array[E100_ALL_BOARDS].idstr; +} + +/** + * e100_pci_setup - setup the adapter's PCI information + * @pcid: adapter's pci_dev struct + * @bdp: atapter's private data struct + * + * This routine sets up all PCI information for the adapter. It enables the bus + * master bit (some BIOS don't do this), requests memory ans I/O regions, and + * calls ioremap() on the adapter's memory region. + * + * Returns: + * true: if successfull + * false: otherwise + */ +static unsigned char __devinit +e100_pci_setup(struct pci_dev *pcid, struct e100_private *bdp) +{ + struct net_device *dev = bdp->device; + int rc = 0; + + if ((rc = pci_enable_device(pcid)) != 0) { + goto err; + } + + /* dev and ven ID have already been checked so it is our device */ + pci_read_config_byte(pcid, PCI_REVISION_ID, (u8 *) &(bdp->rev_id)); + + /* address #0 is a memory region */ + dev->mem_start = pci_resource_start(pcid, 0); + dev->mem_end = dev->mem_start + sizeof (scb_t); + + /* address #1 is a IO region */ + dev->base_addr = pci_resource_start(pcid, 1); + + if ((rc = pci_request_regions(pcid, e100_short_driver_name)) != 0) { + goto err_disable; + } + + pci_enable_wake(pcid, 0, 0); + + /* if Bus Mastering is off, turn it on! */ + pci_set_master(pcid); + + /* address #0 is a memory mapping */ + bdp->scb = (scb_t *) ioremap_nocache(dev->mem_start, sizeof (scb_t)); + + if (!bdp->scb) { + printk(KERN_ERR "%s - %s: Failed to map PCI address 0x%lX\n", + e100_short_driver_name, dev->name, + pci_resource_start(pcid, 0)); + rc = -ENOMEM; + goto err_region; + } + + return 0; + +err_region: + pci_release_regions(pcid); +err_disable: + pci_disable_device(pcid); +err: + return rc; +} + +void +e100_isolate_driver(struct e100_private *bdp) +{ + write_lock_irq(&(bdp->isolate_lock)); + bdp->driver_isolated = true; + write_unlock_irq(&(bdp->isolate_lock)); + + del_timer_sync(&bdp->watchdog_timer); + + netif_stop_queue(bdp->device); + + bdp->last_tcb = NULL; + + e100_sw_reset(bdp, PORT_SELECTIVE_RESET); +} + +#ifdef E100_ETHTOOL_IOCTL +static int +e100_do_ethtool_ioctl(struct net_device *dev, struct ifreq *ifr) +{ + struct ethtool_cmd ecmd; + int rc = -EOPNOTSUPP; + + if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd.cmd))) + return -EFAULT; + + switch (ecmd.cmd) { + case ETHTOOL_GSET: + rc = e100_ethtool_get_settings(dev, ifr); + break; + case ETHTOOL_SSET: + rc = e100_ethtool_set_settings(dev, ifr); + break; +#ifdef ETHTOOL_GDRVINFO + case ETHTOOL_GDRVINFO: + rc = e100_ethtool_get_drvinfo(dev, ifr); + break; +#endif +#ifdef ETHTOOL_NWAY_RST + case ETHTOOL_NWAY_RST: + rc = e100_ethtool_nway_rst(dev, ifr); + break; +#endif +#ifdef ETHTOOL_GLINK + case ETHTOOL_GLINK: + rc = e100_ethtool_glink(dev, ifr); + break; +#endif +#ifdef ETHTOOL_GEEPROM + case ETHTOOL_GEEPROM: + case ETHTOOL_SEEPROM: + rc = e100_ethtool_eeprom(dev, ifr); + break; +#endif +#ifdef ETHTOOL_GWOL + case ETHTOOL_GWOL: + case ETHTOOL_SWOL: + rc = e100_ethtool_wol(dev, ifr); + break; +#endif + default: + break; + } //switch + return rc; +} + +static int +e100_ethtool_get_settings(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + struct ethtool_cmd ecmd; + u16 advert = 0; + + memset((void *) &ecmd, 0, sizeof (ecmd)); + + bdp = dev->priv; + + ecmd.supported = bdp->speed_duplex_caps; + + ecmd.port = + (bdp->speed_duplex_caps & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE; + ecmd.transceiver = XCVR_INTERNAL; + ecmd.phy_address = bdp->phy_addr; + + ecmd.speed = bdp->cur_line_speed; + ecmd.duplex = + (bdp->cur_dplx_mode == HALF_DUPLEX) ? DUPLEX_HALF : DUPLEX_FULL; + + ecmd.advertising = ADVERTISED_TP; + + if (bdp->params.e100_speed_duplex == E100_AUTONEG) { + ecmd.autoneg = AUTONEG_ENABLE; + ecmd.advertising |= ADVERTISED_Autoneg; + } else { + ecmd.autoneg = AUTONEG_DISABLE; + } + + if (bdp->speed_duplex_caps & SUPPORTED_MII) { + spin_lock_bh(&(bdp->mdi_access_lock)); + e100_mdi_read(bdp, MII_ADVERTISE, bdp->phy_addr, &advert); + spin_unlock_bh(&(bdp->mdi_access_lock)); + + if (advert & ADVERTISE_10HALF) + ecmd.advertising |= ADVERTISED_10baseT_Half; + if (advert & ADVERTISE_10FULL) + ecmd.advertising |= ADVERTISED_10baseT_Full; + if (advert & ADVERTISE_100HALF) + ecmd.advertising |= ADVERTISED_100baseT_Half; + if (advert & ADVERTISE_100FULL) + ecmd.advertising |= ADVERTISED_100baseT_Full; + } else { + ecmd.autoneg = AUTONEG_DISABLE; + ecmd.advertising &= ~ADVERTISED_Autoneg; + } + + if (copy_to_user(ifr->ifr_data, &ecmd, sizeof (ecmd))) + return -EFAULT; + + return 0; +} + +static int +e100_ethtool_set_settings(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + int current_duplex; + int e100_new_speed_duplex; + int ethtool_new_speed_duplex; + int speed_duplex_change_required; + struct ethtool_cmd ecmd; + + if (!capable(CAP_NET_ADMIN)) { + return -EPERM; + } + + bdp = dev->priv; + if (netif_running(dev)) { + return -EBUSY; + } + if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd))) { + return -EFAULT; + } + current_duplex = + (bdp->cur_dplx_mode == HALF_DUPLEX) ? DUPLEX_HALF : DUPLEX_FULL; + speed_duplex_change_required = (ecmd.speed != bdp->cur_line_speed) + || (ecmd.duplex != current_duplex); + + if ((ecmd.autoneg == AUTONEG_ENABLE) && speed_duplex_change_required) { + return -EINVAL; + } + + if ((ecmd.autoneg == AUTONEG_ENABLE) + && (bdp->speed_duplex_caps & SUPPORTED_Autoneg)) { + bdp->params.e100_speed_duplex = E100_AUTONEG; + e100_set_speed_duplex(bdp); + } else { + if (speed_duplex_change_required) { + if (ecmd.speed == SPEED_10) { + if (ecmd.duplex == DUPLEX_HALF) { + e100_new_speed_duplex = + E100_SPEED_10_HALF; + ethtool_new_speed_duplex = + SUPPORTED_10baseT_Half; + + } else { + e100_new_speed_duplex = + E100_SPEED_10_FULL; + ethtool_new_speed_duplex = + SUPPORTED_10baseT_Full; + } + + } else { + if (ecmd.duplex == DUPLEX_HALF) { + e100_new_speed_duplex = + E100_SPEED_100_HALF; + ethtool_new_speed_duplex = + SUPPORTED_100baseT_Half; + + } else { + e100_new_speed_duplex = + E100_SPEED_100_FULL; + ethtool_new_speed_duplex = + SUPPORTED_100baseT_Full; + } + } + + if (bdp->speed_duplex_caps & ethtool_new_speed_duplex) { + bdp->params.e100_speed_duplex = + e100_new_speed_duplex; + e100_set_speed_duplex(bdp); + } else { + return -EOPNOTSUPP; + } + } + } + + return 0; +} + +#ifdef ETHTOOL_GLINK +static int +e100_ethtool_glink(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + struct ethtool_value info; + + memset((void *) &info, 0, sizeof (info)); + + bdp = dev->priv; + info.cmd = ETHTOOL_GLINK; + + spin_lock_bh(&(bdp->mdi_access_lock)); + info.data = e100_get_link_state(bdp); + spin_unlock_bh(&(bdp->mdi_access_lock)); + + if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) + return -EFAULT; + + return 0; +} +#endif + +#ifdef ETHTOOL_NWAY_RST +static int +e100_ethtool_nway_rst(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + bdp = dev->priv; + + if ((bdp->speed_duplex_caps & SUPPORTED_Autoneg) && + (bdp->params.e100_speed_duplex == E100_AUTONEG)) { + e100_set_speed_duplex(bdp); + } else { + return -EFAULT; + } + return 0; +} +#endif + +#ifdef ETHTOOL_GDRVINFO +static int +e100_ethtool_get_drvinfo(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + struct ethtool_drvinfo info; + + memset((void *) &info, 0, sizeof (info)); + + bdp = dev->priv; + + strncpy(info.driver, e100_short_driver_name, sizeof (info.driver) - 1); + strncpy(info.version, e100_version, sizeof (info.version) - 1); + strncpy(info.fw_version, e100_get_brand_msg(bdp), + sizeof (info.fw_version) - 1); + strncpy(info.bus_info, bdp->pdev->slot_name, + sizeof (info.bus_info) - 1); +#ifdef ETHTOOL_GEEPROM + info.eedump_len = (bdp->eeprom_size << 1); +#endif + + if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) + return -EFAULT; + + return 0; +} +#endif //ETHTOOL_GDRVINFO + +#ifdef ETHTOOL_GEEPROM +static int +e100_ethtool_eeprom(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + struct ethtool_eeprom ecmd; + u16 eeprom_data[256]; + u16 *usr_eeprom_ptr; + u16 word_length, word_offset; + int i; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + bdp = dev->priv; + + if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd))) + return -EFAULT; + + usr_eeprom_ptr = + (u16 *) (ifr->ifr_data + offsetof(struct ethtool_eeprom, data)); + + word_offset = (ecmd.offset >> 1); + if (word_offset >= bdp->eeprom_size) + return -EFAULT; + + word_length = + min_t(u32, (ecmd.len >> 1), (bdp->eeprom_size - word_offset)); + + if (ecmd.cmd == ETHTOOL_GEEPROM) { + for (i = word_offset; i < (word_length + word_offset); i++) + eeprom_data[i] = e100_eeprom_read(bdp, i); + + ecmd.len = (word_length << 1); + ecmd.magic = E100_EEPROM_MAGIC; + + if (copy_to_user(ifr->ifr_data, &ecmd, sizeof (ecmd))) + return -EFAULT; + + if (copy_to_user(usr_eeprom_ptr, &(eeprom_data[word_offset]), + (ecmd.len << 1))) + return -EFAULT; + } else { + if (ecmd.magic != E100_EEPROM_MAGIC) + return -EFAULT; + + if (copy_from_user(&(eeprom_data[word_offset]), usr_eeprom_ptr, + (ecmd.len << 1))) + return -EFAULT; + + e100_eeprom_write_block(bdp, word_offset, + &(eeprom_data[word_offset]), + word_length); + + ecmd.len = (word_length << 1); + + if (copy_to_user(ifr->ifr_data, &ecmd, sizeof (ecmd))) + return -EFAULT; + } + return 0; +} +#endif + +static inline int __devinit +e100_10BaseT_adapter(struct e100_private *bdp) +{ + return ((bdp->pdev->device == 0x1229) && + (bdp->pdev->subsystem_vendor == 0x8086) && + (bdp->pdev->subsystem_device == 0x0003)); +} + +static void __devinit +e100_get_speed_duplex_caps(struct e100_private *bdp) +{ + u16 status; + + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &status); + + bdp->speed_duplex_caps = 0; + + bdp->speed_duplex_caps |= + (status & BMSR_ANEGCAPABLE) ? SUPPORTED_Autoneg : 0; + + bdp->speed_duplex_caps |= + (status & BMSR_10HALF) ? SUPPORTED_10baseT_Half : 0; + + bdp->speed_duplex_caps |= + (status & BMSR_10FULL) ? SUPPORTED_10baseT_Full : 0; + + bdp->speed_duplex_caps |= + (status & BMSR_100HALF) ? SUPPORTED_100baseT_Half : 0; + + bdp->speed_duplex_caps |= + (status & BMSR_100FULL) ? SUPPORTED_100baseT_Full : 0; + + if (IS_NC3133(bdp)) + bdp->speed_duplex_caps = + (SUPPORTED_FIBRE | SUPPORTED_100baseT_Full); + else + bdp->speed_duplex_caps |= SUPPORTED_TP; + + if ((status == 0xFFFF) && e100_10BaseT_adapter(bdp)) { + bdp->speed_duplex_caps = + (SUPPORTED_10baseT_Half | SUPPORTED_TP); + } else { + bdp->speed_duplex_caps |= SUPPORTED_MII; + } + +} + +static void +e100_set_speed_duplex(struct e100_private *bdp) +{ + e100_phy_set_speed_duplex(bdp, true); + e100_config_fc(bdp); /* re-config flow-control if necessary */ + e100_config(bdp); +} + +#ifdef ETHTOOL_GWOL +static unsigned char +e100_setup_filter(struct e100_private *bdp) +{ + cb_header_t *ntcb_hdr; + unsigned char res = false; + nxmit_cb_entry_t *cmd; + + if ((cmd = e100_alloc_non_tx_cmd(bdp)) == NULL) { + goto exit; + } + + ntcb_hdr = (cb_header_t *) cmd->non_tx_cmd; + ntcb_hdr->cb_cmd = __constant_cpu_to_le16(CB_LOAD_FILTER); + + /* Set EL and FIX bit */ + (cmd->non_tx_cmd)->ntcb.filter.filter_data[0] = + __constant_cpu_to_le32(CB_FILTER_EL | CB_FILTER_FIX); + + if (bdp->wolopts & WAKE_UCAST) { + (cmd->non_tx_cmd)->ntcb.filter.filter_data[0] |= + __constant_cpu_to_le32(CB_FILTER_IA_MATCH); + } + + if (bdp->wolopts & WAKE_ARP) { + /* Setup ARP bit and lower IP parts */ + /* bdp->ip_lbytes contains 2 lower bytes of IP address in network byte order */ + (cmd->non_tx_cmd)->ntcb.filter.filter_data[0] |= + cpu_to_le32(CB_FILTER_ARP | bdp->ip_lbytes); + } + + res = e100_exec_non_cu_cmd(bdp, cmd); + if (!res) + printk(KERN_WARNING "%s Filter setup failed\n", + bdp->device->name); + +exit: + return res; + +} + +static void +e100_do_wol(struct pci_dev *pcid, struct e100_private *bdp) +{ + int enable = 0; + u32 state = 0; + + if (bdp->wolopts) { + e100_config_wol(bdp); + + if (!e100_config(bdp)) { + printk("e100_config WOL options failed\n"); + goto exit; + } + + if (bdp->wolopts & (WAKE_UCAST | WAKE_ARP)) { + if (!e100_setup_filter(bdp)) { + printk("e100_config WOL options failed\n"); + goto exit; + } + state = 1; + pci_set_power_state(pcid, state); + } + enable = 1; + } +exit: + pci_enable_wake(pcid, state, enable); +} + +static u16 +e100_get_ip_lbytes(struct net_device *dev) +{ + struct in_ifaddr *ifa; + struct in_device *in_dev; + u32 res = 0; + + in_dev = (struct in_device *) dev->ip_ptr; + /* Check if any in_device bound to interface */ + if (in_dev) { + /* Check if any IP address is bound to interface */ + if ((ifa = in_dev->ifa_list) != NULL) { + res = __constant_ntohl(ifa->ifa_address); + res = __constant_htons(res & 0x0000ffff); + } + } + return res; +} + +static int +e100_ethtool_wol(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + struct ethtool_wolinfo wolinfo; + int res = 0; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + bdp = dev->priv; + + if (copy_from_user(&wolinfo, ifr->ifr_data, sizeof (wolinfo))) { + return -EFAULT; + } + + switch (wolinfo.cmd) { + case ETHTOOL_GWOL: + wolinfo.supported = bdp->wolsupported; + wolinfo.wolopts = bdp->wolopts; + if (copy_to_user(ifr->ifr_data, &wolinfo, sizeof (wolinfo))) + res = -EFAULT; + break; + + case ETHTOOL_SWOL: + /* If ALL requests are supported or request is DISABLE wol */ + if (((wolinfo.wolopts & bdp->wolsupported) == wolinfo.wolopts) + || (wolinfo.wolopts == 0)) { + bdp->wolopts = wolinfo.wolopts; + } else { + res = -EOPNOTSUPP; + } + break; + default: + break; + } + return res; +} + +#endif +#endif /*E100_ETHTOOL_IOCTL */ + +#ifdef E100_MII_IOCTL +static int +e100_mii_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct e100_private *bdp; + struct mii_ioctl_data *data_ptr = + (struct mii_ioctl_data *) &(ifr->ifr_data); + + bdp = dev->priv; + + switch (cmd) { + case SIOCGMIIPHY: + data_ptr->phy_id = bdp->phy_addr & 0x1f; + break; + + case SIOCGMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + spin_lock_bh(&(bdp->mdi_access_lock)); + e100_mdi_read(bdp, data_ptr->reg_num & 0x1f, bdp->phy_addr, + &(data_ptr->val_out)); + spin_unlock_bh(&(bdp->mdi_access_lock)); + break; + + case SIOCSMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (netif_running(dev)) { + return -EBUSY; + } + spin_lock_bh(&(bdp->mdi_access_lock)); + e100_mdi_write(bdp, data_ptr->reg_num & 0x1f, bdp->phy_addr, + data_ptr->val_in); + spin_unlock_bh(&(bdp->mdi_access_lock)); + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} +#endif //E100_MII_IOCTL + +nxmit_cb_entry_t * +e100_alloc_non_tx_cmd(struct e100_private *bdp) +{ + nxmit_cb_entry_t *non_tx_cmd_elem; + + if (!(non_tx_cmd_elem = (nxmit_cb_entry_t *) + kmalloc(sizeof (nxmit_cb_entry_t), GFP_ATOMIC))) { + return NULL; + } + non_tx_cmd_elem->non_tx_cmd = + pci_alloc_consistent(bdp->pdev, sizeof (nxmit_cb_t), + &(non_tx_cmd_elem->dma_addr)); + if (non_tx_cmd_elem->non_tx_cmd == NULL) { + kfree(non_tx_cmd_elem); + return NULL; + } + return non_tx_cmd_elem; +} + +void +e100_free_non_tx_cmd(struct e100_private *bdp, + nxmit_cb_entry_t *non_tx_cmd_elem) +{ + pci_free_consistent(bdp->pdev, sizeof (nxmit_cb_t), + non_tx_cmd_elem->non_tx_cmd, + non_tx_cmd_elem->dma_addr); + kfree(non_tx_cmd_elem); +} + +static void +e100_free_nontx_list(struct e100_private *bdp) +{ + nxmit_cb_entry_t *command; + int i; + + while (!list_empty(&bdp->non_tx_cmd_list)) { + command = list_entry(bdp->non_tx_cmd_list.next, + nxmit_cb_entry_t, list_elem); + list_del(&(command->list_elem)); + e100_free_non_tx_cmd(bdp, command); + } + + for (i = 0; i < CB_MAX_NONTX_CMD; i++) { + bdp->same_cmd_entry[i] = NULL; + } +} + +static unsigned char +e100_delayed_exec_non_cu_cmd(struct e100_private *bdp, + nxmit_cb_entry_t *command) +{ + nxmit_cb_entry_t *same_command; + cb_header_t *ntcb_hdr; + u16 cmd; + + ntcb_hdr = (cb_header_t *) command->non_tx_cmd; + + cmd = CB_CMD_MASK & le16_to_cpu(ntcb_hdr->cb_cmd); + + spin_lock_bh(&(bdp->bd_non_tx_lock)); + + same_command = bdp->same_cmd_entry[cmd]; + + if (same_command != NULL) { + memcpy((void *) (same_command->non_tx_cmd), + (void *) (command->non_tx_cmd), sizeof (nxmit_cb_t)); + e100_free_non_tx_cmd(bdp, command); + } else { + list_add_tail(&(command->list_elem), &(bdp->non_tx_cmd_list)); + bdp->same_cmd_entry[cmd] = command; + } + + if (bdp->non_tx_command_state == E100_NON_TX_IDLE) { + bdp->non_tx_command_state = E100_WAIT_TX_FINISH; + mod_timer(&(bdp->nontx_timer_id), jiffies + 1); + } + + spin_unlock_bh(&(bdp->bd_non_tx_lock)); + return true; +} + +static void +e100_non_tx_background(unsigned long ptr) +{ + struct e100_private *bdp = (struct e100_private *) ptr; + nxmit_cb_entry_t *active_command; + int restart = true; + + spin_lock_bh(&(bdp->bd_non_tx_lock)); + + switch (bdp->non_tx_command_state) { + case E100_WAIT_TX_FINISH: + if (bdp->last_tcb != NULL) { + rmb(); + if ((bdp->last_tcb->tcb_hdr.cb_status & + __constant_cpu_to_le16(CB_STATUS_COMPLETE)) == 0) + goto exit; + } + if ((readw(&bdp->scb->scb_status) & SCB_CUS_MASK) == + SCB_CUS_ACTIVE) { + goto exit; + } + break; + + case E100_WAIT_NON_TX_FINISH: + active_command = list_entry(bdp->non_tx_cmd_list.next, + nxmit_cb_entry_t, list_elem); + rmb(); + + if (((((cb_header_t *) (active_command->non_tx_cmd))->cb_status + & __constant_cpu_to_le16(CB_STATUS_COMPLETE)) == 0) + && time_before(jiffies, active_command->expiration_time)) { + goto exit; + } else { + list_del(&(active_command->list_elem)); + e100_free_non_tx_cmd(bdp, active_command); + } + break; + + default: + break; + } //switch + + if (list_empty(&bdp->non_tx_cmd_list)) { + bdp->non_tx_command_state = E100_NON_TX_IDLE; + spin_lock_irq(&(bdp->bd_lock)); + bdp->next_cu_cmd = START_WAIT; + spin_unlock_irq(&(bdp->bd_lock)); + restart = false; + goto exit; + } else { + u16 cmd_type; + + bdp->non_tx_command_state = E100_WAIT_NON_TX_FINISH; + active_command = list_entry(bdp->non_tx_cmd_list.next, + nxmit_cb_entry_t, list_elem); + spin_lock_irq(&(bdp->bd_lock)); + e100_wait_exec_cmplx(bdp, active_command->dma_addr, + SCB_CUC_START); + spin_unlock_irq(&(bdp->bd_lock)); + active_command->expiration_time = jiffies + HZ; + cmd_type = CB_CMD_MASK & + le16_to_cpu(((cb_header_t *) + (active_command->non_tx_cmd))->cb_cmd); + bdp->same_cmd_entry[cmd_type] = NULL; + } + +exit: + if (restart) { + mod_timer(&(bdp->nontx_timer_id), jiffies + 1); + } else { + if (netif_running(bdp->device)) + netif_wake_queue(bdp->device); + } + spin_unlock_bh(&(bdp->bd_non_tx_lock)); +} diff -Nru a/drivers/net/e100/e100_phy.c b/drivers/net/e100/e100_phy.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e100/e100_phy.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,1133 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. +*******************************************************************************/ + +#include "e100_phy.h" + +void e100_handle_zlock(struct e100_private *bdp); + +/* + * Procedure: e100_mdi_write + * + * Description: This routine will write a value to the specified MII register + * of an external MDI compliant device (e.g. PHY 100). The + * command will execute in polled mode. + * + * Arguments: + * bdp - Ptr to this card's e100_bdconfig structure + * reg_addr - The MII register that we are writing to + * phy_addr - The MDI address of the Phy component. + * data - The value that we are writing to the MII register. + * + * Returns: + * NOTHING + */ +void +e100_mdi_write(struct e100_private *bdp, u32 reg_addr, u32 phy_addr, u16 data) +{ + int e100_retry; + u32 temp_val; + + temp_val = (((u32) data) | (reg_addr << 16) | + (phy_addr << 21) | (MDI_WRITE << 26)); + writel(temp_val, &bdp->scb->scb_mdi_cntrl); + + /* wait 20usec before checking status */ + udelay(20); + + /* poll for the mdi write to complete */ + e100_retry = E100_CMD_WAIT; + while ((!(readl(&bdp->scb->scb_mdi_cntrl) & MDI_PHY_READY)) && + (e100_retry)) { + + udelay(20); + e100_retry--; + } +} + +/* + * Procedure: e100_mdi_read + * + * Description: This routine will read a value from the specified MII register + * of an external MDI compliant device (e.g. PHY 100), and return + * it to the calling routine. The command will execute in polled + * mode. + * + * Arguments: + * bdp - Ptr to this card's e100_bdconfig structure + * reg_addr - The MII register that we are reading from + * phy_addr - The MDI address of the Phy component. + * + * Results: + * data - The value that we read from the MII register. + * + * Returns: + * NOTHING + */ +void +e100_mdi_read(struct e100_private *bdp, u32 reg_addr, u32 phy_addr, u16 *data) +{ + int e100_retry; + u32 temp_val; + + /* Issue the read command to the MDI control register. */ + temp_val = ((reg_addr << 16) | (phy_addr << 21) | (MDI_READ << 26)); + writel(temp_val, &bdp->scb->scb_mdi_cntrl); + + /* wait 20usec before checking status */ + udelay(20); + + /* poll for the mdi read to complete */ + e100_retry = E100_CMD_WAIT; + while ((!(readl(&bdp->scb->scb_mdi_cntrl) & MDI_PHY_READY)) && + (e100_retry)) { + + udelay(20); + e100_retry--; + } + + // return the lower word + *data = (u16) readl(&bdp->scb->scb_mdi_cntrl); +} + +static unsigned char __devinit +e100_phy_valid(struct e100_private *bdp, unsigned int phy_address) +{ + u16 ctrl_reg, stat_reg; + + /* Read the MDI control register */ + e100_mdi_read(bdp, MII_BMCR, phy_address, &ctrl_reg); + + /* Read the status register twice, bacause of sticky bits */ + e100_mdi_read(bdp, MII_BMSR, phy_address, &stat_reg); + e100_mdi_read(bdp, MII_BMSR, phy_address, &stat_reg); + + if ((ctrl_reg == 0xffff) || ((stat_reg == 0) && (ctrl_reg == 0))) + return false; + + return true; +} + +static void __devinit +e100_phy_address_detect(struct e100_private *bdp) +{ + unsigned int addr; + unsigned char valid_phy_found = false; + + if (IS_NC3133(bdp)) { + bdp->phy_addr = 0; + return; + } + + if (e100_phy_valid(bdp, PHY_DEFAULT_ADDRESS)) { + bdp->phy_addr = PHY_DEFAULT_ADDRESS; + valid_phy_found = true; + + } else { + for (addr = MIN_PHY_ADDR; addr <= MAX_PHY_ADDR; addr++) { + if (e100_phy_valid(bdp, addr)) { + bdp->phy_addr = addr; + valid_phy_found = true; + break; + } + } + } + + if (!valid_phy_found) { + bdp->phy_addr = PHY_ADDRESS_503; + } +} + +static void __devinit +e100_phy_id_detect(struct e100_private *bdp) +{ + u16 low_id_reg, high_id_reg; + + if (bdp->phy_addr == PHY_ADDRESS_503) { + bdp->PhyId = PHY_503; + return; + } + if (!(bdp->flags & IS_ICH)) { + if (bdp->rev_id >= D102_REV_ID) { + bdp->PhyId = PHY_82562ET; + return; + } + } + + /* Read phy id from the MII register */ + e100_mdi_read(bdp, MII_PHYSID1, bdp->phy_addr, &low_id_reg); + e100_mdi_read(bdp, MII_PHYSID2, bdp->phy_addr, &high_id_reg); + + bdp->PhyId = ((unsigned int) low_id_reg | + ((unsigned int) high_id_reg << 16)); +} + +static void __devinit +e100_phy_isolate(struct e100_private *bdp) +{ + unsigned int phy_address; + u16 ctrl_reg; + + /* Go over all phy addresses. Deisolate the selected one, and isolate + * all the rest */ + for (phy_address = 0; phy_address <= MAX_PHY_ADDR; phy_address++) { + if (phy_address != bdp->phy_addr) { + e100_mdi_write(bdp, MII_BMCR, phy_address, + BMCR_ISOLATE); + + } else { + e100_mdi_read(bdp, MII_BMCR, bdp->phy_addr, &ctrl_reg); + ctrl_reg &= ~BMCR_ISOLATE; + e100_mdi_write(bdp, MII_BMCR, bdp->phy_addr, ctrl_reg); + } + + udelay(100); + } +} + +static unsigned char __devinit +e100_phy_specific_setup(struct e100_private *bdp) +{ + u16 misc_reg; + + if (bdp->phy_addr == PHY_ADDRESS_503) { + switch (bdp->params.e100_speed_duplex) { + case E100_AUTONEG: + /* The adapter can't autoneg. so set to 10/HALF */ + printk(KERN_INFO + "503 serial component detected which " + "cannot autonegotiate\n"); + printk(KERN_INFO + "speed/duplex forced to 10Mbps / Half duplex\n"); + bdp->params.e100_speed_duplex = E100_SPEED_10_HALF; + break; + + case E100_SPEED_100_HALF: + case E100_SPEED_100_FULL: + printk(KERN_ERR + "503 serial component detected which does not " + "support 100Mbps\n"); + printk(KERN_ERR + "Change the forced speed/duplex to a supported " + "setting\n"); + return false; + } + + return true; + } + + if (IS_NC3133(bdp)) { + u16 int_reg; + + /* enable 100BASE fiber interface */ + e100_mdi_write(bdp, MDI_NC3133_CONFIG_REG, bdp->phy_addr, + MDI_NC3133_100FX_ENABLE); + + if ((bdp->params.e100_speed_duplex != E100_AUTONEG) && + (bdp->params.e100_speed_duplex != E100_SPEED_100_FULL)) { + /* just inform user about 100 full */ + printk(KERN_ERR "NC3133 NIC can only run " + "at 100Mbps full duplex\n"); + } + + bdp->params.e100_speed_duplex = E100_SPEED_100_FULL; + + /* enable interrupts */ + e100_mdi_read(bdp, MDI_NC3133_INT_ENABLE_REG, + bdp->phy_addr, &int_reg); + int_reg |= MDI_NC3133_INT_ENABLE; + e100_mdi_write(bdp, MDI_NC3133_INT_ENABLE_REG, + bdp->phy_addr, int_reg); + } + + /* Handle the National TX */ + if ((bdp->PhyId & PHY_MODEL_REV_ID_MASK) == PHY_NSC_TX) { + e100_mdi_read(bdp, NSC_CONG_CONTROL_REG, + bdp->phy_addr, &misc_reg); + + misc_reg |= NSC_TX_CONG_TXREADY; + + /* disable the congestion control bit in the National Phy */ + misc_reg &= ~NSC_TX_CONG_ENABLE; + + e100_mdi_write(bdp, NSC_CONG_CONTROL_REG, + bdp->phy_addr, misc_reg); + } + + return true; +} + +/* + * Procedure: e100_phy_fix_squelch + * + * Description: + * Help find link on certain rare scenarios. + * NOTE: This routine must be called once per watchdog, + * and *after* setting the current link state. + * + * Arguments: + * bdp - Ptr to this card's e100_bdconfig structure + * + * Returns: + * NOTHING + */ +static void +e100_phy_fix_squelch(struct e100_private *bdp) +{ + if ((bdp->PhyId != PHY_82555_TX) || (bdp->flags & DF_SPEED_FORCED)) + return; + + if (netif_carrier_ok(bdp->device)) { + switch (bdp->PhyState) { + case 0: + break; + case 1: + e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, 0x0000); + break; + case 2: + e100_mdi_write(bdp, PHY_82555_MDI_EQUALIZER_CSR, + bdp->phy_addr, 0x3000); + break; + } + bdp->PhyState = 0; + bdp->PhyDelay = 0; + + } else if (!bdp->PhyDelay--) { + switch (bdp->PhyState) { + case 0: + e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, EXTENDED_SQUELCH_BIT); + bdp->PhyState = 1; + break; + case 1: + e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, 0x0000); + e100_mdi_write(bdp, PHY_82555_MDI_EQUALIZER_CSR, + bdp->phy_addr, 0x2010); + bdp->PhyState = 2; + break; + case 2: + e100_mdi_write(bdp, PHY_82555_MDI_EQUALIZER_CSR, + bdp->phy_addr, 0x3000); + bdp->PhyState = 0; + break; + } + + e100_mdi_write(bdp, MII_BMCR, bdp->phy_addr, + BMCR_ANENABLE | BMCR_ANRESTART); + bdp->PhyDelay = 3; + } +} + +/* + * Procedure: e100_fix_polarity + * + * Description: + * Fix for 82555 auto-polarity toggle problem. With a short cable + * connecting an 82555 with an 840A link partner, if the medium is noisy, + * the 82555 sometime thinks that the polarity might be wrong and so + * toggles polarity. This happens repeatedly and results in a high bit + * error rate. + * NOTE: This happens only at 10 Mbps + * + * Arguments: + * bdp - Ptr to this card's e100_bdconfig structure + * + * Returns: + * NOTHING + */ +static void __devinit +e100_fix_polarity(struct e100_private *bdp) +{ + u16 status; + u16 errors; + u16 misc_reg; + int speed; + + if ((bdp->PhyId != PHY_82555_TX) && (bdp->PhyId != PHY_82562ET) && + (bdp->PhyId != PHY_82562EM)) + return; + + /* If the user wants auto-polarity disabled, do only that and nothing * + * else. * e100_autopolarity == 0 means disable --- we do just the + * disabling * e100_autopolarity == 1 means enable --- we do nothing at + * all * e100_autopolarity >= 2 means we do the workaround code. */ + /* Change for 82558 enhancement */ + switch (E100_AUTOPOLARITY) { + case 0: + e100_mdi_read(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, &misc_reg); + e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, + (u16) (misc_reg | DISABLE_AUTO_POLARITY)); + break; + + case 1: + e100_mdi_read(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, &misc_reg); + e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, + (u16) (misc_reg & ~DISABLE_AUTO_POLARITY)); + break; + + case 2: + /* we do this only if link is up */ + if (!netif_carrier_ok(bdp->device)) { + break; + } + + e100_mdi_read(bdp, PHY_82555_CSR, bdp->phy_addr, &status); + speed = (status & PHY_82555_SPEED_BIT) ? 100 : 10; + + /* we need to do this only if speed is 10 */ + if (speed != 10) { + break; + } + + /* see if we have any end of frame errors */ + e100_mdi_read(bdp, PHY_82555_EOF_COUNTER, + bdp->phy_addr, &errors); + + /* if non-zero, wait for 100 ms before reading again */ + if (errors) { + udelay(200); + e100_mdi_read(bdp, PHY_82555_EOF_COUNTER, + bdp->phy_addr, &errors); + + /* if non-zero again, we disable polarity */ + if (errors) { + e100_mdi_read(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, &misc_reg); + e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, + (u16) (misc_reg | + DISABLE_AUTO_POLARITY)); + } + } + + if (!errors) { + /* it is safe to read the polarity now */ + e100_mdi_read(bdp, PHY_82555_CSR, + bdp->phy_addr, &status); + + /* if polarity is normal, disable polarity */ + if (!(status & PHY_82555_POLARITY_BIT)) { + e100_mdi_read(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, &misc_reg); + e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, + (u16) (misc_reg | + DISABLE_AUTO_POLARITY)); + } + } + break; + + default: + break; + } +} + +/* + * Procedure: e100_find_speed_duplex + * + * Description: This routine will figure out what line speed and duplex mode + * the PHY is currently using. + * + * Arguments: + * bdp - Ptr to this card's e100_bdconfig structure + * + * Returns: + * NOTHING + */ +static void +e100_find_speed_duplex(struct e100_private *bdp) +{ + unsigned int PhyId; + u16 stat_reg, misc_reg; + u16 ad_reg, lp_ad_reg; + + PhyId = bdp->PhyId & PHY_MODEL_REV_ID_MASK; + + /* First we should check to see if we have link */ + /* If we don't have a link no reason to print a speed and duplex */ + if (!e100_update_link_state(bdp)) { + return; + } + + if (bdp->flags & DF_SPEED_FORCED) { + return; + } + + /* On the 82559 and later controllers, speed/duplex is part of the * + * SCB. So, we save an mdi_read and get these from the SCB. * */ + if (bdp->rev_id >= D101MA_REV_ID) { + /* Read speed */ + if (readb(&bdp->scb->scb_ext.d101m_scb.scb_gen_stat) & BIT_1) + bdp->cur_line_speed = 100; + else + bdp->cur_line_speed = 10; + + /* Read duplex */ + if (readb(&bdp->scb->scb_ext.d101m_scb.scb_gen_stat) & BIT_2) + bdp->cur_dplx_mode = FULL_DUPLEX; + else + bdp->cur_dplx_mode = HALF_DUPLEX; + + return; + } + + /* If this is a Phy 100, then read bits 1 and 0 of extended register 0, + * to get the current speed and duplex settings. */ + if ((PhyId == PHY_100_A) || (PhyId == PHY_100_C) || + (PhyId == PHY_82555_TX)) { + + /* Read Phy 100 extended register 0 */ + e100_mdi_read(bdp, EXTENDED_REG_0, bdp->phy_addr, &misc_reg); + + /* Get current speed setting */ + if (misc_reg & PHY_100_ER0_SPEED_INDIC) + bdp->cur_line_speed = 100; + else + bdp->cur_line_speed = 10; + + /* Get current duplex setting -- FDX enabled if bit is set */ + if (misc_reg & PHY_100_ER0_FDX_INDIC) + bdp->cur_dplx_mode = FULL_DUPLEX; + else + bdp->cur_dplx_mode = HALF_DUPLEX; + + return; + } + + /* See if link partner is capable of Auto-Negotiation (bit 0, reg 6) */ + e100_mdi_read(bdp, MII_EXPANSION, bdp->phy_addr, &misc_reg); + + /* See if Auto-Negotiation was complete (bit 5, reg 1) */ + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &stat_reg); + + /* If a True NWAY connection was made, then we can detect speed/dplx + * by ANDing our adapter's advertised abilities with our link partner's + * advertised ablilities, and then assuming that the highest common + * denominator was chosed by NWAY. */ + if ((misc_reg & EXPANSION_NWAY) && (stat_reg & BMSR_ANEGCOMPLETE)) { + + /* Read our advertisement register */ + e100_mdi_read(bdp, MII_ADVERTISE, bdp->phy_addr, &ad_reg); + + /* Read our link partner's advertisement register */ + e100_mdi_read(bdp, MII_LPA, bdp->phy_addr, &lp_ad_reg); + + /* AND the two advertisement registers together, and get rid + * of any extraneous bits. */ + ad_reg &= (lp_ad_reg & NWAY_LP_ABILITY); + + /* Get speed setting */ + if (ad_reg & + (ADVERTISE_100HALF | ADVERTISE_100FULL | + ADVERTISE_100BASE4)) + + bdp->cur_line_speed = 100; + else + bdp->cur_line_speed = 10; + + /* Get duplex setting -- use priority resolution algorithm */ + if (ad_reg & ADVERTISE_100BASE4) { + bdp->cur_dplx_mode = HALF_DUPLEX; + } else if (ad_reg & ADVERTISE_100FULL) { + bdp->cur_dplx_mode = FULL_DUPLEX; + } else if (ad_reg & ADVERTISE_100HALF) { + bdp->cur_dplx_mode = HALF_DUPLEX; + } else if (ad_reg & ADVERTISE_10FULL) { + bdp->cur_dplx_mode = FULL_DUPLEX; + } else { + bdp->cur_dplx_mode = HALF_DUPLEX; + } + + return; + } + + /* If we are connected to a dumb (non-NWAY) repeater or hub, and the + * line speed was determined automatically by parallel detection, then + * we have no way of knowing exactly what speed the PHY is set to + * unless that PHY has a propietary register which indicates speed in + * this situation. The NSC TX PHY does have such a register. Also, + * since NWAY didn't establish the connection, the duplex setting + * should HALF duplex. */ + bdp->cur_dplx_mode = HALF_DUPLEX; + + if (PhyId == PHY_NSC_TX) { + /* Read register 25 to get the SPEED_10 bit */ + e100_mdi_read(bdp, NSC_SPEED_IND_REG, bdp->phy_addr, &misc_reg); + + /* If bit 6 was set then we're at 10Mbps */ + if (misc_reg & NSC_TX_SPD_INDC_SPEED) + bdp->cur_line_speed = 10; + else + bdp->cur_line_speed = 100; + + } else { + /* If we don't know the line speed, default to 10Mbps */ + bdp->cur_line_speed = 10; + } +} + +/* + * Procedure: e100_force_speed_duplex + * + * Description: This routine forces line speed and duplex mode of the + * adapter based on the values the user has set in e100.c. + * + * Arguments: bdp - Pointer to the e100_private structure for the board + * + * Returns: void + * + */ +static void +e100_force_speed_duplex(struct e100_private *bdp) +{ + u16 control; + int neg_timeout = 2 * HZ; //2 sec in jiffies + + bdp->flags |= DF_SPEED_FORCED; + + spin_lock_bh(&(bdp->mdi_access_lock)); + + e100_mdi_read(bdp, MII_BMCR, bdp->phy_addr, &control); + control &= ~BMCR_ANENABLE; + + /* Check e100.c values */ + switch (bdp->params.e100_speed_duplex) { + case E100_SPEED_10_HALF: + control &= ~BMCR_SPEED100; + control &= ~BMCR_FULLDPLX; + bdp->cur_line_speed = 10; + bdp->cur_dplx_mode = HALF_DUPLEX; + break; + + case E100_SPEED_10_FULL: + control &= ~BMCR_SPEED100; + control |= BMCR_FULLDPLX; + bdp->cur_line_speed = 10; + bdp->cur_dplx_mode = FULL_DUPLEX; + break; + + case E100_SPEED_100_HALF: + control |= BMCR_SPEED100; + control &= ~BMCR_FULLDPLX; + bdp->cur_line_speed = 100; + bdp->cur_dplx_mode = HALF_DUPLEX; + break; + + case E100_SPEED_100_FULL: + control |= BMCR_SPEED100; + control |= BMCR_FULLDPLX; + bdp->cur_line_speed = 100; + bdp->cur_dplx_mode = FULL_DUPLEX; + break; + } + + e100_mdi_write(bdp, MII_BMCR, bdp->phy_addr, control); + + /* loop must run at least once */ + do { + spin_unlock_bh(&(bdp->mdi_access_lock)); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(SLEEP_TIME); + + spin_lock_bh(&(bdp->mdi_access_lock)); + + if (e100_update_link_state(bdp)) { + break; + } + neg_timeout -= SLEEP_TIME; + + } while (neg_timeout > 0); + + spin_unlock_bh(&(bdp->mdi_access_lock)); +} + +/* + * Procedure: e100_set_fc + * + * Description: Checks the link's capability for flow control. + * + * Arguments: bdp - Pointer to the e100_private structure for the board + * + * Returns: void + * + */ +static void +e100_set_fc(struct e100_private *bdp) +{ + u16 ad_reg; + u16 lp_ad_reg; + u16 exp_reg; + + /* no flow control for 82557, forced links or half duplex */ + if (!netif_carrier_ok(bdp->device) || (bdp->flags & DF_SPEED_FORCED) || + (bdp->cur_dplx_mode == HALF_DUPLEX) || + !(bdp->flags & IS_BACHELOR)) { + + bdp->flags &= ~DF_LINK_FC_CAP; + return; + } + + /* See if link partner is capable of Auto-Negotiation (bit 0, reg 6) */ + e100_mdi_read(bdp, MII_EXPANSION, bdp->phy_addr, &exp_reg); + + if (exp_reg & EXPANSION_NWAY) { + /* Read our advertisement register */ + e100_mdi_read(bdp, MII_ADVERTISE, bdp->phy_addr, &ad_reg); + + /* Read our link partner's advertisement register */ + e100_mdi_read(bdp, MII_LPA, bdp->phy_addr, &lp_ad_reg); + + ad_reg &= lp_ad_reg; /* AND the 2 ad registers */ + + if (ad_reg & NWAY_AD_FC_SUPPORTED) + bdp->flags |= DF_LINK_FC_CAP; + else + bdp->flags &= ~DF_LINK_FC_CAP; + + } else { + bdp->flags &= ~DF_LINK_FC_CAP; + } +} + +/* + * Procedure: e100_phy_check + * + * Arguments: bdp - Pointer to the e100_private structure for the board + * + * Returns: true if link state was changed + * B_FLASE otherwise + * + */ +unsigned char +e100_phy_check(struct e100_private *bdp) +{ + unsigned char old_link; + unsigned char changed = false; + + old_link = netif_carrier_ok(bdp->device) ? 1 : 0; + e100_find_speed_duplex(bdp); + + if (!old_link && netif_carrier_ok(bdp->device)) { + e100_set_fc(bdp); + changed = true; + } + + if (old_link && !netif_carrier_ok(bdp->device)) { + /* reset the zero lock state */ + bdp->zlock_state = ZLOCK_INITIAL; + + // set auto lock for phy auto-negotiation on link up + if ((bdp->PhyId & PHY_MODEL_REV_ID_MASK) == PHY_82555_TX) + e100_mdi_write(bdp, PHY_82555_MDI_EQUALIZER_CSR, + bdp->phy_addr, 0); + changed = true; + } + + e100_phy_fix_squelch(bdp); + e100_handle_zlock(bdp); + + return changed; +} + +/* + * Procedure: e100_auto_neg + * + * Description: This routine will start autonegotiation and wait + * for it to complete + * + * Arguments: + * bdp - pointer to this card's e100_bdconfig structure + * force_restart - defines if autoneg should be restarted even if it + * has been completed before + * Returns: + * NOTHING + */ +static void +e100_auto_neg(struct e100_private *bdp, unsigned char force_restart) +{ + u16 stat_reg; + unsigned int i; + + bdp->flags &= ~DF_SPEED_FORCED; + + spin_lock_bh(&(bdp->mdi_access_lock)); + + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &stat_reg); + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &stat_reg); + + /* if we are capable of performing autoneg then we restart if needed */ + if ((stat_reg != 0xFFFF) && (stat_reg & BMSR_ANEGCAPABLE)) { + + if ((!force_restart) && + (stat_reg & BMSR_ANEGCOMPLETE)) { + goto exit; + } + + e100_mdi_write(bdp, MII_BMCR, bdp->phy_addr, + BMCR_ANENABLE | BMCR_ANRESTART); + + /* wait for autoneg to complete (up to 3 seconds) */ + for (i = 0; i < 60; i++) { + /* now re-read the value. Sticky so read twice */ + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &stat_reg); + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &stat_reg); + + if (stat_reg & BMSR_ANEGCOMPLETE) + goto exit; + + spin_unlock_bh(&(bdp->mdi_access_lock)); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(SLEEP_TIME); + + spin_lock_bh(&(bdp->mdi_access_lock)); + + } + } + +exit: + e100_find_speed_duplex(bdp); + spin_unlock_bh(&(bdp->mdi_access_lock)); +} + +void +e100_phy_set_speed_duplex(struct e100_private *bdp, unsigned char force_restart) +{ + if (bdp->params.e100_speed_duplex == E100_AUTONEG) { + e100_auto_neg(bdp, force_restart); + + } else { + e100_force_speed_duplex(bdp); + } + + e100_set_fc(bdp); +} + +void __devexit +e100_phy_reset(struct e100_private *bdp) +{ + u16 ctrl_reg; + + ctrl_reg = BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET; + + e100_mdi_write(bdp, MII_BMCR, bdp->phy_addr, ctrl_reg); + + udelay(100); +} + +unsigned char __devinit +e100_phy_init(struct e100_private *bdp) +{ + e100_phy_address_detect(bdp); + e100_phy_isolate(bdp); + e100_phy_id_detect(bdp); + + if (!e100_phy_specific_setup(bdp)) + return false; + + bdp->PhyState = 0; + bdp->PhyDelay = 0; + bdp->zlock_state = ZLOCK_INITIAL; + + e100_phy_set_speed_duplex(bdp, false); + e100_fix_polarity(bdp); + + return true; +} + +/* + * Procedure: e100_get_link_state + * + * Description: This routine checks the link status of the adapter + * + * Arguments: bdp - Pointer to the e100_private structure for the board + * + * + * Returns: true - If a link is found + * false - If there is no link + * + */ +unsigned char +e100_get_link_state(struct e100_private *bdp) +{ + unsigned char link = false; + u16 status; + + /* Check link status */ + /* If the controller is a 82559 or later one, link status is available + * from the CSR. This avoids the mdi_read. */ + if (bdp->rev_id >= D101MA_REV_ID) { + if (readb(&bdp->scb->scb_ext.d101m_scb.scb_gen_stat) & BIT_0) { + link = true; + } else { + link = false; + } + + } else { + /* Read the status register twice because of sticky bits */ + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &status); + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &status); + + if (status & BMSR_LSTATUS) { + link = true; + } else { + link = false; + } + } + + return link; +} + +/* + * Procedure: e100_update_link_state + * + * Description: This routine updates the link status of the adapter + * + * Arguments: bdp - Pointer to the e100_private structure for the board + * + * + * Returns: true - If a link is found + * false - If there is no link + * + */ +unsigned char +e100_update_link_state(struct e100_private *bdp) +{ + unsigned char link; + + link = e100_get_link_state(bdp); + + if (link) { + if (!netif_carrier_ok(bdp->device)) + netif_carrier_on(bdp->device); + } else { + if (netif_carrier_ok(bdp->device)) + netif_carrier_off(bdp->device); + } + + return link; +} + +/**************************************************************************\ + ** + ** PROC NAME: e100_handle_zlock + ** This function manages a state machine that controls + ** the driver's zero locking algorithm. + ** This function is called by e100_watchdog() every ~2 second. + ** States: + ** The current link handling state is stored in + ** bdp->zlock_state, and is one of: + ** ZLOCK_INITIAL, ZLOCK_READING, ZLOCK_SLEEPING + ** Detailed description of the states and the transitions + ** between states is found below. + ** Note that any time the link is down / there is a reset + ** state will be changed outside this function to ZLOCK_INITIAL + ** Algorithm: + ** 1. If link is up & 100 Mbps continue else stay in #1: + ** 2. Set 'auto lock' + ** 3. Read & Store 100 times 'Zero' locked in 1 sec interval + ** 4. If max zero read >= 0xB continue else goto 1 + ** 5. Set most popular 'Zero' read in #3 + ** 6. Sleep 5 minutes + ** 7. Read number of errors, if it is > 300 goto 2 else goto 6 + ** Data Structures (in DRIVER_DATA): + ** zlock_state - current state of the algorithm + ** zlock_read_cnt - counts number of reads (up to 100) + ** zlock_read_data[i] - counts number of times 'Zero' read was i, 0 <= i <= 15 + ** zlock_sleep_cnt - keeps track of "sleep" time (up to 300 secs = 5 minutes) + ** + ** Parameters: DRIVER_DATA *bdp + ** + ** bdp - Pointer to HSM's adapter data space + ** + ** Return Value: NONE + ** + ** See Also: e100_watchdog() + ** + \**************************************************************************/ +void +e100_handle_zlock(struct e100_private *bdp) +{ + u16 pos; + u16 eq_reg; + u16 err_cnt; + u8 mpz; /* Most Popular Zero */ + + switch (bdp->zlock_state) { + case ZLOCK_INITIAL: + + if (((u8) bdp->rev_id <= D102_REV_ID) || + !(bdp->cur_line_speed == 100) || + !netif_carrier_ok(bdp->device)) { + break; + } + + /* initialize hw and sw and start reading */ + e100_mdi_write(bdp, PHY_82555_MDI_EQUALIZER_CSR, + bdp->phy_addr, 0); + /* reset read counters: */ + bdp->zlock_read_cnt = 0; + for (pos = 0; pos < 16; pos++) + bdp->zlock_read_data[pos] = 0; + /* start reading in the next call back: */ + bdp->zlock_state = ZLOCK_READING; + + /* FALL THROUGH !! */ + + case ZLOCK_READING: + /* state: reading (100 times) zero locked in 1 sec interval + * prev states: ZLOCK_INITIAL + * next states: ZLOCK_INITIAL, ZLOCK_SLEEPING */ + + e100_mdi_read(bdp, PHY_82555_MDI_EQUALIZER_CSR, + bdp->phy_addr, &eq_reg); + pos = (eq_reg & ZLOCK_ZERO_MASK) >> 4; + bdp->zlock_read_data[pos]++; + bdp->zlock_read_cnt++; + + if (bdp->zlock_read_cnt == ZLOCK_MAX_READS) { + /* check if we read a 'Zero' value of 0xB or greater */ + if ((bdp->zlock_read_data[0xB]) || + (bdp->zlock_read_data[0xC]) || + (bdp->zlock_read_data[0xD]) || + (bdp->zlock_read_data[0xE]) || + (bdp->zlock_read_data[0xF])) { + + /* we've read 'Zero' value of 0xB or greater, + * find most popular 'Zero' value and lock it */ + mpz = 0; + /* this loop finds the most popular 'Zero': */ + for (pos = 1; pos < 16; pos++) { + if (bdp->zlock_read_data[pos] > + bdp->zlock_read_data[mpz]) + + mpz = pos; + } + /* now lock the most popular 'Zero': */ + eq_reg = (ZLOCK_SET_ZERO | mpz); + e100_mdi_write(bdp, + PHY_82555_MDI_EQUALIZER_CSR, + bdp->phy_addr, eq_reg); + + /* sleep for 5 minutes: */ + bdp->zlock_sleep_cnt = jiffies; + bdp->zlock_state = ZLOCK_SLEEPING; + /* we will be reading the # of errors after 5 + * minutes, so we need to reset the error + * counters - these registers are self clearing + * on read, so read them */ + e100_mdi_read(bdp, PHY_82555_SYMBOL_ERR, + bdp->phy_addr, &err_cnt); + + } else { + /* we did not read a 'Zero' value of 0xB or + * above. go back to the start */ + bdp->zlock_state = ZLOCK_INITIAL; + } + + } + break; + + case ZLOCK_SLEEPING: + /* state: sleeping for 5 minutes + * prev states: ZLOCK_READING + * next states: ZLOCK_READING, ZLOCK_SLEEPING */ + + /* if 5 minutes have passed: */ + if ((jiffies - bdp->zlock_sleep_cnt) >= ZLOCK_MAX_SLEEP) { + /* read and sum up the number of errors: */ + e100_mdi_read(bdp, PHY_82555_SYMBOL_ERR, + bdp->phy_addr, &err_cnt); + /* if we've more than 300 errors (this number was + * calculated according to the spec max allowed errors + * (80 errors per 1 million frames) for 5 minutes in + * 100 Mbps (or the user specified max BER number) */ + if (err_cnt > bdp->params.ber) { + /* start again in the next callback: */ + bdp->zlock_state = ZLOCK_INITIAL; + } else { + /* we don't have more errors than allowed, + * sleep for 5 minutes */ + bdp->zlock_sleep_cnt = jiffies; + } + } + break; + + default: + break; + } +} diff -Nru a/drivers/net/e100/e100_phy.h b/drivers/net/e100/e100_phy.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e100/e100_phy.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,183 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. +*******************************************************************************/ + +#ifndef _E100_PHY_INC_ +#define _E100_PHY_INC_ + +#include "e100.h" + +#include + +/* + * Auto-polarity enable/disable + * e100_autopolarity = 0 => disable auto-polarity + * e100_autopolarity = 1 => enable auto-polarity + * e100_autopolarity = 2 => let software determine + */ +#define E100_AUTOPOLARITY 2 + +#define IS_NC3133(bdp) (((bdp)->pdev->subsystem_vendor == 0x0E11) && \ + ((bdp)->pdev->subsystem_device == 0xB0E1)) + +#define PHY_503 0 +#define PHY_100_A 0x000003E0 +#define PHY_100_C 0x035002A8 +#define PHY_NSC_TX 0x5c002000 +#define PHY_82562ET 0x033002A8 +#define PHY_82562EM 0x032002A8 +#define PHY_82562EH 0x017002A8 +#define PHY_82555_TX 0x015002a8 /* added this for 82555 */ +#define PHY_OTHER 0xFFFF +#define MAX_PHY_ADDR 31 +#define MIN_PHY_ADDR 0 + +#define PHY_MODEL_REV_ID_MASK 0xFFF0FFFF + +#define PHY_DEFAULT_ADDRESS 1 +#define PHY_ADDRESS_503 32 + +/* MDI Control register bit definitions */ +#define MDI_PHY_READY BIT_28 /* PHY is ready for next MDI cycle */ + +#define MDI_NC3133_CONFIG_REG 0x19 +#define MDI_NC3133_100FX_ENABLE BIT_2 +#define MDI_NC3133_INT_ENABLE_REG 0x17 +#define MDI_NC3133_INT_ENABLE BIT_1 + +/* MDI Control register opcode definitions */ +#define MDI_WRITE 1 /* Phy Write */ +#define MDI_READ 2 /* Phy read */ + +/* MDI register set*/ +#define AUTO_NEG_NEXT_PAGE_REG 0x07 /* Auto-negotiation next page xmit */ +#define EXTENDED_REG_0 0x10 /* Extended reg 0 (Phy 100 modes) */ +#define EXTENDED_REG_1 0x14 /* Extended reg 1 (Phy 100 error indications) */ +#define NSC_CONG_CONTROL_REG 0x17 /* National (TX) congestion control */ +#define NSC_SPEED_IND_REG 0x19 /* National (TX) speed indication */ + +/* ############Start of 82555 specific defines################## */ + +/* Intel 82555 specific registers */ +#define PHY_82555_CSR 0x10 /* 82555 CSR */ +#define PHY_82555_SPECIAL_CONTROL 0x11 /* 82555 special control register */ + +#define PHY_82555_RCV_ERR 0x15 /* 82555 100BaseTx Receive Error + * Frame Counter */ +#define PHY_82555_SYMBOL_ERR 0x16 /* 82555 RCV Symbol Error Counter */ +#define PHY_82555_PREM_EOF_ERR 0x17 /* 82555 100BaseTx RCV Premature End + * of Frame Error Counter */ +#define PHY_82555_EOF_COUNTER 0x18 /* 82555 end of frame error counter */ +#define PHY_82555_MDI_EQUALIZER_CSR 0x1a /* 82555 specific equalizer reg. */ + +/* 82555 CSR bits */ +#define PHY_82555_SPEED_BIT BIT_1 +#define PHY_82555_POLARITY_BIT BIT_8 + +/* 82555 equalizer reg. opcodes */ +#define ENABLE_ZERO_FORCING 0x2010 /* write to ASD conf. reg. 0 */ +#define DISABLE_ZERO_FORCING 0x2000 /* write to ASD conf. reg. 0 */ + +/* 82555 special control reg. opcodes */ +#define DISABLE_AUTO_POLARITY 0x0010 +#define EXTENDED_SQUELCH_BIT BIT_2 + +/* ############End of 82555 specific defines##################### */ + +/* Auto-Negotiation advertisement register bit definitions*/ +#define NWAY_AD_FC_SUPPORTED 0x0400 /* Flow Control supported */ + +/* Auto-Negotiation link partner ability register bit definitions*/ +#define NWAY_LP_ABILITY 0x07e0 /* technologies supported */ + +/* PHY 100 Extended Register 0 bit definitions*/ +#define PHY_100_ER0_FDX_INDIC BIT_0 /* 1 = FDX, 0 = half duplex */ +#define PHY_100_ER0_SPEED_INDIC BIT_1 /* 1 = 100Mbps, 0= 10Mbps */ + +/* National Semiconductor TX phy congestion control register bit definitions*/ +#define NSC_TX_CONG_TXREADY BIT_10 /* Makes TxReady an input */ +#define NSC_TX_CONG_ENABLE BIT_8 /* Enables congestion control */ + +/* National Semiconductor TX phy speed indication register bit definitions*/ +#define NSC_TX_SPD_INDC_SPEED BIT_6 /* 0 = 100Mbps, 1=10Mbps */ + +/************* function prototypes ************/ +extern unsigned char e100_phy_init(struct e100_private *bdp); +extern unsigned char e100_update_link_state(struct e100_private *bdp); +extern unsigned char e100_phy_check(struct e100_private *bdp); +extern void e100_phy_set_speed_duplex(struct e100_private *bdp, + unsigned char force_restart); +extern void e100_phy_reset(struct e100_private *bdp); +extern void e100_mdi_write(struct e100_private *, u32, u32, u16); +extern void e100_mdi_read(struct e100_private *, u32, u32, u16 *); + +#endif diff -Nru a/drivers/net/e100/e100_proc.c b/drivers/net/e100/e100_proc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e100/e100_proc.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,925 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. +*******************************************************************************/ + +/********************************************************************** +* * +* INTEL CORPORATION * +* * +* This software is supplied under the terms of the license included * +* above. All use of this driver must be in accordance with the terms * +* of that license. * +* * +* Module Name: e100_proc.c * +* * +* Abstract: Functions to handle the proc file system. * +* Create the proc directories and files and run read and * +* write requests from the user * +* * +* Environment: This file is intended to be specific to the Linux * +* operating system. * +* * +**********************************************************************/ + +#include + +#ifndef CONFIG_PROC_FS +#undef E100_CONFIG_PROC_FS +#endif + +#ifdef E100_CONFIG_PROC_FS +#include "e100.h" + +/***************************************************************************/ +/* /proc File System Interaface Support Functions */ +/***************************************************************************/ + +static struct proc_dir_entry *adapters_proc_dir = 0; + +/* externs from e100_main.c */ +extern const char *e100_short_driver_name; +extern const char *e100_version; +extern struct net_device_stats *e100_get_stats(struct net_device *dev); +extern char *e100_get_brand_msg(struct e100_private *bdp); +extern void e100_mdi_write(struct e100_private *, u32, u32, u16); + +static void e100_proc_cleanup(void); +static unsigned char e100_init_proc_dir(void); + +#define E100_EOU + +#define ADAPTERS_PROC_DIR "PRO_LAN_Adapters" +#define WRITE_BUF_MAX_LEN 20 +#define READ_BUF_MAX_LEN 256 +#define E100_PE_LEN 25 + +#define bdp_drv_off(off) (unsigned long)(offsetof(struct e100_private, drv_stats.off)) +#define bdp_prm_off(off) (unsigned long)(offsetof(struct e100_private, params.off)) + +typedef struct _e100_proc_entry { + char *name; + read_proc_t *read_proc; + write_proc_t *write_proc; + unsigned long offset; /* offset into bdp. ~0 means no value, pass NULL. */ +} e100_proc_entry; + +static int +generic_read(char *page, char **start, off_t off, int count, int *eof, int len) +{ + if (len <= off + count) + *eof = 1; + + *start = page + off; + len -= off; + if (len > count) + len = count; + + if (len < 0) + len = 0; + + return len; +} + +static int +read_ulong(char *page, char **start, off_t off, + int count, int *eof, unsigned long l) +{ + int len; + + len = sprintf(page, "%lu\n", l); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_gen_ulong(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long val = 0; + + if (data) + val = *((unsigned long *) data); + + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_hwaddr(char *page, char **start, off_t off, + int count, int *eof, unsigned char *hwaddr) +{ + int len; + + len = sprintf(page, "%02X:%02X:%02X:%02X:%02X:%02X\n", + hwaddr[0], hwaddr[1], hwaddr[2], + hwaddr[3], hwaddr[4], hwaddr[5]); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_descr(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + int len; + + len = sprintf(page, "%s\n", e100_get_brand_msg(bdp)); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_permanent_hwaddr(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + unsigned char *hwaddr = bdp->perm_node_address; + + return read_hwaddr(page, start, off, count, eof, hwaddr); +} + +static int +read_part_number(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + int len; + + len = sprintf(page, "%06lx-%03x\n", + (unsigned long) (bdp->pwa_no >> 8), + (unsigned int) (bdp->pwa_no & 0xFF)); + + return generic_read(page, start, off, count, eof, len); +} + +static void +set_led(struct e100_private *bdp, u16 led_mdi_op) +{ + spin_lock_bh(&bdp->mdi_access_lock); + + e100_mdi_write(bdp, PHY_82555_LED_SWITCH_CONTROL, + bdp->phy_addr, led_mdi_op); + + spin_unlock_bh(&bdp->mdi_access_lock); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(SLEEP_TIME); + + spin_lock_bh(&bdp->mdi_access_lock); + + /* turn led ownership to the chip */ + e100_mdi_write(bdp, PHY_82555_LED_SWITCH_CONTROL, + bdp->phy_addr, PHY_82555_LED_NORMAL_CONTROL); + + spin_unlock_bh(&bdp->mdi_access_lock); +} + +static int +write_blink_led_timer(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + struct e100_private *bdp = data; + char s_blink_op[WRITE_BUF_MAX_LEN + 1]; + char *res; + unsigned long i_blink_op; + + if (!buffer) + return -EINVAL; + + if (count > WRITE_BUF_MAX_LEN) { + count = WRITE_BUF_MAX_LEN; + } + copy_from_user(s_blink_op, buffer, count); + s_blink_op[count] = '\0'; + i_blink_op = simple_strtoul(s_blink_op, &res, 0); + if (res == s_blink_op) { + return -EINVAL; + } + + switch (i_blink_op) { + + case LED_OFF: + set_led(bdp, PHY_82555_LED_OFF); + break; + case LED_ON: + if (bdp->rev_id >= D101MA_REV_ID) + set_led(bdp, PHY_82555_LED_ON_559); + else + set_led(bdp, PHY_82555_LED_ON_PRE_559); + + break; + default: + return -EINVAL; + } + + return count; +} + +static e100_proc_entry e100_proc_list[] = { + {"Description", read_descr, 0, 0}, + {"Permanent_HWaddr", read_permanent_hwaddr, 0, 0}, + {"Part_Number", read_part_number, 0, 0}, + {"\n",}, + {"Rx_TCP_Checksum_Good", read_gen_ulong, 0, ~0}, + {"Rx_TCP_Checksum_Bad", read_gen_ulong, 0, ~0}, + {"Tx_TCP_Checksum_Good", read_gen_ulong, 0, ~0}, + {"Tx_TCP_Checksum_Bad", read_gen_ulong, 0, ~0}, + {"\n",}, + {"Tx_Abort_Late_Coll", read_gen_ulong, 0, bdp_drv_off(tx_late_col)}, + {"Tx_Deferred_Ok", read_gen_ulong, 0, bdp_drv_off(tx_ok_defrd)}, + {"Tx_Single_Coll_Ok", read_gen_ulong, 0, bdp_drv_off(tx_one_retry)}, + {"Tx_Multi_Coll_Ok", read_gen_ulong, 0, bdp_drv_off(tx_mt_one_retry)}, + {"Rx_Long_Length_Errors", read_gen_ulong, 0, ~0}, + {"\n",}, + {"Tx_Flow_Control_Pause", read_gen_ulong, 0, bdp_drv_off(xmt_fc_pkts)}, + {"Rx_Flow_Control_Pause", read_gen_ulong, 0, bdp_drv_off(rcv_fc_pkts)}, + {"Rx_Flow_Control_Unsup", read_gen_ulong, 0, bdp_drv_off(rcv_fc_unsupported)}, + {"\n",}, + {"Tx_TCO_Packets", read_gen_ulong, 0, bdp_drv_off(xmt_tco_pkts)}, + {"Rx_TCO_Packets", read_gen_ulong, 0, bdp_drv_off(rcv_tco_pkts)}, + {"\n",}, + {"Rx_Interrupt_Packets", read_gen_ulong, 0, bdp_drv_off(rx_intr_pkts)}, + {"Rx_Polling_Packets", read_gen_ulong, 0, bdp_drv_off(rx_tasklet_pkts)}, + {"Polling_Interrupt_Switch", read_gen_ulong, 0, bdp_drv_off(poll_intr_switch)}, + {"Identify_Adapter", 0, write_blink_led_timer, 0}, + {"", 0, 0, 0} +}; + +static int +read_info(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + e100_proc_entry *pe; + int tmp; + void *val; + int len = 0; + + for (pe = e100_proc_list; pe->name[0]; pe++) { + if (pe->name[0] == '\n') { + len += sprintf(page + len, "\n"); + continue; + } + + if (pe->read_proc) { + if ((len + READ_BUF_MAX_LEN + E100_PE_LEN + 1) >= + PAGE_SIZE) + break; + + if (pe->offset != ~0) + val = ((char *) bdp) + pe->offset; + else + val = NULL; + + len += sprintf(page + len, "%-" + __MODULE_STRING(E100_PE_LEN) + "s ", pe->name); + len += pe->read_proc(page + len, start, 0, + READ_BUF_MAX_LEN + 1, &tmp, val); + } + } + + return generic_read(page, start, off, count, eof, len); +} + +#ifdef E100_EOU +/********************** + * parameter entries + **********************/ +static int +read_int_param(char *page, char *name, char *desc, int def, int min, int max) +{ + int len; + + len = sprintf(page, "Name: %s\n", name); + len += sprintf(page + len, "Description: %s\n", desc); + len += sprintf(page + len, "Default_Value: %d\n", def); + len += sprintf(page + len, "Type: Range\n"); + len += sprintf(page + len, "Min: %d\n", min); + len += sprintf(page + len, "Max: %d\n", max); + len += sprintf(page + len, "Step:1\n"); + len += sprintf(page + len, "Radix: dec\n"); + + return len; +} + +static int +read_bool_param(char *page, char *name, char *desc, int def) +{ + int len; + + len = sprintf(page, "Name: %s\n", name); + len += sprintf(page + len, "Description: %s\n", desc); + len += sprintf(page + len, "Default_Value: %d\n", def); + len += sprintf(page + len, "Type: Enum\n"); + len += sprintf(page + len, "0: Off\n"); + len += sprintf(page + len, "1: On\n"); + + return len; +} + +static int +read_speed_duplex_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = sprintf(page, "Name: Speed and Duplex\n"); + len += sprintf(page + len, "Description: Sets the adapter's " + "speed and duplex mode\n"); + len += sprintf(page + len, "Default_Value: 0\n"); + len += sprintf(page + len, "Type: Enum\n"); + len += sprintf(page + len, "0: Auto-Negotiate\n"); + len += sprintf(page + len, "1: 10 Mbps / Half Duplex\n"); + len += sprintf(page + len, "2: 10 Mbps / Full Duplex\n"); + len += sprintf(page + len, "3: 100 Mbps / Half Duplex\n"); + len += sprintf(page + len, "4: 100 Mbps / Full Duplex\n"); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_tx_desc_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_int_param(page, "Transmit Descriptors", + "Sets the number of Tx descriptors " + "available for the adapter", + E100_DEFAULT_TCB, E100_MIN_TCB, E100_MAX_TCB); + return generic_read(page, start, off, count, eof, len); +} + +static int +read_rx_desc_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_int_param(page, "Receive Descriptors", + "Sets the number of Rx descriptors " + "available for the adapter", + E100_DEFAULT_RFD, E100_MIN_RFD, E100_MAX_RFD); + return generic_read(page, start, off, count, eof, len); +} + +static int +read_ber_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_int_param(page, "Bit Error Rate", + "Sets the value for the BER correction algorithm", + E100_DEFAULT_BER, 0, ZLOCK_MAX_ERRORS); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_xsum_rx_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_bool_param(page, "RX Checksum", + "Setting this value to \"On\" enables " + "receive checksum", E100_DEFAULT_XSUM); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_ucode_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_bool_param(page, "Microcode", + "Setting this value to \"On\" enables " + "the adapter's microcode", E100_DEFAULT_UCODE); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_bundle_small_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_bool_param(page, "Bundle Small Frames", + "Setting this value to \"On\" enables " + "interrupt bundling of small frames", + E100_DEFAULT_BUNDLE_SMALL_FR); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_fc_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_bool_param(page, "Flow Control", + "Setting this value to \"On\" enables processing " + "flow-control packets", E100_DEFAULT_FC); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_rcv_cong_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_bool_param(page, "Receive Congestion Control", + "Setting this value to \"On\" enables switching " + "to polling mode on receive", + E100_DEFAULT_RX_CONGESTION_CONTROL); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_poll_max_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + + int len; + + len = read_int_param(page, "Maximum Polling Work", + "Sets the max number of RX packets processed" + " by single polling function call", + bdp->params.RxDescriptors, 1, E100_MAX_RFD); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_int_delay_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_int_param(page, "CPU Saver Interrupt Delay", + "Sets the value for CPU saver's interrupt delay", + E100_DEFAULT_CPUSAVER_INTERRUPT_DELAY, 0x0, + 0xFFFF); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_bundle_max_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_int_param(page, "CPU Saver Maximum Bundle", + "Sets the value for CPU saver's maximum value", + E100_DEFAULT_CPUSAVER_BUNDLE_MAX, 0x1, 0xFFFF); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_ifs_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_bool_param(page, "IFS", + "Setting this value to \"On\" enables " + "the adaptive IFS algorithm", E100_DEFAULT_IFS); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_xsum_rx_val(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + unsigned long val; + + val = (bdp->params.b_params & PRM_XSUMRX) ? 1 : 0; + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_ucode_val(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + unsigned long val; + + val = (bdp->params.b_params & PRM_UCODE) ? 1 : 0; + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_fc_val(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + unsigned long val; + + val = (bdp->params.b_params & PRM_FC) ? 1 : 0; + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_ifs_val(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + unsigned long val; + + val = (bdp->params.b_params & PRM_IFS) ? 1 : 0; + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_bundle_small_val(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + unsigned long val; + + val = (bdp->params.b_params & PRM_BUNDLE_SMALL) ? 1 : 0; + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_rcv_cong_val(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + unsigned long val; + + val = (bdp->params.b_params & PRM_RX_CONG) ? 1 : 0; + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_gen_prm(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int val = 0; + + if (data) + val = *((int *) data); + + return read_ulong(page, start, off, count, eof, (unsigned long) val); +} + +static e100_proc_entry e100_proc_params[] = { + /* definitions */ + {"e100_speed_duplex.def", read_speed_duplex_def, 0, 0}, + {"RxDescriptors.def", read_rx_desc_def, 0, 0}, + {"TxDescriptors.def", read_tx_desc_def, 0, 0}, + {"XsumRX.def", read_xsum_rx_def, 0, 0}, + {"ucode.def", read_ucode_def, 0, 0}, + {"BundleSmallFr.def", read_bundle_small_def, 0, 0}, + {"IntDelay.def", read_int_delay_def, 0, 0}, + {"BundleMax.def", read_bundle_max_def, 0, 0}, + {"ber.def", read_ber_def, 0, 0}, + {"flow_control.def", read_fc_def, 0, 0}, + {"IFS.def", read_ifs_def, 0, 0}, + {"RxCongestionControl.def", read_rcv_cong_def, 0, 0}, + {"PollingMaxWork.def", read_poll_max_def, 0, 0}, + /* values */ + {"e100_speed_duplex.val", read_gen_prm, 0, bdp_prm_off(e100_speed_duplex)}, + {"RxDescriptors.val", read_gen_prm, 0, bdp_prm_off(RxDescriptors)}, + {"TxDescriptors.val", read_gen_prm, 0, bdp_prm_off(TxDescriptors)}, + {"XsumRX.val", read_xsum_rx_val, 0, 0}, + {"ucode.val", read_ucode_val, 0, 0}, + {"BundleSmallFr.val", read_bundle_small_val, 0, 0}, + {"IntDelay.val", read_gen_prm, 0, bdp_prm_off(IntDelay)}, + {"BundleMax.val", read_gen_prm, 0, bdp_prm_off(BundleMax)}, + {"ber.val", read_gen_prm, 0, bdp_prm_off(ber)}, + {"flow_control.val", read_fc_val, 0, 0}, + {"IFS.val", read_ifs_val, 0, 0}, + {"RxCongestionControl.val", read_rcv_cong_val, 0, 0}, + {"PollingMaxWork.val", read_gen_prm, 0, bdp_prm_off(PollingMaxWork)}, + {"", 0, 0, 0} +}; +#endif /* E100_EOU */ + +static struct proc_dir_entry * __devinit +create_proc_rw(char *name, void *data, struct proc_dir_entry *parent, + read_proc_t * read_proc, write_proc_t * write_proc) +{ + struct proc_dir_entry *pdep; + mode_t mode = S_IFREG; + + if (write_proc) { + mode |= S_IWUSR; + if (read_proc) { + mode |= S_IRUSR; + } + + } else if (read_proc) { + mode |= S_IRUGO; + } + + if (!(pdep = create_proc_entry(name, mode, parent))) + return NULL; + + pdep->read_proc = read_proc; + pdep->write_proc = write_proc; + pdep->data = data; + return pdep; +} + +#ifdef E100_EOU +static int __devinit +create_proc_param_subdir(struct e100_private *bdp, + struct proc_dir_entry *dev_dir) +{ + struct proc_dir_entry *param_dir; + e100_proc_entry *pe; + void *data; + + param_dir = create_proc_entry("LoadParameters", S_IFDIR, dev_dir); + if (!param_dir) + return -ENOMEM; + + for (pe = e100_proc_params; pe->name[0]; pe++) { + + data = ((char *) bdp) + pe->offset; + + if (!(create_proc_rw(pe->name, data, param_dir, + pe->read_proc, pe->write_proc))) { + return -ENOMEM; + } + } + + return 0; +} + +static void +remove_proc_param_subdir(struct proc_dir_entry *parent) +{ + struct proc_dir_entry *de; + e100_proc_entry *pe; + int len; + + len = strlen("LoadParameters"); + + for (de = parent->subdir; de; de = de->next) { + if ((de->namelen == len) && + (!memcmp(de->name, "LoadParameters", len))) + break; + } + + if (!de) + return; + + for (pe = e100_proc_params; pe->name[0]; pe++) { + remove_proc_entry(pe->name, de); + } + + remove_proc_entry("LoadParameters", parent); +} +#endif /* E100_EOU */ + +void +e100_remove_proc_subdir(struct e100_private *bdp) +{ + e100_proc_entry *pe; + char info[256]; + int len; + + /* If our root /proc dir was not created, there is nothing to remove */ + if (adapters_proc_dir == NULL) { + return; + } + + len = strlen(bdp->device->name); + strncpy(info, bdp->device->name, sizeof (info)); + strncat(info + len, ".info", sizeof (info) - len); + + if (bdp->proc_parent) { + for (pe = e100_proc_list; pe->name[0]; pe++) { + if (pe->name[0] == '\n') + continue; + + remove_proc_entry(pe->name, bdp->proc_parent); + } + +#ifdef E100_EOU + remove_proc_param_subdir(bdp->proc_parent); +#endif + remove_proc_entry(bdp->device->name, adapters_proc_dir); + bdp->proc_parent = NULL; + } + + remove_proc_entry(info, adapters_proc_dir); + + /* try to remove the main /proc dir, if it's empty */ + e100_proc_cleanup(); +} + +int __devinit +e100_create_proc_subdir(struct e100_private *bdp) +{ + struct proc_dir_entry *dev_dir; + e100_proc_entry *pe; + char info[256]; + int len; + void *data; + + /* create the main /proc dir if needed */ + if (!adapters_proc_dir) { + if (!e100_init_proc_dir()) + return -ENOMEM; + } + + strncpy(info, bdp->device->name, sizeof (info)); + len = strlen(info); + strncat(info + len, ".info", sizeof (info) - len); + + /* info */ + if (!(create_proc_rw(info, bdp, adapters_proc_dir, read_info, 0))) { + e100_proc_cleanup(); + return -ENOMEM; + } + + dev_dir = create_proc_entry(bdp->device->name, S_IFDIR, + adapters_proc_dir); + bdp->proc_parent = dev_dir; + + if (!dev_dir) { + e100_remove_proc_subdir(bdp); + return -ENOMEM; + } + + for (pe = e100_proc_list; pe->name[0]; pe++) { + if (pe->name[0] == '\n') + continue; + + if (pe->offset != ~0) + data = ((char *) bdp) + pe->offset; + else + data = NULL; + + if (!(create_proc_rw(pe->name, data, dev_dir, + pe->read_proc, pe->write_proc))) { + e100_remove_proc_subdir(bdp); + return -ENOMEM; + } + } + +#ifdef E100_EOU + if (create_proc_param_subdir(bdp, dev_dir)) { + e100_remove_proc_subdir(bdp); + return -ENOMEM; + } +#endif + + return 0; +} + +/**************************************************************************** + * Name: e100_init_proc_dir + * + * Description: This routine creates the top-level /proc directory for the + * driver in /proc/net + * + * Arguments: none + * + * Returns: true on success, false on fail + * + ***************************************************************************/ +static unsigned char +e100_init_proc_dir(void) +{ + int len; + + /* first check if adapters_proc_dir already exists */ + len = strlen(ADAPTERS_PROC_DIR); + for (adapters_proc_dir = proc_net->subdir; + adapters_proc_dir; adapters_proc_dir = adapters_proc_dir->next) { + + if ((adapters_proc_dir->namelen == len) && + (!memcmp(adapters_proc_dir->name, ADAPTERS_PROC_DIR, len))) + break; + } + + if (!adapters_proc_dir) + adapters_proc_dir = + create_proc_entry(ADAPTERS_PROC_DIR, S_IFDIR, proc_net); + + if (!adapters_proc_dir) + return false; + + return true; +} + +/**************************************************************************** + * Name: e100_proc_cleanup + * + * Description: This routine clears the top-level /proc directory, if empty. + * + * Arguments: none + * + * Returns: none + * + ***************************************************************************/ +static void +e100_proc_cleanup(void) +{ + struct proc_dir_entry *de; + + if (adapters_proc_dir == NULL) { + return; + } + + /* check if subdir list is empty before removing adapters_proc_dir */ + for (de = adapters_proc_dir->subdir; de; de = de->next) { + /* ignore . and .. */ + if (*(de->name) != '.') + break; + } + + if (de) + return; + + remove_proc_entry(ADAPTERS_PROC_DIR, proc_net); + adapters_proc_dir = NULL; +} + +#endif /* CONFIG_PROC_FS */ diff -Nru a/drivers/net/e100/e100_ucode.h b/drivers/net/e100/e100_ucode.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e100/e100_ucode.h Thu Mar 7 18:17:47 2002 @@ -0,0 +1,411 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. +*******************************************************************************/ + +#ifndef _E100_UCODE_H_ +#define _E100_UCODE_H_ + +/* +e100_ucode.h + +This file contains the loadable micro code arrays to implement receive +bundling on the D101 A-step, D101 B-step, D101M (B-step only), D101S, +D102 B-step, D102 B-step with TCO work around and D102 C-step. + +Each controller has its own specific micro code array. The array for one +controller is totally incompatible with any other controller, and if used +will most likely cause the controller to lock up and stop responding to +the driver. Each micro code array has its own parameter offsets (described +below), and they each have their own version number. +*/ + +/************************************************************************* +* CPUSaver parameters +* +* All CPUSaver parameters are 16-bit literals that are part of a +* "move immediate value" instruction. By changing the value of +* the literal in the instruction before the code is loaded, the +* driver can change algorithm. +* +* CPUSAVER_DWORD - This is the location of the instruction that loads +* the dead-man timer with its inital value. By writing a 16-bit +* value to the low word of this instruction, the driver can change +* the timer value. The current default is either x600 or x800; +* experiments show that the value probably should stay within the +* range of x200 - x1000. +* +* CPUSAVER_BUNDLE_MAX_DWORD - This is the location of the instruction +* that sets the maximum number of frames that will be bundled. In +* some situations, such as the TCP windowing algorithm, it may be +* better to limit the growth of the bundle size than let it go as +* high as it can, because that could cause too much added latency. +* The default is six, because this is the number of packets in the +* default TCP window size. A value of 1 would make CPUSaver indicate +* an interrupt for every frame received. If you do not want to put +* a limit on the bundle size, set this value to xFFFF. +* +* CPUSAVER_MIN_SIZE_DWORD - This is the location of the instruction +* that contains a bit-mask describing the minimum size frame that +* will be bundled. The default masks the lower 7 bits, which means +* that any frame less than 128 bytes in length will not be bundled, +* but will instead immediately generate an interrupt. This does +* not affect the current bundle in any way. Any frame that is 128 +* bytes or large will be bundled normally. This feature is meant +* to provide immediate indication of ACK frames in a TCP environment. +* Customers were seeing poor performance when a machine with CPUSaver +* enabled was sending but not receiving. The delay introduced when +* the ACKs were received was enough to reduce total throughput, because +* the sender would sit idle until the ACK was finally seen. +* +* The current default is 0xFF80, which masks out the lower 7 bits. +* This means that any frame which is x7F (127) bytes or smaller +* will cause an immediate interrupt. Because this value must be a +* bit mask, there are only a few valid values that can be used. To +* turn this feature off, the driver can write the value xFFFF to the +* lower word of this instruction (in the same way that the other +* parameters are used). Likewise, a value of 0xF800 (2047) would +* cause an interrupt to be generated for every frame, because all +* standard Ethernet frames are <= 2047 bytes in length. +*************************************************************************/ + +#ifndef UCODE_MAX_DWORDS +#define UCODE_MAX_DWORDS 134 +#endif + +/********************************************************/ +/* CPUSaver micro code for the D101A */ +/********************************************************/ + +/* Version 2.0 */ + +/* This value is the same for both A and B step of 558. */ + +#define D101_CPUSAVER_TIMER_DWORD 72 +#define D101_CPUSAVER_BUNDLE_DWORD UCODE_MAX_DWORDS +#define D101_CPUSAVER_MIN_SIZE_DWORD UCODE_MAX_DWORDS + +#define D101_A_RCVBUNDLE_UCODE \ +{\ +0x03B301BB, 0x0046FFFF, 0xFFFFFFFF, 0x051DFFFF, 0xFFFFFFFF, 0xFFFFFFFF, \ +0x000C0001, 0x00101212, 0x000C0008, 0x003801BC, \ +0x00000000, 0x00124818, 0x000C1000, 0x00220809, \ +0x00010200, 0x00124818, 0x000CFFFC, 0x003803B5, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x0010009C, 0x0024B81D, 0x00130836, 0x000C0001, \ +0x0026081C, 0x0020C81B, 0x00130824, 0x00222819, \ +0x00101213, 0x00041000, 0x003A03B3, 0x00010200, \ +0x00101B13, 0x00238081, 0x00213049, 0x0038003B, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x0010009C, 0x0024B83E, 0x00130826, 0x000C0001, \ +0x0026083B, 0x00010200, 0x00134824, 0x000C0001, \ +0x00101213, 0x00041000, 0x0038051E, 0x00101313, \ +0x00010400, 0x00380521, 0x00050600, 0x00100824, \ +0x00101310, 0x00041000, 0x00080600, 0x00101B10, \ +0x0038051E, 0x00000000, 0x00000000, 0x00000000 \ +} + +/********************************************************/ +/* CPUSaver micro code for the D101B */ +/********************************************************/ + +/* Version 2.0 */ + +#define D101_B0_RCVBUNDLE_UCODE \ +{\ +0x03B401BC, 0x0047FFFF, 0xFFFFFFFF, 0x051EFFFF, 0xFFFFFFFF, 0xFFFFFFFF, \ +0x000C0001, 0x00101B92, 0x000C0008, 0x003801BD, \ +0x00000000, 0x00124818, 0x000C1000, 0x00220809, \ +0x00010200, 0x00124818, 0x000CFFFC, 0x003803B6, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x0010009C, 0x0024B81D, 0x0013082F, 0x000C0001, \ +0x0026081C, 0x0020C81B, 0x00130837, 0x00222819, \ +0x00101B93, 0x00041000, 0x003A03B4, 0x00010200, \ +0x00101793, 0x00238082, 0x0021304A, 0x0038003C, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x0010009C, 0x0024B83E, 0x00130826, 0x000C0001, \ +0x0026083B, 0x00010200, 0x00134837, 0x000C0001, \ +0x00101B93, 0x00041000, 0x0038051F, 0x00101313, \ +0x00010400, 0x00380522, 0x00050600, 0x00100837, \ +0x00101310, 0x00041000, 0x00080600, 0x00101790, \ +0x0038051F, 0x00000000, 0x00000000, 0x00000000 \ +} + +/********************************************************/ +/* CPUSaver micro code for the D101M (B-step only) */ +/********************************************************/ + +/* Version 2.10.1 */ + +/* Parameter values for the D101M B-step */ +#define D101M_CPUSAVER_TIMER_DWORD 78 +#define D101M_CPUSAVER_BUNDLE_DWORD 65 +#define D101M_CPUSAVER_MIN_SIZE_DWORD 126 + +#define D101M_B_RCVBUNDLE_UCODE \ +{\ +0x00550215, 0xFFFF0437, 0xFFFFFFFF, 0x06A70789, 0xFFFFFFFF, 0x0558FFFF, \ +0x000C0001, 0x00101312, 0x000C0008, 0x00380216, \ +0x0010009C, 0x00204056, 0x002380CC, 0x00380056, \ +0x0010009C, 0x00244C0B, 0x00000800, 0x00124818, \ +0x00380438, 0x00000000, 0x00140000, 0x00380555, \ +0x00308000, 0x00100662, 0x00100561, 0x000E0408, \ +0x00134861, 0x000C0002, 0x00103093, 0x00308000, \ +0x00100624, 0x00100561, 0x000E0408, 0x00100861, \ +0x000C007E, 0x00222C21, 0x000C0002, 0x00103093, \ +0x00380C7A, 0x00080000, 0x00103090, 0x00380C7A, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x0010009C, 0x00244C2D, 0x00010004, 0x00041000, \ +0x003A0437, 0x00044010, 0x0038078A, 0x00000000, \ +0x00100099, 0x00206C7A, 0x0010009C, 0x00244C48, \ +0x00130824, 0x000C0001, 0x00101213, 0x00260C75, \ +0x00041000, 0x00010004, 0x00130826, 0x000C0006, \ +0x002206A8, 0x0013C926, 0x00101313, 0x003806A8, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00080600, 0x00101B10, 0x00050004, 0x00100826, \ +0x00101210, 0x00380C34, 0x00000000, 0x00000000, \ +0x0021155B, 0x00100099, 0x00206559, 0x0010009C, \ +0x00244559, 0x00130836, 0x000C0000, 0x00220C62, \ +0x000C0001, 0x00101B13, 0x00229C0E, 0x00210C0E, \ +0x00226C0E, 0x00216C0E, 0x0022FC0E, 0x00215C0E, \ +0x00214C0E, 0x00380555, 0x00010004, 0x00041000, \ +0x00278C67, 0x00040800, 0x00018100, 0x003A0437, \ +0x00130826, 0x000C0001, 0x00220559, 0x00101313, \ +0x00380559, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00130831, 0x0010090B, 0x00124813, \ +0x000CFF80, 0x002606AB, 0x00041000, 0x00010004, \ +0x003806A8, 0x00000000, 0x00000000, 0x00000000, \ +} + +/********************************************************/ +/* CPUSaver micro code for the D101S */ +/********************************************************/ + +/* Version 1.20.1 */ + +/* Parameter values for the D101S */ +#define D101S_CPUSAVER_TIMER_DWORD 78 +#define D101S_CPUSAVER_BUNDLE_DWORD 67 +#define D101S_CPUSAVER_MIN_SIZE_DWORD 128 + +#define D101S_RCVBUNDLE_UCODE \ +{\ +0x00550242, 0xFFFF047E, 0xFFFFFFFF, 0x06FF0818, 0xFFFFFFFF, 0x05A6FFFF, \ +0x000C0001, 0x00101312, 0x000C0008, 0x00380243, \ +0x0010009C, 0x00204056, 0x002380D0, 0x00380056, \ +0x0010009C, 0x00244F8B, 0x00000800, 0x00124818, \ +0x0038047F, 0x00000000, 0x00140000, 0x003805A3, \ +0x00308000, 0x00100610, 0x00100561, 0x000E0408, \ +0x00134861, 0x000C0002, 0x00103093, 0x00308000, \ +0x00100624, 0x00100561, 0x000E0408, 0x00100861, \ +0x000C007E, 0x00222FA1, 0x000C0002, 0x00103093, \ +0x00380F90, 0x00080000, 0x00103090, 0x00380F90, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x0010009C, 0x00244FAD, 0x00010004, 0x00041000, \ +0x003A047E, 0x00044010, 0x00380819, 0x00000000, \ +0x00100099, 0x00206FFD, 0x0010009A, 0x0020AFFD, \ +0x0010009C, 0x00244FC8, 0x00130824, 0x000C0001, \ +0x00101213, 0x00260FF7, 0x00041000, 0x00010004, \ +0x00130826, 0x000C0006, 0x00220700, 0x0013C926, \ +0x00101313, 0x00380700, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00080600, 0x00101B10, 0x00050004, 0x00100826, \ +0x00101210, 0x00380FB6, 0x00000000, 0x00000000, \ +0x002115A9, 0x00100099, 0x002065A7, 0x0010009A, \ +0x0020A5A7, 0x0010009C, 0x002445A7, 0x00130836, \ +0x000C0000, 0x00220FE4, 0x000C0001, 0x00101B13, \ +0x00229F8E, 0x00210F8E, 0x00226F8E, 0x00216F8E, \ +0x0022FF8E, 0x00215F8E, 0x00214F8E, 0x003805A3, \ +0x00010004, 0x00041000, 0x00278FE9, 0x00040800, \ +0x00018100, 0x003A047E, 0x00130826, 0x000C0001, \ +0x002205A7, 0x00101313, 0x003805A7, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00130831, \ +0x0010090B, 0x00124813, 0x000CFF80, 0x00260703, \ +0x00041000, 0x00010004, 0x00380700 \ +} + +/********************************************************/ +/* CPUSaver micro code for the D102 B-step */ +/********************************************************/ + +/* Version 2.0 */ +/* Parameter values for the D102 B-step */ +#define D102_B_CPUSAVER_TIMER_DWORD 82 +#define D102_B_CPUSAVER_BUNDLE_DWORD 106 +#define D102_B_CPUSAVER_MIN_SIZE_DWORD 70 + +#define D102_B_RCVBUNDLE_UCODE \ +{\ +0x006F0276, 0x0EF71FFF, 0x0ED30F86, 0x0D250ED9, 0x1FFF1FFF, 0x1FFF04D2, \ +0x00300001, 0x0140D871, 0x00300008, 0x00E00277, \ +0x01406C57, 0x00816073, 0x008700FA, 0x00E00070, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x01406CBA, 0x00807F9A, 0x00901F9A, 0x0024FFFF, \ +0x014B6F6F, 0x0030FFFE, 0x01407172, 0x01496FBA, \ +0x014B6F72, 0x00308000, 0x01406C52, 0x00912EFC, \ +0x00E00EF8, 0x00000000, 0x00000000, 0x00000000, \ +0x00906F8C, 0x00900F8C, 0x00E00F87, 0x00000000, \ +0x00906ED8, 0x01406C55, 0x00E00ED4, 0x00000000, \ +0x01406C51, 0x0080DFC2, 0x01406C52, 0x00815FC2, \ +0x01406C57, 0x00917FCC, 0x00E01FDD, 0x00000000, \ +0x00822D30, 0x01406C51, 0x0080CD26, 0x01406C52, \ +0x00814D26, 0x01406C57, 0x00916D26, 0x014C6FD7, \ +0x00300000, 0x00841FD2, 0x00300001, 0x0140D772, \ +0x00E012B3, 0x014C6F91, 0x0150710B, 0x01496F72, \ +0x0030FF80, 0x00940EDD, 0x00102000, 0x00038400, \ +0x00E00EDA, 0x00000000, 0x00000000, 0x00000000, \ +0x01406C57, 0x00917FE9, 0x00001000, 0x00E01FE9, \ +0x00200600, 0x0140D76F, 0x00138400, 0x01406FD8, \ +0x0140D96F, 0x00E01FDD, 0x00038400, 0x00102000, \ +0x00971FD7, 0x00101000, 0x00050200, 0x00E804D2, \ +0x014C6FD8, 0x00300001, 0x00840D26, 0x0140D872, \ +0x00E00D26, 0x014C6FD9, 0x00300001, 0x0140D972, \ +0x00941FBD, 0x00102000, 0x00038400, 0x014C6FD8, \ +0x00300006, 0x00840EDA, 0x014F71D8, 0x0140D872, \ +0x00E00EDA, 0x01496F50, 0x00E004D3, 0x00000000, \ +} + +/********************************************************/ +/* Micro code for the D102 C-step */ +/********************************************************/ + +/* Parameter values for the D102 C-step */ +#define D102_C_CPUSAVER_TIMER_DWORD 46 +#define D102_C_CPUSAVER_BUNDLE_DWORD 74 +#define D102_C_CPUSAVER_MIN_SIZE_DWORD 54 + +#define D102_C_RCVBUNDLE_UCODE \ +{ \ +0x00700279, 0x0E6604E2, 0x02BF0CAE, 0x1508150C, 0x15190E5B, 0x0E840F13, \ +0x00E014D8, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014DC, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014F4, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014E0, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014E7, 0x00000000, 0x00000000, 0x00000000, \ +0x00141000, 0x015D6F0D, 0x00E002C0, 0x00000000, \ +0x00200600, 0x00E0150D, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x0030FF80, 0x00940E6A, 0x00038200, 0x00102000, \ +0x00E00E67, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00906E65, 0x00800E60, 0x00E00E5D, 0x00000000, \ +0x00300006, 0x00E0151A, 0x00000000, 0x00000000, \ +0x00906F19, 0x00900F19, 0x00E00F14, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x01406CBA, 0x00807FDA, 0x00901FDA, 0x0024FFFF, \ +0x014B6F6F, 0x0030FFFE, 0x01407172, 0x01496FBA, \ +0x014B6F72, 0x00308000, 0x01406C52, 0x00912E89, \ +0x00E00E85, 0x00000000, 0x00000000, 0x00000000 \ +} + +/********************************************************/ +/* Micro code for the D102 E-step */ +/********************************************************/ + +/* Parameter values for the D102 E-step */ +#define D102_E_CPUSAVER_TIMER_DWORD 42 +#define D102_E_CPUSAVER_BUNDLE_DWORD 54 +#define D102_E_CPUSAVER_MIN_SIZE_DWORD 46 + +#define D102_E_RCVBUNDLE_UCODE \ +{\ +0x007D028F, 0x0E4204F9, 0x14ED0C85, 0x14FA14E9, 0x1FFF1FFF, 0x1FFF1FFF, \ +0x00E014B9, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014BD, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014D5, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014C1, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014C8, 0x00000000, 0x00000000, 0x00000000, \ +0x00200600, 0x00E014EE, 0x00000000, 0x00000000, \ +0x0030FF80, 0x00940E46, 0x00038200, 0x00102000, \ +0x00E00E43, 0x00000000, 0x00000000, 0x00000000, \ +0x00300006, 0x00E014FB, 0x00000000, 0x00000000 \ +} + +#endif /* _E100_UCODE_H_ */ diff -Nru a/drivers/net/e100/e100_vendor.h b/drivers/net/e100/e100_vendor.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e100/e100_vendor.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,348 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. +*******************************************************************************/ + +#ifndef E100_VENDOR_ID_INFO +#define E100_VENDOR_ID_INFO +/* ====================================================================== */ +/* vendor_info */ +/* ====================================================================== */ + +struct e100_vendor_info { + unsigned long device_type; + char *idstr; +}; + +enum e100_device_type { + E100_BRD_100TX = 1, + E100_BRD_100T4, + E100_BRD_10T, + E100_BRD_100WFM, + E100_BRD_82557, + E100_BRD_82557_WOL, + E100_BRD_82558, + E100_BRD_82558_WOL, + E100_BRD_100, + E100_BRD_100M, + E100_BRD_AOL2, + E100_BRD_AOL, + E100_PROS_M, + E100_PROS_AM, + E100_PROS_AM_AOL, + E100_PROS_DT, + E100_PRO_DT, + E100_PROM_DT, + E100_PRO_SRV, + E100_PRO_SRVP, + E100_PROS_SRV, + E100_PRO_DUAL, + E100_PROS_DUAL, + E100_PROP_DUAL, + E100_PROP_WOL, + E100_PROS_MOB, + E100_PRO_CB, + E100_PRO_CB_M, + E100_PROSR_MOB, + E100_PROS_MC, + E100_PROSR_MC, + E100_PROP_MC, + E100_PROSP_MC, + E100_PROP_MOB, + E100_PROSP_MOB, + E100_PRO_MINI, + E100_PRO_NET, + E100_PROS_NET, + E100_PROVM_NET, + E100_PROVE_D, + E100_82559_LOM, + E100_82559_LOM_AOL, + E100_82559_LOM_AOL2, + E100_IBM_MDS, + E100_CMPQ_S, + E100_PROVE_DA, + E100_PROVM_DA, + E100_PROVE_LOM, + E100_PROVE_NET, + E100_82562, + E100_ALL_BOARDS, +}; + +struct e100_vendor_info e100_vendor_info_array[] = { + { E100_BRD_100TX, "Intel(R) PRO/100B PCI Adapter (TX)"}, + { E100_BRD_100T4, "Intel(R) PRO/100B PCI Adapter (T4)"}, + { E100_BRD_10T, "Intel(R) PRO/10+ PCI Adapter"}, + { E100_BRD_100WFM, "Intel(R) PRO/100 WfM PCI Adapter"}, + { E100_BRD_82557, "Intel(R) 82557-based Integrated Ethernet PCI (10/100)"}, + { E100_BRD_82557_WOL, "Intel(R) 82557-based Integrated Ethernet with Wake on LAN*"}, + { E100_BRD_82558, "Intel(R) 82558-based Integrated Ethernet"}, + { E100_BRD_82558_WOL, "Intel(R) 82558-based Integrated Ethernet with Wake on LAN*"}, + { E100_BRD_100, "Intel(R) PRO/100+ PCI Adapter"}, + { E100_BRD_100M, "Intel(R) PRO/100+ Management Adapter"}, + { E100_BRD_AOL2, "Intel(R) PRO/100+ Alert on LAN* 2 Management Adapter"}, + { E100_BRD_AOL, "Intel(R) PRO/100+ Alert on LAN* Management Adapter"}, + { E100_PROS_M, "Intel(R) PRO/100 S Management Adapter"}, + { E100_PROS_AM, "Intel(R) PRO/100 S Advanced Management Adapter"}, + { E100_PROS_AM_AOL, "Intel(R) PRO/100+ Management Adapter with Alert On LAN* GC"}, + { E100_PROS_DT, "Intel(R) PRO/100 S Desktop Adapter"}, + { E100_PRO_DT, "Intel(R) PRO/100 Desktop Adapter"}, + { E100_PROM_DT, "Intel(R) PRO/100 M Desktop Adapter"}, + { E100_PRO_SRV, "Intel(R) PRO/100+ Server Adapter"}, + { E100_PRO_SRVP, "Intel(R) PRO/100+ Server Adapter (PILA8470B)"}, + { E100_PROS_SRV, "Intel(R) PRO/100 S Server Adapter"}, + { E100_PRO_DUAL, "Intel(R) PRO/100 Dual Port Server Adapter"}, + { E100_PROS_DUAL, "Intel(R) PRO/100 S Dual Port Server Adapter"}, + { E100_PROP_DUAL, "Intel(R) PRO/100+ Dual Port Server Adapter"}, + { E100_PROP_WOL, "Intel(R) PRO/100+ Management Adapter with Alert On LAN* G Server"}, + { E100_PROS_MOB, "Intel(R) PRO/100 S Mobile Adapter"}, + { E100_PRO_CB, "Intel(R) PRO/100 CardBus II"}, + { E100_PRO_CB_M, "Intel(R) PRO/100 LAN+Modem56 CardBus II"}, + { E100_PROSR_MOB, "Intel(R) PRO/100 SR Mobile Adapter"}, + { E100_PROS_MC, "Intel(R) PRO/100 S Mobile Combo Adapter"}, + { E100_PROSR_MC, "Intel(R) PRO/100 SR Mobile Combo Adapter"}, + { E100_PROP_MC, "Intel(R) PRO/100 P Mobile Combo Adapter"}, + { E100_PROSP_MC, "Intel(R) PRO/100 SP Mobile Combo Adapter"}, + { E100_PROP_MOB, "Intel(R) PRO/100 P Mobile Adapter"}, + { E100_PROSP_MOB, "Intel(R) PRO/100 SP Mobile Adapter"}, + { E100_PRO_MINI, "Intel(R) PRO/100+ Mini PCI"}, + { E100_PRO_NET, "Intel(R) PRO/100 Network Connection" }, + { E100_PROS_NET, "Intel(R) PRO/100 S Network Connection" }, + { E100_PROVM_NET, "Intel(R) PRO/100 VM Network Connection"}, + { E100_PROVE_D, "Intel(R) PRO/100 VE Desktop Connection"}, + { E100_82559_LOM, "Intel(R) 82559 Fast Ethernet LAN on Motherboard"}, + { E100_82559_LOM_AOL, "Intel(R) 82559 Fast Ethernet LOM with Alert on LAN*" }, + { E100_82559_LOM_AOL2, "Intel(R) 82559 Fast Ethernet LOM with Alert on LAN* 2" }, + { E100_IBM_MDS, "IBM Mobile, Desktop & Server Adapters"}, + { E100_CMPQ_S, "Compaq Fast Ethernet Server Adapter" }, + { E100_PROVE_DA, "Intel(R) PRO/100 VE Desktop Adapter"}, + { E100_PROVM_DA, "Intel(R) PRO/100 VM Desktop Adapter"}, + { E100_PROVE_LOM, "Intel(R) PRO/100 VE Network ConnectionPLC LOM" }, + { E100_PROVE_NET, "Intel(R) PRO/100 VE Network Connection"}, + { E100_82562, "Intel(R)82562 based Fast Ethernet Connection"}, + { E100_ALL_BOARDS, "Intel(R) 8255x-based Ethernet Adapter"}, + {0,NULL} +}; + +static struct pci_device_id e100_id_table[] __devinitdata = { + {0x8086, 0x1229, 0x8086, 0x0001, 0, 0, E100_BRD_100TX}, + {0x8086, 0x1229, 0x8086, 0x0002, 0, 0, E100_BRD_100T4}, + {0x8086, 0x1229, 0x8086, 0x0003, 0, 0, E100_BRD_10T}, + {0x8086, 0x1229, 0x8086, 0x0004, 0, 0, E100_BRD_100WFM}, + {0x8086, 0x1229, 0x8086, 0x0005, 0, 0, E100_BRD_82557}, + {0x8086, 0x1229, 0x8086, 0x0006, 0, 0, E100_BRD_82557_WOL}, + {0x8086, 0x1229, 0x8086, 0x0002, 0, 0, E100_BRD_100T4}, + {0x8086, 0x1229, 0x8086, 0x0003, 0, 0, E100_BRD_10T}, + {0x8086, 0x1229, 0x8086, 0x0004, 0, 0, E100_BRD_100WFM}, + {0x8086, 0x1229, 0x8086, 0x0005, 0, 0, E100_BRD_82557}, + {0x8086, 0x1229, 0x8086, 0x0006, 0, 0, E100_BRD_82557_WOL}, + {0x8086, 0x1229, 0x8086, 0x0007, 0, 0, E100_BRD_82558}, + {0x8086, 0x1229, 0x8086, 0x0008, 0, 0, E100_BRD_82558_WOL}, + {0x8086, 0x1229, 0x8086, 0x0009, 0, 0, E100_BRD_100}, + {0x8086, 0x1229, 0x8086, 0x000A, 0, 0, E100_BRD_100M}, + {0x8086, 0x1229, 0x8086, 0x000B, 0, 0, E100_BRD_100}, + {0x8086, 0x1229, 0x8086, 0x000C, 0, 0, E100_BRD_100M}, + {0x8086, 0x1229, 0x8086, 0x000D, 0, 0, E100_BRD_AOL2}, + {0x8086, 0x1229, 0x8086, 0x000E, 0, 0, E100_BRD_AOL}, + {0x8086, 0x1229, 0x8086, 0x0010, 0, 0, E100_PROS_M}, + {0x8086, 0x1229, 0x8086, 0x0011, 0, 0, E100_PROS_M}, + {0x8086, 0x1229, 0x8086, 0x0012, 0, 0, E100_PROS_AM}, + {0x8086, 0x1229, 0x8086, 0x0013, 0, 0, E100_PROS_AM}, + {0x8086, 0x1229, 0x8086, 0x0030, 0, 0, E100_PROS_AM_AOL}, + {0x8086, 0x1229, 0x8086, 0x0040, 0, 0, E100_PROS_DT}, + {0x8086, 0x1229, 0x8086, 0x0041, 0, 0, E100_PROS_DT}, + {0x8086, 0x1229, 0x8086, 0x0042, 0, 0, E100_PRO_DT}, + {0x8086, 0x1229, 0x8086, 0x0050, 0, 0, E100_PROS_DT}, + {0x8086, 0x1229, 0x8086, 0x0070, 0, 0, E100_PROM_DT}, + {0x8086, 0x1229, 0x8086, 0x1009, 0, 0, E100_PRO_SRV}, + {0x8086, 0x1229, 0x8086, 0x100C, 0, 0, E100_PRO_SRVP}, + {0x8086, 0x1229, 0x8086, 0x1012, 0, 0, E100_PROS_SRV}, + {0x8086, 0x1229, 0x8086, 0x1013, 0, 0, E100_PROS_SRV}, + {0x8086, 0x1229, 0x8086, 0x1014, 0, 0, E100_PRO_DUAL}, + {0x8086, 0x1229, 0x8086, 0x1015, 0, 0, E100_PROS_DUAL}, + {0x8086, 0x1229, 0x8086, 0x1016, 0, 0, E100_PROS_DUAL}, + {0x8086, 0x1229, 0x8086, 0x1017, 0, 0, E100_PROP_DUAL}, + {0x8086, 0x1229, 0x8086, 0x1030, 0, 0, E100_PROP_WOL}, + {0x8086, 0x1229, 0x8086, 0x1040, 0, 0, E100_PROS_SRV}, + {0x8086, 0x1229, 0x8086, 0x1041, 0, 0, E100_PROS_SRV}, + {0x8086, 0x1229, 0x8086, 0x1042, 0, 0, E100_PRO_SRV}, + {0x8086, 0x1229, 0x8086, 0x1050, 0, 0, E100_PROS_SRV}, + {0x8086, 0x1229, 0x8086, 0x10F0, 0, 0, E100_PROP_DUAL}, + {0x8086, 0x1229, 0x8086, 0x10F0, 0, 0, E100_PROP_DUAL}, + {0x8086, 0x1229, 0x8086, 0x2009, 0, 0, E100_PROS_MOB}, + {0x8086, 0x1229, 0x8086, 0x200D, 0, 0, E100_PRO_CB}, + {0x8086, 0x1229, 0x8086, 0x200E, 0, 0, E100_PRO_CB_M}, + {0x8086, 0x1229, 0x8086, 0x200F, 0, 0, E100_PROSR_MOB}, + {0x8086, 0x1229, 0x8086, 0x2010, 0, 0, E100_PROS_MC}, + {0x8086, 0x1229, 0x8086, 0x2013, 0, 0, E100_PROSR_MC}, + {0x8086, 0x1229, 0x8086, 0x2016, 0, 0, E100_PROS_MOB}, + {0x8086, 0x1229, 0x8086, 0x2017, 0, 0, E100_PROS_MC}, + {0x8086, 0x1229, 0x8086, 0x2018, 0, 0, E100_PROSR_MOB}, + {0x8086, 0x1229, 0x8086, 0x2019, 0, 0, E100_PROSR_MC}, + {0x8086, 0x1229, 0x8086, 0x2101, 0, 0, E100_PROP_MOB}, + {0x8086, 0x1229, 0x8086, 0x2102, 0, 0, E100_PROSP_MOB}, + {0x8086, 0x1229, 0x8086, 0x2103, 0, 0, E100_PROSP_MOB}, + {0x8086, 0x1229, 0x8086, 0x2104, 0, 0, E100_PROSP_MOB}, + {0x8086, 0x1229, 0x8086, 0x2105, 0, 0, E100_PROSP_MOB}, + {0x8086, 0x1229, 0x8086, 0x2106, 0, 0, E100_PROP_MOB}, + {0x8086, 0x1229, 0x8086, 0x2107, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x8086, 0x2108, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x8086, 0x2200, 0, 0, E100_PROP_MC}, + {0x8086, 0x1229, 0x8086, 0x2201, 0, 0, E100_PROP_MC}, + {0x8086, 0x1229, 0x8086, 0x2202, 0, 0, E100_PROSP_MC}, + {0x8086, 0x1229, 0x8086, 0x2203, 0, 0, E100_PRO_MINI}, + {0x8086, 0x1229, 0x8086, 0x2204, 0, 0, E100_PRO_MINI}, + {0x8086, 0x1229, 0x8086, 0x2205, 0, 0, E100_PROSP_MC}, + {0x8086, 0x1229, 0x8086, 0x2206, 0, 0, E100_PROSP_MC}, + {0x8086, 0x1229, 0x8086, 0x2207, 0, 0, E100_PROSP_MC}, + {0x8086, 0x1229, 0x8086, 0x2208, 0, 0, E100_PROP_MC}, + {0x8086, 0x1229, 0x8086, 0x2408, 0, 0, E100_PRO_MINI}, + {0x8086, 0x1229, 0x8086, 0x240F, 0, 0, E100_PRO_MINI}, + {0x8086, 0x1229, 0x8086, 0x2411, 0, 0, E100_PRO_MINI}, + {0x8086, 0x1229, 0x8086, 0x3400, 0, 0, E100_82559_LOM}, + {0x8086, 0x1229, 0x8086, 0x3000, 0, 0, E100_82559_LOM}, + {0x8086, 0x1229, 0x8086, 0x3001, 0, 0, E100_82559_LOM_AOL}, + {0x8086, 0x1229, 0x8086, 0x3002, 0, 0, E100_82559_LOM_AOL2}, + {0x8086, 0x1229, 0x8086, 0x3006, 0, 0, E100_PROS_NET}, + {0x8086, 0x1229, 0x8086, 0x3007, 0, 0, E100_PROS_NET}, + {0x8086, 0x1229, 0x8086, 0x3008, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x8086, 0x3010, 0, 0, E100_PROS_NET}, + {0x8086, 0x1229, 0x8086, 0x3011, 0, 0, E100_PROS_NET}, + {0x8086, 0x1229, 0x8086, 0x3012, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x005C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x305C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x405C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x605C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x505C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x105C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x805C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x705C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x01F1, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x0232, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x0207, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x023F, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x01BC, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x01CE, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x01DC, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x01EB, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x01EC, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x0202, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x0205, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x0217, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x0E11, 0xB01E, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB02F, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB04A, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB0C6, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB0C7, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB0D7, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB0DD, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB0DE, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB0E1, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB134, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB13C, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB144, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB163, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB164, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_ALL_BOARDS}, + + {0x8086, 0x2449, 0x1014, 0x0265, 0, 0, E100_PROVE_D}, + {0x8086, 0x2449, 0x1014, 0x0267, 0, 0, E100_PROVE_D}, + {0x8086, 0x2449, 0x1014, 0x026A, 0, 0, E100_PROVE_D}, + {0x8086, 0x2449, 0x8086, 0x3010, 0, 0, E100_PROVE_DA}, + {0x8086, 0x2449, 0x8086, 0x3011, 0, 0, E100_PROVM_DA}, + {0x8086, 0x2449, 0x8086, 0x3013, 0, 0, E100_PROVE_NET}, + {0x8086, 0x2449, 0x8086, 0x3014, 0, 0, E100_PROVM_NET}, + {0x8086, 0x2449, 0x8086, 0x3016, 0, 0, E100_PROP_MC}, + {0x8086, 0x2449, 0x8086, 0x3017, 0, 0, E100_PROP_MOB}, + {0x8086, 0x2449, 0x8086, 0x3018, 0, 0, E100_PRO_NET}, + {0x8086, 0x2449, 0x0E11, PCI_ANY_ID, 0, 0, E100_PROVM_NET}, + {0x8086, 0x2449, 0x1014, PCI_ANY_ID, 0, 0, E100_PROVE_D}, + {0x8086, 0x2449, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_ALL_BOARDS}, + + {0x8086, 0x1209, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_ALL_BOARDS}, + {0x8086, 0x1029, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_ALL_BOARDS}, + {0x8086, 0x1030, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_ALL_BOARDS}, + {0x8086, 0x1031, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVE_NET}, + {0x8086, 0x1032, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVE_NET}, + {0x8086, 0x1033, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET}, + {0x8086, 0x1034, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET}, + {0x8086, 0x1038, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET}, + {0x8086, 0x1039, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVE_NET}, + {0x8086, 0x103A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVE_NET}, + {0x8086, 0x103B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET}, + {0x8086, 0x103C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET}, + {0x8086, 0x103D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVE_NET}, + {0x8086, 0x103E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET}, + {0x8086, 0x2459, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_82562}, + {0x8086, 0x245D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_82562}, + {0,} /* This has to be the last entry*/ +}; + +#endif /* E100_VENDOR_ID_INFO */ diff -Nru a/drivers/net/e1000/LICENSE b/drivers/net/e1000/LICENSE --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e1000/LICENSE Thu Mar 7 18:17:47 2002 @@ -0,0 +1,69 @@ +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/1000 Family of Adapters (e1000) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. diff -Nru a/drivers/net/e1000/Makefile b/drivers/net/e1000/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e1000/Makefile Thu Mar 7 18:17:46 2002 @@ -0,0 +1,16 @@ +# +# Makefile for the Intel(R) PRO/1000 ethernet driver +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := e1000.o + +obj-y := e1000_main.o e1000_mac.o e1000_phy.o \ + e1000_ethtool.o e1000_param.o e1000_proc.o +obj-m := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff -Nru a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e1000/e1000.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,230 @@ +/******************************************************************************* + + This software program is available to you under a choice of one of two + licenses. You may choose to be licensed under either the GNU General Public + License (GPL) Version 2, June 1991, available at + http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the + text of which follows: + + Recipient has requested a license and Intel Corporation ("Intel") is willing + to grant a license for the software entitled Linux Base Driver for the + Intel(R) PRO/1000 Family of Adapters (e1000) (the "Software") being provided + by Intel Corporation. The following definitions apply to this license: + + "Licensed Patents" means patent claims licensable by Intel Corporation which + are necessarily infringed by the use of sale of the Software alone or when + combined with the operating system referred to below. + + "Recipient" means the party to whom Intel delivers this Software. + + "Licensee" means Recipient and those third parties that receive a license to + any operating system available under the GNU Public License version 2.0 or + later. + + Copyright (c) 1999 - 2002 Intel Corporation. + All rights reserved. + + The license is provided to Recipient and Recipient's Licensees under the + following terms. + + Redistribution and use in source and binary forms of the Software, with or + without modification, are permitted provided that the following conditions + are met: + + Redistributions of source code of the Software may retain the above + copyright notice, this list of conditions and the following disclaimer. + + Redistributions in binary form of the Software may reproduce the above + copyright notice, this list of conditions and the following disclaimer in + the documentation and/or materials provided with the distribution. + + Neither the name of Intel Corporation nor the names of its contributors + shall be used to endorse or promote products derived from this Software + without specific prior written permission. + + Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, + royalty-free patent license under Licensed Patents to make, use, sell, offer + to sell, import and otherwise transfer the Software, if any, in source code + and object code form. This license shall include changes to the Software + that are error corrections or other minor changes to the Software that do + not add functionality or features when the Software is incorporated in any + version of an operating system that has been distributed under the GNU + General Public License 2.0 or later. This patent license shall apply to the + combination of the Software and any operating system licensed under the GNU + Public License version 2.0 or later if, at the time Intel provides the + Software to Recipient, such addition of the Software to the then publicly + available versions of such operating systems available under the GNU Public + License version 2.0 or later (whether in gold, beta or alpha form) causes + such combination to be covered by the Licensed Patents. The patent license + shall not apply to any other combinations which include the Software. NO + hardware per se is licensed hereunder. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. + +*******************************************************************************/ + + +/* Linux PRO/1000 Ethernet Driver main header file */ + +#ifndef _E1000_H_ +#define _E1000_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct e1000_adapter; + +#include "e1000_mac.h" +#include "e1000_phy.h" + +#define BAR_0 0 + +#if DBG +#define E1000_DBG(args...) printk(KERN_DEBUG "e1000: " args) +#else +#define E1000_DBG(args...) +#endif + +#define E1000_ERR(args...) printk(KERN_ERR "e1000: " args) + +#ifdef CONFIG_PPC +#define E1000_MAX_INTR 1 +#else +#define E1000_MAX_INTR 10 +#endif + +/* Supported Rx Buffer Sizes */ +#define E1000_RXBUFFER_2048 2048 +#define E1000_RXBUFFER_4096 4096 +#define E1000_RXBUFFER_8192 8192 +#define E1000_RXBUFFER_16384 16384 + +/* How many Tx Descriptors do we need to call netif_wake_queue ? */ +#define E1000_TX_QUEUE_WAKE 16 + +#define E1000_JUMBO_PBA 0x00000028 +#define E1000_DEFAULT_PBA 0x00000030 + +/* only works for sizes that are powers of 2 */ +#define E1000_ROUNDUP(i, size) ((i) = (((i) + (size) - 1) & ~((size) - 1))) + +/* wrapper around a pointer to a socket buffer, + * so a DMA handle can be stored along with the buffer */ +struct e1000_buffer { + struct sk_buff *skb; + uint64_t dma; + unsigned long length; +}; + +struct e1000_desc_ring { + /* pointer to the descriptor ring memory */ + void *desc; + /* physical address of the descriptor ring */ + dma_addr_t dma; + /* length of descriptor ring in bytes */ + unsigned int size; + /* number of descriptors in the ring */ + unsigned int count; + /* (atomic) number of desc with no buffer */ + atomic_t unused; + /* number of desc with no buffer */ + unsigned int unused_count; + /* next descriptor to associate a buffer with */ + unsigned int next_to_use; + /* next descriptor to check for DD status bit */ + unsigned int next_to_clean; + /* array of buffer information structs */ + struct e1000_buffer *buffer_info; +}; + +#define E1000_RX_DESC(ring, i) \ + (&(((struct e1000_rx_desc *)((ring).desc))[i])) + +#define E1000_TX_DESC(ring, i) \ + (&(((struct e1000_tx_desc *)((ring).desc))[i])) + +#define E1000_CONTEXT_DESC(ring, i) \ + (&(((struct e1000_context_desc *)((ring).desc))[i])) + +/* board specific private data structure */ + +struct e1000_adapter { + struct timer_list watchdog_timer; + struct timer_list phy_info_timer; +#ifdef CONFIG_PROC_FS + struct list_head proc_list_head; +#endif + char *id_string; + uint32_t bd_number; + uint32_t rx_buffer_len; + uint32_t part_num; + uint32_t wol; + uint16_t link_speed; + uint16_t link_duplex; + spinlock_t stats_lock; + atomic_t irq_sem; + boolean_t rx_csum; + + /* TX */ + struct e1000_desc_ring tx_ring; + unsigned long trans_finish; + uint32_t tx_int_delay; + uint32_t txd_cmd; + + /* RX */ + struct e1000_desc_ring rx_ring; + uint64_t hw_csum_err; + uint64_t hw_csum_good; + uint32_t rx_int_delay; + + /* OS defined structs */ + struct net_device *netdev; + struct pci_dev *pdev; + struct net_device_stats net_stats; + + /* structs defined in e1000_mac.h or e1000_phy.h */ + struct e1000_shared_adapter shared; + struct e1000_shared_stats stats; + struct e1000_phy_info phy_info; + struct e1000_phy_stats phy_stats; +}; + +#endif /* _E1000_H_ */ diff -Nru a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e1000/e1000_ethtool.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,377 @@ +/******************************************************************************* + + This software program is available to you under a choice of one of two + licenses. You may choose to be licensed under either the GNU General Public + License (GPL) Version 2, June 1991, available at + http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the + text of which follows: + + Recipient has requested a license and Intel Corporation ("Intel") is willing + to grant a license for the software entitled Linux Base Driver for the + Intel(R) PRO/1000 Family of Adapters (e1000) (the "Software") being provided + by Intel Corporation. The following definitions apply to this license: + + "Licensed Patents" means patent claims licensable by Intel Corporation which + are necessarily infringed by the use of sale of the Software alone or when + combined with the operating system referred to below. + + "Recipient" means the party to whom Intel delivers this Software. + + "Licensee" means Recipient and those third parties that receive a license to + any operating system available under the GNU Public License version 2.0 or + later. + + Copyright (c) 1999 - 2002 Intel Corporation. + All rights reserved. + + The license is provided to Recipient and Recipient's Licensees under the + following terms. + + Redistribution and use in source and binary forms of the Software, with or + without modification, are permitted provided that the following conditions + are met: + + Redistributions of source code of the Software may retain the above + copyright notice, this list of conditions and the following disclaimer. + + Redistributions in binary form of the Software may reproduce the above + copyright notice, this list of conditions and the following disclaimer in + the documentation and/or materials provided with the distribution. + + Neither the name of Intel Corporation nor the names of its contributors + shall be used to endorse or promote products derived from this Software + without specific prior written permission. + + Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, + royalty-free patent license under Licensed Patents to make, use, sell, offer + to sell, import and otherwise transfer the Software, if any, in source code + and object code form. This license shall include changes to the Software + that are error corrections or other minor changes to the Software that do + not add functionality or features when the Software is incorporated in any + version of an operating system that has been distributed under the GNU + General Public License 2.0 or later. This patent license shall apply to the + combination of the Software and any operating system licensed under the GNU + Public License version 2.0 or later if, at the time Intel provides the + Software to Recipient, such addition of the Software to the then publicly + available versions of such operating systems available under the GNU Public + License version 2.0 or later (whether in gold, beta or alpha form) causes + such combination to be covered by the Licensed Patents. The patent license + shall not apply to any other combinations which include the Software. NO + hardware per se is licensed hereunder. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. + +*******************************************************************************/ + +/* ethtool support for e1000 */ + +#include "e1000.h" + +#include +#include + +extern char e1000_driver_name[]; +extern char e1000_driver_version[]; + +extern int e1000_up(struct e1000_adapter *adapter); +extern void e1000_down(struct e1000_adapter *adapter); +extern void e1000_enable_WOL(struct e1000_adapter *adapter); + +static void +e1000_ethtool_gset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd) +{ + struct e1000_shared_adapter *shared = &adapter->shared; + + if(shared->media_type == e1000_media_type_copper) { + + ecmd->supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full| + SUPPORTED_Autoneg | + SUPPORTED_TP); + + ecmd->advertising = ADVERTISED_TP; + + if(shared->autoneg == 1) { + ecmd->advertising |= ADVERTISED_Autoneg; + + /* the e1000 autoneg seems to match ethtool nicely */ + + ecmd->advertising |= shared->autoneg_advertised; + } + + ecmd->port = PORT_TP; + ecmd->phy_address = shared->phy_addr; + + if(shared->mac_type == e1000_82543) + ecmd->transceiver = XCVR_EXTERNAL; + else + ecmd->transceiver = XCVR_INTERNAL; + + } else { + ecmd->supported = (SUPPORTED_1000baseT_Full | + SUPPORTED_FIBRE | + SUPPORTED_Autoneg); + + ecmd->advertising = (SUPPORTED_1000baseT_Full | + SUPPORTED_FIBRE | + SUPPORTED_Autoneg); + + ecmd->port = PORT_FIBRE; + + ecmd->transceiver = XCVR_EXTERNAL; + } + + if(netif_carrier_ok(adapter->netdev)) { + + e1000_get_speed_and_duplex(shared, &adapter->link_speed, + &adapter->link_duplex); + ecmd->speed = adapter->link_speed; + + /* unfortunatly FULL_DUPLEX != DUPLEX_FULL + * and HALF_DUPLEX != DUPLEX_HALF */ + + if(adapter->link_duplex == FULL_DUPLEX) + ecmd->duplex = DUPLEX_FULL; + else + ecmd->duplex = DUPLEX_HALF; + } else { + ecmd->speed = -1; + ecmd->duplex = -1; + } + + ecmd->autoneg = (shared->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE); + + return; +} + +static int +e1000_ethtool_sset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd) +{ + struct e1000_shared_adapter *shared = &adapter->shared; + + if(ecmd->autoneg == AUTONEG_ENABLE) { + shared->autoneg = 1; + shared->autoneg_advertised = (ecmd->advertising & 0x002F); + } else { + shared->autoneg = 0; + switch(ecmd->speed + ecmd->duplex) { + case SPEED_10 + DUPLEX_HALF: + shared->forced_speed_duplex = e1000_10_half; + break; + case SPEED_10 + DUPLEX_FULL: + shared->forced_speed_duplex = e1000_10_full; + break; + case SPEED_100 + DUPLEX_HALF: + shared->forced_speed_duplex = e1000_100_half; + break; + case SPEED_100 + DUPLEX_FULL: + shared->forced_speed_duplex = e1000_100_full; + break; + case SPEED_1000 + DUPLEX_FULL: + shared->autoneg = 1; + shared->autoneg_advertised = ADVERTISE_1000_FULL; + break; + case SPEED_1000 + DUPLEX_HALF: /* not supported */ + default: + return -EINVAL; + } + } + + /* reset the link */ + + e1000_down(adapter); + e1000_up(adapter); + + return 0; +} + +static inline int +e1000_eeprom_size(struct e1000_shared_adapter *shared) +{ + return 128; +} + +static void +e1000_ethtool_gdrvinfo(struct e1000_adapter *adapter, + struct ethtool_drvinfo *drvinfo) +{ + strncpy(drvinfo->driver, e1000_driver_name, 32); + strncpy(drvinfo->version, e1000_driver_version, 32); + strncpy(drvinfo->fw_version, "", 32); + strncpy(drvinfo->bus_info, adapter->pdev->slot_name, 32); + drvinfo->eedump_len = e1000_eeprom_size(&adapter->shared); + return; +} + +static void +e1000_ethtool_geeprom(struct e1000_adapter *adapter, + struct ethtool_eeprom *eeprom, uint16_t *eeprom_buff) +{ + struct e1000_shared_adapter *shared = &adapter->shared; + int i, max_len; + + eeprom->magic = shared->vendor_id | (shared->device_id << 16); + + max_len = e1000_eeprom_size(shared); + + if ((eeprom->offset + eeprom->len) > max_len) + eeprom->len = (max_len - eeprom->offset); + + for(i = 0; i < max_len; i++) + eeprom_buff[i] = e1000_read_eeprom(&adapter->shared, i); + + return; +} + +static void +e1000_ethtool_gwol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol) +{ + struct e1000_shared_adapter *shared = &adapter->shared; + + if(shared->mac_type < e1000_82544) { + wol->supported = 0; + wol->wolopts = 0; + return; + } + + wol->supported = WAKE_PHY | WAKE_UCAST | + WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC; + + wol->wolopts = 0; + if(adapter->wol & E1000_WUFC_LNKC) + wol->wolopts |= WAKE_PHY; + if(adapter->wol & E1000_WUFC_EX) + wol->wolopts |= WAKE_UCAST; + if(adapter->wol & E1000_WUFC_MC) + wol->wolopts |= WAKE_MCAST; + if(adapter->wol & E1000_WUFC_BC) + wol->wolopts |= WAKE_BCAST; + if(adapter->wol & E1000_WUFC_MAG) + wol->wolopts |= WAKE_MAGIC; + + return; +} + +static int +e1000_ethtool_swol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol) +{ + struct e1000_shared_adapter *shared = &adapter->shared; + + if(shared->mac_type < e1000_82544) + return wol->wolopts == 0 ? 0 : -EOPNOTSUPP; + + adapter->wol = 0; + + if(wol->wolopts & WAKE_PHY) + adapter->wol |= E1000_WUFC_LNKC; + if(wol->wolopts & WAKE_UCAST) + adapter->wol |= E1000_WUFC_EX; + if(wol->wolopts & WAKE_MCAST) + adapter->wol |= E1000_WUFC_MC; + if(wol->wolopts & WAKE_BCAST) + adapter->wol |= E1000_WUFC_BC; + if(wol->wolopts & WAKE_MAGIC) + adapter->wol |= E1000_WUFC_MAG; + + e1000_enable_WOL(adapter); + return 0; +} + +int +e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr) +{ + struct e1000_adapter *adapter = netdev->priv; + void *addr = ifr->ifr_data; + uint32_t cmd; + + if(get_user(cmd, (uint32_t *) addr)) + return -EFAULT; + + switch(cmd) { + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd = {ETHTOOL_GSET}; + e1000_ethtool_gset(adapter, &ecmd); + if(copy_to_user(addr, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + } + case ETHTOOL_SSET: { + struct ethtool_cmd ecmd; + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if(copy_from_user(&ecmd, addr, sizeof(ecmd))) + return -EFAULT; + return e1000_ethtool_sset(adapter, &ecmd); + } + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo drvinfo = {ETHTOOL_GDRVINFO}; + e1000_ethtool_gdrvinfo(adapter, &drvinfo); + if(copy_to_user(addr, &drvinfo, sizeof(drvinfo))) + return -EFAULT; + return 0; + } + case ETHTOOL_NWAY_RST: { + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + e1000_down(adapter); + e1000_up(adapter); + return 0; + } + case ETHTOOL_GLINK: { + struct ethtool_value link = {ETHTOOL_GLINK}; + link.data = netif_carrier_ok(netdev); + if(copy_to_user(addr, &link, sizeof(link))) + return -EFAULT; + return 0; + } + case ETHTOOL_GWOL: { + struct ethtool_wolinfo wol = {ETHTOOL_GWOL}; + e1000_ethtool_gwol(adapter, &wol); + if(copy_to_user(addr, &wol, sizeof(wol)) != 0) + return -EFAULT; + return 0; + } + case ETHTOOL_SWOL: { + struct ethtool_wolinfo wol; + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if(copy_from_user(&wol, addr, sizeof(wol)) != 0) + return -EFAULT; + return e1000_ethtool_swol(adapter, &wol); + } + case ETHTOOL_GEEPROM: { + struct ethtool_eeprom eeprom = {ETHTOOL_GEEPROM}; + uint16_t eeprom_buff[256]; + + if(copy_from_user(&eeprom, addr, sizeof(eeprom))) + return -EFAULT; + + e1000_ethtool_geeprom(adapter, &eeprom, eeprom_buff); + + if(copy_to_user(addr, &eeprom, sizeof(eeprom))) + return -EFAULT; + + addr += offsetof(struct ethtool_eeprom, data); + + if(copy_to_user(addr, eeprom_buff + eeprom.offset, eeprom.len)) + return -EFAULT; + return 0; + } + default: + return -EOPNOTSUPP; + } +} + + diff -Nru a/drivers/net/e1000/e1000_mac.c b/drivers/net/e1000/e1000_mac.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e1000/e1000_mac.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,1821 @@ +/******************************************************************************* + + This software program is available to you under a choice of one of two + licenses. You may choose to be licensed under either the GNU General Public + License (GPL) Version 2, June 1991, available at + http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the + text of which follows: + + Recipient has requested a license and Intel Corporation ("Intel") is willing + to grant a license for the software entitled Linux Base Driver for the + Intel(R) PRO/1000 Family of Adapters (e1000) (the "Software") being provided + by Intel Corporation. The following definitions apply to this license: + + "Licensed Patents" means patent claims licensable by Intel Corporation which + are necessarily infringed by the use of sale of the Software alone or when + combined with the operating system referred to below. + + "Recipient" means the party to whom Intel delivers this Software. + + "Licensee" means Recipient and those third parties that receive a license to + any operating system available under the GNU Public License version 2.0 or + later. + + Copyright (c) 1999 - 2002 Intel Corporation. + All rights reserved. + + The license is provided to Recipient and Recipient's Licensees under the + following terms. + + Redistribution and use in source and binary forms of the Software, with or + without modification, are permitted provided that the following conditions + are met: + + Redistributions of source code of the Software may retain the above + copyright notice, this list of conditions and the following disclaimer. + + Redistributions in binary form of the Software may reproduce the above + copyright notice, this list of conditions and the following disclaimer in + the documentation and/or materials provided with the distribution. + + Neither the name of Intel Corporation nor the names of its contributors + shall be used to endorse or promote products derived from this Software + without specific prior written permission. + + Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, + royalty-free patent license under Licensed Patents to make, use, sell, offer + to sell, import and otherwise transfer the Software, if any, in source code + and object code form. This license shall include changes to the Software + that are error corrections or other minor changes to the Software that do + not add functionality or features when the Software is incorporated in any + version of an operating system that has been distributed under the GNU + General Public License 2.0 or later. This patent license shall apply to the + combination of the Software and any operating system licensed under the GNU + Public License version 2.0 or later if, at the time Intel provides the + Software to Recipient, such addition of the Software to the then publicly + available versions of such operating systems available under the GNU Public + License version 2.0 or later (whether in gold, beta or alpha form) causes + such combination to be covered by the Licensed Patents. The patent license + shall not apply to any other combinations which include the Software. NO + hardware per se is licensed hereunder. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. + +*******************************************************************************/ + +/* e1000_mac.c + * Shared functions for accessing and configuring the MAC + */ + +#include "e1000_mac.h" +#include "e1000_phy.h" + +/****************************************************************************** + * Raises the EEPROM's clock input. + * + * shared - Struct containing variables accessed by shared code + * eecd_reg - EECD's current value + *****************************************************************************/ +static void +e1000_raise_clock(struct e1000_shared_adapter *shared, + uint32_t *eecd_reg) +{ + /* Raise the clock input to the EEPROM (by setting the SK bit), and then + * wait 50 microseconds. + */ + *eecd_reg = *eecd_reg | E1000_EECD_SK; + E1000_WRITE_REG(shared, EECD, *eecd_reg); + usec_delay(50); + return; +} + +/****************************************************************************** + * Lowers the EEPROM's clock input. + * + * shared - Struct containing variables accessed by shared code + * eecd_reg - EECD's current value + *****************************************************************************/ +static void +e1000_lower_clock(struct e1000_shared_adapter *shared, + uint32_t *eecd_reg) +{ + /* Lower the clock input to the EEPROM (by clearing the SK bit), and then + * wait 50 microseconds. + */ + *eecd_reg = *eecd_reg & ~E1000_EECD_SK; + E1000_WRITE_REG(shared, EECD, *eecd_reg); + usec_delay(50); + return; +} + +/****************************************************************************** + * Shift data bits out to the EEPROM. + * + * shared - Struct containing variables accessed by shared code + * data - data to send to the EEPROM + * count - number of bits to shift out + *****************************************************************************/ +static void +e1000_shift_out_bits(struct e1000_shared_adapter *shared, + uint16_t data, + uint16_t count) +{ + uint32_t eecd_reg; + uint32_t mask; + + /* We need to shift "count" bits out to the EEPROM. So, value in the + * "data" parameter will be shifted out to the EEPROM one bit at a time. + * In order to do this, "data" must be broken down into bits. + */ + mask = 0x01 << (count - 1); + eecd_reg = E1000_READ_REG(shared, EECD); + eecd_reg &= ~(E1000_EECD_DO | E1000_EECD_DI); + do { + /* A "1" is shifted out to the EEPROM by setting bit "DI" to a "1", + * and then raising and then lowering the clock (the SK bit controls + * the clock input to the EEPROM). A "0" is shifted out to the EEPROM + * by setting "DI" to "0" and then raising and then lowering the clock. + */ + eecd_reg &= ~E1000_EECD_DI; + + if(data & mask) + eecd_reg |= E1000_EECD_DI; + + E1000_WRITE_REG(shared, EECD, eecd_reg); + + usec_delay(50); + + e1000_raise_clock(shared, &eecd_reg); + e1000_lower_clock(shared, &eecd_reg); + + mask = mask >> 1; + + } while(mask); + + /* We leave the "DI" bit set to "0" when we leave this routine. */ + eecd_reg &= ~E1000_EECD_DI; + E1000_WRITE_REG(shared, EECD, eecd_reg); + return; +} + +/****************************************************************************** + * Shift data bits in from the EEPROM + * + * shared - Struct containing variables accessed by shared code + *****************************************************************************/ +static uint16_t +e1000_shift_in_bits(struct e1000_shared_adapter *shared) +{ + uint32_t eecd_reg; + uint32_t i; + uint16_t data; + + /* In order to read a register from the EEPROM, we need to shift 16 bits + * in from the EEPROM. Bits are "shifted in" by raising the clock input to + * the EEPROM (setting the SK bit), and then reading the value of the "DO" + * bit. During this "shifting in" process the "DI" bit should always be + * clear.. + */ + + eecd_reg = E1000_READ_REG(shared, EECD); + + eecd_reg &= ~(E1000_EECD_DO | E1000_EECD_DI); + data = 0; + + for(i = 0; i < 16; i++) { + data = data << 1; + e1000_raise_clock(shared, &eecd_reg); + + eecd_reg = E1000_READ_REG(shared, EECD); + + eecd_reg &= ~(E1000_EECD_DI); + if(eecd_reg & E1000_EECD_DO) + data |= 1; + + e1000_lower_clock(shared, &eecd_reg); + } + + return data; +} + +/****************************************************************************** + * Prepares EEPROM for access + * + * shared - Struct containing variables accessed by shared code + * + * Lowers EEPROM clock. Clears input pin. Sets the chip select pin. This + * function should be called before issuing a command to the EEPROM. + *****************************************************************************/ +static void +e1000_setup_eeprom(struct e1000_shared_adapter *shared) +{ + uint32_t eecd_reg; + + eecd_reg = E1000_READ_REG(shared, EECD); + + /* Clear SK and DI */ + eecd_reg &= ~(E1000_EECD_SK | E1000_EECD_DI); + E1000_WRITE_REG(shared, EECD, eecd_reg); + + /* Set CS */ + eecd_reg |= E1000_EECD_CS; + E1000_WRITE_REG(shared, EECD, eecd_reg); + return; +} + +/****************************************************************************** + * Returns EEPROM to a "standby" state + * + * shared - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +e1000_standby_eeprom(struct e1000_shared_adapter *shared) +{ + uint32_t eecd_reg; + + eecd_reg = E1000_READ_REG(shared, EECD); + + /* Deselct EEPROM */ + eecd_reg &= ~(E1000_EECD_CS | E1000_EECD_SK); + E1000_WRITE_REG(shared, EECD, eecd_reg); + usec_delay(50); + + /* Clock high */ + eecd_reg |= E1000_EECD_SK; + E1000_WRITE_REG(shared, EECD, eecd_reg); + usec_delay(50); + + /* Select EEPROM */ + eecd_reg |= E1000_EECD_CS; + E1000_WRITE_REG(shared, EECD, eecd_reg); + usec_delay(50); + + /* Clock low */ + eecd_reg &= ~E1000_EECD_SK; + E1000_WRITE_REG(shared, EECD, eecd_reg); + usec_delay(50); + return; +} + + +/****************************************************************************** + * Forces the MAC's flow control settings. + * + * shared - Struct containing variables accessed by shared code + * + * Sets the TFCE and RFCE bits in the device control register to reflect + * the adapter settings. TFCE and RFCE need to be explicitly set by + * software when a Copper PHY is used because autonegotiation is managed + * by the PHY rather than the MAC. Software must also configure these + * bits when link is forced on a fiber connection. + *****************************************************************************/ +static void +e1000_force_mac_fc(struct e1000_shared_adapter *shared) +{ + uint32_t ctrl_reg; + + DEBUGFUNC("e1000_force_mac_fc"); + + /* Get the current configuration of the Device Control Register */ + ctrl_reg = E1000_READ_REG(shared, CTRL); + + /* Because we didn't get link via the internal auto-negotiation + * mechanism (we either forced link or we got link via PHY + * auto-neg), we have to manually enable/disable transmit an + * receive flow control. + * + * The "Case" statement below enables/disable flow control + * according to the "shared->fc" parameter. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause + * frames but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * frames but we do not receive pause frames). + * 3: Both Rx and TX flow control (symmetric) is enabled. + * other: No other values should be possible at this point. + */ + + switch (shared->fc) { + case e1000_fc_none: + ctrl_reg &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE)); + break; + case e1000_fc_rx_pause: + ctrl_reg &= (~E1000_CTRL_TFCE); + ctrl_reg |= E1000_CTRL_RFCE; + break; + case e1000_fc_tx_pause: + ctrl_reg &= (~E1000_CTRL_RFCE); + ctrl_reg |= E1000_CTRL_TFCE; + break; + case e1000_fc_full: + ctrl_reg |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + ASSERT(0); + break; + } + + /* Disable TX Flow Control for 82542 (rev 2.0) */ + if(shared->mac_type == e1000_82542_rev2_0) + ctrl_reg &= (~E1000_CTRL_TFCE); + + + E1000_WRITE_REG(shared, CTRL, ctrl_reg); + return; +} + +/****************************************************************************** + * Reset the transmit and receive units; mask and clear all interrupts. + * + * shared - Struct containing variables accessed by shared code + *****************************************************************************/ +void +e1000_adapter_stop(struct e1000_shared_adapter *shared) +{ + uint32_t ctrl_reg; + uint32_t ctrl_ext_reg; + uint32_t icr_reg; + uint16_t pci_cmd_word; + + DEBUGFUNC("e1000_shared_adapter_stop"); + + /* If we are stopped or resetting exit gracefully and wait to be + * started again before accessing the hardware. + */ + if(shared->adapter_stopped) { + DEBUGOUT("Exiting because the adapter is already stopped!!!\n"); + return; + } + + /* Set the Adapter Stopped flag so other driver functions stop + * touching the Hardware. + */ + shared->adapter_stopped = TRUE; + + /* For 82542 (rev 2.0), disable MWI before issuing a device reset */ + if(shared->mac_type == e1000_82542_rev2_0) { + if(shared->pci_cmd_word & CMD_MEM_WRT_INVALIDATE) { + DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); + + pci_cmd_word = shared->pci_cmd_word & ~CMD_MEM_WRT_INVALIDATE; + + e1000_write_pci_cfg(shared, PCI_COMMAND_REGISTER, &pci_cmd_word); + } + } + + /* Clear interrupt mask to stop board from generating interrupts */ + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(shared, IMC, 0xffffffff); + + /* Disable the Transmit and Receive units. Then delay to allow + * any pending transactions to complete before we hit the MAC with + * the global reset. + */ + E1000_WRITE_REG(shared, RCTL, 0); + E1000_WRITE_REG(shared, TCTL, E1000_TCTL_PSP); + + /* The tbi_compatibility_on Flag must be cleared when Rctl is cleared. */ + shared->tbi_compatibility_on = FALSE; + + msec_delay(10); + + /* Issue a global reset to the MAC. This will reset the chip's + * transmit, receive, DMA, and link units. It will not effect + * the current PCI configuration. The global reset bit is self- + * clearing, and should clear within a microsecond. + */ + DEBUGOUT("Issuing a global reset to MAC\n"); + ctrl_reg = E1000_READ_REG(shared, CTRL); + E1000_WRITE_REG(shared, CTRL, (ctrl_reg | E1000_CTRL_RST)); + + /* Delay a few ms just to allow the reset to complete */ + msec_delay(10); + +#if DBG + /* Make sure the self-clearing global reset bit did self clear */ + ctrl_reg = E1000_READ_REG(shared, CTRL); + + ASSERT(!(ctrl_reg & E1000_CTRL_RST)); +#endif + + /* Force a reload from the EEPROM */ + ctrl_ext_reg = E1000_READ_REG(shared, CTRL_EXT); + ctrl_ext_reg |= E1000_CTRL_EXT_EE_RST; + E1000_WRITE_REG(shared, CTRL_EXT, ctrl_ext_reg); + msec_delay(2); + + /* Clear interrupt mask to stop board from generating interrupts */ + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(shared, IMC, 0xffffffff); + + /* Clear any pending interrupt events. */ + icr_reg = E1000_READ_REG(shared, ICR); + + /* If MWI was previously enabled, reenable it. */ + if(shared->mac_type == e1000_82542_rev2_0) { + if(shared->pci_cmd_word & CMD_MEM_WRT_INVALIDATE) { + e1000_write_pci_cfg(shared, + PCI_COMMAND_REGISTER, &shared->pci_cmd_word); + } + } + return; +} + +/****************************************************************************** + * Performs basic configuration of the adapter. + * + * shared - Struct containing variables accessed by shared code + * + * Assumes that the controller has previously been reset and is in a + * post-reset uninitialized state. Initializes the receive address registers, + * multicast table, and VLAN filter table. Calls routines to setup link + * configuration and flow control settings. Clears all on-chip counters. Leaves + * the transmit and receive units disabled and uninitialized. + *****************************************************************************/ +boolean_t +e1000_init_hw(struct e1000_shared_adapter *shared) +{ + uint32_t status_reg; + uint32_t i; + uint16_t pci_cmd_word; + boolean_t status; + + DEBUGFUNC("e1000_init_hw"); + + /* Set the Media Type and exit with error if it is not valid. */ + if(shared->mac_type != e1000_82543) { + /* tbi_compatibility is only valid on 82543 */ + shared->tbi_compatibility_en = FALSE; + } + + if(shared->mac_type >= e1000_82543) { + status_reg = E1000_READ_REG(shared, STATUS); + if(status_reg & E1000_STATUS_TBIMODE) { + shared->media_type = e1000_media_type_fiber; + /* tbi_compatibility not valid on fiber */ + shared->tbi_compatibility_en = FALSE; + } else { + shared->media_type = e1000_media_type_copper; + } + } else { + /* This is an 82542 (fiber only) */ + shared->media_type = e1000_media_type_fiber; + } + + /* Disabling VLAN filtering. */ + DEBUGOUT("Initializing the IEEE VLAN\n"); + E1000_WRITE_REG(shared, VET, 0); + + e1000_clear_vfta(shared); + + /* For 82542 (rev 2.0), disable MWI and put the receiver into reset */ + if(shared->mac_type == e1000_82542_rev2_0) { + if(shared->pci_cmd_word & CMD_MEM_WRT_INVALIDATE) { + DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); + pci_cmd_word = shared->pci_cmd_word & ~CMD_MEM_WRT_INVALIDATE; + e1000_write_pci_cfg(shared, PCI_COMMAND_REGISTER, &pci_cmd_word); + } + E1000_WRITE_REG(shared, RCTL, E1000_RCTL_RST); + + msec_delay(5); + } + + /* Setup the receive address. This involves initializing all of the Receive + * Address Registers (RARs 0 - 15). + */ + e1000_init_rx_addrs(shared); + + /* For 82542 (rev 2.0), take the receiver out of reset and enable MWI */ + if(shared->mac_type == e1000_82542_rev2_0) { + E1000_WRITE_REG(shared, RCTL, 0); + + msec_delay(1); + + if(shared->pci_cmd_word & CMD_MEM_WRT_INVALIDATE) { + e1000_write_pci_cfg(shared, + PCI_COMMAND_REGISTER, &shared->pci_cmd_word); + } + } + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + for(i = 0; i < E1000_MC_TBL_SIZE; i++) + E1000_WRITE_REG_ARRAY(shared, MTA, i, 0); + + /* Call a subroutine to configure the link and setup flow control. */ + status = e1000_setup_fc_and_link(shared); + + /* Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs(shared); + + return (status); +} + +/****************************************************************************** + * Initializes receive address filters. + * + * shared - Struct containing variables accessed by shared code + * + * Places the MAC address in receive address register 0 and clears the rest + * of the receive addresss registers. Clears the multicast table. Assumes + * the receiver is in reset when the routine is called. + *****************************************************************************/ +void +e1000_init_rx_addrs(struct e1000_shared_adapter *shared) +{ + uint32_t i; + uint32_t addr_low; + uint32_t addr_high; + + DEBUGFUNC("e1000_init_rx_addrs"); + + /* Setup the receive address. */ + DEBUGOUT("Programming MAC Address into RAR[0]\n"); + addr_low = (shared->mac_addr[0] | + (shared->mac_addr[1] << 8) | + (shared->mac_addr[2] << 16) | (shared->mac_addr[3] << 24)); + + addr_high = (shared->mac_addr[4] | + (shared->mac_addr[5] << 8) | E1000_RAH_AV); + + E1000_WRITE_REG_ARRAY(shared, RA, 0, addr_low); + E1000_WRITE_REG_ARRAY(shared, RA, 1, addr_high); + + /* Zero out the other 15 receive addresses. */ + DEBUGOUT("Clearing RAR[1-15]\n"); + for(i = 1; i < E1000_RAR_ENTRIES; i++) { + E1000_WRITE_REG_ARRAY(shared, RA, (i << 1), 0); + E1000_WRITE_REG_ARRAY(shared, RA, ((i << 1) + 1), 0); + } + + return; +} + +/****************************************************************************** + * Updates the MAC's list of multicast addresses. + * + * shared - Struct containing variables accessed by shared code + * mc_addr_list - the list of new multicast addresses + * mc_addr_count - number of addresses + * pad - number of bytes between addresses in the list + * + * The given list replaces any existing list. Clears the last 15 receive + * address registers and the multicast table. Uses receive address registers + * for the first 15 multicast addresses, and hashes the rest into the + * multicast table. + *****************************************************************************/ +void +e1000_mc_addr_list_update(struct e1000_shared_adapter *shared, + uint8_t *mc_addr_list, + uint32_t mc_addr_count, + uint32_t pad) +{ + uint32_t hash_value; + uint32_t i; + uint32_t rar_used_count = 1; /* RAR[0] is used for our MAC address */ + + DEBUGFUNC("e1000_mc_addr_list_update"); + + /* Set the new number of MC addresses that we are being requested to use. */ + shared->num_mc_addrs = mc_addr_count; + + /* Clear RAR[1-15] */ + DEBUGOUT(" Clearing RAR[1-15]\n"); + for(i = rar_used_count; i < E1000_RAR_ENTRIES; i++) { + E1000_WRITE_REG_ARRAY(shared, RA, (i << 1), 0); + E1000_WRITE_REG_ARRAY(shared, RA, ((i << 1) + 1), 0); + } + + /* Clear the MTA */ + DEBUGOUT(" Clearing MTA\n"); + for(i = 0; i < E1000_NUM_MTA_REGISTERS; i++) { + E1000_WRITE_REG_ARRAY(shared, MTA, i, 0); + } + + /* Add the new addresses */ + for(i = 0; i < mc_addr_count; i++) { + DEBUGOUT(" Adding the multicast addresses:\n"); + DEBUGOUT7(" MC Addr #%d =%.2X %.2X %.2X %.2X %.2X %.2X\n", i, + mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad)], + mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 1], + mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 2], + mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 3], + mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 4], + mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 5]); + + hash_value = e1000_hash_mc_addr(shared, + mc_addr_list + + (i * (ETH_LENGTH_OF_ADDRESS + pad))); + + DEBUGOUT1(" Hash value = 0x%03X\n", hash_value); + + /* Place this multicast address in the RAR if there is room, * + * else put it in the MTA + */ + if(rar_used_count < E1000_RAR_ENTRIES) { + e1000_rar_set(shared, + mc_addr_list + (i * (ETH_LENGTH_OF_ADDRESS + pad)), + rar_used_count); + rar_used_count++; + } else { + e1000_mta_set(shared, hash_value); + } + } + + DEBUGOUT("MC Update Complete\n"); + return; +} + +/****************************************************************************** + * Hashes an address to determine its location in the multicast table + * + * shared - Struct containing variables accessed by shared code + * mc_addr - the multicast address to hash + *****************************************************************************/ +uint32_t +e1000_hash_mc_addr(struct e1000_shared_adapter *shared, + uint8_t *mc_addr) +{ + uint32_t hash_value = 0; + + /* The portion of the address that is used for the hash table is + * determined by the mc_filter_type setting. + */ + switch (shared->mc_filter_type) { + /* [0] [1] [2] [3] [4] [5] + * 01 AA 00 12 34 56 + * LSB MSB - According to H/W docs */ + case 0: + /* [47:36] i.e. 0x563 for above example address */ + hash_value = ((mc_addr[4] >> 4) | (((uint16_t) mc_addr[5]) << 4)); + break; + case 1: /* [46:35] i.e. 0xAC6 for above example address */ + hash_value = ((mc_addr[4] >> 3) | (((uint16_t) mc_addr[5]) << 5)); + break; + case 2: /* [45:34] i.e. 0x5D8 for above example address */ + hash_value = ((mc_addr[4] >> 2) | (((uint16_t) mc_addr[5]) << 6)); + break; + case 3: /* [43:32] i.e. 0x634 for above example address */ + hash_value = ((mc_addr[4]) | (((uint16_t) mc_addr[5]) << 8)); + break; + } + + hash_value &= 0xFFF; + return (hash_value); +} + +/****************************************************************************** + * Sets the bit in the multicast table corresponding to the hash value. + * + * shared - Struct containing variables accessed by shared code + * hash_value - Multicast address hash value + *****************************************************************************/ +void +e1000_mta_set(struct e1000_shared_adapter *shared, + uint32_t hash_value) +{ + uint32_t hash_bit, hash_reg; + uint32_t mta_reg; + uint32_t temp; + + /* The MTA is a register array of 128 32-bit registers. + * It is treated like an array of 4096 bits. We want to set + * bit BitArray[hash_value]. So we figure out what register + * the bit is in, read it, OR in the new bit, then write + * back the new value. The register is determined by the + * upper 7 bits of the hash value and the bit within that + * register are determined by the lower 5 bits of the value. + */ + hash_reg = (hash_value >> 5) & 0x7F; + hash_bit = hash_value & 0x1F; + + mta_reg = E1000_READ_REG_ARRAY(shared, MTA, hash_reg); + + mta_reg |= (1 << hash_bit); + + /* If we are on an 82544 and we are trying to write an odd offset + * in the MTA, save off the previous entry before writing and + * restore the old value after writing. + */ + if((shared->mac_type == e1000_82544) && ((hash_reg & 0x1) == 1)) { + temp = E1000_READ_REG_ARRAY(shared, MTA, (hash_reg - 1)); + E1000_WRITE_REG_ARRAY(shared, MTA, hash_reg, mta_reg); + E1000_WRITE_REG_ARRAY(shared, MTA, (hash_reg - 1), temp); + } else { + E1000_WRITE_REG_ARRAY(shared, MTA, hash_reg, mta_reg); + } + return; +} + +/****************************************************************************** + * Puts an ethernet address into a receive address register. + * + * shared - Struct containing variables accessed by shared code + * addr - Address to put into receive address register + * index - Receive address register to write + *****************************************************************************/ +void +e1000_rar_set(struct e1000_shared_adapter *shared, + uint8_t *addr, + uint32_t index) +{ + uint32_t rar_low, rar_high; + + /* HW expects these in little endian so we reverse the byte order + * from network order (big endian) to little endian + */ + rar_low = ((uint32_t) addr[0] | + ((uint32_t) addr[1] << 8) | + ((uint32_t) addr[2] << 16) | ((uint32_t) addr[3] << 24)); + + rar_high = ((uint32_t) addr[4] | ((uint32_t) addr[5] << 8) | E1000_RAH_AV); + + E1000_WRITE_REG_ARRAY(shared, RA, (index << 1), rar_low); + E1000_WRITE_REG_ARRAY(shared, RA, ((index << 1) + 1), rar_high); + return; +} + +/****************************************************************************** + * Writes a value to the specified offset in the VLAN filter table. + * + * shared - Struct containing variables accessed by shared code + * offset - Offset in VLAN filer table to write + * value - Value to write into VLAN filter table + *****************************************************************************/ +void +e1000_write_vfta(struct e1000_shared_adapter *shared, + uint32_t offset, + uint32_t value) +{ + uint32_t temp; + + if((shared->mac_type == e1000_82544) && ((offset & 0x1) == 1)) { + temp = E1000_READ_REG_ARRAY(shared, VFTA, (offset - 1)); + E1000_WRITE_REG_ARRAY(shared, VFTA, offset, value); + E1000_WRITE_REG_ARRAY(shared, VFTA, (offset - 1), temp); + } else { + E1000_WRITE_REG_ARRAY(shared, VFTA, offset, value); + } + return; +} + +/****************************************************************************** + * Clears the VLAN filer table + * + * shared - Struct containing variables accessed by shared code + *****************************************************************************/ +void +e1000_clear_vfta(struct e1000_shared_adapter *shared) +{ + uint32_t offset; + + for(offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) + E1000_WRITE_REG_ARRAY(shared, VFTA, offset, 0); + return; +} + +/****************************************************************************** + * Configures flow control and link settings. + * + * shared - Struct containing variables accessed by shared code + * + * Determines which flow control settings to use. Calls the apropriate media- + * specific link configuration function. Configures the flow control settings. + * Assuming the adapter has a valid link partner, a valid link should be + * established. Assumes the hardware has previously been reset and the + * transmitter and receiver are not enabled. + *****************************************************************************/ +boolean_t +e1000_setup_fc_and_link(struct e1000_shared_adapter *shared) +{ + uint32_t ctrl_reg; + uint32_t eecd_reg; + uint32_t ctrl_ext_reg; + boolean_t status = TRUE; + + DEBUGFUNC("e1000_setup_fc_and_link"); + + /* Read the SWDPIO bits and the ILOS bit out of word 0x0A in the + * EEPROM. Store these bits in a variable that we will later write + * to the Device Control Register (CTRL). + */ + eecd_reg = e1000_read_eeprom(shared, EEPROM_INIT_CONTROL1_REG); + + ctrl_reg = + (((eecd_reg & EEPROM_WORD0A_SWDPIO) << SWDPIO_SHIFT) | + ((eecd_reg & EEPROM_WORD0A_ILOS) << ILOS_SHIFT)); + + /* Set the PCI priority bit correctly in the CTRL register. This + * determines if the adapter gives priority to receives, or if it + * gives equal priority to transmits and receives. + */ + if(shared->dma_fairness) + ctrl_reg |= E1000_CTRL_PRIOR; + + /* Read and store word 0x0F of the EEPROM. This word contains bits + * that determine the hardware's default PAUSE (flow control) mode, + * a bit that determines whether the HW defaults to enabling or + * disabling auto-negotiation, and the direction of the + * SW defined pins. If there is no SW over-ride of the flow + * control setting, then the variable shared->fc will + * be initialized based on a value in the EEPROM. + */ + eecd_reg = e1000_read_eeprom(shared, EEPROM_INIT_CONTROL2_REG); + + if(shared->fc > e1000_fc_full) { + if((eecd_reg & EEPROM_WORD0F_PAUSE_MASK) == 0) + shared->fc = e1000_fc_none; + else if((eecd_reg & EEPROM_WORD0F_PAUSE_MASK) == EEPROM_WORD0F_ASM_DIR) + shared->fc = e1000_fc_tx_pause; + else + shared->fc = e1000_fc_full; + } + + /* We want to save off the original Flow Control configuration just + * in case we get disconnected and then reconnected into a different + * hub or switch with different Flow Control capabilities. + */ + shared->original_fc = shared->fc; + + if(shared->mac_type == e1000_82542_rev2_0) + shared->fc &= (~e1000_fc_tx_pause); + + if((shared->mac_type < e1000_82543) && (shared->report_tx_early == 1)) + shared->fc &= (~e1000_fc_rx_pause); + + DEBUGOUT1("After fix-ups FlowControl is now = %x\n", shared->fc); + + /* Take the 4 bits from EEPROM word 0x0F that determine the initial + * polarity value for the SW controlled pins, and setup the + * Extended Device Control reg with that info. + * This is needed because one of the SW controlled pins is used for + * signal detection. So this should be done before e1000_setup_pcs_link() + * or e1000_phy_setup() is called. + */ + if(shared->mac_type == e1000_82543) { + ctrl_ext_reg = ((eecd_reg & EEPROM_WORD0F_SWPDIO_EXT) + << SWDPIO__EXT_SHIFT); + E1000_WRITE_REG(shared, CTRL_EXT, ctrl_ext_reg); + } + + /* Call the necessary subroutine to configure the link. */ + if(shared->media_type == e1000_media_type_fiber) + status = e1000_setup_pcs_link(shared, ctrl_reg); + else + status = e1000_phy_setup(shared, ctrl_reg); + + /* Initialize the flow control address, type, and PAUSE timer + * registers to their default values. This is done even if flow + * control is disabled, because it does not hurt anything to + * initialize these registers. + */ + DEBUGOUT("Initializing the Flow Control address, type and timer regs\n"); + + E1000_WRITE_REG(shared, FCAL, FLOW_CONTROL_ADDRESS_LOW); + E1000_WRITE_REG(shared, FCAH, FLOW_CONTROL_ADDRESS_HIGH); + E1000_WRITE_REG(shared, FCT, FLOW_CONTROL_TYPE); + E1000_WRITE_REG(shared, FCTTV, shared->fc_pause_time); + + /* Set the flow control receive threshold registers. Normally, + * these registers will be set to a default threshold that may be + * adjusted later by the driver's runtime code. However, if the + * ability to transmit pause frames in not enabled, then these + * registers will be set to 0. + */ + if(!(shared->fc & e1000_fc_tx_pause)) { + E1000_WRITE_REG(shared, FCRTL, 0); + E1000_WRITE_REG(shared, FCRTH, 0); + } else { + /* We need to set up the Receive Threshold high and low water marks + * as well as (optionally) enabling the transmission of XON frames. + */ + if(shared->fc_send_xon) { + E1000_WRITE_REG(shared, FCRTL, + (shared->fc_low_water | E1000_FCRTL_XONE)); + E1000_WRITE_REG(shared, FCRTH, shared->fc_high_water); + } else { + E1000_WRITE_REG(shared, FCRTL, shared->fc_low_water); + E1000_WRITE_REG(shared, FCRTH, shared->fc_high_water); + } + } + return (status); +} + +/****************************************************************************** + * Sets up link for a fiber based adapter + * + * shared - Struct containing variables accessed by shared code + * ctrl_reg - Current value of the device control register + * + * Manipulates Physical Coding Sublayer functions in order to configure + * link. Assumes the hardware has been previously reset and the transmitter + * and receiver are not enabled. + *****************************************************************************/ +boolean_t +e1000_setup_pcs_link(struct e1000_shared_adapter *shared, + uint32_t ctrl_reg) +{ + uint32_t status_reg; + uint32_t tctl_reg; + uint32_t txcw_reg = 0; + uint32_t i; + + DEBUGFUNC("e1000_setup_pcs_link"); + + /* Setup the collsion distance. Since this is configuring the + * TBI it is assumed that we are in Full Duplex. + */ + tctl_reg = E1000_READ_REG(shared, TCTL); + i = E1000_FDX_COLLISION_DISTANCE; + i <<= E1000_COLD_SHIFT; + tctl_reg |= i; + E1000_WRITE_REG(shared, TCTL, tctl_reg); + + /* Check for a software override of the flow control settings, and + * setup the device accordingly. If auto-negotiation is enabled, + * then software will have to set the "PAUSE" bits to the correct + * value in the Tranmsit Config Word Register (TXCW) and re-start + * auto-negotiation. However, if auto-negotiation is disabled, + * then software will have to manually configure the two flow + * control enable bits in the CTRL register. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * but we do not support receiving pause frames). + * 3: Both Rx and TX flow control (symmetric) are enabled. + * other: No software override. The flow control configuration + * in the EEPROM is used. + */ + switch (shared->fc) { + case e1000_fc_none: /* 0 */ + /* Flow control (RX & TX) is completely disabled by a + * software over-ride. + */ + txcw_reg = (E1000_TXCW_ANE | E1000_TXCW_FD); + break; + case e1000_fc_rx_pause: /* 1 */ + /* RX Flow control is enabled, and TX Flow control is + * disabled, by a software over-ride. + */ + /* Since there really isn't a way to advertise that we are + * capable of RX Pause ONLY, we will advertise that we + * support both symmetric and asymmetric RX PAUSE. Later + * we will disable the adapter's ability to send PAUSE + * frames. + */ + txcw_reg = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + case e1000_fc_tx_pause: /* 2 */ + /* TX Flow control is enabled, and RX Flow control is + * disabled, by a software over-ride. + */ + txcw_reg = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR); + break; + case e1000_fc_full: /* 3 */ + /* Flow control (both RX and TX) is enabled by a software + * over-ride. + */ + txcw_reg = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + default: + /* We should never get here. The value should be 0-3. */ + DEBUGOUT("Flow control param set incorrectly\n"); + ASSERT(0); + break; + } + + /* Since auto-negotiation is enabled, take the link out of reset. + * (the link will be in reset, because we previously reset the + * chip). This will restart auto-negotiation. If auto-neogtiation + * is successful then the link-up status bit will be set and the + * flow control enable bits (RFCE and TFCE) will be set according + * to their negotiated value. + */ + DEBUGOUT("Auto-negotiation enabled\n"); + + E1000_WRITE_REG(shared, TXCW, txcw_reg); + E1000_WRITE_REG(shared, CTRL, ctrl_reg); + + shared->txcw_reg = txcw_reg; + msec_delay(1); + + /* If we have a signal then poll for a "Link-Up" indication in the + * Device Status Register. Time-out if a link isn't seen in 500 + * milliseconds seconds (Auto-negotiation should complete in less + * than 500 milliseconds even if the other end is doing it in SW). + */ + if(!(E1000_READ_REG(shared, CTRL) & E1000_CTRL_SWDPIN1)) { + + DEBUGOUT("Looking for Link\n"); + for(i = 0; i < (LINK_UP_TIMEOUT / 10); i++) { + msec_delay(10); + status_reg = E1000_READ_REG(shared, STATUS); + if(status_reg & E1000_STATUS_LU) + break; + } + + if(i == (LINK_UP_TIMEOUT / 10)) { + /* AutoNeg failed to achieve a link, so we'll call the + * "CheckForLink" routine. This routine will force the link + * up if we have "signal-detect". This will allow us to + * communicate with non-autonegotiating link partners. + */ + DEBUGOUT("Never got a valid link from auto-neg!!!\n"); + + shared->autoneg_failed = 1; + e1000_check_for_link(shared); + shared->autoneg_failed = 0; + } else { + shared->autoneg_failed = 0; + DEBUGOUT("Valid Link Found\n"); + } + } else { + DEBUGOUT("No Signal Detected\n"); + } + + return (TRUE); +} + +/****************************************************************************** + * Configures flow control settings after link is established + * + * shared - Struct containing variables accessed by shared code + * + * Should be called immediately after a valid link has been established. + * Forces MAC flow control settings if link was forced. When in MII/GMII mode + * and autonegotiation is enabled, the MAC flow control settings will be set + * based on the flow control negotiated by the PHY. In TBI mode, the TFCE + * and RFCE bits will be automaticaly set to the negotiated flow control mode. + *****************************************************************************/ +void +e1000_config_fc_after_link_up(struct e1000_shared_adapter *shared) +{ + uint16_t mii_status_reg; + uint16_t mii_nway_adv_reg; + uint16_t mii_nway_lp_ability_reg; + uint16_t speed; + uint16_t duplex; + + DEBUGFUNC("e1000_config_fc_after_link_up"); + + /* Check for the case where we have fiber media and auto-neg failed + * so we had to force link. In this case, we need to force the + * configuration of the MAC to match the "fc" parameter. + */ + if(((shared->media_type == e1000_media_type_fiber) + && (shared->autoneg_failed)) + || ((shared->media_type == e1000_media_type_copper) + && (!shared->autoneg))) { + e1000_force_mac_fc(shared); + } + + /* Check for the case where we have copper media and auto-neg is + * enabled. In this case, we need to check and see if Auto-Neg + * has completed, and if so, how the PHY and link partner has + * flow control configured. + */ + if((shared->media_type == e1000_media_type_copper) && shared->autoneg) { + /* Read the MII Status Register and check to see if AutoNeg + * has completed. We read this twice because this reg has + * some "sticky" (latched) bits. + */ + mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + + if(mii_status_reg & MII_SR_AUTONEG_COMPLETE) { + /* The AutoNeg process has completed, so we now need to + * read both the Auto Negotiation Advertisement Register + * (Address 4) and the Auto_Negotiation Base Page Ability + * Register (Address 5) to determine how flow control was + * negotiated. + */ + mii_nway_adv_reg = e1000_read_phy_reg(shared, + PHY_AUTONEG_ADV); + mii_nway_lp_ability_reg = e1000_read_phy_reg(shared, + PHY_LP_ABILITY); + + /* Two bits in the Auto Negotiation Advertisement Register + * (Address 4) and two bits in the Auto Negotiation Base + * Page Ability Register (Address 5) determine flow control + * for both the PHY and the link partner. The following + * table, taken out of the IEEE 802.3ab/D6.0 dated March 25, + * 1999, describes these PAUSE resolution bits and how flow + * control is determined based upon these settings. + * NOTE: DC = Don't Care + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution + *-------|---------|-------|---------|-------------------- + * 0 | 0 | DC | DC | e1000_fc_none + * 0 | 1 | 0 | DC | e1000_fc_none + * 0 | 1 | 1 | 0 | e1000_fc_none + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + * 1 | 0 | 0 | DC | e1000_fc_none + * 1 | DC | 1 | DC | e1000_fc_full + * 1 | 1 | 0 | 0 | e1000_fc_none + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + * + */ + /* Are both PAUSE bits set to 1? If so, this implies + * Symmetric Flow Control is enabled at both ends. The + * ASM_DIR bits are irrelevant per the spec. + * + * For Symmetric Flow Control: + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | DC | 1 | DC | e1000_fc_full + * + */ + if((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) { + /* Now we need to check if the user selected RX ONLY + * of pause frames. In this case, we had to advertise + * FULL flow control because we could not advertise RX + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if(shared->original_fc == e1000_fc_full) { + shared->fc = e1000_fc_full; + DEBUGOUT("Flow Control = FULL.\r\n"); + } else { + shared->fc = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = RX PAUSE frames only.\r\n"); + } + } + /* For receiving PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + * + */ + else if(!(mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { + shared->fc = e1000_fc_tx_pause; + DEBUGOUT("Flow Control = TX PAUSE frames only.\r\n"); + } + /* For transmitting PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + * + */ + else if((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { + shared->fc = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = RX PAUSE frames only.\r\n"); + } + /* Per the IEEE spec, at this point flow control should be + * disabled. However, we want to consider that we could + * be connected to a legacy switch that doesn't advertise + * desired flow control, but can be forced on the link + * partner. So if we advertised no flow control, that is + * what we will resolve to. If we advertised some kind of + * receive capability (Rx Pause Only or Full Flow Control) + * and the link partner advertised none, we will configure + * ourselves to enable Rx Flow Control only. We can do + * this safely for two reasons: If the link partner really + * didn't want flow control enabled, and we enable Rx, no + * harm done since we won't be receiving any PAUSE frames + * anyway. If the intent on the link partner was to have + * flow control enabled, then by us enabling RX only, we + * can at least receive pause frames and process them. + * This is a good idea because in most cases, since we are + * predominantly a server NIC, more times than not we will + * be asked to delay transmission of packets than asking + * our link partner to pause transmission of frames. + */ + else if(shared->original_fc == e1000_fc_none || + shared->original_fc == e1000_fc_tx_pause) { + shared->fc = e1000_fc_none; + DEBUGOUT("Flow Control = NONE.\r\n"); + } else { + shared->fc = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = RX PAUSE frames only.\r\n"); + } + + /* Now we need to do one last check... If we auto- + * negotiated to HALF DUPLEX, flow control should not be + * enabled per IEEE 802.3 spec. + */ + e1000_get_speed_and_duplex(shared, &speed, &duplex); + + if(duplex == HALF_DUPLEX) + shared->fc = e1000_fc_none; + + /* Now we call a subroutine to actually force the MAC + * controller to use the correct flow control settings. + */ + e1000_force_mac_fc(shared); + } else { + DEBUGOUT("Copper PHY and Auto Neg has not completed.\r\n"); + } + } + return; +} + +/****************************************************************************** + * Checks to see if the link status of the hardware has changed. + * + * shared - Struct containing variables accessed by shared code + * + * Called by any function that needs to check the link status of the adapter. + *****************************************************************************/ +void +e1000_check_for_link(struct e1000_shared_adapter *shared) +{ + uint32_t rxcw_reg; + uint32_t ctrl_reg; + uint32_t status_reg; + uint32_t rctl_reg; + uint16_t phy_data; + uint16_t lp_capability; + + DEBUGFUNC("e1000_check_for_link"); + + ctrl_reg = E1000_READ_REG(shared, CTRL); + status_reg = E1000_READ_REG(shared, STATUS); + rxcw_reg = E1000_READ_REG(shared, RXCW); + + /* If we have a copper PHY then we only want to go out to the PHY + * registers to see if Auto-Neg has completed and/or if our link + * status has changed. The get_link_status flag will be set if we + * receive a Link Status Change interrupt or we have Rx Sequence + * Errors. + */ + if(shared->media_type == e1000_media_type_copper + && shared->get_link_status) { + /* First we want to see if the MII Status Register reports + * link. If so, then we want to get the current speed/duplex + * of the PHY. + * Read the register twice since the link bit is sticky. + */ + phy_data = e1000_read_phy_reg(shared, PHY_STATUS); + phy_data = e1000_read_phy_reg(shared, PHY_STATUS); + + if(phy_data & MII_SR_LINK_STATUS) { + shared->get_link_status = FALSE; + } else { + DEBUGOUT("**** CFL - No link detected. ****\r\n"); + return; + } + + /* If we are forcing speed/duplex, then we simply return since + * we have already determined whether we have link or not. + */ + if(!shared->autoneg) { + return; + } + + switch (shared->phy_id) { + case M88E1000_12_PHY_ID: + case M88E1000_14_PHY_ID: + case M88E1000_I_PHY_ID: + /* We have a M88E1000 PHY and Auto-Neg is enabled. If we + * have Si on board that is 82544 or newer, Auto + * Speed Detection takes care of MAC speed/duplex + * configuration. So we only need to configure Collision + * Distance in the MAC. Otherwise, we need to force + * speed/duplex on the MAC to the current PHY speed/duplex + * settings. + */ + if(shared->mac_type >= e1000_82544) { + DEBUGOUT("CFL - Auto-Neg complete."); + DEBUGOUT("Configuring Collision Distance."); + e1000_config_collision_dist(shared); + } else { + /* Read the Phy Specific Status register to get the + * resolved speed/duplex settings. Then call + * e1000_config_mac_to_phy which will retrieve + * PHY register information and configure the MAC to + * equal the negotiated speed/duplex. + */ + phy_data = e1000_read_phy_reg(shared, + M88E1000_PHY_SPEC_STATUS); + + DEBUGOUT1("CFL - Auto-Neg complete. phy_data = %x\r\n", + phy_data); + e1000_config_mac_to_phy(shared, phy_data); + } + + /* Configure Flow Control now that Auto-Neg has completed. + * We need to first restore the users desired Flow + * Control setting since we may have had to re-autoneg + * with a different link partner. + */ + e1000_config_fc_after_link_up(shared); + break; + + default: + DEBUGOUT("CFL - Invalid PHY detected.\r\n"); + + } /* end switch statement */ + + /* At this point we know that we are on copper, link is up, + * and we are auto-neg'd. These are pre-conditions for checking + * the link parter capabilities register. We use the link partner + * capabilities to determine if TBI Compatibility needs to be turned on + * or turned off. If the link partner advertises any speed in addition + * to Gigabit, then we assume that they are GMII-based and TBI + * compatibility is not needed. + * If no other speeds are advertised, then we assume the link partner + * is TBI-based and we turn on TBI Compatibility. + */ + if(shared->tbi_compatibility_en) { + lp_capability = e1000_read_phy_reg(shared, PHY_LP_ABILITY); + if(lp_capability & (NWAY_LPAR_10T_HD_CAPS | + NWAY_LPAR_10T_FD_CAPS | + NWAY_LPAR_100TX_HD_CAPS | + NWAY_LPAR_100TX_FD_CAPS | + NWAY_LPAR_100T4_CAPS)) { + /* If our link partner advertises below Gig, then they do not + * need the special Tbi Compatibility mode. + */ + if(shared->tbi_compatibility_on) { + /* If we previously were in the mode, turn it off, now. */ + rctl_reg = E1000_READ_REG(shared, RCTL); + rctl_reg &= ~E1000_RCTL_SBP; + E1000_WRITE_REG(shared, RCTL, rctl_reg); + shared->tbi_compatibility_on = FALSE; + } + } else { + /* If the mode is was previously off, turn it on. + * For compatibility with a suspected Tbi link partners, + * we will store bad packets. + * (Certain frames have an additional byte on the end and will + * look like CRC errors to to the hardware). + */ + if(!shared->tbi_compatibility_on) { + shared->tbi_compatibility_on = TRUE; + rctl_reg = E1000_READ_REG(shared, RCTL); + rctl_reg |= E1000_RCTL_SBP; + E1000_WRITE_REG(shared, RCTL, rctl_reg); + } + } + } + } /* end if e1000_media_type_copper statement */ + /* If we don't have link (auto-negotiation failed or link partner + * cannot auto-negotiate) and the cable is plugged in since we don't + * have Loss-Of-Signal (we HAVE a signal) and our link partner is + * not trying to AutoNeg with us (we are receiving idles/data + * then we need to force our link to connect to a non + * auto-negotiating link partner. We also need to give + * auto-negotiation time to complete in case the cable was just + * plugged in. The autoneg_failed flag does this. + */ + else if((shared->media_type == e1000_media_type_fiber) && /* Fiber PHY */ + (!(status_reg & E1000_STATUS_LU)) && /* no link and */ + (!(ctrl_reg & E1000_CTRL_SWDPIN1)) && /* we have signal */ + (!(rxcw_reg & E1000_RXCW_C))) { /* and rxing idle/data */ + if(shared->autoneg_failed == 0) { /* given AutoNeg time */ + shared->autoneg_failed = 1; + return; + } + + DEBUGOUT("NOT RXing /C/, disable AutoNeg and force link.\r\n"); + + /* Disable auto-negotiation in the TXCW register */ + E1000_WRITE_REG(shared, TXCW, (shared->txcw_reg & ~E1000_TXCW_ANE)); + + /* Force link-up and also force full-duplex. */ + ctrl_reg = E1000_READ_REG(shared, CTRL); + ctrl_reg |= (E1000_CTRL_SLU | E1000_CTRL_FD); + E1000_WRITE_REG(shared, CTRL, ctrl_reg); + + /* Configure Flow Control after forcing link up. */ + e1000_config_fc_after_link_up(shared); + + } else if((shared->media_type == e1000_media_type_fiber) && /* Fiber */ + (ctrl_reg & E1000_CTRL_SLU) && /* we have forced link */ + (rxcw_reg & E1000_RXCW_C)) { /* and Rxing /C/ ordered sets */ + /* If we are forcing link and we are receiving /C/ ordered sets, + * then re-enable auto-negotiation in the RXCW register and + * disable forced link in the Device Control register in an attempt + * to AutoNeg with our link partner. + */ + DEBUGOUT("RXing /C/, enable AutoNeg and stop forcing link.\r\n"); + + /* Enable auto-negotiation in the TXCW register and stop + * forcing link. + */ + E1000_WRITE_REG(shared, TXCW, shared->txcw_reg); + + E1000_WRITE_REG(shared, CTRL, (ctrl_reg & ~E1000_CTRL_SLU)); + } + + return; +} + +/****************************************************************************** + * Clears all hardware statistics counters. + * + * shared - Struct containing variables accessed by shared code + *****************************************************************************/ +void +e1000_clear_hw_cntrs(struct e1000_shared_adapter *shared) +{ + volatile uint32_t temp_reg; + + DEBUGFUNC("e1000_clear_hw_cntrs"); + + /* if we are stopped or resetting exit gracefully */ + if(shared->adapter_stopped) { + DEBUGOUT("Exiting because the adapter is stopped!!!\n"); + return; + } + + temp_reg = E1000_READ_REG(shared, CRCERRS); + temp_reg = E1000_READ_REG(shared, SYMERRS); + temp_reg = E1000_READ_REG(shared, MPC); + temp_reg = E1000_READ_REG(shared, SCC); + temp_reg = E1000_READ_REG(shared, ECOL); + temp_reg = E1000_READ_REG(shared, MCC); + temp_reg = E1000_READ_REG(shared, LATECOL); + temp_reg = E1000_READ_REG(shared, COLC); + temp_reg = E1000_READ_REG(shared, DC); + temp_reg = E1000_READ_REG(shared, SEC); + temp_reg = E1000_READ_REG(shared, RLEC); + temp_reg = E1000_READ_REG(shared, XONRXC); + temp_reg = E1000_READ_REG(shared, XONTXC); + temp_reg = E1000_READ_REG(shared, XOFFRXC); + temp_reg = E1000_READ_REG(shared, XOFFTXC); + temp_reg = E1000_READ_REG(shared, FCRUC); + temp_reg = E1000_READ_REG(shared, PRC64); + temp_reg = E1000_READ_REG(shared, PRC127); + temp_reg = E1000_READ_REG(shared, PRC255); + temp_reg = E1000_READ_REG(shared, PRC511); + temp_reg = E1000_READ_REG(shared, PRC1023); + temp_reg = E1000_READ_REG(shared, PRC1522); + temp_reg = E1000_READ_REG(shared, GPRC); + temp_reg = E1000_READ_REG(shared, BPRC); + temp_reg = E1000_READ_REG(shared, MPRC); + temp_reg = E1000_READ_REG(shared, GPTC); + temp_reg = E1000_READ_REG(shared, GORCL); + temp_reg = E1000_READ_REG(shared, GORCH); + temp_reg = E1000_READ_REG(shared, GOTCL); + temp_reg = E1000_READ_REG(shared, GOTCH); + temp_reg = E1000_READ_REG(shared, RNBC); + temp_reg = E1000_READ_REG(shared, RUC); + temp_reg = E1000_READ_REG(shared, RFC); + temp_reg = E1000_READ_REG(shared, ROC); + temp_reg = E1000_READ_REG(shared, RJC); + temp_reg = E1000_READ_REG(shared, TORL); + temp_reg = E1000_READ_REG(shared, TORH); + temp_reg = E1000_READ_REG(shared, TOTL); + temp_reg = E1000_READ_REG(shared, TOTH); + temp_reg = E1000_READ_REG(shared, TPR); + temp_reg = E1000_READ_REG(shared, TPT); + temp_reg = E1000_READ_REG(shared, PTC64); + temp_reg = E1000_READ_REG(shared, PTC127); + temp_reg = E1000_READ_REG(shared, PTC255); + temp_reg = E1000_READ_REG(shared, PTC511); + temp_reg = E1000_READ_REG(shared, PTC1023); + temp_reg = E1000_READ_REG(shared, PTC1522); + temp_reg = E1000_READ_REG(shared, MPTC); + temp_reg = E1000_READ_REG(shared, BPTC); + + if(shared->mac_type < e1000_82543) + return; + + temp_reg = E1000_READ_REG(shared, ALGNERRC); + temp_reg = E1000_READ_REG(shared, RXERRC); + temp_reg = E1000_READ_REG(shared, TNCRS); + temp_reg = E1000_READ_REG(shared, CEXTERR); + temp_reg = E1000_READ_REG(shared, TSCTC); + temp_reg = E1000_READ_REG(shared, TSCTFC); + return; +} + +/****************************************************************************** + * Detects the current speed and duplex settings of the hardware. + * + * shared - Struct containing variables accessed by shared code + * speed - Speed of the connection + * duplex - Duplex setting of the connection + *****************************************************************************/ +void +e1000_get_speed_and_duplex(struct e1000_shared_adapter *shared, + uint16_t *speed, + uint16_t *duplex) +{ + uint32_t status_reg; +#if DBG + uint16_t phy_data; +#endif + + DEBUGFUNC("e1000_get_speed_and_duplex"); + + /* If the adapter is stopped we don't have a speed or duplex */ + if(shared->adapter_stopped) { + *speed = 0; + *duplex = 0; + return; + } + + if(shared->mac_type >= e1000_82543) { + status_reg = E1000_READ_REG(shared, STATUS); + if(status_reg & E1000_STATUS_SPEED_1000) { + *speed = SPEED_1000; + DEBUGOUT("1000 Mbs, "); + } else if(status_reg & E1000_STATUS_SPEED_100) { + *speed = SPEED_100; + DEBUGOUT("100 Mbs, "); + } else { + *speed = SPEED_10; + DEBUGOUT("10 Mbs, "); + } + + if(status_reg & E1000_STATUS_FD) { + *duplex = FULL_DUPLEX; + DEBUGOUT("Full Duplex\r\n"); + } else { + *duplex = HALF_DUPLEX; + DEBUGOUT(" Half Duplex\r\n"); + } + } else { + DEBUGOUT("1000 Mbs, Full Duplex\r\n"); + *speed = SPEED_1000; + *duplex = FULL_DUPLEX; + } + +#if DBG + if(shared->phy_id == M88E1000_12_PHY_ID || + shared->phy_id == M88E1000_14_PHY_ID || + shared->phy_id == M88E1000_I_PHY_ID) { + /* read the phy specific status register */ + phy_data = e1000_read_phy_reg(shared, M88E1000_PHY_SPEC_STATUS); + DEBUGOUT1("M88E1000 Phy Specific Status Reg contents = %x\n", phy_data); + phy_data = e1000_read_phy_reg(shared, PHY_STATUS); + DEBUGOUT1("Phy MII Status Reg contents = %x\n", phy_data); + DEBUGOUT1("Device Status Reg contents = %x\n", + E1000_READ_REG(shared, STATUS)); + } +#endif + return; +} + +/****************************************************************************** + * Reads a 16 bit word from the EEPROM. + * + * shared - Struct containing variables accessed by shared code + * offset - offset of 16 bit word in the EEPROM to read + *****************************************************************************/ +uint16_t +e1000_read_eeprom(struct e1000_shared_adapter *shared, + uint16_t offset) +{ + uint16_t data; + + /* Prepare the EEPROM for reading */ + e1000_setup_eeprom(shared); + + /* Send the READ command (opcode + addr) */ + e1000_shift_out_bits(shared, EEPROM_READ_OPCODE, 3); + e1000_shift_out_bits(shared, offset, 6); + + /* Read the data */ + data = e1000_shift_in_bits(shared); + + /* End this read operation */ + e1000_standby_eeprom(shared); + + return (data); +} + +/****************************************************************************** + * Verifies that the EEPROM has a valid checksum + * + * shared - Struct containing variables accessed by shared code + * + * Reads the first 64 16 bit words of the EEPROM and sums the values read. + * If the the sum of the 64 16 bit words is 0xBABA, the EEPROM's checksum is + * valid. + *****************************************************************************/ +boolean_t +e1000_validate_eeprom_checksum(struct e1000_shared_adapter *shared) +{ + uint16_t checksum = 0; + uint16_t i; + + for(i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) + checksum += e1000_read_eeprom(shared, i); + + if(checksum == (uint16_t) EEPROM_SUM) + return (TRUE); + else + return (FALSE); +} +/****************************************************************************** + * Reads the adapter's part number from the EEPROM + * + * shared - Struct containing variables accessed by shared code + * part_num - Adapter's part number + *****************************************************************************/ +boolean_t +e1000_read_part_num(struct e1000_shared_adapter *shared, + uint32_t *part_num) +{ + uint16_t eeprom_word; + + DEBUGFUNC("e1000_read_part_num"); + + /* Don't read the EEPROM if we are stopped */ + if(shared->adapter_stopped) { + *part_num = 0; + return (FALSE); + } + + /* Get word 0 from EEPROM */ + eeprom_word = e1000_read_eeprom(shared, (uint16_t) (EEPROM_PBA_BYTE_1)); + + DEBUGOUT("Read first part number word\n"); + + /* Save word 0 in upper half is PartNumber */ + *part_num = (uint32_t) eeprom_word; + *part_num = *part_num << 16; + + /* Get word 1 from EEPROM */ + eeprom_word = + e1000_read_eeprom(shared, (uint16_t) (EEPROM_PBA_BYTE_1 + 1)); + + DEBUGOUT("Read second part number word\n"); + + /* Save word 1 in lower half of PartNumber */ + *part_num |= eeprom_word; + + /* read a valid part number */ + return (TRUE); +} + +void +e1000_read_mac_addr(struct e1000_shared_adapter * shared) +{ + uint16_t temp, x; + + for(x = 0; x < NODE_ADDRESS_SIZE; x += 2) { + temp = e1000_read_eeprom(shared, (uint16_t) + (EEPROM_NODE_ADDRESS_BYTE_0 + (x/2))); + shared->perm_mac_addr[x] = (uint8_t) (temp & 0x00FF); + shared->perm_mac_addr[x+1] = (uint8_t) (temp >> 8); + } + + for(x = 0; x < NODE_ADDRESS_SIZE; x++) + shared->mac_addr[x] = shared->perm_mac_addr[x]; +} + +/****************************************************************************** + * Adjusts the statistic counters when a frame is accepted by TBI_ACCEPT + * + * shared - Struct containing variables accessed by shared code + * frame_len - The length of the frame in question + * mac_addr - The Ethernet destination address of the frame in question + *****************************************************************************/ +uint32_t +e1000_tbi_adjust_stats(struct e1000_shared_adapter *shared, + struct e1000_shared_stats *stats, + uint32_t frame_len, + uint8_t *mac_addr) +{ + uint64_t carry_bit; + + /* First adjust the frame length. */ + frame_len--; + /* We need to adjust the statistics counters, since the hardware + * counters overcount this packet as a CRC error and undercount + * the packet as a good packet + */ + /* This packet should not be counted as a CRC error. */ + stats->crcerrs--; + /* This packet does count as a Good Packet Received. */ + stats->gprc++; + + /* Adjust the Good Octets received counters */ + carry_bit = 0x80000000 & stats->gorcl; + stats->gorcl += frame_len; + /* If the high bit of Gorcl (the low 32 bits of the Good Octets + * Received Count) was one before the addition, + * AND it is zero after, then we lost the carry out, + * need to add one to Gorch (Good Octets Received Count High). + * This could be simplified if all environments supported + * 64-bit integers. + */ + if(carry_bit && ((stats->gorcl & 0x80000000) == 0)) + stats->gorch++; + /* Is this a broadcast or multicast? Check broadcast first, + * since the test for a multicast frame will test positive on + * a broadcast frame. + */ + if((mac_addr[0] == (uint8_t) 0xff) && (mac_addr[1] == (uint8_t) 0xff)) + /* Broadcast packet */ + stats->bprc++; + else if(*mac_addr & 0x01) + /* Multicast packet */ + stats->mprc++; + + if(frame_len == shared->max_frame_size) { + /* In this case, the hardware has overcounted the number of + * oversize frames. + */ + if(stats->roc > 0) + stats->roc--; + } + + /* Adjust the bin counters when the extra byte put the frame in the + * wrong bin. Remember that the frame_len was adjusted above. + */ + if(frame_len == 64) { + stats->prc64++; + stats->prc127--; + } else if(frame_len == 127) { + stats->prc127++; + stats->prc255--; + } else if(frame_len == 255) { + stats->prc255++; + stats->prc511--; + } else if(frame_len == 511) { + stats->prc511++; + stats->prc1023--; + } else if(frame_len == 1023) { + stats->prc1023++; + stats->prc1522--; + } else if(frame_len == 1522) { + stats->prc1522++; + } + return frame_len; +} + +/****************************************************************************** + * Gets the current PCI bus type, speed, and width of the hardware + * + * shared - Struct containing variables accessed by shared code + *****************************************************************************/ +void +e1000_get_bus_info(struct e1000_shared_adapter *shared) +{ + uint32_t status_reg; + + if(shared->mac_type < e1000_82543) { + shared->bus_type = e1000_bus_type_unknown; + shared->bus_speed = e1000_bus_speed_unknown; + shared->bus_width = e1000_bus_width_unknown; + return; + } + + status_reg = E1000_READ_REG(shared, STATUS); + + shared->bus_type = (status_reg & E1000_STATUS_PCIX_MODE) ? + e1000_bus_type_pcix : e1000_bus_type_pci; + + if(shared->bus_type == e1000_bus_type_pci) { + shared->bus_speed = (status_reg & E1000_STATUS_PCI66) ? + e1000_bus_speed_66 : e1000_bus_speed_33; + } else { + switch (status_reg & E1000_STATUS_PCIX_SPEED) { + case E1000_STATUS_PCIX_SPEED_66: + shared->bus_speed = e1000_bus_speed_66; + break; + case E1000_STATUS_PCIX_SPEED_100: + shared->bus_speed = e1000_bus_speed_100; + break; + case E1000_STATUS_PCIX_SPEED_133: + shared->bus_speed = e1000_bus_speed_133; + break; + default: + shared->bus_speed = e1000_bus_speed_reserved; + break; + } + } + + shared->bus_width = (status_reg & E1000_STATUS_BUS64) ? + e1000_bus_width_64 : e1000_bus_width_32; + + return; +} diff -Nru a/drivers/net/e1000/e1000_mac.h b/drivers/net/e1000/e1000_mac.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e1000/e1000_mac.h Thu Mar 7 18:17:47 2002 @@ -0,0 +1,1383 @@ +/******************************************************************************* + + This software program is available to you under a choice of one of two + licenses. You may choose to be licensed under either the GNU General Public + License (GPL) Version 2, June 1991, available at + http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the + text of which follows: + + Recipient has requested a license and Intel Corporation ("Intel") is willing + to grant a license for the software entitled Linux Base Driver for the + Intel(R) PRO/1000 Family of Adapters (e1000) (the "Software") being provided + by Intel Corporation. The following definitions apply to this license: + + "Licensed Patents" means patent claims licensable by Intel Corporation which + are necessarily infringed by the use of sale of the Software alone or when + combined with the operating system referred to below. + + "Recipient" means the party to whom Intel delivers this Software. + + "Licensee" means Recipient and those third parties that receive a license to + any operating system available under the GNU Public License version 2.0 or + later. + + Copyright (c) 1999 - 2002 Intel Corporation. + All rights reserved. + + The license is provided to Recipient and Recipient's Licensees under the + following terms. + + Redistribution and use in source and binary forms of the Software, with or + without modification, are permitted provided that the following conditions + are met: + + Redistributions of source code of the Software may retain the above + copyright notice, this list of conditions and the following disclaimer. + + Redistributions in binary form of the Software may reproduce the above + copyright notice, this list of conditions and the following disclaimer in + the documentation and/or materials provided with the distribution. + + Neither the name of Intel Corporation nor the names of its contributors + shall be used to endorse or promote products derived from this Software + without specific prior written permission. + + Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, + royalty-free patent license under Licensed Patents to make, use, sell, offer + to sell, import and otherwise transfer the Software, if any, in source code + and object code form. This license shall include changes to the Software + that are error corrections or other minor changes to the Software that do + not add functionality or features when the Software is incorporated in any + version of an operating system that has been distributed under the GNU + General Public License 2.0 or later. This patent license shall apply to the + combination of the Software and any operating system licensed under the GNU + Public License version 2.0 or later if, at the time Intel provides the + Software to Recipient, such addition of the Software to the then publicly + available versions of such operating systems available under the GNU Public + License version 2.0 or later (whether in gold, beta or alpha form) causes + such combination to be covered by the Licensed Patents. The patent license + shall not apply to any other combinations which include the Software. NO + hardware per se is licensed hereunder. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. + +*******************************************************************************/ + +/* e1000_mac.h + * Structures, enums, and macros for the MAC + */ + +#ifndef _E1000_MAC_H_ +#define _E1000_MAC_H_ + +#include "e1000_osdep.h" + +/* Forward declarations of structures used by the shared code */ +struct e1000_shared_adapter; +struct e1000_shared_stats; + +/* Enumerated types specific to the e1000 hardware */ +/* Media Access Controlers */ +typedef enum { + e1000_82542_rev2_0 = 0, + e1000_82542_rev2_1, + e1000_82543, + e1000_82544, + e1000_num_macs +} e1000_mac_type; + +/* Media Types */ +typedef enum { + e1000_media_type_copper = 0, + e1000_media_type_fiber = 1, + e1000_num_media_types +} e1000_media_type; + +typedef enum { + e1000_10_half = 0, + e1000_10_full = 1, + e1000_100_half = 2, + e1000_100_full = 3 +} e1000_speed_duplex_type; + +/* Flow Control Settings */ +typedef enum { + e1000_fc_none = 0, + e1000_fc_rx_pause = 1, + e1000_fc_tx_pause = 2, + e1000_fc_full = 3, + e1000_fc_default = 0xFF +} e1000_fc_type; + +/* PCI bus types */ +typedef enum { + e1000_bus_type_unknown = 0, + e1000_bus_type_pci, + e1000_bus_type_pcix +} e1000_bus_type; + +/* PCI bus speeds */ +typedef enum { + e1000_bus_speed_unknown = 0, + e1000_bus_speed_33, + e1000_bus_speed_66, + e1000_bus_speed_100, + e1000_bus_speed_133, + e1000_bus_speed_reserved +} e1000_bus_speed; + +/* PCI bus widths */ +typedef enum { + e1000_bus_width_unknown = 0, + e1000_bus_width_32, + e1000_bus_width_64 +} e1000_bus_width; + + + +/* Function prototypes */ +/* Setup */ +void e1000_adapter_stop(struct e1000_shared_adapter *shared); +boolean_t e1000_init_hw(struct e1000_shared_adapter *shared); +void e1000_init_rx_addrs(struct e1000_shared_adapter *shared); + +/* Filters (multicast, vlan, receive) */ +void e1000_mc_addr_list_update(struct e1000_shared_adapter *shared, uint8_t * mc_addr_list, uint32_t mc_addr_count, uint32_t pad); +uint32_t e1000_hash_mc_addr(struct e1000_shared_adapter *shared, uint8_t * mc_addr); +void e1000_mta_set(struct e1000_shared_adapter *shared, uint32_t hash_value); +void e1000_rar_set(struct e1000_shared_adapter *shared, uint8_t * mc_addr, uint32_t rar_index); +void e1000_write_vfta(struct e1000_shared_adapter *shared, uint32_t offset, uint32_t value); +void e1000_clear_vfta(struct e1000_shared_adapter *shared); + +/* Link layer setup functions */ +boolean_t e1000_setup_fc_and_link(struct e1000_shared_adapter *shared); +boolean_t e1000_setup_pcs_link(struct e1000_shared_adapter *shared, uint32_t dev_ctrl_reg); +void e1000_config_fc_after_link_up(struct e1000_shared_adapter *shared); +void e1000_check_for_link(struct e1000_shared_adapter *shared); +void e1000_get_speed_and_duplex(struct e1000_shared_adapter *shared, uint16_t * speed, uint16_t * duplex); + +/* EEPROM Functions */ +uint16_t e1000_read_eeprom(struct e1000_shared_adapter *shared, uint16_t reg); +boolean_t e1000_validate_eeprom_checksum(struct e1000_shared_adapter *shared); + +/* Everything else */ +void e1000_clear_hw_cntrs(struct e1000_shared_adapter *shared); +boolean_t e1000_read_part_num(struct e1000_shared_adapter *shared, uint32_t * part_num); +void e1000_read_mac_addr(struct e1000_shared_adapter * shared); +void e1000_get_bus_info(struct e1000_shared_adapter *shared); +uint32_t e1000_tbi_adjust_stats(struct e1000_shared_adapter *shared, struct e1000_shared_stats *stats, uint32_t frame_len, uint8_t * mac_addr); +void e1000_write_pci_cfg(struct e1000_shared_adapter *shared, uint32_t reg, uint16_t * value); + +/* PCI Device IDs */ +#define E1000_DEV_ID_82542 0x1000 +#define E1000_DEV_ID_82543GC_FIBER 0x1001 +#define E1000_DEV_ID_82543GC_COPPER 0x1004 +#define E1000_DEV_ID_82544EI_COPPER 0x1008 +#define E1000_DEV_ID_82544EI_FIBER 0x1009 +#define E1000_DEV_ID_82544GC_COPPER 0x100C +#define E1000_DEV_ID_82544GC_LOM 0x100D +#define NUM_DEV_IDS 7 + +#define NODE_ADDRESS_SIZE 6 +#define ETH_LENGTH_OF_ADDRESS 6 + +/* MAC decode size is 128K - This is the size of BAR0 */ +#define MAC_DECODE_SIZE (128 * 1024) + +#define E1000_82542_2_0_REV_ID 2 +#define E1000_82542_2_1_REV_ID 3 + +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +/* The sizes (in bytes) of a ethernet packet */ +#define ENET_HEADER_SIZE 14 +#define MAXIMUM_ETHERNET_PACKET_SIZE 1514 /* Without FCS */ +#define MINIMUM_ETHERNET_PACKET_SIZE 60 /* Without FCS */ +#define CRC_LENGTH 4 +#define MAX_JUMBO_FRAME_SIZE 0x3F00 + + +/* 802.1q VLAN Packet Sizes */ +#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMAed) */ + +/* Ethertype field values */ +#define ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.3ac packet */ +#define ETHERNET_IP_TYPE 0x0800 /* IP packets */ +#define ETHERNET_ARP_TYPE 0x0806 /* Address Resolution Protocol (ARP) */ + +/* Packet Header defines */ +#define IP_PROTOCOL_TCP 6 +#define IP_PROTOCOL_UDP 0x11 + +/* This defines the bits that are set in the Interrupt Mask + * Set/Read Register. Each bit is documented below: + * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0) + * o RXSEQ = Receive Sequence Error + */ +#define POLL_IMS_ENABLE_MASK ( \ + E1000_IMS_RXDMT0 | \ + E1000_IMS_RXSEQ) + +/* This defines the bits that are set in the Interrupt Mask + * Set/Read Register. Each bit is documented below: + * o RXT0 = Receiver Timer Interrupt (ring 0) + * o TXDW = Transmit Descriptor Written Back + * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0) + * o RXSEQ = Receive Sequence Error + * o LSC = Link Status Change + */ +#define IMS_ENABLE_MASK ( \ + E1000_IMS_RXT0 | \ + E1000_IMS_TXDW | \ + E1000_IMS_RXDMT0 | \ + E1000_IMS_RXSEQ | \ + E1000_IMS_LSC) + +/* The number of high/low register pairs in the RAR. The RAR (Receive Address + * Registers) holds the directed and multicast addresses that we monitor. We + * reserve one of these spots for our directed address, allowing us room for + * E1000_RAR_ENTRIES - 1 multicast addresses. + */ +#define E1000_RAR_ENTRIES 16 + +#define MIN_NUMBER_OF_DESCRIPTORS 8 +#define MAX_NUMBER_OF_DESCRIPTORS 0xFFF8 + +/* Receive Descriptor */ +struct e1000_rx_desc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + uint16_t length; /* Length of data DMAed into data buffer */ + uint16_t csum; /* Packet checksum */ + uint8_t status; /* Descriptor status */ + uint8_t errors; /* Descriptor Errors */ + uint16_t special; +}; + +/* Receive Decriptor bit definitions */ +#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ +#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ +#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */ +#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */ +#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */ +#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */ +#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */ +#define E1000_RXD_ERR_CE 0x01 /* CRC Error */ +#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */ +#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */ +#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */ +#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */ +#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */ +#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */ +#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */ +#define E1000_RXD_SPC_PRI_MASK 0xE000 /* Priority is in upper 3 bits */ +#define E1000_RXD_SPC_PRI_SHIFT 0x000D /* Priority is in upper 3 of 16 */ +#define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */ +#define E1000_RXD_SPC_CFI_SHIFT 0x000C /* CFI is bit 12 */ + +/* mask to determine if packets should be dropped due to frame errors */ +#define E1000_RXD_ERR_FRAME_ERR_MASK ( \ + E1000_RXD_ERR_CE | \ + E1000_RXD_ERR_SE | \ + E1000_RXD_ERR_SEQ | \ + E1000_RXD_ERR_CXE | \ + E1000_RXD_ERR_RXE) + +/* Transmit Descriptor */ +struct e1000_tx_desc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + union { + uint32_t data; + struct { + uint16_t length; /* Data buffer length */ + uint8_t cso; /* Checksum offset */ + uint8_t cmd; /* Descriptor control */ + } flags; + } lower; + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t css; /* Checksum start */ + uint16_t special; + } fields; + } upper; +}; + +/* Transmit Descriptor bit definitions */ +#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ +#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ +#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */ +#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */ +#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ +#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */ +#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ +#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */ +#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */ +#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ +#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */ +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ +#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ +#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ + +/* Offload Context Descriptor */ +struct e1000_context_desc { + union { + uint32_t ip_config; + struct { + uint8_t ipcss; /* IP checksum start */ + uint8_t ipcso; /* IP checksum offset */ + uint16_t ipcse; /* IP checksum end */ + } ip_fields; + } lower_setup; + union { + uint32_t tcp_config; + struct { + uint8_t tucss; /* TCP checksum start */ + uint8_t tucso; /* TCP checksum offset */ + uint16_t tucse; /* TCP checksum end */ + } tcp_fields; + } upper_setup; + uint32_t cmd_and_length; /* */ + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t hdr_len; /* Header length */ + uint16_t mss; /* Maximum segment size */ + } fields; + } tcp_seg_setup; +}; + +/* Offload data descriptor */ +struct e1000_data_desc { + uint64_t buffer_addr; /* Address of the descriptor's buffer address */ + union { + uint32_t data; + struct { + uint16_t length; /* Data buffer length */ + uint8_t typ_len_ext; /* */ + uint8_t cmd; /* */ + } flags; + } lower; + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t popts; /* Packet Options */ + uint16_t special; /* */ + } fields; + } upper; +}; + +/* Filters */ +#define E1000_NUM_UNICAST 16 /* Unicast filter entries */ +#define E1000_MC_TBL_SIZE 128 /* Multicast Filter Table (4096 bits) */ +#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */ + + +/* Receive Address Register */ +struct e1000_rar { + volatile uint32_t low; /* receive address low */ + volatile uint32_t high; /* receive address high */ +}; + +/* The number of entries in the Multicast Table Array (MTA). */ +#define E1000_NUM_MTA_REGISTERS 128 + +/* IPv4 Address Table Entry */ +struct e1000_ipv4_at_entry { + volatile uint32_t ipv4_addr; /* IP Address (RW) */ + volatile uint32_t reserved; +}; + +/* Four wakeup IP addresses are supported */ +#define E1000_WAKEUP_IP_ADDRESS_COUNT_MAX 4 +#define E1000_IP4AT_SIZE E1000_WAKEUP_IP_ADDRESS_COUNT_MAX +#define E1000_IP6AT_SIZE 1 + +/* IPv6 Address Table Entry */ +struct e1000_ipv6_at_entry { + volatile uint8_t ipv6_addr[16]; +}; + +/* Flexible Filter Length Table Entry */ +struct e1000_fflt_entry { + volatile uint32_t length; /* Flexible Filter Length (RW) */ + volatile uint32_t reserved; +}; + +/* Flexible Filter Mask Table Entry */ +struct e1000_ffmt_entry { + volatile uint32_t mask; /* Flexible Filter Mask (RW) */ + volatile uint32_t reserved; +}; + +/* Flexible Filter Value Table Entry */ +struct e1000_ffvt_entry { + volatile uint32_t value; /* Flexible Filter Value (RW) */ + volatile uint32_t reserved; +}; + +/* Four Flexible Filters are supported */ +#define E1000_FLEXIBLE_FILTER_COUNT_MAX 4 + +/* Each Flexible Filter is at most 128 (0x80) bytes in length */ +#define E1000_FLEXIBLE_FILTER_SIZE_MAX 128 + +#define E1000_FFLT_SIZE E1000_FLEXIBLE_FILTER_COUNT_MAX +#define E1000_FFMT_SIZE E1000_FLEXIBLE_FILTER_SIZE_MAX +#define E1000_FFVT_SIZE E1000_FLEXIBLE_FILTER_SIZE_MAX + +/* Register Set. (82543, 82544) + * + * Registers are defined to be 32 bits and should be accessed as 32 bit values. + * These registers are physically located on the NIC, but are mapped into the + * host memory address space. + * + * RW - register is both readable and writable + * RO - register is read only + * WO - register is write only + * R/clr - register is read only and is cleared when read + * A - register array + */ +#define E1000_CTRL 0x00000 /* Device Control - RW */ +#define E1000_STATUS 0x00008 /* Device Status - RO */ +#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ +#define E1000_EERD 0x00014 /* EEPROM Read - RW */ +#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ +#define E1000_MDIC 0x00020 /* MDI Control - RW */ +#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ +#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ +#define E1000_FCT 0x00030 /* Flow Control Type - RW */ +#define E1000_VET 0x00038 /* VLAN Ether Type - RW */ +#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */ +#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */ +#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ +#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ +#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ +#define E1000_RCTL 0x00100 /* RX Control - RW */ +#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */ +#define E1000_TXCW 0x00178 /* TX Configuration Word - RW */ +#define E1000_RXCW 0x00180 /* RX Configuration Word - RO */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ +#define E1000_TBT 0x00448 /* TX Burst Timer - RW */ +#define E1000_LEDCTL 0x00E00 /* LED Control - RW */ +#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ +#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */ +#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */ +#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ +#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */ +#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */ +#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */ +#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */ +#define E1000_RDTR 0x02820 /* RX Delay Timer - RW */ +#define E1000_RXDCTL 0x02828 /* RX Descriptor Control - RW */ +#define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - RW */ +#define E1000_RSRPD 0x02C00 /* RX Small Packet Detect - RW */ +#define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */ +#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ +#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ +#define E1000_TIDV 0x03820 /* TX Interrupt Delay Value - RW */ +#define E1000_TXDCTL 0x03828 /* TX Descriptor Control - RW */ +#define E1000_TADV 0x0382C /* TX Interrupt Absolute Delay Val - RW */ +#define E1000_TSPMT 0x03830 /* TCP Segmentation PAD & Min Threshold - RW */ +#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */ +#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */ +#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */ +#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */ +#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */ +#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */ +#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */ +#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */ +#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */ +#define E1000_COLC 0x04028 /* Collision Count - R/clr */ +#define E1000_DC 0x04030 /* Defer Count - R/clr */ +#define E1000_TNCRS 0x04034 /* TX-No CRS - R/clr */ +#define E1000_SEC 0x04038 /* Sequence Error Count - R/clr */ +#define E1000_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */ +#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */ +#define E1000_XONRXC 0x04048 /* XON RX Count - R/clr */ +#define E1000_XONTXC 0x0404C /* XON TX Count - R/clr */ +#define E1000_XOFFRXC 0x04050 /* XOFF RX Count - R/clr */ +#define E1000_XOFFTXC 0x04054 /* XOFF TX Count - R/clr */ +#define E1000_FCRUC 0x04058 /* Flow Control RX Unsupported Count- R/clr */ +#define E1000_PRC64 0x0405C /* Packets RX (64 bytes) - R/clr */ +#define E1000_PRC127 0x04060 /* Packets RX (65-127 bytes) - R/clr */ +#define E1000_PRC255 0x04064 /* Packets RX (128-255 bytes) - R/clr */ +#define E1000_PRC511 0x04068 /* Packets RX (255-511 bytes) - R/clr */ +#define E1000_PRC1023 0x0406C /* Packets RX (512-1023 bytes) - R/clr */ +#define E1000_PRC1522 0x04070 /* Packets RX (1024-1522 bytes) - R/clr */ +#define E1000_GPRC 0x04074 /* Good Packets RX Count - R/clr */ +#define E1000_BPRC 0x04078 /* Broadcast Packets RX Count - R/clr */ +#define E1000_MPRC 0x0407C /* Multicast Packets RX Count - R/clr */ +#define E1000_GPTC 0x04080 /* Good Packets TX Count - R/clr */ +#define E1000_GORCL 0x04088 /* Good Octets RX Count Low - R/clr */ +#define E1000_GORCH 0x0408C /* Good Octets RX Count High - R/clr */ +#define E1000_GOTCL 0x04090 /* Good Octets TX Count Low - R/clr */ +#define E1000_GOTCH 0x04094 /* Good Octets TX Count High - R/clr */ +#define E1000_RNBC 0x040A0 /* RX No Buffers Count - R/clr */ +#define E1000_RUC 0x040A4 /* RX Undersize Count - R/clr */ +#define E1000_RFC 0x040A8 /* RX Fragment Count - R/clr */ +#define E1000_ROC 0x040AC /* RX Oversize Count - R/clr */ +#define E1000_RJC 0x040B0 /* RX Jabber Count - R/clr */ +#define E1000_MGTPRC 0x040B4 /* Management Packets RX Count - R/clr */ +#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */ +#define E1000_MGTPTC 0x040BC /* Management Packets TX Count - R/clr */ +#define E1000_TORL 0x040C0 /* Total Octets RX Low - R/clr */ +#define E1000_TORH 0x040C4 /* Total Octets RX High - R/clr */ +#define E1000_TOTL 0x040C8 /* Total Octets TX Low - R/clr */ +#define E1000_TOTH 0x040CC /* Total Octets TX High - R/clr */ +#define E1000_TPR 0x040D0 /* Total Packets RX - R/clr */ +#define E1000_TPT 0x040D4 /* Total Packets TX - R/clr */ +#define E1000_PTC64 0x040D8 /* Packets TX (64 bytes) - R/clr */ +#define E1000_PTC127 0x040DC /* Packets TX (65-127 bytes) - R/clr */ +#define E1000_PTC255 0x040E0 /* Packets TX (128-255 bytes) - R/clr */ +#define E1000_PTC511 0x040E4 /* Packets TX (256-511 bytes) - R/clr */ +#define E1000_PTC1023 0x040E8 /* Packets TX (512-1023 bytes) - R/clr */ +#define E1000_PTC1522 0x040EC /* Packets TX (1024-1522 Bytes) - R/clr */ +#define E1000_MPTC 0x040F0 /* Multicast Packets TX Count - R/clr */ +#define E1000_BPTC 0x040F4 /* Broadcast Packets TX Count - R/clr */ +#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context TX - R/clr */ +#define E1000_TSCTFC 0x040FC /* TCP Segmentation Context TX Fail - R/clr */ +#define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */ +#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ +#define E1000_RA 0x05400 /* Receive Address - RW Array */ +#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */ +#define E1000_WUC 0x05800 /* Wakeup Control - RW */ +#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */ +#define E1000_WUS 0x05810 /* Wakeup Status - RO */ +#define E1000_MANC 0x05820 /* Management Control - RW */ +#define E1000_IPAV 0x05838 /* IP Address Valid - RW */ +#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */ +#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */ +#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */ +#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */ +#define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */ +#define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ +#define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */ + +/* Register Set (82542) + * + * Some of the 82542 registers are located at different offsets than they are + * in more current versions of the 8254x. Despite the difference in location, + * the registers function in the same manner. + */ +#define E1000_82542_CTRL E1000_CTRL +#define E1000_82542_STATUS E1000_STATUS +#define E1000_82542_EECD E1000_EECD +#define E1000_82542_EERD E1000_EERD +#define E1000_82542_CTRL_EXT E1000_CTRL_EXT +#define E1000_82542_MDIC E1000_MDIC +#define E1000_82542_FCAL E1000_FCAL +#define E1000_82542_FCAH E1000_FCAH +#define E1000_82542_FCT E1000_FCT +#define E1000_82542_VET E1000_VET +#define E1000_82542_RA 0x00040 +#define E1000_82542_ICR E1000_ICR +#define E1000_82542_ITR E1000_ITR +#define E1000_82542_ICS E1000_ICS +#define E1000_82542_IMS E1000_IMS +#define E1000_82542_IMC E1000_IMC +#define E1000_82542_RCTL E1000_RCTL +#define E1000_82542_RDTR 0x00108 +#define E1000_82542_RDBAL 0x00110 +#define E1000_82542_RDBAH 0x00114 +#define E1000_82542_RDLEN 0x00118 +#define E1000_82542_RDH 0x00120 +#define E1000_82542_RDT 0x00128 +#define E1000_82542_FCRTH 0x00160 +#define E1000_82542_FCRTL 0x00168 +#define E1000_82542_FCTTV E1000_FCTTV +#define E1000_82542_TXCW E1000_TXCW +#define E1000_82542_RXCW E1000_RXCW +#define E1000_82542_MTA 0x00200 +#define E1000_82542_TCTL E1000_TCTL +#define E1000_82542_TIPG E1000_TIPG +#define E1000_82542_TDBAL 0x00420 +#define E1000_82542_TDBAH 0x00424 +#define E1000_82542_TDLEN 0x00428 +#define E1000_82542_TDH 0x00430 +#define E1000_82542_TDT 0x00438 +#define E1000_82542_TIDV 0x00440 +#define E1000_82542_TBT E1000_TBT +#define E1000_82542_VFTA 0x00600 +#define E1000_82542_LEDCTL E1000_LEDCTL +#define E1000_82542_PBA E1000_PBA +#define E1000_82542_RXDCTL E1000_RXDCTL +#define E1000_82542_RADV E1000_RADV +#define E1000_82542_RSRPD E1000_RSRPD +#define E1000_82542_TXDMAC E1000_TXDMAC +#define E1000_82542_TXDCTL E1000_TXDCTL +#define E1000_82542_TADV E1000_TADV +#define E1000_82542_TSPMT E1000_TSPMT +#define E1000_82542_CRCERRS E1000_CRCERRS +#define E1000_82542_ALGNERRC E1000_ALGNERRC +#define E1000_82542_SYMERRS E1000_SYMERRS +#define E1000_82542_RXERRC E1000_RXERRC +#define E1000_82542_MPC E1000_MPC +#define E1000_82542_SCC E1000_SCC +#define E1000_82542_ECOL E1000_ECOL +#define E1000_82542_MCC E1000_MCC +#define E1000_82542_LATECOL E1000_LATECOL +#define E1000_82542_COLC E1000_COLC +#define E1000_82542_DC E1000_DC +#define E1000_82542_TNCRS E1000_TNCRS +#define E1000_82542_SEC E1000_SEC +#define E1000_82542_CEXTERR E1000_CEXTERR +#define E1000_82542_RLEC E1000_RLEC +#define E1000_82542_XONRXC E1000_XONRXC +#define E1000_82542_XONTXC E1000_XONTXC +#define E1000_82542_XOFFRXC E1000_XOFFRXC +#define E1000_82542_XOFFTXC E1000_XOFFTXC +#define E1000_82542_FCRUC E1000_FCRUC +#define E1000_82542_PRC64 E1000_PRC64 +#define E1000_82542_PRC127 E1000_PRC127 +#define E1000_82542_PRC255 E1000_PRC255 +#define E1000_82542_PRC511 E1000_PRC511 +#define E1000_82542_PRC1023 E1000_PRC1023 +#define E1000_82542_PRC1522 E1000_PRC1522 +#define E1000_82542_GPRC E1000_GPRC +#define E1000_82542_BPRC E1000_BPRC +#define E1000_82542_MPRC E1000_MPRC +#define E1000_82542_GPTC E1000_GPTC +#define E1000_82542_GORCL E1000_GORCL +#define E1000_82542_GORCH E1000_GORCH +#define E1000_82542_GOTCL E1000_GOTCL +#define E1000_82542_GOTCH E1000_GOTCH +#define E1000_82542_RNBC E1000_RNBC +#define E1000_82542_RUC E1000_RUC +#define E1000_82542_RFC E1000_RFC +#define E1000_82542_ROC E1000_ROC +#define E1000_82542_RJC E1000_RJC +#define E1000_82542_MGTPRC E1000_MGTPRC +#define E1000_82542_MGTPDC E1000_MGTPDC +#define E1000_82542_MGTPTC E1000_MGTPTC +#define E1000_82542_TORL E1000_TORL +#define E1000_82542_TORH E1000_TORH +#define E1000_82542_TOTL E1000_TOTL +#define E1000_82542_TOTH E1000_TOTH +#define E1000_82542_TPR E1000_TPR +#define E1000_82542_TPT E1000_TPT +#define E1000_82542_PTC64 E1000_PTC64 +#define E1000_82542_PTC127 E1000_PTC127 +#define E1000_82542_PTC255 E1000_PTC255 +#define E1000_82542_PTC511 E1000_PTC511 +#define E1000_82542_PTC1023 E1000_PTC1023 +#define E1000_82542_PTC1522 E1000_PTC1522 +#define E1000_82542_MPTC E1000_MPTC +#define E1000_82542_BPTC E1000_BPTC +#define E1000_82542_TSCTC E1000_TSCTC +#define E1000_82542_TSCTFC E1000_TSCTFC +#define E1000_82542_RXCSUM E1000_RXCSUM +#define E1000_82542_WUC E1000_WUC +#define E1000_82542_WUFC E1000_WUFC +#define E1000_82542_WUS E1000_WUS +#define E1000_82542_MANC E1000_MANC +#define E1000_82542_IPAV E1000_IPAV +#define E1000_82542_IP4AT E1000_IP4AT +#define E1000_82542_IP6AT E1000_IP6AT +#define E1000_82542_WUPL E1000_WUPL +#define E1000_82542_WUPM E1000_WUPM +#define E1000_82542_FFLT E1000_FFLT +#define E1000_82542_FFMT E1000_FFMT +#define E1000_82542_FFVT E1000_FFVT + +/* Statistics counters collected by the MAC */ +struct e1000_shared_stats { + uint64_t crcerrs; + uint64_t algnerrc; + uint64_t symerrs; + uint64_t rxerrc; + uint64_t mpc; + uint64_t scc; + uint64_t ecol; + uint64_t mcc; + uint64_t latecol; + uint64_t colc; + uint64_t dc; + uint64_t tncrs; + uint64_t sec; + uint64_t cexterr; + uint64_t rlec; + uint64_t xonrxc; + uint64_t xontxc; + uint64_t xoffrxc; + uint64_t xofftxc; + uint64_t fcruc; + uint64_t prc64; + uint64_t prc127; + uint64_t prc255; + uint64_t prc511; + uint64_t prc1023; + uint64_t prc1522; + uint64_t gprc; + uint64_t bprc; + uint64_t mprc; + uint64_t gptc; + uint64_t gorcl; + uint64_t gorch; + uint64_t gotcl; + uint64_t gotch; + uint64_t rnbc; + uint64_t ruc; + uint64_t rfc; + uint64_t roc; + uint64_t rjc; + uint64_t mgprc; + uint64_t mgpdc; + uint64_t mgptc; + uint64_t torl; + uint64_t torh; + uint64_t totl; + uint64_t toth; + uint64_t tpr; + uint64_t tpt; + uint64_t ptc64; + uint64_t ptc127; + uint64_t ptc255; + uint64_t ptc511; + uint64_t ptc1023; + uint64_t ptc1522; + uint64_t mptc; + uint64_t bptc; + uint64_t tsctc; + uint64_t tsctfc; +}; + +/* Structure containing variables used by the shared code (e1000_mac.c and + * e1000_phy.c) + */ +struct e1000_shared_adapter { + uint8_t *hw_addr; + e1000_mac_type mac_type; + e1000_media_type media_type; + void *back; + e1000_fc_type fc; + e1000_bus_speed bus_speed; + e1000_bus_width bus_width; + e1000_bus_type bus_type; + uint32_t phy_id; + uint32_t phy_addr; + uint32_t original_fc; + uint32_t txcw_reg; + uint32_t autoneg_failed; + uint32_t max_frame_size; + uint32_t min_frame_size; + uint32_t mc_filter_type; + uint32_t num_mc_addrs; + uint16_t autoneg_advertised; + uint16_t pci_cmd_word; + uint16_t fc_high_water; + uint16_t fc_low_water; + uint16_t fc_pause_time; + uint16_t device_id; + uint16_t vendor_id; + uint16_t subsystem_id; + uint16_t subsystem_vendor_id; + uint8_t revision_id; + boolean_t disable_polarity_correction; + boolean_t get_link_status; + boolean_t tbi_compatibility_en; + boolean_t tbi_compatibility_on; + boolean_t adapter_stopped; + boolean_t fc_send_xon; + boolean_t report_tx_early; + uint8_t autoneg; + uint8_t mdix; + uint8_t forced_speed_duplex; + uint8_t wait_autoneg_complete; + uint8_t dma_fairness; + uint8_t mac_addr[NODE_ADDRESS_SIZE]; + uint8_t perm_mac_addr[NODE_ADDRESS_SIZE]; +}; + + +#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */ +#define E1000_EEPROM_LED_LOGIC 0x0020 /* Led Logic Word */ + +/* Register Bit Masks */ +/* Device Control */ +#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ +#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */ +#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */ +#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */ +#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */ +#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */ +#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */ +#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */ +#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */ +#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */ +#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */ +#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */ +#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */ +#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */ +#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */ +#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */ +#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ +#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ +#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */ +#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */ +#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */ +#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */ +#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */ +#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */ +#define E1000_CTRL_RST 0x04000000 /* Global reset */ +#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */ +#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */ +#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */ +#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ +#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ + +/* Device Status */ +#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */ +#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */ +#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */ +#define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */ +#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */ +#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */ +#define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */ +#define E1000_STATUS_SPEED_MASK 0x000000C0 +#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */ +#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */ +#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */ +#define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */ +#define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */ +#define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */ +#define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */ +#define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ +#define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */ + +/* Constants used to intrepret the masked PCI-X bus speed. */ +#define E1000_STATUS_PCIX_SPEED_66 0x00000000 /* PCI-X bus speed 50-66 MHz */ +#define E1000_STATUS_PCIX_SPEED_100 0x00004000 /* PCI-X bus speed 66-100 MHz */ +#define E1000_STATUS_PCIX_SPEED_133 0x00008000 /* PCI-X bus speed 100-133 MHz */ + +/* EEPROM/Flash Control */ +#define E1000_EECD_SK 0x00000001 /* EEPROM Clock */ +#define E1000_EECD_CS 0x00000002 /* EEPROM Chip Select */ +#define E1000_EECD_DI 0x00000004 /* EEPROM Data In */ +#define E1000_EECD_DO 0x00000008 /* EEPROM Data Out */ +#define E1000_EECD_FWE_MASK 0x00000030 +#define E1000_EECD_FWE_DIS 0x00000010 /* Disable FLASH writes */ +#define E1000_EECD_FWE_EN 0x00000020 /* Enable FLASH writes */ +#define E1000_EECD_FWE_SHIFT 4 +#define E1000_EECD_SIZE 0x00000200 /* EEPROM Size (0=64 word 1=256 word) */ +#define E1000_EECD_REQ 0x00000040 /* EEPROM Access Request */ +#define E1000_EECD_GNT 0x00000080 /* EEPROM Access Grant */ +#define E1000_EECD_PRES 0x00000100 /* EEPROM Present */ + +/* EEPROM Read */ +#define E1000_EERD_START 0x00000001 /* Start Read */ +#define E1000_EERD_DONE 0x00000010 /* Read Done */ +#define E1000_EERD_ADDR_SHIFT 8 +#define E1000_EERD_ADDR_MASK 0x0000FF00 /* Read Address */ +#define E1000_EERD_DATA_SHIFT 16 +#define E1000_EERD_DATA_MASK 0xFFFF0000 /* Read Data */ + +/* Extended Device Control */ +#define E1000_CTRL_EXT_GPI0_EN 0x00000001 /* Maps SDP4 to GPI0 */ +#define E1000_CTRL_EXT_GPI1_EN 0x00000002 /* Maps SDP5 to GPI1 */ +#define E1000_CTRL_EXT_PHYINT_EN E1000_CTRL_EXT_GPI1_EN +#define E1000_CTRL_EXT_GPI2_EN 0x00000004 /* Maps SDP6 to GPI2 */ +#define E1000_CTRL_EXT_GPI3_EN 0x00000008 /* Maps SDP7 to GPI3 */ +#define E1000_CTRL_EXT_SDP4_DATA 0x00000010 /* Value of SW Defineable Pin 4 */ +#define E1000_CTRL_EXT_SDP5_DATA 0x00000020 /* Value of SW Defineable Pin 5 */ +#define E1000_CTRL_EXT_PHY_INT E1000_CTRL_EXT_SDP5_DATA +#define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* Value of SW Defineable Pin 6 */ +#define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Defineable Pin 7 */ +#define E1000_CTRL_EXT_SDP4_DIR 0x00000100 /* Direction of SDP4 0=in 1=out */ +#define E1000_CTRL_EXT_SDP5_DIR 0x00000200 /* Direction of SDP5 0=in 1=out */ +#define E1000_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */ +#define E1000_CTRL_EXT_SDP7_DIR 0x00000800 /* Direction of SDP7 0=in 1=out */ +#define E1000_CTRL_EXT_ASDCHK 0x00001000 /* Initiate an ASD sequence */ +#define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */ +#define E1000_CTRL_EXT_IPS 0x00004000 /* Invert Power State */ +#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ +#define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000 +#define E1000_CTRL_EXT_LINK_MODE_TBI 0x00C00000 +#define E1000_CTRL_EXT_WR_WMARK_MASK 0x03000000 +#define E1000_CTRL_EXT_WR_WMARK_256 0x00000000 +#define E1000_CTRL_EXT_WR_WMARK_320 0x01000000 +#define E1000_CTRL_EXT_WR_WMARK_384 0x02000000 +#define E1000_CTRL_EXT_WR_WMARK_448 0x03000000 + +/* MDI Control */ +#define E1000_MDIC_DATA_MASK 0x0000FFFF +#define E1000_MDIC_REG_MASK 0x001F0000 +#define E1000_MDIC_REG_SHIFT 16 +#define E1000_MDIC_PHY_MASK 0x03E00000 +#define E1000_MDIC_PHY_SHIFT 21 +#define E1000_MDIC_OP_WRITE 0x04000000 +#define E1000_MDIC_OP_READ 0x08000000 +#define E1000_MDIC_READY 0x10000000 +#define E1000_MDIC_INT_EN 0x20000000 +#define E1000_MDIC_ERROR 0x40000000 + +/* LED Control */ +#define E1000_LEDCTL_LED0_MODE_MASK 0x0000000F +#define E1000_LEDCTL_LED0_MODE_SHIFT 0 +#define E1000_LEDCTL_LED0_IVRT 0x00000040 +#define E1000_LEDCTL_LED0_BLINK 0x00000080 +#define E1000_LEDCTL_LED1_MODE_MASK 0x00000F00 +#define E1000_LEDCTL_LED1_MODE_SHIFT 8 +#define E1000_LEDCTL_LED1_IVRT 0x00004000 +#define E1000_LEDCTL_LED1_BLINK 0x00008000 +#define E1000_LEDCTL_LED2_MODE_MASK 0x000F0000 +#define E1000_LEDCTL_LED2_MODE_SHIFT 16 +#define E1000_LEDCTL_LED2_IVRT 0x00400000 +#define E1000_LEDCTL_LED2_BLINK 0x00800000 +#define E1000_LEDCTL_LED3_MODE_MASK 0x0F000000 +#define E1000_LEDCTL_LED3_MODE_SHIFT 24 +#define E1000_LEDCTL_LED3_IVRT 0x40000000 +#define E1000_LEDCTL_LED3_BLINK 0x80000000 + +#define E1000_LEDCTL_MODE_LINK_10_1000 0x0 +#define E1000_LEDCTL_MODE_LINK_100_1000 0x1 +#define E1000_LEDCTL_MODE_LINK_UP 0x2 +#define E1000_LEDCTL_MODE_ACTIVITY 0x3 +#define E1000_LEDCTL_MODE_LINK_ACTIVITY 0x4 +#define E1000_LEDCTL_MODE_LINK_10 0x5 +#define E1000_LEDCTL_MODE_LINK_100 0x6 +#define E1000_LEDCTL_MODE_LINK_1000 0x7 +#define E1000_LEDCTL_MODE_PCIX_MODE 0x8 +#define E1000_LEDCTL_MODE_FULL_DUPLEX 0x9 +#define E1000_LEDCTL_MODE_COLLISION 0xA +#define E1000_LEDCTL_MODE_BUS_SPEED 0xB +#define E1000_LEDCTL_MODE_BUS_SIZE 0xC +#define E1000_LEDCTL_MODE_PAUSED 0xD +#define E1000_LEDCTL_MODE_LED_ON 0xE +#define E1000_LEDCTL_MODE_LED_OFF 0xF + +/* Receive Address */ +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ + +/* Interrupt Cause Read */ +#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ +#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */ +#define E1000_ICR_LSC 0x00000004 /* Link Status Change */ +#define E1000_ICR_RXSEQ 0x00000008 /* rx sequence error */ +#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */ +#define E1000_ICR_RXO 0x00000040 /* rx overrun */ +#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */ +#define E1000_ICR_MDAC 0x00000200 /* MDIO access complete */ +#define E1000_ICR_RXCFG 0x00000400 /* RX /c/ ordered set */ +#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */ +#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */ +#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */ +#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */ +#define E1000_ICR_TXD_LOW 0x00008000 +#define E1000_ICR_SRPD 0x00010000 + +/* Interrupt Cause Set */ +#define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_ICS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_ICS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_ICS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_ICS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_ICS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_ICS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_ICS_SRPD E1000_ICR_SRPD + +/* Interrupt Mask Set */ +#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMS_SRPD E1000_ICR_SRPD + +/* Interrupt Mask Clear */ +#define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMC_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMC_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMC_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMC_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMC_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMC_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMC_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMC_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMC_SRPD E1000_ICR_SRPD + +/* Receive Control */ +#define E1000_RCTL_RST 0x00000001 /* Software reset */ +#define E1000_RCTL_EN 0x00000002 /* enable */ +#define E1000_RCTL_SBP 0x00000004 /* store bad packet */ +#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */ +#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */ +#define E1000_RCTL_LPE 0x00000020 /* long packet enable */ +#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */ +#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */ +#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */ +#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */ +#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */ +#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */ +#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */ +#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */ +#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */ +#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */ +#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */ +#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */ +#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */ +#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */ +#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */ +#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */ +#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */ +#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */ +#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */ +#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */ +#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */ +#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */ +#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */ +#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */ +#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */ + +/* Receive Descriptor */ +#define E1000_RDT_DELAY 0x0000ffff /* Delay timer (1=1024us) */ +#define E1000_RDT_FPDB 0x80000000 /* Flush descriptor block */ +#define E1000_RDLEN_LEN 0x0007ff80 /* descriptor length */ +#define E1000_RDH_RDH 0x0000ffff /* receive descriptor head */ +#define E1000_RDT_RDT 0x0000ffff /* receive descriptor tail */ + +/* Flow Control */ +#define E1000_FCRTH_RTH 0x0000FFF8 /* Mask Bits[15:3] for RTH */ +#define E1000_FCRTH_XFCE 0x80000000 /* External Flow Control Enable */ +#define E1000_FCRTL_RTL 0x0000FFF8 /* Mask Bits[15:3] for RTL */ +#define E1000_FCRTL_XONE 0x80000000 /* Enable XON frame transmission */ + +/* Receive Descriptor Control */ +#define E1000_RXDCTL_PTHRESH 0x0000003F /* RXDCTL Prefetch Threshold */ +#define E1000_RXDCTL_HTHRESH 0x00003F00 /* RXDCTL Host Threshold */ +#define E1000_RXDCTL_WTHRESH 0x003F0000 /* RXDCTL Writeback Threshold */ +#define E1000_RXDCTL_GRAN 0x01000000 /* RXDCTL Granularity */ + +/* Transmit Descriptor Control */ +#define E1000_TXDCTL_PTHRESH 0x000000FF /* TXDCTL Prefetch Threshold */ +#define E1000_TXDCTL_HTHRESH 0x0000FF00 /* TXDCTL Host Threshold */ +#define E1000_TXDCTL_WTHRESH 0x00FF0000 /* TXDCTL Writeback Threshold */ +#define E1000_TXDCTL_GRAN 0x01000000 /* TXDCTL Granularity */ +#define E1000_TXDCTL_LWTHRESH 0xFE000000 /* TXDCTL Low Threshold */ + +/* Transmit Configuration Word */ +#define E1000_TXCW_FD 0x00000020 /* TXCW full duplex */ +#define E1000_TXCW_HD 0x00000040 /* TXCW half duplex */ +#define E1000_TXCW_PAUSE 0x00000080 /* TXCW sym pause request */ +#define E1000_TXCW_ASM_DIR 0x00000100 /* TXCW astm pause direction */ +#define E1000_TXCW_PAUSE_MASK 0x00000180 /* TXCW pause request mask */ +#define E1000_TXCW_RF 0x00003000 /* TXCW remote fault */ +#define E1000_TXCW_NP 0x00008000 /* TXCW next page */ +#define E1000_TXCW_CW 0x0000ffff /* TxConfigWord mask */ +#define E1000_TXCW_TXC 0x40000000 /* Transmit Config control */ +#define E1000_TXCW_ANE 0x80000000 /* Auto-neg enable */ + +/* Receive Configuration Word */ +#define E1000_RXCW_CW 0x0000ffff /* RxConfigWord mask */ +#define E1000_RXCW_NC 0x04000000 /* Receive config no carrier */ +#define E1000_RXCW_IV 0x08000000 /* Receive config invalid */ +#define E1000_RXCW_CC 0x10000000 /* Receive config change */ +#define E1000_RXCW_C 0x20000000 /* Receive config */ +#define E1000_RXCW_SYNCH 0x40000000 /* Receive config synch */ +#define E1000_RXCW_ANC 0x80000000 /* Auto-neg complete */ + +/* Transmit Control */ +#define E1000_TCTL_RST 0x00000001 /* software reset */ +#define E1000_TCTL_EN 0x00000002 /* enable tx */ +#define E1000_TCTL_BCE 0x00000004 /* busy check enable */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ +#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ + +/* Receive Checksum Control */ +#define E1000_RXCSUM_PCSS_MASK 0x000000FF /* Packet Checksum Start */ +#define E1000_RXCSUM_IPOFL 0x00000100 /* IPv4 checksum offload */ +#define E1000_RXCSUM_TUOFL 0x00000200 /* TCP / UDP checksum offload */ +#define E1000_RXCSUM_IPV6OFL 0x00000400 /* IPv6 checksum offload */ + +/* Definitions for power management and wakeup registers */ +/* Wake Up Control */ +#define E1000_WUC_APME 0x00000001 /* APM Enable */ +#define E1000_WUC_PME_EN 0x00000002 /* PME Enable */ +#define E1000_WUC_PME_STATUS 0x00000004 /* PME Status */ +#define E1000_WUC_APMPME 0x00000008 /* Assert PME on APM Wakeup */ + +/* Wake Up Filter Control */ +#define E1000_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ +#define E1000_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ +#define E1000_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ +#define E1000_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */ +#define E1000_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ +#define E1000_WUFC_ARP 0x00000020 /* ARP Request Packet Wakeup Enable */ +#define E1000_WUFC_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Enable */ +#define E1000_WUFC_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Enable */ +#define E1000_WUFC_FLX0 0x00010000 /* Flexible Filter 0 Enable */ +#define E1000_WUFC_FLX1 0x00020000 /* Flexible Filter 1 Enable */ +#define E1000_WUFC_FLX2 0x00040000 /* Flexible Filter 2 Enable */ +#define E1000_WUFC_FLX3 0x00080000 /* Flexible Filter 3 Enable */ +#define E1000_WUFC_ALL_FILTERS 0x000F00FF /* Mask for all wakeup filters */ +#define E1000_WUFC_FLX_OFFSET 16 /* Offset to the Flexible Filters bits */ +#define E1000_WUFC_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */ + +/* Wake Up Status */ +#define E1000_WUS_LNKC 0x00000001 /* Link Status Changed */ +#define E1000_WUS_MAG 0x00000002 /* Magic Packet Received */ +#define E1000_WUS_EX 0x00000004 /* Directed Exact Received */ +#define E1000_WUS_MC 0x00000008 /* Directed Multicast Received */ +#define E1000_WUS_BC 0x00000010 /* Broadcast Received */ +#define E1000_WUS_ARP 0x00000020 /* ARP Request Packet Received */ +#define E1000_WUS_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Received */ +#define E1000_WUS_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Received */ +#define E1000_WUS_FLX0 0x00010000 /* Flexible Filter 0 Match */ +#define E1000_WUS_FLX1 0x00020000 /* Flexible Filter 1 Match */ +#define E1000_WUS_FLX2 0x00040000 /* Flexible Filter 2 Match */ +#define E1000_WUS_FLX3 0x00080000 /* Flexible Filter 3 Match */ +#define E1000_WUS_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */ + +/* Management Control */ +#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */ +#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */ +#define E1000_MANC_R_ON_FORCE 0x00000004 /* Reset on Force TCO - RO */ +#define E1000_MANC_RMCP_EN 0x00000100 /* Enable RCMP 026Fh Filtering */ +#define E1000_MANC_0298_EN 0x00000200 /* Enable RCMP 0298h Filtering */ +#define E1000_MANC_IPV4_EN 0x00000400 /* Enable IPv4 */ +#define E1000_MANC_IPV6_EN 0x00000800 /* Enable IPv6 */ +#define E1000_MANC_SNAP_EN 0x00001000 /* Accept LLC/SNAP */ +#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */ +#define E1000_MANC_NEIGHBOR_EN 0x00004000 /* Enable Neighbor Discovery + * Filtering */ +#define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */ +#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */ +#define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */ +#define E1000_MANC_SMB_REQ 0x01000000 /* SMBus Request */ +#define E1000_MANC_SMB_GNT 0x02000000 /* SMBus Grant */ +#define E1000_MANC_SMB_CLK_IN 0x04000000 /* SMBus Clock In */ +#define E1000_MANC_SMB_DATA_IN 0x08000000 /* SMBus Data In */ +#define E1000_MANC_SMB_DATA_OUT 0x10000000 /* SMBus Data Out */ +#define E1000_MANC_SMB_CLK_OUT 0x20000000 /* SMBus Clock Out */ + +#define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */ +#define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */ + +/* Wake Up Packet Length */ +#define E1000_WUPL_LENGTH_MASK 0x0FFF /* Only the lower 12 bits are valid */ + +#define E1000_MDALIGN 4096 + +/* EEPROM Commands */ +#define EEPROM_READ_OPCODE 0x6 /* EERPOM read opcode */ +#define EEPROM_WRITE_OPCODE 0x5 /* EERPOM write opcode */ +#define EEPROM_ERASE_OPCODE 0x7 /* EERPOM erase opcode */ +#define EEPROM_EWEN_OPCODE 0x13 /* EERPOM erase/write enable */ +#define EEPROM_EWDS_OPCODE 0x10 /* EERPOM erast/write disable */ + +/* EEPROM Word Offsets */ +#define EEPROM_INIT_CONTROL1_REG 0x000A +#define EEPROM_INIT_CONTROL2_REG 0x000F +#define EEPROM_FLASH_VERSION 0x0032 +#define EEPROM_CHECKSUM_REG 0x003F + +/* Mask bits for fields in Word 0x0a of the EEPROM */ +#define EEPROM_WORD0A_ILOS 0x0010 +#define EEPROM_WORD0A_SWDPIO 0x01E0 +#define EEPROM_WORD0A_LRST 0x0200 +#define EEPROM_WORD0A_FD 0x0400 +#define EEPROM_WORD0A_66MHZ 0x0800 + +/* Mask bits for fields in Word 0x0f of the EEPROM */ +#define EEPROM_WORD0F_PAUSE_MASK 0x3000 +#define EEPROM_WORD0F_PAUSE 0x1000 +#define EEPROM_WORD0F_ASM_DIR 0x2000 +#define EEPROM_WORD0F_ANE 0x0800 +#define EEPROM_WORD0F_SWPDIO_EXT 0x00F0 + +/* For checksumming, the sum of all words in the EEPROM should equal 0xBABA. */ +#define EEPROM_SUM 0xBABA + +/* EEPROM Map defines (WORD OFFSETS)*/ +#define EEPROM_NODE_ADDRESS_BYTE_0 0 +#define EEPROM_PBA_BYTE_1 8 + +/* EEPROM Map Sizes (Byte Counts) */ +#define PBA_SIZE 4 + +/* Collision related configuration parameters */ +#define E1000_COLLISION_THRESHOLD 16 +#define E1000_CT_SHIFT 4 +#define E1000_FDX_COLLISION_DISTANCE 64 +#define E1000_HDX_COLLISION_DISTANCE 64 +#define E1000_GB_HDX_COLLISION_DISTANCE 512 +#define E1000_COLD_SHIFT 12 + +/* The number of Transmit and Receive Descriptors must be a multiple of 8 */ +#define REQ_TX_DESCRIPTOR_MULTIPLE 8 +#define REQ_RX_DESCRIPTOR_MULTIPLE 8 + +/* Default values for the transmit IPG register */ +#define DEFAULT_82542_TIPG_IPGT 10 +#define DEFAULT_82543_TIPG_IPGT_FIBER 9 +#define DEFAULT_82543_TIPG_IPGT_COPPER 8 + +#define E1000_TIPG_IPGT_MASK 0x000003FF +#define E1000_TIPG_IPGR1_MASK 0x000FFC00 +#define E1000_TIPG_IPGR2_MASK 0x3FF00000 + +#define DEFAULT_82542_TIPG_IPGR1 2 +#define DEFAULT_82543_TIPG_IPGR1 8 +#define E1000_TIPG_IPGR1_SHIFT 10 + +#define DEFAULT_82542_TIPG_IPGR2 10 +#define DEFAULT_82543_TIPG_IPGR2 6 +#define E1000_TIPG_IPGR2_SHIFT 20 + +#define E1000_TXDMAC_DPP 0x00000001 + +/* PBA constants */ +#define E1000_PBA_16K 0x0010 /* 16KB, default TX allocation */ +#define E1000_PBA_24K 0x0018 +#define E1000_PBA_40K 0x0028 +#define E1000_PBA_48K 0x0030 /* 48KB, default RX allocation */ + +/* Flow Control Constants */ +#define FLOW_CONTROL_ADDRESS_LOW 0x00C28001 +#define FLOW_CONTROL_ADDRESS_HIGH 0x00000100 +#define FLOW_CONTROL_TYPE 0x8808 + +/* The historical defaults for the flow control values are given below. */ +#define FC_DEFAULT_HI_THRESH (0x8000) /* 32KB */ +#define FC_DEFAULT_LO_THRESH (0x4000) /* 16KB */ +#define FC_DEFAULT_TX_TIMER (0x100) /* ~130 us */ + + +/* The number of bits that we need to shift right to move the "pause" + * bits from the EEPROM (bits 13:12) to the "pause" (bits 8:7) field + * in the TXCW register + */ +#define PAUSE_SHIFT 5 + +/* The number of bits that we need to shift left to move the "SWDPIO" + * bits from the EEPROM (bits 8:5) to the "SWDPIO" (bits 25:22) field + * in the CTRL register + */ +#define SWDPIO_SHIFT 17 + +/* The number of bits that we need to shift left to move the "SWDPIO_EXT" + * bits from the EEPROM word F (bits 7:4) to the bits 11:8 of The + * Extended CTRL register. + * in the CTRL register + */ +#define SWDPIO__EXT_SHIFT 4 + +/* The number of bits that we need to shift left to move the "ILOS" + * bit from the EEPROM (bit 4) to the "ILOS" (bit 7) field + * in the CTRL register + */ +#define ILOS_SHIFT 3 + + +#define RECEIVE_BUFFER_ALIGN_SIZE (256) + +/* The number of milliseconds we wait for auto-negotiation to complete */ +#define LINK_UP_TIMEOUT 500 + +#define E1000_TX_BUFFER_SIZE ((uint32_t)1514) + +/* The carrier extension symbol, as received by the NIC. */ +#define CARRIER_EXTENSION 0x0F + +/* TBI_ACCEPT macro definition: + * + * If Tbi Compatibility mode is turned-on, then we should accept frames with + * receive errors if and only if: + * 1) errors is equal to the CRC error bit. + * 2) The last byte is a Carrier extension (0x0F). + * 3) The frame length (as reported by Hardware) is greater than 64 (60 + * if a VLAN tag was stripped from the frame. + * 4) " " " " " " " <= max_frame_size+1. + * + * This macro requires: + * adapter = a pointer to struct e1000_shared_adapter + * special = the 16 bit special field of the RX descriptor with EOP set + * error = the 8 bit error field of the RX descriptor with EOP set + * length = the sum of all the length fields of the RX descriptors that + * make up the current frame + * last_byte = the last byte of the frame DMAed by the hardware + * max_frame_length = the maximum frame length we want to accept. + * min_frame_length = the minimum frame length we want to accept. + * + * This macro is a conditional that should be used in the interrupt + * handler's Rx processing routine when RxErrors have been detected. + * + * Typical use: + * ... + * if (TBI_ACCEPT) { + * accept_frame = TRUE; + * e1000_tbi_adjust_stats(adapter, MacAddress); + * frame_length--; + * } else { + * accept_frame = FALSE; + * } + * ... + */ + +#define TBI_ACCEPT(adapter, special, errors, length, last_byte) \ + ((adapter)->tbi_compatibility_on && \ + (((errors) & E1000_RXD_ERR_FRAME_ERR_MASK) == E1000_RXD_ERR_CE) && \ + ((last_byte) == CARRIER_EXTENSION) && \ + ((length) <= ((adapter)->max_frame_size + 1)) && \ + ((length) > ((special == 0x0000) ? \ + ((adapter)->min_frame_size) : \ + ((adapter)->min_frame_size - VLAN_TAG_SIZE)))) + + +#endif /* _E1000_MAC_H_ */ diff -Nru a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e1000/e1000_main.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,2016 @@ +/******************************************************************************* + + This software program is available to you under a choice of one of two + licenses. You may choose to be licensed under either the GNU General Public + License (GPL) Version 2, June 1991, available at + http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the + text of which follows: + + Recipient has requested a license and Intel Corporation ("Intel") is willing + to grant a license for the software entitled Linux Base Driver for the + Intel(R) PRO/1000 Family of Adapters (e1000) (the "Software") being provided + by Intel Corporation. The following definitions apply to this license: + + "Licensed Patents" means patent claims licensable by Intel Corporation which + are necessarily infringed by the use of sale of the Software alone or when + combined with the operating system referred to below. + + "Recipient" means the party to whom Intel delivers this Software. + + "Licensee" means Recipient and those third parties that receive a license to + any operating system available under the GNU Public License version 2.0 or + later. + + Copyright (c) 1999 - 2002 Intel Corporation. + All rights reserved. + + The license is provided to Recipient and Recipient's Licensees under the + following terms. + + Redistribution and use in source and binary forms of the Software, with or + without modification, are permitted provided that the following conditions + are met: + + Redistributions of source code of the Software may retain the above + copyright notice, this list of conditions and the following disclaimer. + + Redistributions in binary form of the Software may reproduce the above + copyright notice, this list of conditions and the following disclaimer in + the documentation and/or materials provided with the distribution. + + Neither the name of Intel Corporation nor the names of its contributors + shall be used to endorse or promote products derived from this Software + without specific prior written permission. + + Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, + royalty-free patent license under Licensed Patents to make, use, sell, offer + to sell, import and otherwise transfer the Software, if any, in source code + and object code form. This license shall include changes to the Software + that are error corrections or other minor changes to the Software that do + not add functionality or features when the Software is incorporated in any + version of an operating system that has been distributed under the GNU + General Public License 2.0 or later. This patent license shall apply to the + combination of the Software and any operating system licensed under the GNU + Public License version 2.0 or later if, at the time Intel provides the + Software to Recipient, such addition of the Software to the then publicly + available versions of such operating systems available under the GNU Public + License version 2.0 or later (whether in gold, beta or alpha form) causes + such combination to be covered by the Licensed Patents. The patent license + shall not apply to any other combinations which include the Software. NO + hardware per se is licensed hereunder. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. + +*******************************************************************************/ + +#define __E1000_MAIN__ +#include "e1000.h" + +char e1000_driver_name[] = "e1000"; + +char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver"; + +char e1000_driver_version[] = "4.2.4-k2"; + +char e1000_copyright[] = "Copyright (c) 1999-2002 Intel Corporation."; + +/* e1000_pci_tbl - PCI Device ID Table + * + * Private driver_data field (last one) stores an index into e1000_strings + * Wildcard entries (PCI_ANY_ID) should come last + * Last entry must be all 0s + * + * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, + * Class, Class Mask, String Index } + */ +static struct pci_device_id e1000_pci_tbl[] __devinitdata = { + /* Intel(R) PRO/1000 Network Connection */ + {0x8086, 0x1000, 0x8086, 0x1000, 0, 0, 0}, + {0x8086, 0x1001, 0x8086, 0x1003, 0, 0, 0}, + {0x8086, 0x1004, 0x8086, 0x1004, 0, 0, 0}, + {0x8086, 0x1008, 0x8086, 0x1107, 0, 0, 0}, + {0x8086, 0x1009, 0x8086, 0x1109, 0, 0, 0}, + {0x8086, 0x100C, 0x8086, 0x1112, 0, 0, 0}, + /* Compaq Gigabit Ethernet Server Adapter */ + {0x8086, 0x1000, 0x0E11, PCI_ANY_ID, 0, 0, 1}, + {0x8086, 0x1001, 0x0E11, PCI_ANY_ID, 0, 0, 1}, + {0x8086, 0x1004, 0x0E11, PCI_ANY_ID, 0, 0, 1}, + /* IBM Mobile, Desktop & Server Adapters */ + {0x8086, 0x1000, 0x1014, PCI_ANY_ID, 0, 0, 2}, + {0x8086, 0x1001, 0x1014, PCI_ANY_ID, 0, 0, 2}, + {0x8086, 0x1004, 0x1014, PCI_ANY_ID, 0, 0, 2}, + /* Generic */ + {0x8086, 0x1000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x8086, 0x1001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x8086, 0x1004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x8086, 0x1008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x8086, 0x1009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x8086, 0x100C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x8086, 0x100D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + /* required last entry */ + {0,} +}; + +MODULE_DEVICE_TABLE(pci, e1000_pci_tbl); + +static char *e1000_strings[] = { + "Intel(R) PRO/1000 Network Connection", + "Compaq Gigabit Ethernet Server Adapter", + "IBM Mobile, Desktop & Server Adapters" +}; + +/* e1000_main.c Function Prototypes */ + +int e1000_up(struct e1000_adapter *adapter); +void e1000_down(struct e1000_adapter *adapter); +static int e1000_init_module(void); +static void e1000_exit_module(void); +static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent); +static void e1000_remove(struct pci_dev *pdev); +static void e1000_sw_init(struct e1000_adapter *adapter); +static int e1000_open(struct net_device *netdev); +static int e1000_close(struct net_device *netdev); +static int e1000_setup_tx_resources(struct e1000_adapter *adapter); +static int e1000_setup_rx_resources(struct e1000_adapter *adapter); +static void e1000_configure_tx(struct e1000_adapter *adapter); +static void e1000_configure_rx(struct e1000_adapter *adapter); +static void e1000_setup_rctl(struct e1000_adapter *adapter); +static void e1000_clean_tx_ring(struct e1000_adapter *adapter); +static void e1000_clean_rx_ring(struct e1000_adapter *adapter); +static void e1000_free_tx_resources(struct e1000_adapter *adapter); +static void e1000_free_rx_resources(struct e1000_adapter *adapter); +static void e1000_set_multi(struct net_device *netdev); +static void e1000_update_phy_info(unsigned long data); +static void e1000_watchdog(unsigned long data); +static int e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev); +static void e1000_tx_timeout(struct net_device *dev); +static struct net_device_stats * e1000_get_stats(struct net_device *netdev); +static int e1000_change_mtu(struct net_device *netdev, int new_mtu); +static int e1000_set_mac(struct net_device *netdev, void *p); +static void e1000_update_stats(struct e1000_adapter *adapter); +static inline void e1000_irq_disable(struct e1000_adapter *adapter); +static inline void e1000_irq_enable(struct e1000_adapter *adapter); +static void e1000_intr(int irq, void *data, struct pt_regs *regs); +static void e1000_clean_tx_irq(struct e1000_adapter *adapter); +static void e1000_clean_rx_irq(struct e1000_adapter *adapter); +static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter); +static int e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd); +static void e1000_reset(struct e1000_adapter *adapter); +static void e1000_enter_82542_rst(struct e1000_adapter *adapter); +static void e1000_leave_82542_rst(struct e1000_adapter *adapter); +static inline void e1000_rx_checksum(struct e1000_adapter *adapter, + struct e1000_rx_desc *rx_desc, + struct sk_buff *skb); +void e1000_enable_WOL(struct e1000_adapter *adapter); + +/* Exported from other modules */ + +extern void e1000_check_options(struct e1000_adapter *adapter); +extern void e1000_proc_dev_setup(struct e1000_adapter *adapter); +extern void e1000_proc_dev_free(struct e1000_adapter *adapter); +extern int e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr); + +static struct pci_driver e1000_driver = { + name: e1000_driver_name, + id_table: e1000_pci_tbl, + probe: e1000_probe, + remove: e1000_remove, + /* Power Managment Hooks */ + suspend: NULL, + resume: NULL +}; + +MODULE_AUTHOR("Intel Corporation, "); +MODULE_DESCRIPTION("Intel(R) PRO/1000 Network Driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +/** + * e1000_init_module - Driver Registration Routine + * + * e1000_init_module is the first routine called when the driver is + * loaded. All it does is register with the PCI subsystem. + **/ + +static int __init +e1000_init_module(void) +{ + printk(KERN_INFO "%s - version %s\n", + e1000_driver_string, e1000_driver_version); + + printk(KERN_INFO "%s\n", e1000_copyright); + + return pci_module_init(&e1000_driver); +} + +module_init(e1000_init_module); + +/** + * e1000_exit_module - Driver Exit Cleanup Routine + * + * e1000_exit_module is called just before the driver is removed + * from memory. + **/ + +static void __exit +e1000_exit_module(void) +{ + pci_unregister_driver(&e1000_driver); + + return; +} + +module_exit(e1000_exit_module); + + +int +e1000_up(struct e1000_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + if(request_irq(netdev->irq, &e1000_intr, SA_SHIRQ, + netdev->name, netdev)) + return -1; + + /* hardware has been reset, we need to reload some things */ + + e1000_set_multi(netdev); + + e1000_configure_tx(adapter); + e1000_setup_rctl(adapter); + e1000_configure_rx(adapter); + e1000_alloc_rx_buffers(adapter); + + e1000_clear_hw_cntrs(&adapter->shared); + + mod_timer(&adapter->watchdog_timer, jiffies); + e1000_irq_enable(adapter); + + return 0; +} + +void +e1000_down(struct e1000_adapter *adapter) +{ + struct e1000_shared_adapter *shared = &adapter->shared; + struct net_device *netdev = adapter->netdev; + + e1000_irq_disable(adapter); + free_irq(netdev->irq, netdev); + del_timer_sync(&adapter->watchdog_timer); + del_timer_sync(&adapter->phy_info_timer); + netif_carrier_off(netdev); + netif_stop_queue(netdev); + + /* disable the transmit and receive units */ + + E1000_WRITE_REG(shared, RCTL, 0); + E1000_WRITE_REG(shared, TCTL, E1000_TCTL_PSP); + + /* delay to allow PCI transactions to complete */ + + msec_delay(10); + + e1000_clean_tx_ring(adapter); + e1000_clean_rx_ring(adapter); + + e1000_reset(adapter); +} + +static void +e1000_reset(struct e1000_adapter *adapter) +{ + struct e1000_shared_adapter *shared = &adapter->shared; + uint32_t ctrl_ext; + + /* Repartition Pba for greater than 9k mtu + * To take effect CTRL.RST is required. + */ + + if(adapter->rx_buffer_len > E1000_RXBUFFER_8192) + E1000_WRITE_REG(shared, PBA, E1000_JUMBO_PBA); + else + E1000_WRITE_REG(shared, PBA, E1000_DEFAULT_PBA); + + /* 82542 2.0 needs MWI disabled while issuing a reset */ + + if(shared->mac_type == e1000_82542_rev2_0) + e1000_enter_82542_rst(adapter); + + /* global reset */ + + E1000_WRITE_REG(shared, CTRL, E1000_CTRL_RST); + msec_delay(10); + + /* EEPROM reload */ + + ctrl_ext = E1000_READ_REG(shared, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_EE_RST; + E1000_WRITE_REG(shared, CTRL_EXT, ctrl_ext); + msec_delay(5); + + if(shared->mac_type == e1000_82542_rev2_0) + e1000_leave_82542_rst(adapter); + + shared->tbi_compatibility_on = FALSE; + shared->fc = shared->original_fc; + + e1000_init_hw(shared); + + e1000_enable_WOL(adapter); + + return; +} + +/** + * e1000_probe - Device Initialization Routine + * @pdev: PCI device information struct + * @ent: entry in e1000_pci_tbl + * + * Returns 0 on success, negative on failure + * + * e1000_probe initializes an adapter identified by a pci_dev structure. + * The OS initialization, configuring of the adapter private structure, + * and a hardware reset occur. + **/ + +static int __devinit +e1000_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *netdev; + struct e1000_adapter *adapter; + static int cards_found = 0; + unsigned long mmio_start; + int mmio_len; + int pci_using_dac; + int i; + + if((i = pci_enable_device(pdev))) + return i; + + if(!(i = pci_set_dma_mask(pdev, (u64) 0xffffffffffffffff))) { + pci_using_dac = 1; + } else { + if((i = pci_set_dma_mask(pdev, (u64) 0xffffffff))) { + E1000_ERR("No usable DMA configuration, aborting\n"); + return i; + } + pci_using_dac = 0; + } + + if((i = pci_request_regions(pdev, e1000_driver_name))) + return i; + + pci_set_master(pdev); + + netdev = alloc_etherdev(sizeof(struct e1000_adapter)); + if(!netdev) + goto err_alloc_etherdev; + + SET_MODULE_OWNER(netdev); + + pci_set_drvdata(pdev, netdev); + adapter = netdev->priv; + adapter->netdev = netdev; + adapter->pdev = pdev; + adapter->shared.back = adapter; + + mmio_start = pci_resource_start(pdev, BAR_0); + mmio_len = pci_resource_len(pdev, BAR_0); + + adapter->shared.hw_addr = ioremap(mmio_start, mmio_len); + if(!adapter->shared.hw_addr) + goto err_ioremap; + + netdev->open = &e1000_open; + netdev->stop = &e1000_close; + netdev->hard_start_xmit = &e1000_xmit_frame; + netdev->get_stats = &e1000_get_stats; + netdev->set_multicast_list = &e1000_set_multi; + netdev->set_mac_address = &e1000_set_mac; + netdev->change_mtu = &e1000_change_mtu; + netdev->do_ioctl = &e1000_ioctl; + netdev->tx_timeout = &e1000_tx_timeout; + netdev->watchdog_timeo = HZ; + + netdev->irq = pdev->irq; + netdev->base_addr = mmio_start; + + adapter->bd_number = cards_found; + adapter->id_string = e1000_strings[ent->driver_data]; + + /* setup the private structure */ + + e1000_sw_init(adapter); + + if(adapter->shared.mac_type >= e1000_82543) { + netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM; + } else { + netdev->features = NETIF_F_SG; + } + + if(pci_using_dac) + netdev->features |= NETIF_F_HIGHDMA; + + /* make sure the EEPROM is good */ + + if(!e1000_validate_eeprom_checksum(&adapter->shared)) + goto err_eeprom; + + /* copy the MAC address out of the EEPROM */ + + e1000_read_mac_addr(&adapter->shared); + memcpy(netdev->dev_addr, adapter->shared.mac_addr, netdev->addr_len); + + if(!is_valid_ether_addr(netdev->dev_addr)) + goto err_eeprom; + + e1000_read_part_num(&adapter->shared, &(adapter->part_num)); + e1000_get_bus_info(&adapter->shared); + + init_timer(&adapter->watchdog_timer); + adapter->watchdog_timer.function = &e1000_watchdog; + adapter->watchdog_timer.data = (unsigned long) adapter; + + init_timer(&adapter->phy_info_timer); + adapter->phy_info_timer.function = &e1000_update_phy_info; + adapter->phy_info_timer.data = (unsigned long) adapter; + + register_netdev(netdev); + + /* we're going to reset, so assume we have no link for now */ + + netif_carrier_off(netdev); + netif_stop_queue(netdev); + + printk(KERN_INFO "%s: %s\n", netdev->name, adapter->id_string); + e1000_check_options(adapter); + e1000_proc_dev_setup(adapter); + + /* reset the hardware with the new settings */ + + e1000_reset(adapter); + + cards_found++; + return 0; + +err_eeprom: + iounmap(adapter->shared.hw_addr); +err_ioremap: + pci_release_regions(pdev); + kfree(netdev); +err_alloc_etherdev: + return -ENOMEM; +} + +/** + * e1000_remove - Device Removal Routine + * @pdev: PCI device information struct + * + * e1000_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. The could be caused by a + * Hot-Plug event, or because the driver is going to be removed from + * memory. + * + * This routine is also called to clean up from a failure in + * e1000_probe. The Adapter struct and netdev will always exist, + * all other pointers must be checked for NULL before freeing. + **/ + +static void __devexit +e1000_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct e1000_adapter *adapter = netdev->priv; + + unregister_netdev(netdev); + + e1000_phy_hw_reset(&adapter->shared); + + e1000_proc_dev_free(adapter); + + iounmap(adapter->shared.hw_addr); + pci_release_regions(pdev); + + kfree(netdev); + return; +} + +/** + * e1000_sw_init - Initialize general software structures (struct e1000_adapter) + * @adapter: board private structure to initialize + * + * e1000_sw_init initializes the Adapter private data structure. + * Fields are initialized based on PCI device information and + * OS network device settings (MTU size). + **/ + +static void __devinit +e1000_sw_init(struct e1000_adapter *adapter) +{ + struct e1000_shared_adapter *shared = &adapter->shared; + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + + /* PCI config space info */ + + uint16_t *vendor = &shared->vendor_id; + uint16_t *device = &shared->device_id; + uint16_t *subvendor = &shared->subsystem_vendor_id; + uint16_t *subsystem = &shared->subsystem_id; + uint8_t *revision = &shared->revision_id; + + pci_read_config_word(pdev, PCI_VENDOR_ID, vendor); + pci_read_config_word(pdev, PCI_DEVICE_ID, device); + pci_read_config_byte(pdev, PCI_REVISION_ID, revision); + pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, subvendor); + pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, subsystem); + + pci_read_config_word(pdev, PCI_COMMAND, &shared->pci_cmd_word); + + adapter->rx_buffer_len = E1000_RXBUFFER_2048; + shared->max_frame_size = netdev->mtu + ENET_HEADER_SIZE + CRC_LENGTH; + shared->min_frame_size = MINIMUM_ETHERNET_PACKET_SIZE + CRC_LENGTH; + + /* identify the MAC */ + + switch (*device) { + case E1000_DEV_ID_82542: + switch (*revision) { + case E1000_82542_2_0_REV_ID: + shared->mac_type = e1000_82542_rev2_0; + break; + case E1000_82542_2_1_REV_ID: + shared->mac_type = e1000_82542_rev2_1; + break; + default: + shared->mac_type = e1000_82542_rev2_0; + E1000_ERR("Could not identify 82542 revision\n"); + } + break; + case E1000_DEV_ID_82543GC_FIBER: + case E1000_DEV_ID_82543GC_COPPER: + shared->mac_type = e1000_82543; + break; + case E1000_DEV_ID_82544EI_COPPER: + case E1000_DEV_ID_82544EI_FIBER: + case E1000_DEV_ID_82544GC_COPPER: + case E1000_DEV_ID_82544GC_LOM: + shared->mac_type = e1000_82544; + break; + default: + /* should never have loaded on this device */ + BUG(); + } + + /* flow control settings */ + + shared->fc_high_water = FC_DEFAULT_HI_THRESH; + shared->fc_low_water = FC_DEFAULT_LO_THRESH; + shared->fc_pause_time = FC_DEFAULT_TX_TIMER; + shared->fc_send_xon = 1; + + /* Media type - copper or fiber */ + + if(shared->mac_type >= e1000_82543) { + uint32_t status = E1000_READ_REG(shared, STATUS); + + if(status & E1000_STATUS_TBIMODE) + shared->media_type = e1000_media_type_fiber; + else + shared->media_type = e1000_media_type_copper; + } else { + shared->media_type = e1000_media_type_fiber; + } + + if(shared->mac_type < e1000_82543) + shared->report_tx_early = 0; + else + shared->report_tx_early = 1; + + shared->wait_autoneg_complete = FALSE; + shared->tbi_compatibility_en = TRUE; + + atomic_set(&adapter->irq_sem, 1); + spin_lock_init(&adapter->stats_lock); +} + +/** + * e1000_open - Called when a network interface is made active + * @netdev: network interface device structure + * + * Returns 0 on success, negative value on failure + * + * The open entry point is called when a network interface is made + * active by the system (IFF_UP). At this point all resources needed + * for transmit and receive operations are allocated, the interrupt + * handler is registered with the OS, the watchdog timer is started, + * and the stack is notified that the interface is ready. + **/ + +static int +e1000_open(struct net_device *netdev) +{ + struct e1000_adapter *adapter = netdev->priv; + + /* allocate transmit descriptors */ + + if(e1000_setup_tx_resources(adapter)) + goto err_setup_tx; + + /* allocate receive descriptors */ + + if(e1000_setup_rx_resources(adapter)) + goto err_setup_rx; + + if(e1000_up(adapter)) + goto err_up; + + return 0; + +err_up: + e1000_free_rx_resources(adapter); +err_setup_rx: + e1000_free_tx_resources(adapter); +err_setup_tx: + e1000_reset(adapter); + + return -EBUSY; +} + +/** + * e1000_close - Disables a network interface + * @netdev: network interface device structure + * + * Returns 0, this is not allowed to fail + * + * The close entry point is called when an interface is de-activated + * by the OS. The hardware is still under the drivers control, but + * needs to be disabled. A global MAC reset is issued to stop the + * hardware, and all transmit and receive resources are freed. + **/ + +static int +e1000_close(struct net_device *netdev) +{ + struct e1000_adapter *adapter = netdev->priv; + + e1000_down(adapter); + + e1000_free_tx_resources(adapter); + e1000_free_rx_resources(adapter); + + return 0; +} + +/** + * e1000_setup_tx_resources - allocate Tx resources (Descriptors) + * @adapter: board private structure + * + * Return 0 on success, negative on failure + * + * e1000_setup_tx_resources allocates all software transmit resources + * and enabled the Tx unit of the MAC. + **/ + +static int +e1000_setup_tx_resources(struct e1000_adapter *adapter) +{ + struct e1000_desc_ring *txdr = &adapter->tx_ring; + struct pci_dev *pdev = adapter->pdev; + int size; + + size = sizeof(struct e1000_buffer) * txdr->count; + txdr->buffer_info = kmalloc(size, GFP_KERNEL); + if(!txdr->buffer_info) { + return -ENOMEM; + } + memset(txdr->buffer_info, 0, size); + + /* round up to nearest 4K */ + + txdr->size = txdr->count * sizeof(struct e1000_tx_desc); + E1000_ROUNDUP(txdr->size, 4096); + + txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma); + if(!txdr->desc) { + kfree(txdr->buffer_info); + return -ENOMEM; + } + memset(txdr->desc, 0, txdr->size); + + atomic_set(&txdr->unused, txdr->count); + txdr->next_to_use = 0; + txdr->next_to_clean = 0; + + return 0; +} + +/** + * e1000_configure_tx - Configure 8254x Transmit Unit after Reset + * @adapter: board private structure + * + * Configure the Tx unit of the MAC after a reset. + **/ + +static void +e1000_configure_tx(struct e1000_adapter *adapter) +{ + uint64_t tdba = adapter->tx_ring.dma; + uint32_t tdlen = adapter->tx_ring.count * sizeof(struct e1000_tx_desc); + uint32_t tctl, tipg; + + E1000_WRITE_REG(&adapter->shared, TDBAL, (tdba & 0x00000000FFFFFFFF)); + E1000_WRITE_REG(&adapter->shared, TDBAH, (tdba >> 32)); + + E1000_WRITE_REG(&adapter->shared, TDLEN, tdlen); + + /* Setup the HW Tx Head and Tail descriptor pointers */ + + E1000_WRITE_REG(&adapter->shared, TDH, 0); + E1000_WRITE_REG(&adapter->shared, TDT, 0); + + /* Set the default values for the Tx Inter Packet Gap timer */ + + switch (adapter->shared.mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + tipg = DEFAULT_82542_TIPG_IPGT; + tipg |= DEFAULT_82542_TIPG_IPGR1 << E1000_TIPG_IPGR1_SHIFT; + tipg |= DEFAULT_82542_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT; + break; + default: + if(adapter->shared.media_type == e1000_media_type_fiber) + tipg = DEFAULT_82543_TIPG_IPGT_FIBER; + else + tipg = DEFAULT_82543_TIPG_IPGT_COPPER; + tipg |= DEFAULT_82543_TIPG_IPGR1 << E1000_TIPG_IPGR1_SHIFT; + tipg |= DEFAULT_82543_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT; + } + E1000_WRITE_REG(&adapter->shared, TIPG, tipg); + + /* Set the Tx Interrupt Delay register */ + + E1000_WRITE_REG(&adapter->shared, TIDV, adapter->tx_int_delay); + + /* Program the Transmit Control Register */ + + tctl = E1000_TCTL_PSP | E1000_TCTL_EN | + (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT); + + if(adapter->link_duplex == FULL_DUPLEX) { + tctl |= E1000_FDX_COLLISION_DISTANCE << E1000_COLD_SHIFT; + } else { + tctl |= E1000_HDX_COLLISION_DISTANCE << E1000_COLD_SHIFT; + } + + E1000_WRITE_REG(&adapter->shared, TCTL, tctl); + + /* Setup Transmit Descriptor Settings for this adapter */ + adapter->txd_cmd = E1000_TXD_CMD_IFCS; + + if(adapter->tx_int_delay > 0) + adapter->txd_cmd |= E1000_TXD_CMD_IDE; + if(adapter->shared.report_tx_early == 1) + adapter->txd_cmd |= E1000_TXD_CMD_RS; + else + adapter->txd_cmd |= E1000_TXD_CMD_RPS; + + return; +} + +/** + * e1000_setup_rx_resources - allocate Rx resources (Descriptors, receive SKBs) + * @adapter: board private structure + * + * Returns 0 on success, negative on failure + * + * e1000_setup_rx_resources allocates all software receive resources + * and network buffers, and enables the Rx unit of the MAC. + **/ + +static int +e1000_setup_rx_resources(struct e1000_adapter *adapter) +{ + struct e1000_desc_ring *rxdr = &adapter->rx_ring; + struct pci_dev *pdev = adapter->pdev; + int size; + + size = sizeof(struct e1000_buffer) * rxdr->count; + rxdr->buffer_info = kmalloc(size, GFP_KERNEL); + if(!rxdr->buffer_info) { + return -ENOMEM; + } + memset(rxdr->buffer_info, 0, size); + + /* Round up to nearest 4K */ + + rxdr->size = rxdr->count * sizeof(struct e1000_rx_desc); + E1000_ROUNDUP(rxdr->size, 4096); + + rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma); + + if(!rxdr->desc) { + kfree(rxdr->buffer_info); + return -ENOMEM; + } + memset(rxdr->desc, 0, rxdr->size); + + rxdr->next_to_clean = 0; + rxdr->unused_count = rxdr->count; + rxdr->next_to_use = 0; + + return 0; +} + +/** + * e1000_setup_rctl - configure the receive control register + * @adapter: Board private structure + **/ + +static void +e1000_setup_rctl(struct e1000_adapter *adapter) +{ + uint32_t rctl; + + /* Setup the Receive Control Register */ + rctl = E1000_RCTL_EN | E1000_RCTL_BAM | + E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF | + (adapter->shared.mc_filter_type << E1000_RCTL_MO_SHIFT); + + if(adapter->shared.tbi_compatibility_on == 1) + rctl |= E1000_RCTL_SBP; + + switch (adapter->rx_buffer_len) { + case E1000_RXBUFFER_2048: + default: + rctl |= E1000_RCTL_SZ_2048; + break; + case E1000_RXBUFFER_4096: + rctl |= E1000_RCTL_SZ_4096 | E1000_RCTL_BSEX | E1000_RCTL_LPE; + break; + case E1000_RXBUFFER_8192: + rctl |= E1000_RCTL_SZ_8192 | E1000_RCTL_BSEX | E1000_RCTL_LPE; + break; + case E1000_RXBUFFER_16384: + rctl |= E1000_RCTL_SZ_16384 | E1000_RCTL_BSEX | E1000_RCTL_LPE; + break; + } + + E1000_WRITE_REG(&adapter->shared, RCTL, rctl); +} + +/** + * e1000_configure_rx - Configure 8254x Receive Unit after Reset + * @adapter: board private structure + * + * Configure the Rx unit of the MAC after a reset. + **/ + +static void +e1000_configure_rx(struct e1000_adapter *adapter) +{ + uint64_t rdba = adapter->rx_ring.dma; + uint32_t rdlen = adapter->rx_ring.count * sizeof(struct e1000_rx_desc); + uint32_t rctl; + uint32_t rxcsum; + + /* make sure receives are disabled while setting up the descriptors */ + + rctl = E1000_READ_REG(&adapter->shared, RCTL); + E1000_WRITE_REG(&adapter->shared, RCTL, rctl & ~E1000_RCTL_EN); + + /* set the Receive Delay Timer Register */ + + E1000_WRITE_REG(&adapter->shared, RDTR, + adapter->rx_int_delay | E1000_RDT_FPDB); + + /* Setup the Base and Length of the Rx Descriptor Ring */ + + E1000_WRITE_REG(&adapter->shared, RDBAL, (rdba & 0x00000000FFFFFFFF)); + E1000_WRITE_REG(&adapter->shared, RDBAH, (rdba >> 32)); + + E1000_WRITE_REG(&adapter->shared, RDLEN, rdlen); + + /* Setup the HW Rx Head and Tail Descriptor Pointers */ + E1000_WRITE_REG(&adapter->shared, RDH, 0); + E1000_WRITE_REG(&adapter->shared, RDT, 0); + + /* Enable 82543 Receive Checksum Offload for TCP and UDP */ + if((adapter->shared.mac_type >= e1000_82543) && + (adapter->rx_csum == TRUE)) { + rxcsum = E1000_READ_REG(&adapter->shared, RXCSUM); + rxcsum |= E1000_RXCSUM_TUOFL; + E1000_WRITE_REG(&adapter->shared, RXCSUM, rxcsum); + } + + /* Enable Receives */ + + E1000_WRITE_REG(&adapter->shared, RCTL, rctl); + + return; +} + +/** + * e1000_free_tx_resources - Free Tx Resources + * @adapter: board private structure + * + * Free all transmit software resources + **/ + +static void +e1000_free_tx_resources(struct e1000_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + + e1000_clean_tx_ring(adapter); + + kfree(adapter->tx_ring.buffer_info); + adapter->tx_ring.buffer_info = NULL; + + pci_free_consistent(pdev, adapter->tx_ring.size, + adapter->tx_ring.desc, adapter->tx_ring.dma); + + adapter->tx_ring.desc = NULL; + + return; +} + +/** + * e1000_clean_tx_ring - Free Tx Buffers + * @adapter: board private structure + **/ + +static void +e1000_clean_tx_ring(struct e1000_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + unsigned long size; + int i; + + /* Free all the Tx ring sk_buffs */ + + for(i = 0; i < adapter->tx_ring.count; i++) { + if(adapter->tx_ring.buffer_info[i].skb) { + + pci_unmap_page(pdev, + adapter->tx_ring.buffer_info[i].dma, + adapter->tx_ring.buffer_info[i].length, + PCI_DMA_TODEVICE); + + dev_kfree_skb(adapter->tx_ring.buffer_info[i].skb); + + adapter->tx_ring.buffer_info[i].skb = NULL; + } + } + + size = sizeof(struct e1000_buffer) * adapter->tx_ring.count; + memset(adapter->tx_ring.buffer_info, 0, size); + + /* Zero out the descriptor ring */ + + memset(adapter->tx_ring.desc, 0, adapter->tx_ring.size); + + atomic_set(&adapter->tx_ring.unused, adapter->tx_ring.count); + adapter->tx_ring.next_to_use = 0; + adapter->tx_ring.next_to_clean = 0; + + E1000_WRITE_REG(&adapter->shared, TDH, 0); + E1000_WRITE_REG(&adapter->shared, TDT, 0); + + return; +} + +/** + * e1000_free_rx_resources - Free Rx Resources + * @adapter: board private structure + * + * Free all receive software resources + **/ + +static void +e1000_free_rx_resources(struct e1000_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + + e1000_clean_rx_ring(adapter); + + kfree(adapter->rx_ring.buffer_info); + adapter->rx_ring.buffer_info = NULL; + + pci_free_consistent(pdev, adapter->rx_ring.size, + adapter->rx_ring.desc, adapter->rx_ring.dma); + + adapter->rx_ring.desc = NULL; + + return; +} + +/** + * e1000_clean_rx_ring - Free Rx Buffers + * @adapter: board private structure + **/ + +static void +e1000_clean_rx_ring(struct e1000_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + unsigned long size; + int i; + + /* Free all the Rx ring sk_buffs */ + + for(i = 0; i < adapter->rx_ring.count; i++) { + if(adapter->rx_ring.buffer_info[i].skb) { + + pci_unmap_single(pdev, + adapter->rx_ring.buffer_info[i].dma, + adapter->rx_ring.buffer_info[i].length, + PCI_DMA_FROMDEVICE); + + dev_kfree_skb(adapter->rx_ring.buffer_info[i].skb); + + adapter->rx_ring.buffer_info[i].skb = NULL; + } + } + + size = sizeof(struct e1000_buffer) * adapter->rx_ring.count; + memset(adapter->rx_ring.buffer_info, 0, size); + + /* Zero out the descriptor ring */ + + memset(adapter->rx_ring.desc, 0, adapter->rx_ring.size); + + adapter->rx_ring.unused_count = adapter->rx_ring.count; + adapter->rx_ring.next_to_clean = 0; + adapter->rx_ring.next_to_use = 0; + + E1000_WRITE_REG(&adapter->shared, RDH, 0); + E1000_WRITE_REG(&adapter->shared, RDT, 0); + + return; +} + +/* The 82542 2.0 (revision 2) needs to have the receive unit in reset + * and memory write and invalidate disabled for certain operations + */ +static void +e1000_enter_82542_rst(struct e1000_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct net_device *netdev = adapter->netdev; + uint16_t pci_command_word = adapter->shared.pci_cmd_word; + uint32_t rctl; + + if(pci_command_word & PCI_COMMAND_INVALIDATE) { + pci_command_word &= ~PCI_COMMAND_INVALIDATE; + pci_write_config_word(pdev, PCI_COMMAND, pci_command_word); + } + + rctl = E1000_READ_REG(&adapter->shared, RCTL); + rctl |= E1000_RCTL_RST; + E1000_WRITE_REG(&adapter->shared, RCTL, rctl); + msec_delay(5); + + if(netif_running(netdev)) + e1000_clean_rx_ring(adapter); + return; +} + +static void +e1000_leave_82542_rst(struct e1000_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct net_device *netdev = adapter->netdev; + uint16_t pci_command_word = adapter->shared.pci_cmd_word; + uint32_t rctl; + + rctl = E1000_READ_REG(&adapter->shared, RCTL); + rctl &= ~E1000_RCTL_RST; + E1000_WRITE_REG(&adapter->shared, RCTL, rctl); + msec_delay(5); + + if(pci_command_word & PCI_COMMAND_INVALIDATE) + pci_write_config_word(pdev, PCI_COMMAND, pci_command_word); + + if(netif_running(netdev)) { + e1000_configure_rx(adapter); + e1000_alloc_rx_buffers(adapter); + } + return; +} + +/** + * e1000_set_mac - Change the Ethernet Address of the NIC + * @netdev: network interface device structure + * @p: pointer to an address structure + * + * Returns 0 on success, negative on failure + **/ + +static int +e1000_set_mac(struct net_device *netdev, void *p) +{ + struct e1000_adapter *adapter = netdev->priv; + struct sockaddr *addr = p; + + /* 82542 2.0 needs to be in reset to write receive address registers */ + + if(adapter->shared.mac_type == e1000_82542_rev2_0) + e1000_enter_82542_rst(adapter); + + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + memcpy(adapter->shared.mac_addr, addr->sa_data, netdev->addr_len); + + e1000_rar_set(&adapter->shared, adapter->shared.mac_addr, 0); + + if(adapter->shared.mac_type == e1000_82542_rev2_0) + e1000_leave_82542_rst(adapter); + + return 0; +} + +/** + * e1000_set_multi - Multicast and Promiscuous mode set + * @netdev: network interface device structure + * + * The set_multi entry point is called whenever the multicast address + * list or the network interface flags are updated. This routine is + * resposible for configuring the hardware for proper multicast, + * promiscuous mode, and all-multi behavior. + **/ + +static void +e1000_set_multi(struct net_device *netdev) +{ + struct e1000_adapter *adapter = netdev->priv; + struct e1000_shared_adapter *shared = &adapter->shared; + struct dev_mc_list *mc_ptr; + uint32_t rctl; + uint32_t hash_value; + int i; + + /* Check for Promiscuous and All Multicast modes */ + + rctl = E1000_READ_REG(shared, RCTL); + + if(netdev->flags & IFF_PROMISC) { + rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE); + } else if(netdev->flags & IFF_ALLMULTI) { + rctl |= E1000_RCTL_MPE; + rctl &= ~E1000_RCTL_UPE; + } else { + rctl &= ~(E1000_RCTL_UPE | E1000_RCTL_MPE); + } + + E1000_WRITE_REG(shared, RCTL, rctl); + + /* 82542 2.0 needs to be in reset to write receive address registers */ + + if(shared->mac_type == e1000_82542_rev2_0) + e1000_enter_82542_rst(adapter); + + /* load the first 15 multicast address into the exact filters 1-15 + * RAR 0 is used for the station MAC adddress + * if there are not 15 addresses, go ahead and clear the filters + */ + mc_ptr = netdev->mc_list; + + for(i = 1; i < E1000_RAR_ENTRIES; i++) { + if(mc_ptr) { + e1000_rar_set(shared, mc_ptr->dmi_addr, i); + mc_ptr = mc_ptr->next; + } else { + E1000_WRITE_REG_ARRAY(shared, RA, i << 1, 0); + E1000_WRITE_REG_ARRAY(shared, RA, (i << 1) + 1, 0); + } + } + + /* clear the old settings from the multicast hash table */ + + for(i = 0; i < E1000_NUM_MTA_REGISTERS; i++) + E1000_WRITE_REG_ARRAY(shared, MTA, i, 0); + + /* load any remaining addresses into the hash table */ + + for(; mc_ptr; mc_ptr = mc_ptr->next) { + hash_value = e1000_hash_mc_addr(shared, mc_ptr->dmi_addr); + e1000_mta_set(shared, hash_value); + } + + if(shared->mac_type == e1000_82542_rev2_0) + e1000_leave_82542_rst(adapter); + return; +} + + +/* need to wait a few seconds after link up to get diagnostic information from the phy */ + +static void +e1000_update_phy_info(unsigned long data) +{ + struct e1000_adapter *adapter = (struct e1000_adapter *) data; + e1000_phy_get_info(&adapter->shared, &adapter->phy_info); + return; +} + +/** + * e1000_watchdog - Timer Call-back + * @data: pointer to netdev cast into an unsigned long + **/ + +static void +e1000_watchdog(unsigned long data) +{ + struct e1000_adapter *adapter = (struct e1000_adapter *) data; + struct net_device *netdev = adapter->netdev; + + e1000_check_for_link(&adapter->shared); + + if(E1000_READ_REG(&adapter->shared, STATUS) & E1000_STATUS_LU) { + if(!netif_carrier_ok(netdev)) { + e1000_get_speed_and_duplex(&adapter->shared, + &adapter->link_speed, + &adapter->link_duplex); + + printk(KERN_INFO + "e1000: %s NIC Link is Up %d Mbps %s\n", + netdev->name, adapter->link_speed, + adapter->link_duplex == FULL_DUPLEX ? + "Full Duplex" : "Half Duplex"); + + netif_carrier_on(netdev); + adapter->trans_finish = jiffies; + netif_wake_queue(netdev); + mod_timer(&adapter->phy_info_timer, jiffies + 2 * HZ); + } + } else { + if(netif_carrier_ok(netdev)) { + adapter->link_speed = 0; + adapter->link_duplex = 0; + printk(KERN_INFO + "e1000: %s NIC Link is Down\n", + netdev->name); + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } + } + + e1000_update_stats(adapter); + + /* Reset the timer */ + mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ); + + return; +} + +/** + * e1000_xmit_frame - Transmit entry point + * @skb: buffer with frame data to transmit + * @netdev: network interface device structure + * + * Returns 0 on success, 1 on error + * + * e1000_xmit_frame is called by the stack to initiate a transmit. + * The out of resource condition is checked after each successful Tx + * so that the stack can be notified, preventing the driver from + * ever needing to drop a frame. The atomic operations on + * tx_ring.unused are used to syncronize with the transmit + * interrupt processing code without the need for a spinlock. + **/ + +#define TXD_USE_COUNT(x) (((x) >> 12) + ((x) & 0x0fff ? 1 : 0)) + +#define SETUP_TXD_PAGE(L, P, O) do { \ + tx_ring->buffer_info[i].length = (L); \ + tx_ring->buffer_info[i].dma = \ + pci_map_page(pdev, (P), (O), (L), PCI_DMA_TODEVICE); \ + tx_desc->buffer_addr = cpu_to_le64(tx_ring->buffer_info[i].dma); \ + tx_desc->lower.data = cpu_to_le32(txd_lower | (L)); \ + tx_desc->upper.data = cpu_to_le32(txd_upper); \ +} while (0) + +#define SETUP_TXD_PTR(L, P) \ + SETUP_TXD_PAGE((L), virt_to_page(P), (unsigned long)(P) & ~PAGE_MASK) + +#define QUEUE_TXD() do { i = (i + 1) % tx_ring->count; \ + atomic_dec(&tx_ring->unused); } while (0) + + +static int +e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct e1000_adapter *adapter = netdev->priv; + struct e1000_desc_ring *tx_ring = &adapter->tx_ring; + struct pci_dev *pdev = adapter->pdev; + struct e1000_tx_desc *tx_desc; + int f, len, offset, txd_needed; + skb_frag_t *frag; + + int i = tx_ring->next_to_use; + uint32_t txd_upper = 0; + uint32_t txd_lower = adapter->txd_cmd; + + + /* If controller appears hung, force transmit timeout */ + + if (time_after(netdev->trans_start, adapter->trans_finish + HZ) && + /* If transmitting XOFFs, we're not really hung */ + !(E1000_READ_REG(&adapter->shared, STATUS) & E1000_STATUS_TXOFF)) { + adapter->trans_finish = jiffies; + netif_stop_queue(netdev); + return 1; + } + + txd_needed = TXD_USE_COUNT(skb->len - skb->data_len); + + for(f = 0; f < skb_shinfo(skb)->nr_frags; f++) { + frag = &skb_shinfo(skb)->frags[f]; + txd_needed += TXD_USE_COUNT(frag->size); + } + + if(skb->ip_summed == CHECKSUM_HW) + txd_needed += 1; + + /* make sure there are enough Tx descriptors available in the ring */ + + if(atomic_read(&tx_ring->unused) <= (txd_needed + 1)) { + adapter->net_stats.tx_dropped++; + netif_stop_queue(netdev); + return 1; + } + + if(skb->ip_summed == CHECKSUM_HW) { + struct e1000_context_desc *context_desc; + uint8_t css = skb->h.raw - skb->data; + uint8_t cso = (skb->h.raw + skb->csum) - skb->data; + + context_desc = E1000_CONTEXT_DESC(*tx_ring, i); + + context_desc->upper_setup.tcp_fields.tucss = css; + context_desc->upper_setup.tcp_fields.tucso = cso; + context_desc->upper_setup.tcp_fields.tucse = 0; + context_desc->tcp_seg_setup.data = 0; + context_desc->cmd_and_length = + cpu_to_le32(txd_lower | E1000_TXD_CMD_DEXT); + + QUEUE_TXD(); + + txd_upper |= E1000_TXD_POPTS_TXSM << 8; + txd_lower |= E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D; + } + + tx_desc = E1000_TX_DESC(*tx_ring, i); + len = skb->len - skb->data_len; + offset = 0; + + while(len > 4096) { + SETUP_TXD_PTR(4096, skb->data + offset); + QUEUE_TXD(); + + tx_desc = E1000_TX_DESC(*tx_ring, i); + len -= 4096; + offset += 4096; + } + + SETUP_TXD_PTR(len, skb->data + offset); + + for(f = 0; f < skb_shinfo(skb)->nr_frags; f++) { + frag = &skb_shinfo(skb)->frags[f]; + + QUEUE_TXD(); + + tx_desc = E1000_TX_DESC(*tx_ring, i); + len = frag->size; + offset = 0; + + while(len > 4096) { + SETUP_TXD_PAGE(4096, frag->page, + frag->page_offset + offset); + QUEUE_TXD(); + + tx_desc = E1000_TX_DESC(*tx_ring, i); + len -= 4096; + offset += 4096; + } + SETUP_TXD_PAGE(len, frag->page, frag->page_offset + offset); + } + + /* EOP and SKB pointer go with the last fragment */ + + tx_desc->lower.data |= cpu_to_le32(E1000_TXD_CMD_EOP); + tx_ring->buffer_info[i].skb = skb; + + QUEUE_TXD(); + + tx_ring->next_to_use = i; + + /* Move the HW Tx Tail Pointer */ + + E1000_WRITE_REG(&adapter->shared, TDT, i); + + netdev->trans_start = jiffies; + + return 0; +} + +#undef TXD_USE_COUNT +#undef SETUP_TXD +#undef QUEUE_TXD + +/** + * e1000_tx_timeout - Respond to a Tx Hang + * @netdev: network interface device structure + **/ + +static void +e1000_tx_timeout(struct net_device *netdev) +{ + struct e1000_adapter *adapter = netdev->priv; + + e1000_down(adapter); + e1000_up(adapter); +} + +/** + * e1000_get_stats - Get System Network Statistics + * @netdev: network interface device structure + * + * Returns the address of the device statistics structure. + * The statistics are actually updated from the timer callback. + **/ + +static struct net_device_stats * +e1000_get_stats(struct net_device *netdev) +{ + struct e1000_adapter *adapter = netdev->priv; + + return &adapter->net_stats; +} + +/** + * e1000_change_mtu - Change the Maximum Transfer Unit + * @netdev: network interface device structure + * @new_mtu: new value for maximum frame size + * + * Returns 0 on success, negative on failure + **/ + +static int +e1000_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct e1000_adapter *adapter = netdev->priv; + int old_mtu = adapter->rx_buffer_len; + int max_frame = new_mtu + ENET_HEADER_SIZE + CRC_LENGTH; + + if((max_frame < MINIMUM_ETHERNET_PACKET_SIZE + CRC_LENGTH) || + (max_frame > MAX_JUMBO_FRAME_SIZE + CRC_LENGTH)) { + E1000_ERR("Invalid MTU setting\n"); + return -EINVAL; + } + + if(max_frame <= MAXIMUM_ETHERNET_PACKET_SIZE + CRC_LENGTH) { + adapter->rx_buffer_len = E1000_RXBUFFER_2048; + + } else if(adapter->shared.mac_type < e1000_82543) { + E1000_ERR("Jumbo Frames not supported on 82542\n"); + return -EINVAL; + + } else if(max_frame <= E1000_RXBUFFER_2048) { + adapter->rx_buffer_len = E1000_RXBUFFER_2048; + + } else if(max_frame <= E1000_RXBUFFER_4096) { + adapter->rx_buffer_len = E1000_RXBUFFER_4096; + + } else if(max_frame <= E1000_RXBUFFER_8192) { + adapter->rx_buffer_len = E1000_RXBUFFER_8192; + + } else { + adapter->rx_buffer_len = E1000_RXBUFFER_16384; + } + + if(old_mtu != adapter->rx_buffer_len && netif_running(netdev)) { + + e1000_down(adapter); + e1000_clean_rx_ring(adapter); + e1000_clean_tx_ring(adapter); + e1000_up(adapter); + } + + netdev->mtu = new_mtu; + adapter->shared.max_frame_size = max_frame; + + return 0; +} + +/** + * e1000_update_stats - Update the board statistics counters + * @adapter: board private structure + **/ + +static void +e1000_update_stats(struct e1000_adapter *adapter) +{ + struct e1000_shared_adapter *shared = &adapter->shared; + unsigned long flags; + +#define PHY_IDLE_ERROR_COUNT_MASK 0x00FF + + spin_lock_irqsave(&adapter->stats_lock, flags); + + /* these counters are modified from e1000_adjust_tbi_stats, + * called from the interrupt context, so they must only + * be written while holding adapter->stats_lock + */ + + adapter->stats.crcerrs += E1000_READ_REG(shared, CRCERRS); + adapter->stats.gprc += E1000_READ_REG(shared, GPRC); + adapter->stats.gorcl += E1000_READ_REG(shared, GORCL); + adapter->stats.gorch += E1000_READ_REG(shared, GORCH); + adapter->stats.bprc += E1000_READ_REG(shared, BPRC); + adapter->stats.mprc += E1000_READ_REG(shared, MPRC); + adapter->stats.roc += E1000_READ_REG(shared, ROC); + adapter->stats.prc64 += E1000_READ_REG(shared, PRC64); + adapter->stats.prc127 += E1000_READ_REG(shared, PRC127); + adapter->stats.prc255 += E1000_READ_REG(shared, PRC255); + adapter->stats.prc511 += E1000_READ_REG(shared, PRC511); + adapter->stats.prc1023 += E1000_READ_REG(shared, PRC1023); + adapter->stats.prc1522 += E1000_READ_REG(shared, PRC1522); + + spin_unlock_irqrestore(&adapter->stats_lock, flags); + + /* the rest of the counters are only modified here */ + + adapter->stats.symerrs += E1000_READ_REG(shared, SYMERRS); + adapter->stats.mpc += E1000_READ_REG(shared, MPC); + adapter->stats.scc += E1000_READ_REG(shared, SCC); + adapter->stats.ecol += E1000_READ_REG(shared, ECOL); + adapter->stats.mcc += E1000_READ_REG(shared, MCC); + adapter->stats.latecol += E1000_READ_REG(shared, LATECOL); + adapter->stats.colc += E1000_READ_REG(shared, COLC); + adapter->stats.dc += E1000_READ_REG(shared, DC); + adapter->stats.sec += E1000_READ_REG(shared, SEC); + adapter->stats.rlec += E1000_READ_REG(shared, RLEC); + adapter->stats.xonrxc += E1000_READ_REG(shared, XONRXC); + adapter->stats.xontxc += E1000_READ_REG(shared, XONTXC); + adapter->stats.xoffrxc += E1000_READ_REG(shared, XOFFRXC); + adapter->stats.xofftxc += E1000_READ_REG(shared, XOFFTXC); + adapter->stats.fcruc += E1000_READ_REG(shared, FCRUC); + adapter->stats.gptc += E1000_READ_REG(shared, GPTC); + adapter->stats.gotcl += E1000_READ_REG(shared, GOTCL); + adapter->stats.gotch += E1000_READ_REG(shared, GOTCH); + adapter->stats.rnbc += E1000_READ_REG(shared, RNBC); + adapter->stats.ruc += E1000_READ_REG(shared, RUC); + adapter->stats.rfc += E1000_READ_REG(shared, RFC); + adapter->stats.rjc += E1000_READ_REG(shared, RJC); + adapter->stats.torl += E1000_READ_REG(shared, TORL); + adapter->stats.torh += E1000_READ_REG(shared, TORH); + adapter->stats.totl += E1000_READ_REG(shared, TOTL); + adapter->stats.toth += E1000_READ_REG(shared, TOTH); + adapter->stats.tpr += E1000_READ_REG(shared, TPR); + adapter->stats.tpt += E1000_READ_REG(shared, TPT); + adapter->stats.ptc64 += E1000_READ_REG(shared, PTC64); + adapter->stats.ptc127 += E1000_READ_REG(shared, PTC127); + adapter->stats.ptc255 += E1000_READ_REG(shared, PTC255); + adapter->stats.ptc511 += E1000_READ_REG(shared, PTC511); + adapter->stats.ptc1023 += E1000_READ_REG(shared, PTC1023); + adapter->stats.ptc1522 += E1000_READ_REG(shared, PTC1522); + adapter->stats.mptc += E1000_READ_REG(shared, MPTC); + adapter->stats.bptc += E1000_READ_REG(shared, BPTC); + + if(adapter->shared.mac_type >= e1000_82543) { + adapter->stats.algnerrc += E1000_READ_REG(shared, ALGNERRC); + adapter->stats.rxerrc += E1000_READ_REG(shared, RXERRC); + adapter->stats.tncrs += E1000_READ_REG(shared, TNCRS); + adapter->stats.cexterr += E1000_READ_REG(shared, CEXTERR); + adapter->stats.tsctc += E1000_READ_REG(shared, TSCTC); + adapter->stats.tsctfc += E1000_READ_REG(shared, TSCTFC); + } + + /* Fill out the OS statistics structure */ + + adapter->net_stats.rx_packets = adapter->stats.gprc; + adapter->net_stats.tx_packets = adapter->stats.gptc; + adapter->net_stats.rx_bytes = adapter->stats.gorcl; + adapter->net_stats.tx_bytes = adapter->stats.gotcl; + adapter->net_stats.multicast = adapter->stats.mprc; + adapter->net_stats.collisions = adapter->stats.colc; + + /* Rx Errors */ + + adapter->net_stats.rx_errors = adapter->stats.rxerrc + + adapter->stats.crcerrs + adapter->stats.algnerrc + + adapter->stats.rlec + adapter->stats.rnbc + + adapter->stats.mpc + adapter->stats.cexterr; + adapter->net_stats.rx_dropped = adapter->stats.rnbc; + adapter->net_stats.rx_length_errors = adapter->stats.rlec; + adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs; + adapter->net_stats.rx_frame_errors = adapter->stats.algnerrc; + adapter->net_stats.rx_fifo_errors = adapter->stats.mpc; + adapter->net_stats.rx_missed_errors = adapter->stats.mpc; + + /* Tx Errors */ + + adapter->net_stats.tx_errors = adapter->stats.ecol + + adapter->stats.latecol; + adapter->net_stats.tx_aborted_errors = adapter->stats.ecol; + adapter->net_stats.tx_window_errors = adapter->stats.latecol; + + /* Tx Dropped needs to be maintained elsewhere */ + + if(adapter->shared.media_type == e1000_media_type_copper) { + adapter->phy_stats.idle_errors += + (e1000_read_phy_reg(shared, PHY_1000T_STATUS) + & PHY_IDLE_ERROR_COUNT_MASK); + adapter->phy_stats.receive_errors += + e1000_read_phy_reg(shared, M88E1000_RX_ERR_CNTR); + } + return; +} + +/** + * e1000_irq_disable - Mask off interrupt generation on the NIC + * @adapter: board private structure + **/ + +static inline void +e1000_irq_disable(struct e1000_adapter *adapter) +{ + atomic_inc(&adapter->irq_sem); + E1000_WRITE_REG(&adapter->shared, IMC, ~0); + synchronize_irq(); + return; +} + +/** + * e1000_irq_enable - Enable default interrupt generation settings + * @adapter: board private structure + **/ + +static inline void +e1000_irq_enable(struct e1000_adapter *adapter) +{ + if(atomic_dec_and_test(&adapter->irq_sem)) + E1000_WRITE_REG(&adapter->shared, IMS, IMS_ENABLE_MASK); + return; +} + +/** + * e1000_intr - Interrupt Handler + * @irq: interrupt number + * @data: pointer to a network interface device structure + * @pt_regs: CPU registers structure + **/ + +static void +e1000_intr(int irq, void *data, struct pt_regs *regs) +{ + struct net_device *netdev = data; + struct e1000_adapter *adapter = netdev->priv; + uint32_t icr; + int i = E1000_MAX_INTR; + + while(i && (icr = E1000_READ_REG(&adapter->shared, ICR))) { + + if(icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) { + /* run the watchdog ASAP */ + adapter->shared.get_link_status = 1; + mod_timer(&adapter->watchdog_timer, jiffies); + } + + e1000_clean_rx_irq(adapter); + e1000_clean_tx_irq(adapter); + i--; + } + + return; +} + +/** + * e1000_clean_tx_irq - Reclaim resources after transmit completes + * @adapter: board private structure + **/ + +static void +e1000_clean_tx_irq(struct e1000_adapter *adapter) +{ + struct e1000_desc_ring *tx_ring = &adapter->tx_ring; + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + struct e1000_tx_desc *tx_desc; + int i; + + i = tx_ring->next_to_clean; + tx_desc = E1000_TX_DESC(*tx_ring, i); + + while(tx_desc->upper.data & cpu_to_le32(E1000_TXD_STAT_DD)) { + + if(tx_ring->buffer_info[i].dma) { + + pci_unmap_page(pdev, + tx_ring->buffer_info[i].dma, + tx_ring->buffer_info[i].length, + PCI_DMA_TODEVICE); + + tx_ring->buffer_info[i].dma = 0; + } + + if(tx_ring->buffer_info[i].skb) { + + dev_kfree_skb_irq(tx_ring->buffer_info[i].skb); + + tx_ring->buffer_info[i].skb = NULL; + } + + memset(tx_desc, 0, sizeof(struct e1000_tx_desc)); + mb(); + + atomic_inc(&tx_ring->unused); + i = (i + 1) % tx_ring->count; + tx_desc = E1000_TX_DESC(*tx_ring, i); + + adapter->trans_finish = jiffies; + } + + tx_ring->next_to_clean = i; + + if(netif_queue_stopped(netdev) && netif_carrier_ok(netdev) && + (atomic_read(&tx_ring->unused) > E1000_TX_QUEUE_WAKE)) { + + netif_wake_queue(netdev); + } + return; +} + +/** + * e1000_clean_rx_irq - Send received data up the network stack, + * @adapter: board private structure + **/ + +static void +e1000_clean_rx_irq(struct e1000_adapter *adapter) +{ + struct e1000_desc_ring *rx_ring = &adapter->rx_ring; + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + struct e1000_rx_desc *rx_desc; + struct sk_buff *skb; + unsigned long flags; + uint32_t length; + uint8_t last_byte; + int i; + + i = rx_ring->next_to_clean; + rx_desc = E1000_RX_DESC(*rx_ring, i); + + while(rx_desc->status & E1000_RXD_STAT_DD) { + + pci_unmap_single(pdev, + rx_ring->buffer_info[i].dma, + rx_ring->buffer_info[i].length, + PCI_DMA_FROMDEVICE); + + skb = rx_ring->buffer_info[i].skb; + length = le16_to_cpu(rx_desc->length); + + if(!(rx_desc->status & E1000_RXD_STAT_EOP)) { + + /* All receives must fit into a single buffer */ + + E1000_DBG("Receive packet consumed multiple buffers\n"); + + dev_kfree_skb_irq(skb); + memset(rx_desc, 0, 16); + mb(); + rx_ring->buffer_info[i].skb = NULL; + + rx_ring->unused_count++; + + i = (i + 1) % rx_ring->count; + + rx_desc = E1000_RX_DESC(*rx_ring, i); + continue; + } + + if(rx_desc->errors & E1000_RXD_ERR_FRAME_ERR_MASK) { + + last_byte = *(skb->data + length - 1); + + if(TBI_ACCEPT(&adapter->shared, rx_desc->special, + rx_desc->errors, length, last_byte)) { + + spin_lock_irqsave(&adapter->stats_lock, flags); + + e1000_tbi_adjust_stats(&adapter->shared, + &adapter->stats, + length, skb->data); + + spin_unlock_irqrestore(&adapter->stats_lock, + flags); + length--; + } else { + + dev_kfree_skb_irq(skb); + memset(rx_desc, 0, 16); + mb(); + rx_ring->buffer_info[i].skb = NULL; + + rx_ring->unused_count++; + i = (i + 1) % rx_ring->count; + + rx_desc = E1000_RX_DESC(*rx_ring, i); + continue; + } + } + + /* Good Receive */ + skb_put(skb, length - CRC_LENGTH); + + /* Receive Checksum Offload */ + e1000_rx_checksum(adapter, rx_desc, skb); + + skb->protocol = eth_type_trans(skb, netdev); + netif_rx(skb); + + memset(rx_desc, 0, sizeof(struct e1000_rx_desc)); + mb(); + rx_ring->buffer_info[i].skb = NULL; + + rx_ring->unused_count++; + + i = (i + 1) % rx_ring->count; + + rx_desc = E1000_RX_DESC(*rx_ring, i); + } + + rx_ring->next_to_clean = i; + + e1000_alloc_rx_buffers(adapter); + + return; +} + +/** + * e1000_alloc_rx_buffers - Replace used receive buffers + * @data: address of board private structure + **/ + +static void +e1000_alloc_rx_buffers(struct e1000_adapter *adapter) +{ + struct e1000_desc_ring *rx_ring = &adapter->rx_ring; + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + struct e1000_rx_desc *rx_desc; + struct sk_buff *skb; + int reserve_len; + int i; + + if(!netif_running(netdev)) + return; + + reserve_len = 2; + + i = rx_ring->next_to_use; + + while(!rx_ring->buffer_info[i].skb) { + rx_desc = E1000_RX_DESC(*rx_ring, i); + + skb = alloc_skb(adapter->rx_buffer_len + reserve_len, + GFP_ATOMIC); + + if(!skb) { + /* Better luck next round */ + break; + } + + /* Make buffer alignment 2 beyond a 16 byte boundary + * this will result in a 16 byte aligned IP header after + * the 14 byte MAC header is removed + */ + skb_reserve(skb, reserve_len); + + skb->dev = netdev; + + rx_ring->buffer_info[i].skb = skb; + rx_ring->buffer_info[i].length = adapter->rx_buffer_len; + rx_ring->buffer_info[i].dma = + pci_map_single(pdev, + skb->data, + adapter->rx_buffer_len, + PCI_DMA_FROMDEVICE); + + rx_desc->buffer_addr = cpu_to_le64(rx_ring->buffer_info[i].dma); + + /* move tail */ + E1000_WRITE_REG(&adapter->shared, RDT, i); + + atomic_dec(&rx_ring->unused); + + i = (i + 1) % rx_ring->count; + } + + rx_ring->next_to_use = i; + return; +} + +/** + * e1000_ioctl - + * @netdev: + * @ifreq: + * @cmd: + **/ + +static int +e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCETHTOOL: + return e1000_ethtool_ioctl(netdev, ifr); + default: + return -EOPNOTSUPP; + } +} + +/** + * e1000_rx_checksum - Receive Checksum Offload for 82543 + * @adapter: board private structure + * @rx_desc: receive descriptor + * @sk_buff: socket buffer with received data + **/ + +static inline void +e1000_rx_checksum(struct e1000_adapter *adapter, + struct e1000_rx_desc *rx_desc, + struct sk_buff *skb) +{ + /* 82543 or newer only */ + if((adapter->shared.mac_type < e1000_82543) || + /* Ignore Checksum bit is set */ + (rx_desc->status & E1000_RXD_STAT_IXSM) || + /* TCP Checksum has not been calculated */ + (!(rx_desc->status & E1000_RXD_STAT_TCPCS))) { + skb->ip_summed = CHECKSUM_NONE; + return; + } + + /* At this point we know the hardware did the TCP checksum */ + /* now look at the TCP checksum error bit */ + if(rx_desc->errors & E1000_RXD_ERR_TCPE) { + /* let the stack verify checksum errors */ + skb->ip_summed = CHECKSUM_NONE; + adapter->hw_csum_err++; + } else { + /* TCP checksum is good */ + skb->ip_summed = CHECKSUM_UNNECESSARY; + adapter->hw_csum_good++; + } + return; +} + + +/** + * e1000_enable_WOL - Wake On Lan Support (Magic Pkt) + * @adapter: Adapter structure + **/ + +void +e1000_enable_WOL(struct e1000_adapter *adapter) +{ + uint32_t wuc; + + if(adapter->shared.mac_type < e1000_82544) + return; + + if(adapter->wol) { + wuc = E1000_WUC_APME | E1000_WUC_PME_EN | + E1000_WUC_PME_STATUS | E1000_WUC_APMPME; + + E1000_WRITE_REG(&adapter->shared, WUC, wuc); + + E1000_WRITE_REG(&adapter->shared, WUFC, adapter->wol); + } + + return; +} + +void +e1000_write_pci_cfg(struct e1000_shared_adapter *shared, + uint32_t reg, uint16_t *value) +{ + struct e1000_adapter *adapter = shared->back; + + pci_write_config_word(adapter->pdev, reg, *value); + return; +} + +/* e1000_main.c */ diff -Nru a/drivers/net/e1000/e1000_osdep.h b/drivers/net/e1000/e1000_osdep.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e1000/e1000_osdep.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,142 @@ +/******************************************************************************* + + This software program is available to you under a choice of one of two + licenses. You may choose to be licensed under either the GNU General Public + License (GPL) Version 2, June 1991, available at + http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the + text of which follows: + + Recipient has requested a license and Intel Corporation ("Intel") is willing + to grant a license for the software entitled Linux Base Driver for the + Intel(R) PRO/1000 Family of Adapters (e1000) (the "Software") being provided + by Intel Corporation. The following definitions apply to this license: + + "Licensed Patents" means patent claims licensable by Intel Corporation which + are necessarily infringed by the use of sale of the Software alone or when + combined with the operating system referred to below. + + "Recipient" means the party to whom Intel delivers this Software. + + "Licensee" means Recipient and those third parties that receive a license to + any operating system available under the GNU Public License version 2.0 or + later. + + Copyright (c) 1999 - 2002 Intel Corporation. + All rights reserved. + + The license is provided to Recipient and Recipient's Licensees under the + following terms. + + Redistribution and use in source and binary forms of the Software, with or + without modification, are permitted provided that the following conditions + are met: + + Redistributions of source code of the Software may retain the above + copyright notice, this list of conditions and the following disclaimer. + + Redistributions in binary form of the Software may reproduce the above + copyright notice, this list of conditions and the following disclaimer in + the documentation and/or materials provided with the distribution. + + Neither the name of Intel Corporation nor the names of its contributors + shall be used to endorse or promote products derived from this Software + without specific prior written permission. + + Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, + royalty-free patent license under Licensed Patents to make, use, sell, offer + to sell, import and otherwise transfer the Software, if any, in source code + and object code form. This license shall include changes to the Software + that are error corrections or other minor changes to the Software that do + not add functionality or features when the Software is incorporated in any + version of an operating system that has been distributed under the GNU + General Public License 2.0 or later. This patent license shall apply to the + combination of the Software and any operating system licensed under the GNU + Public License version 2.0 or later if, at the time Intel provides the + Software to Recipient, such addition of the Software to the then publicly + available versions of such operating systems available under the GNU Public + License version 2.0 or later (whether in gold, beta or alpha form) causes + such combination to be covered by the Licensed Patents. The patent license + shall not apply to any other combinations which include the Software. NO + hardware per se is licensed hereunder. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. + +*******************************************************************************/ + + +/* glue for the OS independant part of e1000 + * includes register access macros + */ + +#ifndef _E1000_OSDEP_H_ +#define _E1000_OSDEP_H_ + +#include +#include +#include +#include +#include + +#define usec_delay(x) udelay(x) +#define msec_delay(x) do { if(in_interrupt()) { \ + mdelay(x); \ + } else { \ + set_current_state(TASK_UNINTERRUPTIBLE); \ + schedule_timeout((x * HZ)/1000); \ + } } while(0) + +#define PCI_COMMAND_REGISTER PCI_COMMAND +#define CMD_MEM_WRT_INVALIDATE PCI_COMMAND_INVALIDATE + +typedef enum { + FALSE = 0, + TRUE = 1 +} boolean_t; + +#define ASSERT(x) if(!(x)) BUG() +#define MSGOUT(S, A, B) printk(KERN_DEBUG S "\n", A, B) + +#if DBG +#define DEBUGOUT(S) printk(KERN_DEBUG S "\n") +#define DEBUGOUT1(S, A...) printk(KERN_DEBUG S "\n", A) +#else +#define DEBUGOUT(S) +#define DEBUGOUT1(S, A...) +#endif + +#define DEBUGFUNC(F) DEBUGOUT(F) +#define DEBUGOUT2 DEBUGOUT1 +#define DEBUGOUT3 DEBUGOUT2 +#define DEBUGOUT7 DEBUGOUT3 + + +#define E1000_WRITE_REG(a, reg, value) ( \ + ((a)->mac_type >= e1000_82543) ? \ + (writel((value), ((a)->hw_addr + E1000_##reg))) : \ + (writel((value), ((a)->hw_addr + E1000_82542_##reg)))) + +#define E1000_READ_REG(a, reg) ( \ + ((a)->mac_type >= e1000_82543) ? \ + readl((a)->hw_addr + E1000_##reg) : \ + readl((a)->hw_addr + E1000_82542_##reg)) + +#define E1000_WRITE_REG_ARRAY(a, reg, offset, value) ( \ + ((a)->mac_type >= e1000_82543) ? \ + writel((value), ((a)->hw_addr + E1000_##reg + ((offset) << 2))) : \ + writel((value), ((a)->hw_addr + E1000_82542_##reg + ((offset) << 2)))) + +#define E1000_READ_REG_ARRAY(a, reg, offset) ( \ + ((a)->mac_type >= e1000_82543) ? \ + readl((a)->hw_addr + E1000_##reg + ((offset) << 2)) : \ + readl((a)->hw_addr + E1000_82542_##reg + ((offset) << 2))) + +#endif /* _E1000_OSDEP_H_ */ diff -Nru a/drivers/net/e1000/e1000_param.c b/drivers/net/e1000/e1000_param.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e1000/e1000_param.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,709 @@ +/******************************************************************************* + + This software program is available to you under a choice of one of two + licenses. You may choose to be licensed under either the GNU General Public + License (GPL) Version 2, June 1991, available at + http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the + text of which follows: + + Recipient has requested a license and Intel Corporation ("Intel") is willing + to grant a license for the software entitled Linux Base Driver for the + Intel(R) PRO/1000 Family of Adapters (e1000) (the "Software") being provided + by Intel Corporation. The following definitions apply to this license: + + "Licensed Patents" means patent claims licensable by Intel Corporation which + are necessarily infringed by the use of sale of the Software alone or when + combined with the operating system referred to below. + + "Recipient" means the party to whom Intel delivers this Software. + + "Licensee" means Recipient and those third parties that receive a license to + any operating system available under the GNU Public License version 2.0 or + later. + + Copyright (c) 1999 - 2002 Intel Corporation. + All rights reserved. + + The license is provided to Recipient and Recipient's Licensees under the + following terms. + + Redistribution and use in source and binary forms of the Software, with or + without modification, are permitted provided that the following conditions + are met: + + Redistributions of source code of the Software may retain the above + copyright notice, this list of conditions and the following disclaimer. + + Redistributions in binary form of the Software may reproduce the above + copyright notice, this list of conditions and the following disclaimer in + the documentation and/or materials provided with the distribution. + + Neither the name of Intel Corporation nor the names of its contributors + shall be used to endorse or promote products derived from this Software + without specific prior written permission. + + Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, + royalty-free patent license under Licensed Patents to make, use, sell, offer + to sell, import and otherwise transfer the Software, if any, in source code + and object code form. This license shall include changes to the Software + that are error corrections or other minor changes to the Software that do + not add functionality or features when the Software is incorporated in any + version of an operating system that has been distributed under the GNU + General Public License 2.0 or later. This patent license shall apply to the + combination of the Software and any operating system licensed under the GNU + Public License version 2.0 or later if, at the time Intel provides the + Software to Recipient, such addition of the Software to the then publicly + available versions of such operating systems available under the GNU Public + License version 2.0 or later (whether in gold, beta or alpha form) causes + such combination to be covered by the Licensed Patents. The patent license + shall not apply to any other combinations which include the Software. NO + hardware per se is licensed hereunder. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. + +*******************************************************************************/ + +#include "e1000.h" + +/* This is the only thing that needs to be changed to adjust the + * maximum number of ports that the driver can manage. + */ + +#define E1000_MAX_NIC 32 + +#define OPTION_UNSET -1 +#define OPTION_DISABLED 0 +#define OPTION_ENABLED 1 + +/* Module Parameters are always initialized to -1, so that the driver + * can tell the difference between no user specified value or the + * user asking for the default value. + * The true default values are loaded in when e1000_check_options is called. + * + * This is a GCC extension to ANSI C. + * See the item "Labeled Elements in Initializers" in the section + * "Extensions to the C Language Family" of the GCC documentation. + */ + +#define E1000_PARAM_INIT { [0 ... E1000_MAX_NIC] = OPTION_UNSET } + +/* All parameters are treated the same, as an integer array of values. + * This macro just reduces the need to repeat the same declaration code + * over and over (plus this helps to avoid typo bugs). + */ + +#define E1000_PARAM(X, S) \ +static const int __devinitdata X[E1000_MAX_NIC + 1] = E1000_PARAM_INIT; \ +MODULE_PARM(X, "1-" __MODULE_STRING(E1000_MAX_NIC) "i"); \ +MODULE_PARM_DESC(X, S); + +/* Transmit Descriptor Count + * + * Valid Range: 80-256 for 82542 and 82543 gigabit ethernet controllers + * Valid Range: 80-4096 for 82544 + * + * Default Value: 256 + */ + +E1000_PARAM(TxDescriptors, "Number of transmit descriptors"); + +/* Receive Descriptor Count + * + * Valid Range: 80-256 for 82542 and 82543 gigabit ethernet controllers + * Valid Range: 80-4096 for 82544 + * + * Default Value: 80 + */ + +E1000_PARAM(RxDescriptors, "Number of receive descriptors"); + +/* User Specified Speed Override + * + * Valid Range: 0, 10, 100, 1000 + * - 0 - auto-negotiate at all supported speeds + * - 10 - only link at 10 Mbps + * - 100 - only link at 100 Mbps + * - 1000 - only link at 1000 Mbps + * + * Default Value: 0 + */ + +E1000_PARAM(Speed, "Speed setting"); + +/* User Specified Duplex Override + * + * Valid Range: 0-2 + * - 0 - auto-negotiate for duplex + * - 1 - only link at half duplex + * - 2 - only link at full duplex + * + * Default Value: 0 + */ + +E1000_PARAM(Duplex, "Duplex setting"); + +/* Auto-negotiation Advertisement Override + * + * Valid Range: 0x00-0x0F, 0x20-0x2F + * + * The AutoNeg value is a bit mask describing which speed and duplex + * combinations should be advertised during auto-negotiation. + * The supported speed and duplex modes are listed below + * + * Bit 7 6 5 4 3 2 1 0 + * Speed (Mbps) N/A N/A 1000 N/A 100 100 10 10 + * Duplex Full Full Half Full Half + * + * Default Value: 0x2F + */ + +E1000_PARAM(AutoNeg, "Advertised auto-negotiation setting"); + +/* User Specified Flow Control Override + * + * Valid Range: 0-3 + * - 0 - No Flow Control + * - 1 - Rx only, respond to PAUSE frames but do not generate them + * - 2 - Tx only, generate PAUSE frames but ignore them on receive + * - 3 - Full Flow Control Support + * + * Default Value: Read flow control settings from the EEPROM + */ + +E1000_PARAM(FlowControl, "Flow Control setting"); + +/* XsumRX - Receive Checksum Offload Enable/Disable + * + * Valid Range: 0, 1 + * - 0 - disables all checksum offload + * - 1 - enables receive IP/TCP/UDP checksum offload + * on 82543 based NICs + * + * Default Value: 1 + */ + +E1000_PARAM(XsumRX, "Disable or enable Receive Checksum offload"); + +/* Transmit Interrupt Delay in units of 1.024 microseconds + * + * Valid Range: 0-65535 + * + * Default Value: 64 + */ + +E1000_PARAM(TxIntDelay, "Transmit Interrupt Delay"); + +/* Receive Interrupt Delay in units of 1.024 microseconds + * + * Valid Range: 0-65535 + * + * Default Value: 64 + */ + +E1000_PARAM(RxIntDelay, "Receive Interrupt Delay"); + +/* MDI-X Support Enable/Disable - Applies only to Copper PHY + * + * Valid Range: 0, 3 + * - 0 - Auto in all modes + * - 1 - MDI + * - 2 - MDI-X + * - 3 - Auto in 1000 Base-T mode (MDI in 10 Base-T and 100 Base-T) + * + * Default Value: 0 (Auto) + */ + +E1000_PARAM(MdiX, "Set MDI/MDI-X Mode"); + +/* Automatic Correction of Reversed Cable Polarity Enable/Disable + * This setting applies only to Copper PHY + * + * Valid Range: 0, 1 + * - 0 - Disabled + * - 1 - Enabled + * + * Default Value: 1 (Enabled) + */ + +E1000_PARAM(DisablePolarityCorrection, + "Disable or enable Automatic Correction for Reversed Cable Polarity"); + + +#define AUTONEG_ADV_DEFAULT 0x2F +#define AUTONEG_ADV_MASK 0x2F +#define FLOW_CONTROL_DEFAULT FLOW_CONTROL_FULL + +#define DEFAULT_TXD 256 +#define MAX_TXD 256 +#define MIN_TXD 80 +#define MAX_82544_TXD 4096 + +#define DEFAULT_RXD 80 +#define MAX_RXD 256 +#define MIN_RXD 80 +#define MAX_82544_RXD 4096 + +#define DEFAULT_TIDV 64 +#define MAX_TIDV 0xFFFF +#define MIN_TIDV 0 + +#define DEFAULT_RIDV 64 +#define MAX_RIDV 0xFFFF +#define MIN_RIDV 0 + +#define DEFAULT_MDIX 0 +#define MAX_MDIX 3 +#define MIN_MDIX 0 + + +struct e1000_option { + enum { enable_option, range_option, list_option } type; + char *name; + char *err; + int def; + union { + struct { /* range_option info */ + int min; + int max; + } r; + struct { /* list_option info */ + int nr; + struct e1000_opt_list { int i; char *str; } *p; + } l; + } arg; +}; + + +static int __devinit +e1000_validate_option(int *value, struct e1000_option *opt) +{ + if(*value == OPTION_UNSET) { + *value = opt->def; + return 0; + } + + switch (opt->type) { + case enable_option: + switch (*value) { + case OPTION_ENABLED: + printk(KERN_INFO "%s Enabled\n", opt->name); + return 0; + case OPTION_DISABLED: + printk(KERN_INFO "%s Disabled\n", opt->name); + return 0; + } + break; + case range_option: + if(*value >= opt->arg.r.min && *value <= opt->arg.r.max) { + printk(KERN_INFO "%s set to %i\n", opt->name, *value); + return 0; + } + break; + case list_option: { + int i; + struct e1000_opt_list *ent; + + for(i = 0; i < opt->arg.l.nr; i++) { + ent = &opt->arg.l.p[i]; + if(*value == ent->i) { + if(ent->str[0] != '\0') + printk(KERN_INFO "%s\n", ent->str); + return 0; + } + } + } + break; + default: + BUG(); + } + + printk(KERN_INFO "Invalid %s specified (%i) %s\n", + opt->name, *value, opt->err); + *value = opt->def; + return -1; +} + +#define LIST_LEN(l) (sizeof(l) / sizeof(l[0])) + +static void e1000_check_fiber_options(struct e1000_adapter *adapter); +static void e1000_check_copper_options(struct e1000_adapter *adapter); + +/** + * e1000_check_options - Range Checking for Command Line Parameters + * @adapter: board private structure + * + * This routine checks all command line paramters for valid user + * input. If an invalid value is given, or if no user specified + * value exists, a default value is used. The final value is stored + * in a variable in the adapter structure. + **/ + +void __devinit +e1000_check_options(struct e1000_adapter *adapter) +{ + int bd = adapter->bd_number; + if(bd >= E1000_MAX_NIC) { + printk(KERN_NOTICE + "Warning: no configuration for board #%i\n", bd); + printk(KERN_NOTICE "Using defaults for all values\n"); + bd = E1000_MAX_NIC; + } + + { /* Transmit Descriptor Count */ + struct e1000_option opt = { + type: range_option, + name: "Transmit Descriptors", + err: "using default of " __MODULE_STRING(DEFAULT_TXD), + def: DEFAULT_TXD, + arg: { r: { min: MIN_TXD }} + }; + struct e1000_desc_ring *tx_ring = &adapter->tx_ring; + e1000_mac_type mac_type = adapter->shared.mac_type; + opt.arg.r.max = mac_type < e1000_82544 ? MAX_TXD : MAX_82544_TXD; + + tx_ring->count = TxDescriptors[bd]; + e1000_validate_option(&tx_ring->count, &opt); + E1000_ROUNDUP(tx_ring->count, REQ_TX_DESCRIPTOR_MULTIPLE); + } + { /* Receive Descriptor Count */ + struct e1000_option opt = { + type: range_option, + name: "Receive Descriptors", + err: "using default of " __MODULE_STRING(DEFAULT_RXD), + def: DEFAULT_RXD, + arg: { r: { min: MIN_RXD }} + }; + struct e1000_desc_ring *rx_ring = &adapter->rx_ring; + e1000_mac_type mac_type = adapter->shared.mac_type; + opt.arg.r.max = mac_type < e1000_82544 ? MAX_RXD : MAX_82544_RXD; + + rx_ring->count = RxDescriptors[bd]; + e1000_validate_option(&rx_ring->count, &opt); + E1000_ROUNDUP(rx_ring->count, REQ_RX_DESCRIPTOR_MULTIPLE); + } + { /* Checksum Offload Enable/Disable */ + struct e1000_option opt = { + type: enable_option, + name: "Checksum Offload", + err: "defaulting to Enabled", + def: OPTION_ENABLED + }; + + int rx_csum = XsumRX[bd]; + e1000_validate_option(&rx_csum, &opt); + adapter->rx_csum = rx_csum; + } + { /* Flow Control */ + + struct e1000_opt_list fc_list[] = + {{ e1000_fc_none, "Flow Control Disabled" }, + { e1000_fc_rx_pause,"Flow Control Receive Only" }, + { e1000_fc_tx_pause,"Flow Control Transmit Only" }, + { e1000_fc_full, "Flow Control Enabled" }, + { e1000_fc_default, "Flow Control Hardware Default" }}; + + struct e1000_option opt = { + type: list_option, + name: "Flow Control", + err: "reading default settings from EEPROM", + def: e1000_fc_default, + arg: { l: { nr: LIST_LEN(fc_list), p: fc_list }} + }; + + int fc = FlowControl[bd]; + e1000_validate_option(&fc, &opt); + adapter->shared.fc = adapter->shared.original_fc = fc; + } + { /* Transmit Interrupt Delay */ + struct e1000_option opt = { + type: range_option, + name: "Transmit Interrupt Delay", + err: "using default of " __MODULE_STRING(DEFAULT_TIDV), + def: DEFAULT_TIDV, + arg: { r: { min: MIN_TIDV, max: MAX_TIDV }} + }; + + adapter->tx_int_delay = TxIntDelay[bd]; + e1000_validate_option(&adapter->tx_int_delay, &opt); + } + { /* Receive Interrupt Delay */ + struct e1000_option opt = { + type: range_option, + name: "Receive Interrupt Delay", + err: "using default of " __MODULE_STRING(DEFAULT_RIDV), + def: DEFAULT_RIDV, + arg: { r: { min: MIN_RIDV, max: MAX_RIDV }} + }; + + adapter->rx_int_delay = RxIntDelay[bd]; + e1000_validate_option(&adapter->rx_int_delay, &opt); + } + + switch(adapter->shared.media_type) { + case e1000_media_type_fiber: + e1000_check_fiber_options(adapter); + break; + case e1000_media_type_copper: + e1000_check_copper_options(adapter); + break; + default: + BUG(); + } +} + +/** + * e1000_check_fiber_options - Range Checking for Link Options, Fiber Version + * @adapter: board private structure + * + * Handles speed and duplex options on fiber adapters + **/ + +static void __devinit +e1000_check_fiber_options(struct e1000_adapter *adapter) +{ + int bd = adapter->bd_number; + bd = bd > E1000_MAX_NIC ? E1000_MAX_NIC : bd; + + if((Speed[bd] != OPTION_UNSET)) { + printk(KERN_INFO "Speed not valid for fiber adapters, " + "parameter ignored\n"); + } + if((Duplex[bd] != OPTION_UNSET)) { + printk(KERN_INFO "Duplex not valid for fiber adapters, " + "parameter ignored\n"); + } + if((AutoNeg[bd] != OPTION_UNSET)) { + printk(KERN_INFO "AutoNeg not valid for fiber adapters, " + "parameter ignored\n"); + } +} + +/** + * e1000_check_copper_options - Range Checking for Link Options, Copper Version + * @adapter: board private structure + * + * Handles speed and duplex options on copper adapters + **/ + +static void __devinit +e1000_check_copper_options(struct e1000_adapter *adapter) +{ + int speed, dplx; + int bd = adapter->bd_number; + bd = bd > E1000_MAX_NIC ? E1000_MAX_NIC : bd; + + { /* Speed */ + struct e1000_opt_list speed_list[] = {{ 0, "" }, + { SPEED_10, "" }, + { SPEED_100, "" }, + { SPEED_1000, "" }}; + struct e1000_option opt = { + type: list_option, + name: "Speed", + err: "parameter ignored", + def: 0, + arg: { l: { nr: LIST_LEN(speed_list), p: speed_list }} + }; + + speed = Speed[bd]; + e1000_validate_option(&speed, &opt); + } + { /* Duplex */ + struct e1000_opt_list dplx_list[] = {{ 0, "" }, + { HALF_DUPLEX, "" }, + { FULL_DUPLEX, "" }}; + struct e1000_option opt = { + type: list_option, + name: "Duplex", + err: "parameter ignored", + def: 0, + arg: { l: { nr: LIST_LEN(dplx_list), p: dplx_list }} + }; + + dplx = Duplex[bd]; + e1000_validate_option(&dplx, &opt); + } + + if(AutoNeg[bd] != OPTION_UNSET && (speed != 0 || dplx != 0)) { + printk(KERN_INFO + "AutoNeg specified along with Speed or Duplex, " + "parameter ignored\n"); + adapter->shared.autoneg_advertised = AUTONEG_ADV_DEFAULT; + } else { /* Autoneg */ + struct e1000_opt_list an_list[] = + #define AA "Autoneg advertising " + {{ 0x01, AA "10/HD" }, + { 0x02, AA "10/FD" }, + { 0x03, AA "10/FD, 10/HD" }, + { 0x04, AA "100/HD" }, + { 0x05, AA "100/HD, 10/HD" }, + { 0x06, AA "100/HD, 10/FD" }, + { 0x07, AA "100/HD, 10/FD, 10/HD" }, + { 0x08, AA "100/FD" }, + { 0x09, AA "100/FD, 10/HD" }, + { 0x0a, AA "100/FD, 10/FD" }, + { 0x0b, AA "100/FD, 10/FD, 10/HD" }, + { 0x0c, AA "100/FD, 100/HD" }, + { 0x0d, AA "100/FD, 100/HD, 10/HD" }, + { 0x0e, AA "100/FD, 100/HD, 10/FD" }, + { 0x0f, AA "100/FD, 100/HD, 10/FD, 10/HD" }, + { 0x20, AA "1000/FD" }, + { 0x21, AA "1000/FD, 10/HD" }, + { 0x22, AA "1000/FD, 10/FD" }, + { 0x23, AA "1000/FD, 10/FD, 10/HD" }, + { 0x24, AA "1000/FD, 100/HD" }, + { 0x25, AA "1000/FD, 100/HD, 10/HD" }, + { 0x26, AA "1000/FD, 100/HD, 10/FD" }, + { 0x27, AA "1000/FD, 100/HD, 10/FD, 10/HD" }, + { 0x28, AA "1000/FD, 100/FD" }, + { 0x29, AA "1000/FD, 100/FD, 10/HD" }, + { 0x2a, AA "1000/FD, 100/FD, 10/FD" }, + { 0x2b, AA "1000/FD, 100/FD, 10/FD, 10/HD" }, + { 0x2c, AA "1000/FD, 100/FD, 100/HD" }, + { 0x2d, AA "1000/FD, 100/FD, 100/HD, 10/HD" }, + { 0x2e, AA "1000/FD, 100/FD, 100/HD, 10/FD" }, + { 0x2f, AA "1000/FD, 100/FD, 100/HD, 10/FD, 10/HD" }}; + + struct e1000_option opt = { + type: list_option, + name: "Autoneg", + err: "parameter ignored", + def: AUTONEG_ADV_DEFAULT, + arg: { l: { nr: LIST_LEN(an_list), p: an_list }} + }; + + int an = AutoNeg[bd]; + e1000_validate_option(&an, &opt); + adapter->shared.autoneg_advertised = an; + } + + switch (speed + dplx) { + case 0: + adapter->shared.autoneg = 1; + if(Speed[bd] != OPTION_UNSET || Duplex[bd] != OPTION_UNSET) + printk(KERN_INFO + "Speed and duplex autonegotiation enabled\n"); + break; + case HALF_DUPLEX: + printk(KERN_INFO "Half Duplex specified without Speed\n"); + printk(KERN_INFO "Using Autonegotiation at Half Duplex only\n"); + adapter->shared.autoneg = 1; + adapter->shared.autoneg_advertised = ADVERTISE_10_HALF | + ADVERTISE_100_HALF; + break; + case FULL_DUPLEX: + printk(KERN_INFO "Full Duplex specified without Speed\n"); + printk(KERN_INFO "Using Autonegotiation at Full Duplex only\n"); + adapter->shared.autoneg = 1; + adapter->shared.autoneg_advertised = ADVERTISE_10_FULL | + ADVERTISE_100_FULL | + ADVERTISE_1000_FULL; + break; + case SPEED_10: + printk(KERN_INFO "10 Mbps Speed specified without Duplex\n"); + printk(KERN_INFO "Using Autonegotiation at 10 Mbps only\n"); + adapter->shared.autoneg = 1; + adapter->shared.autoneg_advertised = ADVERTISE_10_HALF | + ADVERTISE_10_FULL; + break; + case SPEED_10 + HALF_DUPLEX: + printk(KERN_INFO "Forcing to 10 Mbps Half Duplex\n"); + adapter->shared.autoneg = 0; + adapter->shared.forced_speed_duplex = e1000_10_half; + adapter->shared.autoneg_advertised = 0; + break; + case SPEED_10 + FULL_DUPLEX: + printk(KERN_INFO "Forcing to 10 Mbps Full Duplex\n"); + adapter->shared.autoneg = 0; + adapter->shared.forced_speed_duplex = e1000_10_full; + adapter->shared.autoneg_advertised = 0; + break; + case SPEED_100: + printk(KERN_INFO "100 Mbps Speed specified without Duplex\n"); + printk(KERN_INFO "Using Autonegotiation at 100 Mbps only\n"); + adapter->shared.autoneg = 1; + adapter->shared.autoneg_advertised = ADVERTISE_100_HALF | + ADVERTISE_100_FULL; + break; + case SPEED_100 + HALF_DUPLEX: + printk(KERN_INFO "Forcing to 100 Mbps Half Duplex\n"); + adapter->shared.autoneg = 0; + adapter->shared.forced_speed_duplex = e1000_100_half; + adapter->shared.autoneg_advertised = 0; + break; + case SPEED_100 + FULL_DUPLEX: + printk(KERN_INFO "Forcing to 100 Mbps Full Duplex\n"); + adapter->shared.autoneg = 0; + adapter->shared.forced_speed_duplex = e1000_100_full; + adapter->shared.autoneg_advertised = 0; + break; + case SPEED_1000: + printk(KERN_INFO "1000 Mbps Speed specified without Duplex\n"); + printk(KERN_INFO + "Using Autonegotiation at 1000 Mbps Full Duplex only\n"); + adapter->shared.autoneg = 1; + adapter->shared.autoneg_advertised = ADVERTISE_1000_FULL; + break; + case SPEED_1000 + HALF_DUPLEX: + printk(KERN_INFO "Half Duplex is not supported at 1000 Mbps\n"); + printk(KERN_INFO + "Using Autonegotiation at 1000 Mbps Full Duplex only\n"); + adapter->shared.autoneg = 1; + adapter->shared.autoneg_advertised = ADVERTISE_1000_FULL; + break; + case SPEED_1000 + FULL_DUPLEX: + printk(KERN_INFO + "Using Autonegotiation at 1000 Mbps Full Duplex only\n"); + adapter->shared.autoneg = 1; + adapter->shared.autoneg_advertised = ADVERTISE_1000_FULL; + break; + default: + BUG(); + } + + /* a few other copper only options */ + + { /* MDI/MDI-X */ + struct e1000_option opt = { + type: range_option, + name: "MDI/MDI-X", + err: "using default of " __MODULE_STRING(DEFAULT_MDIX), + def: DEFAULT_MDIX, + arg: { r: { min: MIN_MDIX, max: MAX_MDIX }} + }; + + int mdix = MdiX[bd]; + e1000_validate_option(&mdix, &opt); + adapter->shared.mdix = mdix; + } + { /* Automatic Correction for Reverse Cable Polarity */ + /* option is actually to disable polarity correction, + * so setting to OPTION_ENABLED turns the hardware feature off */ + struct e1000_option opt = { + type: enable_option, + name: "Disable Polarity Correction", + err: "defaulting to Disabled", + def: OPTION_DISABLED, + }; + + int dpc = DisablePolarityCorrection[bd]; + e1000_validate_option(&dpc, &opt); + adapter->shared.disable_polarity_correction = dpc; + } + + /* Speed, AutoNeg and MDI/MDI-X must all play nice */ + if (!e1000_validate_mdi_setting(&(adapter->shared))) { + printk(KERN_INFO "Speed, AutoNeg and MDI-X specifications are " + "incompatible. Setting MDI-X to a compatible value.\n"); + } +} + diff -Nru a/drivers/net/e1000/e1000_phy.c b/drivers/net/e1000/e1000_phy.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e1000/e1000_phy.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,1485 @@ +/******************************************************************************* + + This software program is available to you under a choice of one of two + licenses. You may choose to be licensed under either the GNU General Public + License (GPL) Version 2, June 1991, available at + http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the + text of which follows: + + Recipient has requested a license and Intel Corporation ("Intel") is willing + to grant a license for the software entitled Linux Base Driver for the + Intel(R) PRO/1000 Family of Adapters (e1000) (the "Software") being provided + by Intel Corporation. The following definitions apply to this license: + + "Licensed Patents" means patent claims licensable by Intel Corporation which + are necessarily infringed by the use of sale of the Software alone or when + combined with the operating system referred to below. + + "Recipient" means the party to whom Intel delivers this Software. + + "Licensee" means Recipient and those third parties that receive a license to + any operating system available under the GNU Public License version 2.0 or + later. + + Copyright (c) 1999 - 2002 Intel Corporation. + All rights reserved. + + The license is provided to Recipient and Recipient's Licensees under the + following terms. + + Redistribution and use in source and binary forms of the Software, with or + without modification, are permitted provided that the following conditions + are met: + + Redistributions of source code of the Software may retain the above + copyright notice, this list of conditions and the following disclaimer. + + Redistributions in binary form of the Software may reproduce the above + copyright notice, this list of conditions and the following disclaimer in + the documentation and/or materials provided with the distribution. + + Neither the name of Intel Corporation nor the names of its contributors + shall be used to endorse or promote products derived from this Software + without specific prior written permission. + + Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, + royalty-free patent license under Licensed Patents to make, use, sell, offer + to sell, import and otherwise transfer the Software, if any, in source code + and object code form. This license shall include changes to the Software + that are error corrections or other minor changes to the Software that do + not add functionality or features when the Software is incorporated in any + version of an operating system that has been distributed under the GNU + General Public License 2.0 or later. This patent license shall apply to the + combination of the Software and any operating system licensed under the GNU + Public License version 2.0 or later if, at the time Intel provides the + Software to Recipient, such addition of the Software to the then publicly + available versions of such operating systems available under the GNU Public + License version 2.0 or later (whether in gold, beta or alpha form) causes + such combination to be covered by the Licensed Patents. The patent license + shall not apply to any other combinations which include the Software. NO + hardware per se is licensed hereunder. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. + +*******************************************************************************/ + +/* e1000_phy.c + * Shared functions for accessing and configuring the PHY + */ + +#include "e1000_mac.h" +#include "e1000_phy.h" + +/****************************************************************************** +* Raises the Management Data Clock +* +* shared - Struct containing variables accessed by shared code +* ctrl_reg - Device control register's current value +******************************************************************************/ +static void +e1000_raise_mdc(struct e1000_shared_adapter *shared, + uint32_t *ctrl_reg) +{ + /* Raise the clock input to the Management Data Clock (by setting + * the MDC bit), and then delay 2 microseconds. + */ + E1000_WRITE_REG(shared, CTRL, (*ctrl_reg | E1000_CTRL_MDC)); + usec_delay(2); + return; +} + +/****************************************************************************** +* Lowers the Management Data Clock +* +* shared - Struct containing variables accessed by shared code +* ctrl_reg - Device control register's current value +******************************************************************************/ +static void +e1000_lower_mdc(struct e1000_shared_adapter *shared, + uint32_t *ctrl_reg) +{ + /* Lower the clock input to the Management Data Clock (by clearing + * the MDC bit), and then delay 2 microseconds. + */ + E1000_WRITE_REG(shared, CTRL, (*ctrl_reg & ~E1000_CTRL_MDC)); + usec_delay(2); + return; +} + +/****************************************************************************** +* Shifts data bits out to the PHY +* +* shared - Struct containing variables accessed by shared code +* data - Data to send out to the PHY +* count - Number of bits to shift out +* +* Bits are shifted out in MSB to LSB order. +******************************************************************************/ +static void +e1000_phy_shift_out(struct e1000_shared_adapter *shared, + uint32_t data, + uint16_t count) +{ + uint32_t ctrl_reg; + uint32_t mask; + + ASSERT(count <= 32); + + /* We need to shift "count" number of bits out to the PHY. So, the + * value in the "Data" parameter will be shifted out to the PHY + * one bit at a time. In order to do this, "Data" must be broken + * down into bits, which is what the "while" logic does below. + */ + mask = 0x01; + mask <<= (count - 1); + + ctrl_reg = E1000_READ_REG(shared, CTRL); + + /* Set MDIO_DIR (SWDPIO1) and MDC_DIR (SWDPIO2) direction bits to + * be used as output pins. + */ + ctrl_reg |= (E1000_CTRL_MDIO_DIR | E1000_CTRL_MDC_DIR); + + while(mask) { + /* A "1" is shifted out to the PHY by setting the MDIO bit to + * "1" and then raising and lowering the Management Data Clock + * (MDC). A "0" is shifted out to the PHY by setting the MDIO + * bit to "0" and then raising and lowering the clock. + */ + if(data & mask) + ctrl_reg |= E1000_CTRL_MDIO; + else + ctrl_reg &= ~E1000_CTRL_MDIO; + + E1000_WRITE_REG(shared, CTRL, ctrl_reg); + + usec_delay(2); + + e1000_raise_mdc(shared, &ctrl_reg); + e1000_lower_mdc(shared, &ctrl_reg); + + mask = mask >> 1; + } + + /* Clear the data bit just before leaving this routine. */ + ctrl_reg &= ~E1000_CTRL_MDIO; + return; +} + +/****************************************************************************** +* Shifts data bits in from the PHY +* +* shared - Struct containing variables accessed by shared code +* +* Bits are shifted in in MSB to LSB order. +******************************************************************************/ +static uint16_t +e1000_phy_shift_in(struct e1000_shared_adapter *shared) +{ + uint32_t ctrl_reg; + uint16_t data = 0; + uint8_t i; + + /* In order to read a register from the PHY, we need to shift in a + * total of 18 bits from the PHY. The first two bit (TurnAround) + * times are used to avoid contention on the MDIO pin when a read + * operation is performed. These two bits are ignored by us and + * thrown away. Bits are "shifted in" by raising the clock input + * to the Management Data Clock (setting the MDC bit), and then + * reading the value of the MDIO bit. + */ + ctrl_reg = E1000_READ_REG(shared, CTRL); + + /* Clear MDIO_DIR (SWDPIO1) to indicate this bit is to be used as + * input. + */ + ctrl_reg &= ~E1000_CTRL_MDIO_DIR; + ctrl_reg &= ~E1000_CTRL_MDIO; + + E1000_WRITE_REG(shared, CTRL, ctrl_reg); + + /* Raise and Lower the clock before reading in the data. This + * accounts for the TurnAround bits. The first clock occurred + * when we clocked out the last bit of the Register Address. + */ + e1000_raise_mdc(shared, &ctrl_reg); + e1000_lower_mdc(shared, &ctrl_reg); + + for(data = 0, i = 0; i < 16; i++) { + data = data << 1; + e1000_raise_mdc(shared, &ctrl_reg); + + ctrl_reg = E1000_READ_REG(shared, CTRL); + + /* Check to see if we shifted in a "1". */ + if(ctrl_reg & E1000_CTRL_MDIO) + data |= 1; + + e1000_lower_mdc(shared, &ctrl_reg); + } + + e1000_raise_mdc(shared, &ctrl_reg); + e1000_lower_mdc(shared, &ctrl_reg); + + /* Clear the MDIO bit just before leaving this routine. */ + ctrl_reg &= ~E1000_CTRL_MDIO; + + return (data); +} + +/****************************************************************************** +* Force PHY speed and duplex settings to shared->forced_speed_duplex +* +* shared - Struct containing variables accessed by shared code +******************************************************************************/ +static void +e1000_phy_force_speed_duplex(struct e1000_shared_adapter *shared) +{ + uint32_t tctl_reg; + uint32_t ctrl_reg; + uint32_t shift; + uint16_t mii_ctrl_reg; + uint16_t mii_status_reg; + uint16_t phy_data; + uint16_t i; + + DEBUGFUNC("e1000_phy_force_speed_duplex"); + + /* Turn off Flow control if we are forcing speed and duplex. */ + shared->fc = e1000_fc_none; + + DEBUGOUT1("shared->fc = %d\n", shared->fc); + + /* Read the Device Control Register. */ + ctrl_reg = E1000_READ_REG(shared, CTRL); + + /* Set the bits to Force Speed and Duplex in the Device Ctrl Reg. */ + ctrl_reg |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + ctrl_reg &= ~(DEVICE_SPEED_MASK); + + /* Clear the Auto Speed Detect Enable bit. */ + ctrl_reg &= ~E1000_CTRL_ASDE; + + /* Read the MII Control Register. */ + mii_ctrl_reg = e1000_read_phy_reg(shared, PHY_CTRL); + + /* We need to disable autoneg in order to force link and duplex. */ + + mii_ctrl_reg &= ~MII_CR_AUTO_NEG_EN; + + /* Are we forcing Full or Half Duplex? */ + if(shared->forced_speed_duplex == e1000_100_full || + shared->forced_speed_duplex == e1000_10_full) { + + /* We want to force full duplex so we SET the full duplex bits + * in the Device and MII Control Registers. + */ + ctrl_reg |= E1000_CTRL_FD; + mii_ctrl_reg |= MII_CR_FULL_DUPLEX; + + DEBUGOUT("Full Duplex\n"); + } else { + + /* We want to force half duplex so we CLEAR the full duplex + * bits in the Device and MII Control Registers. + */ + ctrl_reg &= ~E1000_CTRL_FD; + mii_ctrl_reg &= ~MII_CR_FULL_DUPLEX; /* Do this implies HALF */ + + DEBUGOUT("Half Duplex\n"); + } + + /* Are we forcing 100Mbps??? */ + if(shared->forced_speed_duplex == e1000_100_full || + shared->forced_speed_duplex == e1000_100_half) { + + /* Set the 100Mb bit and turn off the 1000Mb and 10Mb bits. */ + ctrl_reg |= E1000_CTRL_SPD_100; + mii_ctrl_reg |= MII_CR_SPEED_100; + mii_ctrl_reg &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_10); + + DEBUGOUT("Forcing 100mb "); + } else { /* Force 10MB Full or Half */ + + /* Set the 10Mb bit and turn off the 1000Mb and 100Mb bits. */ + ctrl_reg &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100); + mii_ctrl_reg |= MII_CR_SPEED_10; + mii_ctrl_reg &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100); + + DEBUGOUT("Forcing 10mb "); + } + + /* Now we need to configure the Collision Distance. We need to read + * the Transmit Control Register to do this. + * Note: This must be done for both Half or Full Duplex. + */ + tctl_reg = E1000_READ_REG(shared, TCTL); + DEBUGOUT1("tctl_reg = %x\n", tctl_reg); + + if(!(mii_ctrl_reg & MII_CR_FULL_DUPLEX)) { + + /* We are in Half Duplex mode so we need to set up our collision + * distance for 10/100. + */ + tctl_reg &= ~E1000_TCTL_COLD; + shift = E1000_HDX_COLLISION_DISTANCE; + shift <<= E1000_COLD_SHIFT; + tctl_reg |= shift; + } else { + /* We are in Full Duplex mode. We have the same collision + * distance regardless of speed. + */ + tctl_reg &= ~E1000_TCTL_COLD; + shift = E1000_FDX_COLLISION_DISTANCE; + shift <<= E1000_COLD_SHIFT; + tctl_reg |= shift; + } + + /* Write the configured values back to the Transmit Control Reg. */ + E1000_WRITE_REG(shared, TCTL, tctl_reg); + + /* Write the configured values back to the Device Control Reg. */ + E1000_WRITE_REG(shared, CTRL, ctrl_reg); + + /* Write the MII Control Register with the new PHY configuration. */ + phy_data = e1000_read_phy_reg(shared, M88E1000_PHY_SPEC_CTRL); + + /* Clear Auto-Crossover to force MDI manually. + * M88E1000 requires MDI forced whenever speed/duplex is forced + */ + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + + e1000_write_phy_reg(shared, M88E1000_PHY_SPEC_CTRL, phy_data); + + DEBUGOUT1("M88E1000 PSCR: %x \n", phy_data); + + /* Need to reset the PHY or these bits will get ignored. */ + mii_ctrl_reg |= MII_CR_RESET; + + e1000_write_phy_reg(shared, PHY_CTRL, mii_ctrl_reg); + + /* The wait_autoneg_complete flag may be a little misleading here. + * Since we are forcing speed and duplex, Auto-Neg is not enabled. + * But we do want to delay for a period while forcing only so we + * don't generate false No Link messages. So we will wait here + * only if the user has set wait_autoneg_complete to 1, which is + * the default. + */ + if(shared->wait_autoneg_complete) { + /* We will wait for autoneg to complete. */ + DEBUGOUT("Waiting for forced speed/duplex link.\n"); + mii_status_reg = 0; + + /* We will wait for autoneg to complete or 4.5 seconds to expire. */ + for(i = PHY_FORCE_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Auto-Neg + * Complete bit to be set. + */ + mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + + if(mii_status_reg & MII_SR_LINK_STATUS) + break; + + msec_delay(100); + } /* end for loop */ + + if(i == 0) { /* We didn't get link */ + + /* Reset the DSP and wait again for link. */ + e1000_phy_reset_dsp(shared); + } + + /* This loop will early-out if the link condition has been met. */ + for(i = PHY_FORCE_TIME; i > 0; i--) { + if(mii_status_reg & MII_SR_LINK_STATUS) + break; + + msec_delay(100); + /* Read the MII Status Register and wait for Auto-Neg + * Complete bit to be set. + */ + mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + + } /* end for loop */ + } /* end if wait_autoneg_complete */ + /* + * Because we reset the PHY above, we need to re-force TX_CLK in the + * Extended PHY Specific Control Register to 25MHz clock. This + * value defaults back to a 2.5MHz clock when the PHY is reset. + */ + phy_data = e1000_read_phy_reg(shared, M88E1000_EXT_PHY_SPEC_CTRL); + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + + e1000_write_phy_reg(shared, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + + /* In addition, because of the s/w reset above, we need to enable + * CRS on TX. This must be set for both full and half duplex + * operation. + */ + phy_data = e1000_read_phy_reg(shared, M88E1000_PHY_SPEC_CTRL); + + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + + e1000_write_phy_reg(shared, M88E1000_PHY_SPEC_CTRL, phy_data); + DEBUGOUT1("M88E1000 Phy Specific Ctrl Reg = %4x\r\n", phy_data); + + return; +} + +/***************************************************************************** +* Reads the value from a PHY register +* +* shared - Struct containing variables accessed by shared code +* reg_addr - address of the PHY register to read +******************************************************************************/ +uint16_t +e1000_read_phy_reg(struct e1000_shared_adapter *shared, + uint32_t reg_addr) +{ + uint32_t i; + uint32_t data = 0; + uint32_t command = 0; + + ASSERT(reg_addr <= MAX_PHY_REG_ADDRESS); + + if(shared->mac_type > e1000_82543) { + /* Set up Op-code, Phy Address, and + * register address in the MDI Control register. The MAC will + * take care of interfacing with the PHY to retrieve the + * desired data. + */ + command = ((reg_addr << E1000_MDIC_REG_SHIFT) | + (shared->phy_addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_READ)); + + E1000_WRITE_REG(shared, MDIC, command); + + /* Check every 10 usec to see if the read completed. The read + * may take as long as 64 usecs (we'll wait 100 usecs max) + * from the CPU Write to the Ready bit assertion. + */ + for(i = 0; i < 64; i++) { + usec_delay(10); + + data = E1000_READ_REG(shared, MDIC); + + if(data & E1000_MDIC_READY) + break; + } + } else { + /* We must first send a preamble through the MDIO pin to signal the + * beginning of an MII instruction. This is done by sending 32 + * consecutive "1" bits. + */ + e1000_phy_shift_out(shared, PHY_PREAMBLE, PHY_PREAMBLE_SIZE); + + /* Now combine the next few fields that are required for a read + * operation. We use this method instead of calling the + * e1000_phy_shift_out routine five different times. The format of + * a MII read instruction consists of a shift out of 14 bits and is + * defined as follows: + * + * followed by a shift in of 18 bits. This first two bits shifted + * in are TurnAround bits used to avoid contention on the MDIO pin + * when a READ operation is performed. These two bits are thrown + * away followed by a shift in of 16 bits which contains the + * desired data. + */ + command = ((reg_addr) | + (shared->phy_addr << 5) | + (PHY_OP_READ << 10) | (PHY_SOF << 12)); + + e1000_phy_shift_out(shared, command, 14); + + /* Now that we've shifted out the read command to the MII, we need + * to "shift in" the 16-bit value (18 total bits) of the requested + * PHY register address. + */ + data = (uint32_t) e1000_phy_shift_in(shared); + } + + ASSERT(!(data & E1000_MDIC_ERROR)); + + return ((uint16_t) data); +} + +/****************************************************************************** +* Writes a value to a PHY register +* +* shared - Struct containing variables accessed by shared code +* reg_addr - address of the PHY register to write +* data - data to write to the PHY +******************************************************************************/ +void +e1000_write_phy_reg(struct e1000_shared_adapter *shared, + uint32_t reg_addr, + uint16_t data) +{ + uint32_t i; + uint32_t command = 0; + uint32_t mdic_reg; + + ASSERT(reg_addr <= MAX_PHY_REG_ADDRESS); + + if(shared->mac_type > e1000_82543) { + /* Set up Op-code, Phy Address, register + * address, and data intended for the PHY register in the MDI + * Control register. The MAC will take care of interfacing + * with the PHY to send the desired data. + */ + command = (((uint32_t) data) | + (reg_addr << E1000_MDIC_REG_SHIFT) | + (shared->phy_addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_WRITE)); + + E1000_WRITE_REG(shared, MDIC, command); + + /* Check every 10 usec to see if the read completed. The read + * may take as long as 64 usecs (we'll wait 100 usecs max) + * from the CPU Write to the Ready bit assertion. + */ + for(i = 0; i < 10; i++) { + usec_delay(10); + + mdic_reg = E1000_READ_REG(shared, MDIC); + + if(mdic_reg & E1000_MDIC_READY) + break; + } + } else { + /* We'll need to use the SW defined pins to shift the write command + * out to the PHY. We first send a preamble to the PHY to signal the + * beginning of the MII instruction. This is done by sending 32 + * consecutive "1" bits. + */ + e1000_phy_shift_out(shared, PHY_PREAMBLE, PHY_PREAMBLE_SIZE); + + /* Now combine the remaining required fields that will indicate + * a write operation. We use this method instead of calling the + * e1000_phy_shift_out routine for each field in the command. The + * format of a MII write instruction is as follows: + * . + */ + command = ((PHY_TURNAROUND) | + (reg_addr << 2) | + (shared->phy_addr << 7) | + (PHY_OP_WRITE << 12) | (PHY_SOF << 14)); + command <<= 16; + command |= ((uint32_t) data); + + e1000_phy_shift_out(shared, command, 32); + } + return; +} + +/****************************************************************************** +* Returns the PHY to the power-on reset state +* +* shared - Struct containing variables accessed by shared code +******************************************************************************/ +void +e1000_phy_hw_reset(struct e1000_shared_adapter *shared) +{ + uint32_t ctrl_reg; + uint32_t ctrl_ext_reg; + + DEBUGFUNC("e1000_phy_hw_reset"); + + DEBUGOUT("Resetting Phy...\n"); + + if(shared->mac_type > e1000_82543) { + /* Read the device control register and assert the + * E1000_CTRL_PHY_RST bit. Hold for 20ms and then take it out + * of reset. + */ + ctrl_reg = E1000_READ_REG(shared, CTRL); + + ctrl_reg |= E1000_CTRL_PHY_RST; + + E1000_WRITE_REG(shared, CTRL, ctrl_reg); + + msec_delay(20); + + ctrl_reg &= ~E1000_CTRL_PHY_RST; + + E1000_WRITE_REG(shared, CTRL, ctrl_reg); + + msec_delay(20); + } else { + /* Read the Extended Device Control Register, assert the + * PHY_RESET_DIR bit. Then clock it out to the PHY. + */ + ctrl_ext_reg = E1000_READ_REG(shared, CTRL_EXT); + + ctrl_ext_reg |= E1000_CTRL_PHY_RESET_DIR4; + + E1000_WRITE_REG(shared, CTRL_EXT, ctrl_ext_reg); + + msec_delay(20); + + /* Set the reset bit in the device control register and clock + * it out to the PHY. + */ + ctrl_ext_reg = E1000_READ_REG(shared, CTRL_EXT); + + ctrl_ext_reg &= ~E1000_CTRL_PHY_RESET4; + + E1000_WRITE_REG(shared, CTRL_EXT, ctrl_ext_reg); + + msec_delay(20); + + ctrl_ext_reg = E1000_READ_REG(shared, CTRL_EXT); + + ctrl_ext_reg |= E1000_CTRL_PHY_RESET4; + + E1000_WRITE_REG(shared, CTRL_EXT, ctrl_ext_reg); + + msec_delay(20); + } + return; +} + +/****************************************************************************** +* Resets the PHY +* +* shared - Struct containing variables accessed by shared code +* +* Sets bit 15 of the MII Control regiser +******************************************************************************/ +boolean_t +e1000_phy_reset(struct e1000_shared_adapter *shared) +{ + uint16_t reg_data; + uint16_t i; + + DEBUGFUNC("e1000_phy_reset"); + + /* Read the MII control register, set the reset bit and write the + * value back by clocking it out to the PHY. + */ + reg_data = e1000_read_phy_reg(shared, PHY_CTRL); + + reg_data |= MII_CR_RESET; + + e1000_write_phy_reg(shared, PHY_CTRL, reg_data); + + /* Wait for bit 15 of the MII Control Register to be cleared + * indicating the PHY has been reset. + */ + i = 0; + while((reg_data & MII_CR_RESET) && i++ < 500) { + reg_data = e1000_read_phy_reg(shared, PHY_CTRL); + usec_delay(1); + } + + if(i >= 500) { + DEBUGOUT("Timeout waiting for PHY to reset.\n"); + return FALSE; + } + return TRUE; +} + +/****************************************************************************** +* Detects which PHY is present and the speed and duplex +* +* shared - Struct containing variables accessed by shared code +* ctrl_reg - current value of the device control register +******************************************************************************/ +boolean_t +e1000_phy_setup(struct e1000_shared_adapter *shared, + uint32_t ctrl_reg) +{ + uint16_t mii_ctrl_reg; + uint16_t mii_status_reg; + uint16_t phy_specific_ctrl_reg; + uint16_t mii_autoneg_adv_reg; + uint16_t mii_1000t_ctrl_reg; + uint16_t i; + uint16_t data; + uint16_t autoneg_hw_setting; + uint16_t autoneg_fc_setting; + boolean_t restart_autoneg = FALSE; + boolean_t force_autoneg_restart = FALSE; + + DEBUGFUNC("e1000_phy_setup"); + + /* We want to enable the Auto-Speed Detection bit in the Device + * Control Register. When set to 1, the MAC automatically detects + * the resolved speed of the link and self-configures appropriately. + * The Set Link Up bit must also be set for this behavior work + * properly. + */ + /* Nothing but 82543 and newer */ + ASSERT(shared->mac_type >= e1000_82543); + + /* With 82543, we need to force speed/duplex + * on the MAC equal to what the PHY speed/duplex configuration is. + * In addition, on 82543, we need to perform a hardware reset + * on the PHY to take it out of reset. + */ + if(shared->mac_type >= e1000_82544) { + ctrl_reg |= E1000_CTRL_SLU; + E1000_WRITE_REG(shared, CTRL, ctrl_reg); + } else { + ctrl_reg |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX | E1000_CTRL_SLU); + E1000_WRITE_REG(shared, CTRL, ctrl_reg); + + if(shared->mac_type == e1000_82543) + e1000_phy_hw_reset(shared); + } + + if(!e1000_detect_gig_phy(shared)) { + /* No PHY detected, return FALSE */ + DEBUGOUT("PhySetup failure, did not detect valid phy.\n"); + return (FALSE); + } + + DEBUGOUT1("Phy ID = %x \n", shared->phy_id); + + /* Read the MII Control Register. */ + mii_ctrl_reg = e1000_read_phy_reg(shared, PHY_CTRL); + + DEBUGOUT1("MII Ctrl Reg contents = %x\n", mii_ctrl_reg); + + /* Check to see if the Auto Neg Enable bit is set in the MII Control + * Register. If not, we could be in a situation where a driver was + * loaded previously and was forcing speed and duplex. Then the + * driver was unloaded but a e1000_phy_hw_reset was not performed, so + * link was still being forced and link was still achieved. Then + * the driver was reloaded with the intention to auto-negotiate, but + * since link is already established we end up not restarting + * auto-neg. So if the auto-neg bit is not enabled and the driver + * is being loaded with the desire to auto-neg, we set this flag to + * to ensure the restart of the auto-neg engine later in the logic. + */ + if(!(mii_ctrl_reg & MII_CR_AUTO_NEG_EN)) + force_autoneg_restart = TRUE; + + /* Clear the isolate bit for normal operation and write it back to + * the MII Control Reg. Although the spec says this doesn't need + * to be done when the PHY address is not equal to zero, we do it + * anyway just to be safe. + */ + mii_ctrl_reg &= ~(MII_CR_ISOLATE); + + e1000_write_phy_reg(shared, PHY_CTRL, mii_ctrl_reg); + + data = e1000_read_phy_reg(shared, M88E1000_PHY_SPEC_CTRL); + + /* Enable CRS on TX. This must be set for half-duplex operation. */ + data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + + DEBUGOUT1("M88E1000 PSCR: %x \n", data); + + e1000_write_phy_reg(shared, M88E1000_PHY_SPEC_CTRL, data); + + data = e1000_read_phy_reg(shared, M88E1000_EXT_PHY_SPEC_CTRL); + + /* Force TX_CLK in the Extended PHY Specific Control Register + * to 25MHz clock. + */ + data |= M88E1000_EPSCR_TX_CLK_25; + + e1000_write_phy_reg(shared, M88E1000_EXT_PHY_SPEC_CTRL, data); + + /* Certain PHYs will set the default of MII register 4 differently. + * We need to check this against our fc value. If it is + * different, we need to setup up register 4 correctly and restart + * autonegotiation. + */ + /* Read the MII Auto-Neg Advertisement Register (Address 4). */ + mii_autoneg_adv_reg = e1000_read_phy_reg(shared, PHY_AUTONEG_ADV); + + /* Shift right to put 10T-Half bit in bit 0 + * Isolate the four bits for 100/10 Full/Half. + */ + autoneg_hw_setting = (mii_autoneg_adv_reg >> 5) & 0xF; + + /* Get the 1000T settings. */ + mii_1000t_ctrl_reg = e1000_read_phy_reg(shared, PHY_1000T_CTRL); + + /* Isolate and OR in the 1000T settings. */ + autoneg_hw_setting |= ((mii_1000t_ctrl_reg & 0x0300) >> 4); + + /* mask all bits in the MII Auto-Neg Advertisement Register + * except for ASM_DIR and PAUSE and shift. This value + * will be used later to see if we need to restart Auto-Negotiation. + */ + autoneg_fc_setting = ((mii_autoneg_adv_reg & 0x0C00) >> 10); + + /* Perform some bounds checking on the shared->autoneg_advertised + * parameter. If this variable is zero, then set it to the default. + */ + shared->autoneg_advertised &= AUTONEG_ADVERTISE_SPEED_DEFAULT; + + /* If autoneg_advertised is zero, we assume it was not defaulted + * by the calling code so we set to advertise full capability. + */ + if(shared->autoneg_advertised == 0) + shared->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT; + + /* We could be in the situation where Auto-Neg has already completed + * and the user has not indicated any overrides. In this case we + * simply need to call e1000_get_speed_and_duplex to obtain the Auto- + * Negotiated speed and duplex, then return. + */ + if(!force_autoneg_restart && shared->autoneg && + (shared->autoneg_advertised == autoneg_hw_setting) && + (shared->fc == autoneg_fc_setting)) { + + DEBUGOUT("No overrides - Reading MII Status Reg..\n"); + + /* Read the MII Status Register. We read this twice because + * certain bits are "sticky" and need to be read twice. + */ + mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + + DEBUGOUT1("MII Status Reg contents = %x\n", mii_status_reg); + + /* Do we have link now? (if so, auto-neg has completed) */ + if(mii_status_reg & MII_SR_LINK_STATUS) { + data = e1000_read_phy_reg(shared, M88E1000_PHY_SPEC_STATUS); + DEBUGOUT1("M88E1000 Phy Specific Status Reg contents = %x\n", data); + + /* We have link, so we need to finish the config process: + * 1) Set up the MAC to the current PHY speed/duplex + * if we are on 82543. If we + * are on newer silicon, we only need to configure + * collision distance in the Transmit Control Register. + * 2) Set up flow control on the MAC to that established + * with the link partner. + */ + if(shared->mac_type >= e1000_82544) + e1000_config_collision_dist(shared); + else + e1000_config_mac_to_phy(shared, data); + + e1000_config_fc_after_link_up(shared); + + return (TRUE); + } + } + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + phy_specific_ctrl_reg = e1000_read_phy_reg(shared, M88E1000_PHY_SPEC_CTRL); + + phy_specific_ctrl_reg &= ~M88E1000_PSCR_AUTO_X_MODE; + + switch (shared->mdix) { + case 1: + phy_specific_ctrl_reg |= M88E1000_PSCR_MDI_MANUAL_MODE; + break; + case 2: + phy_specific_ctrl_reg |= M88E1000_PSCR_MDIX_MANUAL_MODE; + break; + case 3: + phy_specific_ctrl_reg |= M88E1000_PSCR_AUTO_X_1000T; + break; + case 0: + default: + phy_specific_ctrl_reg |= M88E1000_PSCR_AUTO_X_MODE; + break; + } + + e1000_write_phy_reg(shared, M88E1000_PHY_SPEC_CTRL, phy_specific_ctrl_reg); + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_specific_ctrl_reg = e1000_read_phy_reg(shared, M88E1000_PHY_SPEC_CTRL); + + phy_specific_ctrl_reg &= ~M88E1000_PSCR_POLARITY_REVERSAL; + + if(shared->disable_polarity_correction == 1) + phy_specific_ctrl_reg |= M88E1000_PSCR_POLARITY_REVERSAL; + + e1000_write_phy_reg(shared, M88E1000_PHY_SPEC_CTRL, phy_specific_ctrl_reg); + + /* Options: + * autoneg = 1 (default) + * PHY will advertise value(s) parsed from + * autoneg_advertised and fc + * autoneg = 0 + * PHY will be set to 10H, 10F, 100H, or 100F + * depending on value parsed from forced_speed_duplex. + */ + + /* Is autoneg enabled? This is enabled by default or by software override. + * If so, call e1000_phy_setup_autoneg routine to parse the + * autoneg_advertised and fc options. If autoneg is NOT enabled, then the + * user should have provided a speed/duplex override. If so, then call + * e1000_phy_force_speed_duplex to parse and set this up. Otherwise, + * we are in an error situation and need to bail. + */ + if(shared->autoneg) { + DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); + restart_autoneg = e1000_phy_setup_autoneg(shared); + } else { + DEBUGOUT("Forcing speed and duplex\n"); + e1000_phy_force_speed_duplex(shared); + } + + /* Based on information parsed above, check the flag to indicate + * whether we need to restart Auto-Neg. + */ + if(restart_autoneg) { + DEBUGOUT("Restarting Auto-Neg\n"); + + /* Read the MII Control Register. */ + mii_ctrl_reg = e1000_read_phy_reg(shared, PHY_CTRL); + + /* Restart auto-negotiation by setting the Auto Neg Enable bit and + * the Auto Neg Restart bit. + */ + mii_ctrl_reg |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); + + e1000_write_phy_reg(shared, PHY_CTRL, mii_ctrl_reg); + + /* Does the user want to wait for Auto-Neg to complete here, or + * check at a later time (for example, callback routine). + */ + if(shared->wait_autoneg_complete) + e1000_wait_autoneg(shared); + } /* end if restart_autoneg */ + + /* Read the MII Status Register. */ + mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + + DEBUGOUT1("Checking for link status - MII Status Reg contents = %x\n", + mii_status_reg); + + /* Check link status. Wait up to 100 microseconds for link to + * become valid. + */ + for(i = 0; i < 10; i++) { + if(mii_status_reg & MII_SR_LINK_STATUS) + break; + usec_delay(10); + DEBUGOUT(". "); + + mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + } + + if(mii_status_reg & MII_SR_LINK_STATUS) { + /* Yes, so configure MAC to PHY settings as well as flow control + * registers. + */ + data = e1000_read_phy_reg(shared, M88E1000_PHY_SPEC_STATUS); + + DEBUGOUT1("M88E1000 Phy Specific Status Reg contents = %x\n", data); + + /* We have link, so we need to finish the config process: + * 1) Set up the MAC to the current PHY speed/duplex + * if we are on 82543. If we + * are on newer silicon, we only need to configure + * collision distance in the Transmit Control Register. + * 2) Set up flow control on the MAC to that established with + * the link partner. + */ + if(shared->mac_type >= e1000_82544) + e1000_config_collision_dist(shared); + else + e1000_config_mac_to_phy(shared, data); + + e1000_config_fc_after_link_up(shared); + + DEBUGOUT("Valid link established!!!\n"); + } else { + DEBUGOUT("Unable to establish link!!!\n"); + } + + return (TRUE); +} + +/****************************************************************************** +* Configures PHY autoneg and flow control advertisement settings +* +* shared - Struct containing variables accessed by shared code +******************************************************************************/ +boolean_t +e1000_phy_setup_autoneg(struct e1000_shared_adapter *shared) +{ + uint16_t mii_autoneg_adv_reg; + uint16_t mii_1000t_ctrl_reg; + + DEBUGFUNC("e1000_phy_setup_autoneg"); + + /* Read the MII Auto-Neg Advertisement Register (Address 4). */ + mii_autoneg_adv_reg = e1000_read_phy_reg(shared, PHY_AUTONEG_ADV); + + /* Read the MII 1000Base-T Control Register (Address 9). */ + mii_1000t_ctrl_reg = e1000_read_phy_reg(shared, PHY_1000T_CTRL); + + /* Need to parse both autoneg_advertised and fc and set up + * the appropriate PHY registers. First we will parse for + * autoneg_advertised software override. Since we can advertise + * a plethora of combinations, we need to check each bit + * individually. + */ + + /* First we clear all the 10/100 mb speed bits in the Auto-Neg + * Advertisement Register (Address 4) and the 1000 mb speed bits in + * the 1000Base-T Control Register (Address 9). + */ + mii_autoneg_adv_reg &= ~REG4_SPEED_MASK; + mii_1000t_ctrl_reg &= ~REG9_SPEED_MASK; + + DEBUGOUT1("autoneg_advertised %x\n", shared->autoneg_advertised); + + /* Do we want to advertise 10 Mb Half Duplex? */ + if(shared->autoneg_advertised & ADVERTISE_10_HALF) { + DEBUGOUT("Advertise 10mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS; + } + + /* Do we want to advertise 10 Mb Full Duplex? */ + if(shared->autoneg_advertised & ADVERTISE_10_FULL) { + DEBUGOUT("Advertise 10mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS; + } + + /* Do we want to advertise 100 Mb Half Duplex? */ + if(shared->autoneg_advertised & ADVERTISE_100_HALF) { + DEBUGOUT("Advertise 100mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS; + } + + /* Do we want to advertise 100 Mb Full Duplex? */ + if(shared->autoneg_advertised & ADVERTISE_100_FULL) { + DEBUGOUT("Advertise 100mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS; + } + + /* We do not allow the Phy to advertise 1000 Mb Half Duplex */ + if(shared->autoneg_advertised & ADVERTISE_1000_HALF) { + DEBUGOUT("Advertise 1000mb Half duplex requested, request denied!\n"); + } + + /* Do we want to advertise 1000 Mb Full Duplex? */ + if(shared->autoneg_advertised & ADVERTISE_1000_FULL) { + DEBUGOUT("Advertise 1000mb Full duplex\n"); + mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; + } + + /* Check for a software override of the flow control settings, and + * setup the PHY advertisement registers accordingly. If + * auto-negotiation is enabled, then software will have to set the + * "PAUSE" bits to the correct value in the Auto-Negotiation + * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto-negotiation. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * but we do not support receiving pause frames). + * 3: Both Rx and TX flow control (symmetric) are enabled. + * other: No software override. The flow control configuration + * in the EEPROM is used. + */ + switch (shared->fc) { + case e1000_fc_none: /* 0 */ + /* Flow control (RX & TX) is completely disabled by a + * software over-ride. + */ + mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case e1000_fc_rx_pause: /* 1 */ + /* RX Flow control is enabled, and TX Flow control is + * disabled, by a software over-ride. + */ + + /* Since there really isn't a way to advertise that we are + * capable of RX Pause ONLY, we will advertise that we + * support both symmetric and asymmetric RX PAUSE. Later + * (in e1000_config_fc_after_link_up) we will disable the + *shared's ability to send PAUSE frames. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case e1000_fc_tx_pause: /* 2 */ + /* TX Flow control is enabled, and RX Flow control is + * disabled, by a software over-ride. + */ + mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR; + mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE; + break; + case e1000_fc_full: /* 3 */ + /* Flow control (both RX and TX) is enabled by a software + * over-ride. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + default: + /* We should never get here. The value should be 0-3. */ + DEBUGOUT("Flow control param set incorrectly\n"); + ASSERT(0); + break; + } + + /* Write the MII Auto-Neg Advertisement Register (Address 4). */ + e1000_write_phy_reg(shared, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); + + DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); + + /* Write the MII 1000Base-T Control Register (Address 9). */ + e1000_write_phy_reg(shared, PHY_1000T_CTRL, mii_1000t_ctrl_reg); + return (TRUE); +} + +/****************************************************************************** +* Sets MAC speed and duplex settings to reflect the those in the PHY +* +* shared - Struct containing variables accessed by shared code +* mii_reg - data to write to the MII control register +* +* The contents of the PHY register containing the needed information need to +* be passed in. +******************************************************************************/ +void +e1000_config_mac_to_phy(struct e1000_shared_adapter *shared, + uint16_t mii_reg) +{ + uint32_t ctrl_reg; + uint32_t tctl_reg; + uint32_t shift; + + DEBUGFUNC("e1000_config_mac_to_phy"); + + /* We need to read the Transmit Control register to configure the + * collision distance. + * Note: This must be done for both Half or Full Duplex. + */ + tctl_reg = E1000_READ_REG(shared, TCTL); + DEBUGOUT1("tctl_reg = %x\n", tctl_reg); + + /* Read the Device Control Register and set the bits to Force Speed + * and Duplex. + */ + ctrl_reg = E1000_READ_REG(shared, CTRL); + + ctrl_reg |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + ctrl_reg &= ~(DEVICE_SPEED_MASK); + + DEBUGOUT1("MII Register Data = %x\r\n", mii_reg); + + /* Clear the ILOS bit. */ + ctrl_reg &= ~E1000_CTRL_ILOS; + + /* Set up duplex in the Device Control and Transmit Control + * registers depending on negotiated values. + */ + if(mii_reg & M88E1000_PSSR_DPLX) { + ctrl_reg |= E1000_CTRL_FD; + + /* We are in Full Duplex mode. We have the same collision + * distance regardless of speed. + */ + tctl_reg &= ~E1000_TCTL_COLD; + shift = E1000_FDX_COLLISION_DISTANCE; + shift <<= E1000_COLD_SHIFT; + tctl_reg |= shift; + } else { + ctrl_reg &= ~E1000_CTRL_FD; + + /* We are in Half Duplex mode. Our Half Duplex collision + * distance is different for Gigabit than for 10/100 so we will + * set accordingly. + */ + if((mii_reg & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) { + /* 1000Mbs HDX */ + tctl_reg &= ~E1000_TCTL_COLD; + shift = E1000_GB_HDX_COLLISION_DISTANCE; + shift <<= E1000_COLD_SHIFT; + tctl_reg |= shift; + tctl_reg |= E1000_TCTL_PBE; /* Enable Packet Bursting */ + } else { + /* 10/100Mbs HDX */ + tctl_reg &= ~E1000_TCTL_COLD; + shift = E1000_HDX_COLLISION_DISTANCE; + shift <<= E1000_COLD_SHIFT; + tctl_reg |= shift; + } + } + + /* Set up speed in the Device Control register depending on + * negotiated values. + */ + if((mii_reg & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) + ctrl_reg |= E1000_CTRL_SPD_1000; + else if((mii_reg & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS) + ctrl_reg |= E1000_CTRL_SPD_100; + else + ctrl_reg &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100); + + /* Write the configured values back to the Transmit Control Reg. */ + E1000_WRITE_REG(shared, TCTL, tctl_reg); + + /* Write the configured values back to the Device Control Reg. */ + E1000_WRITE_REG(shared, CTRL, ctrl_reg); + + return; +} + +/****************************************************************************** +* Sets the collision distance in the Transmit Control register +* +* shared - Struct containing variables accessed by shared code +* +* Link should have been established previously. Reads the speed and duplex +* information from the Device Status register. +******************************************************************************/ +void +e1000_config_collision_dist(struct e1000_shared_adapter *shared) +{ + uint32_t tctl_reg; + uint16_t speed; + uint16_t duplex; + uint32_t shift; + + DEBUGFUNC("e1000_config_collision_dist"); + + /* Get our current speed and duplex from the Device Status Register. */ + e1000_get_speed_and_duplex(shared, &speed, &duplex); + + /* We need to configure the Collision Distance for both Full or + * Half Duplex. + */ + tctl_reg = E1000_READ_REG(shared, TCTL); + DEBUGOUT1("tctl_reg = %x\n", tctl_reg); + + /* mask the Collision Distance bits in the Transmit Control Reg. */ + tctl_reg &= ~E1000_TCTL_COLD; + + if(duplex == FULL_DUPLEX) { + /* We are in Full Duplex mode. Therefore, the collision distance + * is the same regardless of speed. + */ + shift = E1000_FDX_COLLISION_DISTANCE; + shift <<= E1000_COLD_SHIFT; + tctl_reg |= shift; + } else { + /* We are in Half Duplex mode. Half Duplex collision distance is + * different for Gigabit vs. 10/100, so we will set accordingly. + */ + if(speed == SPEED_1000) { /* 1000Mbs HDX */ + shift = E1000_GB_HDX_COLLISION_DISTANCE; + shift <<= E1000_COLD_SHIFT; + tctl_reg |= shift; + tctl_reg |= E1000_TCTL_PBE; /* Enable Packet Bursting */ + } else { /* 10/100Mbs HDX */ + shift = E1000_HDX_COLLISION_DISTANCE; + shift <<= E1000_COLD_SHIFT; + tctl_reg |= shift; + } + } + + /* Write the configured values back to the Transmit Control Reg. */ + E1000_WRITE_REG(shared, TCTL, tctl_reg); + + return; +} + +/****************************************************************************** +* Probes the expected PHY address for known PHY IDs +* +* shared - Struct containing variables accessed by shared code +******************************************************************************/ +boolean_t +e1000_detect_gig_phy(struct e1000_shared_adapter *shared) +{ + uint32_t phy_id_high; + uint16_t phy_id_low; + + DEBUGFUNC("e1000_detect_gig_phy"); + + /* Read the PHY ID Registers to identify which PHY is onboard. */ + shared->phy_addr = 1; + + phy_id_high = e1000_read_phy_reg(shared, PHY_ID1); + + usec_delay(2); + + phy_id_low = e1000_read_phy_reg(shared, PHY_ID2); + + shared->phy_id = (phy_id_low | (phy_id_high << 16)) & PHY_REVISION_MASK; + + if(shared->phy_id == M88E1000_12_PHY_ID || + shared->phy_id == M88E1000_14_PHY_ID || + shared->phy_id == M88E1000_I_PHY_ID) { + + DEBUGOUT2("phy_id 0x%x detected at address 0x%x\n", + shared->phy_id, shared->phy_addr); + return (TRUE); + } else { + DEBUGOUT("Could not auto-detect Phy!\n"); + return (FALSE); + } +} + +/****************************************************************************** +* Resets the PHY's DSP +* +* shared - Struct containing variables accessed by shared code +******************************************************************************/ +void +e1000_phy_reset_dsp(struct e1000_shared_adapter *shared) +{ + e1000_write_phy_reg(shared, 29, 0x1d); + e1000_write_phy_reg(shared, 30, 0xc1); + e1000_write_phy_reg(shared, 30, 0x00); + return; +} + +/****************************************************************************** +* Blocks until autoneg completes or times out (~4.5 seconds) +* +* shared - Struct containing variables accessed by shared code +******************************************************************************/ +boolean_t +e1000_wait_autoneg(struct e1000_shared_adapter *shared) +{ + uint16_t i; + uint16_t mii_status_reg; + boolean_t autoneg_complete = FALSE; + + DEBUGFUNC("e1000_wait_autoneg"); + + /* We will wait for autoneg to complete. */ + DEBUGOUT("Waiting for Auto-Neg to complete.\n"); + mii_status_reg = 0; + + /* We will wait for autoneg to complete or 4.5 seconds to expire. */ + + for(i = PHY_AUTO_NEG_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Auto-Neg + * Complete bit to be set. + */ + mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + + if(mii_status_reg & MII_SR_AUTONEG_COMPLETE) { + autoneg_complete = TRUE; + break; + } + + msec_delay(100); + } + + return (autoneg_complete); +} + +/****************************************************************************** +* Get PHY information from various PHY registers +* +* shared - Struct containing variables accessed by shared code +* phy_status_info - PHY information structure +******************************************************************************/ +boolean_t +e1000_phy_get_info(struct e1000_shared_adapter *shared, + struct e1000_phy_info *phy_status_info) +{ + uint16_t phy_mii_status_reg; + uint16_t phy_specific_ctrl_reg; + uint16_t phy_specific_status_reg; + uint16_t phy_specific_ext_ctrl_reg; + uint16_t phy_1000t_stat_reg; + + phy_status_info->cable_length = e1000_cable_length_undefined; + phy_status_info->extended_10bt_distance = + e1000_10bt_ext_dist_enable_undefined; + phy_status_info->cable_polarity = e1000_rev_polarity_undefined; + phy_status_info->polarity_correction = e1000_polarity_reversal_undefined; + phy_status_info->link_reset = e1000_down_no_idle_undefined; + phy_status_info->mdix_mode = e1000_auto_x_mode_undefined; + phy_status_info->local_rx = e1000_1000t_rx_status_undefined; + phy_status_info->remote_rx = e1000_1000t_rx_status_undefined; + + /* PHY info only valid for copper media. */ + if(shared == NULL || shared->media_type != e1000_media_type_copper) + return FALSE; + + /* PHY info only valid for LINK UP. Read MII status reg + * back-to-back to get link status. + */ + phy_mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + phy_mii_status_reg = e1000_read_phy_reg(shared, PHY_STATUS); + if((phy_mii_status_reg & MII_SR_LINK_STATUS) != MII_SR_LINK_STATUS) + return FALSE; + + /* Read various PHY registers to get the PHY info. */ + phy_specific_ctrl_reg = e1000_read_phy_reg(shared, M88E1000_PHY_SPEC_CTRL); + phy_specific_status_reg = + e1000_read_phy_reg(shared, M88E1000_PHY_SPEC_STATUS); + phy_specific_ext_ctrl_reg = + e1000_read_phy_reg(shared, M88E1000_EXT_PHY_SPEC_CTRL); + phy_1000t_stat_reg = e1000_read_phy_reg(shared, PHY_1000T_STATUS); + + phy_status_info->cable_length = + ((phy_specific_status_reg & M88E1000_PSSR_CABLE_LENGTH) >> + M88E1000_PSSR_CABLE_LENGTH_SHIFT); + + phy_status_info->extended_10bt_distance = + (phy_specific_ctrl_reg & M88E1000_PSCR_10BT_EXT_DIST_ENABLE) >> + M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT; + + phy_status_info->cable_polarity = + (phy_specific_status_reg & M88E1000_PSSR_REV_POLARITY) >> + M88E1000_PSSR_REV_POLARITY_SHIFT; + + phy_status_info->polarity_correction = + (phy_specific_ctrl_reg & M88E1000_PSCR_POLARITY_REVERSAL) >> + M88E1000_PSCR_POLARITY_REVERSAL_SHIFT; + + phy_status_info->link_reset = + (phy_specific_ext_ctrl_reg & M88E1000_EPSCR_DOWN_NO_IDLE) >> + M88E1000_EPSCR_DOWN_NO_IDLE_SHIFT; + + phy_status_info->mdix_mode = + (phy_specific_status_reg & M88E1000_PSSR_MDIX) >> + M88E1000_PSSR_MDIX_SHIFT; + + phy_status_info->local_rx = + (phy_1000t_stat_reg & SR_1000T_LOCAL_RX_STATUS) >> + SR_1000T_LOCAL_RX_STATUS_SHIFT; + + phy_status_info->remote_rx = + (phy_1000t_stat_reg & SR_1000T_REMOTE_RX_STATUS) >> + SR_1000T_REMOTE_RX_STATUS_SHIFT; + + return TRUE; +} + +boolean_t +e1000_validate_mdi_setting(struct e1000_shared_adapter *shared) +{ + if(!shared->autoneg && (shared->mdix == 0 || shared->mdix == 3)) { + shared->mdix = 1; + return FALSE; + } + return TRUE; +} diff -Nru a/drivers/net/e1000/e1000_phy.h b/drivers/net/e1000/e1000_phy.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e1000/e1000_phy.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,422 @@ +/******************************************************************************* + + This software program is available to you under a choice of one of two + licenses. You may choose to be licensed under either the GNU General Public + License (GPL) Version 2, June 1991, available at + http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the + text of which follows: + + Recipient has requested a license and Intel Corporation ("Intel") is willing + to grant a license for the software entitled Linux Base Driver for the + Intel(R) PRO/1000 Family of Adapters (e1000) (the "Software") being provided + by Intel Corporation. The following definitions apply to this license: + + "Licensed Patents" means patent claims licensable by Intel Corporation which + are necessarily infringed by the use of sale of the Software alone or when + combined with the operating system referred to below. + + "Recipient" means the party to whom Intel delivers this Software. + + "Licensee" means Recipient and those third parties that receive a license to + any operating system available under the GNU Public License version 2.0 or + later. + + Copyright (c) 1999 - 2002 Intel Corporation. + All rights reserved. + + The license is provided to Recipient and Recipient's Licensees under the + following terms. + + Redistribution and use in source and binary forms of the Software, with or + without modification, are permitted provided that the following conditions + are met: + + Redistributions of source code of the Software may retain the above + copyright notice, this list of conditions and the following disclaimer. + + Redistributions in binary form of the Software may reproduce the above + copyright notice, this list of conditions and the following disclaimer in + the documentation and/or materials provided with the distribution. + + Neither the name of Intel Corporation nor the names of its contributors + shall be used to endorse or promote products derived from this Software + without specific prior written permission. + + Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, + royalty-free patent license under Licensed Patents to make, use, sell, offer + to sell, import and otherwise transfer the Software, if any, in source code + and object code form. This license shall include changes to the Software + that are error corrections or other minor changes to the Software that do + not add functionality or features when the Software is incorporated in any + version of an operating system that has been distributed under the GNU + General Public License 2.0 or later. This patent license shall apply to the + combination of the Software and any operating system licensed under the GNU + Public License version 2.0 or later if, at the time Intel provides the + Software to Recipient, such addition of the Software to the then publicly + available versions of such operating systems available under the GNU Public + License version 2.0 or later (whether in gold, beta or alpha form) causes + such combination to be covered by the Licensed Patents. The patent license + shall not apply to any other combinations which include the Software. NO + hardware per se is licensed hereunder. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. + +*******************************************************************************/ + +/* e1000_phy.h + * Structures, enums, and macros for the PHY + */ + +#ifndef _E1000_PHY_H_ +#define _E1000_PHY_H_ + +#include "e1000_osdep.h" + +/* PHY status info structure and supporting enums */ +typedef enum { + e1000_cable_length_50 = 0, + e1000_cable_length_50_80, + e1000_cable_length_80_110, + e1000_cable_length_110_140, + e1000_cable_length_140, + e1000_cable_length_undefined = 0xFF +} e1000_cable_length; + +typedef enum { + e1000_10bt_ext_dist_enable_normal = 0, + e1000_10bt_ext_dist_enable_lower, + e1000_10bt_ext_dist_enable_undefined = 0xFF +} e1000_10bt_ext_dist_enable; + +typedef enum { + e1000_rev_polarity_normal = 0, + e1000_rev_polarity_reversed, + e1000_rev_polarity_undefined = 0xFF +} e1000_rev_polarity; + +typedef enum { + e1000_polarity_reversal_enabled = 0, + e1000_polarity_reversal_disabled, + e1000_polarity_reversal_undefined = 0xFF +} e1000_polarity_reversal; + +typedef enum { + e1000_down_no_idle_no_detect = 0, + e1000_down_no_idle_detect, + e1000_down_no_idle_undefined = 0xFF +} e1000_down_no_idle; + +typedef enum { + e1000_auto_x_mode_manual_mdi = 0, + e1000_auto_x_mode_manual_mdix, + e1000_auto_x_mode_auto1, + e1000_auto_x_mode_auto2, + e1000_auto_x_mode_undefined = 0xFF +} e1000_auto_x_mode; + +typedef enum { + e1000_1000t_rx_status_not_ok = 0, + e1000_1000t_rx_status_ok, + e1000_1000t_rx_status_undefined = 0xFF +} e1000_1000t_rx_status; + +struct e1000_phy_info { + e1000_cable_length cable_length; + e1000_10bt_ext_dist_enable extended_10bt_distance; + e1000_rev_polarity cable_polarity; + e1000_polarity_reversal polarity_correction; + e1000_down_no_idle link_reset; + e1000_auto_x_mode mdix_mode; + e1000_1000t_rx_status local_rx; + e1000_1000t_rx_status remote_rx; +}; + +struct e1000_phy_stats { + uint32_t idle_errors; + uint32_t receive_errors; +}; + +/* Function Prototypes */ +uint16_t e1000_read_phy_reg(struct e1000_shared_adapter *shared, uint32_t reg_addr); +void e1000_write_phy_reg(struct e1000_shared_adapter *shared, uint32_t reg_addr, uint16_t data); +void e1000_phy_hw_reset(struct e1000_shared_adapter *shared); +boolean_t e1000_phy_reset(struct e1000_shared_adapter *shared); +boolean_t e1000_phy_setup(struct e1000_shared_adapter *shared, uint32_t ctrl_reg); +boolean_t e1000_phy_setup_autoneg(struct e1000_shared_adapter *shared); +void e1000_config_mac_to_phy(struct e1000_shared_adapter *shared, uint16_t mii_reg); +void e1000_config_collision_dist(struct e1000_shared_adapter *shared); +boolean_t e1000_detect_gig_phy(struct e1000_shared_adapter *shared); +void e1000_phy_reset_dsp(struct e1000_shared_adapter *shared); +boolean_t e1000_wait_autoneg(struct e1000_shared_adapter *shared); +boolean_t e1000_phy_get_info(struct e1000_shared_adapter *shared, struct e1000_phy_info *phy_status_info); +boolean_t e1000_validate_mdi_setting(struct e1000_shared_adapter *shared); + +/* Bit definitions for the Management Data IO (MDIO) and Management Data + * Clock (MDC) pins in the Device Control Register. + */ +#define E1000_CTRL_PHY_RESET_DIR E1000_CTRL_SWDPIO0 +#define E1000_CTRL_PHY_RESET E1000_CTRL_SWDPIN0 +#define E1000_CTRL_MDIO_DIR E1000_CTRL_SWDPIO2 +#define E1000_CTRL_MDIO E1000_CTRL_SWDPIN2 +#define E1000_CTRL_MDC_DIR E1000_CTRL_SWDPIO3 +#define E1000_CTRL_MDC E1000_CTRL_SWDPIN3 +#define E1000_CTRL_PHY_RESET_DIR4 E1000_CTRL_EXT_SDP4_DIR +#define E1000_CTRL_PHY_RESET4 E1000_CTRL_EXT_SDP4_DATA + +/* PHY 1000 MII Register/Bit Definitions */ +/* PHY Registers defined by IEEE */ +#define PHY_CTRL 0x00 /* Control Register */ +#define PHY_STATUS 0x01 /* Status Regiser */ +#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ +#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ +#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ +#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ +#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ + +/* M88E1000 Specific Registers */ +#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */ +#define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Register */ +#define M88E1000_INT_ENABLE 0x12 /* Interrupt Enable Register */ +#define M88E1000_INT_STATUS 0x13 /* Interrupt Status Register */ +#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Control */ +#define M88E1000_RX_ERR_CNTR 0x15 /* Receive Error Counter */ + +#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ + +/* PHY Control Register */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ + +/* PHY Status Register */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* Autoneg Advertisement Register */ +#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */ +#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Link Partner Ability Register (Base Page) */ +#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ +#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */ +#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */ +#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */ +#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */ +#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */ +#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */ +#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */ +#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */ +#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */ +#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Autoneg Expansion Register */ +#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */ +#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */ +#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */ +#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */ +#define NWAY_ER_PAR_DETECT_FAULT 0x0100 /* LP is 100TX Full Duplex Capable */ + +/* Next Page TX Register */ +#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* Link Partner Next Page Register */ +#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */ +#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* 1000BASE-T Control Register */ +#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */ +#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ + /* 0=DTE device */ +#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ + /* 0=Configure PHY as Slave */ +#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ + /* 0=Automatic Master/Slave config */ +#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ + +/* 1000BASE-T Status Register */ +#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */ +#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */ +#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ +#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ +#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12 +#define SR_1000T_LOCAL_RX_STATUS_SHIFT 13 + +/* Extended Status Register */ +#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ +#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ +#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ +#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ + +#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */ +#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */ + +#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */ + /* (0=enable, 1=disable) */ + +/* M88E1000 PHY Specific Control Register */ +#define M88E1000_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */ +#define M88E1000_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */ +#define M88E1000_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */ +#define M88E1000_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low, + * 0=CLK125 toggling + */ +#define M88E1000_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5 */ + /* Manual MDI configuration */ +#define M88E1000_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */ +#define M88E1000_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover, + * 100BASE-TX/10BASE-T: + * MDI Mode + */ +#define M88E1000_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled + * all speeds. + */ +#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE 0x0080 + /* 1=Enable Extended 10BASE-T distance + * (Lower 10BASE-T RX Threshold) + * 0=Normal 10BASE-T RX Threshold */ +#define M88E1000_PSCR_MII_5BIT_ENABLE 0x0100 + /* 1=5-Bit interface in 100BASE-TX + * 0=MII interface in 100BASE-TX */ +#define M88E1000_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */ +#define M88E1000_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */ +#define M88E1000_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */ + +#define M88E1000_PSCR_POLARITY_REVERSAL_SHIFT 1 +#define M88E1000_PSCR_AUTO_X_MODE_SHIFT 5 +#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7 + +/* M88E1000 PHY Specific Status Register */ +#define M88E1000_PSSR_JABBER 0x0001 /* 1=Jabber */ +#define M88E1000_PSSR_REV_POLARITY 0x0002 /* 1=Polarity reversed */ +#define M88E1000_PSSR_MDIX 0x0040 /* 1=MDIX; 0=MDI */ +#define M88E1000_PSSR_CABLE_LENGTH 0x0380 /* 0=<50M;1=50-80M;2=80-110M; + * 3=110-140M;4=>140M */ +#define M88E1000_PSSR_LINK 0x0400 /* 1=Link up, 0=Link down */ +#define M88E1000_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ +#define M88E1000_PSSR_PAGE_RCVD 0x1000 /* 1=Page received */ +#define M88E1000_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ +#define M88E1000_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ +#define M88E1000_PSSR_10MBS 0x0000 /* 00=10Mbs */ +#define M88E1000_PSSR_100MBS 0x4000 /* 01=100Mbs */ +#define M88E1000_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ + +#define M88E1000_PSSR_REV_POLARITY_SHIFT 1 +#define M88E1000_PSSR_MDIX_SHIFT 6 +#define M88E1000_PSSR_CABLE_LENGTH_SHIFT 7 + +/* M88E1000 Extended PHY Specific Control Register */ +#define M88E1000_EPSCR_FIBER_LOOPBACK 0x4000 /* 1=Fiber loopback */ +#define M88E1000_EPSCR_DOWN_NO_IDLE 0x8000 /* 1=Lost lock detect enabled. + * Will assert lost lock and bring + * link down if idle not seen + * within 1ms in 1000BASE-T + */ +#define M88E1000_EPSCR_TX_CLK_2_5 0x0060 /* 2.5 MHz TX_CLK */ +#define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */ +#define M88E1000_EPSCR_TX_CLK_0 0x0000 /* NO TX_CLK */ + +#define M88E1000_EPSCR_DOWN_NO_IDLE_SHIFT 15 + +/* Bit definitions for valid PHY IDs. */ +#define M88E1000_12_PHY_ID 0x01410C50 +#define M88E1000_14_PHY_ID 0x01410C40 +#define M88E1000_I_PHY_ID 0x01410C30 + +/* Miscellaneous PHY bit definitions. */ +#define PHY_PREAMBLE 0xFFFFFFFF +#define PHY_SOF 0x01 +#define PHY_OP_READ 0x02 +#define PHY_OP_WRITE 0x01 +#define PHY_TURNAROUND 0x02 +#define PHY_PREAMBLE_SIZE 32 +#define MII_CR_SPEED_1000 0x0040 +#define MII_CR_SPEED_100 0x2000 +#define MII_CR_SPEED_10 0x0000 +#define E1000_PHY_ADDRESS 0x01 +#define PHY_AUTO_NEG_TIME 45 /* 4.5 Seconds */ +#define PHY_FORCE_TIME 20 /* 2.0 Seconds */ +#define PHY_REVISION_MASK 0xFFFFFFF0 +#define DEVICE_SPEED_MASK 0x00000300 /* Device Ctrl Reg Speed Mask */ +#define REG4_SPEED_MASK 0x01E0 +#define REG9_SPEED_MASK 0x0300 +#define ADVERTISE_10_HALF 0x0001 +#define ADVERTISE_10_FULL 0x0002 +#define ADVERTISE_100_HALF 0x0004 +#define ADVERTISE_100_FULL 0x0008 +#define ADVERTISE_1000_HALF 0x0010 +#define ADVERTISE_1000_FULL 0x0020 +#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F /* Everything but 1000-Half */ + +#endif /* _E1000_PHY_H_ */ diff -Nru a/drivers/net/e1000/e1000_proc.c b/drivers/net/e1000/e1000_proc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e1000/e1000_proc.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,760 @@ +/******************************************************************************* + + This software program is available to you under a choice of one of two + licenses. You may choose to be licensed under either the GNU General Public + License (GPL) Version 2, June 1991, available at + http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the + text of which follows: + + Recipient has requested a license and Intel Corporation ("Intel") is willing + to grant a license for the software entitled Linux Base Driver for the + Intel(R) PRO/1000 Family of Adapters (e1000) (the "Software") being provided + by Intel Corporation. The following definitions apply to this license: + + "Licensed Patents" means patent claims licensable by Intel Corporation which + are necessarily infringed by the use of sale of the Software alone or when + combined with the operating system referred to below. + + "Recipient" means the party to whom Intel delivers this Software. + + "Licensee" means Recipient and those third parties that receive a license to + any operating system available under the GNU Public License version 2.0 or + later. + + Copyright (c) 1999 - 2002 Intel Corporation. + All rights reserved. + + The license is provided to Recipient and Recipient's Licensees under the + following terms. + + Redistribution and use in source and binary forms of the Software, with or + without modification, are permitted provided that the following conditions + are met: + + Redistributions of source code of the Software may retain the above + copyright notice, this list of conditions and the following disclaimer. + + Redistributions in binary form of the Software may reproduce the above + copyright notice, this list of conditions and the following disclaimer in + the documentation and/or materials provided with the distribution. + + Neither the name of Intel Corporation nor the names of its contributors + shall be used to endorse or promote products derived from this Software + without specific prior written permission. + + Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, + royalty-free patent license under Licensed Patents to make, use, sell, offer + to sell, import and otherwise transfer the Software, if any, in source code + and object code form. This license shall include changes to the Software + that are error corrections or other minor changes to the Software that do + not add functionality or features when the Software is incorporated in any + version of an operating system that has been distributed under the GNU + General Public License 2.0 or later. This patent license shall apply to the + combination of the Software and any operating system licensed under the GNU + Public License version 2.0 or later if, at the time Intel provides the + Software to Recipient, such addition of the Software to the then publicly + available versions of such operating systems available under the GNU Public + License version 2.0 or later (whether in gold, beta or alpha form) causes + such combination to be covered by the Licensed Patents. The patent license + shall not apply to any other combinations which include the Software. NO + hardware per se is licensed hereunder. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) 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. + +*******************************************************************************/ + +/* + * Proc fs support. + * + * Read-only files created by driver (if CONFIG_PROC_FS): + * + * /proc/net/PRO_LAN_Adapters/.info + * /proc/net/PRO_LAN_Adapters// + * + * where is the system device name, i.e eth0. + * is the driver attribute name. + * + * There is one file for each driver attribute, where the contents + * of the file is the attribute value. The ethx.info file contains + * a list of all driver attributes in one file. + * + */ + +#include "e1000.h" + +#ifdef CONFIG_PROC_FS + +#include + +#define ADAPTERS_PROC_DIR "PRO_LAN_Adapters" +#define TAG_MAX_LENGTH 36 + +extern char e1000_driver_name[]; +extern char e1000_driver_version[]; + +/* + * The list of driver proc attributes is stored in a proc_list link + * list. The list is build with proc_list_setup and is used to + * build the proc fs nodes. The private data for each node is the + * corresponding link in the link list. + */ + +struct proc_list { + struct list_head list; /* link list */ + char tag[TAG_MAX_LENGTH]; /* attribute name */ + void *data; /* attribute data */ + size_t len; /* sizeof data */ + char *(*func)(void *, size_t, char *); /* format data func */ +}; + +static int +e1000_proc_read(char *page, char **start, off_t off, int count, int *eof) +{ + int len = strlen(page); + + page[len++] = '\n'; + + if(len <= off + count) + *eof = 1; + *start = page + off; + len -= off; + if(len > count) + len = count; + if(len < 0) + len = 0; + + return len; +} + +static int +e1000_proc_info_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct list_head *proc_list_head = data, *curr; + struct proc_list *elem; + char *p = page; + char buf[64]; + + list_for_each(curr, proc_list_head) { + elem = list_entry(curr, struct proc_list, list); + + if(strlen(elem->tag) == 0) + p += sprintf(p, "\n"); + else + p += sprintf(p, "%-32s %s\n", elem->tag, + elem->func(elem->data, elem->len, buf)); + } + + *p = '\0'; + + return e1000_proc_read(page, start, off, count, eof); +} + +static int +e1000_proc_single_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct proc_list *elem = data; + + sprintf(page, "%s", elem->func(elem->data, elem->len, page)); + + return e1000_proc_read(page, start, off, count, eof); +} + +static void __devexit +e1000_proc_dirs_free(char *name, struct list_head *proc_list_head) +{ + struct proc_dir_entry *intel_proc_dir, *proc_dir; + char info_name[strlen(name) + strlen(".info")]; + + for(intel_proc_dir = proc_net->subdir; intel_proc_dir; + intel_proc_dir = intel_proc_dir->next) { + if((intel_proc_dir->namelen == strlen(ADAPTERS_PROC_DIR)) && + (memcmp(intel_proc_dir->name, + ADAPTERS_PROC_DIR, strlen(ADAPTERS_PROC_DIR)) == 0)) + break; + } + + if(!intel_proc_dir) + return; + + for(proc_dir = intel_proc_dir->subdir; proc_dir; + proc_dir = proc_dir->next) { + if ((proc_dir->namelen == strlen(name)) && + !memcmp(proc_dir->name, name, strlen(name))) + break; + } + + if(proc_dir) { + struct list_head *curr; + struct proc_list *elem; + + list_for_each(curr, proc_list_head) { + elem = list_entry(curr, struct proc_list, list); + remove_proc_entry(elem->tag, proc_dir); + } + + strcpy(info_name, name); + strcat(info_name, ".info"); + + remove_proc_entry(info_name, intel_proc_dir); + remove_proc_entry(name, intel_proc_dir); + } + + /* If the intel dir is empty, remove it */ + + for(proc_dir = intel_proc_dir->subdir; proc_dir; + proc_dir = proc_dir->next) { + + /* ignore . and .. */ + + if(*(proc_dir->name) == '.') + continue; + break; + } + + if(!proc_dir) + remove_proc_entry(ADAPTERS_PROC_DIR, proc_net); + + return; +} + + +static int __devinit +e1000_proc_singles_create(struct proc_dir_entry *parent, + struct list_head *proc_list_head) +{ + struct list_head *curr; + struct proc_list *elem; + + list_for_each(curr, proc_list_head) { + struct proc_dir_entry *proc_entry; + + elem = list_entry(curr, struct proc_list, list); + + if(strlen(elem->tag) == 0) + continue; + + if(!(proc_entry = + create_proc_entry(elem->tag, S_IFREG, parent))) + return 0; + + proc_entry->read_proc = e1000_proc_single_read; + proc_entry->data = elem; + SET_MODULE_OWNER(proc_entry); + } + + return 1; +} + +static int __devinit +e1000_proc_dirs_create(char *name, struct list_head *proc_list_head) +{ + struct proc_dir_entry *intel_proc_dir, *proc_dir, *info_entry; + char info_name[strlen(name) + strlen(".info")]; + + for(intel_proc_dir = proc_net->subdir; intel_proc_dir; + intel_proc_dir = intel_proc_dir->next) { + if((intel_proc_dir->namelen == strlen(ADAPTERS_PROC_DIR)) && + (memcmp(intel_proc_dir->name, + ADAPTERS_PROC_DIR, strlen(ADAPTERS_PROC_DIR)) == 0)) + break; + } + + if(!intel_proc_dir) + if(!(intel_proc_dir = + create_proc_entry(ADAPTERS_PROC_DIR, + S_IFDIR, proc_net))) + return 0; + + if(!(proc_dir = + create_proc_entry(name, S_IFDIR, intel_proc_dir))) + return 0; + SET_MODULE_OWNER(proc_dir); + + if(!e1000_proc_singles_create(proc_dir, proc_list_head)) + return 0; + + strcpy(info_name, name); + strcat(info_name, ".info"); + + if(!(info_entry = + create_proc_entry(info_name, S_IFREG, intel_proc_dir))) + return 0; + SET_MODULE_OWNER(info_entry); + + info_entry->read_proc = e1000_proc_info_read; + info_entry->data = proc_list_head; + + return 1; +} + +static void __devinit +e1000_proc_list_add(struct list_head *proc_list_head, char *tag, + void *data, size_t len, + char *(*func)(void *, size_t, char *)) +{ + struct proc_list *new = (struct proc_list *) + kmalloc(sizeof(struct proc_list), GFP_KERNEL); + + if(!new) + return; + + strncpy(new->tag, tag, TAG_MAX_LENGTH); + new->data = data; + new->len = len; + new->func = func; + + list_add_tail(&new->list, proc_list_head); + + return; +} + +static void __devexit +e1000_proc_list_free(struct list_head *proc_list_head) +{ + struct proc_list *elem; + + while(!list_empty(proc_list_head)) { + elem = list_entry(proc_list_head->next, struct proc_list, list); + list_del(&elem->list); + kfree(elem); + } + + return; +} + +/* + * General purpose formating functions + */ + +static char * +e1000_proc_str(void *data, size_t len, char *buf) +{ + sprintf(buf, "%s", (char *)data); + return buf; +} + +static char * +e1000_proc_hex(void *data, size_t len, char *buf) +{ + switch(len) { + case sizeof(uint8_t): + sprintf(buf, "0x%02x", *(uint8_t *)data); + break; + case sizeof(uint16_t): + sprintf(buf, "0x%04x", *(uint16_t *)data); + break; + case sizeof(uint32_t): + sprintf(buf, "0x%08x", *(uint32_t *)data); + break; + case sizeof(uint64_t): + sprintf(buf, "0x%08Lx", (unsigned long long)*(uint64_t *)data); + break; + } + return buf; +} + +static char * +e1000_proc_unsigned(void *data, size_t len, char *buf) +{ + switch(len) { + case sizeof(uint8_t): + sprintf(buf, "%u", *(uint8_t *)data); + break; + case sizeof(uint16_t): + sprintf(buf, "%u", *(uint16_t *)data); + break; + case sizeof(uint32_t): + sprintf(buf, "%u", *(uint32_t *)data); + break; + case sizeof(uint64_t): + sprintf(buf, "%Lu", (unsigned long long)*(uint64_t *)data); + break; + } + return buf; +} + +/* + * Specific formating functions + */ + +static char * +e1000_proc_part_number(void *data, size_t len, char *buf) +{ + sprintf(buf, "%06x-%03x", *(uint32_t *)data >> 8, + *(uint32_t *)data & 0x000000FF); + return buf; +} + +static char * +e1000_proc_slot(void *data, size_t len, char *buf) +{ + struct e1000_adapter *adapter = data; + sprintf(buf, "%u", PCI_SLOT(adapter->pdev->devfn)); + return buf; +} + +static char * +e1000_proc_bus_type(void *data, size_t len, char *buf) +{ + e1000_bus_type bus_type = *(e1000_bus_type *)data; + sprintf(buf, + bus_type == e1000_bus_type_pci ? "PCI" : + bus_type == e1000_bus_type_pcix ? "PCI-X" : + "UNKNOWN"); + return buf; +} + +static char * +e1000_proc_bus_speed(void *data, size_t len, char *buf) +{ + e1000_bus_speed bus_speed = *(e1000_bus_speed *)data; + sprintf(buf, + bus_speed == e1000_bus_speed_33 ? "33MHz" : + bus_speed == e1000_bus_speed_66 ? "66MHz" : + bus_speed == e1000_bus_speed_100 ? "100MHz" : + bus_speed == e1000_bus_speed_133 ? "133MHz" : + "UNKNOWN"); + return buf; +} + +static char * +e1000_proc_bus_width(void *data, size_t len, char *buf) +{ + e1000_bus_width bus_width = *(e1000_bus_width *)data; + sprintf(buf, + bus_width == e1000_bus_width_32 ? "32-bit" : + bus_width == e1000_bus_width_64 ? "64-bit" : + "UNKNOWN"); + return buf; +} + +static char * +e1000_proc_hwaddr(void *data, size_t len, char *buf) +{ + unsigned char *hwaddr = data; + sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", + hwaddr[0], hwaddr[1], hwaddr[2], + hwaddr[3], hwaddr[4], hwaddr[5]); + return buf; +} + +static char * +e1000_proc_link(void *data, size_t len, char *buf) +{ + struct e1000_adapter *adapter = data; + sprintf(buf, netif_running(adapter->netdev) ? + netif_carrier_ok(adapter->netdev) ? + "up" : "down" : "N/A"); + return buf; +} + +static char * +e1000_proc_link_speed(void *data, size_t len, char *buf) +{ + uint16_t link_speed = *(uint16_t *)data; + sprintf(buf, link_speed ? "%u" : "N/A", link_speed); + return buf; +} + +static char * +e1000_proc_link_duplex(void *data, size_t len, char *buf) +{ + uint16_t link_duplex = *(uint16_t *)data; + sprintf(buf, + link_duplex == FULL_DUPLEX ? "Full" : + link_duplex == HALF_DUPLEX ? "Half" : + "N/A"); + return buf; +} + +static char * +e1000_proc_state(void *data, size_t len, char *buf) +{ + struct e1000_adapter *adapter = data; + sprintf(buf, adapter->netdev->flags & IFF_UP ? "up" : "down"); + return buf; +} + +static char * +e1000_proc_media_type(void *data, size_t len, char *buf) +{ + struct e1000_adapter *adapter = data; + sprintf(buf, + adapter->shared.media_type == e1000_media_type_copper ? + "Copper" : "Fiber"); + return buf; +} + +static char * +e1000_proc_cable_length(void *data, size_t len, char *buf) +{ + struct e1000_adapter *adapter = data; + e1000_cable_length cable_length = adapter->phy_info.cable_length; + sprintf(buf, "%s%s", + cable_length == e1000_cable_length_50 ? "0-50" : + cable_length == e1000_cable_length_50_80 ? "50-80" : + cable_length == e1000_cable_length_80_110 ? "80-110" : + cable_length == e1000_cable_length_110_140 ? "110-140" : + cable_length == e1000_cable_length_140 ? "> 140" : + "Unknown", + cable_length != e1000_cable_length_undefined ? + " Meters (+/- 20 Meters)" : ""); + return buf; +} + +static char * +e1000_proc_extended(void *data, size_t len, char *buf) +{ + struct e1000_adapter *adapter = data; + e1000_10bt_ext_dist_enable dist_enable = + adapter->phy_info.extended_10bt_distance; + sprintf(buf, + dist_enable == e1000_10bt_ext_dist_enable_normal ? "Disabled" : + dist_enable == e1000_10bt_ext_dist_enable_lower ? "Enabled" : + "Unknown"); + return buf; +} + +static char * +e1000_proc_cable_polarity(void *data, size_t len, char *buf) +{ + struct e1000_adapter *adapter = data; + e1000_rev_polarity polarity = adapter->phy_info.cable_polarity; + sprintf(buf, + polarity == e1000_rev_polarity_normal ? "Normal" : + polarity == e1000_rev_polarity_reversed ? "Reversed" : + "Unknown"); + return buf; +} + +static char * +e1000_proc_polarity_correction(void *data, size_t len, char *buf) +{ + struct e1000_adapter *adapter = data; + e1000_polarity_reversal correction = + adapter->phy_info.polarity_correction; + sprintf(buf, + correction == e1000_polarity_reversal_enabled ? "Disabled" : + correction == e1000_polarity_reversal_disabled ? "Enabled" : + "Undefined"); + return buf; +} + +static char * +e1000_proc_link_reset_enabled(void *data, size_t len, char *buf) +{ + struct e1000_adapter *adapter = data; + e1000_down_no_idle link_reset = adapter->phy_info.link_reset; + sprintf(buf, + link_reset == e1000_down_no_idle_no_detect ? "Disabled" : + link_reset == e1000_down_no_idle_detect ? "Enabled" : + "Unknown"); + return buf; +} + +static char * +e1000_proc_mdi_x_enabled(void *data, size_t len, char *buf) +{ + struct e1000_adapter *adapter = data; + e1000_auto_x_mode mdix_mode = adapter->phy_info.mdix_mode; + sprintf(buf, mdix_mode == 0 ? "MDI" : "MDI-X"); + return buf; +} + +static char * +e1000_proc_rx_status(void *data, size_t len, char *buf) +{ + e1000_1000t_rx_status rx_status = *(e1000_1000t_rx_status *)data; + sprintf(buf, + rx_status == e1000_1000t_rx_status_not_ok ? "NOT_OK" : + rx_status == e1000_1000t_rx_status_ok ? "OK" : + "Unknown"); + return buf; +} + +/* + * e1000_proc_list_setup - build link list of proc praramters + * @adapter: board private structure + * + * Order matters - ethx.info entries are ordered in the order links + * are added to list. + */ + +#define LIST_ADD_F(T,D,F) \ + e1000_proc_list_add(proc_list_head, (T), (D), sizeof(*(D)), (F)) +#define LIST_ADD_BLANK() LIST_ADD_F("", NULL, NULL) +#define LIST_ADD_S(T,D) LIST_ADD_F((T), (D), e1000_proc_str) +#define LIST_ADD_H(T,D) LIST_ADD_F((T), (D), e1000_proc_hex) +#define LIST_ADD_U(T,D) LIST_ADD_F((T), (D), e1000_proc_unsigned) + +static void __devinit +e1000_proc_list_setup(struct e1000_adapter *adapter) +{ + struct e1000_shared_adapter *shared = &adapter->shared; + struct list_head *proc_list_head = &adapter->proc_list_head; + + INIT_LIST_HEAD(proc_list_head); + + LIST_ADD_S("Description", adapter->id_string); + LIST_ADD_F("Part_Number", &adapter->part_num, e1000_proc_part_number); + LIST_ADD_S("Driver_Name", e1000_driver_name); + LIST_ADD_S("Driver_Version", e1000_driver_version); + LIST_ADD_H("PCI_Vendor", &shared->vendor_id); + LIST_ADD_H("PCI_Device_ID", &shared->device_id); + LIST_ADD_H("PCI_Subsystem_Vendor", &shared->subsystem_vendor_id); + LIST_ADD_H("PCI_Subsystem_ID", &shared->subsystem_id); + LIST_ADD_H("PCI_Revision_ID", &shared->revision_id); + LIST_ADD_U("PCI_Bus", &adapter->pdev->bus->number); + LIST_ADD_F("PCI_Slot", adapter, e1000_proc_slot); + + if(adapter->shared.mac_type >= e1000_82543) { + LIST_ADD_F("PCI_Bus_Type", + &shared->bus_type, e1000_proc_bus_type); + LIST_ADD_F("PCI_Bus_Speed", + &shared->bus_speed, e1000_proc_bus_speed); + LIST_ADD_F("PCI_Bus_Width", + &shared->bus_width, e1000_proc_bus_width); + } + + LIST_ADD_U("IRQ", &adapter->pdev->irq); + LIST_ADD_S("System_Device_Name", adapter->netdev->name); + LIST_ADD_F("Current_HWaddr", + adapter->netdev->dev_addr, e1000_proc_hwaddr); + LIST_ADD_F("Permanent_HWaddr", + adapter->shared.perm_mac_addr, e1000_proc_hwaddr); + + LIST_ADD_BLANK(); + + LIST_ADD_F("Link", adapter, e1000_proc_link); + LIST_ADD_F("Speed", &adapter->link_speed, e1000_proc_link_speed); + LIST_ADD_F("Duplex", &adapter->link_duplex, e1000_proc_link_duplex); + LIST_ADD_F("State", adapter, e1000_proc_state); + + LIST_ADD_BLANK(); + + /* Standard net device stats */ + LIST_ADD_U("Rx_Packets", &adapter->net_stats.rx_packets); + LIST_ADD_U("Tx_Packets", &adapter->net_stats.tx_packets); + LIST_ADD_U("Rx_Bytes", &adapter->net_stats.rx_bytes); + LIST_ADD_U("Tx_Bytes", &adapter->net_stats.tx_bytes); + LIST_ADD_U("Rx_Errors", &adapter->net_stats.rx_errors); + LIST_ADD_U("Tx_Errors", &adapter->net_stats.tx_errors); + LIST_ADD_U("Rx_Dropped", &adapter->net_stats.rx_dropped); + LIST_ADD_U("Tx_Dropped", &adapter->net_stats.tx_dropped); + + LIST_ADD_U("Multicast", &adapter->net_stats.multicast); + LIST_ADD_U("Collisions", &adapter->net_stats.collisions); + + LIST_ADD_U("Rx_Length_Errors", &adapter->net_stats.rx_length_errors); + LIST_ADD_U("Rx_Over_Errors", &adapter->net_stats.rx_over_errors); + LIST_ADD_U("Rx_CRC_Errors", &adapter->net_stats.rx_crc_errors); + LIST_ADD_U("Rx_Frame_Errors", &adapter->net_stats.rx_frame_errors); + LIST_ADD_U("Rx_FIFO_Errors", &adapter->net_stats.rx_fifo_errors); + LIST_ADD_U("Rx_Missed_Errors", &adapter->net_stats.rx_missed_errors); + + LIST_ADD_U("Tx_Aborted_Errors", &adapter->net_stats.tx_aborted_errors); + LIST_ADD_U("Tx_Carrier_Errors", &adapter->net_stats.tx_carrier_errors); + LIST_ADD_U("Tx_FIFO_Errors", &adapter->net_stats.tx_fifo_errors); + LIST_ADD_U("Tx_Heartbeat_Errors", + &adapter->net_stats.tx_heartbeat_errors); + LIST_ADD_U("Tx_Window_Errors", &adapter->net_stats.tx_window_errors); + + /* 8254x-specific stats */ + LIST_ADD_U("Tx_Abort_Late_Coll", &adapter->stats.latecol); + LIST_ADD_U("Tx_Deferred_Ok", &adapter->stats.dc); + LIST_ADD_U("Tx_Single_Coll_Ok", &adapter->stats.scc); + LIST_ADD_U("Tx_Multi_Coll_Ok", &adapter->stats.mcc); + LIST_ADD_U("Rx_Long_Length_Errors", &adapter->stats.roc); + LIST_ADD_U("Rx_Short_Length_Errors", &adapter->stats.ruc); + + /* The 82542 does not have an alignment error count register */ + if(adapter->shared.mac_type >= e1000_82543) + LIST_ADD_U("Rx_Align_Errors", &adapter->stats.algnerrc); + + LIST_ADD_U("Rx_Flow_Control_XON", &adapter->stats.xonrxc); + LIST_ADD_U("Rx_Flow_Control_XOFF", &adapter->stats.xoffrxc); + LIST_ADD_U("Tx_Flow_Control_XON", &adapter->stats.xontxc); + LIST_ADD_U("Tx_Flow_Control_XOFF", &adapter->stats.xofftxc); + LIST_ADD_U("Rx_CSum_Offload_Good", &adapter->hw_csum_good); + LIST_ADD_U("Rx_CSum_Offload_Errors", &adapter->hw_csum_err); + + LIST_ADD_BLANK(); + + /* Cable diags */ + LIST_ADD_F("PHY_Media_Type", adapter, e1000_proc_media_type); + if(adapter->shared.media_type == e1000_media_type_copper) { + LIST_ADD_F("PHY_Cable_Length", + adapter, e1000_proc_cable_length); + LIST_ADD_F("PHY_Extended_10Base_T_Distance", + adapter, e1000_proc_extended); + LIST_ADD_F("PHY_Cable_Polarity", + adapter, e1000_proc_cable_polarity); + LIST_ADD_F("PHY_Disable_Polarity_Correction", + adapter, e1000_proc_polarity_correction); + LIST_ADD_U("PHY_Idle_Errors", + &adapter->phy_stats.idle_errors); + LIST_ADD_F("PHY_Link_Reset_Enabled", + adapter, e1000_proc_link_reset_enabled); + LIST_ADD_U("PHY_Receive_Errors", + &adapter->phy_stats.receive_errors); + LIST_ADD_F("PHY_MDI_X_Enabled", + adapter, e1000_proc_mdi_x_enabled); + LIST_ADD_F("PHY_Local_Receiver_Status", + &adapter->phy_info.local_rx, + e1000_proc_rx_status); + LIST_ADD_F("PHY_Remote_Receiver_Status", + &adapter->phy_info.remote_rx, + e1000_proc_rx_status); + } + + return; +} + +/* + * e1000_proc_dev_setup - create proc fs nodes and link list + * @adapter: board private structure + */ + +void __devinit +e1000_proc_dev_setup(struct e1000_adapter *adapter) +{ + e1000_proc_list_setup(adapter); + + e1000_proc_dirs_create(adapter->netdev->name,&adapter->proc_list_head); + + return; +} + +/* + * e1000_proc_dev_free - free proc fs nodes and link list + * @adapter: board private structure + */ + +void __devexit +e1000_proc_dev_free(struct e1000_adapter *adapter) +{ + e1000_proc_dirs_free(adapter->netdev->name, &adapter->proc_list_head); + + e1000_proc_list_free(&adapter->proc_list_head); + + return; +} + +#else /* CONFIG_PROC_FS */ + +void __devinit e1000_proc_dev_setup(struct e1000_adapter *adapter) {} +void __devexit e1000_proc_dev_free(struct e1000_adapter *adapter) {} + +#endif /* CONFIG_PROC_FS */ + diff -Nru a/drivers/net/e2100.c b/drivers/net/e2100.c --- a/drivers/net/e2100.c Thu Mar 7 18:17:46 2002 +++ b/drivers/net/e2100.c Thu Mar 7 18:17:46 2002 @@ -388,10 +388,12 @@ MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_E21_CARDS) "i"); MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_E21_CARDS) "i"); MODULE_PARM(xcvr, "1-" __MODULE_STRING(MAX_E21_CARDS) "i"); -MODULE_PARM_DESC(io, "E2100 I/O base address(es)"); -MODULE_PARM_DESC(irq, "E2100 IRQ number(s)"); -MODULE_PARM_DESC(mem, " E2100 memory base address(es)"); -MODULE_PARM_DESC(xcvr, "E2100 tranceiver(s) (0=internal, 1=external)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s)"); +MODULE_PARM_DESC(mem, " memory base address(es)"); +MODULE_PARM_DESC(xcvr, "tranceiver(s) (0=internal, 1=external)"); +MODULE_DESCRIPTION("Cabletron E2100 ISA ethernet driver"); +MODULE_LICENSE("GPL"); /* This is set up so that only a single autoprobe takes place per call. ISA device autoprobes on a running machine are not recommended. */ @@ -440,7 +442,6 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); /* diff -Nru a/drivers/net/eepro100.c b/drivers/net/eepro100.c --- a/drivers/net/eepro100.c Thu Mar 7 18:17:38 2002 +++ b/drivers/net/eepro100.c Thu Mar 7 18:17:38 2002 @@ -130,17 +130,17 @@ MODULE_PARM(rx_copybreak, "i"); MODULE_PARM(max_interrupt_work, "i"); MODULE_PARM(multicast_filter_limit, "i"); -MODULE_PARM_DESC(debug, "eepro100 debug level (0-6)"); -MODULE_PARM_DESC(options, "eepro100: Bits 0-3: tranceiver type, bit 4: full duplex, bit 5: 100Mbps"); -MODULE_PARM_DESC(full_duplex, "eepro100 full duplex setting(s) (1)"); -MODULE_PARM_DESC(congenb, "eepro100 Enable congestion control (1)"); -MODULE_PARM_DESC(txfifo, "eepro100 Tx FIFO threshold in 4 byte units, (0-15)"); -MODULE_PARM_DESC(rxfifo, "eepro100 Rx FIFO threshold in 4 byte units, (0-15)"); -MODULE_PARM_DESC(txdmaccount, "eepro100 Tx DMA burst length; 128 - disable (0-128)"); -MODULE_PARM_DESC(rxdmaccount, "eepro100 Rx DMA burst length; 128 - disable (0-128)"); -MODULE_PARM_DESC(rx_copybreak, "eepro100 copy breakpoint for copy-only-tiny-frames"); -MODULE_PARM_DESC(max_interrupt_work, "eepro100 maximum events handled per interrupt"); -MODULE_PARM_DESC(multicast_filter_limit, "eepro100 maximum number of filtered multicast addresses"); +MODULE_PARM_DESC(debug, "debug level (0-6)"); +MODULE_PARM_DESC(options, "Bits 0-3: tranceiver type, bit 4: full duplex, bit 5: 100Mbps"); +MODULE_PARM_DESC(full_duplex, "full duplex setting(s) (1)"); +MODULE_PARM_DESC(congenb, "Enable congestion control (1)"); +MODULE_PARM_DESC(txfifo, "Tx FIFO threshold in 4 byte units, (0-15)"); +MODULE_PARM_DESC(rxfifo, "Rx FIFO threshold in 4 byte units, (0-15)"); +MODULE_PARM_DESC(txdmaccount, "Tx DMA burst length; 128 - disable (0-128)"); +MODULE_PARM_DESC(rxdmaccount, "Rx DMA burst length; 128 - disable (0-128)"); +MODULE_PARM_DESC(rx_copybreak, "copy breakpoint for copy-only-tiny-frames"); +MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt"); +MODULE_PARM_DESC(multicast_filter_limit, "maximum number of filtered multicast addresses"); #define RUN_AT(x) (jiffies + (x)) @@ -2288,7 +2288,7 @@ name: "eepro100", id_table: eepro100_pci_tbl, probe: eepro100_init_one, - remove: eepro100_remove_one, + remove: __devexit_p(eepro100_remove_one), #ifdef CONFIG_PM suspend: eepro100_suspend, resume: eepro100_resume, diff -Nru a/drivers/net/epic100.c b/drivers/net/epic100.c --- a/drivers/net/epic100.c Thu Mar 7 18:17:44 2002 +++ b/drivers/net/epic100.c Thu Mar 7 18:17:44 2002 @@ -1533,7 +1533,7 @@ name: DRV_NAME, id_table: epic_pci_tbl, probe: epic_init_one, - remove: epic_remove_one, + remove: __devexit_p(epic_remove_one), #ifdef CONFIG_PM suspend: epic_suspend, resume: epic_resume, diff -Nru a/drivers/net/es3210.c b/drivers/net/es3210.c --- a/drivers/net/es3210.c Thu Mar 7 18:17:40 2002 +++ b/drivers/net/es3210.c Thu Mar 7 18:17:40 2002 @@ -383,9 +383,11 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_ES_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ES_CARDS) "i"); MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_ES_CARDS) "i"); -MODULE_PARM_DESC(io, "ES3210 I/O base address(es)"); -MODULE_PARM_DESC(irq, "ES3210 IRQ number(s)"); -MODULE_PARM_DESC(mem, "ES3210 memory base address(es)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s)"); +MODULE_PARM_DESC(mem, "memory base address(es)"); +MODULE_DESCRIPTION("Racal-Interlan ES3210 EISA ethernet driver"); +MODULE_LICENSE("GPL"); int init_module(void) @@ -429,5 +431,4 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); diff -Nru a/drivers/net/fc/Makefile b/drivers/net/fc/Makefile --- a/drivers/net/fc/Makefile Thu Mar 7 18:17:41 2002 +++ b/drivers/net/fc/Makefile Thu Mar 7 18:17:41 2002 @@ -1,7 +1,7 @@ # # Makefile for linux/drivers/net/fc # -# 9 Aug 2000, Christoph Hellwig +# 9 Aug 2000, Christoph Hellwig # Rewritten to use lists instead of if-statements. # diff -Nru a/drivers/net/fealnx.c b/drivers/net/fealnx.c --- a/drivers/net/fealnx.c Thu Mar 7 18:17:45 2002 +++ b/drivers/net/fealnx.c Thu Mar 7 18:17:45 2002 @@ -1929,7 +1929,7 @@ name: "fealnx", id_table: fealnx_pci_tbl, probe: fealnx_init_one, - remove: fealnx_remove_one, + remove: __devexit_p(fealnx_remove_one), }; static int __init fealnx_init(void) diff -Nru a/drivers/net/hamachi.c b/drivers/net/hamachi.c --- a/drivers/net/hamachi.c Thu Mar 7 18:17:44 2002 +++ b/drivers/net/hamachi.c Thu Mar 7 18:17:44 2002 @@ -1978,7 +1978,7 @@ } -static void __exit hamachi_remove_one (struct pci_dev *pdev) +static void __devexit hamachi_remove_one (struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); @@ -2008,7 +2008,7 @@ name: DRV_NAME, id_table: hamachi_pci_tbl, probe: hamachi_init_one, - remove: hamachi_remove_one, + remove: __devexit_p(hamachi_remove_one), }; static int __init hamachi_init (void) diff -Nru a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c --- a/drivers/net/hamradio/6pack.c Thu Mar 7 18:17:39 2002 +++ b/drivers/net/hamradio/6pack.c Thu Mar 7 18:17:39 2002 @@ -256,6 +256,7 @@ skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_AX25); netif_rx(skb); + dev->last_rx = jiffies; sp->stats.rx_packets++; } diff -Nru a/drivers/net/hamradio/Makefile b/drivers/net/hamradio/Makefile --- a/drivers/net/hamradio/Makefile Thu Mar 7 18:17:38 2002 +++ b/drivers/net/hamradio/Makefile Thu Mar 7 18:17:38 2002 @@ -7,7 +7,7 @@ # Joerg Reuter DL1BKE # # 20000806 Rewritten to use lists instead of if-statements. -# Christoph Hellwig +# Christoph Hellwig # diff -Nru a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c --- a/drivers/net/hamradio/baycom_epp.c Thu Mar 7 18:17:44 2002 +++ b/drivers/net/hamradio/baycom_epp.c Thu Mar 7 18:17:44 2002 @@ -706,6 +706,7 @@ skb->protocol = htons(ETH_P_AX25); skb->mac.raw = skb->data; netif_rx(skb); + dev->last_rx = jiffies; bc->stats.rx_packets++; } diff -Nru a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c --- a/drivers/net/hamradio/bpqether.c Thu Mar 7 18:17:44 2002 +++ b/drivers/net/hamradio/bpqether.c Thu Mar 7 18:17:44 2002 @@ -254,6 +254,7 @@ skb->pkt_type = PACKET_HOST; netif_rx(skb); + dev->last_rx = jiffies; return 0; } diff -Nru a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c --- a/drivers/net/hamradio/hdlcdrv.c Thu Mar 7 18:17:39 2002 +++ b/drivers/net/hamradio/hdlcdrv.c Thu Mar 7 18:17:39 2002 @@ -223,6 +223,7 @@ skb->protocol = htons(ETH_P_AX25); skb->mac.raw = skb->data; netif_rx(skb); + dev->last_rx = jiffies; s->stats.rx_packets++; } diff -Nru a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c --- a/drivers/net/hamradio/mkiss.c Thu Mar 7 18:17:43 2002 +++ b/drivers/net/hamradio/mkiss.c Thu Mar 7 18:17:43 2002 @@ -345,6 +345,7 @@ skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_AX25); netif_rx(skb); + dev->last_rx = jiffies; tmp_ax->rx_packets++; } diff -Nru a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c --- a/drivers/net/hamradio/scc.c Thu Mar 7 18:17:41 2002 +++ b/drivers/net/hamradio/scc.c Thu Mar 7 18:17:41 2002 @@ -1661,6 +1661,7 @@ skb->pkt_type = PACKET_HOST; netif_rx(skb); + dev->last_rx = jiffies; return; } diff -Nru a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c --- a/drivers/net/hamradio/yam.c Thu Mar 7 18:17:45 2002 +++ b/drivers/net/hamradio/yam.c Thu Mar 7 18:17:45 2002 @@ -528,6 +528,7 @@ skb->protocol = htons(ETH_P_AX25); skb->mac.raw = skb->data; netif_rx(skb); + dev->last_rx = jiffies; ++yp->stats.rx_packets; } } diff -Nru a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c --- a/drivers/net/hp-plus.c Thu Mar 7 18:17:38 2002 +++ b/drivers/net/hp-plus.c Thu Mar 7 18:17:38 2002 @@ -408,8 +408,10 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_HPP_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_HPP_CARDS) "i"); -MODULE_PARM_DESC(io, "HP PC-LAN+ I/O port address(es)"); -MODULE_PARM_DESC(irq, "HP PC-LAN+ IRQ number(s); ignored if properly detected"); +MODULE_PARM_DESC(io, "I/O port address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s); ignored if properly detected"); +MODULE_DESCRIPTION("HP PC-LAN+ ISA ethernet driver"); +MODULE_LICENSE("GPL"); /* This is set up so that only a single autoprobe takes place per call. ISA device autoprobes on a running machine are not recommended. */ @@ -457,7 +459,6 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); /* diff -Nru a/drivers/net/hp.c b/drivers/net/hp.c --- a/drivers/net/hp.c Thu Mar 7 18:17:40 2002 +++ b/drivers/net/hp.c Thu Mar 7 18:17:40 2002 @@ -380,8 +380,10 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_HP_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_HP_CARDS) "i"); -MODULE_PARM_DESC(io, "HP PC-LAN I/O base address(es)"); -MODULE_PARM_DESC(irq, "HP PC-LAN IRQ number(s) (assigned)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); +MODULE_DESCRIPTION("HP PC-LAN ISA ethernet driver"); +MODULE_LICENSE("GPL"); /* This is set up so that only a single autoprobe takes place per call. ISA device autoprobes on a running machine are not recommended. */ @@ -429,7 +431,6 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); /* diff -Nru a/drivers/net/ioc3-eth.c b/drivers/net/ioc3-eth.c --- a/drivers/net/ioc3-eth.c Thu Mar 7 18:17:38 2002 +++ b/drivers/net/ioc3-eth.c Thu Mar 7 18:17:38 2002 @@ -1516,7 +1516,7 @@ name: "ioc3-eth", id_table: ioc3_pci_tbl, probe: ioc3_probe, - remove: ioc3_remove_one, + remove: __devexit_p(ioc3_remove_one), }; static int __init ioc3_init_module(void) diff -Nru a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile --- a/drivers/net/irda/Makefile Thu Mar 7 18:17:44 2002 +++ b/drivers/net/irda/Makefile Thu Mar 7 18:17:44 2002 @@ -2,7 +2,7 @@ # # Makefile for the Linux IrDA infrared port device drivers. # -# 9 Aug 2000, Christoph Hellwig +# 9 Aug 2000, Christoph Hellwig # Rewritten to use lists instead of if-statements. # diff -Nru a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c --- a/drivers/net/irda/ali-ircc.c Thu Mar 7 18:17:42 2002 +++ b/drivers/net/irda/ali-ircc.c Thu Mar 7 18:17:42 2002 @@ -1941,6 +1941,7 @@ skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); + self->netdev->last_rx = jiffies; } } diff -Nru a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c --- a/drivers/net/irda/irda-usb.c Thu Mar 7 18:17:45 2002 +++ b/drivers/net/irda/irda-usb.c Thu Mar 7 18:17:45 2002 @@ -255,7 +255,7 @@ self->new_speed, self->new_xbofs); /* Grab the speed URB */ - urb = &self->speed_urb; + urb = self->speed_urb; if (urb->status != 0) { WARNING(__FUNCTION__ "(), URB still in use!\n"); return; @@ -317,7 +317,7 @@ urb->status = 0; /* If it was the speed URB, allow the stack to send more packets */ - if(urb == &self->speed_urb) { + if(urb == self->speed_urb) { netif_wake_queue(self->netdev); } } @@ -329,7 +329,7 @@ static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *netdev) { struct irda_usb_cb *self = netdev->priv; - struct urb *urb = &self->tx_urb; + struct urb *urb = self->tx_urb; unsigned long flags; s32 speed; s16 xbofs; @@ -378,10 +378,17 @@ return 0; } - /* Make room for IrDA-USB header (note skb->len += USB_IRDA_HEADER) */ - if (skb_cow(skb, USB_IRDA_HEADER)) { - dev_kfree_skb(skb); - return 0; + /* Make sure there is room for IrDA-USB header. The actual + * allocation will be done lower in skb_push(). + * Also, we don't use directly skb_cow(), because it require + * headroom >= 16, which force unnecessary copies - Jean II */ + if (skb_headroom(skb) < USB_IRDA_HEADER) { + IRDA_DEBUG(0, __FUNCTION__ "(), Insuficient skb headroom.\n"); + if (skb_cow(skb, USB_IRDA_HEADER)) { + WARNING(__FUNCTION__ "(), failed skb_cow() !!!\n"); + dev_kfree_skb(skb); + return 0; + } } spin_lock_irqsave(&self->lock, flags); @@ -432,7 +439,7 @@ #ifdef IU_USB_MIN_RTT /* Factor in USB delays -> Get rid of udelay() that * would be lost in the noise - Jean II */ - diff -= IU_USB_MIN_RTT; + diff += IU_USB_MIN_RTT; #endif /* IU_USB_MIN_RTT */ if (diff < 0) diff += 1000000; @@ -546,7 +553,7 @@ } /* Check speed URB */ - urb = &(self->speed_urb); + urb = self->speed_urb; if (urb->status != 0) { IRDA_DEBUG(0, "%s: Speed change timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", netdev->name, urb->status, urb->transfer_flags); @@ -571,7 +578,7 @@ } /* Check Tx URB */ - urb = &(self->tx_urb); + urb = self->tx_urb; if (urb->status != 0) { struct sk_buff *skb = urb->context; @@ -832,6 +839,7 @@ new->mac.raw = new->data; new->protocol = htons(ETH_P_IRDA); netif_rx(new); + self->netdev->last_rx = jiffies; done: /* Note : at this point, the URB we've just received (urb) @@ -848,8 +856,8 @@ /* Submit the idle URB to replace the URB we've just received */ irda_usb_submit(self, skb, self->idle_rx_urb); /* Recycle Rx URB : Now, the idle URB is the present one */ - self->idle_rx_urb = urb; urb->context = NULL; + self->idle_rx_urb = urb; } /*------------------------------------------------------------------*/ @@ -952,13 +960,17 @@ /* Allow IrLAP to send data to us */ netif_start_queue(netdev); + /* We submit all the Rx URB except for one that we keep idle. + * Need to be initialised before submitting other USBs, because + * in some cases as soon as we submit the URBs the USB layer + * will trigger a dummy receive - Jean II */ + self->idle_rx_urb = self->rx_urb[IU_MAX_ACTIVE_RX_URBS]; + self->idle_rx_urb->context = NULL; + /* Now that we can pass data to IrLAP, allow the USB layer * to send us some data... */ for (i = 0; i < IU_MAX_ACTIVE_RX_URBS; i++) - irda_usb_submit(self, NULL, &(self->rx_urb[i])); - /* Note : we submit all the Rx URB except for one - Jean II */ - self->idle_rx_urb = &(self->rx_urb[IU_MAX_ACTIVE_RX_URBS]); - self->idle_rx_urb->context = NULL; + irda_usb_submit(self, NULL, self->rx_urb[i]); /* Ready to play !!! */ MOD_INC_USE_COUNT; @@ -992,7 +1004,7 @@ /* Deallocate all the Rx path buffers (URBs and skb) */ for (i = 0; i < IU_MAX_RX_URBS; i++) { - struct urb *urb = &(self->rx_urb[i]); + struct urb *urb = self->rx_urb[i]; struct sk_buff *skb = (struct sk_buff *) urb->context; /* Cancel the receive command */ usb_unlink_urb(urb); @@ -1003,8 +1015,8 @@ } } /* Cancel Tx and speed URB */ - usb_unlink_urb(&(self->tx_urb)); - usb_unlink_urb(&(self->speed_urb)); + usb_unlink_urb(self->tx_urb); + usb_unlink_urb(self->speed_urb); /* Stop and remove instance of IrLAP */ if (self->irlap) @@ -1429,7 +1441,31 @@ self->present = 0; self->netopen = 0; - /* Is this really necessary? */ + /* Create all of the needed urbs */ + for (i = 0; i < IU_MAX_RX_URBS; i++) { + self->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!self->rx_urb[i]) { + int j; + for (j = 0; j < i; j++) + usb_free_urb(self->rx_urb[j]); + return NULL; + } + } + self->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!self->tx_urb) { + for (i = 0; i < IU_MAX_RX_URBS; i++) + usb_free_urb(self->rx_urb[i]); + return NULL; + } + self->speed_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!self->speed_urb) { + for (i = 0; i < IU_MAX_RX_URBS; i++) + usb_free_urb(self->rx_urb[i]); + usb_free_urb(self->tx_urb); + return NULL; + } + + /* Is this really necessary? */ if (usb_set_configuration (dev, dev->config[0].bConfigurationValue) < 0) { err("set_configuration failed"); return NULL; @@ -1501,10 +1537,10 @@ netif_stop_queue(self->netdev); /* Stop all the receive URBs */ for (i = 0; i < IU_MAX_RX_URBS; i++) - usb_unlink_urb(&(self->rx_urb[i])); + usb_unlink_urb(self->rx_urb[i]); /* Cancel Tx and speed URB */ - usb_unlink_urb(&(self->tx_urb)); - usb_unlink_urb(&(self->speed_urb)); + usb_unlink_urb(self->tx_urb); + usb_unlink_urb(self->speed_urb); IRDA_DEBUG(0, __FUNCTION__ "(), postponing disconnect, network is still active...\n"); /* better not do anything just yet, usb_irda_cleanup() @@ -1516,6 +1552,14 @@ irda_usb_close(self); /* No longer attached to USB bus */ self->usbdev = NULL; + + /* Clean up our urbs */ + for (i = 0; i < IU_MAX_RX_URBS; i++) + usb_free_urb(self->rx_urb[i]); + /* Cancel Tx and speed URB */ + usb_free_urb(self->tx_urb); + usb_free_urb(self->speed_urb); + IRDA_DEBUG(0, __FUNCTION__ "(), USB IrDA Disconnected\n"); } diff -Nru a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c --- a/drivers/net/irda/nsc-ircc.c Thu Mar 7 18:17:36 2002 +++ b/drivers/net/irda/nsc-ircc.c Thu Mar 7 18:17:36 2002 @@ -1570,6 +1570,7 @@ skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); + self->netdev->last_rx = jiffies; } } /* Restore bank register */ diff -Nru a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c --- a/drivers/net/irda/sa1100_ir.c Thu Mar 7 18:17:42 2002 +++ b/drivers/net/irda/sa1100_ir.c Thu Mar 7 18:17:42 2002 @@ -132,7 +132,7 @@ Ser2HSCR0 = si->hscr0 | HSCR0_HSSP; /* - * Enable the DMA, receiver and recieve interrupt. + * Enable the DMA, receiver and receive interrupt. */ sa1100_dma_flush_all(si->rxdma); sa1100_dma_queue_buffer(si->rxdma, NULL, si->rxbuf_dma, HPSIR_MAX_RXLEN); diff -Nru a/drivers/net/irda/smc-ircc.c b/drivers/net/irda/smc-ircc.c --- a/drivers/net/irda/smc-ircc.c Thu Mar 7 18:17:36 2002 +++ b/drivers/net/irda/smc-ircc.c Thu Mar 7 18:17:36 2002 @@ -957,6 +957,7 @@ skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); + self->netdev->last_rx = jiffies; } /* diff -Nru a/drivers/net/irda/toshoboe.c b/drivers/net/irda/toshoboe.c --- a/drivers/net/irda/toshoboe.c Thu Mar 7 18:17:36 2002 +++ b/drivers/net/irda/toshoboe.c Thu Mar 7 18:17:36 2002 @@ -433,8 +433,10 @@ self->rxs++; self->rxs %= RX_SLOTS; - if (skb) + if (skb) { netif_rx (skb); + self->netdev->last_rx = jiffies; + } } diff -Nru a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c --- a/drivers/net/irda/vlsi_ir.c Thu Mar 7 18:17:37 2002 +++ b/drivers/net/irda/vlsi_ir.c Thu Mar 7 18:17:37 2002 @@ -637,6 +637,7 @@ skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); + ndev->last_rx = jiffies; } else { idev->stats.rx_dropped++; @@ -1291,7 +1292,7 @@ name: drivername, id_table: vlsi_irda_table, probe: vlsi_irda_probe, - remove: vlsi_irda_remove, + remove: __devexit_p(vlsi_irda_remove), suspend: vlsi_irda_suspend, resume: vlsi_irda_resume, }; diff -Nru a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c --- a/drivers/net/irda/w83977af_ir.c Thu Mar 7 18:17:42 2002 +++ b/drivers/net/irda/w83977af_ir.c Thu Mar 7 18:17:42 2002 @@ -923,6 +923,7 @@ skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); + self->netdev->last_rx = jiffies; } } /* Restore set register */ diff -Nru a/drivers/net/lne390.c b/drivers/net/lne390.c --- a/drivers/net/lne390.c Thu Mar 7 18:17:46 2002 +++ b/drivers/net/lne390.c Thu Mar 7 18:17:46 2002 @@ -381,9 +381,10 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_LNE_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_LNE_CARDS) "i"); MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_LNE_CARDS) "i"); -MODULE_PARM_DESC(io, "LNE390 I/O base address(es)"); -MODULE_PARM_DESC(irq, "LNE390 IRQ number(s)"); -MODULE_PARM_DESC(mem, "LNE390 memory base address(es)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s)"); +MODULE_PARM_DESC(mem, "memory base address(es)"); +MODULE_DESCRIPTION("Mylex LNE390A/B EISA Ethernet driver"); MODULE_LICENSE("GPL"); int init_module(void) diff -Nru a/drivers/net/lp486e.c b/drivers/net/lp486e.c --- a/drivers/net/lp486e.c Thu Mar 7 18:17:41 2002 +++ b/drivers/net/lp486e.c Thu Mar 7 18:17:41 2002 @@ -676,6 +676,7 @@ skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); + dev->last_rx = jiffies; lp->stats.rx_packets++; } else { #if 0 diff -Nru a/drivers/net/natsemi.c b/drivers/net/natsemi.c --- a/drivers/net/natsemi.c Thu Mar 7 18:17:37 2002 +++ b/drivers/net/natsemi.c Thu Mar 7 18:17:37 2002 @@ -1925,6 +1925,8 @@ /* get link status */ case ETHTOOL_GLINK: { struct ethtool_value edata = {ETHTOOL_GLINK}; + /* LSTATUS is latched low until a read - so read twice */ + mdio_read(dev, 1, MII_BMSR); edata.data = (mdio_read(dev, 1, MII_BMSR)&BMSR_LSTATUS) ? 1:0; if (copy_to_user(useraddr, &edata, sizeof(edata))) return -EFAULT; @@ -2504,7 +2506,7 @@ name: DRV_NAME, id_table: natsemi_pci_tbl, probe: natsemi_probe1, - remove: natsemi_remove1, + remove: __devexit_p(natsemi_remove1), #ifdef CONFIG_PM suspend: natsemi_suspend, resume: natsemi_resume, diff -Nru a/drivers/net/ne.c b/drivers/net/ne.c --- a/drivers/net/ne.c Thu Mar 7 18:17:44 2002 +++ b/drivers/net/ne.c Thu Mar 7 18:17:44 2002 @@ -737,9 +737,11 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); MODULE_PARM(bad, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); -MODULE_PARM_DESC(io, "NEx000 I/O base address(es),required"); -MODULE_PARM_DESC(irq, "NEx000 IRQ number(s)"); -MODULE_PARM_DESC(bad, "NEx000 accept bad clone(s)"); +MODULE_PARM_DESC(io, "I/O base address(es),required"); +MODULE_PARM_DESC(irq, "IRQ number(s)"); +MODULE_PARM_DESC(bad, "Accept card(s) with bad signatures"); +MODULE_DESCRIPTION("NE1000/NE2000 ISA/PnP Ethernet driver"); +MODULE_LICENSE("GPL"); /* This is set up so that no ISA autoprobe takes place. We can't guarantee that the ne2k probe is the last 8390 based probe to take place (as it @@ -791,7 +793,6 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); /* diff -Nru a/drivers/net/ne2k-pci.c b/drivers/net/ne2k-pci.c --- a/drivers/net/ne2k-pci.c Thu Mar 7 18:17:40 2002 +++ b/drivers/net/ne2k-pci.c Thu Mar 7 18:17:40 2002 @@ -82,9 +82,9 @@ MODULE_PARM(debug, "i"); MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM_DESC(debug, "PCI NE2000 debug level (1-2)"); -MODULE_PARM_DESC(options, "PCI NE2000: Bit 5: full duplex"); -MODULE_PARM_DESC(full_duplex, "PCI NE2000 full duplex setting(s) (1)"); +MODULE_PARM_DESC(debug, "debug level (1-2)"); +MODULE_PARM_DESC(options, "Bit 5: full duplex"); +MODULE_PARM_DESC(full_duplex, "full duplex setting(s) (1)"); /* Some defines that people can play with if so inclined. */ @@ -642,7 +642,7 @@ static struct pci_driver ne2k_driver = { name: DRV_NAME, probe: ne2k_pci_init_one, - remove: ne2k_pci_remove_one, + remove: __devexit_p(ne2k_pci_remove_one), id_table: ne2k_pci_tbl, }; diff -Nru a/drivers/net/ne3210.c b/drivers/net/ne3210.c --- a/drivers/net/ne3210.c Thu Mar 7 18:17:42 2002 +++ b/drivers/net/ne3210.c Thu Mar 7 18:17:42 2002 @@ -370,9 +370,11 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NE3210_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_NE3210_CARDS) "i"); MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_NE3210_CARDS) "i"); -MODULE_PARM_DESC(io, "NE3210 I/O base address(es)"); -MODULE_PARM_DESC(irq, "NE3210 IRQ number(s)"); -MODULE_PARM_DESC(mem, "NE3210 memory base address(es)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s)"); +MODULE_PARM_DESC(mem, "memory base address(es)"); +MODULE_DESCRIPTION("NE3210 EISA Ethernet driver"); +MODULE_LICENSE("GPL"); int init_module(void) { @@ -415,7 +417,6 @@ } } } -MODULE_LICENSE("GPL"); #endif /* MODULE */ diff -Nru a/drivers/net/ns83820.c b/drivers/net/ns83820.c --- a/drivers/net/ns83820.c Thu Mar 7 18:17:46 2002 +++ b/drivers/net/ns83820.c Thu Mar 7 18:17:46 2002 @@ -1637,7 +1637,7 @@ name: "ns83820", id_table: ns83820_pci_tbl, probe: ns83820_init_one, - remove: ns83820_remove_one, + remove: __devexit_p(ns83820_remove_one), #if 0 /* FIXME: implement */ suspend: , resume: , diff -Nru a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c --- a/drivers/net/pci-skeleton.c Thu Mar 7 18:17:37 2002 +++ b/drivers/net/pci-skeleton.c Thu Mar 7 18:17:37 2002 @@ -1956,7 +1956,7 @@ name: MODNAME, id_table: netdrv_pci_tbl, probe: netdrv_init_one, - remove: netdrv_remove_one, + remove: __devexit_p(netdrv_remove_one), #ifdef CONFIG_PM suspend: netdrv_suspend, resume: netdrv_resume, diff -Nru a/drivers/net/pcmcia/Config.help b/drivers/net/pcmcia/Config.help --- a/drivers/net/pcmcia/Config.help Thu Mar 7 18:17:41 2002 +++ b/drivers/net/pcmcia/Config.help Thu Mar 7 18:17:41 2002 @@ -133,7 +133,7 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called xircom_tulip_cb.o. If you want to compile + The module will be called xircom_cb.o. If you want to compile it as a module, say M here and read . If unsure, say N. diff -Nru a/drivers/net/pcmcia/Config.in b/drivers/net/pcmcia/Config.in --- a/drivers/net/pcmcia/Config.in Thu Mar 7 18:17:45 2002 +++ b/drivers/net/pcmcia/Config.in Thu Mar 7 18:17:45 2002 @@ -20,11 +20,6 @@ dep_tristate ' IBM PCMCIA tokenring adapter support' CONFIG_PCMCIA_IBMTR $CONFIG_TR $CONFIG_PCMCIA fi - if [ "$CONFIG_CARDBUS" = "y" ]; then - 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 if [ "$CONFIG_NET_PCMCIA_RADIO" = "y" ]; then dep_tristate ' Aviator/Raytheon 2.4MHz wireless support' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA diff -Nru a/drivers/net/pcmcia/Makefile b/drivers/net/pcmcia/Makefile --- a/drivers/net/pcmcia/Makefile Thu Mar 7 18:17:36 2002 +++ b/drivers/net/pcmcia/Makefile Thu Mar 7 18:17:36 2002 @@ -29,11 +29,6 @@ obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o obj-$(CONFIG_AIRONET4500_CS) += aironet4500_cs.o -# 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 - diff -Nru a/drivers/net/pcmcia/xircom_cb.c b/drivers/net/pcmcia/xircom_cb.c --- a/drivers/net/pcmcia/xircom_cb.c Thu Mar 7 18:17:46 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1411 +0,0 @@ -/* - * 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, Donald Becker and Ion Badulescu - * - * 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.11 2001/06/05 09:50:57 fenrus Exp $ - */ - - -#include -#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 "); -MODULE_LICENSE("GPL"); - - - -/* 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 int mdio_read(struct xircom_private *card, int phy_id, int location); -static void mdio_write(struct xircom_private *card, int phy_id, int location, int value); - - - -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 = pci_get_drvdata(pdev); - 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 promiscuous 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. - -Update: the above is not enough. It doesn't touch the MII, in fact it ensures -the main chipset and the MII are never in sync if a full-duplex connection -is negotiated. The proper fix is to tell the MII to force a half-duplex -connection. -Ion - -Must be called in locked state -*/ -static void set_half_duplex(struct xircom_private *card) -{ - unsigned int val; - int rx,tx,tmp; - 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); - - /* tell the MII not to advertise 10/100FDX */ - tmp = mdio_read(card, 0, 4); - printk("xircom_cb: capabilities changed from %#x to %#x\n", - tmp, tmp & ~0x140); - tmp &= ~0x140; - mdio_write(card, 0, 4, tmp); - /* restart autonegotiation */ - tmp = mdio_read(card, 0, 0); - mdio_write(card, 0, 0, tmp | 0x1200); - - 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(); -} - - -/* MII transceiver control section. - Read and write the MII registers using software-generated serial - MDIO protocol. See the MII specifications or DP83840A data sheet - for details. */ - -/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually - met by back-to-back PCI I/O cycles, but we insert a delay to avoid - "overclocking" issues or future 66Mhz PCI. */ -#define mdio_delay() inl(mdio_addr) - -/* Read and write the MII registers using software-generated serial - MDIO protocol. It is just different enough from the EEPROM protocol - to not share code. The maxium data clock rate is 2.5 Mhz. */ -#define MDIO_SHIFT_CLK 0x10000 -#define MDIO_DATA_WRITE0 0x00000 -#define MDIO_DATA_WRITE1 0x20000 -#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */ -#define MDIO_ENB_IN 0x40000 -#define MDIO_DATA_READ 0x80000 - -static int mdio_read(struct xircom_private *card, int phy_id, int location) -{ - int i; - int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; - int retval = 0; - long mdio_addr = card->io_port + CSR9; - - /* Establish sync by sending at least 32 logic ones. */ - for (i = 32; i >= 0; i--) { - outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Shift the read command bits out. */ - for (i = 15; i >= 0; i--) { - int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; - - outl(MDIO_ENB | dataval, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Read the two transition, 16 data, and wire-idle bits. */ - for (i = 19; i > 0; i--) { - outl(MDIO_ENB_IN, mdio_addr); - mdio_delay(); - retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - return (retval>>1) & 0xffff; -} - -static void mdio_write(struct xircom_private *card, int phy_id, int location, int value) -{ - int i; - int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; - long mdio_addr = card->io_port + CSR9; - - /* Establish sync by sending 32 logic ones. */ - for (i = 32; i >= 0; i--) { - outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Shift the command bits out. */ - for (i = 31; i >= 0; i--) { - int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; - outl(MDIO_ENB | dataval, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Clear out extra bits. */ - for (i = 2; i > 0; i--) { - outl(MDIO_ENB_IN, mdio_addr); - mdio_delay(); - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } -} - - -/* - 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 -Nru a/drivers/net/pcmcia/xircom_tulip_cb.c b/drivers/net/pcmcia/xircom_tulip_cb.c --- a/drivers/net/pcmcia/xircom_tulip_cb.c Thu Mar 7 18:17:39 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1744 +0,0 @@ -/* xircom_tulip_cb.c: A Xircom CBE-100 ethernet driver for Linux. */ -/* - Written/copyright 1994-1999 by Donald Becker. - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - ----------------------------------------------------------- - - Linux kernel-specific changes: - - LK1.0 (Ion Badulescu) - - Major cleanup - - Use 2.4 PCI API - - Support ethtool - - Rewrite perfect filter/hash code - - Use interrupts for media changes - - LK1.1 (Ion Badulescu) - - Disallow negotiation of unsupported full-duplex modes -*/ - -#define DRV_NAME "xircom_tulip_cb" -#define DRV_VERSION "0.91+LK1.1" -#define DRV_RELDATE "October 11, 2001" - -#define CARDBUS 1 - -/* A few user-configurable values. */ - -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 25; - -#define MAX_UNITS 4 -/* Used to pass the full-duplex flag, etc. */ -static int full_duplex[MAX_UNITS]; -static int options[MAX_UNITS]; -static int mtu[MAX_UNITS]; /* Jumbo MTU for interfaces. */ - -/* Keep the ring sizes a power of two for efficiency. - Making the Tx ring too large decreases the effectiveness of channel - bonding and packet priority. - There are no ill effects from too-large receive rings. */ -#define TX_RING_SIZE 16 -#define RX_RING_SIZE 32 - -/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ -#ifdef __alpha__ -static int rx_copybreak = 1518; -#else -static int rx_copybreak = 100; -#endif - -/* - Set the bus performance register. - Typical: Set 16 longword cache alignment, no burst limit. - Cache alignment bits 15:14 Burst length 13:8 - 0000 No alignment 0x00000000 unlimited 0800 8 longwords - 4000 8 longwords 0100 1 longword 1000 16 longwords - 8000 16 longwords 0200 2 longwords 2000 32 longwords - C000 32 longwords 0400 4 longwords - Warning: many older 486 systems are broken and require setting 0x00A04800 - 8 longword cache alignment, 8 longword burst. - ToDo: Non-Intel setting could be better. -*/ - -#if defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) -static int csr0 = 0x01A00000 | 0xE000; -#elif defined(__powerpc__) -static int csr0 = 0x01B00000 | 0x8000; -#elif defined(__sparc__) -static int csr0 = 0x01B00080 | 0x8000; -#elif defined(__i386__) -static int csr0 = 0x01A00000 | 0x8000; -#else -#warning Processor architecture undefined! -static int csr0 = 0x00A00000 | 0x4800; -#endif - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (4 * HZ) -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ -#define PKT_SETUP_SZ 192 /* Size of the setup frame */ - -/* PCI registers */ -#define PCI_POWERMGMT 0x40 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include /* Processor type for cache alignment. */ -#include - - -/* These identify the driver base version and may not be removed. */ -static char version[] __devinitdata = -KERN_INFO DRV_NAME ".c derived from tulip.c:v0.91 4/14/99 becker@scyld.com\n" -KERN_INFO " unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE "\n"; - -MODULE_AUTHOR("Donald Becker "); -MODULE_DESCRIPTION("Xircom CBE-100 ethernet driver"); -MODULE_LICENSE("GPL v2"); - -MODULE_PARM(debug, "i"); -MODULE_PARM(max_interrupt_work, "i"); -MODULE_PARM(rx_copybreak, "i"); -MODULE_PARM(csr0, "i"); -MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); - -#define RUN_AT(x) (jiffies + (x)) - -#define xircom_debug debug -#ifdef XIRCOM_DEBUG -static int xircom_debug = XIRCOM_DEBUG; -#else -static int xircom_debug = 1; -#endif - -/* - Theory of Operation - -I. Board Compatibility - -This device driver was forked from the driver for the DECchip "Tulip", -Digital's single-chip ethernet controllers for PCI. It supports Xircom's -almost-Tulip-compatible CBE-100 CardBus adapters. - -II. Board-specific settings - -PCI bus devices are configured by the system at boot time, so no jumpers -need to be set on the board. The system BIOS preferably should assign the -PCI INTA signal to an otherwise unused system IRQ line. - -III. Driver operation - -IIIa. Ring buffers - -The Xircom can use either ring buffers or lists of Tx and Rx descriptors. -This driver uses statically allocated rings of Rx and Tx descriptors, set at -compile time by RX/TX_RING_SIZE. This version of the driver allocates skbuffs -for the Rx ring buffers at open() time and passes the skb->data field to the -Xircom as receive data buffers. When an incoming frame is less than -RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is -copied to the new skbuff. When the incoming frame is larger, the skbuff is -passed directly up the protocol stack and replaced by a newly allocated -skbuff. - -The RX_COPYBREAK value is chosen to trade-off the memory wasted by -using a full-sized skbuff for small frames vs. the copying costs of larger -frames. For small frames the copying cost is negligible (esp. considering -that we are pre-loading the cache with immediately useful header -information). For large frames the copying cost is non-trivial, and the -larger copy might flush the cache of useful data. A subtle aspect of this -choice is that the Xircom only receives into longword aligned buffers, thus -the IP header at offset 14 isn't longword aligned for further processing. -Copied frames are put into the new skbuff at an offset of "+2", thus copying -has the beneficial effect of aligning the IP header and preloading the -cache. - -IIIC. Synchronization -The driver runs as two independent, single-threaded flows of control. One -is the send-packet routine, which enforces single-threaded use by the -dev->tbusy flag. The other thread is the interrupt handler, which is single -threaded by the hardware and other software. - -The send packet thread has partial control over the Tx ring and 'dev->tbusy' -flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next -queue slot is empty, it clears the tbusy flag when finished otherwise it sets -the 'tp->tx_full' flag. - -The interrupt handler has exclusive control over the Rx ring and records stats -from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so -we can't avoid the interrupt overhead by having the Tx routine reap the Tx -stats.) After reaping the stats, it marks the queue entry as empty by setting -the 'base' to zero. Iff the 'tp->tx_full' flag is set, it clears both the -tx_full and tbusy flags. - -IV. Notes - -IVb. References - -http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html -http://www.digital.com (search for current 21*4* datasheets and "21X4 SROM") -http://www.national.com/pf/DP/DP83840A.html - -IVc. Errata - -*/ - -/* A full-duplex map for media types. */ -enum MediaIs { - MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, - MediaIs100=16}; -static const char media_cap[] = -{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20 }; - -/* Offsets to the Command and Status Registers, "CSRs". All accesses - must be longword instructions and quadword aligned. */ -enum xircom_offsets { - CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, - CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, - CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x04, }; - -/* The bits in the CSR5 status registers, mostly interrupt sources. */ -enum status_bits { - LinkChange=0x08000000, - NormalIntr=0x10000, NormalIntrMask=0x00014045, - AbnormalIntr=0x8000, AbnormalIntrMask=0x0a00a5a2, - ReservedIntrMask=0xe0001a18, - EarlyRxIntr=0x4000, BusErrorIntr=0x2000, - EarlyTxIntr=0x400, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40, - TxFIFOUnderflow=0x20, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01, -}; - -enum csr0_control_bits { - EnableMWI=0x01000000, EnableMRL=0x00800000, - EnableMRM=0x00200000, EqualBusPrio=0x02, - SoftwareReset=0x01, -}; - -enum csr6_control_bits { - ReceiveAllBit=0x40000000, AllMultiBit=0x80, PromiscBit=0x40, - HashFilterBit=0x01, FullDuplexBit=0x0200, - TxThresh10=0x400000, TxStoreForw=0x200000, - TxThreshMask=0xc000, TxThreshShift=14, - EnableTx=0x2000, EnableRx=0x02, - ReservedZeroMask=0x8d930134, ReservedOneMask=0x320c0000, - EnableTxRx=(EnableTx | EnableRx), -}; - - -enum tbl_flag { - HAS_MII=1, HAS_ACPI=2, -}; -static struct xircom_chip_table { - char *chip_name; - int valid_intrs; /* CSR7 interrupt enable settings */ - int flags; -} xircom_tbl[] = { - { "Xircom Cardbus Adapter", - LinkChange | NormalIntr | AbnormalIntr | BusErrorIntr | - RxDied | RxNoBuf | RxIntr | TxFIFOUnderflow | TxNoBuf | TxDied | TxIntr, - HAS_MII | HAS_ACPI, }, - { NULL, }, -}; -/* This matches the table above. */ -enum chips { - X3201_3, -}; - - -/* The Xircom Rx and Tx buffer descriptors. */ -struct xircom_rx_desc { - s32 status; - s32 length; - u32 buffer1, buffer2; -}; - -struct xircom_tx_desc { - s32 status; - s32 length; - u32 buffer1, buffer2; /* We use only buffer 1. */ -}; - -enum tx_desc0_status_bits { - Tx0DescOwned=0x80000000, Tx0DescError=0x8000, Tx0NoCarrier=0x0800, - Tx0LateColl=0x0200, Tx0ManyColl=0x0100, Tx0Underflow=0x02, -}; -enum tx_desc1_status_bits { - Tx1ComplIntr=0x80000000, Tx1LastSeg=0x40000000, Tx1FirstSeg=0x20000000, - Tx1SetupPkt=0x08000000, Tx1DisableCRC=0x04000000, Tx1RingWrap=0x02000000, - Tx1ChainDesc=0x01000000, Tx1NoPad=0x800000, Tx1HashSetup=0x400000, - Tx1WholePkt=(Tx1FirstSeg | Tx1LastSeg), -}; -enum rx_desc0_status_bits { - Rx0DescOwned=0x80000000, Rx0DescError=0x8000, Rx0NoSpace=0x4000, - Rx0Runt=0x0800, Rx0McastPkt=0x0400, Rx0FirstSeg=0x0200, Rx0LastSeg=0x0100, - Rx0HugeFrame=0x80, Rx0CRCError=0x02, - Rx0WholePkt=(Rx0FirstSeg | Rx0LastSeg), -}; -enum rx_desc1_status_bits { - Rx1RingWrap=0x02000000, Rx1ChainDesc=0x01000000, -}; - -struct xircom_private { - struct xircom_rx_desc rx_ring[RX_RING_SIZE]; - struct xircom_tx_desc tx_ring[TX_RING_SIZE]; - /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - struct sk_buff* tx_skbuff[TX_RING_SIZE]; -#ifdef CARDBUS - /* The X3201-3 requires 4-byte aligned tx bufs */ - struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE]; -#endif - /* The addresses of receive-in-place skbuffs. */ - struct sk_buff* rx_skbuff[RX_RING_SIZE]; - u16 setup_frame[PKT_SETUP_SZ / sizeof(u16)]; /* Pseudo-Tx frame to init address table. */ - int chip_id; - struct net_device_stats stats; - unsigned int cur_rx, cur_tx; /* The next free ring entry */ - unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ - unsigned int tx_full:1; /* The Tx queue is full. */ - unsigned int speed100:1; - unsigned int full_duplex:1; /* Full-duplex operation requested. */ - unsigned int autoneg:1; - unsigned int default_port:4; /* Last dev->if_port value. */ - unsigned int open:1; - unsigned int csr0; /* CSR0 setting. */ - unsigned int csr6; /* Current CSR6 control settings. */ - u16 to_advertise; /* NWay capabilities advertised. */ - u16 advertising[4]; - signed char phys[4], mii_cnt; /* MII device addresses. */ - int saved_if_port; - struct pci_dev *pdev; - spinlock_t lock; -}; - -static int mdio_read(struct net_device *dev, int phy_id, int location); -static void mdio_write(struct net_device *dev, int phy_id, int location, int value); -static void xircom_up(struct net_device *dev); -static void xircom_down(struct net_device *dev); -static int xircom_open(struct net_device *dev); -static void xircom_tx_timeout(struct net_device *dev); -static void xircom_init_ring(struct net_device *dev); -static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev); -static int xircom_rx(struct net_device *dev); -static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs); -static int xircom_close(struct net_device *dev); -static struct net_device_stats *xircom_get_stats(struct net_device *dev); -static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static void set_rx_mode(struct net_device *dev); -static void check_duplex(struct net_device *dev); - - -/* The Xircom cards are picky about when certain bits in CSR6 can be - manipulated. Keith Owens . */ -static void outl_CSR6(u32 newcsr6, long ioaddr) -{ - const int strict_bits = - TxThresh10 | TxStoreForw | TxThreshMask | EnableTxRx | FullDuplexBit; - int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200; - long flags; - save_flags(flags); - cli(); - /* mask out the reserved bits that always read 0 on the Xircom cards */ - newcsr6 &= ~ReservedZeroMask; - /* or in the reserved bits that always read 1 */ - newcsr6 |= ReservedOneMask; - currcsr6 = inl(ioaddr + CSR6); - if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) || - ((currcsr6 & ~EnableTxRx) == 0)) { - outl(newcsr6, ioaddr + CSR6); /* safe */ - restore_flags(flags); - return; - } - /* make sure the transmitter and receiver are stopped first */ - currcsr6 &= ~EnableTxRx; - while (1) { - csr5 = inl(ioaddr + CSR5); - if (csr5 == 0xffffffff) - break; /* cannot read csr5, card removed? */ - csr5_22_20 = csr5 & 0x700000; - csr5_19_17 = csr5 & 0x0e0000; - if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) && - (csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000)) - break; /* both are stopped or suspended */ - if (!--attempts) { - printk(KERN_INFO DRV_NAME ": outl_CSR6 too many attempts," - "csr5=0x%08x\n", csr5); - outl(newcsr6, ioaddr + CSR6); /* unsafe but do it anyway */ - restore_flags(flags); - return; - } - outl(currcsr6, ioaddr + CSR6); - udelay(1); - } - /* now it is safe to change csr6 */ - outl(newcsr6, ioaddr + CSR6); - restore_flags(flags); -} - - -static void __devinit read_mac_address(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - int i, j; - unsigned char tuple, link, data_id, data_count; - - /* Xircom has its address stored in the CIS; - * we access it through the boot rom interface for now - * this might not work, as the CIS is not parsed but I - * (danilo) use the offset I found on my card's CIS !!! - * - * Doug Ledford: I changed this routine around so that it - * walks the CIS memory space, parsing the config items, and - * finds the proper lan_node_id tuple and uses the data - * stored there. - */ - outl(1 << 12, ioaddr + CSR9); /* enable boot rom access */ - for (i = 0x100; i < 0x1f7; i += link+2) { - outl(i, ioaddr + CSR10); - tuple = inl(ioaddr + CSR9) & 0xff; - outl(i + 1, ioaddr + CSR10); - link = inl(ioaddr + CSR9) & 0xff; - outl(i + 2, ioaddr + CSR10); - data_id = inl(ioaddr + CSR9) & 0xff; - outl(i + 3, ioaddr + CSR10); - data_count = inl(ioaddr + 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, ioaddr + CSR10); - dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff; - } - break; - } else if (link == 0) { - break; - } - } -} - - -/* - * locate the MII interfaces and initialize them. - * we disable full-duplex modes here, - * because we don't know how to handle them. - */ -static void find_mii_transceivers(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - int phy, phy_idx; - - if (media_cap[tp->default_port] & MediaIsMII) { - u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 }; - tp->to_advertise = media2advert[tp->default_port - 9]; - } else - tp->to_advertise = - /*ADVERTISE_100BASE4 | ADVERTISE_100FULL |*/ ADVERTISE_100HALF | - /*ADVERTISE_10FULL |*/ ADVERTISE_10HALF | ADVERTISE_CSMA; - - /* Find the connected MII xcvrs. - Doing this in open() would allow detecting external xcvrs later, - but takes much time. */ - for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { - int mii_status = mdio_read(dev, phy, MII_BMSR); - if ((mii_status & (BMSR_100BASE4 | BMSR_100HALF | BMSR_10HALF)) == BMSR_100BASE4 || - ((mii_status & BMSR_100BASE4) == 0 && - (mii_status & (BMSR_100FULL | BMSR_100HALF | BMSR_10FULL | BMSR_10HALF)) != 0)) { - int mii_reg0 = mdio_read(dev, phy, MII_BMCR); - int mii_advert = mdio_read(dev, phy, MII_ADVERTISE); - int reg4 = ((mii_status >> 6) & tp->to_advertise) | ADVERTISE_CSMA; - tp->phys[phy_idx] = phy; - tp->advertising[phy_idx++] = reg4; - printk(KERN_INFO "%s: MII transceiver #%d " - "config %4.4x status %4.4x advertising %4.4x.\n", - dev->name, phy, mii_reg0, mii_status, mii_advert); - } - } - tp->mii_cnt = phy_idx; - if (phy_idx == 0) { - printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n", - dev->name); - tp->phys[0] = 0; - } -} - - -/* - * To quote Arjan van de Ven: - * 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. - * Actually it seems to go like this: - * - GPIO2 enables the MII itself so we can talk to it. The MII gets reset - * so any prior MII settings are lost. - * - GPIO0 enables the TP port so the MII can talk to the network. - * - a software reset will reset both GPIO pins. - * I also moved the software reset here, because doing it in xircom_up() - * required enabling the GPIO pins each time, which reset the MII each time. - * Thus we couldn't control the MII -- which sucks because we don't know - * how to handle full-duplex modes so we *must* disable them. - */ -static void transceiver_voodoo(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - long ioaddr = dev->base_addr; - - /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ - outl(SoftwareReset, ioaddr + CSR0); - udelay(2); - - /* Deassert reset. */ - outl(tp->csr0, ioaddr + CSR0); - - /* Reset the xcvr interface and turn on heartbeat. */ - outl(0x0008, ioaddr + CSR15); - udelay(5); /* The delays are Xircom-recommended to give the - * chipset time to reset the actual hardware - * on the PCMCIA card - */ - outl(0xa8050000, ioaddr + CSR15); - udelay(5); - outl(0xa00f0000, ioaddr + CSR15); - udelay(5); - - outl_CSR6(0, ioaddr); - //outl_CSR6(FullDuplexBit, ioaddr); -} - - -static int __devinit xircom_init_one(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct net_device *dev; - struct xircom_private *tp; - static int board_idx = -1; - int chip_idx = id->driver_data; - long ioaddr; - int i; - u8 chip_rev; - -/* 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 - - //printk(KERN_INFO "xircom_init_one(%s)\n", pdev->slot_name); - - board_idx++; - - if (pci_enable_device(pdev)) - return -ENODEV; - - pci_set_master(pdev); - - ioaddr = pci_resource_start(pdev, 0); - dev = alloc_etherdev(sizeof(*tp)); - if (!dev) { - printk (KERN_ERR DRV_NAME "%d: cannot alloc etherdev, aborting\n", board_idx); - return -ENOMEM; - } - SET_MODULE_OWNER(dev); - - dev->base_addr = ioaddr; - dev->irq = pdev->irq; - - if (pci_request_regions(pdev, dev->name)) { - printk (KERN_ERR DRV_NAME " %d: cannot reserve PCI resources, aborting\n", board_idx); - goto err_out_free_netdev; - } - - /* Bring the chip out of sleep mode. - Caution: Snooze mode does not work with some boards! */ - if (xircom_tbl[chip_idx].flags & HAS_ACPI) - pci_write_config_dword(pdev, PCI_POWERMGMT, 0); - - /* Stop the chip's Tx and Rx processes. */ - outl_CSR6(inl(ioaddr + CSR6) & ~EnableTxRx, ioaddr); - /* Clear the missed-packet counter. */ - (volatile int)inl(ioaddr + CSR8); - - tp = dev->priv; - - tp->lock = SPIN_LOCK_UNLOCKED; - tp->pdev = pdev; - tp->chip_id = chip_idx; - /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles. */ - /* XXX: is this necessary for Xircom? */ - tp->csr0 = csr0 & ~EnableMWI; - - pci_set_drvdata(pdev, dev); - - /* The lower four bits are the media type. */ - if (board_idx >= 0 && board_idx < MAX_UNITS) { - tp->default_port = options[board_idx] & 15; - if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0) - tp->full_duplex = 1; - if (mtu[board_idx] > 0) - dev->mtu = mtu[board_idx]; - } - if (dev->mem_start) - tp->default_port = dev->mem_start; - if (tp->default_port) { - if (media_cap[tp->default_port] & MediaAlwaysFD) - tp->full_duplex = 1; - } - if (tp->full_duplex) - tp->autoneg = 0; - else - tp->autoneg = 1; - tp->speed100 = 1; - - /* The Xircom-specific entries in the device structure. */ - dev->open = &xircom_open; - dev->hard_start_xmit = &xircom_start_xmit; - dev->stop = &xircom_close; - dev->get_stats = &xircom_get_stats; - dev->do_ioctl = &xircom_ioctl; -#ifdef HAVE_MULTICAST - dev->set_multicast_list = &set_rx_mode; -#endif - dev->tx_timeout = xircom_tx_timeout; - dev->watchdog_timeo = TX_TIMEOUT; - - transceiver_voodoo(dev); - - read_mac_address(dev); - - if (register_netdev(dev)) - goto err_out_cleardev; - - pci_read_config_byte(pdev, PCI_REVISION_ID, &chip_rev); - printk(KERN_INFO "%s: %s rev %d at %#3lx,", - dev->name, xircom_tbl[chip_idx].chip_name, chip_rev, ioaddr); - for (i = 0; i < 6; i++) - printk("%c%2.2X", i ? ':' : ' ', dev->dev_addr[i]); - printk(", IRQ %d.\n", dev->irq); - - if (xircom_tbl[chip_idx].flags & HAS_MII) { - find_mii_transceivers(dev); - check_duplex(dev); - } - - return 0; - -err_out_cleardev: - pci_set_drvdata(pdev, NULL); - pci_release_regions(pdev); -err_out_free_netdev: - unregister_netdev(dev); - kfree(dev); - return -ENODEV; -} - - -/* MII transceiver control section. - Read and write the MII registers using software-generated serial - MDIO protocol. See the MII specifications or DP83840A data sheet - for details. */ - -/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually - met by back-to-back PCI I/O cycles, but we insert a delay to avoid - "overclocking" issues or future 66Mhz PCI. */ -#define mdio_delay() inl(mdio_addr) - -/* Read and write the MII registers using software-generated serial - MDIO protocol. It is just different enough from the EEPROM protocol - to not share code. The maxium data clock rate is 2.5 Mhz. */ -#define MDIO_SHIFT_CLK 0x10000 -#define MDIO_DATA_WRITE0 0x00000 -#define MDIO_DATA_WRITE1 0x20000 -#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */ -#define MDIO_ENB_IN 0x40000 -#define MDIO_DATA_READ 0x80000 - -static int mdio_read(struct net_device *dev, int phy_id, int location) -{ - int i; - int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; - int retval = 0; - long ioaddr = dev->base_addr; - long mdio_addr = ioaddr + CSR9; - - /* Establish sync by sending at least 32 logic ones. */ - for (i = 32; i >= 0; i--) { - outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Shift the read command bits out. */ - for (i = 15; i >= 0; i--) { - int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; - - outl(MDIO_ENB | dataval, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Read the two transition, 16 data, and wire-idle bits. */ - for (i = 19; i > 0; i--) { - outl(MDIO_ENB_IN, mdio_addr); - mdio_delay(); - retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - return (retval>>1) & 0xffff; -} - - -static void mdio_write(struct net_device *dev, int phy_id, int location, int value) -{ - int i; - int cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; - long ioaddr = dev->base_addr; - long mdio_addr = ioaddr + CSR9; - - /* Establish sync by sending 32 logic ones. */ - for (i = 32; i >= 0; i--) { - outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Shift the command bits out. */ - for (i = 31; i >= 0; i--) { - int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; - outl(MDIO_ENB | dataval, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Clear out extra bits. */ - for (i = 2; i > 0; i--) { - outl(MDIO_ENB_IN, mdio_addr); - mdio_delay(); - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - return; -} - - -static void -xircom_up(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - long ioaddr = dev->base_addr; - int i; - - /* Clear the tx ring */ - for (i = 0; i < TX_RING_SIZE; i++) { - tp->tx_skbuff[i] = 0; - tp->tx_ring[i].status = 0; - } - - if (xircom_debug > 1) - printk(KERN_DEBUG "%s: xircom_up() irq %d.\n", dev->name, dev->irq); - - outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3); - outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); - - tp->saved_if_port = dev->if_port; - if (dev->if_port == 0) - dev->if_port = tp->default_port; - - tp->csr6 = TxThresh10 /*| FullDuplexBit*/; /* XXX: why 10 and not 100? */ - - set_rx_mode(dev); - - /* Start the chip's Tx to process setup frame. */ - outl_CSR6(tp->csr6, ioaddr); - outl_CSR6(tp->csr6 | EnableTx, ioaddr); - - /* Acknowledge all outstanding interrupts sources */ - outl(xircom_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5); - /* Enable interrupts by setting the interrupt mask. */ - outl(xircom_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); - /* Enable Rx */ - outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); - /* Rx poll demand */ - outl(0, ioaddr + CSR2); - - /* Tell the net layer we're ready */ - netif_start_queue (dev); - - if (xircom_debug > 2) { - printk(KERN_DEBUG "%s: Done xircom_up(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n", - dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5), - inl(ioaddr + CSR6)); - } -} - - -static int -xircom_open(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - - if (request_irq(dev->irq, &xircom_interrupt, SA_SHIRQ, dev->name, dev)) - return -EAGAIN; - - xircom_init_ring(dev); - - xircom_up(dev); - tp->open = 1; - - return 0; -} - - -static void xircom_tx_timeout(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - long ioaddr = dev->base_addr; - - if (media_cap[dev->if_port] & MediaIsMII) { - /* Do nothing -- the media monitor should handle this. */ - if (xircom_debug > 1) - printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", - dev->name); - } - -#if defined(way_too_many_messages) - if (xircom_debug > 3) { - int i; - for (i = 0; i < RX_RING_SIZE; i++) { - u8 *buf = (u8 *)(tp->rx_ring[i].buffer1); - int j; - printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x " - "%2.2x %2.2x %2.2x.\n", - i, (unsigned int)tp->rx_ring[i].status, - (unsigned int)tp->rx_ring[i].length, - (unsigned int)tp->rx_ring[i].buffer1, - (unsigned int)tp->rx_ring[i].buffer2, - buf[0], buf[1], buf[2]); - for (j = 0; buf[j] != 0xee && j < 1600; j++) - if (j < 100) printk(" %2.2x", buf[j]); - printk(" j=%d.\n", j); - } - printk(KERN_DEBUG " Rx ring %8.8x: ", (int)tp->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); - printk("\n" KERN_DEBUG " Tx ring %8.8x: ", (int)tp->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); - printk("\n"); - } -#endif - - /* Stop and restart the chip's Tx/Rx processes . */ - outl_CSR6(tp->csr6 | EnableRx, ioaddr); - outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); - /* Trigger an immediate transmit demand. */ - outl(0, ioaddr + CSR1); - - dev->trans_start = jiffies; - netif_wake_queue (dev); - tp->stats.tx_errors++; -} - - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void xircom_init_ring(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - int i; - - tp->tx_full = 0; - tp->cur_rx = tp->cur_tx = 0; - tp->dirty_rx = tp->dirty_tx = 0; - - for (i = 0; i < RX_RING_SIZE; i++) { - tp->rx_ring[i].status = 0; - tp->rx_ring[i].length = PKT_BUF_SZ; - tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]); - tp->rx_skbuff[i] = NULL; - } - /* Mark the last entry as wrapping the ring. */ - tp->rx_ring[i-1].length = PKT_BUF_SZ | Rx1RingWrap; - tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]); - - for (i = 0; i < RX_RING_SIZE; i++) { - /* Note the receive buffer must be longword aligned. - dev_alloc_skb() provides 16 byte alignment. But do *not* - use skb_reserve() to align the IP header! */ - struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ); - tp->rx_skbuff[i] = skb; - if (skb == NULL) - break; - skb->dev = dev; /* Mark as being used by this device. */ - tp->rx_ring[i].status = Rx0DescOwned; /* Owned by Xircom chip */ - tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail); - } - tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); - - /* The Tx buffer descriptor is filled in as needed, but we - do need to clear the ownership bit. */ - for (i = 0; i < TX_RING_SIZE; i++) { - tp->tx_skbuff[i] = 0; - tp->tx_ring[i].status = 0; - tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]); -#ifdef CARDBUS - if (tp->chip_id == X3201_3) - tp->tx_aligned_skbuff[i] = dev_alloc_skb(PKT_BUF_SZ); -#endif /* CARDBUS */ - } - tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]); -} - - -static int -xircom_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - int entry; - u32 flag; - - /* Caution: the write order is important here, set the base address - with the "ownership" bits last. */ - - /* Calculate the next Tx descriptor entry. */ - entry = tp->cur_tx % TX_RING_SIZE; - - tp->tx_skbuff[entry] = skb; -#ifdef CARDBUS - if (tp->chip_id == X3201_3) { - memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len); - tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data); - } else -#endif - tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); - - if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ - flag = Tx1WholePkt; /* No interrupt */ - } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { - flag = Tx1WholePkt | Tx1ComplIntr; /* Tx-done intr. */ - } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { - flag = Tx1WholePkt; /* No Tx-done intr. */ - } else { - /* Leave room for set_rx_mode() to fill entries. */ - flag = Tx1WholePkt | Tx1ComplIntr; /* Tx-done intr. */ - tp->tx_full = 1; - } - if (entry == TX_RING_SIZE - 1) - flag |= Tx1WholePkt | Tx1ComplIntr | Tx1RingWrap; - - tp->tx_ring[entry].length = skb->len | flag; - tp->tx_ring[entry].status = Tx0DescOwned; /* Pass ownership to the chip. */ - tp->cur_tx++; - if (tp->tx_full) - netif_stop_queue (dev); - else - netif_wake_queue (dev); - - /* Trigger an immediate transmit demand. */ - outl(0, dev->base_addr + CSR1); - - dev->trans_start = jiffies; - - return 0; -} - - -static void xircom_media_change(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - long ioaddr = dev->base_addr; - u16 reg0, reg1, reg4, reg5; - u32 csr6 = inl(ioaddr + CSR6), newcsr6; - - /* reset status first */ - mdio_read(dev, tp->phys[0], MII_BMCR); - mdio_read(dev, tp->phys[0], MII_BMSR); - - reg0 = mdio_read(dev, tp->phys[0], MII_BMCR); - reg1 = mdio_read(dev, tp->phys[0], MII_BMSR); - - if (reg1 & BMSR_LSTATUS) { - /* link is up */ - if (reg0 & BMCR_ANENABLE) { - /* autonegotiation is enabled */ - reg4 = mdio_read(dev, tp->phys[0], MII_ADVERTISE); - reg5 = mdio_read(dev, tp->phys[0], MII_LPA); - if (reg4 & ADVERTISE_100FULL && reg5 & LPA_100FULL) { - tp->speed100 = 1; - tp->full_duplex = 1; - } else if (reg4 & ADVERTISE_100HALF && reg5 & LPA_100HALF) { - tp->speed100 = 1; - tp->full_duplex = 0; - } else if (reg4 & ADVERTISE_10FULL && reg5 & LPA_10FULL) { - tp->speed100 = 0; - tp->full_duplex = 1; - } else { - tp->speed100 = 0; - tp->full_duplex = 0; - } - } else { - /* autonegotiation is disabled */ - if (reg0 & BMCR_SPEED100) - tp->speed100 = 1; - else - tp->speed100 = 0; - if (reg0 & BMCR_FULLDPLX) - tp->full_duplex = 1; - else - tp->full_duplex = 0; - } - printk(KERN_DEBUG "%s: Link is up, running at %sMbit %s-duplex\n", - dev->name, - tp->speed100 ? "100" : "10", - tp->full_duplex ? "full" : "half"); - newcsr6 = csr6 & ~FullDuplexBit; - if (tp->full_duplex) - newcsr6 |= FullDuplexBit; - if (newcsr6 != csr6) - outl_CSR6(newcsr6, ioaddr + CSR6); - } else { - printk(KERN_DEBUG "%s: Link is down\n", dev->name); - } -} - - -static void check_duplex(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - u16 reg0; - - mdio_write(dev, tp->phys[0], MII_BMCR, BMCR_RESET); - udelay(500); - while (mdio_read(dev, tp->phys[0], MII_BMCR) & BMCR_RESET); - - reg0 = mdio_read(dev, tp->phys[0], MII_BMCR); - mdio_write(dev, tp->phys[0], MII_ADVERTISE, tp->advertising[0]); - - if (tp->autoneg) { - reg0 &= ~(BMCR_SPEED100 | BMCR_FULLDPLX); - reg0 |= BMCR_ANENABLE | BMCR_ANRESTART; - } else { - reg0 &= ~(BMCR_ANENABLE | BMCR_ANRESTART); - if (tp->speed100) - reg0 |= BMCR_SPEED100; - if (tp->full_duplex) - reg0 |= BMCR_FULLDPLX; - printk(KERN_DEBUG "%s: Link forced to %sMbit %s-duplex\n", - dev->name, - tp->speed100 ? "100" : "10", - tp->full_duplex ? "full" : "half"); - } - mdio_write(dev, tp->phys[0], MII_BMCR, reg0); -} - - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ -static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs) -{ - struct net_device *dev = dev_instance; - struct xircom_private *tp = dev->priv; - long ioaddr = dev->base_addr; - int csr5, work_budget = max_interrupt_work; - - spin_lock (&tp->lock); - - do { - csr5 = inl(ioaddr + CSR5); - /* Acknowledge all of the current interrupt sources ASAP. */ - outl(csr5 & 0x0001ffff, ioaddr + CSR5); - - if (xircom_debug > 4) - printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", - dev->name, csr5, inl(dev->base_addr + CSR5)); - - if (csr5 == 0xffffffff) - break; /* all bits set, assume PCMCIA card removed */ - - if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) - break; - - if (csr5 & (RxIntr | RxNoBuf)) - work_budget -= xircom_rx(dev); - - if (csr5 & (TxNoBuf | TxDied | TxIntr)) { - unsigned int dirty_tx; - - for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; - dirty_tx++) { - int entry = dirty_tx % TX_RING_SIZE; - int status = tp->tx_ring[entry].status; - - if (status < 0) - break; /* It still hasn't been Txed */ - /* Check for Rx filter setup frames. */ - if (tp->tx_skbuff[entry] == NULL) - continue; - - if (status & Tx0DescError) { - /* There was an major error, log it. */ -#ifndef final_version - if (xircom_debug > 1) - printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", - dev->name, status); -#endif - tp->stats.tx_errors++; - if (status & Tx0ManyColl) { - tp->stats.tx_aborted_errors++; -#ifdef ETHER_STATS - tp->stats.collisions16++; -#endif - } - if (status & Tx0NoCarrier) tp->stats.tx_carrier_errors++; - if (status & Tx0LateColl) tp->stats.tx_window_errors++; - if (status & Tx0Underflow) tp->stats.tx_fifo_errors++; - } else { - tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff; - tp->stats.collisions += (status >> 3) & 15; - tp->stats.tx_packets++; - } - - /* Free the original skb. */ - dev_kfree_skb_irq(tp->tx_skbuff[entry]); - tp->tx_skbuff[entry] = 0; - } - -#ifndef final_version - if (tp->cur_tx - dirty_tx > TX_RING_SIZE) { - printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", - dev->name, dirty_tx, tp->cur_tx, tp->tx_full); - dirty_tx += TX_RING_SIZE; - } -#endif - - if (tp->tx_full && - tp->cur_tx - dirty_tx < TX_RING_SIZE - 2) - /* The ring is no longer full */ - tp->tx_full = 0; - - if (tp->tx_full) - netif_stop_queue (dev); - else - netif_wake_queue (dev); - - tp->dirty_tx = dirty_tx; - if (csr5 & TxDied) { - if (xircom_debug > 2) - printk(KERN_WARNING "%s: The transmitter stopped." - " CSR5 is %x, CSR6 %x, new CSR6 %x.\n", - dev->name, csr5, inl(ioaddr + CSR6), tp->csr6); - outl_CSR6(tp->csr6 | EnableRx, ioaddr); - outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); - } - } - - /* Log errors. */ - if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */ - if (csr5 & LinkChange) - xircom_media_change(dev); - if (csr5 & TxFIFOUnderflow) { - if ((tp->csr6 & TxThreshMask) != TxThreshMask) - tp->csr6 += (1 << TxThreshShift); /* Bump up the Tx threshold */ - else - tp->csr6 |= TxStoreForw; /* Store-n-forward. */ - /* Restart the transmit process. */ - outl_CSR6(tp->csr6 | EnableRx, ioaddr); - outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); - } - if (csr5 & RxDied) { /* Missed a Rx frame. */ - tp->stats.rx_errors++; - tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; - outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); - } - /* Clear all error sources, included undocumented ones! */ - outl(0x0800f7ba, ioaddr + CSR5); - } - if (--work_budget < 0) { - if (xircom_debug > 1) - printk(KERN_WARNING "%s: Too much work during an interrupt, " - "csr5=0x%8.8x.\n", dev->name, csr5); - /* Acknowledge all interrupt sources. */ - outl(0x8001ffff, ioaddr + CSR5); - break; - } - } while (1); - - if (xircom_debug > 3) - printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", - dev->name, inl(ioaddr + CSR5)); - - spin_unlock (&tp->lock); -} - - -static int -xircom_rx(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - int entry = tp->cur_rx % RX_RING_SIZE; - int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; - int work_done = 0; - - if (xircom_debug > 4) - printk(KERN_DEBUG " In xircom_rx(), entry %d %8.8x.\n", entry, - tp->rx_ring[entry].status); - /* If we own the next entry, it's a new packet. Send it up. */ - while (tp->rx_ring[entry].status >= 0) { - s32 status = tp->rx_ring[entry].status; - - if (xircom_debug > 5) - printk(KERN_DEBUG " In xircom_rx(), entry %d %8.8x.\n", entry, - tp->rx_ring[entry].status); - if (--rx_work_limit < 0) - break; - if ((status & 0x38008300) != 0x0300) { - if ((status & 0x38000300) != 0x0300) { - /* Ignore earlier buffers. */ - if ((status & 0xffff) != 0x7fff) { - if (xircom_debug > 1) - printk(KERN_WARNING "%s: Oversized Ethernet frame " - "spanned multiple buffers, status %8.8x!\n", - dev->name, status); - tp->stats.rx_length_errors++; - } - } else if (status & Rx0DescError) { - /* There was a fatal error. */ - if (xircom_debug > 2) - printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", - dev->name, status); - tp->stats.rx_errors++; /* end of a packet.*/ - if (status & (Rx0Runt | Rx0HugeFrame)) tp->stats.rx_length_errors++; - if (status & Rx0CRCError) tp->stats.rx_crc_errors++; - } - } else { - /* Omit the four octet CRC from the length. */ - short pkt_len = ((status >> 16) & 0x7ff) - 4; - struct sk_buff *skb; - -#ifndef final_version - if (pkt_len > 1518) { - printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n", - dev->name, pkt_len, pkt_len); - pkt_len = 1518; - tp->stats.rx_length_errors++; - } -#endif - /* Check if the packet is long enough to accept without copying - to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak - && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; - skb_reserve(skb, 2); /* 16 byte align the IP header */ -#if ! defined(__alpha__) - eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), - pkt_len, 0); - skb_put(skb, pkt_len); -#else - memcpy(skb_put(skb, pkt_len), - bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); -#endif - work_done++; - } else { /* Pass up the skb already on the Rx ring. */ - skb_put(skb = tp->rx_skbuff[entry], pkt_len); - tp->rx_skbuff[entry] = NULL; - } - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->last_rx = jiffies; - tp->stats.rx_packets++; - tp->stats.rx_bytes += pkt_len; - } - entry = (++tp->cur_rx) % RX_RING_SIZE; - } - - /* Refill the Rx ring buffers. */ - for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { - entry = tp->dirty_rx % RX_RING_SIZE; - if (tp->rx_skbuff[entry] == NULL) { - struct sk_buff *skb; - skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ); - if (skb == NULL) - break; - skb->dev = dev; /* Mark as being used by this device. */ - tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail); - work_done++; - } - tp->rx_ring[entry].status = Rx0DescOwned; - } - - return work_done; -} - - -static void -xircom_down(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - struct xircom_private *tp = dev->priv; - - /* Disable interrupts by clearing the interrupt mask. */ - outl(0, ioaddr + CSR7); - /* Stop the chip's Tx and Rx processes. */ - outl_CSR6(inl(ioaddr + CSR6) & ~EnableTxRx, ioaddr); - - if (inl(ioaddr + CSR6) != 0xffffffff) - tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; - - dev->if_port = tp->saved_if_port; -} - - -static int -xircom_close(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - struct xircom_private *tp = dev->priv; - int i; - - if (xircom_debug > 1) - printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n", - dev->name, inl(ioaddr + CSR5)); - - netif_stop_queue(dev); - - if (netif_device_present(dev)) - xircom_down(dev); - - free_irq(dev->irq, dev); - - /* Free all the skbuffs in the Rx queue. */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = tp->rx_skbuff[i]; - tp->rx_skbuff[i] = 0; - tp->rx_ring[i].status = 0; /* Not owned by Xircom chip. */ - tp->rx_ring[i].length = 0; - tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */ - if (skb) { - dev_kfree_skb(skb); - } - } - for (i = 0; i < TX_RING_SIZE; i++) { - if (tp->tx_skbuff[i]) - dev_kfree_skb(tp->tx_skbuff[i]); - tp->tx_skbuff[i] = 0; - } - - tp->open = 0; - return 0; -} - - -static struct net_device_stats *xircom_get_stats(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - long ioaddr = dev->base_addr; - - if (netif_device_present(dev)) - tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; - - return &tp->stats; -} - - -static int xircom_ethtool_ioctl(struct net_device *dev, void *useraddr) -{ - struct ethtool_cmd ecmd; - struct xircom_private *tp = dev->priv; - - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - - switch (ecmd.cmd) { - case ETHTOOL_GSET: - ecmd.supported = - SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_Autoneg | - SUPPORTED_MII; - - ecmd.advertising = ADVERTISED_MII; - if (tp->advertising[0] & ADVERTISE_10HALF) - ecmd.advertising |= ADVERTISED_10baseT_Half; - if (tp->advertising[0] & ADVERTISE_10FULL) - ecmd.advertising |= ADVERTISED_10baseT_Full; - if (tp->advertising[0] & ADVERTISE_100HALF) - ecmd.advertising |= ADVERTISED_100baseT_Half; - if (tp->advertising[0] & ADVERTISE_100FULL) - ecmd.advertising |= ADVERTISED_100baseT_Full; - if (tp->autoneg) { - ecmd.advertising |= ADVERTISED_Autoneg; - ecmd.autoneg = AUTONEG_ENABLE; - } else - ecmd.autoneg = AUTONEG_DISABLE; - - ecmd.port = PORT_MII; - ecmd.transceiver = XCVR_INTERNAL; - ecmd.phy_address = tp->phys[0]; - ecmd.speed = tp->speed100 ? SPEED_100 : SPEED_10; - ecmd.duplex = tp->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; - ecmd.maxtxpkt = TX_RING_SIZE / 2; - ecmd.maxrxpkt = 0; - - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - - case ETHTOOL_SSET: { - u16 autoneg, speed100, full_duplex; - - autoneg = (ecmd.autoneg == AUTONEG_ENABLE); - speed100 = (ecmd.speed == SPEED_100); - full_duplex = (ecmd.duplex == DUPLEX_FULL); - - tp->autoneg = autoneg; - if (speed100 != tp->speed100 || - full_duplex != tp->full_duplex) { - tp->speed100 = speed100; - tp->full_duplex = full_duplex; - /* change advertising bits */ - tp->advertising[0] &= ~(ADVERTISE_10HALF | - ADVERTISE_10FULL | - ADVERTISE_100HALF | - ADVERTISE_100FULL | - ADVERTISE_100BASE4); - if (speed100) { - if (full_duplex) - tp->advertising[0] |= ADVERTISE_100FULL; - else - tp->advertising[0] |= ADVERTISE_100HALF; - } else { - if (full_duplex) - tp->advertising[0] |= ADVERTISE_10FULL; - else - tp->advertising[0] |= ADVERTISE_10HALF; - } - } - check_duplex(dev); - return 0; - } - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info; - memset(&info, 0, sizeof(info)); - info.cmd = ecmd.cmd; - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - *info.fw_version = 0; - strcpy(info.bus_info, tp->pdev->slot_name); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - default: - return -EOPNOTSUPP; - } -} - - -/* Provide ioctl() calls to examine the MII xcvr state. */ -static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct xircom_private *tp = dev->priv; - u16 *data = (u16 *)&rq->ifr_data; - int phy = tp->phys[0] & 0x1f; - long flags; - - switch(cmd) { - case SIOCETHTOOL: - return xircom_ethtool_ioctl(dev, (void *) rq->ifr_data); - - /* Legacy mii-diag interface */ - case SIOCGMIIPHY: /* Get address of MII PHY in use. */ - if (tp->mii_cnt) - data[0] = phy; - else - return -ENODEV; - return 0; - case SIOCGMIIREG: /* Read MII PHY register. */ - save_flags(flags); - cli(); - data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); - restore_flags(flags); - return 0; - case SIOCSMIIREG: /* Write MII PHY register. */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - save_flags(flags); - cli(); - if (data[0] == tp->phys[0]) { - u16 value = data[2]; - switch (data[1]) { - case 0: - if (value & (BMCR_RESET | BMCR_ANENABLE)) - /* Autonegotiation. */ - tp->autoneg = 1; - else { - tp->full_duplex = (value & BMCR_FULLDPLX) ? 1 : 0; - tp->autoneg = 0; - } - break; - case 4: - tp->advertising[0] = value; - break; - } - check_duplex(dev); - } - mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); - restore_flags(flags); - return 0; - default: - return -EOPNOTSUPP; - } - - return -EOPNOTSUPP; -} - -/* Set or clear the multicast filter for this adaptor. - Note that we only use exclusion around actually queueing the - new frame, not around filling tp->setup_frame. This is non-deterministic - when re-entered but still correct. */ -static void set_rx_mode(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - struct dev_mc_list *mclist; - long ioaddr = dev->base_addr; - int csr6 = inl(ioaddr + CSR6); - u16 *eaddrs, *setup_frm; - u32 tx_flags; - int i; - - tp->csr6 &= ~(AllMultiBit | PromiscBit | HashFilterBit); - csr6 &= ~(AllMultiBit | PromiscBit | HashFilterBit); - if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ - tp->csr6 |= PromiscBit; - csr6 |= PromiscBit; - goto out; - } - - if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { - /* Too many to filter well -- accept all multicasts. */ - tp->csr6 |= AllMultiBit; - csr6 |= AllMultiBit; - goto out; - } - - tx_flags = Tx1WholePkt | Tx1SetupPkt | PKT_SETUP_SZ; - - /* Note that only the low-address shortword of setup_frame is valid! */ - setup_frm = tp->setup_frame; - mclist = dev->mc_list; - - /* Fill the first entry with our physical address. */ - eaddrs = (u16 *)dev->dev_addr; - *setup_frm = cpu_to_le16(eaddrs[0]); setup_frm += 2; - *setup_frm = cpu_to_le16(eaddrs[1]); setup_frm += 2; - *setup_frm = cpu_to_le16(eaddrs[2]); setup_frm += 2; - - if (dev->mc_count > 14) { /* Must use a multicast hash table. */ - u32 *hash_table = (u32 *)(tp->setup_frame + 4 * 12); - u32 hash, hash2; - - tx_flags |= Tx1HashSetup; - tp->csr6 |= HashFilterBit; - csr6 |= HashFilterBit; - - /* Fill the unused 3 entries with the broadcast address. - At least one entry *must* contain the broadcast address!!!*/ - for (i = 0; i < 3; i++) { - *setup_frm = 0xffff; setup_frm += 2; - *setup_frm = 0xffff; setup_frm += 2; - *setup_frm = 0xffff; setup_frm += 2; - } - - /* Truly brain-damaged hash filter layout */ - /* XXX: not sure if I should take the last or the first 9 bits */ - for (i = 0; i < dev->mc_count; i++, mclist = mclist->next) { - u32 *hptr; - hash = ether_crc(ETH_ALEN, mclist->dmi_addr) & 0x1ff; - if (hash < 384) { - hash2 = hash + ((hash >> 4) << 4) + - ((hash >> 5) << 5); - } else { - hash -= 384; - hash2 = 64 + hash + (hash >> 4) * 80; - } - hptr = &hash_table[hash2 & ~0x1f]; - *hptr |= cpu_to_le32(1 << (hash2 & 0x1f)); - } - } else { - /* We have <= 14 mcast addresses so we can use Xircom's - wonderful 16-address perfect filter. */ - for (i = 0; i < dev->mc_count; i++, mclist = mclist->next) { - eaddrs = (u16 *)mclist->dmi_addr; - *setup_frm = cpu_to_le16(eaddrs[0]); setup_frm += 2; - *setup_frm = cpu_to_le16(eaddrs[1]); setup_frm += 2; - *setup_frm = cpu_to_le16(eaddrs[2]); setup_frm += 2; - } - /* Fill the unused entries with the broadcast address. - At least one entry *must* contain the broadcast address!!!*/ - for (; i < 15; i++) { - *setup_frm = 0xffff; setup_frm += 2; - *setup_frm = 0xffff; setup_frm += 2; - *setup_frm = 0xffff; setup_frm += 2; - } - } - - /* Now add this frame to the Tx list. */ - if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { - /* Same setup recently queued, we need not add it. */ - /* XXX: Huh? All it means is that the Tx list is full...*/ - } else { - unsigned long flags; - unsigned int entry; - int dummy = -1; - - save_flags(flags); cli(); - entry = tp->cur_tx++ % TX_RING_SIZE; - - if (entry != 0) { - /* Avoid a chip errata by prefixing a dummy entry. */ - tp->tx_skbuff[entry] = 0; - tp->tx_ring[entry].length = - (entry == TX_RING_SIZE - 1) ? Tx1RingWrap : 0; - tp->tx_ring[entry].buffer1 = 0; - /* race with chip, set Tx0DescOwned later */ - dummy = entry; - entry = tp->cur_tx++ % TX_RING_SIZE; - } - - tp->tx_skbuff[entry] = 0; - /* Put the setup frame on the Tx list. */ - if (entry == TX_RING_SIZE - 1) - tx_flags |= Tx1RingWrap; /* Wrap ring. */ - tp->tx_ring[entry].length = tx_flags; - tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame); - tp->tx_ring[entry].status = Tx0DescOwned; - if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) { - tp->tx_full = 1; - netif_stop_queue (dev); - } - if (dummy >= 0) - tp->tx_ring[dummy].status = Tx0DescOwned; - restore_flags(flags); - /* Trigger an immediate transmit demand. */ - outl(0, ioaddr + CSR1); - } - -out: - outl_CSR6(csr6, ioaddr); -} - - -static struct pci_device_id xircom_pci_table[] __devinitdata = { - { 0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 }, - {0}, -}; -MODULE_DEVICE_TABLE(pci, xircom_pci_table); - - -#ifdef CONFIG_PM -static int xircom_suspend(struct pci_dev *pdev, u32 state) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct xircom_private *tp = dev->priv; - printk(KERN_INFO "xircom_suspend(%s)\n", dev->name); - if (tp->open) - xircom_down(dev); - return 0; -} - - -static int xircom_resume(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct xircom_private *tp = dev->priv; - printk(KERN_INFO "xircom_resume(%s)\n", dev->name); - - /* Bring the chip out of sleep mode. - Caution: Snooze mode does not work with some boards! */ - if (xircom_tbl[tp->chip_id].flags & HAS_ACPI) - pci_write_config_dword(tp->pdev, PCI_POWERMGMT, 0); - - transceiver_voodoo(dev); - if (xircom_tbl[tp->chip_id].flags & HAS_MII) - check_duplex(dev); - - if (tp->open) - xircom_up(dev); - return 0; -} -#endif /* CONFIG_PM */ - - -static void __devexit xircom_remove_one(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - - printk(KERN_INFO "xircom_remove_one(%s)\n", dev->name); - unregister_netdev(dev); - pci_release_regions(pdev); - kfree(dev); - pci_set_drvdata(pdev, NULL); -} - - -static struct pci_driver xircom_driver = { - name: DRV_NAME, - id_table: xircom_pci_table, - probe: xircom_init_one, - remove: xircom_remove_one, -#ifdef CONFIG_PM - suspend: xircom_suspend, - resume: xircom_resume -#endif /* CONFIG_PM */ -}; - - -static int __init xircom_init(void) -{ -/* when a module, this is printed whether or not devices are found in probe */ -#ifdef MODULE - printk(version); -#endif - return pci_module_init(&xircom_driver); -} - - -static void __exit xircom_exit(void) -{ - pci_unregister_driver(&xircom_driver); -} - -module_init(xircom_init) -module_exit(xircom_exit) - -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 4 - * End: - */ diff -Nru a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c --- a/drivers/net/pcnet32.c Thu Mar 7 18:17:41 2002 +++ b/drivers/net/pcnet32.c Thu Mar 7 18:17:41 2002 @@ -22,8 +22,9 @@ *************************************************************************/ #define DRV_NAME "pcnet32" -#define DRV_VERSION "1.25kf" -#define DRV_RELDATE "17.11.2001" +#define DRV_VERSION "1.27a" +#define DRV_RELDATE "10.02.2002" +#define PFX DRV_NAME ": " static const char *version = DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " tsbogend@alpha.franken.de\n"; @@ -54,8 +55,6 @@ #include #include -static unsigned int pcnet32_portlist[] __initdata = {0x300, 0x320, 0x340, 0x360, 0}; - /* * PCI device identifiers for "new style" Linux PCI Device Drivers */ @@ -65,13 +64,26 @@ { 0, } }; +MODULE_DEVICE_TABLE (pci, pcnet32_pci_tbl); + +int cards_found __initdata; + +/* + * VLB I/O addresses + */ +static unsigned int pcnet32_portlist[] __initdata = + { 0x300, 0x320, 0x340, 0x360, 0 }; + + + static int pcnet32_debug = 1; static int tx_start = 1; /* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */ +static int pcnet32vlb; /* check for VLB cards ? */ static struct net_device *pcnet32_dev; -static const int max_interrupt_work = 80; -static const int rx_copybreak = 200; +static int max_interrupt_work = 80; +static int rx_copybreak = 200; #define PCNET32_PORT_AUI 0x00 #define PCNET32_PORT_10BT 0x01 @@ -94,21 +106,21 @@ PCNET32_PORT_AUI, /* 1 BNC/AUI */ PCNET32_PORT_AUI, /* 2 AUI/BNC */ PCNET32_PORT_ASEL, /* 3 not supported */ - PCNET32_PORT_10BT | PCNET32_PORT_FD, /* 4 10baseT-FD */ + PCNET32_PORT_10BT | PCNET32_PORT_FD, /* 4 10baseT-FD */ PCNET32_PORT_ASEL, /* 5 not supported */ PCNET32_PORT_ASEL, /* 6 not supported */ PCNET32_PORT_ASEL, /* 7 not supported */ PCNET32_PORT_ASEL, /* 8 not supported */ PCNET32_PORT_MII, /* 9 MII 10baseT */ - PCNET32_PORT_MII | PCNET32_PORT_FD, /* 10 MII 10baseT-FD */ + PCNET32_PORT_MII | PCNET32_PORT_FD, /* 10 MII 10baseT-FD */ PCNET32_PORT_MII, /* 11 MII (autosel) */ PCNET32_PORT_10BT, /* 12 10BaseT */ - PCNET32_PORT_MII | PCNET32_PORT_100, /* 13 MII 100BaseTx */ + PCNET32_PORT_MII | PCNET32_PORT_100, /* 13 MII 100BaseTx */ PCNET32_PORT_MII | PCNET32_PORT_100 | PCNET32_PORT_FD, /* 14 MII 100BaseTx-FD */ PCNET32_PORT_ASEL /* 15 not supported */ }; -#define MAX_UNITS 8 +#define MAX_UNITS 8 /* More are supported, limit only on options */ static int options[MAX_UNITS]; static int full_duplex[MAX_UNITS]; @@ -187,7 +199,19 @@ * v1.25kf Added No Interrupt on successful Tx for some Tx's * v1.26 Converted to pci_alloc_consistent, Jamey Hicks / George France * - * v1.26p Fix oops on rmmod+insmod; plug i/o resource leak - Paul Gortmaker + * - Fixed a few bugs, related to running the controller in 32bit mode. + * 23 Oct, 2000. Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. + * v1.26p Fix oops on rmmod+insmod; plug i/o resource leak - Paul Gortmaker + * v1.27 improved CSR/PROM address detection, lots of cleanups, + * new pcnet32vlb module option, HP-PARISC support, + * added module parameter descriptions, + * initial ethtool support - Helge Deller + * v1.27a Sun Feb 10 2002 Go Taniguchi + * use alloc_etherdev and register_netdev + * fix pci probe not increment cards_found + * FD auto negotiate error workaround for xSeries250 + * clean up and using new mii module */ @@ -201,13 +225,13 @@ #define PCNET32_LOG_RX_BUFFERS 5 #endif -#define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS)) -#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) -#define TX_RING_LEN_BITS ((PCNET32_LOG_TX_BUFFERS) << 12) - -#define RX_RING_SIZE (1 << (PCNET32_LOG_RX_BUFFERS)) -#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) -#define RX_RING_LEN_BITS ((PCNET32_LOG_RX_BUFFERS) << 4) +#define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS)) +#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) +#define TX_RING_LEN_BITS ((PCNET32_LOG_TX_BUFFERS) << 12) + +#define RX_RING_SIZE (1 << (PCNET32_LOG_RX_BUFFERS)) +#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) +#define RX_RING_LEN_BITS ((PCNET32_LOG_RX_BUFFERS) << 4) #define PKT_BUF_SZ 1544 @@ -222,7 +246,7 @@ #define PCNET32_DWIO_RESET 0x18 #define PCNET32_DWIO_BDP 0x1C -#define PCNET32_TOTAL_SIZE 0x20 +#define PCNET32_TOTAL_SIZE 0x20 /* The PCNET32 Rx and Tx ring descriptors. */ struct pcnet32_rx_head { @@ -270,37 +294,36 @@ */ struct pcnet32_private { /* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */ - struct pcnet32_rx_head rx_ring[RX_RING_SIZE]; - struct pcnet32_tx_head tx_ring[TX_RING_SIZE]; - struct pcnet32_init_block init_block; - dma_addr_t dma_addr; /* DMA address of beginning of this object, returned by pci_alloc_consistent */ - struct pci_dev *pci_dev; /* Pointer to the associated pci device structure */ - const char *name; + struct pcnet32_rx_head rx_ring[RX_RING_SIZE]; + struct pcnet32_tx_head tx_ring[TX_RING_SIZE]; + struct pcnet32_init_block init_block; + dma_addr_t dma_addr; /* DMA address of beginning of this object, + returned by pci_alloc_consistent */ + struct pci_dev *pci_dev; /* Pointer to the associated pci device structure */ + const char *name; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - struct sk_buff *tx_skbuff[TX_RING_SIZE]; - struct sk_buff *rx_skbuff[RX_RING_SIZE]; - dma_addr_t tx_dma_addr[TX_RING_SIZE]; - dma_addr_t rx_dma_addr[RX_RING_SIZE]; + struct sk_buff *tx_skbuff[TX_RING_SIZE]; + struct sk_buff *rx_skbuff[RX_RING_SIZE]; + dma_addr_t tx_dma_addr[TX_RING_SIZE]; + dma_addr_t rx_dma_addr[RX_RING_SIZE]; struct pcnet32_access a; - spinlock_t lock; /* Guard lock */ - unsigned int cur_rx, cur_tx; /* The next free ring entry */ - unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + spinlock_t lock; /* Guard lock */ + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ struct net_device_stats stats; - char tx_full; - int options; - int shared_irq:1, /* shared irq possible */ - ltint:1, -#ifdef DO_DXSUFLO - dxsuflo:1, /* disable transmit stop on uflo */ -#endif - mii:1; /* mii port available */ - struct net_device *next; + char tx_full; + int options; + int shared_irq:1, /* shared irq possible */ + ltint:1, /* enable TxDone-intr inhibitor */ + dxsuflo:1, /* disable transmit stop on uflo */ + mii:1; /* mii port available */ + struct net_device *next; struct mii_if_info mii_if; }; -static int pcnet32_probe_vlbus(int cards_found); +static void pcnet32_probe_vlbus(void); static int pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *); -static int pcnet32_probe1(unsigned long, unsigned char, int, int, struct pci_dev *); +static int pcnet32_probe1(unsigned long, unsigned char, int, struct pci_dev *); static int pcnet32_open(struct net_device *); static int pcnet32_init_ring(struct net_device *); static int pcnet32_start_xmit(struct sk_buff *, struct net_device *); @@ -319,15 +342,6 @@ PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3, }; -struct pcnet32_pci_id_info { - const char *name; - u16 vendor_id, device_id, svid, sdid, flags; - int io_size; - int (*probe1) (unsigned long, unsigned char, int, int, struct pci_dev *); -}; - - -MODULE_DEVICE_TABLE (pci, pcnet32_pci_tbl); static u16 pcnet32_wio_read_csr (unsigned long addr, int index) { @@ -375,13 +389,13 @@ } static struct pcnet32_access pcnet32_wio = { - pcnet32_wio_read_csr, - pcnet32_wio_write_csr, - pcnet32_wio_read_bcr, - pcnet32_wio_write_bcr, - pcnet32_wio_read_rap, - pcnet32_wio_write_rap, - pcnet32_wio_reset + read_csr: pcnet32_wio_read_csr, + write_csr: pcnet32_wio_write_csr, + read_bcr: pcnet32_wio_read_bcr, + write_bcr: pcnet32_wio_write_bcr, + read_rap: pcnet32_wio_read_rap, + write_rap: pcnet32_wio_write_rap, + reset: pcnet32_wio_reset }; static u16 pcnet32_dwio_read_csr (unsigned long addr, int index) @@ -430,82 +444,61 @@ } static struct pcnet32_access pcnet32_dwio = { - pcnet32_dwio_read_csr, - pcnet32_dwio_write_csr, - pcnet32_dwio_read_bcr, - pcnet32_dwio_write_bcr, - pcnet32_dwio_read_rap, - pcnet32_dwio_write_rap, - pcnet32_dwio_reset - + read_csr: pcnet32_dwio_read_csr, + write_csr: pcnet32_dwio_write_csr, + read_bcr: pcnet32_dwio_read_bcr, + write_bcr: pcnet32_dwio_write_bcr, + read_rap: pcnet32_dwio_read_rap, + write_rap: pcnet32_dwio_write_rap, + reset: pcnet32_dwio_reset }; - -/* only probes for non-PCI devices, the rest are handled by pci_register_driver via pcnet32_probe_pci*/ -static int __init pcnet32_probe_vlbus(int cards_found) + +/* only probes for non-PCI devices, the rest are handled by + * pci_register_driver via pcnet32_probe_pci */ + +static void __devinit +pcnet32_probe_vlbus(void) { - unsigned long ioaddr = 0; // FIXME dev ? dev->base_addr: 0; - unsigned int irq_line = 0; // FIXME dev ? dev->irq : 0; - int *port; + unsigned int *port, ioaddr; - printk(KERN_INFO "pcnet32_probe_vlbus: cards_found=%d\n", cards_found); -#ifndef __powerpc__ - if (ioaddr > 0x1ff) { - if (check_region(ioaddr, PCNET32_TOTAL_SIZE) == 0) - return pcnet32_probe1(ioaddr, irq_line, 0, 0, NULL); - else - return -ENODEV; - } else -#endif - if (ioaddr != 0) - return -ENXIO; - - /* now look for PCnet32 VLB cards */ - for (port = pcnet32_portlist; *port; port++) { - unsigned long ioaddr = *port; - - if ( check_region(ioaddr, PCNET32_TOTAL_SIZE) == 0) { + /* search for PCnet32 VLB cards at known addresses */ + for (port = pcnet32_portlist; (ioaddr = *port); port++) { + if (!check_region(ioaddr, PCNET32_TOTAL_SIZE)) { /* check if there is really a pcnet chip on that ioaddr */ - if ((inb(ioaddr + 14) == 0x57) && - (inb(ioaddr + 15) == 0x57) && - (pcnet32_probe1(ioaddr, 0, 0, 0, NULL) == 0)) - cards_found++; + if ((inb(ioaddr + 14) == 0x57) && (inb(ioaddr + 15) == 0x57)) + pcnet32_probe1(ioaddr, 0, 0, NULL); } } - return cards_found ? 0: -ENODEV; } - static int __devinit pcnet32_probe_pci(struct pci_dev *pdev, const struct pci_device_id *ent) { - static int card_idx; - long ioaddr; - int err = 0; - - printk(KERN_INFO "pcnet32_probe_pci: found device %#08x.%#08x\n", ent->vendor, ent->device); + unsigned long ioaddr; + int err; - if ((err = pci_enable_device(pdev)) < 0) { - printk(KERN_ERR "pcnet32.c: failed to enable device -- err=%d\n", err); + err = pci_enable_device(pdev); + if (err < 0) { + printk(KERN_ERR PFX "failed to enable device -- err=%d\n", err); return err; } pci_set_master(pdev); ioaddr = pci_resource_start (pdev, 0); - printk(KERN_INFO " ioaddr=%#08lx resource_flags=%#08lx\n", ioaddr, pci_resource_flags (pdev, 0)); if (!ioaddr) { - printk (KERN_ERR "no PCI IO resources, aborting\n"); + printk (KERN_ERR PFX "card has no PCI IO resources, aborting\n"); return -ENODEV; } - + if (!pci_dma_supported(pdev, PCNET32_DMA_MASK)) { - printk(KERN_ERR "pcnet32.c: architecture does not support 32bit PCI busmaster DMA\n"); + printk(KERN_ERR PFX "architecture does not support 32bit PCI busmaster DMA\n"); return -ENODEV; } - return pcnet32_probe1(ioaddr, pdev->irq, 1, card_idx, pdev); + return pcnet32_probe1(ioaddr, pdev->irq, 1, pdev); } @@ -514,41 +507,44 @@ * pdev will be NULL when called from pcnet32_probe_vlbus. */ static int __devinit -pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int card_idx, struct pci_dev *pdev) +pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, + struct pci_dev *pdev) { struct pcnet32_private *lp; struct resource *res; dma_addr_t lp_dma_addr; - int i,media,fdx = 0, mii = 0, fset = 0; -#ifdef DO_DXSUFLO - int dxsuflo = 0; -#endif - int ltint = 0; + int i, media; + int fdx, mii, fset, dxsuflo, ltint; int chip_version; char *chipname; struct net_device *dev; struct pcnet32_access *a = NULL; + u8 promaddr[6]; /* reset the chip */ pcnet32_dwio_reset(ioaddr); pcnet32_wio_reset(ioaddr); /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */ - if (pcnet32_wio_read_csr (ioaddr, 0) == 4 && pcnet32_wio_check (ioaddr)) { + if (pcnet32_wio_read_csr(ioaddr, 0) == 4 && pcnet32_wio_check(ioaddr)) { a = &pcnet32_wio; } else { - if (pcnet32_dwio_read_csr (ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) { + if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) { a = &pcnet32_dwio; } else return -ENODEV; } - chip_version = a->read_csr (ioaddr, 88) | (a->read_csr (ioaddr,89) << 16); + chip_version = a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr,89) << 16); if (pcnet32_debug > 2) printk(KERN_INFO " PCnet chip version is %#x.\n", chip_version); if ((chip_version & 0xfff) != 0x003) return -ENODEV; + + /* initialize variables */ + fdx = mii = fset = dxsuflo = ltint = 0; chip_version = (chip_version >> 12) & 0xffff; + switch (chip_version) { case 0x2420: chipname = "PCnet/PCI 79C970"; /* PCI */ @@ -587,23 +583,24 @@ * mode by which the card should operate */ /* switch to home wiring mode */ - media = a->read_bcr (ioaddr, 49); + media = a->read_bcr(ioaddr, 49); #if 0 if (pcnet32_debug > 2) - printk(KERN_DEBUG "pcnet32: pcnet32 media value %#x.\n", media); + printk(KERN_DEBUG PFX "media value %#x.\n", media); media &= ~3; media |= 1; #endif if (pcnet32_debug > 2) - printk(KERN_DEBUG "pcnet32: pcnet32 media reset to %#x.\n", media); - a->write_bcr (ioaddr, 49, media); + printk(KERN_DEBUG PFX "media reset to %#x.\n", media); + a->write_bcr(ioaddr, 49, media); break; case 0x2627: chipname = "PCnet/FAST III 79C975"; /* PCI */ fdx = 1; mii = 1; break; default: - printk(KERN_INFO "pcnet32: PCnet version %#x, no PCnet32 chip.\n",chip_version); + printk(KERN_INFO PFX "PCnet version %#x, no PCnet32 chip.\n", + chip_version); return -ENODEV; } @@ -618,17 +615,15 @@ { a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0800)); a->write_csr(ioaddr, 80, (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00); -#ifdef DO_DXSUFLO dxsuflo = 1; -#endif ltint = 1; } - dev = init_etherdev(NULL, 0); - if(dev==NULL) + dev = alloc_etherdev(0); + if(!dev) return -ENOMEM; - printk(KERN_INFO "%s: %s at %#3lx,", dev->name, chipname, ioaddr); + printk(KERN_INFO PFX "%s at %#3lx,", chipname, ioaddr); /* In most chips, after a chip reset, the ethernet address is read from the * station address PROM at the base address and programmed into the @@ -644,31 +639,28 @@ dev->dev_addr[2*i] = val & 0x0ff; dev->dev_addr[2*i+1] = (val >> 8) & 0x0ff; } - { - u8 promaddr[6]; - for (i = 0; i < 6; i++) { - promaddr[i] = inb(ioaddr + i); - } - if( memcmp( promaddr, dev->dev_addr, 6) ) - { - 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); -#elif defined(__powerpc__) - if (!is_valid_ether_addr(dev->dev_addr) - && is_valid_ether_addr(promaddr)) { - printk("\n" KERN_WARNING "%s: using PROM address:", - dev->name); - memcpy(dev->dev_addr, promaddr, 6); - } + + /* read PROM address and compare with CSR address */ + for (i = 0; i < 6; i++) + promaddr[i] = inb(ioaddr + i); + + if( memcmp( promaddr, dev->dev_addr, 6) + || !is_valid_ether_addr(dev->dev_addr) ) { +#ifndef __powerpc__ + if( is_valid_ether_addr(promaddr) ){ +#else + if( !is_valid_ether_addr(dev->dev_addr) + && is_valid_ether_addr(promaddr)) { #endif - } + printk(" warning: CSR address invalid,\n"); + printk(KERN_INFO " using instead PROM address of"); + memcpy(dev->dev_addr, promaddr, 6); + } } + /* if the ethernet address is not valid, force to 00:00:00:00:00:00 */ if( !is_valid_ether_addr(dev->dev_addr) ) - for (i = 0; i < 6; i++) - dev->dev_addr[i]=0; + memset(dev->dev_addr, 0, sizeof(dev->dev_addr)); for (i = 0; i < 6; i++) printk(" %2.2x", dev->dev_addr[i] ); @@ -698,7 +690,7 @@ dev->base_addr = ioaddr; res = request_region(ioaddr, PCNET32_TOTAL_SIZE, chipname); - if (res == NULL) + if (!res) return -EBUSY; /* pci_alloc_consistent returns page-aligned memory, so we do not have to check the alignment */ @@ -710,7 +702,6 @@ memset(lp, 0, sizeof(*lp)); lp->dma_addr = lp_dma_addr; lp->pci_dev = pdev; - printk("\n" KERN_INFO "pcnet32: pcnet32_private lp=%p lp_dma_addr=%#08x", lp, lp_dma_addr); spin_lock_init(&lp->lock); @@ -718,24 +709,23 @@ lp->name = chipname; lp->shared_irq = shared; lp->mii_if.full_duplex = fdx; -#ifdef DO_DXSUFLO lp->dxsuflo = dxsuflo; -#endif lp->ltint = ltint; lp->mii = mii; - if (options[card_idx] > sizeof (options_mapping)) + if ((cards_found >= MAX_UNITS) || (options[cards_found] > sizeof(options_mapping))) lp->options = PCNET32_PORT_ASEL; else - lp->options = options_mapping[options[card_idx]]; + lp->options = options_mapping[options[cards_found]]; lp->mii_if.dev = dev; lp->mii_if.mdio_read = mdio_read; lp->mii_if.mdio_write = mdio_write; - if (fdx && !(lp->options & PCNET32_PORT_ASEL) && full_duplex[card_idx]) + if (fdx && !(lp->options & PCNET32_PORT_ASEL) && + ((cards_found>=MAX_UNITS) || full_duplex[cards_found])) lp->options |= PCNET32_PORT_FD; - if (a == NULL) { - printk(KERN_ERR "pcnet32: No access methods\n"); + if (!a) { + printk(KERN_ERR PFX "No access methods\n"); pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); release_resource(res); return -ENODEV; @@ -790,8 +780,6 @@ } } - if (pcnet32_debug > 0) - printk(KERN_INFO "%s", version); /* The PCNET32-specific entries in the device structure. */ dev->open = &pcnet32_open; @@ -807,11 +795,13 @@ pcnet32_dev = dev; /* Fill in the generic fields of the device structure. */ - ether_setup(dev); + register_netdev(dev); + printk(KERN_INFO "%s: registered as %s\n",dev->name, lp->name); + cards_found++; return 0; } - + static int pcnet32_open(struct net_device *dev) { @@ -856,6 +846,9 @@ val |= 1; if (lp->options == (PCNET32_PORT_FD | PCNET32_PORT_AUI)) val |= 2; + } else if (lp->options & PCNET32_PORT_ASEL) { + /* workaround for xSeries250 */ + val |= 3; } lp->a.write_bcr (ioaddr, 9, val); } @@ -888,6 +881,7 @@ lp->a.write_csr (ioaddr, 3, val); } #endif + if (lp->ltint) { /* Enable TxDone-intr inhibitor */ val = lp->a.read_csr (ioaddr, 5); val |= (1<<14); @@ -922,7 +916,7 @@ if (pcnet32_debug > 2) printk(KERN_DEBUG "%s: pcnet32 open after %d ticks, init block %#x csr0 %4.4x.\n", dev->name, i, (u32) (lp->dma_addr + offsetof(struct pcnet32_private, init_block)), - lp->a.read_csr (ioaddr, 0)); + lp->a.read_csr(ioaddr, 0)); MOD_INC_USE_COUNT; @@ -1032,7 +1026,7 @@ /* Transmitter timeout, serious problems. */ printk(KERN_ERR "%s: transmit timed out, status %4.4x, resetting.\n", - dev->name, lp->a.read_csr (ioaddr, 0)); + dev->name, lp->a.read_csr(ioaddr, 0)); lp->a.write_csr (ioaddr, 0, 0x0004); lp->stats.tx_errors++; if (pcnet32_debug > 2) { @@ -1068,7 +1062,7 @@ if (pcnet32_debug > 3) { printk(KERN_DEBUG "%s: pcnet32_start_xmit() called, csr0 %4.4x.\n", - dev->name, lp->a.read_csr (ioaddr, 0)); + dev->name, lp->a.read_csr(ioaddr, 0)); } spin_lock_irqsave(&lp->lock, flags); @@ -1135,8 +1129,9 @@ int boguscnt = max_interrupt_work; int must_restart; - if (dev == NULL) { - printk (KERN_DEBUG "pcnet32_interrupt(): irq %d for unknown device.\n", irq); + if (!dev) { + printk (KERN_DEBUG "%s(): irq %d for unknown device\n", + __FUNCTION__, irq); return; } @@ -1207,7 +1202,8 @@ /* We must free the original skb */ if (lp->tx_skbuff[entry]) { - pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[entry], lp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE); + pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[entry], + lp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE); dev_kfree_skb_irq(lp->tx_skbuff[entry]); lp->tx_skbuff[entry] = 0; lp->tx_dma_addr[entry] = 0; @@ -1215,13 +1211,12 @@ dirty_tx++; } -#ifndef final_version if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) { - printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d, full=%d.\n", - dirty_tx, lp->cur_tx, lp->tx_full); + printk(KERN_ERR "%s: out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dev->name, dirty_tx, lp->cur_tx, lp->tx_full); dirty_tx += TX_RING_SIZE; } -#endif + if (lp->tx_full && netif_queue_stopped(dev) && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { @@ -1262,7 +1257,7 @@ /* Clear any other interrupt, and set interrupt enable. */ lp->a.write_csr (ioaddr, 0, 0x7940); - lp->a.write_rap(ioaddr,rap); + lp->a.write_rap (ioaddr,rap); if (pcnet32_debug > 4) printk(KERN_DEBUG "%s: exiting interrupt, csr0=%#4.4x.\n", @@ -1315,7 +1310,9 @@ skb_put (skb, pkt_len); lp->rx_skbuff[entry] = newskb; newskb->dev = dev; - lp->rx_dma_addr[entry] = pci_map_single(lp->pci_dev, newskb->tail, newskb->len, PCI_DMA_FROMDEVICE); + lp->rx_dma_addr[entry] = + pci_map_single(lp->pci_dev, newskb->tail, + newskb->len, PCI_DMA_FROMDEVICE); lp->rx_ring[entry].base = le32_to_cpu(lp->rx_dma_addr[entry]); rx_in_place = 1; } else @@ -1349,6 +1346,7 @@ lp->stats.rx_bytes += skb->len; skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); + dev->last_rx = jiffies; lp->stats.rx_packets++; } } @@ -1445,13 +1443,13 @@ /* set all multicast bits */ if (dev->flags & IFF_ALLMULTI){ - ib->filter [0] = 0xffffffff; - ib->filter [1] = 0xffffffff; + ib->filter[0] = 0xffffffff; + ib->filter[1] = 0xffffffff; return; } /* clear the multicast filter */ - ib->filter [0] = 0; - ib->filter [1] = 0; + ib->filter[0] = 0; + ib->filter[1] = 0; /* Add addresses */ for (i = 0; i < dev->mc_count; i++){ @@ -1648,20 +1646,28 @@ } return -EOPNOTSUPP; } - + static struct pci_driver pcnet32_driver = { - name: DRV_NAME, - probe: pcnet32_probe_pci, - remove: NULL, - id_table: pcnet32_pci_tbl, + name: DRV_NAME, + probe: pcnet32_probe_pci, + id_table: pcnet32_pci_tbl, }; MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, DRV_NAME " debug level (0-6)"); MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM_DESC(max_interrupt_work, DRV_NAME " maximum events handled per interrupt"); MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM_DESC(rx_copybreak, DRV_NAME " copy breakpoint for copy-only-tiny-frames"); MODULE_PARM(tx_start_pt, "i"); +MODULE_PARM_DESC(tx_start_pt, DRV_NAME " transmit start point (0-3)"); +MODULE_PARM(pcnet32vlb, "i"); +MODULE_PARM_DESC(pcnet32vlb, DRV_NAME " Vesa local bus (VLB) support (0/1)"); MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM_DESC(options, DRV_NAME " initial option setting(s) (0-15)"); MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM_DESC(full_duplex, DRV_NAME " full duplex setting(s) (1)"); + MODULE_AUTHOR("Thomas Bogendoerfer"); MODULE_DESCRIPTION("Driver for PCnet32 and PCnetPCI based ethercards"); MODULE_LICENSE("GPL"); @@ -1672,36 +1678,25 @@ static int __init pcnet32_init_module(void) { - int cards_found = 0; - int err; + printk(KERN_INFO "%s", version); if (debug > 0) pcnet32_debug = debug; + if ((tx_start_pt >= 0) && (tx_start_pt <= 3)) tx_start = tx_start_pt; - - pcnet32_dev = NULL; + /* find the PCI devices */ -#define USE_PCI_REGISTER_DRIVER -#ifdef USE_PCI_REGISTER_DRIVER - if ((err = pci_module_init(&pcnet32_driver)) < 0 ) - return err; -#else - { - struct pci_device_id *devid = pcnet32_pci_tbl; - for (devid = pcnet32_pci_tbl; devid != NULL && devid->vendor != 0; devid++) { - struct pci_dev *pdev = pci_find_subsys(devid->vendor, devid->device, devid->subvendor, devid->subdevice, NULL); - if (pdev != NULL) { - if (pcnet32_probe_pci(pdev, devid) >= 0) { - cards_found++; - } - } - } - } -#endif - return 0; - /* find any remaining VLbus devices */ - return pcnet32_probe_vlbus(cards_found); + pci_module_init(&pcnet32_driver); + + /* should we find any remaining VLbus devices ? */ + if (pcnet32vlb) + pcnet32_probe_vlbus(); + + if (cards_found) + printk(KERN_INFO PFX "%d cards_found.\n", cards_found); + + return cards_found ? 0 : -ENODEV; } static void __exit pcnet32_cleanup_module(void) @@ -1710,13 +1705,13 @@ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (pcnet32_dev) { - struct pcnet32_private *lp = pcnet32_dev->priv; + struct pcnet32_private *lp = pcnet32_dev->priv; next_dev = lp->next; unregister_netdev(pcnet32_dev); release_region(pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE); - if (lp->pci_dev != NULL) + if (lp->pci_dev) pci_unregister_driver(&pcnet32_driver); - pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); + pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); kfree(pcnet32_dev); pcnet32_dev = next_dev; } diff -Nru a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c --- a/drivers/net/ppp_deflate.c Thu Mar 7 18:17:42 2002 +++ b/drivers/net/ppp_deflate.c Thu Mar 7 18:17:42 2002 @@ -35,12 +35,16 @@ #include #include #include +#include #include #include #include +static spinlock_t comp_free_list_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(comp_free_list); + /* * State for a Deflate (de)compressor. */ @@ -52,6 +56,7 @@ int debug; z_stream strm; struct compstat stats; + struct list_head list; }; #define DEFLATE_OVHD 2 /* Deflate overhead/packet */ @@ -76,6 +81,27 @@ static void z_decomp_reset __P((void *state)); static void z_comp_stats __P((void *state, struct compstat *stats)); +static void z_comp_delayedfree(void *arg) +{ + struct ppp_deflate_state *state; + + spin_lock_bh(&comp_free_list_lock); + while(!list_empty(&comp_free_list)) { + state = list_entry(comp_free_list.next, struct ppp_deflate_state, list); + list_del(&state->list); + spin_unlock_bh(&comp_free_list_lock); + if (state->strm.workspace) + vfree(state->strm.workspace); + kfree(state); + spin_lock_bh(&comp_free_list_lock); + } + spin_unlock_bh(&comp_free_list_lock); +} + +static struct tq_struct z_comp_task = { + routine: z_comp_delayedfree +}; + static void z_comp_free(arg) void *arg; @@ -84,9 +110,12 @@ if (state) { zlib_deflateEnd(&state->strm); - if (state->strm.workspace) - kfree(state->strm.workspace); - kfree(state); + + spin_lock_bh(&comp_free_list_lock); + list_add(&state->list, &comp_free_list); + spin_unlock_bh(&comp_free_list_lock); + + schedule_task(&z_comp_task); MOD_DEC_USE_COUNT; } } @@ -121,8 +150,7 @@ memset (state, 0, sizeof (struct ppp_deflate_state)); state->strm.next_in = NULL; state->w_size = w_size; - state->strm.workspace = kmalloc(zlib_deflate_workspacesize(), - GFP_KERNEL); + state->strm.workspace = vmalloc(zlib_deflate_workspacesize()); if (state->strm.workspace == NULL) goto out_free; @@ -134,7 +162,6 @@ out_free: z_comp_free(state); - MOD_DEC_USE_COUNT; return NULL; } @@ -319,7 +346,6 @@ out_free: z_decomp_free(state); - MOD_DEC_USE_COUNT; return NULL; } @@ -590,8 +616,10 @@ { ppp_unregister_compressor(&ppp_deflate); ppp_unregister_compressor(&ppp_deflate_draft); + /* Ensure that any deflate state pending free is actually freed */ + flush_scheduled_tasks(); } module_init(deflate_init); module_exit(deflate_cleanup); -MODULE_LICENSE("BSD without advertisement clause"); +MODULE_LICENSE("Dual BSD/GPL"); diff -Nru a/drivers/net/rcpci45.c b/drivers/net/rcpci45.c --- a/drivers/net/rcpci45.c Thu Mar 7 18:17:40 2002 +++ b/drivers/net/rcpci45.c Thu Mar 7 18:17:40 2002 @@ -115,7 +115,7 @@ MODULE_DEVICE_TABLE (pci, rcpci45_pci_table); MODULE_LICENSE("GPL"); -static void __exit +static void __devexit rcpci45_remove_one (struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata (pdev); @@ -267,7 +267,7 @@ name: "rcpci45", id_table: rcpci45_pci_table, probe: rcpci45_init_one, - remove: rcpci45_remove_one, + remove: __devexit_p(rcpci45_remove_one), }; static int __init diff -Nru a/drivers/net/saa9730.c b/drivers/net/saa9730.c --- a/drivers/net/saa9730.c Thu Mar 7 18:17:36 2002 +++ b/drivers/net/saa9730.c Thu Mar 7 18:17:36 2002 @@ -685,6 +685,7 @@ len, 0); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); + dev->last_rx = jiffies; } } else { /* We got an error packet. */ diff -Nru a/drivers/net/sis900.c b/drivers/net/sis900.c --- a/drivers/net/sis900.c Thu Mar 7 18:17:43 2002 +++ b/drivers/net/sis900.c Thu Mar 7 18:17:43 2002 @@ -2129,7 +2129,7 @@ name: SIS900_MODULE_NAME, id_table: sis900_pci_tbl, probe: sis900_probe, - remove: sis900_remove, + remove: __devexit_p(sis900_remove), }; static int __init sis900_init_module(void) diff -Nru a/drivers/net/sk98lin/skge.c b/drivers/net/sk98lin/skge.c --- a/drivers/net/sk98lin/skge.c Thu Mar 7 18:17:39 2002 +++ b/drivers/net/sk98lin/skge.c Thu Mar 7 18:17:39 2002 @@ -541,6 +541,48 @@ boards_found++; + /* More then one port found */ + if ((pAC->GIni.GIMacsFound == 2 ) && (pAC->RlmtNets == 2)) { + if ((dev = init_etherdev(NULL, sizeof(DEV_NET))) == 0) { + printk(KERN_ERR "Unable to allocate etherdev " + "structure!\n"); + break; + } + + pAC->dev[1] = dev; + pNet = dev->priv; + pNet->PortNr = 1; + pNet->NetNr = 1; + pNet->pAC = pAC; + pNet->Mtu = 1500; + pNet->Up = 0; + + dev->open = &SkGeOpen; + dev->stop = &SkGeClose; + dev->hard_start_xmit = &SkGeXmit; + dev->get_stats = &SkGeStats; + dev->set_multicast_list = &SkGeSetRxMode; + dev->set_mac_address = &SkGeSetMacAddr; + dev->do_ioctl = &SkGeIoctl; + dev->change_mtu = &SkGeChangeMtu; + + pProcFile = create_proc_entry(dev->name, + S_IFREG | 0444, pSkRootDir); + pProcFile->read_proc = proc_read; + pProcFile->write_proc = NULL; + pProcFile->nlink = 1; + pProcFile->size = sizeof(dev->name+1); + pProcFile->data = (void*)pProcFile; + + memcpy((caddr_t) &dev->dev_addr, + (caddr_t) &pAC->Addr.Net[1].CurrentMacAddress, 6); + + printk("%s: %s\n", dev->name, pAC->DeviceStr); + printk(" PrefPort:B RlmtMode:Dual Check Link State\n"); + + } + + /* * This is bollocks, but we need to tell the net-init * code that it shall go for the next device. diff -Nru a/drivers/net/smc-ultra.c b/drivers/net/smc-ultra.c --- a/drivers/net/smc-ultra.c Thu Mar 7 18:17:39 2002 +++ b/drivers/net/smc-ultra.c Thu Mar 7 18:17:39 2002 @@ -501,8 +501,10 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_ULTRA_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ULTRA_CARDS) "i"); -MODULE_PARM_DESC(io, "SMC Ultra I/O base address(es)"); -MODULE_PARM_DESC(irq, "SMC Ultra IRQ number(s) (assigned)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); +MODULE_DESCRIPTION("SMC Ultra/EtherEZ ISA/PnP Ethernet driver"); +MODULE_LICENSE("GPL"); EXPORT_NO_SYMBOLS; @@ -557,7 +559,6 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); diff -Nru a/drivers/net/smc-ultra32.c b/drivers/net/smc-ultra32.c --- a/drivers/net/smc-ultra32.c Thu Mar 7 18:17:41 2002 +++ b/drivers/net/smc-ultra32.c Thu Mar 7 18:17:41 2002 @@ -379,6 +379,9 @@ #define MAX_ULTRA32_CARDS 4 /* Max number of Ultra cards per module */ static struct net_device dev_ultra[MAX_ULTRA32_CARDS]; +MODULE_DESCRIPTION("SMC Ultra32 EISA ethernet driver"); +MODULE_LICENSE("GPL"); + int init_module(void) { int this_dev, found = 0; @@ -415,5 +418,4 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); diff -Nru a/drivers/net/starfire.c b/drivers/net/starfire.c --- a/drivers/net/starfire.c Thu Mar 7 18:17:40 2002 +++ b/drivers/net/starfire.c Thu Mar 7 18:17:40 2002 @@ -96,13 +96,19 @@ LK1.3.5 (jgarzik) - ethtool NWAY_RST, GLINK, [GS]MSGLVL support + LK1.3.6: + - Sparc64 support and fixes (Ion Badulescu) + - Better stats and error handling (Ion Badulescu) + - Use new pci_set_mwi() PCI API function (jgarzik) + TODO: - implement tx_timeout() properly + - VLAN support */ #define DRV_NAME "starfire" -#define DRV_VERSION "1.03+LK1.3.5" -#define DRV_RELDATE "November 17, 2001" +#define DRV_VERSION "1.03+LK1.3.6" +#define DRV_RELDATE "March 7, 2002" #include #include @@ -128,8 +134,11 @@ * for this driver to really use the firmware. Note that Rx/Tx * hardware TCP checksumming is not possible without the firmware. * - * I'm currently [Feb 2001] talking to Adaptec about this redistribution - * issue. Stay tuned... + * If Adaptec could allow redistribution of the firmware (even in binary + * format), life would become a lot easier. Unfortunately, I've lost my + * Adaptec contacts, so progress on this front is rather unlikely to + * occur. If anybody from Adaptec reads this and can help with this matter, + * please let me know... */ #undef HAS_FIRMWARE /* @@ -609,7 +618,10 @@ long ioaddr; int drv_flags, io_size; int boguscnt; +#ifndef HAVE_PCI_SET_MWI + u16 cmd; u8 cache; +#endif /* when built into the kernel, we only print version if device is found */ #ifndef MODULE @@ -644,14 +656,25 @@ goto err_out_free_netdev; } - ioaddr = (long) ioremap (ioaddr, io_size); + /* ioremap is borken in Linux-2.2.x/sparc64 */ +#if !defined(CONFIG_SPARC64) || LINUX_VERSION_CODE > 0x20300 + ioaddr = (long) ioremap(ioaddr, io_size); if (!ioaddr) { printk (KERN_ERR DRV_NAME " %d: cannot remap 0x%x @ 0x%lx, aborting\n", card_idx, io_size, ioaddr); goto err_out_free_res; } +#endif /* !CONFIG_SPARC64 || Linux 2.3.0+ */ - pci_set_master (pdev); + pci_set_master(pdev); + +#ifdef HAVE_PCI_SET_MWI + pci_set_mwi(pdev); +#else + /* enable MWI -- it vastly improves Rx performance on sparc64 */ + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_INVALIDATE; + pci_write_config_word(pdev, PCI_COMMAND, cmd); /* set PCI cache size */ pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache); @@ -662,6 +685,7 @@ pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, SMP_CACHE_BYTES >> 2); } +#endif #ifdef ZEROCOPY /* Starfire can do SG and TCP/UDP checksumming */ @@ -670,7 +694,7 @@ /* Serial EEPROM reads are hidden by the hardware. */ for (i = 0; i < 6; i++) - dev->dev_addr[i] = readb(ioaddr + EEPROMCtrl + 20-i); + dev->dev_addr[i] = readb(ioaddr + EEPROMCtrl + 20 - i); #if ! defined(final_version) /* Dump the EEPROM contents during development. */ if (debug > 4) @@ -932,7 +956,7 @@ /* Fill both the unused Tx SA register and the Rx perfect filter. */ for (i = 0; i < 6; i++) - writeb(dev->dev_addr[i], ioaddr + StationAddr + 5-i); + writeb(dev->dev_addr[i], ioaddr + StationAddr + 5 - i); for (i = 0; i < 16; i++) { u16 *eaddrs = (u16 *)dev->dev_addr; long setup_frm = ioaddr + PerfFilterTable + i * 16; @@ -979,9 +1003,9 @@ #ifdef HAS_FIRMWARE /* Load Rx/Tx firmware into the frame processors */ for (i = 0; i < FIRMWARE_RX_SIZE * 2; i++) - writel(cpu_to_le32(firmware_rx[i]), ioaddr + RxGfpMem + i * 4); + writel(firmware_rx[i], ioaddr + RxGfpMem + i * 4); for (i = 0; i < FIRMWARE_TX_SIZE * 2; i++) - writel(cpu_to_le32(firmware_tx[i]), ioaddr + TxGfpMem + i * 4); + writel(firmware_tx[i], ioaddr + TxGfpMem + i * 4); /* Enable the Rx and Tx units, and the Rx/Tx frame processors. */ writel(0x003F, ioaddr + GenCtrl); #else /* not HAS_FIRMWARE */ @@ -1156,8 +1180,8 @@ np->tx_ring[entry].first_addr = cpu_to_le32(np->tx_info[entry].first_mapping); #ifdef ZEROCOPY - np->tx_ring[entry].first_len = cpu_to_le32(skb_first_frag_len(skb)); - np->tx_ring[entry].total_len = cpu_to_le32(skb->len); + np->tx_ring[entry].first_len = cpu_to_le16(skb_first_frag_len(skb)); + np->tx_ring[entry].total_len = cpu_to_le16(skb->len); /* Add "| TxDescIntr" to generate Tx-done interrupts. */ np->tx_ring[entry].status = cpu_to_le32(TxDescID | TxCRCEn); np->tx_ring[entry].nbufs = cpu_to_le32(skb_shinfo(skb)->nr_frags + 1); @@ -1170,8 +1194,10 @@ np->tx_ring[entry].status |= cpu_to_le32(TxRingWrap | TxDescIntr); #ifdef ZEROCOPY - if (skb->ip_summed == CHECKSUM_HW) + if (skb->ip_summed == CHECKSUM_HW) { np->tx_ring[entry].status |= cpu_to_le32(TxCalTCP); + np->stats.tx_compressed++; + } #endif /* ZEROCOPY */ if (debug > 5) { @@ -1449,6 +1475,7 @@ #if defined(full_rx_status) || defined(csum_rx_status) if (le32_to_cpu(np->rx_done_q[np->rx_done].status2) & 0x01000000) { skb->ip_summed = CHECKSUM_UNNECESSARY; + np->stats.rx_compressed++; } /* * This feature doesn't seem to be working, at least @@ -1580,12 +1607,17 @@ printk(KERN_NOTICE "%s: Increasing Tx FIFO threshold to %d bytes\n", dev->name, np->tx_threshold * 16); } - if ((intr_status & ~(IntrNormalMask | IntrAbnormalSummary | IntrLinkChange | IntrStatsMax | IntrTxDataLow | IntrPCIPad)) && debug) + if (intr_status & IntrRxGFPDead) { + np->stats.rx_fifo_errors++; + np->stats.rx_errors++; + } + if (intr_status & (IntrNoTxCsum | IntrDMAErr)) { + np->stats.tx_fifo_errors++; + np->stats.tx_errors++; + } + if ((intr_status & ~(IntrNormalMask | IntrAbnormalSummary | IntrLinkChange | IntrStatsMax | IntrTxDataLow | IntrRxGFPDead | IntrNoTxCsum | IntrPCIPad)) && debug) printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", dev->name, intr_status); - /* Hmmmmm, it's not clear how to recover from DMA faults. */ - if (intr_status & IntrDMAErr) - np->stats.tx_fifo_errors++; } @@ -1982,7 +2014,7 @@ static struct pci_driver starfire_driver = { name: DRV_NAME, probe: starfire_init_one, - remove: starfire_remove_one, + remove: __devexit_p(starfire_remove_one), id_table: starfire_pci_tbl, }; diff -Nru a/drivers/net/sundance.c b/drivers/net/sundance.c --- a/drivers/net/sundance.c Thu Mar 7 18:17:37 2002 +++ b/drivers/net/sundance.c Thu Mar 7 18:17:37 2002 @@ -1463,7 +1463,7 @@ name: DRV_NAME, id_table: sundance_pci_tbl, probe: sundance_probe1, - remove: sundance_remove1, + remove: __devexit_p(sundance_remove1), }; static int __init sundance_init(void) diff -Nru a/drivers/net/sungem.c b/drivers/net/sungem.c --- a/drivers/net/sungem.c Thu Mar 7 18:17:46 2002 +++ b/drivers/net/sungem.c Thu Mar 7 18:17:46 2002 @@ -2835,7 +2835,7 @@ name: GEM_MODULE_NAME, id_table: gem_pci_tbl, probe: gem_init_one, - remove: gem_remove_one, + remove: __devexit_p(gem_remove_one), #ifdef CONFIG_PM suspend: gem_suspend, resume: gem_resume, diff -Nru a/drivers/net/tg3.c b/drivers/net/tg3.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/tg3.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,5925 @@ +/* $Id: tg3.c,v 1.43.2.74 2002/03/06 22:22:29 davem Exp $ + * tg3.c: Broadcom Tigon3 ethernet driver. + * + * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) + * Copyright (C) 2001, 2002 Jeff Garzik (jgarzik@mandrakesoft.com) + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifndef PCI_DMA_BUS_IS_PHYS +#define PCI_DMA_BUS_IS_PHYS 1 +#endif + +/* Either I can't figure out how they secretly implemented it (ie. RXD flags + * for mini ring, where it should go in NIC sram, and how many entries the NIC + * firmware expects) or it isn't really fully implemented. Perhaps Broadcom + * wants people to pay for a "performance enhanced" version of their firmware + + * binary-only driver that has the mini ring actually implemented. + * These kids today... -DaveM + */ +#define TG3_MINI_RING_WORKS 0 + +#define TG3_VLAN_TAG_USED 0 + +#include "tg3.h" + +#define DRV_MODULE_NAME "tg3" +#define PFX DRV_MODULE_NAME ": " +#define DRV_MODULE_VERSION "0.96" +#define DRV_MODULE_RELDATE "Mar 6, 2002" + +#define TG3_DEF_MAC_MODE 0 +#define TG3_DEF_RX_MODE 0 +#define TG3_DEF_TX_MODE 0 +#define TG3_DEF_MSG_ENABLE \ + (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_TIMER | \ + NETIF_MSG_IFDOWN | \ + NETIF_MSG_IFUP | \ + NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR) + +/* length of time before we decide the hardware is borked, + * and dev->tx_timeout() should be called to fix the problem + */ +#define TG3_TX_TIMEOUT (5 * HZ) + +/* hardware minimum and maximum for a single frame's data payload */ +#define TG3_MIN_MTU 60 +#define TG3_MAX_MTU 9000 + +/* These numbers seem to be hard coded in the NIC firmware somehow. + * You can't change the ring sizes, but you can change where you place + * them in the NIC onboard memory. + */ +#define TG3_RX_RING_SIZE 512 +#define TG3_RX_RING_PENDING 200 +#if TG3_MINI_RING_WORKS +#define TG3_RX_MINI_RING_SIZE 256 /* ??? */ +#define TG3_RX_MINI_RING_PENDING 100 +#endif +#define TG3_RX_JUMBO_RING_SIZE 256 +#define TG3_RX_JUMBO_RING_PENDING 100 +#define TG3_RX_RCB_RING_SIZE 1024 +#define TG3_TX_RING_SIZE 512 + +#define TG3_RX_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * \ + TG3_RX_RING_SIZE) +#if TG3_MINI_RING_WORKS +#define TG3_RX_MINI_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * \ + TG3_RX_MINI_RING_SIZE) +#endif +#define TG3_RX_JUMBO_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * \ + TG3_RX_JUMBO_RING_SIZE) +#define TG3_RX_RCB_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * \ + TG3_RX_RCB_RING_SIZE) +#define TG3_TX_RING_BYTES (sizeof(struct tg3_tx_buffer_desc) * \ + TG3_TX_RING_SIZE) +#define TX_BUFFS_AVAIL(TP) \ + (((TP)->tx_cons <= (TP)->tx_prod) ? \ + (TP)->tx_cons + (TG3_TX_RING_SIZE - 1) - (TP)->tx_prod : \ + (TP)->tx_cons - (TP)->tx_prod - 1) +#define NEXT_TX(N) (((N) + 1) & (TG3_TX_RING_SIZE - 1)) + +#define RX_PKT_BUF_SZ (1536 + tp->rx_offset + 64) +#if TG3_MINI_RING_WORKS +#define RX_MINI_PKT_BUF_SZ (256 + tp->rx_offset + 64) +#endif +#define RX_JUMBO_PKT_BUF_SZ (9046 + tp->rx_offset + 64) + +/* minimum number of free TX descriptors required to wake up TX process */ +#define TG3_TX_WAKEUP_THRESH (TG3_TX_RING_SIZE / 4) + +static char version[] __devinitdata = + DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; + +MODULE_AUTHOR("David S. Miller (davem@redhat.com) and Jeff Garzik (jgarzik@mandrakesoft.com)"); +MODULE_DESCRIPTION("Broadcom Tigon3 ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(tg3_debug, "i"); +MODULE_PARM_DESC(tg3_debug, "Tigon3 bitmapped debugging message enable value"); + +static int tg3_debug = -1; /* -1 == use TG3_DEF_MSG_ENABLE as value */ + +static struct pci_device_id tg3_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5700, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5701, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702FE, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702X, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703X, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_SYSKONNECT, 0x4400, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, tg3_pci_tbl); + +static void tg3_write_indirect_reg32(struct tg3 *tp, u32 off, u32 val) +{ + if ((tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) != 0) { + unsigned long flags; + + spin_lock_irqsave(&tp->indirect_lock, flags); + pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off); + pci_write_config_dword(tp->pdev, TG3PCI_REG_DATA, val); + spin_unlock_irqrestore(&tp->indirect_lock, flags); + } else { + writel(val, tp->regs + off); + } +} + +#define tw32(reg,val) tg3_write_indirect_reg32(tp,(reg),(val)) +#define tw32_mailbox(reg, val) writel(((val) & 0xffffffff), tp->regs + (reg)) +#define tw16(reg,val) writew(((val) & 0xffff), tp->regs + (reg)) +#define tw8(reg,val) writeb(((val) & 0xff), tp->regs + (reg)) +#define tr32(reg) readl(tp->regs + (reg)) +#define tr16(reg) readw(tp->regs + (reg)) +#define tr8(reg) readb(tp->regs + (reg)) + +static void tg3_write_mem(struct tg3 *tp, u32 off, u32 val) +{ + unsigned long flags; + + spin_lock_irqsave(&tp->indirect_lock, flags); + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off); + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val); + + /* Always leave this as zero. */ + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0); + spin_unlock_irqrestore(&tp->indirect_lock, flags); +} + +static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val) +{ + unsigned long flags; + + spin_lock_irqsave(&tp->indirect_lock, flags); + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off); + pci_read_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val); + + /* Always leave this as zero. */ + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0); + spin_unlock_irqrestore(&tp->indirect_lock, flags); +} + +static void tg3_disable_ints(struct tg3 *tp) +{ + tw32(TG3PCI_MISC_HOST_CTRL, + (tp->misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT)); + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001); +} + +static void tg3_enable_ints(struct tg3 *tp) +{ + tw32(TG3PCI_MISC_HOST_CTRL, + (tp->misc_host_ctrl & ~MISC_HOST_CTRL_MASK_PCI_INT)); + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000000); + + if (tp->hw_status->status & SD_STATUS_UPDATED) + tw32(GRC_LOCAL_CTRL, + tp->grc_local_ctrl | GRC_LCLCTRL_SETINT); +} + +#define PHY_BUSY_LOOPS 5000 + +static int tg3_readphy(struct tg3 *tp, int reg, u32 *val) +{ + u32 frame_val; + int loops, ret; + + if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { + tw32(MAC_MI_MODE, + (tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL)); + udelay(40); + } + + *val = 0xffffffff; + + frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) & + MI_COM_PHY_ADDR_MASK); + frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) & + MI_COM_REG_ADDR_MASK); + frame_val |= (MI_COM_CMD_READ | MI_COM_START); + + tw32(MAC_MI_COM, frame_val); + + loops = PHY_BUSY_LOOPS; + while (loops-- > 0) { + frame_val = tr32(MAC_MI_COM); + + if ((frame_val & MI_COM_BUSY) == 0) { + udelay(5); + frame_val = tr32(MAC_MI_COM); + break; + } + udelay(10); + } + + ret = -EBUSY; + if (loops > 0) { + *val = frame_val & MI_COM_DATA_MASK; + ret = 0; + } + + if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { + tw32(MAC_MI_MODE, tp->mi_mode); + udelay(40); + } + + return ret; +} + +static int tg3_writephy(struct tg3 *tp, int reg, u32 val) +{ + u32 frame_val; + int loops, ret; + + if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { + tw32(MAC_MI_MODE, + (tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL)); + udelay(40); + } + + frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) & + MI_COM_PHY_ADDR_MASK); + frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) & + MI_COM_REG_ADDR_MASK); + frame_val |= (val & MI_COM_DATA_MASK); + frame_val |= (MI_COM_CMD_WRITE | MI_COM_START); + + tw32(MAC_MI_COM, frame_val); + + loops = PHY_BUSY_LOOPS; + while (loops-- > 0) { + frame_val = tr32(MAC_MI_COM); + if ((frame_val & MI_COM_BUSY) == 0) { + udelay(5); + frame_val = tr32(MAC_MI_COM); + break; + } + udelay(10); + } + + ret = -EBUSY; + if (loops > 0) + ret = 0; + + if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { + tw32(MAC_MI_MODE, tp->mi_mode); + udelay(40); + } + + return ret; +} + +/* This will reset the tigon3 PHY if there is no valid + * link unless the FORCE argument is non-zero. + */ +static int tg3_phy_reset(struct tg3 *tp, int force) +{ + u32 phy_status, phy_control; + int err, limit; + + err = tg3_readphy(tp, MII_BMSR, &phy_status); + err |= tg3_readphy(tp, MII_BMSR, &phy_status); + if (err != 0) + return -EBUSY; + + /* If we have link, and not forcing a reset, then nothing + * to do. + */ + if ((phy_status & BMSR_LSTATUS) != 0 && (force == 0)) + return 0; + + /* OK, reset it, and poll the BMCR_RESET bit until it + * clears or we time out. + */ + phy_control = BMCR_RESET; + err = tg3_writephy(tp, MII_BMCR, phy_control); + if (err != 0) + return -EBUSY; + + limit = 5000; + while (limit--) { + err = tg3_readphy(tp, MII_BMCR, &phy_control); + if (err != 0) + return -EBUSY; + + if ((phy_control & BMCR_RESET) == 0) { + udelay(40); + return 0; + } + udelay(10); + } + + return -EBUSY; +} + +static int tg3_setup_phy(struct tg3 *); +static int tg3_halt(struct tg3 *); + +static int tg3_set_power_state(struct tg3 *tp, int state) +{ + u32 misc_host_ctrl; + u16 power_control, power_caps; + int pm = tp->pm_cap; + + /* Make sure register accesses (indirect or otherwise) + * will function correctly. + */ + pci_write_config_dword(tp->pdev, + TG3PCI_MISC_HOST_CTRL, + tp->misc_host_ctrl); + + pci_read_config_word(tp->pdev, + pm + PCI_PM_CTRL, + &power_control); + power_control |= PCI_PM_CTRL_PME_STATUS; + power_control &= ~(PCI_PM_CTRL_STATE_MASK); + switch (state) { + case 0: + power_control |= 0; + pci_write_config_word(tp->pdev, + pm + PCI_PM_CTRL, + power_control); + tw32(GRC_LOCAL_CTRL, tp->grc_local_ctrl); + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x02); + return 0; + + case 1: + power_control |= 1; + break; + + case 2: + power_control |= 2; + break; + + case 3: + power_control |= 3; + break; + + default: + printk(KERN_WARNING "%s: Invalid power state (%d) requested.\n", + tp->dev->name, state); + return -EINVAL; + }; + + power_control |= PCI_PM_CTRL_PME_ENABLE; + + misc_host_ctrl = tr32(TG3PCI_MISC_HOST_CTRL); + tw32(TG3PCI_MISC_HOST_CTRL, + misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT); + + if (tp->link_config.phy_is_low_power == 0) { + tp->link_config.phy_is_low_power = 1; + tp->link_config.orig_speed = tp->link_config.speed; + tp->link_config.orig_duplex = tp->link_config.duplex; + tp->link_config.orig_autoneg = tp->link_config.autoneg; + } + + tp->link_config.speed = SPEED_10; + tp->link_config.autoneg = AUTONEG_ENABLE; + tg3_setup_phy(tp); + + tg3_halt(tp); + + pci_read_config_word(tp->pdev, pm + PCI_PM_PMC, &power_caps); + + if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE) { + u32 mac_mode; + + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x5a); + + mac_mode = MAC_MODE_PORT_MODE_MII | + MAC_MODE_LINK_POLARITY; + + if (((power_caps & PCI_PM_CAP_PME_D3cold) && + (tp->tg3_flags & TG3_FLAG_WOL_ENABLE))) + mac_mode |= MAC_MODE_MAGIC_PKT_ENABLE; + + tw32(MAC_MODE, mac_mode); + tw32(MAC_RX_MODE, RX_MODE_ENABLE); + } + + if (tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB) { + tw32(TG3PCI_CLOCK_CTRL, + (CLOCK_CTRL_RXCLK_DISABLE | + CLOCK_CTRL_TXCLK_DISABLE | + CLOCK_CTRL_ALTCLK)); + tw32(TG3PCI_CLOCK_CTRL, + (CLOCK_CTRL_RXCLK_DISABLE | + CLOCK_CTRL_TXCLK_DISABLE | + CLOCK_CTRL_44MHZ_CORE)); + tw32(TG3PCI_CLOCK_CTRL, + (CLOCK_CTRL_RXCLK_DISABLE | + CLOCK_CTRL_TXCLK_DISABLE | + CLOCK_CTRL_ALTCLK | + CLOCK_CTRL_44MHZ_CORE)); + } else { + tw32(TG3PCI_CLOCK_CTRL, + (CLOCK_CTRL_RXCLK_DISABLE | + CLOCK_CTRL_TXCLK_DISABLE | + CLOCK_CTRL_ALTCLK | + CLOCK_CTRL_PWRDOWN_PLL133)); + } + + udelay(40); + + if ((power_caps & PCI_PM_CAP_PME_D3cold) && + (tp->tg3_flags & TG3_FLAG_WOL_ENABLE)) { + /* Move to auxilliary power. */ + tw32(GRC_LOCAL_CTRL, + (GRC_LCLCTRL_GPIO_OE0 | + GRC_LCLCTRL_GPIO_OE1 | + GRC_LCLCTRL_GPIO_OE2 | + GRC_LCLCTRL_GPIO_OUTPUT0 | + GRC_LCLCTRL_GPIO_OUTPUT1)); + } + + /* Finally, set the new power state. */ + pci_write_config_word(tp->pdev, pm + PCI_PM_CTRL, power_control); + + return 0; +} + +static void tg3_link_report(struct tg3 *tp) +{ + if (!netif_carrier_ok(tp->dev)) { + printk("%s: Link is down.\n", tp->dev->name); + } else { + printk("%s: Link is up at %d Mbps, %s duplex.\n", + tp->dev->name, + (tp->link_config.active_speed == SPEED_1000 ? + 1000 : + (tp->link_config.active_speed == SPEED_100 ? + 100 : 10)), + (tp->link_config.active_duplex == DUPLEX_FULL ? + "full" : "half")); + + printk("%s: Flow control is %s for TX and %s for RX.\n", + tp->dev->name, + (tp->tg3_flags & TG3_FLAG_TX_PAUSE) ? "on" : "off", + (tp->tg3_flags & TG3_FLAG_RX_PAUSE) ? "on" : "off"); + } +} + +static void tg3_setup_flow_control(struct tg3 *tp, u32 local_adv, u32 remote_adv) +{ + u32 new_tg3_flags = 0; + + if (local_adv & ADVERTISE_PAUSE_CAP) { + if (local_adv & ADVERTISE_PAUSE_ASYM) { + if (remote_adv & LPA_PAUSE_CAP) + new_tg3_flags |= + (TG3_FLAG_RX_PAUSE | + TG3_FLAG_TX_PAUSE); + else if (remote_adv & LPA_PAUSE_ASYM) + new_tg3_flags |= + (TG3_FLAG_RX_PAUSE); + } else { + if (remote_adv & LPA_PAUSE_CAP) + new_tg3_flags |= + (TG3_FLAG_RX_PAUSE | + TG3_FLAG_TX_PAUSE); + } + } else if (local_adv & ADVERTISE_PAUSE_ASYM) { + if ((remote_adv & LPA_PAUSE_CAP) && + (remote_adv & LPA_PAUSE_ASYM)) + new_tg3_flags |= TG3_FLAG_TX_PAUSE; + } + + tp->tg3_flags &= ~(TG3_FLAG_RX_PAUSE | TG3_FLAG_TX_PAUSE); + tp->tg3_flags |= new_tg3_flags; + + if (new_tg3_flags & TG3_FLAG_RX_PAUSE) + tp->rx_mode |= RX_MODE_FLOW_CTRL_ENABLE; + else + tp->rx_mode &= ~RX_MODE_FLOW_CTRL_ENABLE; + + if (new_tg3_flags & TG3_FLAG_TX_PAUSE) + tp->tx_mode |= TX_MODE_FLOW_CTRL_ENABLE; + else + tp->tx_mode &= ~TX_MODE_FLOW_CTRL_ENABLE; +} + +static void tg3_aux_stat_to_speed_duplex(struct tg3 *tp, u32 val, u16 *speed, u8 *duplex) +{ + switch (val & MII_TG3_AUX_STAT_SPDMASK) { + case MII_TG3_AUX_STAT_10HALF: + *speed = SPEED_10; + *duplex = DUPLEX_HALF; + break; + + case MII_TG3_AUX_STAT_10FULL: + *speed = SPEED_10; + *duplex = DUPLEX_FULL; + break; + + case MII_TG3_AUX_STAT_100HALF: + *speed = SPEED_100; + *duplex = DUPLEX_HALF; + break; + + case MII_TG3_AUX_STAT_100FULL: + *speed = SPEED_100; + *duplex = DUPLEX_FULL; + break; + + case MII_TG3_AUX_STAT_1000HALF: + *speed = SPEED_1000; + *duplex = DUPLEX_HALF; + break; + + case MII_TG3_AUX_STAT_1000FULL: + *speed = SPEED_1000; + *duplex = DUPLEX_FULL; + break; + + default: + *speed = SPEED_INVALID; + *duplex = DUPLEX_INVALID; + break; + }; +} + +static int tg3_phy_copper_begin(struct tg3 *tp, int wait_for_link) +{ + u32 new_adv; + int i; + + if (tp->link_config.phy_is_low_power) { + /* Entering low power mode. Disable gigabit and + * 100baseT advertisements. + */ + tg3_writephy(tp, MII_TG3_CTRL, 0); + + new_adv = (ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); + if (tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB) + new_adv |= (ADVERTISE_100HALF | ADVERTISE_100FULL); + + tg3_writephy(tp, MII_ADVERTISE, new_adv); + } else if (tp->link_config.speed == SPEED_INVALID) { + tp->link_config.advertising = + (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | + ADVERTISED_Autoneg | ADVERTISED_MII); + + if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) + tp->link_config.advertising &= + ~(ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full); + + new_adv = (ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); + if (tp->link_config.advertising & ADVERTISED_10baseT_Half) + new_adv |= ADVERTISE_10HALF; + if (tp->link_config.advertising & ADVERTISED_10baseT_Full) + new_adv |= ADVERTISE_10FULL; + if (tp->link_config.advertising & ADVERTISED_100baseT_Half) + new_adv |= ADVERTISE_100HALF; + if (tp->link_config.advertising & ADVERTISED_100baseT_Full) + new_adv |= ADVERTISE_100FULL; + tg3_writephy(tp, MII_ADVERTISE, new_adv); + + if (tp->link_config.advertising & + (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full)) { + new_adv = 0; + if (tp->link_config.advertising & ADVERTISED_1000baseT_Half) + new_adv |= MII_TG3_CTRL_ADV_1000_HALF; + if (tp->link_config.advertising & ADVERTISED_1000baseT_Full) + new_adv |= MII_TG3_CTRL_ADV_1000_FULL; + if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) + new_adv |= (MII_TG3_CTRL_AS_MASTER | + MII_TG3_CTRL_ENABLE_AS_MASTER); + tg3_writephy(tp, MII_TG3_CTRL, new_adv); + } else { + tg3_writephy(tp, MII_TG3_CTRL, 0); + } + } else { + /* Asking for a specific link mode. */ + if (tp->link_config.speed == SPEED_1000) { + new_adv = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP; + tg3_writephy(tp, MII_ADVERTISE, new_adv); + + if (tp->link_config.duplex == DUPLEX_FULL) + new_adv = MII_TG3_CTRL_ADV_1000_FULL; + else + new_adv = MII_TG3_CTRL_ADV_1000_HALF; + if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) + new_adv |= (MII_TG3_CTRL_AS_MASTER | + MII_TG3_CTRL_ENABLE_AS_MASTER); + tg3_writephy(tp, MII_TG3_CTRL, new_adv); + } else { + tg3_writephy(tp, MII_TG3_CTRL, 0); + + new_adv = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP; + if (tp->link_config.speed == SPEED_100) { + if (tp->link_config.duplex == DUPLEX_FULL) + new_adv |= ADVERTISE_100FULL; + else + new_adv |= ADVERTISE_100HALF; + } else { + if (tp->link_config.duplex == DUPLEX_FULL) + new_adv |= ADVERTISE_10FULL; + else + new_adv |= ADVERTISE_10HALF; + } + tg3_writephy(tp, MII_ADVERTISE, new_adv); + } + } + + if (tp->link_config.autoneg == AUTONEG_DISABLE && + tp->link_config.speed != SPEED_INVALID) { + u32 bmcr, orig_bmcr; + + tp->link_config.active_speed = tp->link_config.speed; + tp->link_config.active_duplex = tp->link_config.duplex; + + bmcr = 0; + switch (tp->link_config.speed) { + default: + case SPEED_10: + break; + + case SPEED_100: + bmcr |= BMCR_SPEED100; + break; + + case SPEED_1000: + bmcr |= TG3_BMCR_SPEED1000; + break; + }; + + if (tp->link_config.duplex == DUPLEX_FULL) + bmcr |= BMCR_FULLDPLX; + + tg3_readphy(tp, MII_BMCR, &orig_bmcr); + if (bmcr != orig_bmcr) { + tg3_writephy(tp, MII_BMCR, BMCR_LOOPBACK); + for (i = 0; i < 15000; i++) { + u32 tmp; + + udelay(10); + tg3_readphy(tp, MII_BMSR, &tmp); + tg3_readphy(tp, MII_BMSR, &tmp); + if (!(tmp & BMSR_LSTATUS)) { + udelay(40); + break; + } + } + tg3_writephy(tp, MII_BMCR, bmcr); + udelay(40); + } + } else { + tg3_writephy(tp, MII_BMCR, + BMCR_ANENABLE | BMCR_ANRESTART); + } + + if (wait_for_link) { + tp->link_config.active_speed = SPEED_INVALID; + tp->link_config.active_duplex = DUPLEX_INVALID; + for (i = 0; i < 300000; i++) { + u32 tmp; + + udelay(10); + tg3_readphy(tp, MII_BMSR, &tmp); + tg3_readphy(tp, MII_BMSR, &tmp); + if (!(tmp & BMSR_LSTATUS)) + continue; + + tg3_readphy(tp, MII_TG3_AUX_STAT, &tmp); + tg3_aux_stat_to_speed_duplex(tp, tmp, + &tp->link_config.active_speed, + &tp->link_config.active_duplex); + } + if (tp->link_config.active_speed == SPEED_INVALID) + return -EINVAL; + } + + return 0; +} + +static int tg3_init_5401phy_dsp(struct tg3 *tp) +{ + int err; + + /* Turn off tap power management. */ + err = tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c20); + + err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x0012); + err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x1804); + + err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x0013); + err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x1204); + + err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8006); + err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0132); + + err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8006); + err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0232); + + err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f); + err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0a20); + + udelay(40); + + return err; +} + +static int tg3_setup_copper_phy(struct tg3 *tp) +{ + int current_link_up; + u32 bmsr, dummy; + u16 current_speed; + u8 current_duplex; + int i, err; + + tw32(MAC_STATUS, + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + + tp->mi_mode = MAC_MI_MODE_BASE; + tw32(MAC_MI_MODE, tp->mi_mode); + udelay(40); + + if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) { + tg3_readphy(tp, MII_BMSR, &bmsr); + tg3_readphy(tp, MII_BMSR, &bmsr); + + if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE)) + bmsr = 0; + + if (!(bmsr & BMSR_LSTATUS)) { + err = tg3_init_5401phy_dsp(tp); + if (err) + return err; + + tg3_readphy(tp, MII_BMSR, &bmsr); + for (i = 0; i < 1000; i++) { + udelay(10); + tg3_readphy(tp, MII_BMSR, &bmsr); + if (bmsr & BMSR_LSTATUS) { + udelay(40); + break; + } + } + + if ((tp->phy_id & PHY_ID_REV_MASK) == PHY_REV_BCM5401_B0 && + !(bmsr & BMSR_LSTATUS) && + tp->link_config.active_speed == SPEED_1000) { + err = tg3_phy_reset(tp, 1); + if (!err) + err = tg3_init_5401phy_dsp(tp); + if (err) + return err; + } + } + } else if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) { + /* 5701 {A0,B0} CRC bug workaround */ + tg3_writephy(tp, 0x15, 0x0a75); + tg3_writephy(tp, 0x1c, 0x8c68); + tg3_writephy(tp, 0x1c, 0x8d68); + tg3_writephy(tp, 0x1c, 0x8c68); + } + + /* Clear pending interrupts... */ + tg3_readphy(tp, MII_TG3_ISTAT, &dummy); + tg3_readphy(tp, MII_TG3_ISTAT, &dummy); + + if (tp->tg3_flags & TG3_FLAG_USE_MI_INTERRUPT) + tg3_writephy(tp, MII_TG3_IMASK, ~MII_TG3_INT_LINKCHG); + else + tg3_writephy(tp, MII_TG3_IMASK, ~0); + + if (tp->led_mode == led_mode_three_link) + tg3_writephy(tp, MII_TG3_EXT_CTRL, + MII_TG3_EXT_CTRL_LNK3_LED_MODE); + else + tg3_writephy(tp, MII_TG3_EXT_CTRL, 0); + + current_link_up = 0; + current_speed = SPEED_INVALID; + current_duplex = DUPLEX_INVALID; + + tg3_readphy(tp, MII_BMSR, &bmsr); + tg3_readphy(tp, MII_BMSR, &bmsr); + + if (bmsr & BMSR_LSTATUS) { + u32 aux_stat, bmcr; + + tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat); + for (i = 0; i < 2000; i++) { + udelay(10); + tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat); + if (aux_stat) + break; + } + + tg3_aux_stat_to_speed_duplex(tp, aux_stat, + ¤t_speed, + ¤t_duplex); + tg3_readphy(tp, MII_BMCR, &bmcr); + tg3_readphy(tp, MII_BMCR, &bmcr); + if (tp->link_config.autoneg == AUTONEG_ENABLE) { + if (bmcr & BMCR_ANENABLE) { + u32 gig_ctrl; + + current_link_up = 1; + + /* Force autoneg restart if we are exiting + * low power mode. + */ + tg3_readphy(tp, MII_TG3_CTRL, &gig_ctrl); + if (!(gig_ctrl & (MII_TG3_CTRL_ADV_1000_HALF | + MII_TG3_CTRL_ADV_1000_FULL))) { + current_link_up = 0; + } + } else { + current_link_up = 0; + } + } else { + if (!(bmcr & BMCR_ANENABLE) && + tp->link_config.speed == current_speed && + tp->link_config.duplex == current_duplex) { + current_link_up = 1; + } else { + current_link_up = 0; + } + } + + tp->link_config.active_speed = current_speed; + tp->link_config.active_duplex = current_duplex; + } + + if (current_link_up == 1 && + (tp->link_config.active_duplex == DUPLEX_FULL) && + (tp->link_config.autoneg == AUTONEG_ENABLE)) { + u32 local_adv, remote_adv; + + tg3_readphy(tp, MII_ADVERTISE, &local_adv); + local_adv &= (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); + + tg3_readphy(tp, MII_LPA, &remote_adv); + remote_adv &= (LPA_PAUSE_CAP | LPA_PAUSE_ASYM); + + /* If we are not advertising full pause capability, + * something is wrong. Bring the link down and reconfigure. + */ + if (local_adv != ADVERTISE_PAUSE_CAP) { + current_link_up = 0; + } else { + tg3_setup_flow_control(tp, local_adv, remote_adv); + } + } + + if (current_link_up == 0) { + u32 tmp; + + tg3_phy_copper_begin(tp, 0); + + tg3_readphy(tp, MII_BMSR, &tmp); + tg3_readphy(tp, MII_BMSR, &tmp); + if (tmp & BMSR_LSTATUS) + current_link_up = 1; + } + + tp->mac_mode &= ~MAC_MODE_PORT_MODE_MASK; + if (current_link_up == 1) { + if (tp->link_config.active_speed == SPEED_100 || + tp->link_config.active_speed == SPEED_10) + tp->mac_mode |= MAC_MODE_PORT_MODE_MII; + else + tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; + } else + tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; + + tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX; + if (tp->link_config.active_duplex == DUPLEX_HALF) + tp->mac_mode |= MAC_MODE_HALF_DUPLEX; + + tp->mac_mode &= ~MAC_MODE_LINK_POLARITY; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) { + if (current_link_up == 1) + tp->mac_mode |= MAC_MODE_LINK_POLARITY; + tw32(MAC_LED_CTRL, LED_CTRL_PHY_MODE_1); + } else { + if ((tp->led_mode == led_mode_link10) || + (current_link_up == 1 && + tp->link_config.active_speed == SPEED_10)) + tp->mac_mode |= MAC_MODE_LINK_POLARITY; + } + tw32(MAC_MODE, tp->mac_mode); + + if (tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG) { + /* Polled via timer. */ + tw32(MAC_EVENT, 0); + } else { + tw32(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); + } + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 && + current_link_up == 1 && + tp->link_config.active_speed == SPEED_1000 && + ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) || + (tp->tg3_flags & TG3_FLAG_PCI_HIGH_SPEED))) { + udelay(120); + tw32(MAC_STATUS, + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + tg3_write_mem(tp, + NIC_SRAM_FIRMWARE_MBOX, + NIC_SRAM_FIRMWARE_MBOX_MAGIC2); + } + + if (current_link_up != netif_carrier_ok(tp->dev)) { + if (current_link_up) + netif_carrier_on(tp->dev); + else + netif_carrier_off(tp->dev); + tg3_link_report(tp); + } + + return 0; +} + +struct tg3_fiber_aneginfo { + int state; +#define ANEG_STATE_UNKNOWN 0 +#define ANEG_STATE_AN_ENABLE 1 +#define ANEG_STATE_RESTART_INIT 2 +#define ANEG_STATE_RESTART 3 +#define ANEG_STATE_DISABLE_LINK_OK 4 +#define ANEG_STATE_ABILITY_DETECT_INIT 5 +#define ANEG_STATE_ABILITY_DETECT 6 +#define ANEG_STATE_ACK_DETECT_INIT 7 +#define ANEG_STATE_ACK_DETECT 8 +#define ANEG_STATE_COMPLETE_ACK_INIT 9 +#define ANEG_STATE_COMPLETE_ACK 10 +#define ANEG_STATE_IDLE_DETECT_INIT 11 +#define ANEG_STATE_IDLE_DETECT 12 +#define ANEG_STATE_LINK_OK 13 +#define ANEG_STATE_NEXT_PAGE_WAIT_INIT 14 +#define ANEG_STATE_NEXT_PAGE_WAIT 15 + + u32 flags; +#define MR_AN_ENABLE 0x00000001 +#define MR_RESTART_AN 0x00000002 +#define MR_AN_COMPLETE 0x00000004 +#define MR_PAGE_RX 0x00000008 +#define MR_NP_LOADED 0x00000010 +#define MR_TOGGLE_TX 0x00000020 +#define MR_LP_ADV_FULL_DUPLEX 0x00000040 +#define MR_LP_ADV_HALF_DUPLEX 0x00000080 +#define MR_LP_ADV_SYM_PAUSE 0x00000100 +#define MR_LP_ADV_ASYM_PAUSE 0x00000200 +#define MR_LP_ADV_REMOTE_FAULT1 0x00000400 +#define MR_LP_ADV_REMOTE_FAULT2 0x00000800 +#define MR_LP_ADV_NEXT_PAGE 0x00001000 +#define MR_TOGGLE_RX 0x00002000 +#define MR_NP_RX 0x00004000 + +#define MR_LINK_OK 0x80000000 + + unsigned long link_time, cur_time; + + u32 ability_match_cfg; + int ability_match_count; + + char ability_match, idle_match, ack_match; + + u32 txconfig, rxconfig; +#define ANEG_CFG_NP 0x00000080 +#define ANEG_CFG_ACK 0x00000040 +#define ANEG_CFG_RF2 0x00000020 +#define ANEG_CFG_RF1 0x00000010 +#define ANEG_CFG_PS2 0x00000001 +#define ANEG_CFG_PS1 0x00008000 +#define ANEG_CFG_HD 0x00004000 +#define ANEG_CFG_FD 0x00002000 +#define ANEG_CFG_INVAL 0x00001f06 + +}; +#define ANEG_OK 0 +#define ANEG_DONE 1 +#define ANEG_TIMER_ENAB 2 +#define ANEG_FAILED -1 + + +static int tg3_fiber_aneg_smachine(struct tg3 *tp, + struct tg3_fiber_aneginfo *ap) +{ + unsigned long delta; + u32 rx_cfg_reg; + int ret; + + if (ap->state == ANEG_STATE_UNKNOWN) { + ap->rxconfig = 0; + ap->link_time = 0; + ap->cur_time = 0; + ap->ability_match_cfg = 0; + ap->ability_match_count = 0; + ap->ability_match = 0; + ap->idle_match = 0; + ap->ack_match = 0; + } + ap->cur_time++; + + if (tr32(MAC_STATUS) & MAC_STATUS_RCVD_CFG) { + rx_cfg_reg = tr32(MAC_RX_AUTO_NEG); + + if (rx_cfg_reg != ap->ability_match_cfg) { + ap->ability_match_cfg = rx_cfg_reg; + ap->ability_match = 0; + ap->ability_match_count = 0; + } else { + if (++ap->ability_match_count > 1) + ap->ability_match = 1; + } + if (rx_cfg_reg & ANEG_CFG_ACK) + ap->ack_match = 1; + else + ap->ack_match = 0; + + ap->idle_match = 0; + } else { + ap->idle_match = 1; + ap->ability_match_cfg = 0; + ap->ability_match_count = 0; + ap->ability_match = 0; + ap->ack_match = 0; + + rx_cfg_reg = 0; + } + + ap->rxconfig = rx_cfg_reg; + ret = ANEG_OK; + + switch(ap->state) { + case ANEG_STATE_UNKNOWN: + if (ap->flags & (MR_AN_ENABLE | MR_RESTART_AN)) + ap->state = ANEG_STATE_AN_ENABLE; + + /* fallthru */ + case ANEG_STATE_AN_ENABLE: + ap->flags &= ~(MR_AN_COMPLETE | MR_PAGE_RX); + if (ap->flags & MR_AN_ENABLE) { + ap->link_time = 0; + ap->cur_time = 0; + ap->ability_match_cfg = 0; + ap->ability_match_count = 0; + ap->ability_match = 0; + ap->idle_match = 0; + ap->ack_match = 0; + + ap->state = ANEG_STATE_RESTART_INIT; + } else { + ap->state = ANEG_STATE_DISABLE_LINK_OK; + } + break; + + case ANEG_STATE_RESTART_INIT: + ap->link_time = ap->cur_time; + ap->flags &= ~(MR_NP_LOADED); + ap->txconfig = 0; + tw32(MAC_TX_AUTO_NEG, 0); + tp->mac_mode |= MAC_MODE_SEND_CONFIGS; + tw32(MAC_MODE, tp->mac_mode); + ret = ANEG_TIMER_ENAB; + ap->state = ANEG_STATE_RESTART; + + /* fallthru */ + case ANEG_STATE_RESTART: + delta = ap->cur_time - ap->link_time; + if (delta > 100000) + ap->state = ANEG_STATE_ABILITY_DETECT_INIT; + else + ret = ANEG_TIMER_ENAB; + break; + + case ANEG_STATE_DISABLE_LINK_OK: + ret = ANEG_DONE; + break; + + case ANEG_STATE_ABILITY_DETECT_INIT: + ap->flags &= ~(MR_TOGGLE_TX); + ap->txconfig = (ANEG_CFG_FD | ANEG_CFG_PS1); + tw32(MAC_TX_AUTO_NEG, ap->txconfig); + tp->mac_mode |= MAC_MODE_SEND_CONFIGS; + tw32(MAC_MODE, tp->mac_mode); + ap->state = ANEG_STATE_ABILITY_DETECT; + break; + + case ANEG_STATE_ABILITY_DETECT: + if (ap->ability_match != 0 && ap->rxconfig != 0) + ap->state = ANEG_STATE_ACK_DETECT_INIT; + break; + + case ANEG_STATE_ACK_DETECT_INIT: + ap->txconfig |= ANEG_CFG_ACK; + tw32(MAC_TX_AUTO_NEG, ap->txconfig); + tp->mac_mode |= MAC_MODE_SEND_CONFIGS; + tw32(MAC_MODE, tp->mac_mode); + ap->state = ANEG_STATE_ACK_DETECT; + + /* fallthru */ + case ANEG_STATE_ACK_DETECT: + if (ap->ack_match != 0) { + if ((ap->rxconfig & ~ANEG_CFG_ACK) == + (ap->ability_match_cfg & ~ANEG_CFG_ACK)) + ap->state = ANEG_STATE_COMPLETE_ACK_INIT; + else + ap->state = ANEG_STATE_AN_ENABLE; + } else if (ap->ability_match != 0 && + ap->rxconfig == 0) { + ap->state = ANEG_STATE_AN_ENABLE; + } + break; + + case ANEG_STATE_COMPLETE_ACK_INIT: + if (ap->rxconfig & ANEG_CFG_INVAL) { + ret = ANEG_FAILED; + break; + } + ap->flags &= ~(MR_LP_ADV_FULL_DUPLEX | + MR_LP_ADV_HALF_DUPLEX | + MR_LP_ADV_SYM_PAUSE | + MR_LP_ADV_ASYM_PAUSE | + MR_LP_ADV_REMOTE_FAULT1 | + MR_LP_ADV_REMOTE_FAULT2 | + MR_LP_ADV_NEXT_PAGE | + MR_TOGGLE_RX | + MR_NP_RX); + if (ap->rxconfig & ANEG_CFG_FD) + ap->flags |= MR_LP_ADV_FULL_DUPLEX; + if (ap->rxconfig & ANEG_CFG_HD) + ap->flags |= MR_LP_ADV_FULL_DUPLEX; + if (ap->rxconfig & ANEG_CFG_PS1) + ap->flags |= MR_LP_ADV_SYM_PAUSE; + if (ap->rxconfig & ANEG_CFG_PS2) + ap->flags |= MR_LP_ADV_ASYM_PAUSE; + if (ap->rxconfig & ANEG_CFG_RF1) + ap->flags |= MR_LP_ADV_REMOTE_FAULT1; + if (ap->rxconfig & ANEG_CFG_RF2) + ap->flags |= MR_LP_ADV_REMOTE_FAULT2; + if (ap->rxconfig & ANEG_CFG_NP) + ap->flags |= MR_LP_ADV_NEXT_PAGE; + + ap->link_time = ap->cur_time; + + ap->flags ^= (MR_TOGGLE_TX); + if (ap->rxconfig & 0x0008) + ap->flags |= MR_TOGGLE_RX; + if (ap->rxconfig & ANEG_CFG_NP) + ap->flags |= MR_NP_RX; + ap->flags |= MR_PAGE_RX; + + ap->state = ANEG_STATE_COMPLETE_ACK; + ret = ANEG_TIMER_ENAB; + break; + + case ANEG_STATE_COMPLETE_ACK: + if (ap->ability_match != 0 && + ap->rxconfig == 0) { + ap->state = ANEG_STATE_AN_ENABLE; + break; + } + delta = ap->cur_time - ap->link_time; + if (delta > 100000) { + if (!(ap->flags & (MR_LP_ADV_NEXT_PAGE))) { + ap->state = ANEG_STATE_IDLE_DETECT_INIT; + } else { + if ((ap->txconfig & 0x0080) == 0 && + !(ap->flags & MR_NP_RX)) + ap->state = ANEG_STATE_IDLE_DETECT_INIT; + else + ret = ANEG_FAILED; + } + } + break; + + case ANEG_STATE_IDLE_DETECT_INIT: + ap->link_time = ap->cur_time; + tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; + tw32(MAC_MODE, tp->mac_mode); + ap->state = ANEG_STATE_IDLE_DETECT; + ret = ANEG_TIMER_ENAB; + break; + + case ANEG_STATE_IDLE_DETECT: + if (ap->ability_match != 0 && + ap->rxconfig == 0) { + ap->state = ANEG_STATE_AN_ENABLE; + break; + } + delta = ap->cur_time - ap->link_time; + if (delta > 100000) { + /* XXX another gem from the Broadcom driver :( */ + ap->state = ANEG_STATE_LINK_OK; + } + break; + + case ANEG_STATE_LINK_OK: + ap->flags |= (MR_AN_COMPLETE | MR_LINK_OK); + ret = ANEG_DONE; + break; + + case ANEG_STATE_NEXT_PAGE_WAIT_INIT: + /* ??? unimplemented */ + break; + + case ANEG_STATE_NEXT_PAGE_WAIT: + /* ??? unimplemented */ + break; + + default: + ret = ANEG_FAILED; + break; + }; + + return ret; +} + +static int tg3_setup_fiber_phy(struct tg3 *tp) +{ + int current_link_up; + int i; + + tp->mac_mode &= ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX); + tp->mac_mode |= MAC_MODE_PORT_MODE_TBI; + tw32(MAC_MODE, tp->mac_mode); + udelay(40); + + /* Reset when initting first time or we have a link. */ + if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) || + (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED)) { + /* Set PLL lock range. */ + tg3_writephy(tp, 0x16, 0x8007); + + /* SW reset */ + tg3_writephy(tp, 0x00, 0x8000); + + /* Wait for reset to complete. */ + /* XXX schedule_timeout() ... */ + for (i = 0; i < 500; i++) + udelay(10); + + /* Config mode; select PMA/Ch 1 regs. */ + tg3_writephy(tp, 0x10, 0x8411); + + /* Enable auto-lock and comdet, select txclk for tx. */ + tg3_writephy(tp, 0x11, 0x0a10); + + tg3_writephy(tp, 0x18, 0x00a0); + tg3_writephy(tp, 0x16, 0x41ff); + + /* Assert and deassert POR. */ + tg3_writephy(tp, 0x13, 0x0400); + udelay(40); + tg3_writephy(tp, 0x13, 0x0000); + + tg3_writephy(tp, 0x11, 0x0a50); + udelay(40); + tg3_writephy(tp, 0x11, 0x0a10); + + /* Wait for signal to stabilize */ + /* XXX schedule_timeout() ... */ + for (i = 0; i < 15000; i++) + udelay(10); + + /* Deselect the channel register so we can read the PHYID + * later. + */ + tg3_writephy(tp, 0x10, 0x8011); + } + + /* Enable link change interrupt. */ + tw32(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); + + current_link_up = 0; + if (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) { + if (tp->link_config.autoneg == AUTONEG_ENABLE) { + struct tg3_fiber_aneginfo aninfo; + int status = ANEG_FAILED; + + memset(&aninfo, 0, sizeof(aninfo)); + aninfo.flags |= (MR_AN_ENABLE); + + for (i = 0; i < 6; i++) { + u32 tmp; + + tw32(MAC_TX_AUTO_NEG, 0); + + tmp = tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK; + tw32(MAC_MODE, tmp | MAC_MODE_PORT_MODE_GMII); + udelay(20); + + tw32(MAC_MODE, tp->mac_mode | MAC_MODE_SEND_CONFIGS); + + aninfo.state = ANEG_STATE_UNKNOWN; + aninfo.cur_time = 0; + while (aninfo.cur_time < 95000) { + status = tg3_fiber_aneg_smachine(tp, &aninfo); + if (status == ANEG_DONE || + status == ANEG_FAILED) + break; + + udelay(1); + } + if (status == ANEG_DONE || + status == ANEG_FAILED) + break; + } + + tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; + tw32(MAC_MODE, tp->mac_mode); + + if (status == ANEG_DONE && + (aninfo.flags & MR_AN_COMPLETE) && + (aninfo.flags & MR_LINK_OK) && + (aninfo.flags & MR_LP_ADV_FULL_DUPLEX)) { + u32 local_adv, remote_adv; + + local_adv = ADVERTISE_PAUSE_CAP; + remote_adv = 0; + if (aninfo.flags & MR_LP_ADV_SYM_PAUSE) + remote_adv |= LPA_PAUSE_CAP; + if (aninfo.flags & MR_LP_ADV_ASYM_PAUSE) + remote_adv |= LPA_PAUSE_ASYM; + + tg3_setup_flow_control(tp, local_adv, remote_adv); + + current_link_up = 1; + } + for (i = 0; i < 60; i++) { + udelay(20); + tw32(MAC_STATUS, + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + + udelay(20); + if ((tr32(MAC_STATUS) & + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)) == 0) + break; + } + } else { + /* Forcing 1000FD link up. */ + current_link_up = 1; + } + } + + tp->mac_mode &= ~MAC_MODE_LINK_POLARITY; + tw32(MAC_MODE, tp->mac_mode); + + tp->hw_status->status = + (SD_STATUS_UPDATED | + (tp->hw_status->status & ~SD_STATUS_LINK_CHG)); + + for (i = 0; i < 100; i++) { + tw32(MAC_STATUS, + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + udelay(5); + + if ((tr32(MAC_STATUS) & + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)) == 0) + break; + } + + if ((tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) == 0) + current_link_up = 0; + + if (current_link_up == 1) { + tp->link_config.active_speed = SPEED_1000; + tp->link_config.active_duplex = DUPLEX_FULL; + } else { + tp->link_config.active_speed = SPEED_INVALID; + tp->link_config.active_duplex = DUPLEX_INVALID; + } + + if (current_link_up != netif_carrier_ok(tp->dev)) { + if (current_link_up) + netif_carrier_on(tp->dev); + else + netif_carrier_off(tp->dev); + tg3_link_report(tp); + } + + if ((tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) == 0) { + tw32(MAC_MODE, tp->mac_mode | MAC_MODE_LINK_POLARITY); + if (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) { + udelay(1); + tw32(MAC_MODE, tp->mac_mode); + } + } + + return 0; +} + +static int tg3_setup_phy(struct tg3 *tp) +{ + int err; + + if (tp->phy_id == PHY_ID_SERDES) { + err = tg3_setup_fiber_phy(tp); + } else { + err = tg3_setup_copper_phy(tp); + } + + if (tp->link_config.active_speed == SPEED_1000 && + tp->link_config.active_duplex == DUPLEX_HALF) + tw32(MAC_TX_LENGTHS, + ((2 << TX_LENGTHS_IPG_CRS_SHIFT) | + (6 << TX_LENGTHS_IPG_SHIFT) | + (0xff << TX_LENGTHS_SLOT_TIME_SHIFT))); + else + tw32(MAC_TX_LENGTHS, + ((2 << TX_LENGTHS_IPG_CRS_SHIFT) | + (6 << TX_LENGTHS_IPG_SHIFT) | + (32 << TX_LENGTHS_SLOT_TIME_SHIFT))); + + return err; +} + +/* Tigon3 never reports partial packet sends. So we do not + * need special logic to handle SKBs that have not had all + * of their frags sent yet, like SunGEM does. + */ +static void tg3_tx(struct tg3 *tp) +{ + u32 hw_idx = tp->hw_status->idx[0].tx_consumer; + u32 sw_idx = tp->tx_cons; + + while (sw_idx != hw_idx) { + struct ring_info *ri = &tp->tx_buffers[sw_idx]; + struct sk_buff *skb = ri->skb; + int i; + + if (unlikely(skb == NULL)) + BUG(); + + pci_unmap_single(tp->pdev, ri->mapping, + (skb->len - skb->data_len), + PCI_DMA_TODEVICE); + + ri->skb = NULL; + + sw_idx = NEXT_TX(sw_idx); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + if (unlikely(sw_idx == hw_idx)) + BUG(); + + ri = &tp->tx_buffers[sw_idx]; + if (unlikely(ri->skb != NULL)) + BUG(); + + pci_unmap_page(tp->pdev, + ri->mapping, + skb_shinfo(skb)->frags[i].size, + PCI_DMA_TODEVICE); + + sw_idx = NEXT_TX(sw_idx); + } + + dev_kfree_skb_irq(skb); + } + + tp->tx_cons = sw_idx; + + if (netif_queue_stopped(tp->dev) && + (TX_BUFFS_AVAIL(tp) > TG3_TX_WAKEUP_THRESH)) + netif_wake_queue(tp->dev); +} + +/* Returns size of skb allocated or < 0 on error. + * + * We only need to fill in the address because the other members + * of the RX descriptor are invariant, see tg3_init_rings. + * + * Note the purposeful assymetry of cpu vs. chip accesses. For + * posting buffers we only dirty the first cache line of the RX + * descriptor (containing the address). Whereas for the RX status + * buffers the cpu only reads the last cacheline of the RX descriptor + * (to fetch the error flags, vlan tag, checksum, and opaque cookie). + */ +static int tg3_alloc_rx_skb(struct tg3 *tp, u32 opaque_key, + int src_idx, u32 dest_idx_unmasked) +{ + struct tg3_rx_buffer_desc *desc; + struct ring_info *map, *src_map; + struct sk_buff *skb; + dma_addr_t mapping; + int skb_size, dest_idx; + + src_map = NULL; + switch (opaque_key) { + case RXD_OPAQUE_RING_STD: + dest_idx = dest_idx_unmasked % TG3_RX_RING_SIZE; + desc = &tp->rx_std[dest_idx]; + map = &tp->rx_std_buffers[dest_idx]; + if (src_idx >= 0) + src_map = &tp->rx_std_buffers[src_idx]; + skb_size = RX_PKT_BUF_SZ; + break; + + case RXD_OPAQUE_RING_JUMBO: + dest_idx = dest_idx_unmasked % TG3_RX_JUMBO_RING_SIZE; + desc = &tp->rx_jumbo[dest_idx]; + map = &tp->rx_jumbo_buffers[dest_idx]; + if (src_idx >= 0) + src_map = &tp->rx_jumbo_buffers[src_idx]; + skb_size = RX_JUMBO_PKT_BUF_SZ; + break; +#if TG3_MINI_RING_WORKS + case RXD_OPAQUE_RING_MINI: + dest_idx = dest_idx_unmasked % TG3_RX_MINI_RING_SIZE; + desc = &tp->rx_mini[dest_idx]; + map = &tp->rx_mini_buffers[dest_idx]; + if (src_idx >= 0) + src_map = &tp->rx_mini_buffers[src_idx]; + skb_size = RX_MINI_PKT_BUF_SZ; + break; +#endif + default: + return -EINVAL; + }; + + /* Do not overwrite any of the map or rp information + * until we are sure we can commit to a new buffer. + * + * Callers depend upon this behavior and assume that + * we leave everything unchanged if we fail. + */ + skb = dev_alloc_skb(skb_size); + if (skb == NULL) + return -ENOMEM; + + skb->dev = tp->dev; + skb_reserve(skb, tp->rx_offset); + + mapping = pci_map_single(tp->pdev, skb->data, + skb_size - tp->rx_offset, + PCI_DMA_FROMDEVICE); + + map->skb = skb; + map->mapping = mapping; + + if (src_map != NULL) + src_map->skb = NULL; + + desc->addr_hi = ((u64)mapping >> 32); + desc->addr_lo = ((u64)mapping & 0xffffffff); + + return skb_size; +} + +/* We only need to move over in the address because the other + * members of the RX descriptor are invariant. See notes above + * tg3_alloc_rx_skb for full details. + */ +static void tg3_recycle_rx(struct tg3 *tp, u32 opaque_key, + int src_idx, int dest_idx_unmasked) +{ + struct tg3_rx_buffer_desc *src_desc, *dest_desc; + struct ring_info *src_map, *dest_map; + int dest_idx; + + switch (opaque_key) { + case RXD_OPAQUE_RING_STD: + dest_idx = dest_idx_unmasked % TG3_RX_RING_SIZE; + dest_desc = &tp->rx_std[dest_idx]; + dest_map = &tp->rx_std_buffers[dest_idx]; + src_desc = &tp->rx_std[src_idx]; + src_map = &tp->rx_std_buffers[src_idx]; + break; + + case RXD_OPAQUE_RING_JUMBO: + dest_idx = dest_idx_unmasked % TG3_RX_JUMBO_RING_SIZE; + dest_desc = &tp->rx_jumbo[dest_idx]; + dest_map = &tp->rx_jumbo_buffers[dest_idx]; + src_desc = &tp->rx_jumbo[src_idx]; + src_map = &tp->rx_jumbo_buffers[src_idx]; + break; +#if TG3_MINI_RING_WORKS + case RXD_OPAQUE_RING_MINI: + dest_idx = dest_idx_unmasked % TG3_RX_MINI_RING_SIZE; + dest_desc = &tp->rx_mini[dest_idx]; + dest_map = &tp->rx_mini_buffers[dest_idx]; + src_desc = &tp->rx_mini[src_idx]; + src_map = &tp->rx_mini_buffers[src_idx]; + break; +#endif + default: + return; + }; + + dest_map->skb = src_map->skb; + dest_map->mapping = src_map->mapping; + dest_desc->addr_hi = src_desc->addr_hi; + dest_desc->addr_lo = src_desc->addr_lo; + + src_map->skb = NULL; +} + +#if TG3_VLAN_TAG_USED +static int tg3_vlan_rx(struct tg3 *tp, struct sk_buff *skb, u16 vlan_tag) +{ + return vlan_hwaccel_rx(skb, tp->vlgrp, vlan_tag); +} +#endif + +/* The RX ring scheme is composed of multiple rings which post fresh + * buffers to the chip, and one special ring the chip uses to report + * status back to the host. + * + * The special ring reports the status of received packets to the + * host. The chip does not write into the original descriptor the + * RX buffer was obtained from. The chip simply takes the original + * descriptor as provided by the host, updates the status and length + * field, then writes this into the next status ring entry. + * + * Each ring the host uses to post buffers to the chip is described + * by a TG3_BDINFO entry in the chips SRAM area. When a packet arrives, + * it is first placed into the on-chip ram. When the packet's length + * is known, it walks down the TG3_BDINFO entries to select the ring. + * Each TG3_BDINFO specifies a MAXLEN field and the first TG3_BDINFO + * which is within the range of the new packet's length is chosen. + * + * The "seperate ring for rx status" scheme may sound queer, but it makes + * sense from a cache coherency perspective. If only the host writes + * to the buffer post rings, and only the chip writes to the rx status + * rings, then cache lines never move beyond shared-modified state. + * If both the host and chip were to write into the same ring, cache line + * eviction could occur since both entities want it in an exclusive state. + */ +static void tg3_rx(struct tg3 *tp) +{ + u32 work_mask; + u32 rx_rcb_ptr = tp->rx_rcb_ptr; + u16 hw_idx, sw_idx; + + hw_idx = tp->hw_status->idx[0].rx_producer; + sw_idx = rx_rcb_ptr % TG3_RX_RCB_RING_SIZE; + work_mask = 0; + while (sw_idx != hw_idx) { + struct tg3_rx_buffer_desc *desc = &tp->rx_rcb[sw_idx]; + unsigned int len; + struct sk_buff *skb; + dma_addr_t dma_addr; + u32 opaque_key, desc_idx, *post_ptr; + + desc_idx = desc->opaque & RXD_OPAQUE_INDEX_MASK; + opaque_key = desc->opaque & RXD_OPAQUE_RING_MASK; + if (opaque_key == RXD_OPAQUE_RING_STD) { + dma_addr = tp->rx_std_buffers[desc_idx].mapping; + skb = tp->rx_std_buffers[desc_idx].skb; + post_ptr = &tp->rx_std_ptr; + } else if (opaque_key == RXD_OPAQUE_RING_JUMBO) { + dma_addr = tp->rx_jumbo_buffers[desc_idx].mapping; + skb = tp->rx_jumbo_buffers[desc_idx].skb; + post_ptr = &tp->rx_jumbo_ptr; + } +#if TG3_MINI_RING_WORKS + else if (opaque_key == RXD_OPAQUE_RING_MINI) { + dma_addr = tp->rx_mini_buffers[desc_idx].mapping; + skb = tp->rx_mini_buffers[desc_idx].skb; + post_ptr = &tp->rx_mini_ptr; + } +#endif + else { + goto next_pkt_nopost; + } + + work_mask |= opaque_key; + + if ((desc->err_vlan & RXD_ERR_MASK) != 0 && + (desc->err_vlan != RXD_ERR_ODD_NIBBLE_RCVD_MII)) { + drop_it: + tg3_recycle_rx(tp, opaque_key, + desc_idx, *post_ptr); + drop_it_no_recycle: + /* Other statistics kept track of by card. */ + tp->net_stats.rx_dropped++; + goto next_pkt; + } + + len = ((desc->idx_len & RXD_LEN_MASK) >> RXD_LEN_SHIFT) - 4; /* omit crc */ + + /* Kill the copy case if we ever get the mini ring working. */ + if (len > RX_COPY_THRESHOLD) { + int skb_size; + + skb_size = tg3_alloc_rx_skb(tp, opaque_key, + desc_idx, *post_ptr); + if (skb_size < 0) + goto drop_it; + + pci_unmap_single(tp->pdev, dma_addr, + skb_size - tp->rx_offset, + PCI_DMA_FROMDEVICE); + + skb_put(skb, len); + } else { + struct sk_buff *copy_skb; + + tg3_recycle_rx(tp, opaque_key, + desc_idx, *post_ptr); + + copy_skb = dev_alloc_skb(len + 2); + if (copy_skb == NULL) + goto drop_it_no_recycle; + + copy_skb->dev = tp->dev; + skb_reserve(copy_skb, 2); + skb_put(copy_skb, len); + pci_dma_sync_single(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); + memcpy(copy_skb->data, skb->data, len); + + /* We'll reuse the original ring buffer. */ + skb = copy_skb; + } + + if (!(tp->tg3_flags & TG3_FLAG_BROKEN_CHECKSUMS) && + (desc->type_flags & RXD_FLAG_TCPUDP_CSUM)) { + skb->csum = htons((desc->ip_tcp_csum & RXD_TCPCSUM_MASK) + >> RXD_TCPCSUM_SHIFT); + skb->ip_summed = CHECKSUM_HW; + } else { + skb->ip_summed = CHECKSUM_NONE; + } + + skb->protocol = eth_type_trans(skb, tp->dev); +#if TG3_VLAN_TAG_USED + if (tp->vlgrp != NULL && + desc->type_flags & RXD_FLAG_VLAN) { + tg3_vlan_rx(tp, skb, + desc->err_vlan & RXD_VLAN_MASK); + } else +#endif + netif_rx(skb); + + tp->dev->last_rx = jiffies; + +next_pkt: + (*post_ptr)++; +next_pkt_nopost: + rx_rcb_ptr++; + sw_idx = rx_rcb_ptr % TG3_RX_RCB_RING_SIZE; + } + + /* ACK the status ring. */ + tp->rx_rcb_ptr = rx_rcb_ptr; + tw32_mailbox(MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW, + (rx_rcb_ptr % TG3_RX_RCB_RING_SIZE)); + + /* Refill RX ring(s). */ + if (work_mask & RXD_OPAQUE_RING_STD) { + sw_idx = tp->rx_std_ptr % TG3_RX_RING_SIZE; + tw32_mailbox(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW, + sw_idx); + } + if (work_mask & RXD_OPAQUE_RING_JUMBO) { + sw_idx = tp->rx_jumbo_ptr % TG3_RX_JUMBO_RING_SIZE; + tw32_mailbox(MAILBOX_RCV_JUMBO_PROD_IDX + TG3_64BIT_REG_LOW, + sw_idx); + } +#if TG3_MINI_RING_WORKS + if (work_mask & RXD_OPAQUE_RING_MINI) { + sw_idx = tp->rx_mini_ptr % TG3_RX_MINI_RING_SIZE; + tw32_mailbox(MAILBOX_RCV_MINI_PROD_IDX + TG3_64BIT_REG_LOW, + sw_idx); + } +#endif +} + +#define RATE_SAMPLE_INTERVAL (1 * HZ) + +#define PKT_RATE_LOW 22000 +#define PKT_RATE_HIGH 61000 + +static void tg3_rate_sample(struct tg3 *tp, unsigned long ticks) +{ + u32 delta, rx_now, tx_now; + int new_vals; + + rx_now = tp->hw_stats->rx_ucast_packets.low; + tx_now = tp->hw_stats->COS_out_packets[0].low; + + delta = (rx_now - tp->last_rx_count); + delta += (tx_now - tp->last_tx_count); + delta /= (ticks / RATE_SAMPLE_INTERVAL); + + tp->last_rx_count = rx_now; + tp->last_tx_count = tx_now; + + new_vals = 0; + if (delta < PKT_RATE_LOW) { + if (tp->coalesce_config.rx_max_coalesced_frames != + LOW_RXMAX_FRAMES) { + tp->coalesce_config.rx_max_coalesced_frames = + LOW_RXMAX_FRAMES; + tp->coalesce_config.rx_coalesce_ticks = + LOW_RXCOL_TICKS; + new_vals = 1; + } + } else if (delta < PKT_RATE_HIGH) { + if (tp->coalesce_config.rx_max_coalesced_frames != + DEFAULT_RXMAX_FRAMES) { + tp->coalesce_config.rx_max_coalesced_frames = + DEFAULT_RXMAX_FRAMES; + tp->coalesce_config.rx_coalesce_ticks = + DEFAULT_RXCOL_TICKS; + new_vals = 1; + } + } else { + if (tp->coalesce_config.rx_max_coalesced_frames != + HIGH_RXMAX_FRAMES) { + tp->coalesce_config.rx_max_coalesced_frames = + HIGH_RXMAX_FRAMES; + tp->coalesce_config.rx_coalesce_ticks = + HIGH_RXCOL_TICKS; + new_vals = 1; + } + } + + if (new_vals) { + tw32(HOSTCC_RXCOL_TICKS, + tp->coalesce_config.rx_coalesce_ticks); + tw32(HOSTCC_RXMAX_FRAMES, + tp->coalesce_config.rx_max_coalesced_frames); + } + + tp->last_rate_sample = jiffies; +} + +static void tg3_interrupt_main_work(struct tg3 *tp) +{ + struct tg3_hw_status *sblk = tp->hw_status; + int did_pkts; + + if (!(tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG)) { + if (sblk->status & SD_STATUS_LINK_CHG) { + sblk->status = SD_STATUS_UPDATED | + (sblk->status & ~SD_STATUS_LINK_CHG); + tg3_setup_phy(tp); + } + } + + did_pkts = 0; + if (sblk->idx[0].rx_producer != tp->rx_rcb_ptr) { + tg3_rx(tp); + did_pkts = 1; + } + + if (sblk->idx[0].tx_consumer != tp->tx_cons) { + tg3_tx(tp); + did_pkts = 1; + } + + if (did_pkts) { + unsigned long ticks = jiffies - tp->last_rate_sample; + + if (ticks >= RATE_SAMPLE_INTERVAL) + tg3_rate_sample(tp, ticks); + } +} + +static void tg3_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct tg3 *tp = dev->priv; + struct tg3_hw_status *sblk = tp->hw_status; + + spin_lock(&tp->lock); + + while (sblk->status & SD_STATUS_UPDATED) { + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, + 0x00000001); + sblk->status &= ~SD_STATUS_UPDATED; + + tg3_interrupt_main_work(tp); + + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, + 0x00000000); + tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW); + } + + spin_unlock(&tp->lock); +} + +static void tg3_interrupt_tagged(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct tg3 *tp = dev->priv; + struct tg3_hw_status *sblk = tp->hw_status; + + spin_lock(&tp->lock); + + if (sblk->status & SD_STATUS_UPDATED) { + u32 oldtag; + + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, + 0x00000001); + oldtag = sblk->status_tag; + + while (1) { + u32 newtag; + + sblk->status &= ~SD_STATUS_UPDATED; + barrier(); + + tg3_interrupt_main_work(tp); + + newtag = sblk->status_tag; + if (newtag == oldtag) { + tw32_mailbox(MAILBOX_INTERRUPT_0 + + TG3_64BIT_REG_LOW, + newtag << 24); + break; + } + oldtag = newtag; + } + } + + spin_unlock(&tp->lock); +} + +static void tg3_init_rings(struct tg3 *); +static int tg3_init_hw(struct tg3 *); + +static void tg3_tx_timeout(struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + + printk(KERN_ERR "%s: transmit timed out, resetting\n", + dev->name); + + spin_lock_irq(&tp->lock); + + tg3_halt(tp); + tg3_init_rings(tp); + tg3_init_hw(tp); + + spin_unlock_irq(&tp->lock); + + netif_wake_queue(dev); +} + +#if !PCI_DMA_BUS_IS_PHYS +static void tg3_set_txd_addr(struct tg3 *tp, int entry, dma_addr_t mapping) +{ + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { + struct tg3_tx_buffer_desc *txd = &tp->tx_ring[entry]; + + txd->addr_hi = ((u64) mapping >> 32); + txd->addr_lo = ((u64) mapping & 0xffffffff); + } else { + unsigned long txd; + + txd = (tp->regs + + NIC_SRAM_WIN_BASE + + NIC_SRAM_TX_BUFFER_DESC); + txd += (entry * TXD_SIZE); + + writel(((u64) mapping >> 32), + txd + TXD_ADDR + TG3_64BIT_REG_HIGH); + + writel(((u64) mapping & 0xffffffff), + txd + TXD_ADDR + TG3_64BIT_REG_LOW); + } +} +#endif + +static void tg3_set_txd(struct tg3 *, int, dma_addr_t, int, u32, int); + +static int tigon3_4gb_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb, + u32 guilty_entry, int guilty_len, + u32 last_plus_one, u32 *start) +{ + dma_addr_t new_addr; + u32 entry = *start; + int i; + +#if !PCI_DMA_BUS_IS_PHYS + /* IOMMU, just map the guilty area again which is guarenteed to + * use different addresses. + */ + + i = 0; + while (entry != guilty_entry) { + entry = NEXT_TX(entry); + i++; + } + if (i == 0) { + new_addr = pci_map_single(tp->pdev, skb->data, guilty_len, + PCI_DMA_TODEVICE); + } else { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1]; + + new_addr = pci_map_page(tp->pdev, + frag->page, frag->page_offset, + guilty_len, PCI_DMA_TODEVICE); + } + pci_unmap_single(tp->pdev, tp->tx_buffers[guilty_entry].mapping, + guilty_len, PCI_DMA_TODEVICE); + tg3_set_txd_addr(tp, guilty_entry, new_addr); + tp->tx_buffers[guilty_entry].mapping = new_addr; + *start = last_plus_one; +#else + /* Oh well, no IOMMU, have to allocate a whole new SKB. */ + struct sk_buff *new_skb = skb_copy(skb, GFP_ATOMIC); + + if (!new_skb) { + dev_kfree_skb(skb); + return -1; + } + + /* NOTE: Broadcom's driver botches this case up really bad. + * This is especially true if any of the frag pages + * are in highmem. It will instantly oops in that case. + */ + + /* New SKB is guarenteed to be linear. */ + entry = *start; + new_addr = pci_map_single(tp->pdev, new_skb->data, new_skb->len, + PCI_DMA_TODEVICE); + tg3_set_txd(tp, entry, new_addr, new_skb->len, + (skb->ip_summed == CHECKSUM_HW) ? + TXD_FLAG_TCPUDP_CSUM : 0, 1); + *start = NEXT_TX(entry); + + /* Now clean up the sw ring entries. */ + i = 0; + while (entry != last_plus_one) { + int len; + + if (i == 0) + len = skb->len - skb->data_len; + else + len = skb_shinfo(skb)->frags[i-1].size; + pci_unmap_single(tp->pdev, tp->tx_buffers[entry].mapping, + len, PCI_DMA_TODEVICE); + if (i == 0) { + tp->tx_buffers[entry].skb = new_skb; + tp->tx_buffers[entry].mapping = new_addr; + } else { + tp->tx_buffers[entry].skb = NULL; + } + entry = NEXT_TX(entry); + } + + dev_kfree_skb(skb); +#endif + + return 0; +} + +static void tg3_set_txd(struct tg3 *tp, int entry, + dma_addr_t mapping, int len, u32 flags, + int is_end) +{ +#if TG3_VLAN_TAG_USED + u16 vlan_tag = 0; +#endif + + if (is_end) + flags |= TXD_FLAG_END; +#if TG3_VLAN_TAG_USED + if (flags & TXD_FLAG_VLAN) { + vlan_tag = flags >> 16; + flags &= 0xffff; + } +#endif + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { + struct tg3_tx_buffer_desc *txd = &tp->tx_ring[entry]; + + txd->addr_hi = ((u64) mapping >> 32); + txd->addr_lo = ((u64) mapping & 0xffffffff); + txd->len_flags = (len << TXD_LEN_SHIFT) | flags; +#if TG3_VLAN_TAG_USED + txd->vlan_tag = vlan_tag << TXD_VLAN_TAG_SHIFT; +#endif + } else { + unsigned long txd; + + txd = (tp->regs + + NIC_SRAM_WIN_BASE + + NIC_SRAM_TX_BUFFER_DESC); + txd += (entry * TXD_SIZE); + + /* Save some PIOs */ + if (((u64) tp->tx_buffers[entry].mapping >> 32) != + ((u64) mapping >> 32)) + writel(((u64) mapping >> 32), + txd + TXD_ADDR + TG3_64BIT_REG_HIGH); + + writel(((u64) mapping & 0xffffffff), + txd + TXD_ADDR + TG3_64BIT_REG_LOW); + writel(len << TXD_LEN_SHIFT | flags, txd + TXD_LEN_FLAGS); +#if TG3_VLAN_TAG_USED + writel(vlan_tag << TXD_VLAN_TAG_SHIFT, txd + TXD_VLAN_TAG); +#endif + } +} + +static inline int tg3_4g_overflow_test(dma_addr_t mapping, int len) +{ + u32 base = (u32) mapping & 0xffffffff; + + return ((base > 0xffffdcc0) && + ((u64) mapping >> 32) == 0 && + (base + len + 8 < base)); +} + +static int tg3_start_xmit_4gbug(struct sk_buff *skb, struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + dma_addr_t mapping; + unsigned int i; + u32 len, entry, base_flags; + int would_hit_hwbug; + + len = (skb->len - skb->data_len); + + spin_lock_irq(&tp->lock); + + /* This is a hard error, log it. */ + if (unlikely(TX_BUFFS_AVAIL(tp) <= (skb_shinfo(skb)->nr_frags + 1))) { + netif_stop_queue(dev); + spin_unlock_irq(&tp->lock); + printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n", + dev->name); + return 1; + } + + entry = tp->tx_prod; + base_flags = 0; + if (skb->ip_summed == CHECKSUM_HW) + base_flags |= TXD_FLAG_TCPUDP_CSUM; +#if TG3_VLAN_TAG_USED + if (tp->vlgrp != NULL && vlan_tx_tag_present(skb)) + base_flags |= (TXD_FLAG_VLAN | + (vlan_tx_tag_get(skb) << 16)); +#endif + + /* Queue skb data, a.k.a. the main skb fragment. */ + mapping = pci_map_single(tp->pdev, skb->data, len, PCI_DMA_TODEVICE); + + tp->tx_buffers[entry].skb = skb; + tp->tx_buffers[entry].mapping = mapping; + + would_hit_hwbug = tg3_4g_overflow_test(mapping, len); + + tg3_set_txd(tp, entry, mapping, len, base_flags, + (skb_shinfo(skb)->nr_frags == 0)); + + entry = NEXT_TX(entry); + + /* Now loop through additional data fragments, and queue them. */ + if (skb_shinfo(skb)->nr_frags > 0) { + unsigned int i, last; + + last = skb_shinfo(skb)->nr_frags - 1; + for (i = 0; i <= last; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + len = frag->size; + mapping = pci_map_page(tp->pdev, + frag->page, + frag->page_offset, + len, PCI_DMA_TODEVICE); + + tp->tx_buffers[entry].skb = NULL; + tp->tx_buffers[entry].mapping = mapping; + + would_hit_hwbug |= + tg3_4g_overflow_test(mapping, len); + + tg3_set_txd(tp, entry, mapping, len, + base_flags, (i == last)); + + entry = NEXT_TX(entry); + } + } + + if (would_hit_hwbug) { + u32 last_plus_one = entry; + u32 start; + unsigned int len = 0; + + entry = entry - 1 - skb_shinfo(skb)->nr_frags; + entry &= (TG3_TX_RING_SIZE - 1); + start = entry; + i = 0; + while (entry != last_plus_one) { + dma_addr_t mapping = tp->tx_buffers[entry].mapping; + + if (i == 0) + len = skb->len - skb->data_len; + else + len = skb_shinfo(skb)->frags[i-1].size; + + if (tg3_4g_overflow_test(mapping, len)) + break; + + i++; + entry = NEXT_TX(entry); + + } + + /* If the workaround fails due to memory/mapping + * failure, silently drop this packet. + */ + if (tigon3_4gb_hwbug_workaround(tp, skb, + entry, len, + last_plus_one, + &start)) + goto out_unlock; + + entry = start; + } + + /* Packets are ready, update Tx producer idx local and on card. */ + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { + tw32_mailbox((MAILBOX_SNDHOST_PROD_IDX_0 + + TG3_64BIT_REG_LOW), entry); + if (tp->tg3_flags & TG3_FLAG_TXD_MBOX_HWBUG) + tw32_mailbox((MAILBOX_SNDHOST_PROD_IDX_0 + + TG3_64BIT_REG_LOW), entry); + } else { + tw32_mailbox((MAILBOX_SNDNIC_PROD_IDX_0 + + TG3_64BIT_REG_LOW), entry); + if (tp->tg3_flags & TG3_FLAG_TXD_MBOX_HWBUG) + tw32_mailbox((MAILBOX_SNDNIC_PROD_IDX_0 + + TG3_64BIT_REG_LOW), entry); + } + + tp->tx_prod = entry; + if (TX_BUFFS_AVAIL(tp) <= (MAX_SKB_FRAGS + 1)) + netif_stop_queue(dev); + +out_unlock: + spin_unlock_irq(&tp->lock); + + dev->trans_start = jiffies; + + return 0; +} + +static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + dma_addr_t mapping; + u32 len, entry, base_flags; + + len = (skb->len - skb->data_len); + + spin_lock_irq(&tp->lock); + + /* This is a hard error, log it. */ + if (unlikely(TX_BUFFS_AVAIL(tp) <= (skb_shinfo(skb)->nr_frags + 1))) { + netif_stop_queue(dev); + spin_unlock_irq(&tp->lock); + printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n", + dev->name); + return 1; + } + + entry = tp->tx_prod; + base_flags = 0; + if (skb->ip_summed == CHECKSUM_HW) + base_flags |= TXD_FLAG_TCPUDP_CSUM; +#if TG3_VLAN_TAG_USED + if (tp->vlgrp != NULL && vlan_tx_tag_present(skb)) + base_flags |= (TXD_FLAG_VLAN | + (vlan_tx_tag_get(skb) << 16)); +#endif + + /* Queue skb data, a.k.a. the main skb fragment. */ + mapping = pci_map_single(tp->pdev, skb->data, len, PCI_DMA_TODEVICE); + + tp->tx_buffers[entry].skb = skb; + tp->tx_buffers[entry].mapping = mapping; + + tg3_set_txd(tp, entry, mapping, len, base_flags, + (skb_shinfo(skb)->nr_frags == 0)); + + entry = NEXT_TX(entry); + + /* Now loop through additional data fragments, and queue them. */ + if (skb_shinfo(skb)->nr_frags > 0) { + unsigned int i, last; + + last = skb_shinfo(skb)->nr_frags - 1; + for (i = 0; i <= last; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + + len = frag->size; + mapping = pci_map_page(tp->pdev, + frag->page, + frag->page_offset, + len, PCI_DMA_TODEVICE); + + tp->tx_buffers[entry].skb = NULL; + tp->tx_buffers[entry].mapping = mapping; + + tg3_set_txd(tp, entry, mapping, len, + base_flags, (i == last)); + + entry = NEXT_TX(entry); + } + } + + /* Packets are ready, update Tx producer idx local and on card. + * We know this is not a 5700 (by virtue of not being a chip + * requiring the 4GB overflow workaround) so we can safely omit + * the double-write bug tests. + */ + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { + tw32_mailbox((MAILBOX_SNDHOST_PROD_IDX_0 + + TG3_64BIT_REG_LOW), entry); + } else { + tw32_mailbox((MAILBOX_SNDNIC_PROD_IDX_0 + + TG3_64BIT_REG_LOW), entry); + } + + tp->tx_prod = entry; + if (TX_BUFFS_AVAIL(tp) <= (MAX_SKB_FRAGS + 1)) + netif_stop_queue(dev); + + spin_unlock_irq(&tp->lock); + + dev->trans_start = jiffies; + + return 0; +} + +static int tg3_change_mtu(struct net_device *dev, int new_mtu) +{ + struct tg3 *tp = dev->priv; + + if (new_mtu < TG3_MIN_MTU || new_mtu > TG3_MAX_MTU) + return -EINVAL; + + if (!netif_running(dev)) { + /* We'll just catch it later when the + * device is up'd. + */ + dev->mtu = new_mtu; + return 0; + } + + spin_lock_irq(&tp->lock); + + tg3_halt(tp); + + dev->mtu = new_mtu; + + if (new_mtu > ETH_DATA_LEN) + tp->tg3_flags |= TG3_FLAG_JUMBO_ENABLE; + else + tp->tg3_flags &= ~TG3_FLAG_JUMBO_ENABLE; + + tg3_init_rings(tp); + tg3_init_hw(tp); + + spin_unlock_irq(&tp->lock); + + return 0; +} + +/* Free up pending packets in all rx/tx rings. + * + * The chip has been shut down and the driver detached from + * the networking, so no interrupts or new tx packets will + * end up in the driver. tp->lock is not held and we are not + * in an interrupt context and thus may sleep. + */ +static void tg3_free_rings(struct tg3 *tp) +{ + struct ring_info *rxp; + int i; + + for (i = 0; i < TG3_RX_RING_SIZE; i++) { + rxp = &tp->rx_std_buffers[i]; + + if (rxp->skb == NULL) + continue; + pci_unmap_single(tp->pdev, rxp->mapping, + RX_PKT_BUF_SZ - tp->rx_offset, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(rxp->skb); + rxp->skb = NULL; + } +#if TG3_MINI_RING_WORKS + for (i = 0; i < TG3_RX_MINI_RING_SIZE; i++) { + rxp = &tp->rx_mini_buffers[i]; + + if (rxp->skb == NULL) + continue; + pci_unmap_single(tp->pdev, rxp->mapping, + RX_MINI_PKT_BUF_SZ - tp->rx_offset, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(rxp->skb); + rxp->skb = NULL; + } +#endif + for (i = 0; i < TG3_RX_JUMBO_RING_SIZE; i++) { + rxp = &tp->rx_jumbo_buffers[i]; + + if (rxp->skb == NULL) + continue; + pci_unmap_single(tp->pdev, rxp->mapping, + RX_JUMBO_PKT_BUF_SZ - tp->rx_offset, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(rxp->skb); + rxp->skb = NULL; + } + + for (i = 0; i < TG3_TX_RING_SIZE; ) { + struct ring_info *txp; + struct sk_buff *skb; + int j; + + txp = &tp->tx_buffers[i]; + skb = txp->skb; + + if (skb == NULL) { + i++; + continue; + } + + pci_unmap_single(tp->pdev, txp->mapping, + (skb->len - skb->data_len), + PCI_DMA_TODEVICE); + txp->skb = NULL; + + i++; + + for (j = 0; j < skb_shinfo(skb)->nr_frags; j++) { + txp = &tp->tx_buffers[i & (TG3_TX_RING_SIZE - 1)]; + pci_unmap_page(tp->pdev, txp->mapping, + skb_shinfo(skb)->frags[j].size, + PCI_DMA_TODEVICE); + i++; + } + + dev_kfree_skb_any(skb); + } +} + +/* Initialize tx/rx rings for packet processing. + * + * The chip has been shut down and the driver detached from + * the networking, so no interrupts or new tx packets will + * end up in the driver. tp->lock is not held and we are not + * in an interrupt context and thus may sleep. + */ +static void tg3_init_rings(struct tg3 *tp) +{ + unsigned long start, end; + u32 i; + + /* Free up all the SKBs. */ + tg3_free_rings(tp); + + /* Zero out all descriptors. */ + memset(tp->rx_std, 0, TG3_RX_RING_BYTES); +#if TG3_MINI_RING_WORKS + memset(tp->rx_mini, 0, TG3_RX_MINI_RING_BYTES); +#endif + memset(tp->rx_jumbo, 0, TG3_RX_JUMBO_RING_BYTES); + memset(tp->rx_rcb, 0, TG3_RX_RCB_RING_BYTES); + + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { + memset(tp->tx_ring, 0, TG3_TX_RING_BYTES); + } else { + start = (tp->regs + + NIC_SRAM_WIN_BASE + + NIC_SRAM_TX_BUFFER_DESC); + end = start + TG3_TX_RING_BYTES; + while (start < end) { + writel(0, start); + start += 4; + } + } + + /* Initialize invariants of the rings, we only set this + * stuff once. This works because the card does not + * write into the rx buffer posting rings. + */ + for (i = 0; i < TG3_RX_RING_SIZE; i++) { + struct tg3_rx_buffer_desc *rxd; + + rxd = &tp->rx_std[i]; + rxd->idx_len = (RX_PKT_BUF_SZ - tp->rx_offset - 64) + << RXD_LEN_SHIFT; + rxd->type_flags = (RXD_FLAG_END << RXD_FLAGS_SHIFT); + rxd->opaque = (RXD_OPAQUE_RING_STD | + (i << RXD_OPAQUE_INDEX_SHIFT)); + } +#if TG3_MINI_RING_WORKS + for (i = 0; i < TG3_RX_MINI_RING_SIZE; i++) { + struct tg3_rx_buffer_desc *rxd; + + rxd = &tp->rx_mini[i]; + rxd->idx_len = (RX_MINI_PKT_BUF_SZ - tp->rx_offset - 64) + << RXD_LEN_SHIFT; + rxd->type_flags = (RXD_FLAG_END << RXD_FLAGS_SHIFT) | + RXD_FLAG_MINI; + rxd->opaque = (RXD_OPAQUE_RING_MINI | + (i << RXD_OPAQUE_INDEX_SHIFT)); + } +#endif + if (tp->tg3_flags & TG3_FLAG_JUMBO_ENABLE) { + for (i = 0; i < TG3_RX_JUMBO_RING_SIZE; i++) { + struct tg3_rx_buffer_desc *rxd; + + rxd = &tp->rx_jumbo[i]; + rxd->idx_len = (RX_JUMBO_PKT_BUF_SZ - tp->rx_offset - 64) + << RXD_LEN_SHIFT; + rxd->type_flags = (RXD_FLAG_END << RXD_FLAGS_SHIFT) | + RXD_FLAG_JUMBO; + rxd->opaque = (RXD_OPAQUE_RING_JUMBO | + (i << RXD_OPAQUE_INDEX_SHIFT)); + } + } + + /* Now allocate fresh SKBs for each rx ring. */ + for (i = 0; i < TG3_RX_RING_PENDING; i++) { + if (tg3_alloc_rx_skb(tp, RXD_OPAQUE_RING_STD, + -1, i) < 0) + break; + } + +#if TG3_MINI_RING_WORKS + for (i = 0; i < TG3_RX_MINI_RING_PENDING; i++) { + if (tg3_alloc_rx_skb(tp, RXD_OPAQUE_RING_MINI, + -1, i) < 0) + break; + } +#endif + + if (tp->tg3_flags & TG3_FLAG_JUMBO_ENABLE) { + for (i = 0; i < TG3_RX_JUMBO_RING_PENDING; i++) { + if (tg3_alloc_rx_skb(tp, RXD_OPAQUE_RING_JUMBO, + -1, i) < 0) + break; + } + } +} + +/* + * Must not be invoked with interrupt sources disabled and + * the hardware shutdown down. + */ +static void tg3_free_consistent(struct tg3 *tp) +{ + if (tp->rx_std_buffers) { + kfree(tp->rx_std_buffers); + tp->rx_std_buffers = NULL; + } + if (tp->rx_std) { + pci_free_consistent(tp->pdev, TG3_RX_RING_BYTES, + tp->rx_std, tp->rx_std_mapping); + tp->rx_std = NULL; + } +#if TG3_MINI_RING_WORKS + if (tp->rx_mini) { + pci_free_consistent(tp->pdev, TG3_RX_MINI_RING_BYTES, + tp->rx_mini, tp->rx_mini_mapping); + tp->rx_mini = NULL; + } +#endif + if (tp->rx_jumbo) { + pci_free_consistent(tp->pdev, TG3_RX_JUMBO_RING_BYTES, + tp->rx_jumbo, tp->rx_jumbo_mapping); + tp->rx_jumbo = NULL; + } + if (tp->rx_rcb) { + pci_free_consistent(tp->pdev, TG3_RX_RCB_RING_BYTES, + tp->rx_rcb, tp->rx_rcb_mapping); + tp->rx_rcb = NULL; + } + if (tp->tx_ring) { + pci_free_consistent(tp->pdev, TG3_TX_RING_BYTES, + tp->tx_ring, tp->tx_desc_mapping); + tp->tx_ring = NULL; + } + if (tp->hw_status) { + pci_free_consistent(tp->pdev, TG3_HW_STATUS_SIZE, + tp->hw_status, tp->status_mapping); + tp->hw_status = NULL; + } + if (tp->hw_stats) { + pci_free_consistent(tp->pdev, sizeof(struct tg3_hw_stats), + tp->hw_stats, tp->stats_mapping); + tp->hw_stats = NULL; + } +} + +/* + * Must not be invoked with interrupt sources disabled and + * the hardware shutdown down. Can sleep. + */ +static int tg3_alloc_consistent(struct tg3 *tp) +{ + tp->rx_std_buffers = kmalloc(sizeof(struct ring_info) * + (TG3_RX_RING_SIZE + +#if TG3_MINI_RING_WORKS + TG3_RX_MINI_RING_SIZE + +#endif + TG3_RX_JUMBO_RING_SIZE + + TG3_TX_RING_SIZE), + GFP_KERNEL); + if (!tp->rx_std_buffers) + return -ENOMEM; + +#if TG3_MINI_RING_WORKS + memset(tp->rx_std_buffers, 0, + (sizeof(struct ring_info) * + (TG3_RX_RING_SIZE + + TG3_RX_MINI_RING_SIZE + + TG3_RX_JUMBO_RING_SIZE + + TG3_TX_RING_SIZE))); +#else + memset(tp->rx_std_buffers, 0, + (sizeof(struct ring_info) * + (TG3_RX_RING_SIZE + + TG3_RX_JUMBO_RING_SIZE + + TG3_TX_RING_SIZE))); +#endif + +#if TG3_MINI_RING_WORKS + tp->rx_mini_buffers = &tp->rx_std_buffers[TG3_RX_RING_SIZE]; + tp->rx_jumbo_buffers = &tp->rx_mini_buffers[TG3_RX_MINI_RING_SIZE]; +#else + tp->rx_jumbo_buffers = &tp->rx_std_buffers[TG3_RX_RING_SIZE]; +#endif + tp->tx_buffers = &tp->rx_jumbo_buffers[TG3_RX_JUMBO_RING_SIZE]; + + tp->rx_std = pci_alloc_consistent(tp->pdev, TG3_RX_RING_BYTES, + &tp->rx_std_mapping); + if (!tp->rx_std) + goto err_out; + +#if TG3_MINI_RING_WORKS + tp->rx_mini = pci_alloc_consistent(tp->pdev, TG3_RX_MINI_RING_BYTES, + &tp->rx_mini_mapping); + + if (!tp->rx_mini) + goto err_out; +#endif + + tp->rx_jumbo = pci_alloc_consistent(tp->pdev, TG3_RX_JUMBO_RING_BYTES, + &tp->rx_jumbo_mapping); + + if (!tp->rx_jumbo) + goto err_out; + + tp->rx_rcb = pci_alloc_consistent(tp->pdev, TG3_RX_RCB_RING_BYTES, + &tp->rx_rcb_mapping); + if (!tp->rx_rcb) + goto err_out; + + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { + tp->tx_ring = pci_alloc_consistent(tp->pdev, TG3_TX_RING_BYTES, + &tp->tx_desc_mapping); + if (!tp->tx_ring) + goto err_out; + } else { + tp->tx_ring = NULL; + tp->tx_desc_mapping = 0; + } + + tp->hw_status = pci_alloc_consistent(tp->pdev, + TG3_HW_STATUS_SIZE, + &tp->status_mapping); + if (!tp->hw_status) + goto err_out; + + tp->hw_stats = pci_alloc_consistent(tp->pdev, + sizeof(struct tg3_hw_stats), + &tp->stats_mapping); + if (!tp->hw_stats) + goto err_out; + + memset(tp->hw_status, 0, TG3_HW_STATUS_SIZE); + memset(tp->hw_stats, 0, sizeof(struct tg3_hw_stats)); + + return 0; + +err_out: + tg3_free_consistent(tp); + return -ENOMEM; +} + +#define MAX_WAIT_CNT 10000 + +/* To stop a block, clear the enable bit and poll till it + * clears. tp->lock is held. + */ +static int tg3_stop_block(struct tg3 *tp, unsigned long ofs, u32 enable_bit) +{ + unsigned int i; + u32 val; + + val = tr32(ofs); + val &= ~enable_bit; + tw32(ofs, val); + + for (i = 0; i < MAX_WAIT_CNT; i++) { + val = tr32(ofs); + + if ((val & enable_bit) == 0) + break; + udelay(100); + } + + if (i == MAX_WAIT_CNT) { + printk(KERN_ERR PFX "tg3_stop_block timed out, " + "ofs=%lx enable_bit=%x\n", + ofs, enable_bit); + return -ENODEV; + } + + return 0; +} + +/* tp->lock is held. */ +static int tg3_abort_hw(struct tg3 *tp) +{ + int i, err; + + tg3_disable_ints(tp); + + tp->rx_mode &= ~RX_MODE_ENABLE; + tw32(MAC_RX_MODE, tp->rx_mode); + + err = tg3_stop_block(tp, RCVBDI_MODE, RCVBDI_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVLPC_MODE, RCVLPC_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVLSC_MODE, RCVLSC_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVDBDI_MODE, RCVDBDI_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVDCC_MODE, RCVDCC_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVCC_MODE, RCVCC_MODE_ENABLE); + + err |= tg3_stop_block(tp, SNDBDS_MODE, SNDBDS_MODE_ENABLE); + err |= tg3_stop_block(tp, SNDBDI_MODE, SNDBDI_MODE_ENABLE); + err |= tg3_stop_block(tp, SNDDATAI_MODE, SNDDATAI_MODE_ENABLE); + err |= tg3_stop_block(tp, RDMAC_MODE, RDMAC_MODE_ENABLE); + err |= tg3_stop_block(tp, SNDDATAC_MODE, SNDDATAC_MODE_ENABLE); + err |= tg3_stop_block(tp, SNDBDC_MODE, SNDBDC_MODE_ENABLE); + if (err) + goto out; + + tp->mac_mode &= ~MAC_MODE_TDE_ENABLE; + tw32(MAC_MODE, tp->mac_mode); + + tp->tx_mode &= ~TX_MODE_ENABLE; + tw32(MAC_TX_MODE, tp->tx_mode); + for (i = 0; i < MAX_WAIT_CNT; i++) { + udelay(100); + if (!(tr32(MAC_TX_MODE) & TX_MODE_ENABLE)) + break; + } + if (i >= MAX_WAIT_CNT) { + printk(KERN_ERR PFX "tg3_abort_hw timed out for %s, " + "TX_MODE_ENABLE will not clear MAC_TX_MODE=%08x\n", + tp->dev->name, tr32(MAC_TX_MODE)); + return -ENODEV; + } + + err = tg3_stop_block(tp, HOSTCC_MODE, HOSTCC_MODE_ENABLE); + err |= tg3_stop_block(tp, WDMAC_MODE, WDMAC_MODE_ENABLE); + err |= tg3_stop_block(tp, MBFREE_MODE, MBFREE_MODE_ENABLE); + + tw32(FTQ_RESET, 0xffffffff); + tw32(FTQ_RESET, 0x00000000); + + err |= tg3_stop_block(tp, BUFMGR_MODE, BUFMGR_MODE_ENABLE); + err |= tg3_stop_block(tp, MEMARB_MODE, MEMARB_MODE_ENABLE); + if (err) + goto out; + + memset(tp->hw_status, 0, TG3_HW_STATUS_SIZE); + +out: + return err; +} + +/* tp->lock is held. */ +static void tg3_chip_reset(struct tg3 *tp) +{ + u32 val; + + /* Force NVRAM to settle. + * This deals with a chip bug which can result in EEPROM + * corruption. + */ + if (tp->tg3_flags & TG3_FLAG_NVRAM) { + int i; + + tw32(NVRAM_SWARB, SWARB_REQ_SET1); + for (i = 0; i < 100000; i++) { + if (tr32(NVRAM_SWARB) & SWARB_GNT1) + break; + udelay(10); + } + } + + tw32(GRC_MISC_CFG, GRC_MISC_CFG_CORECLK_RESET); + udelay(40); + udelay(40); + udelay(40); + + /* Re-enable indirect register accesses. */ + pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, + tp->misc_host_ctrl); + + /* Set MAX PCI retry to zero. */ + pci_write_config_dword(tp->pdev, TG3PCI_PCISTATE, + (PCISTATE_ROM_ENABLE | + PCISTATE_ROM_RETRY_ENABLE)); + + pci_restore_state(tp->pdev, tp->pci_cfg_state); + + /* Make sure PCI-X relaxed ordering bit is clear. */ + pci_read_config_dword(tp->pdev, TG3PCI_X_CAPS, &val); + val &= ~PCIX_CAPS_RELAXED_ORDERING; + pci_write_config_dword(tp->pdev, TG3PCI_X_CAPS, val); + + tw32(MEMARB_MODE, MEMARB_MODE_ENABLE); + + tw32(TG3PCI_MISC_HOST_CTRL, tp->misc_host_ctrl); +} + +/* tp->lock is held. */ +static int tg3_halt(struct tg3 *tp) +{ + u32 val; + int i; + + tg3_abort_hw(tp); + tg3_chip_reset(tp); + tg3_write_mem(tp, + NIC_SRAM_FIRMWARE_MBOX, + NIC_SRAM_FIRMWARE_MBOX_MAGIC1); + for (i = 0; i < 100000; i++) { + tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val); + if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1) + break; + udelay(10); + } + + if (i >= 100000) { + printk(KERN_ERR PFX "tg3_halt timed out for %s, " + "firmware will not restart magic=%08x\n", + tp->dev->name, val); + return -ENODEV; + } + + return 0; +} + +#define TG3_FW_RELEASE_MAJOR 0x0 +#define TG3_FW_RELASE_MINOR 0x0 +#define TG3_FW_RELEASE_FIX 0x0 +#define TG3_FW_START_ADDR 0x08000000 +#define TG3_FW_TEXT_ADDR 0x08000000 +#define TG3_FW_TEXT_LEN 0x9c0 +#define TG3_FW_RODATA_ADDR 0x080009c0 +#define TG3_FW_RODATA_LEN 0x60 +#define TG3_FW_DATA_ADDR 0x08000a40 +#define TG3_FW_DATA_LEN 0x20 +#define TG3_FW_SBSS_ADDR 0x08000a60 +#define TG3_FW_SBSS_LEN 0xc +#define TG3_FW_BSS_ADDR 0x08000a70 +#define TG3_FW_BSS_LEN 0x10 + +static u32 t3FwText[(TG3_FW_TEXT_LEN / sizeof(u32)) + 1] = { + 0x00000000, 0x10000003, 0x00000000, 0x0000000d, 0x0000000d, 0x3c1d0800, + 0x37bd3ffc, 0x03a0f021, 0x3c100800, 0x26100000, 0x0e000018, 0x00000000, + 0x0000000d, 0x3c1d0800, 0x37bd3ffc, 0x03a0f021, 0x3c100800, 0x26100034, + 0x0e00021c, 0x00000000, 0x0000000d, 0x00000000, 0x00000000, 0x00000000, + 0x27bdffe0, 0x3c1cc000, 0xafbf0018, 0xaf80680c, 0x0e00004c, 0x241b2105, + 0x97850000, 0x97870002, 0x9782002c, 0x9783002e, 0x3c040800, 0x248409c0, + 0xafa00014, 0x00021400, 0x00621825, 0x00052c00, 0xafa30010, 0x8f860010, + 0x00e52825, 0x0e000060, 0x24070102, 0x3c02ac00, 0x34420100, 0x3c03ac01, + 0x34630100, 0xaf820490, 0x3c02ffff, 0xaf820494, 0xaf830498, 0xaf82049c, + 0x24020001, 0xaf825ce0, 0x0e00003f, 0xaf825d00, 0x0e000140, 0x00000000, + 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x2402ffff, 0xaf825404, 0x8f835400, + 0x34630400, 0xaf835400, 0xaf825404, 0x3c020800, 0x24420034, 0xaf82541c, + 0x03e00008, 0xaf805400, 0x00000000, 0x00000000, 0x3c020800, 0x34423000, + 0x3c030800, 0x34633000, 0x3c040800, 0x348437ff, 0x3c010800, 0xac220a64, + 0x24020040, 0x3c010800, 0xac220a68, 0x3c010800, 0xac200a60, 0xac600000, + 0x24630004, 0x0083102b, 0x5040fffd, 0xac600000, 0x03e00008, 0x00000000, + 0x00804821, 0x8faa0010, 0x3c020800, 0x8c420a60, 0x3c040800, 0x8c840a68, + 0x8fab0014, 0x24430001, 0x0044102b, 0x3c010800, 0xac230a60, 0x14400003, + 0x00004021, 0x3c010800, 0xac200a60, 0x3c020800, 0x8c420a60, 0x3c030800, + 0x8c630a64, 0x91240000, 0x00021140, 0x00431021, 0x00481021, 0x25080001, + 0xa0440000, 0x29020008, 0x1440fff4, 0x25290001, 0x3c020800, 0x8c420a60, + 0x3c030800, 0x8c630a64, 0x8f84680c, 0x00021140, 0x00431021, 0xac440008, + 0xac45000c, 0xac460010, 0xac470014, 0xac4a0018, 0x03e00008, 0xac4b001c, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0x02000008, 0x00000000, 0x0a0001e3, 0x3c0a0001, 0x0a0001e3, 0x3c0a0002, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, + 0x0a0001e3, 0x3c0a0007, 0x0a0001e3, 0x3c0a0008, 0x0a0001e3, 0x3c0a0009, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x3c0a000b, + 0x0a0001e3, 0x3c0a000c, 0x0a0001e3, 0x3c0a000d, 0x0a0001e3, 0x00000000, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x3c0a000e, 0x0a0001e3, 0x00000000, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x3c0a0013, 0x0a0001e3, 0x3c0a0014, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x27bdffe0, 0x00001821, 0x00001021, 0xafbf0018, 0xafb10014, 0xafb00010, + 0x3c010800, 0x00220821, 0xac200a70, 0x3c010800, 0x00220821, 0xac200a74, + 0x3c010800, 0x00220821, 0xac200a78, 0x24630001, 0x1860fff5, 0x2442000c, + 0x24110001, 0x8f906810, 0x32020004, 0x14400005, 0x24040001, 0x3c020800, + 0x8c420a78, 0x18400003, 0x00002021, 0x0e000182, 0x00000000, 0x32020001, + 0x10400003, 0x00000000, 0x0e000169, 0x00000000, 0x0a000153, 0xaf915028, + 0x8fbf0018, 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x3c050800, + 0x8ca50a70, 0x3c060800, 0x8cc60a80, 0x3c070800, 0x8ce70a78, 0x27bdffe0, + 0x3c040800, 0x248409d0, 0xafbf0018, 0xafa00010, 0x0e000060, 0xafa00014, + 0x0e00017b, 0x00002021, 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x24020001, + 0x8f836810, 0x00821004, 0x00021027, 0x00621824, 0x03e00008, 0xaf836810, + 0x27bdffd8, 0xafbf0024, 0x1080002e, 0xafb00020, 0x8f825cec, 0xafa20018, + 0x8f825cec, 0x3c100800, 0x26100a78, 0xafa2001c, 0x34028000, 0xaf825cec, + 0x8e020000, 0x18400016, 0x00000000, 0x3c020800, 0x94420a74, 0x8fa3001c, + 0x000221c0, 0xac830004, 0x8fa2001c, 0x3c010800, 0x0e000201, 0xac220a74, + 0x10400005, 0x00000000, 0x8e020000, 0x24420001, 0x0a0001df, 0xae020000, + 0x3c020800, 0x8c420a70, 0x00021c02, 0x000321c0, 0x0a0001c5, 0xafa2001c, + 0x0e000201, 0x00000000, 0x1040001f, 0x00000000, 0x8e020000, 0x8fa3001c, + 0x24420001, 0x3c010800, 0xac230a70, 0x3c010800, 0xac230a74, 0x0a0001df, + 0xae020000, 0x3c100800, 0x26100a78, 0x8e020000, 0x18400028, 0x00000000, + 0x0e000201, 0x00000000, 0x14400024, 0x00000000, 0x8e020000, 0x3c030800, + 0x8c630a70, 0x2442ffff, 0xafa3001c, 0x18400006, 0xae020000, 0x00031402, + 0x000221c0, 0x8c820004, 0x3c010800, 0xac220a70, 0x97a2001e, 0x2442ff00, + 0x2c420300, 0x1440000b, 0x24024000, 0x3c040800, 0x248409dc, 0xafa00010, + 0xafa00014, 0x8fa6001c, 0x24050008, 0x0e000060, 0x00003821, 0x0a0001df, + 0x00000000, 0xaf825cf8, 0x3c020800, 0x8c420a40, 0x8fa3001c, 0x24420001, + 0xaf835cf8, 0x3c010800, 0xac220a40, 0x8fbf0024, 0x8fb00020, 0x03e00008, + 0x27bd0028, 0x27bdffe0, 0x3c040800, 0x248409e8, 0x00002821, 0x00003021, + 0x00003821, 0xafbf0018, 0xafa00010, 0x0e000060, 0xafa00014, 0x8fbf0018, + 0x03e00008, 0x27bd0020, 0x8f82680c, 0x8f85680c, 0x00021827, 0x0003182b, + 0x00031823, 0x00431024, 0x00441021, 0x00a2282b, 0x10a00006, 0x00000000, + 0x00401821, 0x8f82680c, 0x0043102b, 0x1440fffd, 0x00000000, 0x03e00008, + 0x00000000, 0x3c040800, 0x8c840000, 0x3c030800, 0x8c630a40, 0x0064102b, + 0x54400002, 0x00831023, 0x00641023, 0x2c420008, 0x03e00008, 0x38420001, + 0x27bdffe0, 0x00802821, 0x3c040800, 0x24840a00, 0x00003021, 0x00003821, + 0xafbf0018, 0xafa00010, 0x0e000060, 0xafa00014, 0x0a000216, 0x00000000, + 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x00000000, 0x27bdffe0, 0x3c1cc000, + 0xafbf0018, 0x0e00004c, 0xaf80680c, 0x3c040800, 0x24840a10, 0x03802821, + 0x00003021, 0x00003821, 0xafa00010, 0x0e000060, 0xafa00014, 0x2402ffff, + 0xaf825404, 0x3c0200aa, 0x0e000234, 0xaf825434, 0x8fbf0018, 0x03e00008, + 0x27bd0020, 0x00000000, 0x00000000, 0x00000000, 0x27bdffe8, 0xafb00010, + 0x24100001, 0xafbf0014, 0x3c01c003, 0xac200000, 0x8f826810, 0x30422000, + 0x10400003, 0x00000000, 0x0e000246, 0x00000000, 0x0a00023a, 0xaf905428, + 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x27bdfff8, 0x8f845d0c, + 0x3c0200ff, 0x3c030800, 0x8c630a50, 0x3442fff8, 0x00821024, 0x1043001e, + 0x3c0500ff, 0x34a5fff8, 0x3c06c003, 0x3c074000, 0x00851824, 0x8c620010, + 0x3c010800, 0xac230a50, 0x30420008, 0x10400005, 0x00871025, 0x8cc20000, + 0x24420001, 0xacc20000, 0x00871025, 0xaf825d0c, 0x8fa20000, 0x24420001, + 0xafa20000, 0x8fa20000, 0x8fa20000, 0x24420001, 0xafa20000, 0x8fa20000, + 0x8f845d0c, 0x3c030800, 0x8c630a50, 0x00851024, 0x1443ffe8, 0x00851824, + 0x27bd0008, 0x03e00008, 0x00000000, 0x00000000, 0x00000000 +}; + +static u32 t3FwRodata[(TG3_FW_RODATA_LEN / sizeof(u32)) + 1] = { + 0x35373031, 0x726c7341, 0x00000000, 0x00000000, 0x53774576, 0x656e7430, + 0x00000000, 0x726c7045, 0x76656e74, 0x31000000, 0x556e6b6e, 0x45766e74, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x66617461, 0x6c457272, + 0x00000000, 0x00000000, 0x4d61696e, 0x43707542, 0x00000000, 0x00000000, + 0x00000000 +}; + +#if 0 /* All zeros, dont eat up space with it. */ +u32 t3FwData[(TG3_FW_DATA_LEN / sizeof(u32)) + 1] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; +#endif + +#define RX_CPU_SCRATCH_BASE 0x30000 +#define RX_CPU_SCRATCH_SIZE 0x04000 +#define TX_CPU_SCRATCH_BASE 0x34000 +#define TX_CPU_SCRATCH_SIZE 0x04000 + +/* tp->lock is held. */ +static int tg3_reset_cpu(struct tg3 *tp, u32 offset) +{ + int i; + + tw32(offset + CPU_STATE, 0xffffffff); + tw32(offset + CPU_MODE, CPU_MODE_RESET); + if (offset == RX_CPU_BASE) { + for (i = 0; i < 10000; i++) + if (!(tr32(offset + CPU_MODE) & CPU_MODE_RESET)) + break; + tw32(offset + CPU_STATE, 0xffffffff); + tw32(offset + CPU_MODE, CPU_MODE_RESET); + udelay(10); + } else { + for (i = 0; i < 10000; i++) { + if (!(tr32(offset + CPU_MODE) & CPU_MODE_RESET)) + break; + tw32(offset + CPU_STATE, 0xffffffff); + tw32(offset + CPU_MODE, CPU_MODE_RESET); + udelay(10); + } + } + + if (i >= 10000) { + printk(KERN_ERR PFX "tg3_reset_cpu timed out for %s, " + "and %s CPU\n", + tp->dev->name, + (offset == RX_CPU_BASE ? "RX" : "TX")); + return -ENODEV; + } + return 0; +} + +/* tp->lock is held. */ +static int tg3_load_firmware_cpu(struct tg3 *tp, u32 cpu_base, u32 cpu_scratch_base, + int cpu_scratch_size) +{ + int err, i; + + err = tg3_reset_cpu(tp, cpu_base); + if (err) + return err; + + for (i = 0; i < cpu_scratch_size; i += sizeof(u32)) + tg3_write_indirect_reg32(tp, cpu_scratch_base + i, 0); + tw32(cpu_base + CPU_STATE, 0xffffffff); + tw32(cpu_base + CPU_MODE, tr32(cpu_base+CPU_MODE)|CPU_MODE_HALT); + for (i = 0; i < (TG3_FW_TEXT_LEN / sizeof(u32)); i++) + tg3_write_indirect_reg32(tp, (cpu_scratch_base + + (TG3_FW_TEXT_ADDR & 0xffff) + + (i * sizeof(u32))), + t3FwText[i]); + for (i = 0; i < (TG3_FW_RODATA_LEN / sizeof(u32)); i++) + tg3_write_indirect_reg32(tp, (cpu_scratch_base + + (TG3_FW_RODATA_ADDR & 0xffff) + + (i * sizeof(u32))), + t3FwRodata[i]); + for (i = 0; i < (TG3_FW_DATA_LEN / sizeof(u32)); i++) + tg3_write_indirect_reg32(tp, (cpu_scratch_base + + (TG3_FW_DATA_ADDR & 0xffff) + + (i * sizeof(u32))), + 0); + + return 0; +} + +/* tp->lock is held. */ +static int tg3_load_5701_a0_firmware_fix(struct tg3 *tp) +{ + int err, i; + + err = tg3_load_firmware_cpu(tp, RX_CPU_BASE, + RX_CPU_SCRATCH_BASE, RX_CPU_SCRATCH_SIZE); + if (err) + return err; + + err = tg3_load_firmware_cpu(tp, TX_CPU_BASE, + TX_CPU_SCRATCH_BASE, TX_CPU_SCRATCH_SIZE); + if (err) + return err; + + /* Now startup only the RX cpu. */ + tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff); + tw32(RX_CPU_BASE + CPU_PC, TG3_FW_TEXT_ADDR); + for (i = 0; i < 5; i++) { + if (tr32(RX_CPU_BASE + CPU_PC) == TG3_FW_TEXT_ADDR) + break; + tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff); + tw32(RX_CPU_BASE + CPU_MODE, CPU_MODE_HALT); + tw32(RX_CPU_BASE + CPU_PC, TG3_FW_TEXT_ADDR); + udelay(1000); + } + if (i >= 5) { + printk(KERN_ERR PFX "tg3_load_firmware fails for %s " + "to set RX CPU PC, is %08x should be %08x\n", + tp->dev->name, tr32(RX_CPU_BASE + CPU_PC), + TG3_FW_TEXT_ADDR); + return -ENODEV; + } + tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff); + tw32(RX_CPU_BASE + CPU_MODE, 0x00000000); + + return 0; +} + +/* tp->lock is held. */ +static void __tg3_set_mac_addr(struct tg3 *tp) +{ + u32 addr_high, addr_low; + int i; + + addr_high = ((tp->dev->dev_addr[0] << 8) | + tp->dev->dev_addr[1]); + addr_low = ((tp->dev->dev_addr[2] << 24) | + (tp->dev->dev_addr[3] << 16) | + (tp->dev->dev_addr[4] << 8) | + (tp->dev->dev_addr[5] << 0)); + for (i = 0; i < 4; i++) { + tw32(MAC_ADDR_0_HIGH + (i * 8), addr_high); + tw32(MAC_ADDR_0_LOW + (i * 8), addr_low); + } + + addr_high = (tp->dev->dev_addr[0] + + tp->dev->dev_addr[1] + + tp->dev->dev_addr[2] + + tp->dev->dev_addr[3] + + tp->dev->dev_addr[4] + + tp->dev->dev_addr[5]) & + TX_BACKOFF_SEED_MASK; + tw32(MAC_TX_BACKOFF_SEED, addr_high); +} + +static int tg3_set_mac_addr(struct net_device *dev, void *p) +{ + struct tg3 *tp = dev->priv; + struct sockaddr *addr = p; + + if (netif_running(dev)) + return -EBUSY; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + spin_lock_irq(&tp->lock); + __tg3_set_mac_addr(tp); + spin_unlock_irq(&tp->lock); + + return 0; +} + +/* tp->lock is held. */ +static void tg3_set_bdinfo(struct tg3 *tp, u32 bdinfo_addr, + dma_addr_t mapping, u32 maxlen_flags, + u32 nic_addr) +{ + tg3_write_mem(tp, + (bdinfo_addr + + TG3_BDINFO_HOST_ADDR + + TG3_64BIT_REG_HIGH), + ((u64) mapping >> 32)); + tg3_write_mem(tp, + (bdinfo_addr + + TG3_BDINFO_HOST_ADDR + + TG3_64BIT_REG_LOW), + ((u64) mapping & 0xffffffff)); + tg3_write_mem(tp, + (bdinfo_addr + + TG3_BDINFO_MAXLEN_FLAGS), + maxlen_flags); + tg3_write_mem(tp, + (bdinfo_addr + + TG3_BDINFO_NIC_ADDR), + nic_addr); +} + +static void __tg3_set_rx_mode(struct net_device *); + +/* tp->lock is held. */ +static int tg3_reset_hw(struct tg3 *tp) +{ + u32 val; + int i, err; + + tg3_disable_ints(tp); + + if (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) { + err = tg3_abort_hw(tp); + if (err) + return err; + } + + tg3_chip_reset(tp); + + tw32(GRC_MODE, tp->grc_mode); + tg3_write_mem(tp, + NIC_SRAM_FIRMWARE_MBOX, + NIC_SRAM_FIRMWARE_MBOX_MAGIC1); + if (tp->phy_id == PHY_ID_SERDES) { + tp->mac_mode = MAC_MODE_PORT_MODE_TBI; + tw32(MAC_MODE, tp->mac_mode); + } else + tw32(MAC_MODE, 0); + + /* Wait for firmware initialization to complete. */ + for (i = 0; i < 100000; i++) { + tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val); + if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1) + break; + udelay(10); + } + if (i >= 100000) { + printk(KERN_ERR PFX "tg3_reset_hw timed out for %s, " + "firmware will not restart magic=%08x\n", + tp->dev->name, val); + return -ENODEV; + } + + /* This works around an issue with Athlon chipsets on + * B3 tigon3 silicon. This bit has no effect on any + * other revision. + */ + val = tr32(TG3PCI_CLOCK_CTRL); + val |= CLOCK_CTRL_DELAY_PCI_GRANT; + tw32(TG3PCI_CLOCK_CTRL, val); + + /* Clear statistics/status block in chip, and status block in ram. */ + for (i = NIC_SRAM_STATS_BLK; + i < NIC_SRAM_STATUS_BLK + TG3_HW_STATUS_SIZE; + i += sizeof(u32)) + tg3_write_mem(tp, i, 0); + memset(tp->hw_status, 0, TG3_HW_STATUS_SIZE); + + /* This value is determined during the probe time DMA + * engine test, tg3_test_dma. + */ + tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); + + tp->grc_mode &= ~(GRC_MODE_HOST_SENDBDS | + GRC_MODE_4X_NIC_SEND_RINGS | + GRC_MODE_NO_TX_PHDR_CSUM | + GRC_MODE_NO_RX_PHDR_CSUM); + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) + tp->grc_mode |= GRC_MODE_HOST_SENDBDS; + else + tp->grc_mode |= GRC_MODE_4X_NIC_SEND_RINGS; + if (tp->tg3_flags & TG3_FLAG_NO_TX_PSEUDO_CSUM) + tp->grc_mode |= GRC_MODE_NO_TX_PHDR_CSUM; + if (tp->tg3_flags & TG3_FLAG_NO_RX_PSEUDO_CSUM) + tp->grc_mode |= GRC_MODE_NO_RX_PHDR_CSUM; + + tw32(GRC_MODE, + tp->grc_mode | + (GRC_MODE_IRQ_ON_MAC_ATTN | GRC_MODE_HOST_STACKUP)); + + /* Setup the timer prescalar register. Clock is always 66Mhz. */ + tw32(GRC_MISC_CFG, + (65 << GRC_MISC_CFG_PRESCALAR_SHIFT)); + + /* Initialize MBUF/DESC pool. */ + tw32(BUFMGR_MB_POOL_ADDR, NIC_SRAM_MBUF_POOL_BASE); + tw32(BUFMGR_MB_POOL_SIZE, NIC_SRAM_MBUF_POOL_SIZE); + tw32(BUFMGR_DMA_DESC_POOL_ADDR, NIC_SRAM_DMA_DESC_POOL_BASE); + tw32(BUFMGR_DMA_DESC_POOL_SIZE, NIC_SRAM_DMA_DESC_POOL_SIZE); + + if (!(tp->tg3_flags & TG3_FLAG_JUMBO_ENABLE)) { + tw32(BUFMGR_MB_RDMA_LOW_WATER, + tp->bufmgr_config.mbuf_read_dma_low_water); + tw32(BUFMGR_MB_MACRX_LOW_WATER, + tp->bufmgr_config.mbuf_mac_rx_low_water); + tw32(BUFMGR_MB_HIGH_WATER, + tp->bufmgr_config.mbuf_high_water); + } else { + tw32(BUFMGR_MB_RDMA_LOW_WATER, + tp->bufmgr_config.mbuf_read_dma_low_water_jumbo); + tw32(BUFMGR_MB_MACRX_LOW_WATER, + tp->bufmgr_config.mbuf_mac_rx_low_water_jumbo); + tw32(BUFMGR_MB_HIGH_WATER, + tp->bufmgr_config.mbuf_high_water_jumbo); + } + tw32(BUFMGR_DMA_LOW_WATER, + tp->bufmgr_config.dma_low_water); + tw32(BUFMGR_DMA_HIGH_WATER, + tp->bufmgr_config.dma_high_water); + + tw32(BUFMGR_MODE, BUFMGR_MODE_ENABLE | BUFMGR_MODE_ATTN_ENABLE); + for (i = 0; i < 2000; i++) { + if (tr32(BUFMGR_MODE) & BUFMGR_MODE_ENABLE) + break; + udelay(10); + } + if (i >= 2000) { + printk(KERN_ERR PFX "tg3_reset_hw cannot enable BUFMGR for %s.\n", + tp->dev->name); + return -ENODEV; + } + + tw32(FTQ_RESET, 0xffffffff); + tw32(FTQ_RESET, 0x00000000); + for (i = 0; i < 2000; i++) { + if (tr32(FTQ_RESET) == 0x00000000) + break; + udelay(10); + } + if (i >= 2000) { + printk(KERN_ERR PFX "tg3_reset_hw cannot reset FTQ for %s.\n", + tp->dev->name); + return -ENODEV; + } + + /* Initialize TG3_BDINFO's at: + * RCVDBDI_STD_BD: standard eth size rx ring + * RCVDBDI_JUMBO_BD: jumbo frame rx ring + * RCVDBDI_MINI_BD: small frame rx ring (??? does not work) + * + * like so: + * TG3_BDINFO_HOST_ADDR: high/low parts of DMA address of ring + * TG3_BDINFO_MAXLEN_FLAGS: (rx max buffer size << 16) | + * ring attribute flags + * TG3_BDINFO_NIC_ADDR: location of descriptors in nic SRAM + * + * Standard receive ring @ NIC_SRAM_RX_BUFFER_DESC, 512 entries. + * Jumbo receive ring @ NIC_SRAM_RX_JUMBO_BUFFER_DESC, 256 entries. + * + * ??? No space allocated for mini receive ring? :( + * + * The size of each ring is fixed in the firmware, but the location is + * configurable. + */ + tw32(RCVDBDI_STD_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_HIGH, + ((u64) tp->rx_std_mapping >> 32)); + tw32(RCVDBDI_STD_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW, + ((u64) tp->rx_std_mapping & 0xffffffff)); + tw32(RCVDBDI_STD_BD + TG3_BDINFO_MAXLEN_FLAGS, + RX_STD_MAX_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT); + tw32(RCVDBDI_STD_BD + TG3_BDINFO_NIC_ADDR, + NIC_SRAM_RX_BUFFER_DESC); + +#if TG3_MINI_RING_WORKS + tw32(RCVDBDI_MINI_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_HIGH, + ((u64) tp->rx_mini_mapping >> 32)); + tw32(RCVDBDI_MINI_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW, + ((u64) tp->rx_mini_mapping & 0xffffffff)); + tw32(RCVDBDI_MINI_BD + TG3_BDINFO_MAXLEN_FLAGS, + RX_MINI_MAX_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT); + tw32(RCVDBDI_MINI_BD + TG3_BDINFO_NIC_ADDR, + NIC_SRAM_RX_MINI_BUFFER_DESC); +#else + tw32(RCVDBDI_MINI_BD + TG3_BDINFO_MAXLEN_FLAGS, + BDINFO_FLAGS_DISABLED); +#endif + + if (tp->tg3_flags & TG3_FLAG_JUMBO_ENABLE) { + tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_HIGH, + ((u64) tp->rx_jumbo_mapping >> 32)); + tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW, + ((u64) tp->rx_jumbo_mapping & 0xffffffff)); + tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_MAXLEN_FLAGS, + RX_JUMBO_MAX_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT); + tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_NIC_ADDR, + NIC_SRAM_RX_JUMBO_BUFFER_DESC); + } else { + tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_MAXLEN_FLAGS, + BDINFO_FLAGS_DISABLED); + } + + /* Setup replenish thresholds. */ + tw32(RCVBDI_STD_THRESH, TG3_RX_RING_PENDING / 8); +#if TG3_MINI_RING_WORKS + tw32(RCVBDI_MINI_THRESH, TG3_RX_MINI_RING_PENDING / 8); +#endif + tw32(RCVBDI_JUMBO_THRESH, TG3_RX_JUMBO_RING_PENDING / 8); + + /* Clear out send RCB ring in SRAM. */ + for (i = NIC_SRAM_SEND_RCB; i < NIC_SRAM_RCV_RET_RCB; i += TG3_BDINFO_SIZE) + tg3_write_mem(tp, i + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED); + + tp->tx_prod = 0; + tp->tx_cons = 0; + tw32_mailbox(MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW, 0); + tw32_mailbox(MAILBOX_SNDNIC_PROD_IDX_0 + TG3_64BIT_REG_LOW, 0); + + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { + tg3_set_bdinfo(tp, NIC_SRAM_SEND_RCB, + tp->tx_desc_mapping, + (TG3_TX_RING_SIZE << + BDINFO_FLAGS_MAXLEN_SHIFT), + NIC_SRAM_TX_BUFFER_DESC); + } else { + tg3_set_bdinfo(tp, NIC_SRAM_SEND_RCB, + 0, + BDINFO_FLAGS_DISABLED, + NIC_SRAM_TX_BUFFER_DESC); + } + + for (i = NIC_SRAM_RCV_RET_RCB; i < NIC_SRAM_STATS_BLK; i += TG3_BDINFO_SIZE) { + tg3_write_mem(tp, i + TG3_BDINFO_MAXLEN_FLAGS, + BDINFO_FLAGS_DISABLED); + } + + tp->rx_rcb_ptr = 0; + tw32_mailbox(MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW, 0); + + tg3_set_bdinfo(tp, NIC_SRAM_RCV_RET_RCB, + tp->rx_rcb_mapping, + (TG3_RX_RCB_RING_SIZE << + BDINFO_FLAGS_MAXLEN_SHIFT), + 0); + + tp->rx_std_ptr = TG3_RX_RING_PENDING; + tw32_mailbox(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW, + tp->rx_std_ptr); +#if TG3_MINI_RING_WORKS + tp->rx_mini_ptr = TG3_RX_MINI_RING_PENDING; + tw32_mailbox(MAILBOX_RCV_MINI_PROD_IDX + TG3_64BIT_REG_LOW, + tp->rx_mini_ptr); +#endif + + if (tp->tg3_flags & TG3_FLAG_JUMBO_ENABLE) + tp->rx_jumbo_ptr = TG3_RX_JUMBO_RING_PENDING; + else + tp->rx_jumbo_ptr = 0; + tw32_mailbox(MAILBOX_RCV_JUMBO_PROD_IDX + TG3_64BIT_REG_LOW, + tp->rx_jumbo_ptr); + + /* Initialize MAC address and backoff seed. */ + __tg3_set_mac_addr(tp); + + /* MTU + ethernet header + FCS + optional VLAN tag */ + tw32(MAC_RX_MTU_SIZE, tp->dev->mtu + ETH_HLEN + 8); + + /* The slot time is changed by tg3_setup_phy if we + * run at gigabit with half duplex. + */ + tw32(MAC_TX_LENGTHS, + (2 << TX_LENGTHS_IPG_CRS_SHIFT) | + (6 << TX_LENGTHS_IPG_SHIFT) | + (32 << TX_LENGTHS_SLOT_TIME_SHIFT)); + + /* Receive rules. */ + tw32(MAC_RCV_RULE_CFG, RCV_RULE_CFG_DEFAULT_CLASS); + tw32(RCVLPC_CONFIG, 0x0181); + + /* Receive/send statistics. */ + tw32(RCVLPC_STATS_ENABLE, 0xffffff); + tw32(RCVLPC_STATSCTRL, RCVLPC_STATSCTRL_ENABLE); + tw32(SNDDATAI_STATSENAB, 0xffffff); + tw32(SNDDATAI_STATSCTRL, + (SNDDATAI_SCTRL_ENABLE | + SNDDATAI_SCTRL_FASTUPD)); + + /* Setup host coalescing engine. */ + tw32(HOSTCC_MODE, 0); + for (i = 0; i < 2000; i++) { + if (!(tr32(HOSTCC_MODE) & HOSTCC_MODE_ENABLE)) + break; + udelay(10); + } + + tw32(HOSTCC_RXCOL_TICKS, + tp->coalesce_config.rx_coalesce_ticks); + tw32(HOSTCC_RXMAX_FRAMES, + tp->coalesce_config.rx_max_coalesced_frames); + tw32(HOSTCC_RXCOAL_TICK_INT, + tp->coalesce_config.rx_coalesce_ticks_during_int); + tw32(HOSTCC_RXCOAL_MAXF_INT, + tp->coalesce_config.rx_max_coalesced_frames_during_int); + tw32(HOSTCC_TXCOL_TICKS, + tp->coalesce_config.tx_coalesce_ticks); + tw32(HOSTCC_TXMAX_FRAMES, + tp->coalesce_config.tx_max_coalesced_frames); + tw32(HOSTCC_TXCOAL_TICK_INT, + tp->coalesce_config.tx_coalesce_ticks_during_int); + tw32(HOSTCC_TXCOAL_MAXF_INT, + tp->coalesce_config.tx_max_coalesced_frames_during_int); + tw32(HOSTCC_STAT_COAL_TICKS, + tp->coalesce_config.stats_coalesce_ticks); + + /* Status/statistics block address. */ + tw32(HOSTCC_STATS_BLK_HOST_ADDR + TG3_64BIT_REG_HIGH, + ((u64) tp->stats_mapping >> 32)); + tw32(HOSTCC_STATS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW, + ((u64) tp->stats_mapping & 0xffffffff)); + tw32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_HIGH, + ((u64) tp->status_mapping >> 32)); + tw32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW, + ((u64) tp->status_mapping & 0xffffffff)); + tw32(HOSTCC_STATS_BLK_NIC_ADDR, NIC_SRAM_STATS_BLK); + tw32(HOSTCC_STATUS_BLK_NIC_ADDR, NIC_SRAM_STATUS_BLK); + + tw32(HOSTCC_MODE, HOSTCC_MODE_ENABLE | tp->coalesce_mode); + + tw32(RCVCC_MODE, RCVCC_MODE_ENABLE | RCVCC_MODE_ATTN_ENABLE); + tw32(RCVLPC_MODE, RCVLPC_MODE_ENABLE); + tw32(RCVLSC_MODE, RCVLSC_MODE_ENABLE | RCVLSC_MODE_ATTN_ENABLE); + + tp->mac_mode = MAC_MODE_TXSTAT_ENABLE | MAC_MODE_RXSTAT_ENABLE | + MAC_MODE_TDE_ENABLE | MAC_MODE_RDE_ENABLE | MAC_MODE_FHDE_ENABLE; + tw32(MAC_MODE, tp->mac_mode | MAC_MODE_RXSTAT_CLEAR | MAC_MODE_TXSTAT_CLEAR); + + tp->grc_local_ctrl = GRC_LCLCTRL_INT_ON_ATTN | GRC_LCLCTRL_GPIO_OE1 | + GRC_LCLCTRL_GPIO_OUTPUT1 | GRC_LCLCTRL_AUTO_SEEPROM; + tw32(GRC_LOCAL_CTRL, tp->grc_local_ctrl); + + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0); + + tw32(DMAC_MODE, DMAC_MODE_ENABLE); + + tw32(WDMAC_MODE, (WDMAC_MODE_ENABLE | WDMAC_MODE_TGTABORT_ENAB | + WDMAC_MODE_MSTABORT_ENAB | WDMAC_MODE_PARITYERR_ENAB | + WDMAC_MODE_ADDROFLOW_ENAB | WDMAC_MODE_FIFOOFLOW_ENAB | + WDMAC_MODE_FIFOURUN_ENAB | WDMAC_MODE_FIFOOREAD_ENAB | + WDMAC_MODE_LNGREAD_ENAB)); + tw32(RDMAC_MODE, (RDMAC_MODE_ENABLE | RDMAC_MODE_TGTABORT_ENAB | + RDMAC_MODE_MSTABORT_ENAB | RDMAC_MODE_PARITYERR_ENAB | + RDMAC_MODE_ADDROFLOW_ENAB | RDMAC_MODE_FIFOOFLOW_ENAB | + RDMAC_MODE_FIFOURUN_ENAB | RDMAC_MODE_FIFOOREAD_ENAB | + RDMAC_MODE_LNGREAD_ENAB)); + + tw32(RCVDCC_MODE, RCVDCC_MODE_ENABLE | RCVDCC_MODE_ATTN_ENABLE); + tw32(MBFREE_MODE, MBFREE_MODE_ENABLE); + tw32(SNDDATAC_MODE, SNDDATAC_MODE_ENABLE); + tw32(SNDBDC_MODE, SNDBDC_MODE_ENABLE | SNDBDC_MODE_ATTN_ENABLE); + tw32(RCVBDI_MODE, RCVBDI_MODE_ENABLE | RCVBDI_MODE_RCB_ATTN_ENAB); + tw32(RCVDBDI_MODE, RCVDBDI_MODE_ENABLE | RCVDBDI_MODE_INV_RING_SZ); + tw32(SNDDATAI_MODE, SNDDATAI_MODE_ENABLE); + tw32(SNDBDI_MODE, SNDBDI_MODE_ENABLE | SNDBDI_MODE_ATTN_ENABLE); + tw32(SNDBDS_MODE, SNDBDS_MODE_ENABLE | SNDBDS_MODE_ATTN_ENABLE); + + if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0) { + err = tg3_load_5701_a0_firmware_fix(tp); + if (err) + return err; + } + + tp->tx_mode = TX_MODE_ENABLE; + tw32(MAC_TX_MODE, tp->tx_mode); + tp->rx_mode = RX_MODE_ENABLE; + tw32(MAC_RX_MODE, tp->rx_mode); + + if (tp->link_config.phy_is_low_power) { + tp->link_config.phy_is_low_power = 0; + tp->link_config.speed = tp->link_config.orig_speed; + tp->link_config.duplex = tp->link_config.orig_duplex; + tp->link_config.autoneg = tp->link_config.orig_autoneg; + } + + tp->mi_mode = MAC_MI_MODE_BASE; + tw32(MAC_MI_MODE, tp->mi_mode); + tw32(MAC_LED_CTRL, 0); + tw32(MAC_MI_STAT, MAC_MI_STAT_LNKSTAT_ATTN_ENAB); + tw32(MAC_RX_MODE, RX_MODE_RESET); + udelay(10); + tw32(MAC_RX_MODE, tp->rx_mode); + + err = tg3_setup_phy(tp); + if (err) + return err; + + if (tp->phy_id != PHY_ID_SERDES) { + u32 tmp; + + /* Clear CRC stats. */ + tg3_readphy(tp, 0x1e, &tmp); + tg3_writephy(tp, 0x1e, tmp | 0x8000); + tg3_readphy(tp, 0x14, &tmp); + } + + __tg3_set_rx_mode(tp->dev); + + /* Initialize receive rules. */ + tw32(MAC_RCV_RULE_0, 0xc2000000 & RCV_RULE_DISABLE_MASK); + tw32(MAC_RCV_VALUE_0, 0xffffffff & RCV_RULE_DISABLE_MASK); + tw32(MAC_RCV_RULE_1, 0x86000004 & RCV_RULE_DISABLE_MASK); + tw32(MAC_RCV_VALUE_1, 0xffffffff & RCV_RULE_DISABLE_MASK); +#if 0 + tw32(MAC_RCV_RULE_2, 0); tw32(MAC_RCV_VALUE_2, 0); + tw32(MAC_RCV_RULE_3, 0); tw32(MAC_RCV_VALUE_3, 0); +#endif + tw32(MAC_RCV_RULE_4, 0); tw32(MAC_RCV_VALUE_4, 0); + tw32(MAC_RCV_RULE_5, 0); tw32(MAC_RCV_VALUE_5, 0); + tw32(MAC_RCV_RULE_6, 0); tw32(MAC_RCV_VALUE_6, 0); + tw32(MAC_RCV_RULE_7, 0); tw32(MAC_RCV_VALUE_7, 0); + tw32(MAC_RCV_RULE_8, 0); tw32(MAC_RCV_VALUE_8, 0); + tw32(MAC_RCV_RULE_9, 0); tw32(MAC_RCV_VALUE_9, 0); + tw32(MAC_RCV_RULE_10, 0); tw32(MAC_RCV_VALUE_10, 0); + tw32(MAC_RCV_RULE_11, 0); tw32(MAC_RCV_VALUE_11, 0); + tw32(MAC_RCV_RULE_12, 0); tw32(MAC_RCV_VALUE_12, 0); + tw32(MAC_RCV_RULE_13, 0); tw32(MAC_RCV_VALUE_13, 0); + tw32(MAC_RCV_RULE_14, 0); tw32(MAC_RCV_VALUE_14, 0); + tw32(MAC_RCV_RULE_15, 0); tw32(MAC_RCV_VALUE_15, 0); + + if (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) + tg3_enable_ints(tp); + + return 0; +} + +/* Called at device open time to get the chip ready for + * packet processing. Invoked with tp->lock held. + */ +static int tg3_init_hw(struct tg3 *tp) +{ + int err; + + /* Force the chip into D0. */ + err = tg3_set_power_state(tp, 0); + if (err) + goto out; + + tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0); + + err = tg3_reset_hw(tp); + +out: + return err; +} + +static void tg3_timer(unsigned long __opaque) +{ + struct tg3 *tp = (struct tg3 *) __opaque; + + spin_lock_irq(&tp->lock); + + if (!(tp->tg3_flags & TG3_FLAG_TAGGED_IRQ_STATUS)) { + /* All of this garbage is because on the 5700 the + * mailbox/status_block protocol the chip uses with + * the cpu is race prone. + */ + if (tp->hw_status->status & SD_STATUS_UPDATED) { + tw32(GRC_LOCAL_CTRL, + tp->grc_local_ctrl | GRC_LCLCTRL_SETINT); + } else { + tw32(HOSTCC_MODE, + (HOSTCC_MODE_ENABLE | HOSTCC_MODE_NOW)); + } + + if (!(tr32(WDMAC_MODE) & WDMAC_MODE_ENABLE)) { + tg3_halt(tp); + tg3_init_rings(tp); + tg3_init_hw(tp); + } + } + + /* This part only runs once per second. */ + if (!--tp->timer_counter) { + if (tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG) { + u32 mac_stat; + int phy_event; + + mac_stat = tr32(MAC_STATUS); + + phy_event = 0; + if (tp->tg3_flags & TG3_FLAG_USE_MI_INTERRUPT) { + if (mac_stat & MAC_STATUS_MI_INTERRUPT) + phy_event = 1; + } else if (mac_stat & MAC_STATUS_LNKSTATE_CHANGED) + phy_event = 1; + + if (phy_event) + tg3_setup_phy(tp); + } + + tp->timer_counter = tp->timer_multiplier; + } + + spin_unlock_irq(&tp->lock); + + tp->timer.expires = jiffies + tp->timer_offset; + add_timer(&tp->timer); +} + +static int tg3_open(struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + int err; + + spin_lock_irq(&tp->lock); + + tg3_disable_ints(tp); + tp->tg3_flags &= ~TG3_FLAG_INIT_COMPLETE; + + spin_unlock_irq(&tp->lock); + + /* If you move this call, make sure TG3_FLAG_HOST_TXDS in + * tp->tg3_flags is accurate at that new place. + */ + err = tg3_alloc_consistent(tp); + if (err) + return err; + + if (tp->tg3_flags & TG3_FLAG_TAGGED_IRQ_STATUS) + err = request_irq(dev->irq, tg3_interrupt_tagged, + SA_SHIRQ, dev->name, dev); + else + err = request_irq(dev->irq, tg3_interrupt, + SA_SHIRQ, dev->name, dev); + + if (err) { + tg3_free_consistent(tp); + return err; + } + + spin_lock_irq(&tp->lock); + + tg3_init_rings(tp); + + err = tg3_init_hw(tp); + if (err) { + tg3_halt(tp); + tg3_free_rings(tp); + } else { + if (tp->tg3_flags & TG3_FLAG_TAGGED_IRQ_STATUS) { + tp->timer_offset = HZ; + tp->timer_counter = tp->timer_multiplier = 1; + } else { + tp->timer_offset = HZ / 10; + tp->timer_counter = tp->timer_multiplier = 10; + } + + init_timer(&tp->timer); + tp->timer.expires = jiffies + tp->timer_offset; + tp->timer.data = (unsigned long) tp; + tp->timer.function = tg3_timer; + add_timer(&tp->timer); + + tp->last_rate_sample = jiffies; + tp->last_rx_count = 0; + tp->last_tx_count = 0; + + tp->tg3_flags |= TG3_FLAG_INIT_COMPLETE; + } + + spin_unlock_irq(&tp->lock); + + if (err) { + free_irq(dev->irq, dev); + tg3_free_consistent(tp); + return err; + } + + netif_start_queue(dev); + + spin_lock_irq(&tp->lock); + + tg3_enable_ints(tp); + + spin_unlock_irq(&tp->lock); + + return 0; +} + +#if 0 +/*static*/ void tg3_dump_state(struct tg3 *tp) +{ + u32 val32, val32_2, val32_3, val32_4, val32_5; + u16 val16; + int i; + + pci_read_config_word(tp->pdev, PCI_STATUS, &val16); + pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE, &val32); + printk("DEBUG: PCI status [%04x] TG3PCI state[%08x]\n", + val16, val32); + + /* MAC block */ + printk("DEBUG: MAC_MODE[%08x] MAC_STATUS[%08x]\n", + tr32(MAC_MODE), tr32(MAC_STATUS)); + printk(" MAC_EVENT[%08x] MAC_LED_CTRL[%08x]\n", + tr32(MAC_EVENT), tr32(MAC_LED_CTRL)); + printk("DEBUG: MAC_TX_MODE[%08x] MAC_TX_STATUS[%08x]\n", + tr32(MAC_TX_MODE), tr32(MAC_TX_STATUS)); + printk(" MAC_RX_MODE[%08x] MAC_RX_STATUS[%08x]\n", + tr32(MAC_RX_MODE), tr32(MAC_RX_STATUS)); + + /* Send data initiator control block */ + printk("DEBUG: SNDDATAI_MODE[%08x] SNDDATAI_STATUS[%08x]\n", + tr32(SNDDATAI_MODE), tr32(SNDDATAI_STATUS)); + printk(" SNDDATAI_STATSCTRL[%08x]\n", + tr32(SNDDATAI_STATSCTRL)); + + /* Send data completion control block */ + printk("DEBUG: SNDDATAC_MODE[%08x]\n", tr32(SNDDATAC_MODE)); + + /* Send BD ring selector block */ + printk("DEBUG: SNDBDS_MODE[%08x] SNDBDS_STATUS[%08x]\n", + tr32(SNDBDS_MODE), tr32(SNDBDS_STATUS)); + + /* Send BD initiator control block */ + printk("DEBUG: SNDBDI_MODE[%08x] SNDBDI_STATUS[%08x]\n", + tr32(SNDBDI_MODE), tr32(SNDBDI_STATUS)); + + /* Send BD completion control block */ + printk("DEBUG: SNDBDC_MODE[%08x]\n", tr32(SNDBDC_MODE)); + + /* Receive list placement control block */ + printk("DEBUG: RCVLPC_MODE[%08x] RCVLPC_STATUS[%08x]\n", + tr32(RCVLPC_MODE), tr32(RCVLPC_STATUS)); + printk(" RCVLPC_STATSCTRL[%08x]\n", + tr32(RCVLPC_STATSCTRL)); + + /* Receive data and receive BD initiator control block */ + printk("DEBUG: RCVDBDI_MODE[%08x] RCVDBDI_STATUS[%08x]\n", + tr32(RCVDBDI_MODE), tr32(RCVDBDI_STATUS)); + + /* Receive data completion control block */ + printk("DEBUG: RCVDCC_MODE[%08x]\n", + tr32(RCVDCC_MODE)); + + /* Receive BD initiator control block */ + printk("DEBUG: RCVBDI_MODE[%08x] RCVBDI_STATUS[%08x]\n", + tr32(RCVBDI_MODE), tr32(RCVBDI_STATUS)); + + /* Receive BD completion control block */ + printk("DEBUG: RCVCC_MODE[%08x] RCVCC_STATUS[%08x]\n", + tr32(RCVCC_MODE), tr32(RCVCC_STATUS)); + + /* Receive list selector control block */ + printk("DEBUG: RCVLSC_MODE[%08x] RCVLSC_STATUS[%08x]\n", + tr32(RCVLSC_MODE), tr32(RCVLSC_STATUS)); + + /* Mbuf cluster free block */ + printk("DEBUG: MBFREE_MODE[%08x] MBFREE_STATUS[%08x]\n", + tr32(MBFREE_MODE), tr32(MBFREE_STATUS)); + + /* Host coalescing control block */ + printk("DEBUG: HOSTCC_MODE[%08x] HOSTCC_STATUS[%08x]\n", + tr32(HOSTCC_MODE), tr32(HOSTCC_STATUS)); + printk("DEBUG: HOSTCC_STATS_BLK_HOST_ADDR[%08x%08x]\n", + tr32(HOSTCC_STATS_BLK_HOST_ADDR + TG3_64BIT_REG_HIGH), + tr32(HOSTCC_STATS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW)); + printk("DEBUG: HOSTCC_STATUS_BLK_HOST_ADDR[%08x%08x]\n", + tr32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_HIGH), + tr32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW)); + printk("DEBUG: HOSTCC_STATS_BLK_NIC_ADDR[%08x]\n", + tr32(HOSTCC_STATS_BLK_NIC_ADDR)); + printk("DEBUG: HOSTCC_STATUS_BLK_NIC_ADDR[%08x]\n", + tr32(HOSTCC_STATUS_BLK_NIC_ADDR)); + + /* Memory arbiter control block */ + printk("DEBUG: MEMARB_MODE[%08x] MEMARB_STATUS[%08x]\n", + tr32(MEMARB_MODE), tr32(MEMARB_STATUS)); + + /* Buffer manager control block */ + printk("DEBUG: BUFMGR_MODE[%08x] BUFMGR_STATUS[%08x]\n", + tr32(BUFMGR_MODE), tr32(BUFMGR_STATUS)); + printk("DEBUG: BUFMGR_MB_POOL_ADDR[%08x] BUFMGR_MB_POOL_SIZE[%08x]\n", + tr32(BUFMGR_MB_POOL_ADDR), tr32(BUFMGR_MB_POOL_SIZE)); + printk("DEBUG: BUFMGR_DMA_DESC_POOL_ADDR[%08x] " + "BUFMGR_DMA_DESC_POOL_SIZE[%08x]\n", + tr32(BUFMGR_DMA_DESC_POOL_ADDR), + tr32(BUFMGR_DMA_DESC_POOL_SIZE)); + + /* Read DMA control block */ + printk("DEBUG: RDMAC_MODE[%08x] RDMAC_STATUS[%08x]\n", + tr32(RDMAC_MODE), tr32(RDMAC_STATUS)); + + /* Write DMA control block */ + printk("DEBUG: WDMAC_MODE[%08x] WDMAC_STATUS[%08x]\n", + tr32(WDMAC_MODE), tr32(WDMAC_STATUS)); + + /* DMA completion block */ + printk("DEBUG: DMAC_MODE[%08x]\n", + tr32(DMAC_MODE)); + + /* GRC block */ + printk("DEBUG: GRC_MODE[%08x] GRC_MISC_CFG[%08x]\n", + tr32(GRC_MODE), tr32(GRC_MISC_CFG)); + printk("DEBUG: GRC_LOCAL_CTRL[%08x]\n", + tr32(GRC_LOCAL_CTRL)); + + /* TG3_BDINFOs */ + printk("DEBUG: RCVDBDI_JUMBO_BD[%08x%08x:%08x:%08x]\n", + tr32(RCVDBDI_JUMBO_BD + 0x0), + tr32(RCVDBDI_JUMBO_BD + 0x4), + tr32(RCVDBDI_JUMBO_BD + 0x8), + tr32(RCVDBDI_JUMBO_BD + 0xc)); + printk("DEBUG: RCVDBDI_STD_BD[%08x%08x:%08x:%08x]\n", + tr32(RCVDBDI_STD_BD + 0x0), + tr32(RCVDBDI_STD_BD + 0x4), + tr32(RCVDBDI_STD_BD + 0x8), + tr32(RCVDBDI_STD_BD + 0xc)); + printk("DEBUG: RCVDBDI_MINI_BD[%08x%08x:%08x:%08x]\n", + tr32(RCVDBDI_MINI_BD + 0x0), + tr32(RCVDBDI_MINI_BD + 0x4), + tr32(RCVDBDI_MINI_BD + 0x8), + tr32(RCVDBDI_MINI_BD + 0xc)); + + tg3_read_mem(tp, NIC_SRAM_SEND_RCB + 0x0, &val32); + tg3_read_mem(tp, NIC_SRAM_SEND_RCB + 0x4, &val32_2); + tg3_read_mem(tp, NIC_SRAM_SEND_RCB + 0x8, &val32_3); + tg3_read_mem(tp, NIC_SRAM_SEND_RCB + 0xc, &val32_4); + printk("DEBUG: SRAM_SEND_RCB_0[%08x%08x:%08x:%08x]\n", + val32, val32_2, val32_3, val32_4); + + tg3_read_mem(tp, NIC_SRAM_RCV_RET_RCB + 0x0, &val32); + tg3_read_mem(tp, NIC_SRAM_RCV_RET_RCB + 0x4, &val32_2); + tg3_read_mem(tp, NIC_SRAM_RCV_RET_RCB + 0x8, &val32_3); + tg3_read_mem(tp, NIC_SRAM_RCV_RET_RCB + 0xc, &val32_4); + printk("DEBUG: SRAM_RCV_RET_RCB_0[%08x%08x:%08x:%08x]\n", + val32, val32_2, val32_3, val32_4); + + tg3_read_mem(tp, NIC_SRAM_STATUS_BLK + 0x0, &val32); + tg3_read_mem(tp, NIC_SRAM_STATUS_BLK + 0x4, &val32_2); + tg3_read_mem(tp, NIC_SRAM_STATUS_BLK + 0x8, &val32_3); + tg3_read_mem(tp, NIC_SRAM_STATUS_BLK + 0xc, &val32_4); + tg3_read_mem(tp, NIC_SRAM_STATUS_BLK + 0x10, &val32_5); + printk("DEBUG: SRAM_STATUS_BLK[%08x:%08x:%08x:%08x:%08x]\n", + val32, val32_2, val32_3, val32_4, val32_5); + + /* SW status block */ + printk("DEBUG: Host status block [%08x:%08x:(%04x:%04x:%04x):(%04x:%04x)]\n", + tp->hw_status->status, + tp->hw_status->status_tag, + tp->hw_status->rx_jumbo_consumer, + tp->hw_status->rx_consumer, + tp->hw_status->rx_mini_consumer, + tp->hw_status->idx[0].rx_producer, + tp->hw_status->idx[0].tx_consumer); + + /* SW statistics block */ + printk("DEBUG: Host statistics block [%08x:%08x:%08x:%08x]\n", + ((u32 *)tp->hw_stats)[0], + ((u32 *)tp->hw_stats)[1], + ((u32 *)tp->hw_stats)[2], + ((u32 *)tp->hw_stats)[3]); + + /* Mailboxes */ + printk("DEBUG: SNDHOST_PROD[%08x%08x] SNDNIC_PROD[%08x%08x]\n", + tr32(MAILBOX_SNDHOST_PROD_IDX_0 + 0x0), + tr32(MAILBOX_SNDHOST_PROD_IDX_0 + 0x4), + tr32(MAILBOX_SNDNIC_PROD_IDX_0 + 0x0), + tr32(MAILBOX_SNDNIC_PROD_IDX_0 + 0x4)); + + /* NIC side send descriptors. */ + for (i = 0; i < 6; i++) { + unsigned long txd; + + txd = tp->regs + NIC_SRAM_WIN_BASE + NIC_SRAM_TX_BUFFER_DESC + + (i * sizeof(struct tg3_tx_buffer_desc)); + printk("DEBUG: NIC TXD(%d)[%08x:%08x:%08x:%08x]\n", + i, + readl(txd + 0x0), readl(txd + 0x4), + readl(txd + 0x8), readl(txd + 0xc)); + } + + /* NIC side RX descriptors. */ + for (i = 0; i < 6; i++) { + unsigned long rxd; + + rxd = tp->regs + NIC_SRAM_WIN_BASE + NIC_SRAM_RX_BUFFER_DESC + + (i * sizeof(struct tg3_rx_buffer_desc)); + printk("DEBUG: NIC RXD_STD(%d)[0][%08x:%08x:%08x:%08x]\n", + i, + readl(rxd + 0x0), readl(rxd + 0x4), + readl(rxd + 0x8), readl(rxd + 0xc)); + rxd += (4 * sizeof(u32)); + printk("DEBUG: NIC RXD_STD(%d)[1][%08x:%08x:%08x:%08x]\n", + i, + readl(rxd + 0x0), readl(rxd + 0x4), + readl(rxd + 0x8), readl(rxd + 0xc)); + } +#if TG3_MINI_RING_WORKS + for (i = 0; i < 6; i++) { + unsigned long rxd; + + rxd = tp->regs + NIC_SRAM_WIN_BASE + NIC_SRAM_RX_MINI_BUFFER_DESC + + (i * sizeof(struct tg3_rx_buffer_desc)); + printk("DEBUG: NIC RXD_MINI(%d)[0][%08x:%08x:%08x:%08x]\n", + i, + readl(rxd + 0x0), readl(rxd + 0x4), + readl(rxd + 0x8), readl(rxd + 0xc)); + rxd += (4 * sizeof(u32)); + printk("DEBUG: NIC RXD_MINI(%d)[1][%08x:%08x:%08x:%08x]\n", + i, + readl(rxd + 0x0), readl(rxd + 0x4), + readl(rxd + 0x8), readl(rxd + 0xc)); + } +#endif + + for (i = 0; i < 6; i++) { + unsigned long rxd; + + rxd = tp->regs + NIC_SRAM_WIN_BASE + NIC_SRAM_RX_JUMBO_BUFFER_DESC + + (i * sizeof(struct tg3_rx_buffer_desc)); + printk("DEBUG: NIC RXD_JUMBO(%d)[0][%08x:%08x:%08x:%08x]\n", + i, + readl(rxd + 0x0), readl(rxd + 0x4), + readl(rxd + 0x8), readl(rxd + 0xc)); + rxd += (4 * sizeof(u32)); + printk("DEBUG: NIC RXD_JUMBO(%d)[1][%08x:%08x:%08x:%08x]\n", + i, + readl(rxd + 0x0), readl(rxd + 0x4), + readl(rxd + 0x8), readl(rxd + 0xc)); + } +} +#endif + +static int tg3_close(struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + + netif_stop_queue(dev); + + del_timer_sync(&tp->timer); + + spin_lock_irq(&tp->lock); +#if 0 + tg3_dump_state(tp); +#endif + + tg3_disable_ints(tp); + + tg3_halt(tp); + tg3_free_rings(tp); + tp->tg3_flags &= ~TG3_FLAG_INIT_COMPLETE; + + spin_unlock_irq(&tp->lock); + + free_irq(dev->irq, dev); + + tg3_free_consistent(tp); + + return 0; +} + +static inline unsigned long get_stat64(tg3_stat64_t *val) +{ + unsigned long ret; + +#if (BITS_PER_LONG == 32) + if (val->high != 0) + ret = ~0UL; + else + ret = val->low; +#else + ret = ((u64)val->high << 32) | ((u64)val->low); +#endif + return ret; +} + +static unsigned long calc_crc_errors(struct tg3 *tp) +{ + struct tg3_hw_stats *hw_stats = tp->hw_stats; + + if (tp->phy_id != PHY_ID_SERDES && + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701)) { + unsigned long flags; + u32 val; + + spin_lock_irqsave(&tp->lock, flags); + tg3_readphy(tp, 0x1e, &val); + tg3_writephy(tp, 0x1e, val | 0x8000); + tg3_readphy(tp, 0x14, &val); + spin_unlock_irqrestore(&tp->lock, flags); + + tp->phy_crc_errors += val; + + return tp->phy_crc_errors; + } + + return get_stat64(&hw_stats->rx_fcs_errors); +} + +static struct net_device_stats *tg3_get_stats(struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + struct net_device_stats *stats = &tp->net_stats; + struct tg3_hw_stats *hw_stats = tp->hw_stats; + + /* XXX Fix this... this is wrong because + * XXX it means every open/close we lose the stats. + */ + if (!hw_stats) + return stats; + + stats->rx_packets = + get_stat64(&hw_stats->rx_ucast_packets) + + get_stat64(&hw_stats->rx_mcast_packets) + + get_stat64(&hw_stats->rx_bcast_packets); + + stats->tx_packets = + get_stat64(&hw_stats->COS_out_packets[0]); + + stats->rx_bytes = get_stat64(&hw_stats->rx_octets); + stats->tx_bytes = get_stat64(&hw_stats->tx_octets); + + stats->rx_errors = get_stat64(&hw_stats->rx_errors); + stats->tx_errors = + get_stat64(&hw_stats->tx_errors) + + get_stat64(&hw_stats->tx_mac_errors) + + get_stat64(&hw_stats->tx_carrier_sense_errors) + + get_stat64(&hw_stats->tx_discards); + + stats->multicast = get_stat64(&hw_stats->rx_mcast_packets); + stats->collisions = get_stat64(&hw_stats->tx_collisions); + + stats->rx_length_errors = + get_stat64(&hw_stats->rx_frame_too_long_errors) + + get_stat64(&hw_stats->rx_undersize_packets); + + stats->rx_over_errors = get_stat64(&hw_stats->rxbds_empty); + stats->rx_frame_errors = get_stat64(&hw_stats->rx_align_errors); + stats->tx_aborted_errors = get_stat64(&hw_stats->tx_discards); + stats->tx_carrier_errors = + get_stat64(&hw_stats->tx_carrier_sense_errors); + + stats->rx_crc_errors = calc_crc_errors(tp); + + return stats; +} + +static inline u32 calc_crc(unsigned char *buf, int len) +{ + u32 reg; + u32 tmp; + int j, k; + + reg = 0xffffffff; + + for (j = 0; j < len; j++) { + reg ^= buf[j]; + + for (k = 0; k < 8; k++) { + tmp = reg & 0x01; + + reg >>= 1; + + if (tmp) { + reg ^= 0xedb88320; + } + } + } + + return ~reg; +} + +static void tg3_set_multi(struct tg3 *tp, unsigned int accept_all) +{ + /* accept or reject all multicast frames */ + tw32 (MAC_HASH_REG_0, accept_all ? 0xffffffff : 0); + tw32 (MAC_HASH_REG_1, accept_all ? 0xffffffff : 0); + tw32 (MAC_HASH_REG_2, accept_all ? 0xffffffff : 0); + tw32 (MAC_HASH_REG_3, accept_all ? 0xffffffff : 0); +} + +static void __tg3_set_rx_mode(struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + u32 rx_mode; + + rx_mode = tp->rx_mode & ~RX_MODE_PROMISC; + + if (dev->flags & IFF_PROMISC) { + /* Promiscuous mode. */ + rx_mode |= RX_MODE_PROMISC; + } else if (dev->flags & IFF_ALLMULTI) { + /* Accept all multicast. */ + tg3_set_multi (tp, 1); + } else if (dev->mc_count < 1) { + /* Reject all multicast. */ + tg3_set_multi (tp, 0); + } else { + /* Accept one or more multicast(s). */ + struct dev_mc_list *mclist; + unsigned int i; + u32 mc_filter[4] = { 0, }; + u32 regidx; + u32 bit; + u32 crc; + + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + + crc = calc_crc (mclist->dmi_addr, ETH_ALEN); + bit = ~crc & 0x7f; + regidx = (bit & 0x60) >> 5; + bit &= 0x1f; + mc_filter[regidx] |= (1 << bit); + } + + tw32 (MAC_HASH_REG_0, mc_filter[0]); + tw32 (MAC_HASH_REG_1, mc_filter[1]); + tw32 (MAC_HASH_REG_2, mc_filter[2]); + tw32 (MAC_HASH_REG_3, mc_filter[3]); + } + + if (rx_mode != tp->rx_mode) { + tp->rx_mode = rx_mode; + tw32 (MAC_RX_MODE, rx_mode); + } +} + +static void tg3_set_rx_mode(struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + + spin_lock_irq(&tp->lock); + __tg3_set_rx_mode(dev); + spin_unlock_irq(&tp->lock); +} + +static u8 *tg3_get_regs(struct tg3 *tp) +{ + u8 *orig_p = kmalloc((32 * 1024), GFP_KERNEL); + u8 *p; + int i; + + if (orig_p == NULL) + return NULL; + + memset(orig_p, 0, (32 * 1024)); + + spin_lock_irq(&tp->lock); + +#define __GET_REG32(reg) (*((u32 *)(p))++ = tr32(reg)) +#define GET_REG32_LOOP(base,len) \ +do { p = orig_p + (base); \ + for (i = 0; i < len; i += 4) \ + __GET_REG32((base) + i); \ +} while (0) +#define GET_REG32_1(reg) \ +do { p = orig_p + (reg); \ + __GET_REG32((reg)); \ +} while (0) + + GET_REG32_LOOP(TG3PCI_VENDOR, 0xb0); + GET_REG32_LOOP(MAILBOX_INTERRUPT_0, 0x200); + GET_REG32_LOOP(MAC_MODE, 0x4f0); + GET_REG32_LOOP(SNDDATAI_MODE, 0xe0); + GET_REG32_1(SNDDATAC_MODE); + GET_REG32_LOOP(SNDBDS_MODE, 0x80); + GET_REG32_LOOP(SNDBDI_MODE, 0x48); + GET_REG32_1(SNDBDC_MODE); + GET_REG32_LOOP(RCVLPC_MODE, 0x20); + GET_REG32_LOOP(RCVLPC_SELLST_BASE, 0x15c); + GET_REG32_LOOP(RCVDBDI_MODE, 0x0c); + GET_REG32_LOOP(RCVDBDI_JUMBO_BD, 0x3c); + GET_REG32_LOOP(RCVDBDI_BD_PROD_IDX_0, 0x44); + GET_REG32_1(RCVDCC_MODE); + GET_REG32_LOOP(RCVBDI_MODE, 0x20); + GET_REG32_LOOP(RCVCC_MODE, 0x14); + GET_REG32_LOOP(RCVLSC_MODE, 0x08); + GET_REG32_1(MBFREE_MODE); + GET_REG32_LOOP(HOSTCC_MODE, 0x100); + GET_REG32_LOOP(MEMARB_MODE, 0x10); + GET_REG32_LOOP(BUFMGR_MODE, 0x58); + GET_REG32_LOOP(RDMAC_MODE, 0x08); + GET_REG32_LOOP(WDMAC_MODE, 0x08); + GET_REG32_LOOP(RX_CPU_BASE, 0x280); + GET_REG32_LOOP(TX_CPU_BASE, 0x280); + GET_REG32_LOOP(GRCMBOX_INTERRUPT_0, 0x110); + GET_REG32_LOOP(FTQ_RESET, 0x120); + GET_REG32_LOOP(MSGINT_MODE, 0x0c); + GET_REG32_1(DMAC_MODE); + GET_REG32_LOOP(GRC_MODE, 0x4c); + GET_REG32_LOOP(NVRAM_CMD, 0x24); + +#undef __GET_REG32 +#undef GET_REG32_LOOP +#undef GET_REG32_1 + + spin_unlock_irq(&tp->lock); + + return orig_p; +} + +static int tg3_ethtool_ioctl (struct net_device *dev, void *useraddr) +{ + struct tg3 *tp = dev->priv; + struct pci_dev *pci_dev = tp->pdev; + u32 ethcmd; + + if (copy_from_user (ðcmd, useraddr, sizeof (ethcmd))) + return -EFAULT; + + switch (ethcmd) { + case ETHTOOL_GDRVINFO:{ + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strcpy (info.driver, DRV_MODULE_NAME); + strcpy (info.version, DRV_MODULE_VERSION); + strcpy (info.bus_info, pci_dev->slot_name); + if (copy_to_user (useraddr, &info, sizeof (info))) + return -EFAULT; + return 0; + } + + case ETHTOOL_GSET: { + struct ethtool_cmd cmd = { ETHTOOL_GSET }; + + if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) || + tp->link_config.phy_is_low_power) + return -EAGAIN; + cmd.supported = (SUPPORTED_Autoneg); + + if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) + cmd.supported |= (SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full); + + if (tp->phy_id != PHY_ID_SERDES) + cmd.supported |= (SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_MII); + else + cmd.supported |= SUPPORTED_FIBRE; + + cmd.advertising = tp->link_config.advertising; + cmd.speed = tp->link_config.active_speed; + cmd.duplex = tp->link_config.active_duplex; + cmd.port = 0; + cmd.phy_address = PHY_ADDR; + cmd.transceiver = 0; + cmd.autoneg = tp->link_config.autoneg; + cmd.maxtxpkt = tp->coalesce_config.tx_max_coalesced_frames; + cmd.maxrxpkt = tp->coalesce_config.rx_max_coalesced_frames; + if (copy_to_user(useraddr, &cmd, sizeof(cmd))) + return -EFAULT; + return 0; + } + case ETHTOOL_SSET: { + struct ethtool_cmd cmd; + + if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) || + tp->link_config.phy_is_low_power) + return -EAGAIN; + + if (copy_from_user(&cmd, useraddr, sizeof(cmd))) + return -EFAULT; + + /* Fiber PHY only supports 1000 full/half */ + if (cmd.autoneg == AUTONEG_ENABLE) { + if (tp->phy_id == PHY_ID_SERDES && + (cmd.advertising & + (ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full))) + return -EINVAL; + if ((tp->tg3_flags & TG3_FLAG_10_100_ONLY) && + (cmd.advertising & + (ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full))) + return -EINVAL; + } else { + if (tp->phy_id == PHY_ID_SERDES && + (cmd.speed == SPEED_10 || + cmd.speed == SPEED_100)) + return -EINVAL; + if ((tp->tg3_flags & TG3_FLAG_10_100_ONLY) && + (cmd.speed == SPEED_10 || + cmd.speed == SPEED_100)) + return -EINVAL; + } + + spin_lock_irq(&tp->lock); + + tp->link_config.autoneg = cmd.autoneg; + if (cmd.autoneg == AUTONEG_ENABLE) { + tp->link_config.advertising = cmd.advertising; + tp->link_config.speed = SPEED_INVALID; + tp->link_config.duplex = DUPLEX_INVALID; + } else { + tp->link_config.speed = cmd.speed; + tp->link_config.duplex = cmd.duplex; + } + + if (cmd.maxtxpkt || cmd.maxrxpkt) { + tp->coalesce_config.tx_max_coalesced_frames = + cmd.maxtxpkt; + tp->coalesce_config.rx_max_coalesced_frames = + cmd.maxrxpkt; + + /* Coalescing config bits can be updated without + * a full chip reset. + */ + tw32(HOSTCC_TXMAX_FRAMES, + tp->coalesce_config.tx_max_coalesced_frames); + tw32(HOSTCC_RXMAX_FRAMES, + tp->coalesce_config.rx_max_coalesced_frames); + } + tg3_setup_phy(tp); + spin_unlock_irq(&tp->lock); + + return 0; + } + + case ETHTOOL_GREGS: { + struct ethtool_regs regs; + u8 *regbuf; + int ret; + + if (copy_from_user(®s, useraddr, sizeof(regs))) + return -EFAULT; + if (regs.len > (32 * 1024)) + regs.len = (32 * 1024); + regs.version = 0; + if (copy_to_user(useraddr, ®s, sizeof(regs))) + return -EFAULT; + + regbuf = tg3_get_regs(tp); + if (!regbuf) + return -ENOMEM; + + useraddr += offsetof(struct ethtool_regs, data); + ret = 0; + if (copy_to_user(useraddr, regbuf, regs.len)) + ret = -EFAULT; + kfree(regbuf); + return ret; + } + case ETHTOOL_GWOL: { + struct ethtool_wolinfo wol = { ETHTOOL_GWOL }; + + wol.supported = WAKE_MAGIC; + wol.wolopts = 0; + if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE) + wol.wolopts = WAKE_MAGIC; + memset(&wol.sopass, 0, sizeof(wol.sopass)); + if (copy_to_user(useraddr, &wol, sizeof(wol))) + return -EFAULT; + return 0; + } + case ETHTOOL_SWOL: { + struct ethtool_wolinfo wol; + + if (copy_from_user(&wol, useraddr, sizeof(wol))) + return -EFAULT; + if (wol.wolopts & ~WAKE_MAGIC) + return -EINVAL; + spin_lock_irq(&tp->lock); + if (wol.wolopts & WAKE_MAGIC) + tp->tg3_flags |= TG3_FLAG_WOL_ENABLE; + else + tp->tg3_flags &= ~TG3_FLAG_WOL_ENABLE; + spin_unlock_irq(&tp->lock); + + return 0; + } + case ETHTOOL_GMSGLVL: { + struct ethtool_value edata = { ETHTOOL_GMSGLVL }; + edata.data = tp->msg_enable; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + case ETHTOOL_SMSGLVL: { + struct ethtool_value edata; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + tp->msg_enable = edata.data; + return 0; + } + case ETHTOOL_NWAY_RST: { + u32 bmcr; + int r; + + spin_lock_irq(&tp->lock); + tg3_readphy(tp, MII_BMCR, &bmcr); + tg3_readphy(tp, MII_BMCR, &bmcr); + r = -EINVAL; + if (bmcr & BMCR_ANENABLE) { + tg3_writephy(tp, MII_BMCR, + bmcr | BMCR_ANRESTART); + r = 0; + } + spin_unlock_irq(&tp->lock); + + return r; + } + case ETHTOOL_GLINK: { + struct ethtool_value edata = { ETHTOOL_GLINK }; + edata.data = netif_carrier_ok(tp->dev) ? 1 : 0; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + } + }; + + return -EOPNOTSUPP; +} + +static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data; + struct tg3 *tp = dev->priv; + int err; + + switch(cmd) { + case SIOCETHTOOL: + return tg3_ethtool_ioctl(dev, (void *) ifr->ifr_data); + case SIOCGMIIPHY: + data->phy_id = PHY_ADDR; + + /* fallthru */ + case SIOCGMIIREG: { + u32 mii_regval; + + spin_lock_irq(&tp->lock); + err = tg3_readphy(tp, data->reg_num & 0x1f, &mii_regval); + spin_unlock_irq(&tp->lock); + + data->val_out = mii_regval; + + return err; + } + + case SIOCSMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irq(&tp->lock); + err = tg3_writephy(tp, data->reg_num & 0x1f, data->val_in); + spin_unlock_irq(&tp->lock); + + return err; + + default: + /* do nothing */ + break; + } + return -EOPNOTSUPP; +} + +#if TG3_VLAN_TAG_USED +static void tg3_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) +{ + struct tg3 *tp = dev->priv; + + spin_lock_irq(&tp->lock); + tp->vlgrp = grp; + spin_unlock_irq(&tp->lock); +} + +static void tg3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) +{ + struct tg3 *tp = dev->priv; + + spin_lock_irq(&tp->lock); + if (tp->vlgrp) + tp->vlgrp->vlan_devices[vid] = NULL; + spin_unlock_irq(&tp->lock); +} +#endif + +/* Chips other than 5700/5701 use the NVRAM for fetching info. */ +static void __devinit tg3_nvram_init(struct tg3 *tp) +{ + int j; + + tw32(GRC_EEPROM_ADDR, + (EEPROM_ADDR_FSM_RESET | + (EEPROM_DEFAULT_CLOCK_PERIOD << + EEPROM_ADDR_CLKPERD_SHIFT))); + + /* XXX schedule_timeout() ... */ + for (j = 0; j < 100; j++) + udelay(10); + + /* Enable seeprom accesses. */ + tw32(GRC_LOCAL_CTRL, + tr32(GRC_LOCAL_CTRL) | GRC_LCLCTRL_AUTO_SEEPROM); + udelay(100); + + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) { + u32 nvcfg1 = tr32(NVRAM_CFG1); + + tp->tg3_flags |= TG3_FLAG_NVRAM; + if (nvcfg1 & NVRAM_CFG1_FLASHIF_ENAB) { + if (nvcfg1 & NVRAM_CFG1_BUFFERED_MODE) + tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; + } else { + nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS; + tw32(NVRAM_CFG1, nvcfg1); + } + + } else { + tp->tg3_flags &= ~(TG3_FLAG_NVRAM | TG3_FLAG_NVRAM_BUFFERED); + } +} + +static int __devinit tg3_nvram_read_using_eeprom(struct tg3 *tp, + u32 offset, u32 *val) +{ + u32 tmp; + int i; + + if (offset > EEPROM_ADDR_ADDR_MASK || + (offset % 4) != 0) + return -EINVAL; + + tmp = tr32(GRC_EEPROM_ADDR) & ~(EEPROM_ADDR_ADDR_MASK | + EEPROM_ADDR_DEVID_MASK | + EEPROM_ADDR_READ); + tw32(GRC_EEPROM_ADDR, + tmp | + (0 << EEPROM_ADDR_DEVID_SHIFT) | + ((offset << EEPROM_ADDR_ADDR_SHIFT) & + EEPROM_ADDR_ADDR_MASK) | + EEPROM_ADDR_READ | EEPROM_ADDR_START); + + for (i = 0; i < 10000; i++) { + tmp = tr32(GRC_EEPROM_ADDR); + + if (tmp & EEPROM_ADDR_COMPLETE) + break; + udelay(100); + } + if (!(tmp & EEPROM_ADDR_COMPLETE)) + return -EBUSY; + + *val = tr32(GRC_EEPROM_DATA); + return 0; +} + +static int __devinit tg3_nvram_read(struct tg3 *tp, + u32 offset, u32 *val) +{ + int i, saw_done_clear; + + if (!(tp->tg3_flags & TG3_FLAG_NVRAM)) + return tg3_nvram_read_using_eeprom(tp, offset, val); + + if (tp->tg3_flags & TG3_FLAG_NVRAM_BUFFERED) + offset = ((offset / NVRAM_BUFFERED_PAGE_SIZE) << + NVRAM_BUFFERED_PAGE_POS) + + (offset % NVRAM_BUFFERED_PAGE_SIZE); + + if (offset > NVRAM_ADDR_MSK) + return -EINVAL; + + tw32(NVRAM_SWARB, SWARB_REQ_SET1); + for (i = 0; i < 1000; i++) { + if (tr32(NVRAM_SWARB) & SWARB_GNT1) + break; + udelay(20); + } + + tw32(NVRAM_ADDR, offset); + tw32(NVRAM_CMD, + NVRAM_CMD_RD | NVRAM_CMD_GO | + NVRAM_CMD_FIRST | NVRAM_CMD_LAST | NVRAM_CMD_DONE); + + /* Wait for done bit to clear then set again. */ + saw_done_clear = 0; + for (i = 0; i < 1000; i++) { + udelay(10); + if (!saw_done_clear && + !(tr32(NVRAM_CMD) & NVRAM_CMD_DONE)) + saw_done_clear = 1; + else if (saw_done_clear && + (tr32(NVRAM_CMD) & NVRAM_CMD_DONE)) + break; + } + if (i >= 1000) { + tw32(NVRAM_SWARB, SWARB_REQ_CLR1); + return -EBUSY; + } + + *val = swab32(tr32(NVRAM_RDDATA)); + tw32(NVRAM_SWARB, 0x20); + + return 0; +} + +struct subsys_tbl_ent { + u16 subsys_vendor, subsys_devid; + u32 phy_id; +}; + +static struct subsys_tbl_ent subsys_id_to_phy_id[] = { + /* Broadcom boards. */ + { 0x14e4, 0x1644, PHY_ID_BCM5401 }, /* BCM95700A6 */ + { 0x14e4, 0x0001, PHY_ID_BCM5701 }, /* BCM95701A5 */ + { 0x14e4, 0x0002, PHY_ID_BCM8002 }, /* BCM95700T6 */ + { 0x14e4, 0x0003, PHY_ID_SERDES }, /* BCM95700A9 */ + { 0x14e4, 0x0005, PHY_ID_BCM5701 }, /* BCM95701T1 */ + { 0x14e4, 0x0006, PHY_ID_BCM5701 }, /* BCM95701T8 */ + { 0x14e4, 0x0007, PHY_ID_SERDES }, /* BCM95701A7 */ + { 0x14e4, 0x0008, PHY_ID_BCM5701 }, /* BCM95701A10 */ + { 0x14e4, 0x8008, PHY_ID_BCM5701 }, /* BCM95701A12 */ + { 0x14e4, 0x0009, PHY_ID_BCM5701 }, /* BCM95703Ax1 */ + { 0x14e4, 0x8009, PHY_ID_BCM5701 }, /* BCM95703Ax2 */ + + /* 3com boards. */ + { PCI_VENDOR_ID_3COM, 0x1000, PHY_ID_BCM5401 }, /* 3C996T */ + { PCI_VENDOR_ID_3COM, 0x1006, PHY_ID_BCM5701 }, /* 3C996BT */ + /* { PCI_VENDOR_ID_3COM, 0x1002, PHY_ID_XXX }, 3C996CT */ + /* { PCI_VENDOR_ID_3COM, 0x1003, PHY_ID_XXX }, 3C997T */ + { PCI_VENDOR_ID_3COM, 0x1004, PHY_ID_SERDES }, /* 3C996SX */ + /* { PCI_VENDOR_ID_3COM, 0x1005, PHY_ID_XXX }, 3C997SZ */ + { PCI_VENDOR_ID_3COM, 0x1007, PHY_ID_BCM5701 }, /* 3C1000T */ + { PCI_VENDOR_ID_3COM, 0x1008, PHY_ID_BCM5701 }, /* 3C940BR01 */ + + /* DELL boards. */ + { PCI_VENDOR_ID_DELL, 0x00d1, PHY_ID_BCM5401 }, /* VIPER */ + { PCI_VENDOR_ID_DELL, 0x0106, PHY_ID_BCM5401 }, /* JAGUAR */ + { PCI_VENDOR_ID_DELL, 0x0109, PHY_ID_BCM5411 }, /* MERLOT */ + { PCI_VENDOR_ID_DELL, 0x010a, PHY_ID_BCM5411 }, /* SLIM_MERLOT */ + + /* Compaq boards. */ + { PCI_VENDOR_ID_COMPAQ, 0x007c, PHY_ID_BCM5701 }, /* BANSHEE */ + { PCI_VENDOR_ID_COMPAQ, 0x009a, PHY_ID_BCM5701 }, /* BANSHEE_2 */ + { PCI_VENDOR_ID_COMPAQ, 0x007d, PHY_ID_SERDES }, /* CHANGELING */ + { PCI_VENDOR_ID_COMPAQ, 0x0085, PHY_ID_BCM5701 }, /* NC7780 */ + { PCI_VENDOR_ID_COMPAQ, 0x0099, PHY_ID_BCM5701 } /* NC7780_2 */ +}; + +static int __devinit tg3_phy_probe(struct tg3 *tp) +{ + u32 eeprom_phy_id, hw_phy_id_1, hw_phy_id_2; + u32 hw_phy_id, hw_phy_id_masked; + enum phy_led_mode eeprom_led_mode; + u32 val; + int i, eeprom_signature_found, err; + + tp->phy_id = PHY_ID_INVALID; + for (i = 0; i < ARRAY_SIZE(subsys_id_to_phy_id); i++) { + if ((subsys_id_to_phy_id[i].subsys_vendor == + tp->pdev->subsystem_vendor) && + (subsys_id_to_phy_id[i].subsys_devid == + tp->pdev->subsystem_device)) { + tp->phy_id = subsys_id_to_phy_id[i].phy_id; + break; + } + } + + eeprom_phy_id = PHY_ID_INVALID; + eeprom_led_mode = led_mode_auto; + eeprom_signature_found = 0; + tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val); + if (val == NIC_SRAM_DATA_SIG_MAGIC) { + u32 nic_cfg; + + tg3_read_mem(tp, NIC_SRAM_DATA_CFG, &nic_cfg); + + eeprom_signature_found = 1; + + if ((nic_cfg & NIC_SRAM_DATA_CFG_PHY_TYPE_MASK) == + NIC_SRAM_DATA_CFG_PHY_TYPE_FIBER) { + eeprom_phy_id = PHY_ID_SERDES; + } else { + u32 nic_phy_id; + + tg3_read_mem(tp, NIC_SRAM_DATA_PHY_ID, &nic_phy_id); + if (nic_phy_id != 0) { + u32 id1 = nic_phy_id & NIC_SRAM_DATA_PHY_ID1_MASK; + u32 id2 = nic_phy_id & NIC_SRAM_DATA_PHY_ID2_MASK; + + eeprom_phy_id = (id1 >> 16) << 10; + eeprom_phy_id |= (id2 & 0xfc00) << 16; + eeprom_phy_id |= (id2 & 0x03ff) << 0; + } + } + + switch (nic_cfg & NIC_SRAM_DATA_CFG_LED_MODE_MASK) { + case NIC_SRAM_DATA_CFG_LED_TRIPLE_SPD: + eeprom_led_mode = led_mode_three_link; + break; + + case NIC_SRAM_DATA_CFG_LED_LINK_SPD: + eeprom_led_mode = led_mode_link10; + break; + + default: + eeprom_led_mode = led_mode_auto; + break; + }; + } + + err = tg3_phy_reset(tp, 0); + if (err) + return err; + + /* Now read the physical PHY_ID from the chip and verify + * that it is sane. If it doesn't look good, we fall back + * to either the hard-coded table based PHY_ID and failing + * that the value found in the eeprom area. + */ + err = tg3_readphy(tp, MII_PHYSID1, &hw_phy_id_1); + err |= tg3_readphy(tp, MII_PHYSID2, &hw_phy_id_2); + + hw_phy_id = (hw_phy_id_1 & 0xffff) << 10; + hw_phy_id |= (hw_phy_id_2 & 0xfc00) << 16; + hw_phy_id |= (hw_phy_id_2 & 0x03ff) << 0; + + hw_phy_id_masked = hw_phy_id & PHY_ID_MASK; + + if (!err && KNOWN_PHY_ID(hw_phy_id_masked)) { + tp->phy_id = hw_phy_id; + } else { + /* phy_id currently holds the value found in the + * subsys_id_to_phy_id[] table or PHY_ID_INVALID + * if a match was not found there. + */ + if (tp->phy_id == PHY_ID_INVALID) { + if (!eeprom_signature_found || + !KNOWN_PHY_ID(eeprom_phy_id & PHY_ID_MASK)) + return -ENODEV; + tp->phy_id = eeprom_phy_id; + } + } + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) { + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00); + tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f); + tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x2aaa); + } + + if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) + tp->tg3_flags |= TG3_FLAG_PHY_RESET_ON_INIT; + + if (tp->tg3_flags & TG3_FLAG_PHY_RESET_ON_INIT) { + u32 mii_tg3_ctrl; + + err = tg3_phy_reset(tp, 1); + if (err) + return err; + + /* These chips, when reset, only advertise 10Mb capabilities. + * Fix that. + */ + err = tg3_writephy(tp, MII_ADVERTISE, + (ADVERTISE_CSMA | + ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL)); + mii_tg3_ctrl = (MII_TG3_CTRL_ADV_1000_HALF | + MII_TG3_CTRL_ADV_1000_FULL | + MII_TG3_CTRL_AS_MASTER | + MII_TG3_CTRL_ENABLE_AS_MASTER); + if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) + mii_tg3_ctrl = 0; + + err |= tg3_writephy(tp, MII_TG3_CTRL, mii_tg3_ctrl); + err |= tg3_writephy(tp, MII_BMCR, + (BMCR_ANRESTART | BMCR_ANENABLE)); + } + + if (!err && ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401)) { + err = tg3_init_5401phy_dsp(tp); + } + + /* Determine the PHY led mode. */ + if (tp->pdev->subsystem_vendor == PCI_VENDOR_ID_DELL) { + tp->led_mode = led_mode_link10; + } else { + tp->led_mode = led_mode_three_link; + if (eeprom_signature_found && + eeprom_led_mode != led_mode_auto) + tp->led_mode = eeprom_led_mode; + } + + if (tp->phy_id == PHY_ID_SERDES) + tp->link_config.advertising = + (ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full | + ADVERTISED_Autoneg | + ADVERTISED_FIBRE); + if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) + tp->link_config.advertising &= + ~(ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full); + + return err; +} + +static void __devinit tg3_read_partno(struct tg3 *tp) +{ + unsigned char vpd_data[256]; + int i; + + for (i = 0; i < 256; i += 4) { + u32 tmp; + + if (tg3_nvram_read(tp, 0x100 + i, &tmp)) + goto out_not_found; + + vpd_data[i + 0] = ((tmp >> 0) & 0xff); + vpd_data[i + 1] = ((tmp >> 8) & 0xff); + vpd_data[i + 2] = ((tmp >> 16) & 0xff); + vpd_data[i + 3] = ((tmp >> 24) & 0xff); + } + + /* Now parse and find the part number. */ + for (i = 0; i < 256; ) { + unsigned char val = vpd_data[i]; + int block_end; + + if (val == 0x82 || val == 0x91) { + i = (i + 3 + + (vpd_data[i + 1] + + (vpd_data[i + 2] << 8))); + continue; + } + + if (val != 0x90) + goto out_not_found; + + block_end = (i + 3 + + (vpd_data[i + 1] + + (vpd_data[i + 2] << 8))); + i += 3; + while (i < block_end) { + if (vpd_data[i + 0] == 'P' && + vpd_data[i + 1] == 'N') { + int partno_len = vpd_data[i + 2]; + + if (partno_len > 24) + goto out_not_found; + + memcpy(tp->board_part_number, + &vpd_data[i + 3], + partno_len); + + /* Success. */ + return; + } + } + + /* Part number not found. */ + goto out_not_found; + } + +out_not_found: + strcpy(tp->board_part_number, "none"); +} + +static int __devinit tg3_get_invariants(struct tg3 *tp) +{ + u32 misc_ctrl_reg; + u32 cacheline_sz_reg; + u32 pci_state_reg, grc_misc_cfg; + u16 pci_cmd; + int err; + + /* Force memory write invalidate off. If we leave it on, + * then on 5700_BX chips we have to enable a workaround. + * The workaround is to set the TG3PCI_DMA_RW_CTRL boundry + * to match the cacheline size. The Broadcom driver have this + * workaround but turns MWI off all the times so never uses + * it. This seems to suggest that the workaround is insufficient. + */ + pci_read_config_word(tp->pdev, PCI_COMMAND, &pci_cmd); + pci_cmd &= ~PCI_COMMAND_INVALIDATE; + pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd); + + pci_read_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, + &misc_ctrl_reg); + + tp->pci_chip_rev_id = (misc_ctrl_reg >> + MISC_HOST_CTRL_CHIPREV_SHIFT); + + pci_read_config_dword(tp->pdev, TG3PCI_CACHELINESZ, + &cacheline_sz_reg); + + tp->pci_cacheline_sz = (cacheline_sz_reg >> 24) & 0xff; + tp->pci_lat_timer = (cacheline_sz_reg >> 16) & 0xff; + tp->pci_hdr_type = (cacheline_sz_reg >> 8) & 0xff; + tp->pci_bist = (cacheline_sz_reg >> 0) & 0xff; + + pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE, + &pci_state_reg); + + if ((pci_state_reg & PCISTATE_CONV_PCI_MODE) == 0) { + tp->tg3_flags |= TG3_FLAG_PCIX_MODE; + + /* If this is a 5700 BX chipset, and we are in PCI-X + * mode, enable register write workaround. + * + * The workaround is to use indirect register accesses + * for all chip writes not to mailbox registers. + */ + if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5700_BX) { + u32 pm_reg; + u16 pci_cmd; + + tp->tg3_flags |= TG3_FLAG_PCIX_TARGET_HWBUG; + + /* The chip can have it's power management PCI config + * space registers clobbered due to this bug. + * So explicitly force the chip into D0 here. + */ + pci_read_config_dword(tp->pdev, TG3PCI_PM_CTRL_STAT, + &pm_reg); + pm_reg &= ~PCI_PM_CTRL_STATE_MASK; + pm_reg |= PCI_PM_CTRL_PME_ENABLE | 0 /* D0 */; + pci_write_config_dword(tp->pdev, TG3PCI_PM_CTRL_STAT, + pm_reg); + + /* Also, force SERR#/PERR# in PCI command. */ + pci_read_config_word(tp->pdev, PCI_COMMAND, &pci_cmd); + pci_cmd |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR; + pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd); + } + } + if ((pci_state_reg & PCISTATE_BUS_SPEED_HIGH) != 0) + tp->tg3_flags |= TG3_FLAG_PCI_HIGH_SPEED; + if ((pci_state_reg & PCISTATE_BUS_32BIT) != 0) + tp->tg3_flags |= TG3_FLAG_PCI_32BIT; + + /* Force the chip into D0. */ + err = tg3_set_power_state(tp, 0); + if (err) + return err; + + /* 5700 B0 chips do not support checksumming correctly due + * to hardware bugs. + */ + if (tp->pci_chip_rev_id == CHIPREV_ID_5700_B0) + tp->tg3_flags |= TG3_FLAG_BROKEN_CHECKSUMS; + + /* Regardless of whether checksums work or not, we configure + * the StrongARM chips to not compute the pseudo header checksums + * in either direction. Because of the way Linux checksum support + * works we do not need the chips to do this, and taking the load + * off of the TX/RX onboard StrongARM cpus means that they will not be + * the bottleneck. Whoever wrote Broadcom's driver did not + * understand the situation at all. He could have bothered + * to read Jes's Acenic driver because the logic (and this part of + * the Tigon2 hardware/firmware) is pretty much identical. + */ + tp->tg3_flags |= TG3_FLAG_NO_TX_PSEUDO_CSUM; + tp->tg3_flags |= TG3_FLAG_NO_RX_PSEUDO_CSUM; + + /* Derive initial jumbo mode from MTU assigned in + * ether_setup() via the alloc_etherdev() call + */ + if (tp->dev->mtu > ETH_DATA_LEN) + tp->tg3_flags |= TG3_FLAG_JUMBO_ENABLE; + + /* Determine WakeOnLan speed to use. */ + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_B0 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_B2) { + tp->tg3_flags &= ~(TG3_FLAG_WOL_SPEED_100MB); + } else { + tp->tg3_flags |= TG3_FLAG_WOL_SPEED_100MB; + } + + /* Only 5701 and later support tagged irq status mode. */ + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700) { + tp->tg3_flags |= TG3_FLAG_TAGGED_IRQ_STATUS; + tp->misc_host_ctrl |= MISC_HOST_CTRL_TAGGED_STATUS; + + /* ??? Due to a glitch Broadcom's driver ALWAYS sets + * ??? these bits in coalesce_mode. Because MM_GetConfig + * ??? always sets pDevice->UseTaggedStatus correctly + * ??? the following test at tigon3.c:LM_GetAdapterInfo() + * ??? + * ??? pDevice->UseTaggedStatus && + * ??? (pDevice->ChipRevId == T3_CHIP_ID_5700_C0 || + * ??? T3_CHIP_REV(pDevice->ChipRevId) == T3_CHIP_REV_5700_AX || + * ??? T3_CHIP_REV(pDevice->ChipRevId) == T3_CHIP_REV_5700_BX) + * ??? + * ??? will never pass and thus pDevice->CoalesceMode will never + * ??? get set to zero. For now I'll mirror what I believe is + * ??? the intention of their driver. + * ??? + * ??? Update: This is fixed in Broadcom's 2.2.3 and later + * ??? drivers. All the current 2.0.x drivers still + * ??? have the bug. + */ + tp->coalesce_mode = (HOSTCC_MODE_CLRTICK_RXBD | + HOSTCC_MODE_CLRTICK_TXBD); + } else { + tp->coalesce_mode = 0; + + /* If not using tagged status, set the *_during_int + * coalesce default config values to zero. + */ + tp->coalesce_config.rx_coalesce_ticks_during_int = 0; + tp->coalesce_config.rx_max_coalesced_frames_during_int = 0; + tp->coalesce_config.tx_coalesce_ticks_during_int = 0; + tp->coalesce_config.tx_max_coalesced_frames_during_int = 0; + } + + if (GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5700_AX && + GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5700_BX) + tp->coalesce_mode |= HOSTCC_MODE_32BYTE; + + /* Initialize misc host control in PCI block. */ + tp->misc_host_ctrl |= (misc_ctrl_reg & + MISC_HOST_CTRL_CHIPREV); + pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, + tp->misc_host_ctrl); + + /* Initialize MAC MI mode, polling disabled. */ + tw32(MAC_MI_MODE, tp->mi_mode); + udelay(40); + + /* Initialize data/descriptor byte/word swapping. */ + tw32(GRC_MODE, tp->grc_mode); + + /* Clear these out for sanity. */ + tw32(TG3PCI_CLOCK_CTRL, 0); + tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0); + + pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE, + &pci_state_reg); + if ((pci_state_reg & PCISTATE_CONV_PCI_MODE) == 0 && + (tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) == 0) { + u32 chiprevid = GET_CHIP_REV_ID(tp->misc_host_ctrl); + + if (chiprevid == CHIPREV_ID_5701_A0 || + chiprevid == CHIPREV_ID_5701_B0 || + chiprevid == CHIPREV_ID_5701_B2 || + chiprevid == CHIPREV_ID_5701_B5) { + unsigned long sram_base; + + /* Write some dummy words into the SRAM status block + * area, see if it reads back correctly. If the return + * value is bad, force enable the PCIX workaround. + */ + sram_base = tp->regs + NIC_SRAM_WIN_BASE + NIC_SRAM_STATS_BLK; + + writel(0x00000000, sram_base); + writel(0x00000000, sram_base + 4); + writel(0xffffffff, sram_base + 4); + if (readl(sram_base) != 0x00000000) + tp->tg3_flags |= TG3_FLAG_PCIX_TARGET_HWBUG; + } + } + + udelay(50); + tg3_nvram_init(tp); + + /* Determine if TX descriptors will reside in + * main memory or in the chip SRAM. + */ + if (tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) + tp->tg3_flags |= TG3_FLAG_HOST_TXDS; + + /* Quick sanity check. Make sure we see an expected + * value here. + */ + grc_misc_cfg = tr32(GRC_MISC_CFG); + grc_misc_cfg &= GRC_MISC_CFG_BOARD_ID_MASK; + if (grc_misc_cfg != GRC_MISC_CFG_BOARD_ID_5700 && + grc_misc_cfg != GRC_MISC_CFG_BOARD_ID_5701 && + grc_misc_cfg != GRC_MISC_CFG_BOARD_ID_5702FE && + grc_misc_cfg != GRC_MISC_CFG_BOARD_ID_5703 && + grc_misc_cfg != GRC_MISC_CFG_BOARD_ID_5703S) + return -ENODEV; + + /* ROFL, you should see Broadcom's driver code implementing + * this, stuff like "if (a || b)" where a and b are always + * mutually exclusive. DaveM finds like 6 bugs today, hello! + */ + if (grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5702FE) + tp->tg3_flags |= TG3_FLAG_10_100_ONLY; + + err = tg3_phy_probe(tp); + + tg3_read_partno(tp); + + if (tp->phy_id == PHY_ID_SERDES) { + tp->tg3_flags &= ~TG3_FLAG_USE_MI_INTERRUPT; + + /* And override led_mode in case Dell ever makes + * a fibre board. + */ + tp->led_mode = led_mode_three_link; + } else { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) + tp->tg3_flags |= TG3_FLAG_USE_MI_INTERRUPT; + else + tp->tg3_flags &= ~TG3_FLAG_USE_MI_INTERRUPT; + } + + /* 5700 {AX,BX} chips have a broken status block link + * change bit implementation, so we must use the + * status register in those cases. + */ + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) + tp->tg3_flags |= TG3_FLAG_USE_LINKCHG_REG; + else + tp->tg3_flags &= ~TG3_FLAG_USE_LINKCHG_REG; + + /* The led_mode is set during tg3_phy_probe, here we might + * have to force the link status polling mechanism based + * upon subsystem IDs. + */ + if (tp->pdev->subsystem_vendor == PCI_VENDOR_ID_DELL && + tp->phy_id != PHY_ID_SERDES) { + tp->tg3_flags |= (TG3_FLAG_USE_MI_INTERRUPT | + TG3_FLAG_USE_LINKCHG_REG); + } + + /* 5700 BX chips need to have their TX producer index mailboxes + * written twice to workaround a bug. + */ + if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5700_BX) + tp->tg3_flags |= TG3_FLAG_TXD_MBOX_HWBUG; + else + tp->tg3_flags &= ~TG3_FLAG_TXD_MBOX_HWBUG; + + /* 5700 chips can get confused if TX buffers straddle the + * 4GB address boundary in some cases. + */ + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) { + /* ROFL! Latest Broadcom driver disables NETIF_F_HIGHDMA + * in this case instead of fixing their workaround code. + * + * Like, hey, there is this skb_copy() thing guys, + * use it. Oh I can't stop laughing... + */ + tp->dev->hard_start_xmit = tg3_start_xmit_4gbug; + } else { + tp->dev->hard_start_xmit = tg3_start_xmit; + } + + tp->rx_offset = 2; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701 && + (tp->tg3_flags & TG3_FLAG_PCIX_MODE) != 0) + tp->rx_offset = 0; + + return err; +} + +static int __devinit tg3_get_device_address(struct tg3 *tp) +{ + struct net_device *dev = tp->dev; + u32 hi, lo; + + /* First try to get it from MAC address mailbox. */ + tg3_read_mem(tp, NIC_SRAM_MAC_ADDR_HIGH_MBOX, &hi); + if ((hi >> 16) == 0x484b) { + dev->dev_addr[0] = (hi >> 8) & 0xff; + dev->dev_addr[1] = (hi >> 0) & 0xff; + + tg3_read_mem(tp, NIC_SRAM_MAC_ADDR_LOW_MBOX, &lo); + dev->dev_addr[2] = (lo >> 24) & 0xff; + dev->dev_addr[3] = (lo >> 16) & 0xff; + dev->dev_addr[4] = (lo >> 8) & 0xff; + dev->dev_addr[5] = (lo >> 0) & 0xff; + } + /* Next, try NVRAM. */ + else if (!tg3_nvram_read(tp, 0x7c, &hi) && + !tg3_nvram_read(tp, 0x80, &lo)) { + dev->dev_addr[0] = ((hi >> 16) & 0xff); + dev->dev_addr[1] = ((hi >> 24) & 0xff); + dev->dev_addr[2] = ((lo >> 0) & 0xff); + dev->dev_addr[3] = ((lo >> 8) & 0xff); + dev->dev_addr[4] = ((lo >> 16) & 0xff); + dev->dev_addr[5] = ((lo >> 24) & 0xff); + } + /* Finally just fetch it out of the MAC control regs. */ + else { + hi = tr32(MAC_ADDR_0_HIGH); + lo = tr32(MAC_ADDR_0_LOW); + + dev->dev_addr[5] = lo & 0xff; + dev->dev_addr[4] = (lo >> 8) & 0xff; + dev->dev_addr[3] = (lo >> 16) & 0xff; + dev->dev_addr[2] = (lo >> 24) & 0xff; + dev->dev_addr[1] = hi & 0xff; + dev->dev_addr[0] = (hi >> 8) & 0xff; + } + + if (!is_valid_ether_addr(&dev->dev_addr[0])) + return -EINVAL; + + return 0; +} + +static int __devinit tg3_do_test_dma(struct tg3 *tp, u32 *buf, dma_addr_t buf_dma, int size, int to_device) +{ + struct tg3_internal_buffer_desc test_desc; + u32 sram_dma_descs; + int i, ret; + + sram_dma_descs = NIC_SRAM_DMA_DESC_POOL_BASE; + + tw32(FTQ_RCVBD_COMP_FIFO_ENQDEQ, 0); + tw32(FTQ_RCVDATA_COMP_FIFO_ENQDEQ, 0); + tw32(RDMAC_STATUS, 0); + tw32(WDMAC_STATUS, 0); + + tw32(BUFMGR_MODE, 0); + tw32(FTQ_RESET, 0); + + /* pci_alloc_consistent gives only non-DAC addresses */ + test_desc.addr_hi = 0; + test_desc.addr_lo = buf_dma & 0xffffffff; + test_desc.nic_mbuf = 0x00002100; + test_desc.len = size; + if (to_device) { + test_desc.cqid_sqid = (13 << 8) | 2; + tw32(RDMAC_MODE, RDMAC_MODE_RESET); + tw32(RDMAC_MODE, RDMAC_MODE_ENABLE); + } else { + test_desc.cqid_sqid = (16 << 8) | 7; + tw32(WDMAC_MODE, WDMAC_MODE_RESET); + tw32(WDMAC_MODE, WDMAC_MODE_ENABLE); + } + test_desc.flags = 0x00000004; + + for (i = 0; i < (sizeof(test_desc) / sizeof(u32)); i++) { + u32 val; + + val = *(((u32 *)&test_desc) + i); + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, + sram_dma_descs + (i * sizeof(u32))); + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val); + } + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0); + + if (to_device) { + tw32(FTQ_DMA_HIGH_READ_FIFO_ENQDEQ, sram_dma_descs); + } else { + tw32(FTQ_DMA_HIGH_WRITE_FIFO_ENQDEQ, sram_dma_descs); + } + + ret = -ENODEV; + for (i = 0; i < 40; i++) { + u32 val; + + if (to_device) + val = tr32(FTQ_RCVBD_COMP_FIFO_ENQDEQ); + else + val = tr32(FTQ_RCVDATA_COMP_FIFO_ENQDEQ); + if ((val & 0xffff) == sram_dma_descs) { + ret = 0; + break; + } + + udelay(100); + } + + return ret; +} + +#define TEST_BUFFER_SIZE 0x400 + +static int __devinit tg3_test_dma(struct tg3 *tp) +{ + dma_addr_t buf_dma; + u32 *buf; + int ret; + + buf = pci_alloc_consistent(tp->pdev, TEST_BUFFER_SIZE, &buf_dma); + if (!buf) { + ret = -ENOMEM; + goto out_nofree; + } + + tw32(TG3PCI_CLOCK_CTRL, 0); + + if ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) == 0) { + tp->dma_rwctrl = + (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) | + (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) | + (0x7 << DMA_RWCTRL_WRITE_WATER_SHIFT) | + (0x7 << DMA_RWCTRL_READ_WATER_SHIFT) | + (0x0f << DMA_RWCTRL_MIN_DMA_SHIFT); + } else { + tp->dma_rwctrl = + (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) | + (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) | + (0x3 << DMA_RWCTRL_WRITE_WATER_SHIFT) | + (0x3 << DMA_RWCTRL_READ_WATER_SHIFT) | + (0x0f << DMA_RWCTRL_MIN_DMA_SHIFT); + + /* Wheee, some more chip bugs... */ + if (tp->pci_chip_rev_id == CHIPREV_ID_5703_A1 || + tp->pci_chip_rev_id == CHIPREV_ID_5703_A2) + tp->dma_rwctrl |= DMA_RWCTRL_ONE_DMA; + } + + /* We don't do this on x86 because it seems to hurt performace. + * It does help things on other platforms though. + */ +#ifndef CONFIG_X86 + { + u8 byte; + int cacheline_size; + pci_read_config_byte(tp->pdev, PCI_CACHE_LINE_SIZE, &byte); + + if (byte == 0) + cacheline_size = 1024; + else + cacheline_size = (int) byte * 4; + + tp->dma_rwctrl &= ~(DMA_RWCTRL_READ_BNDRY_MASK | + DMA_RWCTRL_WRITE_BNDRY_MASK); + + switch (cacheline_size) { + case 16: + tp->dma_rwctrl |= + (DMA_RWCTRL_READ_BNDRY_16 | + DMA_RWCTRL_WRITE_BNDRY_16); + break; + + case 32: + tp->dma_rwctrl |= + (DMA_RWCTRL_READ_BNDRY_32 | + DMA_RWCTRL_WRITE_BNDRY_32); + break; + + case 64: + tp->dma_rwctrl |= + (DMA_RWCTRL_READ_BNDRY_64 | + DMA_RWCTRL_WRITE_BNDRY_64); + break; + + case 128: + tp->dma_rwctrl |= + (DMA_RWCTRL_READ_BNDRY_128 | + DMA_RWCTRL_WRITE_BNDRY_128); + break; + + case 256: + tp->dma_rwctrl |= + (DMA_RWCTRL_READ_BNDRY_256 | + DMA_RWCTRL_WRITE_BNDRY_256); + break; + + case 512: + tp->dma_rwctrl |= + (DMA_RWCTRL_READ_BNDRY_512 | + DMA_RWCTRL_WRITE_BNDRY_512); + break; + + case 1024: + tp->dma_rwctrl |= + (DMA_RWCTRL_READ_BNDRY_1024 | + DMA_RWCTRL_WRITE_BNDRY_1024); + break; + }; + } +#endif + + /* Remove this if it causes problems for some boards. */ + tp->dma_rwctrl |= DMA_RWCTRL_USE_MEM_READ_MULT; + + tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); + + ret = 0; + while (1) { + u32 *p, i; + + p = buf; + for (i = 0; i < TEST_BUFFER_SIZE / sizeof(u32); i++) + p[i] = i; + + /* Send the buffer to the chip. */ + ret = tg3_do_test_dma(tp, buf, buf_dma, TEST_BUFFER_SIZE, 1); + if (ret) + break; + + p = buf; + for (i = 0; i < TEST_BUFFER_SIZE / sizeof(u32); i++) + p[i] = 0; + + /* Now read it back. */ + ret = tg3_do_test_dma(tp, buf, buf_dma, TEST_BUFFER_SIZE, 0); + if (ret) + break; + + /* Verify it. */ + p = buf; + for (i = 0; i < TEST_BUFFER_SIZE / sizeof(u32); i++) { + if (p[i] == i) + continue; + + if ((tp->dma_rwctrl & DMA_RWCTRL_WRITE_BNDRY_MASK) == + DMA_RWCTRL_WRITE_BNDRY_DISAB) { + tp->dma_rwctrl |= DMA_RWCTRL_WRITE_BNDRY_16; + tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); + break; + } else { + ret = -ENODEV; + goto out; + } + } + + if (i == (TEST_BUFFER_SIZE / sizeof(u32))) { + /* Success. */ + ret = 0; + break; + } + } + +out: + pci_free_consistent(tp->pdev, TEST_BUFFER_SIZE, buf, buf_dma); +out_nofree: + return ret; +} + +static void __devinit tg3_init_link_config(struct tg3 *tp) +{ + tp->link_config.advertising = + (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | + ADVERTISED_Autoneg | ADVERTISED_MII); + tp->link_config.speed = SPEED_INVALID; + tp->link_config.duplex = DUPLEX_INVALID; + tp->link_config.autoneg = AUTONEG_ENABLE; + netif_carrier_off(tp->dev); + tp->link_config.active_speed = SPEED_INVALID; + tp->link_config.active_duplex = DUPLEX_INVALID; + tp->link_config.phy_is_low_power = 0; + tp->link_config.orig_speed = SPEED_INVALID; + tp->link_config.orig_duplex = DUPLEX_INVALID; + tp->link_config.orig_autoneg = AUTONEG_INVALID; +} + +static void __devinit tg3_init_coalesce_config(struct tg3 *tp) +{ + tp->coalesce_config.rx_coalesce_ticks = DEFAULT_RXCOL_TICKS; + tp->coalesce_config.rx_max_coalesced_frames = DEFAULT_RXMAX_FRAMES; + tp->coalesce_config.rx_coalesce_ticks_during_int = + DEFAULT_RXCOAL_TICK_INT; + tp->coalesce_config.rx_max_coalesced_frames_during_int = + DEFAULT_RXCOAL_MAXF_INT; + tp->coalesce_config.tx_coalesce_ticks = DEFAULT_TXCOL_TICKS; + tp->coalesce_config.tx_max_coalesced_frames = DEFAULT_TXMAX_FRAMES; + tp->coalesce_config.tx_coalesce_ticks_during_int = + DEFAULT_TXCOAL_TICK_INT; + tp->coalesce_config.tx_max_coalesced_frames_during_int = + DEFAULT_TXCOAL_MAXF_INT; + tp->coalesce_config.stats_coalesce_ticks = + DEFAULT_STAT_COAL_TICKS; +} + +static void __devinit tg3_init_bufmgr_config(struct tg3 *tp) +{ + tp->bufmgr_config.mbuf_read_dma_low_water = + DEFAULT_MB_RDMA_LOW_WATER; + tp->bufmgr_config.mbuf_mac_rx_low_water = + DEFAULT_MB_MACRX_LOW_WATER; + tp->bufmgr_config.mbuf_high_water = + DEFAULT_MB_HIGH_WATER; + + tp->bufmgr_config.mbuf_read_dma_low_water_jumbo = + DEFAULT_MB_RDMA_LOW_WATER_JUMBO; + tp->bufmgr_config.mbuf_mac_rx_low_water_jumbo = + DEFAULT_MB_MACRX_LOW_WATER_JUMBO; + tp->bufmgr_config.mbuf_high_water_jumbo = + DEFAULT_MB_HIGH_WATER_JUMBO; + + tp->bufmgr_config.dma_low_water = DEFAULT_DMA_LOW_WATER; + tp->bufmgr_config.dma_high_water = DEFAULT_DMA_HIGH_WATER; +} + +static char * __devinit tg3_phy_string(struct tg3 *tp) +{ + switch (tp->phy_id & PHY_ID_MASK) { + case PHY_ID_BCM5400: return "5400"; + case PHY_ID_BCM5401: return "5401"; + case PHY_ID_BCM5411: return "5411"; + case PHY_ID_BCM5701: return "5701"; + case PHY_ID_BCM5703: return "5703"; + case PHY_ID_BCM8002: return "8002"; + case PHY_ID_SERDES: return "serdes"; + default: return "unknown"; + }; +} + +static int __devinit tg3_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + static int tg3_version_printed = 0; + unsigned long tg3reg_base, tg3reg_len; + struct net_device *dev; + struct tg3 *tp; + int i, err, pci_using_dac, pm_cap; + + if (tg3_version_printed++ == 0) + printk(KERN_INFO "%s", version); + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR PFX "Cannot enable PCI device, " + "aborting.\n"); + return err; + } + + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { + printk(KERN_ERR PFX "Cannot find proper PCI device " + "base address, aborting.\n"); + err = -ENODEV; + goto err_out_disable_pdev; + } + + err = pci_request_regions(pdev, DRV_MODULE_NAME); + if (err) { + printk(KERN_ERR PFX "Cannot obtain PCI resources, " + "aborting.\n"); + goto err_out_disable_pdev; + } + + pci_set_master(pdev); + + /* Find power-management capability. */ + pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (pm_cap == 0) { + printk(KERN_ERR PFX "Cannot find PowerManagement capability, " + "aborting.\n"); + goto err_out_free_res; + } + + /* Configure DMA attributes. */ + if (!pci_set_dma_mask(pdev, (u64) 0xffffffffffffffff)) { + pci_using_dac = 1; + } else { + err = pci_set_dma_mask(pdev, (u64) 0xffffffff); + if (err) { + printk(KERN_ERR PFX "No usable DMA configuration, " + "aborting.\n"); + goto err_out_free_res; + } + pci_using_dac = 0; + } + + tg3reg_base = pci_resource_start(pdev, 0); + tg3reg_len = pci_resource_len(pdev, 0); + + dev = alloc_etherdev(sizeof(*tp)); + if (!dev) { + printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); + err = -ENOMEM; + goto err_out_free_res; + } + + SET_MODULE_OWNER(dev); + + if (pci_using_dac) + dev->features |= NETIF_F_HIGHDMA; +#if TG3_VLAN_TAG_USED + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + dev->vlan_rx_register = tg3_vlan_rx_register; + dev->vlan_rx_kill_vid = tg3_vlan_rx_kill_vid; +#endif + + tp = dev->priv; + tp->pdev = pdev; + tp->dev = dev; + tp->pm_cap = pm_cap; + tp->mac_mode = TG3_DEF_MAC_MODE; + tp->rx_mode = TG3_DEF_RX_MODE; + tp->tx_mode = TG3_DEF_TX_MODE; + tp->mi_mode = MAC_MI_MODE_BASE; + if (tg3_debug > 0) + tp->msg_enable = tg3_debug; + else + tp->msg_enable = TG3_DEF_MSG_ENABLE; + + /* The word/byte swap controls here control register access byte + * swapping. DMA data byte swapping is controlled in the GRC_MODE + * setting below. + */ + tp->misc_host_ctrl = + MISC_HOST_CTRL_MASK_PCI_INT | + MISC_HOST_CTRL_WORD_SWAP | + MISC_HOST_CTRL_INDIR_ACCESS | + MISC_HOST_CTRL_PCISTATE_RW; + + /* The NONFRM (non-frame) byte/word swap controls take effect + * on descriptor entries, anything which isn't packet data. + * + * The StrongARM chips on the board (one for tx, one for rx) + * are running in big-endian mode. + */ + tp->grc_mode = (GRC_MODE_WSWAP_DATA | GRC_MODE_BSWAP_DATA | + GRC_MODE_WSWAP_NONFRM_DATA); +#ifdef __BIG_ENDIAN + tp->grc_mode |= GRC_MODE_BSWAP_NONFRM_DATA; +#endif + spin_lock_init(&tp->lock); + spin_lock_init(&tp->indirect_lock); + + tp->regs = (unsigned long) ioremap(tg3reg_base, tg3reg_len); + if (tp->regs == 0UL) { + printk(KERN_ERR PFX "Cannot map device registers, " + "aborting.\n"); + err = -ENOMEM; + goto err_out_free_dev; + } + + tg3_init_link_config(tp); + + tg3_init_coalesce_config(tp); + + tg3_init_bufmgr_config(tp); + + dev->open = tg3_open; + dev->stop = tg3_close; + dev->get_stats = tg3_get_stats; + dev->set_multicast_list = tg3_set_rx_mode; + dev->set_mac_address = tg3_set_mac_addr; + dev->do_ioctl = tg3_ioctl; + dev->tx_timeout = tg3_tx_timeout; + dev->watchdog_timeo = TG3_TX_TIMEOUT; + dev->change_mtu = tg3_change_mtu; + dev->irq = pdev->irq; + + err = tg3_get_invariants(tp); + if (err) { + printk(KERN_ERR PFX "Problem fetching invariants of chip, " + "aborting.\n"); + goto err_out_iounmap; + } + + err = tg3_get_device_address(tp); + if (err) { + printk(KERN_ERR PFX "Could not obtain valid ethernet address, " + "aborting.\n"); + goto err_out_iounmap; + } + + err = tg3_test_dma(tp); + if (err) { + printk(KERN_ERR PFX "DMA engine test failed, aborting.\n"); + goto err_out_iounmap; + } + + /* Tigon3 can do ipv4 only... and some chips have buggy + * checksumming. + */ + if ((tp->tg3_flags & TG3_FLAG_BROKEN_CHECKSUMS) == 0) + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM; + + err = register_netdev(dev); + if (err) { + printk(KERN_ERR PFX "Cannot register net device, " + "aborting.\n"); + goto err_out_iounmap; + } + + pci_set_drvdata(pdev, dev); + + /* Now that we have fully setup the chip, save away a snapshot + * of the PCI config space. We need to restore this after + * GRC_MISC_CFG core clock resets and some resume events. + */ + pci_save_state(tp->pdev, tp->pci_cfg_state); + + printk(KERN_INFO "%s: Tigon3 [partno(%s) rev %04x PHY(%s)] (PCI%s:%s:%s) %sBaseT Ethernet ", + dev->name, + tp->board_part_number, + tp->pci_chip_rev_id, + tg3_phy_string(tp), + ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "X" : ""), + ((tp->tg3_flags & TG3_FLAG_PCI_HIGH_SPEED) ? + ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "133MHz" : "66MHz") : + ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "100MHz" : "33MHz")), + ((tp->tg3_flags & TG3_FLAG_PCI_32BIT) ? "32-bit" : "64-bit"), + (tp->tg3_flags & TG3_FLAG_10_100_ONLY) ? "10/100" : "10/100/1000"); + + for (i = 0; i < 6; i++) + printk("%2.2x%c", dev->dev_addr[i], + i == 5 ? '\n' : ':'); + + return 0; + +err_out_iounmap: + iounmap((void *) tp->regs); + +err_out_free_dev: + kfree(dev); + +err_out_free_res: + pci_release_regions(pdev); + +err_out_disable_pdev: + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + return err; +} + +static void __devexit tg3_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (dev) { + unregister_netdev(dev); + iounmap((void *) ((struct tg3 *)(dev->priv))->regs); + kfree(dev); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + } +} + +static int tg3_suspend(struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct tg3 *tp = dev->priv; + int err; + + if (!netif_running(dev)) + return 0; + + spin_lock_irq(&tp->lock); + tg3_disable_ints(tp); + spin_unlock_irq(&tp->lock); + + netif_device_detach(dev); + + spin_lock_irq(&tp->lock); + tg3_halt(tp); + spin_unlock_irq(&tp->lock); + + err = tg3_set_power_state(tp, state); + if (err) { + spin_lock_irq(&tp->lock); + + tg3_init_rings(tp); + tg3_init_hw(tp); + + spin_unlock_irq(&tp->lock); + + netif_device_attach(dev); + } + + return err; +} + +static int tg3_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct tg3 *tp = dev->priv; + int err; + + if (!netif_running(dev)) + return 0; + + err = tg3_set_power_state(tp, 0); + if (err) + return err; + + netif_device_attach(dev); + + spin_lock_irq(&tp->lock); + + tg3_init_rings(tp); + tg3_init_hw(tp); + tg3_enable_ints(tp); + + spin_unlock_irq(&tp->lock); + + return 0; +} + +static struct pci_driver tg3_driver = { + name: DRV_MODULE_NAME, + id_table: tg3_pci_tbl, + probe: tg3_init_one, + remove: __devexit_p(tg3_remove_one), + suspend: tg3_suspend, + resume: tg3_resume +}; + +static int __init tg3_init(void) +{ + return pci_module_init(&tg3_driver); +} + +static void __exit tg3_cleanup(void) +{ + pci_unregister_driver(&tg3_driver); +} + +module_init(tg3_init); +module_exit(tg3_cleanup); diff -Nru a/drivers/net/tg3.h b/drivers/net/tg3.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/tg3.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,1851 @@ +/* $Id: tg3.h,v 1.37.2.30 2002/03/05 10:08:39 davem Exp $ + * tg3.h: Definitions for Broadcom Tigon3 ethernet driver. + * + * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) + * Copyright (C) 2001 Jeff Garzik (jgarzik@mandrakesoft.com) + */ + +#ifndef _T3_H +#define _T3_H + +#define TG3_64BIT_REG_HIGH 0x00UL +#define TG3_64BIT_REG_LOW 0x04UL + +/* Descriptor block info. */ +#define TG3_BDINFO_HOST_ADDR 0x0UL /* 64-bit */ +#define TG3_BDINFO_MAXLEN_FLAGS 0x8UL /* 32-bit */ +#define BDINFO_FLAGS_USE_EXT_RECV 0x00000001 /* ext rx_buffer_desc */ +#define BDINFO_FLAGS_DISABLED 0x00000002 +#define BDINFO_FLAGS_MAXLEN_MASK 0xffff0000 +#define BDINFO_FLAGS_MAXLEN_SHIFT 16 +#define TG3_BDINFO_NIC_ADDR 0xcUL /* 32-bit */ +#define TG3_BDINFO_SIZE 0x10UL + +#define RX_COPY_THRESHOLD 256 + +#define RX_STD_MAX_SIZE 1536 +#define RX_JUMBO_MAX_SIZE 0xdeadbeef /* XXX */ +#if TG3_MINI_RING_WORKS +#define RX_MINI_MAX_SIZE 256 +#endif + +/* First 256 bytes are a mirror of PCI config space. */ +#define TG3PCI_VENDOR 0x00000000 +#define TG3PCI_VENDOR_BROADCOM 0x14e4 +#define TG3PCI_DEVICE 0x00000002 +#define TG3PCI_DEVICE_TIGON3_1 0x1644 /* BCM5700 */ +#define TG3PCI_DEVICE_TIGON3_2 0x1645 /* BCM5701 */ +#define TG3PCI_DEVICE_TIGON3_3 0x1646 /* BCM5702 */ +#define TG3PCI_DEVICE_TIGON3_4 0x1647 /* BCM5703 */ +#define TG3PCI_COMMAND 0x00000004 +#define TG3PCI_STATUS 0x00000006 +#define TG3PCI_CCREVID 0x00000008 +#define TG3PCI_CACHELINESZ 0x0000000c +#define TG3PCI_LATTIMER 0x0000000d +#define TG3PCI_HEADERTYPE 0x0000000e +#define TG3PCI_BIST 0x0000000f +#define TG3PCI_BASE0_LOW 0x00000010 +#define TG3PCI_BASE0_HIGH 0x00000014 +/* 0x18 --> 0x2c unused */ +#define TG3PCI_SUBSYSVENID 0x0000002c +#define TG3PCI_SUBSYSID 0x0000002e +#define TG3PCI_ROMADDR 0x00000030 +#define TG3PCI_CAPLIST 0x00000034 +/* 0x35 --> 0x3c unused */ +#define TG3PCI_IRQ_LINE 0x0000003c +#define TG3PCI_IRQ_PIN 0x0000003d +#define TG3PCI_MIN_GNT 0x0000003e +#define TG3PCI_MAX_LAT 0x0000003f +#define TG3PCI_X_CAPS 0x00000040 +#define PCIX_CAPS_RELAXED_ORDERING 0x00020000 +#define TG3PCI_PM_CAP_PTR 0x00000041 +#define TG3PCI_X_COMMAND 0x00000042 +#define TG3PCI_X_STATUS 0x00000044 +#define TG3PCI_PM_CAP_ID 0x00000048 +#define TG3PCI_VPD_CAP_PTR 0x00000049 +#define TG3PCI_PM_CAPS 0x0000004a +#define TG3PCI_PM_CTRL_STAT 0x0000004c +#define TG3PCI_BR_SUPP_EXT 0x0000004e +#define TG3PCI_PM_DATA 0x0000004f +#define TG3PCI_VPD_CAP_ID 0x00000050 +#define TG3PCI_MSI_CAP_PTR 0x00000051 +#define TG3PCI_VPD_ADDR_FLAG 0x00000052 +#define VPD_ADDR_FLAG_WRITE 0x00008000 +#define TG3PCI_VPD_DATA 0x00000054 +#define TG3PCI_MSI_CAP_ID 0x00000058 +#define TG3PCI_NXT_CAP_PTR 0x00000059 +#define TG3PCI_MSI_CTRL 0x0000005a +#define TG3PCI_MSI_ADDR_LOW 0x0000005c +#define TG3PCI_MSI_ADDR_HIGH 0x00000060 +#define TG3PCI_MSI_DATA 0x00000064 +/* 0x66 --> 0x68 unused */ +#define TG3PCI_MISC_HOST_CTRL 0x00000068 +#define MISC_HOST_CTRL_CLEAR_INT 0x00000001 +#define MISC_HOST_CTRL_MASK_PCI_INT 0x00000002 +#define MISC_HOST_CTRL_BYTE_SWAP 0x00000004 +#define MISC_HOST_CTRL_WORD_SWAP 0x00000008 +#define MISC_HOST_CTRL_PCISTATE_RW 0x00000010 +#define MISC_HOST_CTRL_CLKREG_RW 0x00000020 +#define MISC_HOST_CTRL_REGWORD_SWAP 0x00000040 +#define MISC_HOST_CTRL_INDIR_ACCESS 0x00000080 +#define MISC_HOST_CTRL_IRQ_MASK_MODE 0x00000100 +#define MISC_HOST_CTRL_TAGGED_STATUS 0x00000200 +#define MISC_HOST_CTRL_CHIPREV 0xffff0000 +#define MISC_HOST_CTRL_CHIPREV_SHIFT 16 +#define GET_CHIP_REV_ID(MISC_HOST_CTRL) \ + (((MISC_HOST_CTRL) & MISC_HOST_CTRL_CHIPREV) >> \ + MISC_HOST_CTRL_CHIPREV_SHIFT) +#define CHIPREV_ID_5700_A0 0x7000 +#define CHIPREV_ID_5700_A1 0x7001 +#define CHIPREV_ID_5700_B0 0x7100 +#define CHIPREV_ID_5700_B1 0x7101 +#define CHIPREV_ID_5700_C0 0x7200 +#define CHIPREV_ID_5701_A0 0x0000 +#define CHIPREV_ID_5701_B0 0x0100 +#define CHIPREV_ID_5701_B2 0x0102 +#define CHIPREV_ID_5701_B5 0x0105 +#define CHIPREV_ID_5703_A0 0x1000 +#define CHIPREV_ID_5703_A1 0x1001 +#define CHIPREV_ID_5703_A2 0x1002 +#define GET_ASIC_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 12) +#define ASIC_REV_5700 0x07 +#define ASIC_REV_5701 0x00 +#define ASIC_REV_5703 0x01 +#define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8) +#define CHIPREV_5700_AX 0x70 +#define CHIPREV_5700_BX 0x71 +#define CHIPREV_5700_CX 0x72 +#define CHIPREV_5701_AX 0x00 +#define GET_METAL_REV(CHIP_REV_ID) ((CHIP_REV_ID) & 0xff) +#define METAL_REV_A0 0x00 +#define METAL_REV_A1 0x01 +#define METAL_REV_B0 0x00 +#define METAL_REV_B1 0x01 +#define METAL_REV_B2 0x02 +#define TG3PCI_DMA_RW_CTRL 0x0000006c +#define DMA_RWCTRL_MIN_DMA 0x000000ff +#define DMA_RWCTRL_MIN_DMA_SHIFT 0 +#define DMA_RWCTRL_READ_BNDRY_MASK 0x00000700 +#define DMA_RWCTRL_READ_BNDRY_DISAB 0x00000000 +#define DMA_RWCTRL_READ_BNDRY_16 0x00000100 +#define DMA_RWCTRL_READ_BNDRY_32 0x00000200 +#define DMA_RWCTRL_READ_BNDRY_64 0x00000300 +#define DMA_RWCTRL_READ_BNDRY_128 0x00000400 +#define DMA_RWCTRL_READ_BNDRY_256 0x00000500 +#define DMA_RWCTRL_READ_BNDRY_512 0x00000600 +#define DMA_RWCTRL_READ_BNDRY_1024 0x00000700 +#define DMA_RWCTRL_WRITE_BNDRY_MASK 0x00003800 +#define DMA_RWCTRL_WRITE_BNDRY_DISAB 0x00000000 +#define DMA_RWCTRL_WRITE_BNDRY_16 0x00000800 +#define DMA_RWCTRL_WRITE_BNDRY_32 0x00001000 +#define DMA_RWCTRL_WRITE_BNDRY_64 0x00001800 +#define DMA_RWCTRL_WRITE_BNDRY_128 0x00002000 +#define DMA_RWCTRL_WRITE_BNDRY_256 0x00002800 +#define DMA_RWCTRL_WRITE_BNDRY_512 0x00003000 +#define DMA_RWCTRL_WRITE_BNDRY_1024 0x00003800 +#define DMA_RWCTRL_ONE_DMA 0x00004000 +#define DMA_RWCTRL_READ_WATER 0x00070000 +#define DMA_RWCTRL_READ_WATER_SHIFT 16 +#define DMA_RWCTRL_WRITE_WATER 0x00380000 +#define DMA_RWCTRL_WRITE_WATER_SHIFT 19 +#define DMA_RWCTRL_USE_MEM_READ_MULT 0x00400000 +#define DMA_RWCTRL_ASSERT_ALL_BE 0x00800000 +#define DMA_RWCTRL_PCI_READ_CMD 0x0f000000 +#define DMA_RWCTRL_PCI_READ_CMD_SHIFT 24 +#define DMA_RWCTRL_PCI_WRITE_CMD 0xf0000000 +#define DMA_RWCTRL_PCI_WRITE_CMD_SHIFT 28 +#define TG3PCI_PCISTATE 0x00000070 +#define PCISTATE_FORCE_RESET 0x00000001 +#define PCISTATE_INT_NOT_ACTIVE 0x00000002 +#define PCISTATE_CONV_PCI_MODE 0x00000004 +#define PCISTATE_BUS_SPEED_HIGH 0x00000008 +#define PCISTATE_BUS_32BIT 0x00000010 +#define PCISTATE_ROM_ENABLE 0x00000020 +#define PCISTATE_ROM_RETRY_ENABLE 0x00000040 +#define PCISTATE_FLAT_VIEW 0x00000100 +#define TG3PCI_CLOCK_CTRL 0x00000074 +#define CLOCK_CTRL_CORECLK_DISABLE 0x00000200 +#define CLOCK_CTRL_RXCLK_DISABLE 0x00000400 +#define CLOCK_CTRL_TXCLK_DISABLE 0x00000800 +#define CLOCK_CTRL_ALTCLK 0x00001000 +#define CLOCK_CTRL_PWRDOWN_PLL133 0x00008000 +#define CLOCK_CTRL_44MHZ_CORE 0x00040000 +#define CLOCK_CTRL_DELAY_PCI_GRANT 0x80000000 +#define TG3PCI_REG_BASE_ADDR 0x00000078 +#define TG3PCI_MEM_WIN_BASE_ADDR 0x0000007c +#define TG3PCI_REG_DATA 0x00000080 +#define TG3PCI_MEM_WIN_DATA 0x00000084 +#define TG3PCI_MODE_CTRL 0x00000088 +#define TG3PCI_MISC_CFG 0x0000008c +#define TG3PCI_MISC_LOCAL_CTRL 0x00000090 +/* 0x94 --> 0x98 unused */ +#define TG3PCI_STD_RING_PROD_IDX 0x00000098 /* 64-bit */ +#define TG3PCI_RCV_RET_RING_CON_IDX 0x000000a0 /* 64-bit */ +#define TG3PCI_SND_PROD_IDX 0x000000a8 /* 64-bit */ +/* 0xb0 --> 0x100 unused */ + +/* 0x100 --> 0x200 unused */ + +/* Mailbox registers */ +#define MAILBOX_INTERRUPT_0 0x00000200 /* 64-bit */ +#define MAILBOX_INTERRUPT_1 0x00000208 /* 64-bit */ +#define MAILBOX_INTERRUPT_2 0x00000210 /* 64-bit */ +#define MAILBOX_INTERRUPT_3 0x00000218 /* 64-bit */ +#define MAILBOX_GENERAL_0 0x00000220 /* 64-bit */ +#define MAILBOX_GENERAL_1 0x00000228 /* 64-bit */ +#define MAILBOX_GENERAL_2 0x00000230 /* 64-bit */ +#define MAILBOX_GENERAL_3 0x00000238 /* 64-bit */ +#define MAILBOX_GENERAL_4 0x00000240 /* 64-bit */ +#define MAILBOX_GENERAL_5 0x00000248 /* 64-bit */ +#define MAILBOX_GENERAL_6 0x00000250 /* 64-bit */ +#define MAILBOX_GENERAL_7 0x00000258 /* 64-bit */ +#define MAILBOX_RELOAD_STAT 0x00000260 /* 64-bit */ +#define MAILBOX_RCV_STD_PROD_IDX 0x00000268 /* 64-bit */ +#define MAILBOX_RCV_JUMBO_PROD_IDX 0x00000270 /* 64-bit */ +#define MAILBOX_RCV_MINI_PROD_IDX 0x00000278 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_0 0x00000280 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_1 0x00000288 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_2 0x00000290 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_3 0x00000298 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_4 0x000002a0 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_5 0x000002a8 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_6 0x000002b0 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_7 0x000002b8 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_8 0x000002c0 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_9 0x000002c8 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_10 0x000002d0 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_11 0x000002d8 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_12 0x000002e0 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_13 0x000002e8 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_14 0x000002f0 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_15 0x000002f8 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_0 0x00000300 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_1 0x00000308 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_2 0x00000310 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_3 0x00000318 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_4 0x00000320 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_5 0x00000328 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_6 0x00000330 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_7 0x00000338 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_8 0x00000340 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_9 0x00000348 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_10 0x00000350 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_11 0x00000358 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_12 0x00000360 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_13 0x00000368 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_14 0x00000370 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_15 0x00000378 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_0 0x00000380 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_1 0x00000388 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_2 0x00000390 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_3 0x00000398 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_4 0x000003a0 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_5 0x000003a8 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_6 0x000003b0 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_7 0x000003b8 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_8 0x000003c0 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_9 0x000003c8 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_10 0x000003d0 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_11 0x000003d8 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_12 0x000003e0 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_13 0x000003e8 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_14 0x000003f0 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_15 0x000003f8 /* 64-bit */ + +/* MAC control registers */ +#define MAC_MODE 0x00000400 +#define MAC_MODE_RESET 0x00000001 +#define MAC_MODE_HALF_DUPLEX 0x00000002 +#define MAC_MODE_PORT_MODE_MASK 0x0000000c +#define MAC_MODE_PORT_MODE_TBI 0x0000000c +#define MAC_MODE_PORT_MODE_GMII 0x00000008 +#define MAC_MODE_PORT_MODE_MII 0x00000004 +#define MAC_MODE_PORT_MODE_NONE 0x00000000 +#define MAC_MODE_PORT_INT_LPBACK 0x00000010 +#define MAC_MODE_TAGGED_MAC_CTRL 0x00000080 +#define MAC_MODE_TX_BURSTING 0x00000100 +#define MAC_MODE_MAX_DEFER 0x00000200 +#define MAC_MODE_LINK_POLARITY 0x00000400 +#define MAC_MODE_RXSTAT_ENABLE 0x00000800 +#define MAC_MODE_RXSTAT_CLEAR 0x00001000 +#define MAC_MODE_RXSTAT_FLUSH 0x00002000 +#define MAC_MODE_TXSTAT_ENABLE 0x00004000 +#define MAC_MODE_TXSTAT_CLEAR 0x00008000 +#define MAC_MODE_TXSTAT_FLUSH 0x00010000 +#define MAC_MODE_SEND_CONFIGS 0x00020000 +#define MAC_MODE_MAGIC_PKT_ENABLE 0x00040000 +#define MAC_MODE_ACPI_ENABLE 0x00080000 +#define MAC_MODE_MIP_ENABLE 0x00100000 +#define MAC_MODE_TDE_ENABLE 0x00200000 +#define MAC_MODE_RDE_ENABLE 0x00400000 +#define MAC_MODE_FHDE_ENABLE 0x00800000 +#define MAC_STATUS 0x00000404 +#define MAC_STATUS_PCS_SYNCED 0x00000001 +#define MAC_STATUS_SIGNAL_DET 0x00000002 +#define MAC_STATUS_RCVD_CFG 0x00000004 +#define MAC_STATUS_CFG_CHANGED 0x00000008 +#define MAC_STATUS_SYNC_CHANGED 0x00000010 +#define MAC_STATUS_PORT_DEC_ERR 0x00000400 +#define MAC_STATUS_LNKSTATE_CHANGED 0x00001000 +#define MAC_STATUS_MI_COMPLETION 0x00400000 +#define MAC_STATUS_MI_INTERRUPT 0x00800000 +#define MAC_STATUS_AP_ERROR 0x01000000 +#define MAC_STATUS_ODI_ERROR 0x02000000 +#define MAC_STATUS_RXSTAT_OVERRUN 0x04000000 +#define MAC_STATUS_TXSTAT_OVERRUN 0x08000000 +#define MAC_EVENT 0x00000408 +#define MAC_EVENT_PORT_DECODE_ERR 0x00000400 +#define MAC_EVENT_LNKSTATE_CHANGED 0x00001000 +#define MAC_EVENT_MI_COMPLETION 0x00400000 +#define MAC_EVENT_MI_INTERRUPT 0x00800000 +#define MAC_EVENT_AP_ERROR 0x01000000 +#define MAC_EVENT_ODI_ERROR 0x02000000 +#define MAC_EVENT_RXSTAT_OVERRUN 0x04000000 +#define MAC_EVENT_TXSTAT_OVERRUN 0x08000000 +#define MAC_LED_CTRL 0x0000040c +#define LED_CTRL_LNKLED_OVERRIDE 0x00000001 +#define LED_CTRL_1000MBPS_ON 0x00000002 +#define LED_CTRL_100MBPS_ON 0x00000004 +#define LED_CTRL_10MBPS_ON 0x00000008 +#define LED_CTRL_TRAFFIC_OVERRIDE 0x00000010 +#define LED_CTRL_TRAFFIC_BLINK 0x00000020 +#define LED_CTRL_TRAFFIC_LED 0x00000040 +#define LED_CTRL_1000MBPS_STATUS 0x00000080 +#define LED_CTRL_100MBPS_STATUS 0x00000100 +#define LED_CTRL_10MBPS_STATUS 0x00000200 +#define LED_CTRL_TRAFFIC_STATUS 0x00000400 +#define LED_CTRL_MAC_MODE 0x00000000 +#define LED_CTRL_PHY_MODE_1 0x00000800 +#define LED_CTRL_PHY_MODE_2 0x00001000 +#define LED_CTRL_BLINK_RATE_MASK 0x7ff80000 +#define LED_CTRL_BLINK_RATE_SHIFT 19 +#define LED_CTRL_BLINK_PER_OVERRIDE 0x00080000 +#define LED_CTRL_BLINK_RATE_OVERRIDE 0x80000000 +#define MAC_ADDR_0_HIGH 0x00000410 /* upper 2 bytes */ +#define MAC_ADDR_0_LOW 0x00000414 /* lower 4 bytes */ +#define MAC_ADDR_1_HIGH 0x00000418 /* upper 2 bytes */ +#define MAC_ADDR_1_LOW 0x0000041c /* lower 4 bytes */ +#define MAC_ADDR_2_HIGH 0x00000420 /* upper 2 bytes */ +#define MAC_ADDR_2_LOW 0x00000424 /* lower 4 bytes */ +#define MAC_ADDR_3_HIGH 0x00000428 /* upper 2 bytes */ +#define MAC_ADDR_3_LOW 0x0000042c /* lower 4 bytes */ +#define MAC_ACPI_MBUF_PTR 0x00000430 +#define MAC_ACPI_LEN_OFFSET 0x00000434 +#define ACPI_LENOFF_LEN_MASK 0x0000ffff +#define ACPI_LENOFF_LEN_SHIFT 0 +#define ACPI_LENOFF_OFF_MASK 0x0fff0000 +#define ACPI_LENOFF_OFF_SHIFT 16 +#define MAC_TX_BACKOFF_SEED 0x00000438 +#define TX_BACKOFF_SEED_MASK 0x000003ff +#define MAC_RX_MTU_SIZE 0x0000043c +#define RX_MTU_SIZE_MASK 0x0000ffff +#define MAC_PCS_TEST 0x00000440 +#define PCS_TEST_PATTERN_MASK 0x000fffff +#define PCS_TEST_PATTERN_SHIFT 0 +#define PCS_TEST_ENABLE 0x00100000 +#define MAC_TX_AUTO_NEG 0x00000444 +#define TX_AUTO_NEG_MASK 0x0000ffff +#define TX_AUTO_NEG_SHIFT 0 +#define MAC_RX_AUTO_NEG 0x00000448 +#define RX_AUTO_NEG_MASK 0x0000ffff +#define RX_AUTO_NEG_SHIFT 0 +#define MAC_MI_COM 0x0000044c +#define MI_COM_CMD_MASK 0x0c000000 +#define MI_COM_CMD_WRITE 0x04000000 +#define MI_COM_CMD_READ 0x08000000 +#define MI_COM_READ_FAILED 0x10000000 +#define MI_COM_START 0x20000000 +#define MI_COM_BUSY 0x20000000 +#define MI_COM_PHY_ADDR_MASK 0x03e00000 +#define MI_COM_PHY_ADDR_SHIFT 21 +#define MI_COM_REG_ADDR_MASK 0x001f0000 +#define MI_COM_REG_ADDR_SHIFT 16 +#define MI_COM_DATA_MASK 0x0000ffff +#define MAC_MI_STAT 0x00000450 +#define MAC_MI_STAT_LNKSTAT_ATTN_ENAB 0x00000001 +#define MAC_MI_MODE 0x00000454 +#define MAC_MI_MODE_CLK_10MHZ 0x00000001 +#define MAC_MI_MODE_SHORT_PREAMBLE 0x00000002 +#define MAC_MI_MODE_AUTO_POLL 0x00000010 +#define MAC_MI_MODE_CORE_CLK_62MHZ 0x00008000 +#define MAC_MI_MODE_BASE 0x000c0000 /* XXX magic values XXX */ +#define MAC_AUTO_POLL_STATUS 0x00000458 +#define MAC_AUTO_POLL_ERROR 0x00000001 +#define MAC_TX_MODE 0x0000045c +#define TX_MODE_RESET 0x00000001 +#define TX_MODE_ENABLE 0x00000002 +#define TX_MODE_FLOW_CTRL_ENABLE 0x00000010 +#define TX_MODE_BIG_BCKOFF_ENABLE 0x00000020 +#define TX_MODE_LONG_PAUSE_ENABLE 0x00000040 +#define MAC_TX_STATUS 0x00000460 +#define TX_STATUS_XOFFED 0x00000001 +#define TX_STATUS_SENT_XOFF 0x00000002 +#define TX_STATUS_SENT_XON 0x00000004 +#define TX_STATUS_LINK_UP 0x00000008 +#define TX_STATUS_ODI_UNDERRUN 0x00000010 +#define TX_STATUS_ODI_OVERRUN 0x00000020 +#define MAC_TX_LENGTHS 0x00000464 +#define TX_LENGTHS_SLOT_TIME_MASK 0x000000ff +#define TX_LENGTHS_SLOT_TIME_SHIFT 0 +#define TX_LENGTHS_IPG_MASK 0x00000f00 +#define TX_LENGTHS_IPG_SHIFT 8 +#define TX_LENGTHS_IPG_CRS_MASK 0x00003000 +#define TX_LENGTHS_IPG_CRS_SHIFT 12 +#define MAC_RX_MODE 0x00000468 +#define RX_MODE_RESET 0x00000001 +#define RX_MODE_ENABLE 0x00000002 +#define RX_MODE_FLOW_CTRL_ENABLE 0x00000004 +#define RX_MODE_KEEP_MAC_CTRL 0x00000008 +#define RX_MODE_KEEP_PAUSE 0x00000010 +#define RX_MODE_ACCEPT_OVERSIZED 0x00000020 +#define RX_MODE_ACCEPT_RUNTS 0x00000040 +#define RX_MODE_LEN_CHECK 0x00000080 +#define RX_MODE_PROMISC 0x00000100 +#define RX_MODE_NO_CRC_CHECK 0x00000200 +#define RX_MODE_KEEP_VLAN_TAG 0x00000400 +#define MAC_RX_STATUS 0x0000046c +#define RX_STATUS_REMOTE_TX_XOFFED 0x00000001 +#define RX_STATUS_XOFF_RCVD 0x00000002 +#define RX_STATUS_XON_RCVD 0x00000004 +#define MAC_HASH_REG_0 0x00000470 +#define MAC_HASH_REG_1 0x00000474 +#define MAC_HASH_REG_2 0x00000478 +#define MAC_HASH_REG_3 0x0000047c +#define MAC_RCV_RULE_0 0x00000480 +#define MAC_RCV_VALUE_0 0x00000484 +#define MAC_RCV_RULE_1 0x00000488 +#define MAC_RCV_VALUE_1 0x0000048c +#define MAC_RCV_RULE_2 0x00000490 +#define MAC_RCV_VALUE_2 0x00000494 +#define MAC_RCV_RULE_3 0x00000498 +#define MAC_RCV_VALUE_3 0x0000049c +#define MAC_RCV_RULE_4 0x000004a0 +#define MAC_RCV_VALUE_4 0x000004a4 +#define MAC_RCV_RULE_5 0x000004a8 +#define MAC_RCV_VALUE_5 0x000004ac +#define MAC_RCV_RULE_6 0x000004b0 +#define MAC_RCV_VALUE_6 0x000004b4 +#define MAC_RCV_RULE_7 0x000004b8 +#define MAC_RCV_VALUE_7 0x000004bc +#define MAC_RCV_RULE_8 0x000004c0 +#define MAC_RCV_VALUE_8 0x000004c4 +#define MAC_RCV_RULE_9 0x000004c8 +#define MAC_RCV_VALUE_9 0x000004cc +#define MAC_RCV_RULE_10 0x000004d0 +#define MAC_RCV_VALUE_10 0x000004d4 +#define MAC_RCV_RULE_11 0x000004d8 +#define MAC_RCV_VALUE_11 0x000004dc +#define MAC_RCV_RULE_12 0x000004e0 +#define MAC_RCV_VALUE_12 0x000004e4 +#define MAC_RCV_RULE_13 0x000004e8 +#define MAC_RCV_VALUE_13 0x000004ec +#define MAC_RCV_RULE_14 0x000004f0 +#define MAC_RCV_VALUE_14 0x000004f4 +#define MAC_RCV_RULE_15 0x000004f8 +#define MAC_RCV_VALUE_15 0x000004fc +#define RCV_RULE_DISABLE_MASK 0x7fffffff +#define MAC_RCV_RULE_CFG 0x00000500 +#define RCV_RULE_CFG_DEFAULT_CLASS 0x00000008 +/* 0x504 --> 0x590 unused */ +#define MAC_SERDES_CFG 0x00000590 +#define MAC_SERDES_STAT 0x00000594 +/* 0x598 --> 0x600 unused */ +#define MAC_TX_MAC_STATE_BASE 0x00000600 /* 16 bytes */ +#define MAC_RX_MAC_STATE_BASE 0x00000610 /* 20 bytes */ +/* 0x624 --> 0x800 unused */ +#define MAC_RX_STATS_BASE 0x00000800 /* 26 32-bit words */ +/* 0x868 --> 0x880 unused */ +#define MAC_TX_STATS_BASE 0x00000880 /* 28 32-bit words */ +/* 0x8f0 --> 0xc00 unused */ + +/* Send data initiator control registers */ +#define SNDDATAI_MODE 0x00000c00 +#define SNDDATAI_MODE_RESET 0x00000001 +#define SNDDATAI_MODE_ENABLE 0x00000002 +#define SNDDATAI_MODE_STAT_OFLOW_ENAB 0x00000004 +#define SNDDATAI_STATUS 0x00000c04 +#define SNDDATAI_STATUS_STAT_OFLOW 0x00000004 +#define SNDDATAI_STATSCTRL 0x00000c08 +#define SNDDATAI_SCTRL_ENABLE 0x00000001 +#define SNDDATAI_SCTRL_FASTUPD 0x00000002 +#define SNDDATAI_SCTRL_CLEAR 0x00000004 +#define SNDDATAI_SCTRL_FLUSH 0x00000008 +#define SNDDATAI_SCTRL_FORCE_ZERO 0x00000010 +#define SNDDATAI_STATSENAB 0x00000c0c +#define SNDDATAI_STATSINCMASK 0x00000c10 +/* 0xc14 --> 0xc80 unused */ +#define SNDDATAI_COS_CNT_0 0x00000c80 +#define SNDDATAI_COS_CNT_1 0x00000c84 +#define SNDDATAI_COS_CNT_2 0x00000c88 +#define SNDDATAI_COS_CNT_3 0x00000c8c +#define SNDDATAI_COS_CNT_4 0x00000c90 +#define SNDDATAI_COS_CNT_5 0x00000c94 +#define SNDDATAI_COS_CNT_6 0x00000c98 +#define SNDDATAI_COS_CNT_7 0x00000c9c +#define SNDDATAI_COS_CNT_8 0x00000ca0 +#define SNDDATAI_COS_CNT_9 0x00000ca4 +#define SNDDATAI_COS_CNT_10 0x00000ca8 +#define SNDDATAI_COS_CNT_11 0x00000cac +#define SNDDATAI_COS_CNT_12 0x00000cb0 +#define SNDDATAI_COS_CNT_13 0x00000cb4 +#define SNDDATAI_COS_CNT_14 0x00000cb8 +#define SNDDATAI_COS_CNT_15 0x00000cbc +#define SNDDATAI_DMA_RDQ_FULL_CNT 0x00000cc0 +#define SNDDATAI_DMA_PRIO_RDQ_FULL_CNT 0x00000cc4 +#define SNDDATAI_SDCQ_FULL_CNT 0x00000cc8 +#define SNDDATAI_NICRNG_SSND_PIDX_CNT 0x00000ccc +#define SNDDATAI_STATS_UPDATED_CNT 0x00000cd0 +#define SNDDATAI_INTERRUPTS_CNT 0x00000cd4 +#define SNDDATAI_AVOID_INTERRUPTS_CNT 0x00000cd8 +#define SNDDATAI_SND_THRESH_HIT_CNT 0x00000cdc +/* 0xce0 --> 0x1000 unused */ + +/* Send data completion control registers */ +#define SNDDATAC_MODE 0x00001000 +#define SNDDATAC_MODE_RESET 0x00000001 +#define SNDDATAC_MODE_ENABLE 0x00000002 +/* 0x1004 --> 0x1400 unused */ + +/* Send BD ring selector */ +#define SNDBDS_MODE 0x00001400 +#define SNDBDS_MODE_RESET 0x00000001 +#define SNDBDS_MODE_ENABLE 0x00000002 +#define SNDBDS_MODE_ATTN_ENABLE 0x00000004 +#define SNDBDS_STATUS 0x00001404 +#define SNDBDS_STATUS_ERROR_ATTN 0x00000004 +#define SNDBDS_HWDIAG 0x00001408 +/* 0x140c --> 0x1440 */ +#define SNDBDS_SEL_CON_IDX_0 0x00001440 +#define SNDBDS_SEL_CON_IDX_1 0x00001444 +#define SNDBDS_SEL_CON_IDX_2 0x00001448 +#define SNDBDS_SEL_CON_IDX_3 0x0000144c +#define SNDBDS_SEL_CON_IDX_4 0x00001450 +#define SNDBDS_SEL_CON_IDX_5 0x00001454 +#define SNDBDS_SEL_CON_IDX_6 0x00001458 +#define SNDBDS_SEL_CON_IDX_7 0x0000145c +#define SNDBDS_SEL_CON_IDX_8 0x00001460 +#define SNDBDS_SEL_CON_IDX_9 0x00001464 +#define SNDBDS_SEL_CON_IDX_10 0x00001468 +#define SNDBDS_SEL_CON_IDX_11 0x0000146c +#define SNDBDS_SEL_CON_IDX_12 0x00001470 +#define SNDBDS_SEL_CON_IDX_13 0x00001474 +#define SNDBDS_SEL_CON_IDX_14 0x00001478 +#define SNDBDS_SEL_CON_IDX_15 0x0000147c +/* 0x1480 --> 0x1800 unused */ + +/* Send BD initiator control registers */ +#define SNDBDI_MODE 0x00001800 +#define SNDBDI_MODE_RESET 0x00000001 +#define SNDBDI_MODE_ENABLE 0x00000002 +#define SNDBDI_MODE_ATTN_ENABLE 0x00000004 +#define SNDBDI_STATUS 0x00001804 +#define SNDBDI_STATUS_ERROR_ATTN 0x00000004 +#define SNDBDI_IN_PROD_IDX_0 0x00001808 +#define SNDBDI_IN_PROD_IDX_1 0x0000180c +#define SNDBDI_IN_PROD_IDX_2 0x00001810 +#define SNDBDI_IN_PROD_IDX_3 0x00001814 +#define SNDBDI_IN_PROD_IDX_4 0x00001818 +#define SNDBDI_IN_PROD_IDX_5 0x0000181c +#define SNDBDI_IN_PROD_IDX_6 0x00001820 +#define SNDBDI_IN_PROD_IDX_7 0x00001824 +#define SNDBDI_IN_PROD_IDX_8 0x00001828 +#define SNDBDI_IN_PROD_IDX_9 0x0000182c +#define SNDBDI_IN_PROD_IDX_10 0x00001830 +#define SNDBDI_IN_PROD_IDX_11 0x00001834 +#define SNDBDI_IN_PROD_IDX_12 0x00001838 +#define SNDBDI_IN_PROD_IDX_13 0x0000183c +#define SNDBDI_IN_PROD_IDX_14 0x00001840 +#define SNDBDI_IN_PROD_IDX_15 0x00001844 +/* 0x1848 --> 0x1c00 unused */ + +/* Send BD completion control registers */ +#define SNDBDC_MODE 0x00001c00 +#define SNDBDC_MODE_RESET 0x00000001 +#define SNDBDC_MODE_ENABLE 0x00000002 +#define SNDBDC_MODE_ATTN_ENABLE 0x00000004 +/* 0x1c04 --> 0x2000 unused */ + +/* Receive list placement control registers */ +#define RCVLPC_MODE 0x00002000 +#define RCVLPC_MODE_RESET 0x00000001 +#define RCVLPC_MODE_ENABLE 0x00000002 +#define RCVLPC_MODE_CLASS0_ATTN_ENAB 0x00000004 +#define RCVLPC_MODE_MAPOOR_AATTN_ENAB 0x00000008 +#define RCVLPC_MODE_STAT_OFLOW_ENAB 0x00000010 +#define RCVLPC_STATUS 0x00002004 +#define RCVLPC_STATUS_CLASS0 0x00000004 +#define RCVLPC_STATUS_MAPOOR 0x00000008 +#define RCVLPC_STATUS_STAT_OFLOW 0x00000010 +#define RCVLPC_LOCK 0x00002008 +#define RCVLPC_LOCK_REQ_MASK 0x0000ffff +#define RCVLPC_LOCK_REQ_SHIFT 0 +#define RCVLPC_LOCK_GRANT_MASK 0xffff0000 +#define RCVLPC_LOCK_GRANT_SHIFT 16 +#define RCVLPC_NON_EMPTY_BITS 0x0000200c +#define RCVLPC_NON_EMPTY_BITS_MASK 0x0000ffff +#define RCVLPC_CONFIG 0x00002010 +#define RCVLPC_STATSCTRL 0x00002014 +#define RCVLPC_STATSCTRL_ENABLE 0x00000001 +#define RCVLPC_STATSCTRL_FASTUPD 0x00000002 +#define RCVLPC_STATS_ENABLE 0x00002018 +#define RCVLPC_STATS_INCMASK 0x0000201c +/* 0x2020 --> 0x2100 unused */ +#define RCVLPC_SELLST_BASE 0x00002100 /* 16 16-byte entries */ +#define SELLST_TAIL 0x00000004 +#define SELLST_CONT 0x00000008 +#define SELLST_UNUSED 0x0000000c +#define RCVLPC_COS_CNTL_BASE 0x00002200 /* 16 4-byte entries */ +#define RCVLPC_DROP_FILTER_CNT 0x00002240 +#define RCVLPC_DMA_WQ_FULL_CNT 0x00002244 +#define RCVLPC_DMA_HIPRIO_WQ_FULL_CNT 0x00002248 +#define RCVLPC_NO_RCV_BD_CNT 0x0000224c +#define RCVLPC_IN_DISCARDS_CNT 0x00002250 +#define RCVLPC_IN_ERRORS_CNT 0x00002254 +#define RCVLPC_RCV_THRESH_HIT_CNT 0x00002258 +/* 0x225c --> 0x2400 unused */ + +/* Receive Data and Receive BD Initiator Control */ +#define RCVDBDI_MODE 0x00002400 +#define RCVDBDI_MODE_RESET 0x00000001 +#define RCVDBDI_MODE_ENABLE 0x00000002 +#define RCVDBDI_MODE_JUMBOBD_NEEDED 0x00000004 +#define RCVDBDI_MODE_FRM_TOO_BIG 0x00000008 +#define RCVDBDI_MODE_INV_RING_SZ 0x00000010 +#define RCVDBDI_STATUS 0x00002404 +#define RCVDBDI_STATUS_JUMBOBD_NEEDED 0x00000004 +#define RCVDBDI_STATUS_FRM_TOO_BIG 0x00000008 +#define RCVDBDI_STATUS_INV_RING_SZ 0x00000010 +#define RCVDBDI_SPLIT_FRAME_MINSZ 0x00002408 +/* 0x240c --> 0x2440 unused */ +#define RCVDBDI_JUMBO_BD 0x00002440 /* TG3_BDINFO_... */ +#define RCVDBDI_STD_BD 0x00002450 /* TG3_BDINFO_... */ +#define RCVDBDI_MINI_BD 0x00002460 /* TG3_BDINFO_... */ +#define RCVDBDI_JUMBO_CON_IDX 0x00002470 +#define RCVDBDI_STD_CON_IDX 0x00002474 +#define RCVDBDI_MINI_CON_IDX 0x00002478 +/* 0x247c --> 0x2480 unused */ +#define RCVDBDI_BD_PROD_IDX_0 0x00002480 +#define RCVDBDI_BD_PROD_IDX_1 0x00002484 +#define RCVDBDI_BD_PROD_IDX_2 0x00002488 +#define RCVDBDI_BD_PROD_IDX_3 0x0000248c +#define RCVDBDI_BD_PROD_IDX_4 0x00002490 +#define RCVDBDI_BD_PROD_IDX_5 0x00002494 +#define RCVDBDI_BD_PROD_IDX_6 0x00002498 +#define RCVDBDI_BD_PROD_IDX_7 0x0000249c +#define RCVDBDI_BD_PROD_IDX_8 0x000024a0 +#define RCVDBDI_BD_PROD_IDX_9 0x000024a4 +#define RCVDBDI_BD_PROD_IDX_10 0x000024a8 +#define RCVDBDI_BD_PROD_IDX_11 0x000024ac +#define RCVDBDI_BD_PROD_IDX_12 0x000024b0 +#define RCVDBDI_BD_PROD_IDX_13 0x000024b4 +#define RCVDBDI_BD_PROD_IDX_14 0x000024b8 +#define RCVDBDI_BD_PROD_IDX_15 0x000024bc +#define RCVDBDI_HWDIAG 0x000024c0 +/* 0x24c4 --> 0x2800 unused */ + +/* Receive Data Completion Control */ +#define RCVDCC_MODE 0x00002800 +#define RCVDCC_MODE_RESET 0x00000001 +#define RCVDCC_MODE_ENABLE 0x00000002 +#define RCVDCC_MODE_ATTN_ENABLE 0x00000004 +/* 0x2804 --> 0x2c00 unused */ + +/* Receive BD Initiator Control Registers */ +#define RCVBDI_MODE 0x00002c00 +#define RCVBDI_MODE_RESET 0x00000001 +#define RCVBDI_MODE_ENABLE 0x00000002 +#define RCVBDI_MODE_RCB_ATTN_ENAB 0x00000004 +#define RCVBDI_STATUS 0x00002c04 +#define RCVBDI_STATUS_RCB_ATTN 0x00000004 +#define RCVBDI_JUMBO_PROD_IDX 0x00002c08 +#define RCVBDI_STD_PROD_IDX 0x00002c0c +#define RCVBDI_MINI_PROD_IDX 0x00002c10 +#define RCVBDI_MINI_THRESH 0x00002c14 +#define RCVBDI_STD_THRESH 0x00002c18 +#define RCVBDI_JUMBO_THRESH 0x00002c1c +/* 0x2c20 --> 0x3000 unused */ + +/* Receive BD Completion Control Registers */ +#define RCVCC_MODE 0x00003000 +#define RCVCC_MODE_RESET 0x00000001 +#define RCVCC_MODE_ENABLE 0x00000002 +#define RCVCC_MODE_ATTN_ENABLE 0x00000004 +#define RCVCC_STATUS 0x00003004 +#define RCVCC_STATUS_ERROR_ATTN 0x00000004 +#define RCVCC_JUMP_PROD_IDX 0x00003008 +#define RCVCC_STD_PROD_IDX 0x0000300c +#define RCVCC_MINI_PROD_IDX 0x00003010 +/* 0x3014 --> 0x3400 unused */ + +/* Receive list selector control registers */ +#define RCVLSC_MODE 0x00003400 +#define RCVLSC_MODE_RESET 0x00000001 +#define RCVLSC_MODE_ENABLE 0x00000002 +#define RCVLSC_MODE_ATTN_ENABLE 0x00000004 +#define RCVLSC_STATUS 0x00003404 +#define RCVLSC_STATUS_ERROR_ATTN 0x00000004 +/* 0x3408 --> 0x3800 unused */ + +/* Mbuf cluster free registers */ +#define MBFREE_MODE 0x00003800 +#define MBFREE_MODE_RESET 0x00000001 +#define MBFREE_MODE_ENABLE 0x00000002 +#define MBFREE_STATUS 0x00003804 +/* 0x3808 --> 0x3c00 unused */ + +/* Host coalescing control registers */ +#define HOSTCC_MODE 0x00003c00 +#define HOSTCC_MODE_RESET 0x00000001 +#define HOSTCC_MODE_ENABLE 0x00000002 +#define HOSTCC_MODE_ATTN 0x00000004 +#define HOSTCC_MODE_NOW 0x00000008 +#define HOSTCC_MODE_FULL_STATUS 0x00000000 +#define HOSTCC_MODE_64BYTE 0x00000080 +#define HOSTCC_MODE_32BYTE 0x00000100 +#define HOSTCC_MODE_CLRTICK_RXBD 0x00000200 +#define HOSTCC_MODE_CLRTICK_TXBD 0x00000400 +#define HOSTCC_MODE_NOINT_ON_NOW 0x00000800 +#define HOSTCC_MODE_NOINT_ON_FORCE 0x00001000 +#define HOSTCC_STATUS 0x00003c04 +#define HOSTCC_STATUS_ERROR_ATTN 0x00000004 +#define HOSTCC_RXCOL_TICKS 0x00003c08 +#define LOW_RXCOL_TICKS 0x00000032 +#define DEFAULT_RXCOL_TICKS 0x00000048 +#define HIGH_RXCOL_TICKS 0x00000096 +#define HOSTCC_TXCOL_TICKS 0x00003c0c +#define DEFAULT_TXCOL_TICKS 0x0000012c +#define HOSTCC_RXMAX_FRAMES 0x00003c10 +#define LOW_RXMAX_FRAMES 0x00000005 +#define DEFAULT_RXMAX_FRAMES 0x00000008 +#define HIGH_RXMAX_FRAMES 0x00000012 +#define HOSTCC_TXMAX_FRAMES 0x00003c14 +#define DEFAULT_TXMAX_FRAMES 0x0000004b +#define HOSTCC_RXCOAL_TICK_INT 0x00003c18 +#define DEFAULT_RXCOAL_TICK_INT 0x00000019 +#define HOSTCC_TXCOAL_TICK_INT 0x00003c1c +#define DEFAULT_TXCOAL_TICK_INT 0x00000019 +#define HOSTCC_RXCOAL_MAXF_INT 0x00003c20 +#define DEFAULT_RXCOAL_MAXF_INT 0x00000005 +#define HOSTCC_TXCOAL_MAXF_INT 0x00003c24 +#define DEFAULT_TXCOAL_MAXF_INT 0x00000005 +#define HOSTCC_STAT_COAL_TICKS 0x00003c28 +#define DEFAULT_STAT_COAL_TICKS 0x000f4240 +/* 0x3c2c --> 0x3c30 unused */ +#define HOSTCC_STATS_BLK_HOST_ADDR 0x00003c30 /* 64-bit */ +#define HOSTCC_STATUS_BLK_HOST_ADDR 0x00003c38 /* 64-bit */ +#define HOSTCC_STATS_BLK_NIC_ADDR 0x00003c40 +#define HOSTCC_STATUS_BLK_NIC_ADDR 0x00003c44 +#define HOSTCC_FLOW_ATTN 0x00003c48 +/* 0x3c4c --> 0x3c50 unused */ +#define HOSTCC_JUMBO_CON_IDX 0x00003c50 +#define HOSTCC_STD_CON_IDX 0x00003c54 +#define HOSTCC_MINI_CON_IDX 0x00003c58 +/* 0x3c5c --> 0x3c80 unused */ +#define HOSTCC_RET_PROD_IDX_0 0x00003c80 +#define HOSTCC_RET_PROD_IDX_1 0x00003c84 +#define HOSTCC_RET_PROD_IDX_2 0x00003c88 +#define HOSTCC_RET_PROD_IDX_3 0x00003c8c +#define HOSTCC_RET_PROD_IDX_4 0x00003c90 +#define HOSTCC_RET_PROD_IDX_5 0x00003c94 +#define HOSTCC_RET_PROD_IDX_6 0x00003c98 +#define HOSTCC_RET_PROD_IDX_7 0x00003c9c +#define HOSTCC_RET_PROD_IDX_8 0x00003ca0 +#define HOSTCC_RET_PROD_IDX_9 0x00003ca4 +#define HOSTCC_RET_PROD_IDX_10 0x00003ca8 +#define HOSTCC_RET_PROD_IDX_11 0x00003cac +#define HOSTCC_RET_PROD_IDX_12 0x00003cb0 +#define HOSTCC_RET_PROD_IDX_13 0x00003cb4 +#define HOSTCC_RET_PROD_IDX_14 0x00003cb8 +#define HOSTCC_RET_PROD_IDX_15 0x00003cbc +#define HOSTCC_SND_CON_IDX_0 0x00003cc0 +#define HOSTCC_SND_CON_IDX_1 0x00003cc4 +#define HOSTCC_SND_CON_IDX_2 0x00003cc8 +#define HOSTCC_SND_CON_IDX_3 0x00003ccc +#define HOSTCC_SND_CON_IDX_4 0x00003cd0 +#define HOSTCC_SND_CON_IDX_5 0x00003cd4 +#define HOSTCC_SND_CON_IDX_6 0x00003cd8 +#define HOSTCC_SND_CON_IDX_7 0x00003cdc +#define HOSTCC_SND_CON_IDX_8 0x00003ce0 +#define HOSTCC_SND_CON_IDX_9 0x00003ce4 +#define HOSTCC_SND_CON_IDX_10 0x00003ce8 +#define HOSTCC_SND_CON_IDX_11 0x00003cec +#define HOSTCC_SND_CON_IDX_12 0x00003cf0 +#define HOSTCC_SND_CON_IDX_13 0x00003cf4 +#define HOSTCC_SND_CON_IDX_14 0x00003cf8 +#define HOSTCC_SND_CON_IDX_15 0x00003cfc +/* 0x3d00 --> 0x4000 unused */ + +/* Memory arbiter control registers */ +#define MEMARB_MODE 0x00004000 +#define MEMARB_MODE_RESET 0x00000001 +#define MEMARB_MODE_ENABLE 0x00000002 +#define MEMARB_STATUS 0x00004004 +#define MEMARB_TRAP_ADDR_LOW 0x00004008 +#define MEMARB_TRAP_ADDR_HIGH 0x0000400c +/* 0x4010 --> 0x4400 unused */ + +/* Buffer manager control registers */ +#define BUFMGR_MODE 0x00004400 +#define BUFMGR_MODE_RESET 0x00000001 +#define BUFMGR_MODE_ENABLE 0x00000002 +#define BUFMGR_MODE_ATTN_ENABLE 0x00000004 +#define BUFMGR_MODE_BM_TEST 0x00000008 +#define BUFMGR_MODE_MBLOW_ATTN_ENAB 0x00000010 +#define BUFMGR_STATUS 0x00004404 +#define BUFMGR_STATUS_ERROR 0x00000004 +#define BUFMGR_STATUS_MBLOW 0x00000010 +#define BUFMGR_MB_POOL_ADDR 0x00004408 +#define BUFMGR_MB_POOL_SIZE 0x0000440c +#define BUFMGR_MB_RDMA_LOW_WATER 0x00004410 +#define DEFAULT_MB_RDMA_LOW_WATER 0x00000040 +#define DEFAULT_MB_RDMA_LOW_WATER_JUMBO 0x00000130 +#define BUFMGR_MB_MACRX_LOW_WATER 0x00004414 +#define DEFAULT_MB_MACRX_LOW_WATER 0x00000020 +#define DEFAULT_MB_MACRX_LOW_WATER_JUMBO 0x00000098 +#define BUFMGR_MB_HIGH_WATER 0x00004418 +#define DEFAULT_MB_HIGH_WATER 0x00000060 +#define DEFAULT_MB_HIGH_WATER_JUMBO 0x0000017c +#define BUFMGR_RX_MB_ALLOC_REQ 0x0000441c +#define BUFMGR_MB_ALLOC_BIT 0x10000000 +#define BUFMGR_RX_MB_ALLOC_RESP 0x00004420 +#define BUFMGR_TX_MB_ALLOC_REQ 0x00004424 +#define BUFMGR_TX_MB_ALLOC_RESP 0x00004428 +#define BUFMGR_DMA_DESC_POOL_ADDR 0x0000442c +#define BUFMGR_DMA_DESC_POOL_SIZE 0x00004430 +#define BUFMGR_DMA_LOW_WATER 0x00004434 +#define DEFAULT_DMA_LOW_WATER 0x00000005 +#define BUFMGR_DMA_HIGH_WATER 0x00004438 +#define DEFAULT_DMA_HIGH_WATER 0x0000000a +#define BUFMGR_RX_DMA_ALLOC_REQ 0x0000443c +#define BUFMGR_RX_DMA_ALLOC_RESP 0x00004440 +#define BUFMGR_TX_DMA_ALLOC_REQ 0x00004444 +#define BUFMGR_TX_DMA_ALLOC_RESP 0x00004448 +#define BUFMGR_HWDIAG_0 0x0000444c +#define BUFMGR_HWDIAG_1 0x00004450 +#define BUFMGR_HWDIAG_2 0x00004454 +/* 0x4458 --> 0x4800 unused */ + +/* Read DMA control registers */ +#define RDMAC_MODE 0x00004800 +#define RDMAC_MODE_RESET 0x00000001 +#define RDMAC_MODE_ENABLE 0x00000002 +#define RDMAC_MODE_TGTABORT_ENAB 0x00000004 +#define RDMAC_MODE_MSTABORT_ENAB 0x00000008 +#define RDMAC_MODE_PARITYERR_ENAB 0x00000010 +#define RDMAC_MODE_ADDROFLOW_ENAB 0x00000020 +#define RDMAC_MODE_FIFOOFLOW_ENAB 0x00000040 +#define RDMAC_MODE_FIFOURUN_ENAB 0x00000080 +#define RDMAC_MODE_FIFOOREAD_ENAB 0x00000100 +#define RDMAC_MODE_LNGREAD_ENAB 0x00000200 +#define RDMAC_STATUS 0x00004804 +#define RDMAC_STATUS_TGTABORT 0x00000004 +#define RDMAC_STATUS_MSTABORT 0x00000008 +#define RDMAC_STATUS_PARITYERR 0x00000010 +#define RDMAC_STATUS_ADDROFLOW 0x00000020 +#define RDMAC_STATUS_FIFOOFLOW 0x00000040 +#define RDMAC_STATUS_FIFOURUN 0x00000080 +#define RDMAC_STATUS_FIFOOREAD 0x00000100 +#define RDMAC_STATUS_LNGREAD 0x00000200 +/* 0x4808 --> 0x4c00 unused */ + +/* Write DMA control registers */ +#define WDMAC_MODE 0x00004c00 +#define WDMAC_MODE_RESET 0x00000001 +#define WDMAC_MODE_ENABLE 0x00000002 +#define WDMAC_MODE_TGTABORT_ENAB 0x00000004 +#define WDMAC_MODE_MSTABORT_ENAB 0x00000008 +#define WDMAC_MODE_PARITYERR_ENAB 0x00000010 +#define WDMAC_MODE_ADDROFLOW_ENAB 0x00000020 +#define WDMAC_MODE_FIFOOFLOW_ENAB 0x00000040 +#define WDMAC_MODE_FIFOURUN_ENAB 0x00000080 +#define WDMAC_MODE_FIFOOREAD_ENAB 0x00000100 +#define WDMAC_MODE_LNGREAD_ENAB 0x00000200 +#define WDMAC_STATUS 0x00004c04 +#define WDMAC_STATUS_TGTABORT 0x00000004 +#define WDMAC_STATUS_MSTABORT 0x00000008 +#define WDMAC_STATUS_PARITYERR 0x00000010 +#define WDMAC_STATUS_ADDROFLOW 0x00000020 +#define WDMAC_STATUS_FIFOOFLOW 0x00000040 +#define WDMAC_STATUS_FIFOURUN 0x00000080 +#define WDMAC_STATUS_FIFOOREAD 0x00000100 +#define WDMAC_STATUS_LNGREAD 0x00000200 +/* 0x4c08 --> 0x5000 unused */ + +/* Per-cpu register offsets (arm9) */ +#define CPU_MODE 0x00000000 +#define CPU_MODE_RESET 0x00000001 +#define CPU_MODE_HALT 0x00000400 +#define CPU_STATE 0x00000004 +#define CPU_EVTMASK 0x00000008 +/* 0xc --> 0x1c reserved */ +#define CPU_PC 0x0000001c +#define CPU_INSN 0x00000020 +#define CPU_SPAD_UFLOW 0x00000024 +#define CPU_WDOG_CLEAR 0x00000028 +#define CPU_WDOG_VECTOR 0x0000002c +#define CPU_WDOG_PC 0x00000030 +#define CPU_HW_BP 0x00000034 +/* 0x38 --> 0x44 unused */ +#define CPU_WDOG_SAVED_STATE 0x00000044 +#define CPU_LAST_BRANCH_ADDR 0x00000048 +#define CPU_SPAD_UFLOW_SET 0x0000004c +/* 0x50 --> 0x200 unused */ +#define CPU_R0 0x00000200 +#define CPU_R1 0x00000204 +#define CPU_R2 0x00000208 +#define CPU_R3 0x0000020c +#define CPU_R4 0x00000210 +#define CPU_R5 0x00000214 +#define CPU_R6 0x00000218 +#define CPU_R7 0x0000021c +#define CPU_R8 0x00000220 +#define CPU_R9 0x00000224 +#define CPU_R10 0x00000228 +#define CPU_R11 0x0000022c +#define CPU_R12 0x00000230 +#define CPU_R13 0x00000234 +#define CPU_R14 0x00000238 +#define CPU_R15 0x0000023c +#define CPU_R16 0x00000240 +#define CPU_R17 0x00000244 +#define CPU_R18 0x00000248 +#define CPU_R19 0x0000024c +#define CPU_R20 0x00000250 +#define CPU_R21 0x00000254 +#define CPU_R22 0x00000258 +#define CPU_R23 0x0000025c +#define CPU_R24 0x00000260 +#define CPU_R25 0x00000264 +#define CPU_R26 0x00000268 +#define CPU_R27 0x0000026c +#define CPU_R28 0x00000270 +#define CPU_R29 0x00000274 +#define CPU_R30 0x00000278 +#define CPU_R31 0x0000027c +/* 0x280 --> 0x400 unused */ + +#define RX_CPU_BASE 0x00005000 +#define TX_CPU_BASE 0x00005400 + +/* Mailboxes */ +#define GRCMBOX_INTERRUPT_0 0x00005800 /* 64-bit */ +#define GRCMBOX_INTERRUPT_1 0x00005808 /* 64-bit */ +#define GRCMBOX_INTERRUPT_2 0x00005810 /* 64-bit */ +#define GRCMBOX_INTERRUPT_3 0x00005818 /* 64-bit */ +#define GRCMBOX_GENERAL_0 0x00005820 /* 64-bit */ +#define GRCMBOX_GENERAL_1 0x00005828 /* 64-bit */ +#define GRCMBOX_GENERAL_2 0x00005830 /* 64-bit */ +#define GRCMBOX_GENERAL_3 0x00005838 /* 64-bit */ +#define GRCMBOX_GENERAL_4 0x00005840 /* 64-bit */ +#define GRCMBOX_GENERAL_5 0x00005848 /* 64-bit */ +#define GRCMBOX_GENERAL_6 0x00005850 /* 64-bit */ +#define GRCMBOX_GENERAL_7 0x00005858 /* 64-bit */ +#define GRCMBOX_RELOAD_STAT 0x00005860 /* 64-bit */ +#define GRCMBOX_RCVSTD_PROD_IDX 0x00005868 /* 64-bit */ +#define GRCMBOX_RCVJUMBO_PROD_IDX 0x00005870 /* 64-bit */ +#define GRCMBOX_RCVMINI_PROD_IDX 0x00005878 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_0 0x00005880 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_1 0x00005888 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_2 0x00005890 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_3 0x00005898 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_4 0x000058a0 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_5 0x000058a8 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_6 0x000058b0 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_7 0x000058b8 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_8 0x000058c0 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_9 0x000058c8 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_10 0x000058d0 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_11 0x000058d8 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_12 0x000058e0 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_13 0x000058e8 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_14 0x000058f0 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_15 0x000058f8 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_0 0x00005900 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_1 0x00005908 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_2 0x00005910 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_3 0x00005918 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_4 0x00005920 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_5 0x00005928 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_6 0x00005930 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_7 0x00005938 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_8 0x00005940 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_9 0x00005948 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_10 0x00005950 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_11 0x00005958 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_12 0x00005960 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_13 0x00005968 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_14 0x00005970 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_15 0x00005978 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_0 0x00005980 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_1 0x00005988 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_2 0x00005990 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_3 0x00005998 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_4 0x000059a0 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_5 0x000059a8 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_6 0x000059b0 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_7 0x000059b8 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_8 0x000059c0 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_9 0x000059c8 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_10 0x000059d0 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_11 0x000059d8 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_12 0x000059e0 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_13 0x000059e8 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_14 0x000059f0 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_15 0x000059f8 /* 64-bit */ +#define GRCMBOX_HIGH_PRIO_EV_VECTOR 0x00005a00 +#define GRCMBOX_HIGH_PRIO_EV_MASK 0x00005a04 +#define GRCMBOX_LOW_PRIO_EV_VEC 0x00005a08 +#define GRCMBOX_LOW_PRIO_EV_MASK 0x00005a0c +/* 0x5a10 --> 0x5c00 */ + +/* Flow Through queues */ +#define FTQ_RESET 0x00005c00 +/* 0x5c04 --> 0x5c10 unused */ +#define FTQ_DMA_NORM_READ_CTL 0x00005c10 +#define FTQ_DMA_NORM_READ_FULL_CNT 0x00005c14 +#define FTQ_DMA_NORM_READ_FIFO_ENQDEQ 0x00005c18 +#define FTQ_DMA_NORM_READ_WRITE_PEEK 0x00005c1c +#define FTQ_DMA_HIGH_READ_CTL 0x00005c20 +#define FTQ_DMA_HIGH_READ_FULL_CNT 0x00005c24 +#define FTQ_DMA_HIGH_READ_FIFO_ENQDEQ 0x00005c28 +#define FTQ_DMA_HIGH_READ_WRITE_PEEK 0x00005c2c +#define FTQ_DMA_COMP_DISC_CTL 0x00005c30 +#define FTQ_DMA_COMP_DISC_FULL_CNT 0x00005c34 +#define FTQ_DMA_COMP_DISC_FIFO_ENQDEQ 0x00005c38 +#define FTQ_DMA_COMP_DISC_WRITE_PEEK 0x00005c3c +#define FTQ_SEND_BD_COMP_CTL 0x00005c40 +#define FTQ_SEND_BD_COMP_FULL_CNT 0x00005c44 +#define FTQ_SEND_BD_COMP_FIFO_ENQDEQ 0x00005c48 +#define FTQ_SEND_BD_COMP_WRITE_PEEK 0x00005c4c +#define FTQ_SEND_DATA_INIT_CTL 0x00005c50 +#define FTQ_SEND_DATA_INIT_FULL_CNT 0x00005c54 +#define FTQ_SEND_DATA_INIT_FIFO_ENQDEQ 0x00005c58 +#define FTQ_SEND_DATA_INIT_WRITE_PEEK 0x00005c5c +#define FTQ_DMA_NORM_WRITE_CTL 0x00005c60 +#define FTQ_DMA_NORM_WRITE_FULL_CNT 0x00005c64 +#define FTQ_DMA_NORM_WRITE_FIFO_ENQDEQ 0x00005c68 +#define FTQ_DMA_NORM_WRITE_WRITE_PEEK 0x00005c6c +#define FTQ_DMA_HIGH_WRITE_CTL 0x00005c70 +#define FTQ_DMA_HIGH_WRITE_FULL_CNT 0x00005c74 +#define FTQ_DMA_HIGH_WRITE_FIFO_ENQDEQ 0x00005c78 +#define FTQ_DMA_HIGH_WRITE_WRITE_PEEK 0x00005c7c +#define FTQ_SWTYPE1_CTL 0x00005c80 +#define FTQ_SWTYPE1_FULL_CNT 0x00005c84 +#define FTQ_SWTYPE1_FIFO_ENQDEQ 0x00005c88 +#define FTQ_SWTYPE1_WRITE_PEEK 0x00005c8c +#define FTQ_SEND_DATA_COMP_CTL 0x00005c90 +#define FTQ_SEND_DATA_COMP_FULL_CNT 0x00005c94 +#define FTQ_SEND_DATA_COMP_FIFO_ENQDEQ 0x00005c98 +#define FTQ_SEND_DATA_COMP_WRITE_PEEK 0x00005c9c +#define FTQ_HOST_COAL_CTL 0x00005ca0 +#define FTQ_HOST_COAL_FULL_CNT 0x00005ca4 +#define FTQ_HOST_COAL_FIFO_ENQDEQ 0x00005ca8 +#define FTQ_HOST_COAL_WRITE_PEEK 0x00005cac +#define FTQ_MAC_TX_CTL 0x00005cb0 +#define FTQ_MAC_TX_FULL_CNT 0x00005cb4 +#define FTQ_MAC_TX_FIFO_ENQDEQ 0x00005cb8 +#define FTQ_MAC_TX_WRITE_PEEK 0x00005cbc +#define FTQ_MB_FREE_CTL 0x00005cc0 +#define FTQ_MB_FREE_FULL_CNT 0x00005cc4 +#define FTQ_MB_FREE_FIFO_ENQDEQ 0x00005cc8 +#define FTQ_MB_FREE_WRITE_PEEK 0x00005ccc +#define FTQ_RCVBD_COMP_CTL 0x00005cd0 +#define FTQ_RCVBD_COMP_FULL_CNT 0x00005cd4 +#define FTQ_RCVBD_COMP_FIFO_ENQDEQ 0x00005cd8 +#define FTQ_RCVBD_COMP_WRITE_PEEK 0x00005cdc +#define FTQ_RCVLST_PLMT_CTL 0x00005ce0 +#define FTQ_RCVLST_PLMT_FULL_CNT 0x00005ce4 +#define FTQ_RCVLST_PLMT_FIFO_ENQDEQ 0x00005ce8 +#define FTQ_RCVLST_PLMT_WRITE_PEEK 0x00005cec +#define FTQ_RCVDATA_INI_CTL 0x00005cf0 +#define FTQ_RCVDATA_INI_FULL_CNT 0x00005cf4 +#define FTQ_RCVDATA_INI_FIFO_ENQDEQ 0x00005cf8 +#define FTQ_RCVDATA_INI_WRITE_PEEK 0x00005cfc +#define FTQ_RCVDATA_COMP_CTL 0x00005d00 +#define FTQ_RCVDATA_COMP_FULL_CNT 0x00005d04 +#define FTQ_RCVDATA_COMP_FIFO_ENQDEQ 0x00005d08 +#define FTQ_RCVDATA_COMP_WRITE_PEEK 0x00005d0c +#define FTQ_SWTYPE2_CTL 0x00005d10 +#define FTQ_SWTYPE2_FULL_CNT 0x00005d14 +#define FTQ_SWTYPE2_FIFO_ENQDEQ 0x00005d18 +#define FTQ_SWTYPE2_WRITE_PEEK 0x00005d1c +/* 0x5d20 --> 0x6000 unused */ + +/* Message signaled interrupt registers */ +#define MSGINT_MODE 0x00006000 +#define MSGINT_MODE_RESET 0x00000001 +#define MSGINT_MODE_ENABLE 0x00000002 +#define MSGINT_STATUS 0x00006004 +#define MSGINT_FIFO 0x00006008 +/* 0x600c --> 0x6400 unused */ + +/* DMA completion registers */ +#define DMAC_MODE 0x00006400 +#define DMAC_MODE_RESET 0x00000001 +#define DMAC_MODE_ENABLE 0x00000002 +/* 0x6404 --> 0x6800 unused */ + +/* GRC registers */ +#define GRC_MODE 0x00006800 +#define GRC_MODE_UPD_ON_COAL 0x00000001 +#define GRC_MODE_BSWAP_NONFRM_DATA 0x00000002 +#define GRC_MODE_WSWAP_NONFRM_DATA 0x00000004 +#define GRC_MODE_BSWAP_DATA 0x00000010 +#define GRC_MODE_WSWAP_DATA 0x00000020 +#define GRC_MODE_SPLITHDR 0x00000100 +#define GRC_MODE_NOFRM_CRACKING 0x00000200 +#define GRC_MODE_INCL_CRC 0x00000400 +#define GRC_MODE_ALLOW_BAD_FRMS 0x00000800 +#define GRC_MODE_NOIRQ_ON_SENDS 0x00002000 +#define GRC_MODE_NOIRQ_ON_RCV 0x00004000 +#define GRC_MODE_FORCE_PCI32BIT 0x00008000 +#define GRC_MODE_HOST_STACKUP 0x00010000 +#define GRC_MODE_HOST_SENDBDS 0x00020000 +#define GRC_MODE_NO_TX_PHDR_CSUM 0x00100000 +#define GRC_MODE_NO_RX_PHDR_CSUM 0x00800000 +#define GRC_MODE_IRQ_ON_TX_CPU_ATTN 0x01000000 +#define GRC_MODE_IRQ_ON_RX_CPU_ATTN 0x02000000 +#define GRC_MODE_IRQ_ON_MAC_ATTN 0x04000000 +#define GRC_MODE_IRQ_ON_DMA_ATTN 0x08000000 +#define GRC_MODE_IRQ_ON_FLOW_ATTN 0x10000000 +#define GRC_MODE_4X_NIC_SEND_RINGS 0x20000000 +#define GRC_MODE_MCAST_FRM_ENABLE 0x40000000 +#define GRC_MISC_CFG 0x00006804 +#define GRC_MISC_CFG_CORECLK_RESET 0x00000001 +#define GRC_MISC_CFG_PRESCALAR_MASK 0x000000fe +#define GRC_MISC_CFG_PRESCALAR_SHIFT 1 +#define GRC_MISC_CFG_BOARD_ID_MASK 0x0001e000 +#define GRC_MISC_CFG_BOARD_ID_5700 0x0001e000 +#define GRC_MISC_CFG_BOARD_ID_5701 0x00000000 +#define GRC_MISC_CFG_BOARD_ID_5702FE 0x00004000 +#define GRC_MISC_CFG_BOARD_ID_5703 0x00000000 +#define GRC_MISC_CFG_BOARD_ID_5703S 0x00002000 +#define GRC_LOCAL_CTRL 0x00006808 +#define GRC_LCLCTRL_INT_ACTIVE 0x00000001 +#define GRC_LCLCTRL_CLEARINT 0x00000002 +#define GRC_LCLCTRL_SETINT 0x00000004 +#define GRC_LCLCTRL_INT_ON_ATTN 0x00000008 +#define GRC_LCLCTRL_GPIO_INPUT0 0x00000100 +#define GRC_LCLCTRL_GPIO_INPUT1 0x00000200 +#define GRC_LCLCTRL_GPIO_INPUT2 0x00000400 +#define GRC_LCLCTRL_GPIO_OE0 0x00000800 +#define GRC_LCLCTRL_GPIO_OE1 0x00001000 +#define GRC_LCLCTRL_GPIO_OE2 0x00002000 +#define GRC_LCLCTRL_GPIO_OUTPUT0 0x00004000 +#define GRC_LCLCTRL_GPIO_OUTPUT1 0x00008000 +#define GRC_LCLCTRL_GPIO_OUTPUT2 0x00010000 +#define GRC_LCLCTRL_EXTMEM_ENABLE 0x00020000 +#define GRC_LCLCTRL_MEMSZ_MASK 0x001c0000 +#define GRC_LCLCTRL_MEMSZ_256K 0x00000000 +#define GRC_LCLCTRL_MEMSZ_512K 0x00040000 +#define GRC_LCLCTRL_MEMSZ_1M 0x00080000 +#define GRC_LCLCTRL_MEMSZ_2M 0x000c0000 +#define GRC_LCLCTRL_MEMSZ_4M 0x00100000 +#define GRC_LCLCTRL_MEMSZ_8M 0x00140000 +#define GRC_LCLCTRL_MEMSZ_16M 0x00180000 +#define GRC_LCLCTRL_BANK_SELECT 0x00200000 +#define GRC_LCLCTRL_SSRAM_TYPE 0x00400000 +#define GRC_LCLCTRL_AUTO_SEEPROM 0x01000000 +#define GRC_TIMER 0x0000680c +#define GRC_RX_CPU_EVENT 0x00006810 +#define GRC_RX_TIMER_REF 0x00006814 +#define GRC_RX_CPU_SEM 0x00006818 +#define GRC_REMOTE_RX_CPU_ATTN 0x0000681c +#define GRC_TX_CPU_EVENT 0x00006820 +#define GRC_TX_TIMER_REF 0x00006824 +#define GRC_TX_CPU_SEM 0x00006828 +#define GRC_REMOTE_TX_CPU_ATTN 0x0000682c +#define GRC_MEM_POWER_UP 0x00006830 /* 64-bit */ +#define GRC_EEPROM_ADDR 0x00006838 +#define EEPROM_ADDR_WRITE 0x00000000 +#define EEPROM_ADDR_READ 0x80000000 +#define EEPROM_ADDR_COMPLETE 0x40000000 +#define EEPROM_ADDR_FSM_RESET 0x20000000 +#define EEPROM_ADDR_DEVID_MASK 0x1c000000 +#define EEPROM_ADDR_DEVID_SHIFT 26 +#define EEPROM_ADDR_START 0x02000000 +#define EEPROM_ADDR_CLKPERD_SHIFT 16 +#define EEPROM_ADDR_ADDR_MASK 0x0000ffff +#define EEPROM_ADDR_ADDR_SHIFT 0 +#define EEPROM_DEFAULT_CLOCK_PERIOD 0x60 +#define EEPROM_CHIP_SIZE (64 * 1024) +#define GRC_EEPROM_DATA 0x0000683c +#define GRC_EEPROM_CTRL 0x00006840 +#define GRC_MDI_CTRL 0x00006844 +#define GRC_SEEPROM_DELAY 0x00006848 +/* 0x684c --> 0x6c00 unused */ + +/* 0x6c00 --> 0x7000 unused */ + +/* NVRAM Control registers */ +#define NVRAM_CMD 0x00007000 +#define NVRAM_CMD_RESET 0x00000001 +#define NVRAM_CMD_DONE 0x00000008 +#define NVRAM_CMD_GO 0x00000010 +#define NVRAM_CMD_WR 0x00000020 +#define NVRAM_CMD_RD 0x00000000 +#define NVRAM_CMD_ERASE 0x00000040 +#define NVRAM_CMD_FIRST 0x00000080 +#define NVRAM_CMD_LAST 0x00000100 +#define NVRAM_STAT 0x00007004 +#define NVRAM_WRDATA 0x00007008 +#define NVRAM_ADDR 0x0000700c +#define NVRAM_ADDR_MSK 0x00ffffff +#define NVRAM_RDDATA 0x00007010 +#define NVRAM_CFG1 0x00007014 +#define NVRAM_CFG1_FLASHIF_ENAB 0x00000001 +#define NVRAM_CFG1_BUFFERED_MODE 0x00000002 +#define NVRAM_CFG1_PASS_THRU 0x00000004 +#define NVRAM_CFG1_BIT_BANG 0x00000008 +#define NVRAM_CFG1_COMPAT_BYPASS 0x80000000 +#define NVRAM_CFG2 0x00007018 +#define NVRAM_CFG3 0x0000701c +#define NVRAM_SWARB 0x00007020 +#define SWARB_REQ_SET0 0x00000001 +#define SWARB_REQ_SET1 0x00000002 +#define SWARB_REQ_SET2 0x00000004 +#define SWARB_REQ_SET3 0x00000008 +#define SWARB_REQ_CLR0 0x00000010 +#define SWARB_REQ_CLR1 0x00000020 +#define SWARB_REQ_CLR2 0x00000040 +#define SWARB_REQ_CLR3 0x00000080 +#define SWARB_GNT0 0x00000100 +#define SWARB_GNT1 0x00000200 +#define SWARB_GNT2 0x00000400 +#define SWARB_GNT3 0x00000800 +#define SWARB_REQ0 0x00001000 +#define SWARB_REQ1 0x00002000 +#define SWARB_REQ2 0x00004000 +#define SWARB_REQ3 0x00008000 +#define NVRAM_BUFFERED_PAGE_SIZE 264 +#define NVRAM_BUFFERED_PAGE_POS 9 +/* 0x7024 --> 0x7400 unused */ + +/* 0x7400 --> 0x8000 unused */ + +/* 32K Window into NIC internal memory */ +#define NIC_SRAM_WIN_BASE 0x00008000 + +/* Offsets into first 32k of NIC internal memory. */ +#define NIC_SRAM_PAGE_ZERO 0x00000000 +#define NIC_SRAM_SEND_RCB 0x00000100 /* 16 * TG3_BDINFO_... */ +#define NIC_SRAM_RCV_RET_RCB 0x00000200 /* 16 * TG3_BDINFO_... */ +#define NIC_SRAM_STATS_BLK 0x00000300 +#define NIC_SRAM_STATUS_BLK 0x00000b00 + +#define NIC_SRAM_FIRMWARE_MBOX 0x00000b50 +#define NIC_SRAM_FIRMWARE_MBOX_MAGIC1 0x4B657654 +#define NIC_SRAM_FIRMWARE_MBOX_MAGIC2 0x4861764b /* !dma on linkchg */ + +#define NIC_SRAM_DATA_SIG 0x00000b54 +#define NIC_SRAM_DATA_SIG_MAGIC 0x4b657654 /* ascii for 'KevT' */ + +#define NIC_SRAM_DATA_CFG 0x00000b58 +#define NIC_SRAM_DATA_CFG_PHY_TYPE_MASK 0x0000000c +#define NIC_SRAM_DATA_CFG_PHY_TYPE_UNKNOWN 0x00000000 +#define NIC_SRAM_DATA_CFG_PHY_TYPE_COPPER 0x00000004 +#define NIC_SRAM_DATA_CFG_PHY_TYPE_FIBER 0x00000008 +#define NIC_SRAM_DATA_CFG_LED_MODE_MASK 0x00000030 +#define NIC_SRAM_DATA_CFG_LED_MODE_UNKNOWN 0x00000000 +#define NIC_SRAM_DATA_CFG_LED_TRIPLE_SPD 0x00000010 +#define NIC_SRAM_DATA_CFG_LED_LINK_SPD 0x00000020 + +#define NIC_SRAM_DATA_PHY_ID 0x00000b74 +#define NIC_SRAM_DATA_PHY_ID1_MASK 0xffff0000 +#define NIC_SRAM_DATA_PHY_ID2_MASK 0x0000ffff + +#define NIC_SRAM_FW_CMD_MBOX 0x00000b78 +#define FWCMD_NICDRV_ALIVE 0x00000001 +#define FWCMD_NICDRV_PAUSE_FW 0x00000002 +#define FWCMD_NICDRV_IPV4ADDR_CHG 0x00000003 +#define FWCMD_NICDRV_IPV6ADDR_CHG 0x00000004 +#define FWCMD_NICDRV_FIX_DMAR 0x00000005 +#define FWCMD_NICDRV_FIX_DMAW 0x00000006 +#define NIC_SRAM_FW_CMD_LEN_MBOX 0x00000b7c +#define NIC_SRAM_FW_CMD_DATA_MBOX 0x00000b80 +#define NIC_SRAM_FW_ASF_STATUS_MBOX 0x00000c00 +#define NIC_SRAM_FW_DRV_STATE_MBOX 0x00000c04 +#define DRV_STATE_START 0x00000001 +#define DRV_STATE_UNLOAD 0x00000002 +#define DRV_STATE_WOL 0x00000003 +#define DRV_STATE_SUSPEND 0x00000004 + +#define NIC_SRAM_FW_RESET_TYPE_MBOX 0x00000c08 + +#define NIC_SRAM_MAC_ADDR_HIGH_MBOX 0x00000c14 +#define NIC_SRAM_MAC_ADDR_LOW_MBOX 0x00000c18 + +#if TG3_MINI_RING_WORKS +#define NIC_SRAM_RX_MINI_BUFFER_DESC 0x00001000 +#endif + +#define NIC_SRAM_DMA_DESC_POOL_BASE 0x00002000 +#define NIC_SRAM_DMA_DESC_POOL_SIZE 0x00002000 +#define NIC_SRAM_TX_BUFFER_DESC 0x00004000 /* 512 entries */ +#define NIC_SRAM_RX_BUFFER_DESC 0x00006000 /* 256 entries */ +#define NIC_SRAM_RX_JUMBO_BUFFER_DESC 0x00007000 /* 256 entries */ +#define NIC_SRAM_MBUF_POOL_BASE 0x00008000 +#define NIC_SRAM_MBUF_POOL_SIZE 0x00018000 + +/* Currently this is fixed. */ +#define PHY_ADDR 0x01 + +/* Tigon3 specific PHY MII registers. */ +#define TG3_BMCR_SPEED1000 0x0040 + +#define MII_TG3_CTRL 0x09 /* 1000-baseT control register */ +#define MII_TG3_CTRL_ADV_1000_HALF 0x0100 +#define MII_TG3_CTRL_ADV_1000_FULL 0x0200 +#define MII_TG3_CTRL_AS_MASTER 0x0800 +#define MII_TG3_CTRL_ENABLE_AS_MASTER 0x1000 + +#define MII_TG3_EXT_CTRL 0x10 /* Extended control register */ +#define MII_TG3_EXT_CTRL_LNK3_LED_MODE 0x0002 +#define MII_TG3_EXT_CTRL_TBI 0x8000 + +#define MII_TG3_EXT_STAT 0x11 /* Extended status register */ +#define MII_TG3_EXT_STAT_LPASS 0x0100 + +#define MII_TG3_DSP_RW_PORT 0x15 /* DSP coefficient read/write port */ + +#define MII_TG3_DSP_ADDRESS 0x17 /* DSP address register */ + +#define MII_TG3_AUX_CTRL 0x18 /* auxilliary control register */ + +#define MII_TG3_AUX_STAT 0x19 /* auxilliary status register */ +#define MII_TG3_AUX_STAT_LPASS 0x0004 +#define MII_TG3_AUX_STAT_SPDMASK 0x0700 +#define MII_TG3_AUX_STAT_10HALF 0x0100 +#define MII_TG3_AUX_STAT_10FULL 0x0200 +#define MII_TG3_AUX_STAT_100HALF 0x0300 +#define MII_TG3_AUX_STAT_100_4 0x0400 +#define MII_TG3_AUX_STAT_100FULL 0x0500 +#define MII_TG3_AUX_STAT_1000HALF 0x0600 +#define MII_TG3_AUX_STAT_1000FULL 0x0700 + +#define MII_TG3_ISTAT 0x1a /* IRQ status register */ +#define MII_TG3_IMASK 0x1b /* IRQ mask register */ + +/* ISTAT/IMASK event bits */ +#define MII_TG3_INT_LINKCHG 0x0002 +#define MII_TG3_INT_SPEEDCHG 0x0004 +#define MII_TG3_INT_DUPLEXCHG 0x0008 +#define MII_TG3_INT_ANEG_PAGE_RX 0x0400 + +/* XXX Add this to mii.h */ +#ifndef ADVERTISE_PAUSE +#define ADVERTISE_PAUSE_CAP 0x0400 +#endif +#ifndef ADVERTISE_PAUSE_ASYM +#define ADVERTISE_PAUSE_ASYM 0x0800 +#endif +#ifndef LPA_PAUSE +#define LPA_PAUSE_CAP 0x0400 +#endif +#ifndef LPA_PAUSE_ASYM +#define LPA_PAUSE_ASYM 0x0800 +#endif + +/* There are two ways to manage the TX descriptors on the tigon3. + * Either the descriptors are in host DMA'able memory, or they + * exist only in the cards on-chip SRAM. All 16 send bds are under + * the same mode, they may not be configured individually. + * + * The mode we use is controlled by TG3_FLAG_HOST_TXDS in tp->tg3_flags. + * + * To use host memory TX descriptors: + * 1) Set GRC_MODE_HOST_SENDBDS in GRC_MODE register. + * Make sure GRC_MODE_4X_NIC_SEND_RINGS is clear. + * 2) Allocate DMA'able memory. + * 3) In NIC_SRAM_SEND_RCB (of desired index) of on-chip SRAM: + * a) Set TG3_BDINFO_HOST_ADDR to DMA address of memory + * obtained in step 2 + * b) Set TG3_BDINFO_NIC_ADDR to NIC_SRAM_TX_BUFFER_DESC. + * c) Set len field of TG3_BDINFO_MAXLEN_FLAGS to number + * of TX descriptors. Leave flags field clear. + * 4) Access TX descriptors via host memory. The chip + * will refetch into local SRAM as needed when producer + * index mailboxes are updated. + * + * To use on-chip TX descriptors: + * 1) Set GRC_MODE_4X_NIC_SEND_RINGS in GRC_MODE register. + * Make sure GRC_MODE_HOST_SENDBDS is clear. + * 2) In NIC_SRAM_SEND_RCB (of desired index) of on-chip SRAM: + * a) Set TG3_BDINFO_HOST_ADDR to zero. + * b) Set TG3_BDINFO_NIC_ADDR to NIC_SRAM_TX_BUFFER_DESC + * c) TG3_BDINFO_MAXLEN_FLAGS is don't care. + * 3) Access TX descriptors directly in on-chip SRAM + * using normal {read,write}l(). (and not using + * pointer dereferencing of ioremap()'d memory like + * the broken Broadcom driver does) + * + * Note that BDINFO_FLAGS_DISABLED should be set in the flags field of + * TG3_BDINFO_MAXLEN_FLAGS of all unused SEND_RCB indices. + */ +struct tg3_tx_buffer_desc { + u32 addr_hi; + u32 addr_lo; + + u32 len_flags; +#define TXD_FLAG_TCPUDP_CSUM 0x0001 +#define TXD_FLAG_IP_CSUM 0x0002 +#define TXD_FLAG_END 0x0004 +#define TXD_FLAG_IP_FRAG 0x0008 +#define TXD_FLAG_IP_FRAG_END 0x0010 +#define TXD_FLAG_VLAN 0x0040 +#define TXD_FLAG_COAL_NOW 0x0080 +#define TXD_FLAG_CPU_PRE_DMA 0x0100 +#define TXD_FLAG_CPU_POST_DMA 0x0200 +#define TXD_FLAG_ADD_SRC_ADDR 0x1000 +#define TXD_FLAG_CHOOSE_SRC_ADDR 0x6000 +#define TXD_FLAG_NO_CRC 0x8000 +#define TXD_LEN_SHIFT 16 + + u32 vlan_tag; +#define TXD_VLAN_TAG_SHIFT 0 +}; + +#define TXD_ADDR 0x00UL /* 64-bit */ +#define TXD_LEN_FLAGS 0x08UL /* 32-bit (upper 16-bits are len) */ +#define TXD_VLAN_TAG 0x0cUL /* 32-bit (upper 16-bits are tag) */ +#define TXD_SIZE 0x10UL + +struct tg3_rx_buffer_desc { + u32 addr_hi; + u32 addr_lo; + + u32 idx_len; +#define RXD_IDX_MASK 0xffff0000 +#define RXD_IDX_SHIFT 16 +#define RXD_LEN_MASK 0x0000ffff +#define RXD_LEN_SHIFT 0 + + u32 type_flags; +#define RXD_TYPE_SHIFT 16 +#define RXD_FLAGS_SHIFT 0 + +#define RXD_FLAG_END 0x0004 +#if TG3_MINI_RING_WORKS +#define RXD_FLAG_MINI 0x0800 +#endif +#define RXD_FLAG_JUMBO 0x0020 +#define RXD_FLAG_VLAN 0x0040 +#define RXD_FLAG_ERROR 0x0400 +#define RXD_FLAG_IP_CSUM 0x1000 +#define RXD_FLAG_TCPUDP_CSUM 0x2000 +#define RXD_FLAG_IS_TCP 0x4000 + + u32 ip_tcp_csum; +#define RXD_IPCSUM_MASK 0xffff0000 +#define RXD_IPCSUM_SHIFT 16 +#define RXD_TCPCSUM_MASK 0x0000ffff +#define RXD_TCPCSUM_SHIFT 0 + + u32 err_vlan; + +#define RXD_VLAN_MASK 0x0000ffff + +#define RXD_ERR_BAD_CRC 0x00010000 +#define RXD_ERR_COLLISION 0x00020000 +#define RXD_ERR_LINK_LOST 0x00040000 +#define RXD_ERR_PHY_DECODE 0x00080000 +#define RXD_ERR_ODD_NIBBLE_RCVD_MII 0x00100000 +#define RXD_ERR_MAC_ABRT 0x00200000 +#define RXD_ERR_TOO_SMALL 0x00400000 +#define RXD_ERR_NO_RESOURCES 0x00800000 +#define RXD_ERR_HUGE_FRAME 0x01000000 +#define RXD_ERR_MASK 0xffff0000 + + u32 reserved; + u32 opaque; +#define RXD_OPAQUE_INDEX_MASK 0x0000ffff +#define RXD_OPAQUE_INDEX_SHIFT 0 +#define RXD_OPAQUE_RING_STD 0x00010000 +#define RXD_OPAQUE_RING_JUMBO 0x00020000 +#if TG3_MINI_RING_WORKS +#define RXD_OPAQUE_RING_MINI 0x00040000 +#endif +#define RXD_OPAQUE_RING_MASK 0x00070000 +}; + +struct tg3_ext_rx_buffer_desc { + struct { + u32 addr_hi; + u32 addr_lo; + } addrlist[3]; + u32 len2_len1; + u32 resv_len3; + struct tg3_rx_buffer_desc std; +}; + +/* We only use this when testing out the DMA engine + * at probe time. This is the internal format of buffer + * descriptors used by the chip at NIC_SRAM_DMA_DESCS. + */ +struct tg3_internal_buffer_desc { + u32 addr_hi; + u32 addr_lo; + u32 nic_mbuf; + /* XXX FIX THIS */ +#ifdef __BIG_ENDIAN + u16 cqid_sqid; + u16 len; +#else + u16 len; + u16 cqid_sqid; +#endif + u32 flags; + u32 __cookie1; + u32 __cookie2; + u32 __cookie3; +}; + +#define TG3_HW_STATUS_SIZE 0x80 +struct tg3_hw_status { + u32 status; +#define SD_STATUS_UPDATED 0x00000001 +#define SD_STATUS_LINK_CHG 0x00000002 +#define SD_STATUS_ERROR 0x00000004 + + u32 status_tag; + +#ifdef __BIG_ENDIAN + u16 rx_consumer; + u16 rx_jumbo_consumer; +#else + u16 rx_jumbo_consumer; + u16 rx_consumer; +#endif + +#ifdef __BIG_ENDIAN + u16 reserved; + u16 rx_mini_consumer; +#else + u16 rx_mini_consumer; + u16 reserved; +#endif + struct { +#ifdef __BIG_ENDIAN + u16 tx_consumer; + u16 rx_producer; +#else + u16 rx_producer; + u16 tx_consumer; +#endif + } idx[16]; +}; + +typedef struct { + u32 high, low; +} tg3_stat64_t; + +struct tg3_hw_stats { + u8 __reserved0[0x400-0x300]; + + /* Statistics maintained by Receive MAC. */ + tg3_stat64_t rx_octets; + u64 __reserved1; + tg3_stat64_t rx_fragments; + tg3_stat64_t rx_ucast_packets; + tg3_stat64_t rx_mcast_packets; + tg3_stat64_t rx_bcast_packets; + tg3_stat64_t rx_fcs_errors; + tg3_stat64_t rx_align_errors; + tg3_stat64_t rx_xon_pause_rcvd; + tg3_stat64_t rx_xoff_pause_rcvd; + tg3_stat64_t rx_mac_ctrl_rcvd; + tg3_stat64_t rx_xoff_entered; + tg3_stat64_t rx_frame_too_long_errors; + tg3_stat64_t rx_jabbers; + tg3_stat64_t rx_undersize_packets; + tg3_stat64_t rx_in_length_errors; + tg3_stat64_t rx_out_length_errors; + tg3_stat64_t rx_64_or_less_octet_packets; + tg3_stat64_t rx_65_to_127_octet_packets; + tg3_stat64_t rx_128_to_255_octet_packets; + tg3_stat64_t rx_256_to_511_octet_packets; + tg3_stat64_t rx_512_to_1023_octet_packets; + tg3_stat64_t rx_1024_to_1522_octet_packets; + tg3_stat64_t rx_1523_to_2047_octet_packets; + tg3_stat64_t rx_2048_to_4095_octet_packets; + tg3_stat64_t rx_4096_to_8191_octet_packets; + tg3_stat64_t rx_8192_to_9022_octet_packets; + + u64 __unused0[37]; + + /* Statistics maintained by Transmit MAC. */ + tg3_stat64_t tx_octets; + u64 __reserved2; + tg3_stat64_t tx_collisions; + tg3_stat64_t tx_xon_sent; + tg3_stat64_t tx_xoff_sent; + tg3_stat64_t tx_flow_control; + tg3_stat64_t tx_mac_errors; + tg3_stat64_t tx_single_collisions; + tg3_stat64_t tx_mult_collisions; + tg3_stat64_t tx_deferred; + u64 __reserved3; + tg3_stat64_t tx_excessive_collisions; + tg3_stat64_t tx_late_collisions; + tg3_stat64_t tx_collide_2times; + tg3_stat64_t tx_collide_3times; + tg3_stat64_t tx_collide_4times; + tg3_stat64_t tx_collide_5times; + tg3_stat64_t tx_collide_6times; + tg3_stat64_t tx_collide_7times; + tg3_stat64_t tx_collide_8times; + tg3_stat64_t tx_collide_9times; + tg3_stat64_t tx_collide_10times; + tg3_stat64_t tx_collide_11times; + tg3_stat64_t tx_collide_12times; + tg3_stat64_t tx_collide_13times; + tg3_stat64_t tx_collide_14times; + tg3_stat64_t tx_collide_15times; + tg3_stat64_t tx_ucast_packets; + tg3_stat64_t tx_mcast_packets; + tg3_stat64_t tx_bcast_packets; + tg3_stat64_t tx_carrier_sense_errors; + tg3_stat64_t tx_discards; + tg3_stat64_t tx_errors; + + u64 __unused1[31]; + + /* Statistics maintained by Receive List Placement. */ + tg3_stat64_t COS_rx_packets[16]; + tg3_stat64_t COS_rx_filter_dropped; + tg3_stat64_t dma_writeq_full; + tg3_stat64_t dma_write_prioq_full; + tg3_stat64_t rxbds_empty; + tg3_stat64_t rx_discards; + tg3_stat64_t rx_errors; + tg3_stat64_t rx_threshold_hit; + + u64 __unused2[9]; + + /* Statistics maintained by Send Data Initiator. */ + tg3_stat64_t COS_out_packets[16]; + tg3_stat64_t dma_readq_full; + tg3_stat64_t dma_read_prioq_full; + tg3_stat64_t tx_comp_queue_full; + + /* Statistics maintained by Host Coalescing. */ + tg3_stat64_t ring_set_send_prod_index; + tg3_stat64_t ring_status_update; + tg3_stat64_t nic_irqs; + tg3_stat64_t nic_avoided_irqs; + tg3_stat64_t nic_tx_threshold_hit; + + u8 __reserved4[0xb00-0x9c0]; +}; + +enum phy_led_mode { + led_mode_auto, + led_mode_three_link, + led_mode_link10 +}; + +/* 'mapping' is superfluous as the chip does not write into + * the tx/rx post rings so we could just fetch it from there. + * But the cache behavior is better how we are doing it now. + */ +struct ring_info { + struct sk_buff *skb; + dma_addr_t mapping; +}; + +struct tg3_config_info { + u32 flags; +}; + +struct tg3_link_config { + /* Describes what we're trying to get. */ + u32 advertising; + u16 speed; + u8 duplex; + u8 autoneg; + + /* Describes what we actually have. */ + u16 active_speed; + u8 active_duplex; +#define SPEED_INVALID 0xffff +#define DUPLEX_INVALID 0xff +#define AUTONEG_INVALID 0xff + + /* When we go in and out of low power mode we need + * to swap with this state. + */ + int phy_is_low_power; + u16 orig_speed; + u8 orig_duplex; + u8 orig_autoneg; +}; + +struct tg3_coalesce_config { + u32 rx_coalesce_ticks; + u32 rx_max_coalesced_frames; + u32 rx_coalesce_ticks_during_int; + u32 rx_max_coalesced_frames_during_int; + + u32 tx_coalesce_ticks; + u32 tx_max_coalesced_frames; + u32 tx_coalesce_ticks_during_int; + u32 tx_max_coalesced_frames_during_int; + + u32 stats_coalesce_ticks; +}; + +struct tg3_bufmgr_config { + u32 mbuf_read_dma_low_water; + u32 mbuf_mac_rx_low_water; + u32 mbuf_high_water; + + u32 mbuf_read_dma_low_water_jumbo; + u32 mbuf_mac_rx_low_water_jumbo; + u32 mbuf_high_water_jumbo; + + u32 dma_low_water; + u32 dma_high_water; +}; + +struct tg3 { + spinlock_t lock; + u32 tx_prod; + u32 tx_cons; + u32 rx_rcb_ptr; + u32 rx_std_ptr; + u32 rx_jumbo_ptr; +#if TG3_MINI_RING_WORKS + u32 rx_mini_ptr; +#endif + spinlock_t indirect_lock; + + struct net_device_stats net_stats; + unsigned long phy_crc_errors; + + /* Adaptive coalescing engine. */ + unsigned long last_rate_sample; + u32 last_rx_count; + u32 last_tx_count; + + u32 rx_offset; + u32 tg3_flags; +#define TG3_FLAG_HOST_TXDS 0x00000001 +#define TG3_FLAG_TXD_MBOX_HWBUG 0x00000002 +#define TG3_FLAG_BROKEN_CHECKSUMS 0x00000004 +#define TG3_FLAG_USE_LINKCHG_REG 0x00000008 +#define TG3_FLAG_USE_MI_INTERRUPT 0x00000010 +#define TG3_FLAG_PHY_RESET_ON_INIT 0x00000100 +#define TG3_FLAG_PCIX_TARGET_HWBUG 0x00000200 +#define TG3_FLAG_TAGGED_IRQ_STATUS 0x00000400 +#define TG3_FLAG_WOL_SPEED_100MB 0x00000800 +#define TG3_FLAG_WOL_ENABLE 0x00001000 +#define TG3_FLAG_NVRAM 0x00002000 +#define TG3_FLAG_NVRAM_BUFFERED 0x00004000 +#define TG3_FLAG_RX_PAUSE 0x00008000 +#define TG3_FLAG_TX_PAUSE 0x00010000 +#define TG3_FLAG_PCIX_MODE 0x00020000 +#define TG3_FLAG_PCI_HIGH_SPEED 0x00040000 +#define TG3_FLAG_PCI_32BIT 0x00080000 +#define TG3_FLAG_NO_TX_PSEUDO_CSUM 0x00100000 +#define TG3_FLAG_NO_RX_PSEUDO_CSUM 0x00200000 +#define TG3_FLAG_AUTONEG_DISABLE 0x00400000 +#define TG3_FLAG_JUMBO_ENABLE 0x00800000 +#define TG3_FLAG_10_100_ONLY 0x01000000 +#define TG3_FLAG_INIT_COMPLETE 0x80000000 + + u32 msg_enable; + + struct timer_list timer; + u16 timer_counter; + u16 timer_multiplier; + u32 timer_offset; + + struct tg3_link_config link_config; + struct tg3_coalesce_config coalesce_config; + struct tg3_bufmgr_config bufmgr_config; + + /* cache h/w values, often passed straight to h/w */ + u32 rx_mode; + u32 tx_mode; + u32 mac_mode; + u32 mi_mode; + u32 misc_host_ctrl; + u32 grc_mode; + u32 grc_local_ctrl; + u32 dma_rwctrl; + u32 coalesce_mode; + + /* PCI block */ + u16 pci_chip_rev_id; + u8 pci_cacheline_sz; + u8 pci_lat_timer; + u8 pci_hdr_type; + u8 pci_bist; + u32 pci_cfg_state[64 / sizeof(u32)]; + + int pm_cap; + + /* PHY info */ + u32 phy_id; +#define PHY_ID_MASK 0xfffffff0 +#define PHY_ID_BCM5400 0x60008040 +#define PHY_ID_BCM5401 0x60008050 +#define PHY_ID_BCM5411 0x60008070 +#define PHY_ID_BCM5701 0x60008110 +#define PHY_ID_BCM5703 0x60008160 +#define PHY_ID_BCM8002 0x60010140 +#define PHY_ID_SERDES 0xfeedbee0 +#define PHY_ID_INVALID 0xffffffff +#define PHY_ID_REV_MASK 0x0000000f +#define PHY_REV_BCM5401_B0 0x1 +#define PHY_REV_BCM5401_B2 0x3 +#define PHY_REV_BCM5401_C0 0x6 + + enum phy_led_mode led_mode; + + char board_part_number[24]; + + /* This macro assumes the passed PHY ID is already masked + * with PHY_ID_MASK. + */ +#define KNOWN_PHY_ID(X) \ + ((X) == PHY_ID_BCM5400 || (X) == PHY_ID_BCM5401 || \ + (X) == PHY_ID_BCM5411 || (X) == PHY_ID_BCM5701 || \ + (X) == PHY_ID_BCM5703 || \ + (X) == PHY_ID_BCM8002 || (X) == PHY_ID_SERDES) + + unsigned long regs; + struct pci_dev *pdev; + struct net_device *dev; +#if TG3_VLAN_TAG_USED + struct vlan_group *vlgrp; +#endif + + struct tg3_rx_buffer_desc *rx_std; + struct ring_info *rx_std_buffers; + dma_addr_t rx_std_mapping; +#if TG3_MINI_RING_WORKS + struct tg3_rx_buffer_desc *rx_mini; + struct ring_info *rx_mini_buffers; + dma_addr_t rx_mini_mapping; +#endif + struct tg3_rx_buffer_desc *rx_jumbo; + struct ring_info *rx_jumbo_buffers; + dma_addr_t rx_jumbo_mapping; + + struct tg3_rx_buffer_desc *rx_rcb; + dma_addr_t rx_rcb_mapping; + + /* TX descs are only used if TG3_FLAG_HOST_TXDS is set. */ + struct tg3_tx_buffer_desc *tx_ring; + struct ring_info *tx_buffers; + dma_addr_t tx_desc_mapping; + + struct tg3_hw_status *hw_status; + dma_addr_t status_mapping; + + struct tg3_hw_stats *hw_stats; + dma_addr_t stats_mapping; +}; + +#endif /* !(_T3_H) */ diff -Nru a/drivers/net/tlan.c b/drivers/net/tlan.c --- a/drivers/net/tlan.c Thu Mar 7 18:17:38 2002 +++ b/drivers/net/tlan.c Thu Mar 7 18:17:38 2002 @@ -431,7 +431,7 @@ name: "tlan", id_table: tlan_pci_tbl, probe: tlan_init_one, - remove: tlan_remove_one, + remove: __devexit_p(tlan_remove_one), }; static int __init tlan_probe(void) diff -Nru a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/tokenring/3c359.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,1816 @@ +/* + * 3c359.c (c) 2000 Mike Phillips (mikep@linuxtr.net) All Rights Reserved + * + * Linux driver for 3Com 3c359 Tokenlink Velocity XL PCI NIC + * + * Base Driver Olympic: + * Written 1999 Peter De Schrijver & Mike Phillips + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * 7/17/00 - Clean up, version number 0.9.0. Ready to release to the world. + * + * 2/16/01 - Port up to kernel 2.4.2 ready for submission into the kernel. + * 3/05/01 - Last clean up stuff before submission. + * 2/15/01 - Finally, update to new pci api. + * + * To Do: + */ + +/* + * Technical Card Details + * + * All access to data is done with 16/8 bit transfers. The transfer + * method really sucks. You can only read or write one location at a time. + * + * Also, the microcode for the card must be uploaded if the card does not have + * the flashrom on board. This is a 28K bloat in the driver when compiled + * as a module. + * + * Rx is very simple, status into a ring of descriptors, dma data transfer, + * interrupts to tell us when a packet is received. + * + * Tx is a little more interesting. Similar scenario, descriptor and dma data + * transfers, but we don't have to interrupt the card to tell it another packet + * is ready for transmission, we are just doing simple memory writes, not io or mmio + * writes. The card can be set up to simply poll on the next + * descriptor pointer and when this value is non-zero will automatically download + * the next packet. The card then interrupts us when the packet is done. + * + */ + +#define XL_DEBUG 0 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "3c359.h" + +static char version[] __devinitdata = +"3c359.c v1.2.0 2/17/01 - Mike Phillips (mikep@linuxtr.net)" ; + +MODULE_AUTHOR("Mike Phillips ") ; +MODULE_DESCRIPTION("3Com 3C359 Velocity XL Token Ring Adapter Driver \n") ; + +/* Module paramters */ + +/* Ring Speed 0,4,16 + * 0 = Autosense + * 4,16 = Selected speed only, no autosense + * This allows the card to be the first on the ring + * and become the active monitor. + * + * WARNING: Some hubs will allow you to insert + * at the wrong speed. + * + * The adapter will _not_ fail to open if there are no + * active monitors on the ring, it will simply open up in + * its last known ringspeed if no ringspeed is specified. + */ + +static int ringspeed[XL_MAX_ADAPTERS] = {0,} ; + +MODULE_PARM(ringspeed, "1-" __MODULE_STRING(XL_MAX_ADAPTERS) "i"); +MODULE_PARM_DESC(ringspeed,"3c359: Ringspeed selection - 4,16 or 0") ; + +/* Packet buffer size */ + +static int pkt_buf_sz[XL_MAX_ADAPTERS] = {0,} ; + +MODULE_PARM(pkt_buf_sz, "1-" __MODULE_STRING(XL_MAX_ADAPTERS) "i") ; +MODULE_PARM_DESC(pkt_buf_sz,"3c359: Initial buffer size") ; +/* Message Level */ + +static int message_level[XL_MAX_ADAPTERS] = {0,} ; + +MODULE_PARM(message_level, "1-" __MODULE_STRING(XL_MAX_ADAPTERS) "i") ; +MODULE_PARM_DESC(message_level, "3c359: Level of reported messages \n") ; +/* + * This is a real nasty way of doing this, but otherwise you + * will be stuck with 1555 lines of hex #'s in the code. + */ + +#include "3c359_microcode.h" + +static struct pci_device_id xl_pci_tbl[] __devinitdata = +{ + {PCI_VENDOR_ID_3COM,PCI_DEVICE_ID_3COM_3C359, PCI_ANY_ID, PCI_ANY_ID, }, + { } /* terminate list */ +}; +MODULE_DEVICE_TABLE(pci,xl_pci_tbl) ; + +static int xl_init(struct net_device *dev); +static int xl_open(struct net_device *dev); +static int xl_open_hw(struct net_device *dev) ; +static int xl_hw_reset(struct net_device *dev); +static int xl_xmit(struct sk_buff *skb, struct net_device *dev); +static void xl_dn_comp(struct net_device *dev); +static int xl_close(struct net_device *dev); +static void xl_set_rx_mode(struct net_device *dev); +static void xl_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static struct net_device_stats * xl_get_stats(struct net_device *dev); +static int xl_set_mac_address(struct net_device *dev, void *addr) ; +static void xl_arb_cmd(struct net_device *dev); +static void xl_asb_cmd(struct net_device *dev) ; +static void xl_srb_cmd(struct net_device *dev, int srb_cmd) ; +static void xl_wait_misr_flags(struct net_device *dev) ; +static int xl_change_mtu(struct net_device *dev, int mtu); +static void xl_srb_bh(struct net_device *dev) ; +static void xl_asb_bh(struct net_device *dev) ; +static void xl_reset(struct net_device *dev) ; +static void xl_freemem(struct net_device *dev) ; + + +/* EEProm Access Functions */ +static u16 xl_ee_read(struct net_device *dev, int ee_addr) ; +static void xl_ee_write(struct net_device *dev, int ee_addr, u16 ee_value) ; + +/* Debugging functions */ +#if XL_DEBUG +static void print_tx_state(struct net_device *dev) ; +static void print_rx_state(struct net_device *dev) ; + +static void print_tx_state(struct net_device *dev) +{ + + struct xl_private *xl_priv = (struct xl_private *)dev->priv ; + struct xl_tx_desc *txd ; + u8 *xl_mmio = xl_priv->xl_mmio ; + int i ; + + printk("tx_ring_head: %d, tx_ring_tail: %d, free_ent: %d \n",xl_priv->tx_ring_head, + xl_priv->tx_ring_tail, xl_priv->free_ring_entries) ; + printk("Ring , Address , FSH , DnNextPtr, Buffer, Buffer_Len \n"); + for (i = 0; i < 16; i++) { + txd = &(xl_priv->xl_tx_ring[i]) ; + printk("%d, %08lx, %08x, %08x, %08x, %08x \n", i, virt_to_bus(txd), + txd->framestartheader, txd->dnnextptr, txd->buffer, txd->buffer_length ) ; + } + + printk("DNLISTPTR = %04x \n", readl(xl_mmio + MMIO_DNLISTPTR) ); + + printk("DmaCtl = %04x \n", readl(xl_mmio + MMIO_DMA_CTRL) ); + printk("Queue status = %0x \n",netif_running(dev) ) ; +} + +static void print_rx_state(struct net_device *dev) +{ + + struct xl_private *xl_priv = (struct xl_private *)dev->priv ; + struct xl_rx_desc *rxd ; + u8 *xl_mmio = xl_priv->xl_mmio ; + int i ; + + printk("rx_ring_tail: %d \n", xl_priv->rx_ring_tail) ; + printk("Ring , Address , FrameState , UPNextPtr, FragAddr, Frag_Len \n"); + for (i = 0; i < 16; i++) { + /* rxd = (struct xl_rx_desc *)xl_priv->rx_ring_dma_addr + (i * sizeof(struct xl_rx_desc)) ; */ + rxd = &(xl_priv->xl_rx_ring[i]) ; + printk("%d, %08lx, %08x, %08x, %08x, %08x \n", i, virt_to_bus(rxd), + rxd->framestatus, rxd->upnextptr, rxd->upfragaddr, rxd->upfraglen ) ; + } + + printk("UPLISTPTR = %04x \n", readl(xl_mmio + MMIO_UPLISTPTR) ); + + printk("DmaCtl = %04x \n", readl(xl_mmio + MMIO_DMA_CTRL) ); + printk("Queue status = %0x \n",netif_running(dev) ) ; +} +#endif + +/* + * Read values from the on-board EEProm. This looks very strange + * but you have to wait for the EEProm to get/set the value before + * passing/getting the next value from the nic. As with all requests + * on this nic it has to be done in two stages, a) tell the nic which + * memory address you want to access and b) pass/get the value from the nic. + * With the EEProm, you have to wait before and inbetween access a) and b). + * As this is only read at initialization time and the wait period is very + * small we shouldn't have to worry about scheduling issues. + */ + +static u16 xl_ee_read(struct net_device *dev, int ee_addr) +{ + struct xl_private *xl_priv = (struct xl_private *)dev->priv ; + u8 *xl_mmio = xl_priv->xl_mmio ; + + /* Wait for EEProm to not be busy */ + writel(IO_WORD_READ | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + while ( readw(xl_mmio + MMIO_MACDATA) & EEBUSY ) ; + + /* Tell EEProm what we want to do and where */ + writel(IO_WORD_WRITE | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writew(EEREAD + ee_addr, xl_mmio + MMIO_MACDATA) ; + + /* Wait for EEProm to not be busy */ + writel(IO_WORD_READ | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + while ( readw(xl_mmio + MMIO_MACDATA) & EEBUSY ) ; + + /* Tell EEProm what we want to do and where */ + writel(IO_WORD_WRITE | EECONTROL , xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writew(EEREAD + ee_addr, xl_mmio + MMIO_MACDATA) ; + + /* Finally read the value from the EEProm */ + writel(IO_WORD_READ | EEDATA , xl_mmio + MMIO_MAC_ACCESS_CMD) ; + return readw(xl_mmio + MMIO_MACDATA) ; +} + +/* + * Write values to the onboard eeprom. As with eeprom read you need to + * set which location to write, wait, value to write, wait, with the + * added twist of having to enable eeprom writes as well. + */ + +static void xl_ee_write(struct net_device *dev, int ee_addr, u16 ee_value) +{ + struct xl_private *xl_priv = (struct xl_private *)dev->priv ; + u8 *xl_mmio = xl_priv->xl_mmio ; + + /* Wait for EEProm to not be busy */ + writel(IO_WORD_READ | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + while ( readw(xl_mmio + MMIO_MACDATA) & EEBUSY ) ; + + /* Enable write/erase */ + writel(IO_WORD_WRITE | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writew(EE_ENABLE_WRITE, xl_mmio + MMIO_MACDATA) ; + + /* Wait for EEProm to not be busy */ + writel(IO_WORD_READ | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + while ( readw(xl_mmio + MMIO_MACDATA) & EEBUSY ) ; + + /* Put the value we want to write into EEDATA */ + writel(IO_WORD_WRITE | EEDATA, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writew(ee_value, xl_mmio + MMIO_MACDATA) ; + + /* Tell EEProm to write eevalue into ee_addr */ + writel(IO_WORD_WRITE | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writew(EEWRITE + ee_addr, xl_mmio + MMIO_MACDATA) ; + + /* Wait for EEProm to not be busy, to ensure write gets done */ + writel(IO_WORD_READ | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + while ( readw(xl_mmio + MMIO_MACDATA) & EEBUSY ) ; + + return ; +} + +int __devinit xl_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct net_device *dev ; + struct xl_private *xl_priv ; + static int card_no = -1 ; + int i ; + + card_no++ ; + + if (pci_enable_device(pdev)) { + return -ENODEV ; + } + + pci_set_master(pdev); + + if ((i = pci_request_regions(pdev,"3c359"))) { + return i ; + } ; + + /* + * Allowing init_trdev to allocate the dev->priv structure will align xl_private + * on a 32 bytes boundary which we need for the rx/tx descriptors + */ + + dev = alloc_trdev(sizeof(struct xl_private)) ; + if (!dev) { + pci_release_regions(pdev) ; + return -ENOMEM ; + } + xl_priv = dev->priv ; + +#if XL_DEBUG + printk("pci_device: %p, dev:%p, dev->priv: %p, ba[0]: %10x, ba[1]:%10x\n", + pdev, dev, dev->priv, (unsigned int)pdev->resource[0].start, (unsigned int)pdev->resource[1].start) ; +#endif + + dev->irq=pdev->irq; + dev->base_addr=pci_resource_start(pdev,0) ; + dev->init=NULL ; /* Must be null with new api, otherwise get called twice */ + xl_priv->xl_card_name = (char *)pdev->name ; + xl_priv->xl_mmio=ioremap(pci_resource_start(pdev,1), XL_IO_SPACE); + xl_priv->pdev = pdev ; + + if ((pkt_buf_sz[card_no] < 100) || (pkt_buf_sz[card_no] > 18000) ) + xl_priv->pkt_buf_sz = PKT_BUF_SZ ; + else + xl_priv->pkt_buf_sz = pkt_buf_sz[card_no] ; + + dev->mtu = xl_priv->pkt_buf_sz - TR_HLEN ; + xl_priv->xl_ring_speed = ringspeed[card_no] ; + xl_priv->xl_message_level = message_level[card_no] ; + xl_priv->xl_functional_addr[0] = xl_priv->xl_functional_addr[1] = xl_priv->xl_functional_addr[2] = xl_priv->xl_functional_addr[3] = 0 ; + xl_priv->xl_copy_all_options = 0 ; + + if((i = xl_init(dev))) { + iounmap(xl_priv->xl_mmio) ; + kfree(dev) ; + pci_release_regions(pdev) ; + return i ; + } + + dev->open=&xl_open; + dev->hard_start_xmit=&xl_xmit; + dev->change_mtu=&xl_change_mtu; + dev->stop=&xl_close; + dev->do_ioctl=NULL; + dev->set_multicast_list=&xl_set_rx_mode; + dev->get_stats=&xl_get_stats ; + dev->set_mac_address=&xl_set_mac_address ; + SET_MODULE_OWNER(dev); + + pci_set_drvdata(pdev,dev) ; + if ((i = register_netdev(dev))) { + printk(KERN_ERR "3C359, register netdev failed\n") ; + pci_set_drvdata(pdev,NULL) ; + iounmap(xl_priv->xl_mmio) ; + kfree(dev) ; + pci_release_regions(pdev) ; + return i ; + } + + printk(KERN_INFO "3C359: %s registered as: %s\n",xl_priv->xl_card_name,dev->name) ; + + return 0; +} + + +static int __init xl_init(struct net_device *dev) +{ + struct xl_private *xl_priv = (struct xl_private *)dev->priv ; + + printk(KERN_INFO "%s \n", version); + printk(KERN_INFO "%s: I/O at %hx, MMIO at %p, using irq %d\n", + xl_priv->xl_card_name, (unsigned int)dev->base_addr ,xl_priv->xl_mmio, dev->irq); + + spin_lock_init(&xl_priv->xl_lock) ; + + return xl_hw_reset(dev) ; + +} + + +/* + * Hardware reset. This needs to be a separate entity as we need to reset the card + * when we change the EEProm settings. + */ + +static int xl_hw_reset(struct net_device *dev) +{ + struct xl_private *xl_priv = (struct xl_private *)dev->priv ; + u8 *xl_mmio = xl_priv->xl_mmio ; + unsigned long t ; + u16 i ; + u16 result_16 ; + u8 result_8 ; + u16 start ; + int j ; + + /* + * Reset the card. If the card has got the microcode on board, we have + * missed the initialization interrupt, so we must always do this. + */ + + writew( GLOBAL_RESET, xl_mmio + MMIO_COMMAND ) ; + + /* + * Must wait for cmdInProgress bit (12) to clear before continuing with + * card configuration. + */ + + t=jiffies; + while (readw(xl_mmio + MMIO_INTSTATUS) & INTSTAT_CMD_IN_PROGRESS) { + schedule(); + if(jiffies-t > 40*HZ) { + printk(KERN_ERR "%s: 3COM 3C359 Velocity XL card not responding to global reset.\n", dev->name); + return -ENODEV; + } + } + + /* + * Enable pmbar by setting bit in CPAttention + */ + + writel( (IO_BYTE_READ | CPATTENTION), xl_mmio + MMIO_MAC_ACCESS_CMD) ; + result_8 = readb(xl_mmio + MMIO_MACDATA) ; + result_8 = result_8 | CPA_PMBARVIS ; + writel( (IO_BYTE_WRITE | CPATTENTION), xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(result_8, xl_mmio + MMIO_MACDATA) ; + + /* + * Read cpHold bit in pmbar, if cleared we have got Flashrom on board. + * If not, we need to upload the microcode to the card + */ + + writel( (IO_WORD_READ | PMBAR),xl_mmio + MMIO_MAC_ACCESS_CMD); + +#if XL_DEBUG + printk(KERN_INFO "Read from PMBAR = %04x \n", readw(xl_mmio + MMIO_MACDATA)) ; +#endif + + if ( readw( (xl_mmio + MMIO_MACDATA)) & PMB_CPHOLD ) { + + /* Set PmBar, privateMemoryBase bits (8:2) to 0 */ + + writel( (IO_WORD_READ | PMBAR),xl_mmio + MMIO_MAC_ACCESS_CMD); + result_16 = readw(xl_mmio + MMIO_MACDATA) ; + result_16 = result_16 & ~((0x7F) << 2) ; + writel( (IO_WORD_WRITE | PMBAR), xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writew(result_16,xl_mmio + MMIO_MACDATA) ; + + /* Set CPAttention, memWrEn bit */ + + writel( (IO_BYTE_READ | CPATTENTION), xl_mmio + MMIO_MAC_ACCESS_CMD) ; + result_8 = readb(xl_mmio + MMIO_MACDATA) ; + result_8 = result_8 | CPA_MEMWREN ; + writel( (IO_BYTE_WRITE | CPATTENTION), xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(result_8, xl_mmio + MMIO_MACDATA) ; + + /* + * Now to write the microcode into the shared ram + * The microcode must finish at position 0xFFFF, so we must subtract + * to get the start position for the code + */ + + start = (0xFFFF - (mc_size) + 1 ) ; /* Looks strange but ensures compiler only uses 16 bit unsigned int for this */ + + printk(KERN_INFO "3C359: Uploading Microcode: "); + + for (i = start,j=0; (j < mc_size && i <= 0xffff) ; i++,j++) { + writel(MEM_BYTE_WRITE | 0XD0000 | i, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(microcode[j],xl_mmio + MMIO_MACDATA) ; + if (j % 1024 == 0) + printk("."); + } + printk("\n") ; + + for (i=0;i < 16; i++) { + writel( (MEM_BYTE_WRITE | 0xDFFF0) + i, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(microcode[mc_size - 16 + i], xl_mmio + MMIO_MACDATA) ; + } + + /* + * Have to write the start address of the upload to FFF4, but + * the address must be >> 4. You do not want to know how long + * it took me to discover this. + */ + + writel(MEM_WORD_WRITE | 0xDFFF4, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writew(start >> 4, xl_mmio + MMIO_MACDATA); + + /* Clear the CPAttention, memWrEn Bit */ + + writel( (IO_BYTE_READ | CPATTENTION), xl_mmio + MMIO_MAC_ACCESS_CMD) ; + result_8 = readb(xl_mmio + MMIO_MACDATA) ; + result_8 = result_8 & ~CPA_MEMWREN ; + writel( (IO_BYTE_WRITE | CPATTENTION), xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(result_8, xl_mmio + MMIO_MACDATA) ; + + /* Clear the cpHold bit in pmbar */ + + writel( (IO_WORD_READ | PMBAR),xl_mmio + MMIO_MAC_ACCESS_CMD); + result_16 = readw(xl_mmio + MMIO_MACDATA) ; + result_16 = result_16 & ~PMB_CPHOLD ; + writel( (IO_WORD_WRITE | PMBAR), xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writew(result_16,xl_mmio + MMIO_MACDATA) ; + + + } /* If microcode upload required */ + + /* + * The card should now go though a self test procedure and get itself ready + * to be opened, we must wait for an srb response with the initialization + * information. + */ + +#if XL_DEBUG + printk(KERN_INFO "%s: Microcode uploaded, must wait for the self test to complete\n", dev->name); +#endif + + writew(SETINDENABLE | 0xFFF, xl_mmio + MMIO_COMMAND) ; + + t=jiffies; + while ( !(readw(xl_mmio + MMIO_INTSTATUS_AUTO) & INTSTAT_SRB) ) { + schedule(); + if(jiffies-t > 15*HZ) { + printk(KERN_ERR "3COM 3C359 Velocity XL card not responding.\n"); + return -ENODEV; + } + } + + /* + * Write the RxBufArea with D000, RxEarlyThresh, TxStartThresh, + * DnPriReqThresh, read the tech docs if you want to know what + * values they need to be. + */ + + writel(MMIO_WORD_WRITE | RXBUFAREA, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writew(0xD000, xl_mmio + MMIO_MACDATA) ; + + writel(MMIO_WORD_WRITE | RXEARLYTHRESH, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writew(0X0020, xl_mmio + MMIO_MACDATA) ; + + writew( SETTXSTARTTHRESH | 0x40 , xl_mmio + MMIO_COMMAND) ; + + writeb(0x04, xl_mmio + MMIO_DNBURSTTHRESH) ; + writeb(0x04, xl_mmio + DNPRIREQTHRESH) ; + + /* + * Read WRBR to provide the location of the srb block, have to use byte reads not word reads. + * Tech docs have this wrong !!!! + */ + + writel(MMIO_BYTE_READ | WRBR, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + xl_priv->srb = readb(xl_mmio + MMIO_MACDATA) << 8 ; + writel( (MMIO_BYTE_READ | WRBR) + 1, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + xl_priv->srb = xl_priv->srb | readb(xl_mmio + MMIO_MACDATA) ; + +#if XL_DEBUG + writel(IO_WORD_READ | SWITCHSETTINGS, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + if ( readw(xl_mmio + MMIO_MACDATA) & 2) { + printk(KERN_INFO "Default ring speed 4 mbps \n") ; + } else { + printk(KERN_INFO "Default ring speed 16 mbps \n") ; + } + printk(KERN_INFO "%s: xl_priv->srb = %04x\n",xl_priv->xl_card_name, xl_priv->srb); +#endif + + return 0; +} + +static int xl_open(struct net_device *dev) +{ + struct xl_private *xl_priv=(struct xl_private *)dev->priv; + u8 * xl_mmio = xl_priv->xl_mmio ; + u8 i ; + u16 hwaddr[3] ; /* Should be u8[6] but we get word return values */ + int open_err ; + + u16 switchsettings, switchsettings_eeprom ; + + if(request_irq(dev->irq, &xl_interrupt, SA_SHIRQ , "3c359", dev)) { + return -EAGAIN; + } + + /* + * Read the information from the EEPROM that we need. I know we + * should use ntohs, but the word gets stored reversed in the 16 + * bit field anyway and it all works its self out when we memcpy + * it into dev->dev_addr. + */ + + hwaddr[0] = xl_ee_read(dev,0x10) ; + hwaddr[1] = xl_ee_read(dev,0x11) ; + hwaddr[2] = xl_ee_read(dev,0x12) ; + + /* Ring speed */ + + switchsettings_eeprom = xl_ee_read(dev,0x08) ; + switchsettings = switchsettings_eeprom ; + + if (xl_priv->xl_ring_speed != 0) { + if (xl_priv->xl_ring_speed == 4) + switchsettings = switchsettings | 0x02 ; + else + switchsettings = switchsettings & ~0x02 ; + } + + /* Only write EEProm if there has been a change */ + if (switchsettings != switchsettings_eeprom) { + xl_ee_write(dev,0x08,switchsettings) ; + /* Hardware reset after changing EEProm */ + xl_hw_reset(dev) ; + } + + memcpy(dev->dev_addr,hwaddr,dev->addr_len) ; + + open_err = xl_open_hw(dev) ; + + /* + * This really needs to be cleaned up with better error reporting. + */ + + if (open_err != 0) { /* Something went wrong with the open command */ + if (open_err & 0x07) { /* Wrong speed, retry at different speed */ + printk(KERN_WARNING "%s: Open Error, retrying at different ringspeed \n", dev->name) ; + switchsettings = switchsettings ^ 2 ; + xl_ee_write(dev,0x08,switchsettings) ; + xl_hw_reset(dev) ; + open_err = xl_open_hw(dev) ; + if (open_err != 0) { + printk(KERN_WARNING "%s: Open error returned a second time, we're bombing out now\n", dev->name); + free_irq(dev->irq,dev) ; + return -ENODEV ; + } + } else { + printk(KERN_WARNING "%s: Open Error = %04x\n", dev->name, open_err) ; + free_irq(dev->irq,dev) ; + return -ENODEV ; + } + } + + /* + * Now to set up the Rx and Tx buffer structures + */ + /* These MUST be on 8 byte boundaries */ + xl_priv->xl_tx_ring = kmalloc((sizeof(struct xl_tx_desc) * XL_TX_RING_SIZE) + 7, GFP_DMA | GFP_KERNEL) ; + xl_priv->xl_rx_ring = kmalloc((sizeof(struct xl_rx_desc) * XL_RX_RING_SIZE) +7, GFP_DMA | GFP_KERNEL) ; + memset(xl_priv->xl_tx_ring,0,sizeof(struct xl_tx_desc) * XL_TX_RING_SIZE) ; + memset(xl_priv->xl_rx_ring,0,sizeof(struct xl_rx_desc) * XL_RX_RING_SIZE) ; + + /* Setup Rx Ring */ + for (i=0 ; i < XL_RX_RING_SIZE ; i++) { + struct sk_buff *skb ; + + skb = dev_alloc_skb(xl_priv->pkt_buf_sz) ; + if (skb==NULL) + break ; + + skb->dev = dev ; + xl_priv->xl_rx_ring[i].upfragaddr = pci_map_single(xl_priv->pdev, skb->data,xl_priv->pkt_buf_sz, PCI_DMA_FROMDEVICE) ; + xl_priv->xl_rx_ring[i].upfraglen = xl_priv->pkt_buf_sz | RXUPLASTFRAG; + xl_priv->rx_ring_skb[i] = skb ; + } + + if (i==0) { + printk(KERN_WARNING "%s: Not enough memory to allocate rx buffers. Adapter disabled \n",dev->name) ; + free_irq(dev->irq,dev) ; + return -EIO ; + } + + xl_priv->rx_ring_no = i ; + xl_priv->rx_ring_tail = 0 ; + xl_priv->rx_ring_dma_addr = pci_map_single(xl_priv->pdev,xl_priv->xl_rx_ring, sizeof(struct xl_rx_desc) * XL_RX_RING_SIZE, PCI_DMA_TODEVICE) ; + for (i=0;i<(xl_priv->rx_ring_no-1);i++) { + xl_priv->xl_rx_ring[i].upnextptr = xl_priv->rx_ring_dma_addr + (sizeof (struct xl_rx_desc) * (i+1)) ; + } + xl_priv->xl_rx_ring[i].upnextptr = 0 ; + + writel(xl_priv->rx_ring_dma_addr, xl_mmio + MMIO_UPLISTPTR) ; + + /* Setup Tx Ring */ + + xl_priv->tx_ring_dma_addr = pci_map_single(xl_priv->pdev,xl_priv->xl_tx_ring, sizeof(struct xl_tx_desc) * XL_TX_RING_SIZE,PCI_DMA_TODEVICE) ; + + xl_priv->tx_ring_head = 1 ; + xl_priv->tx_ring_tail = 255 ; /* Special marker for first packet */ + xl_priv->free_ring_entries = XL_TX_RING_SIZE ; + + /* + * Setup the first dummy DPD entry for polling to start working. + */ + + xl_priv->xl_tx_ring[0].framestartheader = TXDPDEMPTY ; + xl_priv->xl_tx_ring[0].buffer = 0 ; + xl_priv->xl_tx_ring[0].buffer_length = 0 ; + xl_priv->xl_tx_ring[0].dnnextptr = 0 ; + + writel(xl_priv->tx_ring_dma_addr, xl_mmio + MMIO_DNLISTPTR) ; + writel(DNUNSTALL, xl_mmio + MMIO_COMMAND) ; + writel(UPUNSTALL, xl_mmio + MMIO_COMMAND) ; + writel(DNENABLE, xl_mmio + MMIO_COMMAND) ; + writeb(0x40, xl_mmio + MMIO_DNPOLL) ; + + /* + * Enable interrupts on the card + */ + + writel(SETINTENABLE | INT_MASK, xl_mmio + MMIO_COMMAND) ; + writel(SETINDENABLE | INT_MASK, xl_mmio + MMIO_COMMAND) ; + + netif_start_queue(dev) ; + return 0; + +} + +static int xl_open_hw(struct net_device *dev) +{ + struct xl_private *xl_priv=(struct xl_private *)dev->priv; + u8 * xl_mmio = xl_priv->xl_mmio ; + u16 vsoff ; + char ver_str[33]; + int open_err ; + int i ; + unsigned long t ; + + /* + * Okay, let's build up the Open.NIC srb command + * + */ + + writel( (MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb), xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(OPEN_NIC, xl_mmio + MMIO_MACDATA) ; + + /* + * Use this as a test byte, if it comes back with the same value, the command didn't work + */ + + writel( (MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb)+ 2, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(0xff,xl_mmio + MMIO_MACDATA) ; + + /* Open options */ + writel( (MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb) + 8, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(0x00, xl_mmio + MMIO_MACDATA) ; + writel( (MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb) + 9, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(0x00, xl_mmio + MMIO_MACDATA) ; + + /* + * Node address, be careful here, the docs say you can just put zeros here and it will use + * the hardware address, it doesn't, you must include the node address in the open command. + */ + + if (xl_priv->xl_laa[0]) { /* If using a LAA address */ + for (i=10;i<16;i++) { + writel( (MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb) + i, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(xl_priv->xl_laa[i],xl_mmio + MMIO_MACDATA) ; + } + memcpy(dev->dev_addr,xl_priv->xl_laa,dev->addr_len) ; + } else { /* Regular hardware address */ + for (i=10;i<16;i++) { + writel( (MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb) + i, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(dev->dev_addr[i-10], xl_mmio + MMIO_MACDATA) ; + } + } + + /* Default everything else to 0 */ + for (i = 16; i < 34; i++) { + writel( (MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb) + i, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(0x00,xl_mmio + MMIO_MACDATA) ; + } + + /* + * Set the csrb bit in the MISR register + */ + + xl_wait_misr_flags(dev) ; + writel(MEM_BYTE_WRITE | MF_CSRB, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(0xFF, xl_mmio + MMIO_MACDATA) ; + writel(MMIO_BYTE_WRITE | MISR_SET, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(MISR_CSRB , xl_mmio + MMIO_MACDATA) ; + + /* + * Now wait for the command to run + */ + + t=jiffies; + while (! (readw(xl_mmio + MMIO_INTSTATUS) & INTSTAT_SRB)) { + schedule(); + if(jiffies-t > 40*HZ) { + printk(KERN_ERR "3COM 3C359 Velocity XL card not responding.\n"); + break ; + } + } + + /* + * Let's interpret the open response + */ + + writel( (MEM_BYTE_READ | 0xD0000 | xl_priv->srb)+2, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + if (readb(xl_mmio + MMIO_MACDATA)!=0) { + open_err = readb(xl_mmio + MMIO_MACDATA) << 8 ; + writel( (MEM_BYTE_READ | 0xD0000 | xl_priv->srb) + 7, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + open_err |= readb(xl_mmio + MMIO_MACDATA) ; + return open_err ; + } else { + writel( (MEM_WORD_READ | 0xD0000 | xl_priv->srb) + 8, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + xl_priv->asb = ntohs(readw(xl_mmio + MMIO_MACDATA)) ; + printk(KERN_INFO "%s: Adapter Opened Details: ",dev->name) ; + printk("ASB: %04x",xl_priv->asb ) ; + writel( (MEM_WORD_READ | 0xD0000 | xl_priv->srb) + 10, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + printk(", SRB: %04x",ntohs(readw(xl_mmio + MMIO_MACDATA)) ) ; + + writel( (MEM_WORD_READ | 0xD0000 | xl_priv->srb) + 12, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + xl_priv->arb = ntohs(readw(xl_mmio + MMIO_MACDATA)) ; + printk(", ARB: %04x \n",xl_priv->arb ) ; + writel( (MEM_WORD_READ | 0xD0000 | xl_priv->srb) + 14, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + vsoff = ntohs(readw(xl_mmio + MMIO_MACDATA)) ; + + /* + * Interesting, sending the individual characters directly to printk was causing klogd to use + * use 100% of processor time, so we build up the string and print that instead. + */ + + for (i=0;i<0x20;i++) { + writel( (MEM_BYTE_READ | 0xD0000 | vsoff) + i, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + ver_str[i] = readb(xl_mmio + MMIO_MACDATA) ; + } + ver_str[i] = '\0' ; + printk(KERN_INFO "%s: Microcode version String: %s \n",dev->name,ver_str); + } + + /* + * Issue the AckInterrupt + */ + writew(ACK_INTERRUPT | SRBRACK | LATCH_ACK, xl_mmio + MMIO_COMMAND) ; + + return 0 ; +} + +/* + * There are two ways of implementing rx on the 359 NIC, either + * interrupt driven or polling. We are going to uses interrupts, + * it is the easier way of doing things. + * + * The Rx works with a ring of Rx descriptors. At initialise time the ring + * entries point to the next entry except for the last entry in the ring + * which points to 0. The card is programmed with the location of the first + * available descriptor and keeps reading the next_ptr until next_ptr is set + * to 0. Hopefully with a ring size of 16 the card will never get to read a next_ptr + * of 0. As the Rx interrupt is received we copy the frame up to the protocol layers + * and then point the end of the ring to our current position and point our current + * position to 0, therefore making the current position the last position on the ring. + * The last position on the ring therefore loops continually loops around the rx ring. + * + * rx_ring_tail is the position on the ring to process next. (Think of a snake, the head + * expands as the card adds new packets and we go around eating the tail processing the + * packets.) + * + * Undoubtably it could be streamlined and improved upon, but at the moment it works + * and the fast path through the routine is fine. + * + * adv_rx_ring could be inlined to increase performance, but its called a *lot* of times + * in xl_rx so would increase the size of the function significantly. + */ + +static void adv_rx_ring(struct net_device *dev) /* Advance rx_ring, cut down on bloat in xl_rx */ +{ + struct xl_private *xl_priv=(struct xl_private *)dev->priv; + int prev_ring_loc ; + + prev_ring_loc = (xl_priv->rx_ring_tail + XL_RX_RING_SIZE - 1) & (XL_RX_RING_SIZE - 1); + xl_priv->xl_rx_ring[prev_ring_loc].upnextptr = xl_priv->rx_ring_dma_addr + (sizeof (struct xl_rx_desc) * xl_priv->rx_ring_tail) ; + xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].framestatus = 0 ; + xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upnextptr = 0 ; + xl_priv->rx_ring_tail++ ; + xl_priv->rx_ring_tail &= (XL_RX_RING_SIZE-1) ; + + return ; +} + +static void xl_rx(struct net_device *dev) +{ + struct xl_private *xl_priv=(struct xl_private *)dev->priv; + u8 * xl_mmio = xl_priv->xl_mmio ; + struct sk_buff *skb, *skb2 ; + int frame_length = 0, copy_len = 0 ; + int temp_ring_loc ; + + /* + * Receive the next frame, loop around the ring until all frames + * have been received. + */ + + while (xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].framestatus & (RXUPDCOMPLETE | RXUPDFULL) ) { /* Descriptor to process */ + + if (xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].framestatus & RXUPDFULL ) { /* UpdFull, Multiple Descriptors used for the frame */ + + /* + * This is a pain, you need to go through all the descriptors until the last one + * for this frame to find the framelength + */ + + temp_ring_loc = xl_priv->rx_ring_tail ; + + while (xl_priv->xl_rx_ring[temp_ring_loc].framestatus & RXUPDFULL ) { + temp_ring_loc++ ; + temp_ring_loc &= (XL_RX_RING_SIZE-1) ; + } + + frame_length = xl_priv->xl_rx_ring[temp_ring_loc].framestatus & 0x7FFF ; + + skb = dev_alloc_skb(frame_length) ; + + if (skb==NULL) { /* No memory for frame, still need to roll forward the rx ring */ + printk(KERN_WARNING "%s: dev_alloc_skb failed - multi buffer !\n", dev->name) ; + while (xl_priv->rx_ring_tail != temp_ring_loc) + adv_rx_ring(dev) ; + + adv_rx_ring(dev) ; /* One more time just for luck :) */ + xl_priv->xl_stats.rx_dropped++ ; + + writel(ACK_INTERRUPT | UPCOMPACK | LATCH_ACK , xl_mmio + MMIO_COMMAND) ; + return ; + } + + skb->dev = dev ; + + while (xl_priv->rx_ring_tail != temp_ring_loc) { + copy_len = xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfraglen & 0x7FFF ; + frame_length -= copy_len ; + pci_dma_sync_single(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; + memcpy(skb_put(skb,copy_len), xl_priv->rx_ring_skb[xl_priv->rx_ring_tail]->data, copy_len) ; + adv_rx_ring(dev) ; + } + + /* Now we have found the last fragment */ + pci_dma_sync_single(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; + memcpy(skb_put(skb,copy_len), xl_priv->rx_ring_skb[xl_priv->rx_ring_tail]->data, frame_length) ; +/* memcpy(skb_put(skb,frame_length), bus_to_virt(xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr), frame_length) ; */ + adv_rx_ring(dev) ; + skb->protocol = tr_type_trans(skb,dev) ; + netif_rx(skb) ; + + } else { /* Single Descriptor Used, simply swap buffers over, fast path */ + + frame_length = xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].framestatus & 0x7FFF ; + + skb = dev_alloc_skb(xl_priv->pkt_buf_sz) ; + + if (skb==NULL) { /* Still need to fix the rx ring */ + printk(KERN_WARNING "%s: dev_alloc_skb failed in rx, single buffer \n",dev->name) ; + adv_rx_ring(dev) ; + xl_priv->xl_stats.rx_dropped++ ; + writel(ACK_INTERRUPT | UPCOMPACK | LATCH_ACK , xl_mmio + MMIO_COMMAND) ; + return ; + } + + skb->dev = dev ; + + skb2 = xl_priv->rx_ring_skb[xl_priv->rx_ring_tail] ; + pci_unmap_single(xl_priv->pdev, xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr, xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; + skb_put(skb2, frame_length) ; + skb2->protocol = tr_type_trans(skb2,dev) ; + + xl_priv->rx_ring_skb[xl_priv->rx_ring_tail] = skb ; + xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr = pci_map_single(xl_priv->pdev,skb->data,xl_priv->pkt_buf_sz, PCI_DMA_FROMDEVICE) ; + xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfraglen = xl_priv->pkt_buf_sz | RXUPLASTFRAG ; + adv_rx_ring(dev) ; + xl_priv->xl_stats.rx_packets++ ; + xl_priv->xl_stats.rx_bytes += frame_length ; + + netif_rx(skb2) ; + } /* if multiple buffers */ + dev->last_rx = jiffies ; + } /* while packet to do */ + + /* Clear the updComplete interrupt */ + writel(ACK_INTERRUPT | UPCOMPACK | LATCH_ACK , xl_mmio + MMIO_COMMAND) ; + return ; +} + +/* + * This is ruthless, it doesn't care what state the card is in it will + * completely reset the adapter. + */ + +static void xl_reset(struct net_device *dev) +{ + struct xl_private *xl_priv=(struct xl_private *)dev->priv; + u8 * xl_mmio = xl_priv->xl_mmio ; + unsigned long t; + + writew( GLOBAL_RESET, xl_mmio + MMIO_COMMAND ) ; + + /* + * Must wait for cmdInProgress bit (12) to clear before continuing with + * card configuration. + */ + + t=jiffies; + while (readw(xl_mmio + MMIO_INTSTATUS) & INTSTAT_CMD_IN_PROGRESS) { + if(jiffies-t > 40*HZ) { + printk(KERN_ERR "3COM 3C359 Velocity XL card not responding.\n"); + break ; + } + } + +} + +static void xl_freemem(struct net_device *dev) +{ + struct xl_private *xl_priv=(struct xl_private *)dev->priv ; + int i ; + + for (i=0;irx_ring_skb[xl_priv->rx_ring_tail]) ; + pci_unmap_single(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz, PCI_DMA_FROMDEVICE) ; + xl_priv->rx_ring_tail++ ; + xl_priv->rx_ring_tail &= XL_RX_RING_SIZE-1; + } + + /* unmap ring */ + pci_unmap_single(xl_priv->pdev,xl_priv->rx_ring_dma_addr, sizeof(struct xl_rx_desc) * XL_RX_RING_SIZE, PCI_DMA_FROMDEVICE) ; + + pci_unmap_single(xl_priv->pdev,xl_priv->tx_ring_dma_addr, sizeof(struct xl_tx_desc) * XL_TX_RING_SIZE, PCI_DMA_TODEVICE) ; + + kfree(xl_priv->xl_rx_ring) ; + kfree(xl_priv->xl_tx_ring) ; + + return ; +} + +static void xl_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct xl_private *xl_priv =(struct xl_private *)dev->priv; + u8 * xl_mmio = xl_priv->xl_mmio ; + u16 intstatus, macstatus ; + + if (!dev) { + printk(KERN_WARNING "Device structure dead, aaahhhh !\n") ; + return ; + } + + intstatus = readw(xl_mmio + MMIO_INTSTATUS) ; + + if (!(intstatus & 1)) /* We didn't generate the interrupt */ + return ; + + spin_lock(&xl_priv->xl_lock) ; + + /* + * Process the interrupt + */ + /* + * Something fishy going on here, we shouldn't get 0001 ints, not fatal though. + */ + if (intstatus == 0x0001) { + writel(ACK_INTERRUPT | LATCH_ACK, xl_mmio + MMIO_COMMAND) ; + printk(KERN_INFO "%s: 00001 int received \n",dev->name) ; + } else { + if (intstatus & (HOSTERRINT | SRBRINT | ARBCINT | UPCOMPINT | DNCOMPINT | HARDERRINT | (1<<8) | TXUNDERRUN | ASBFINT)) { + + /* + * Host Error. + * It may be possible to recover from this, but usually it means something + * is seriously fubar, so we just close the adapter. + */ + + if (intstatus & HOSTERRINT) { + printk(KERN_WARNING "%s: Host Error, performing global reset, intstatus = %04x \n",dev->name,intstatus) ; + writew( GLOBAL_RESET, xl_mmio + MMIO_COMMAND ) ; + printk(KERN_WARNING "%s: Resetting hardware: \n", dev->name); + netif_stop_queue(dev) ; + xl_freemem(dev) ; + free_irq(dev->irq,dev); + xl_reset(dev) ; + writel(ACK_INTERRUPT | LATCH_ACK, xl_mmio + MMIO_COMMAND) ; + spin_unlock(&xl_priv->xl_lock) ; + return ; + } /* Host Error */ + + if (intstatus & SRBRINT ) { /* Srbc interrupt */ + writel(ACK_INTERRUPT | SRBRACK | LATCH_ACK, xl_mmio + MMIO_COMMAND) ; + if (xl_priv->srb_queued) + xl_srb_bh(dev) ; + } /* SRBR Interrupt */ + + if (intstatus & TXUNDERRUN) { /* Issue DnReset command */ + writel(DNRESET, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + while (readw(xl_mmio + MMIO_INTSTATUS) & INTSTAT_CMD_IN_PROGRESS) { /* Wait for command to run */ + /* !!! FIX-ME !!!! + Must put a timeout check here ! */ + /* Empty Loop */ + } + printk(KERN_WARNING "%s: TX Underrun received \n",dev->name) ; + writel(ACK_INTERRUPT | LATCH_ACK, xl_mmio + MMIO_COMMAND) ; + } /* TxUnderRun */ + + if (intstatus & ARBCINT ) { /* Arbc interrupt */ + xl_arb_cmd(dev) ; + } /* Arbc */ + + if (intstatus & ASBFINT) { + if (xl_priv->asb_queued == 1) { + xl_asb_cmd(dev) ; + } else if (xl_priv->asb_queued == 2) { + xl_asb_bh(dev) ; + } else { + writel(ACK_INTERRUPT | LATCH_ACK | ASBFACK, xl_mmio + MMIO_COMMAND) ; + } + } /* Asbf */ + + if (intstatus & UPCOMPINT ) /* UpComplete */ + xl_rx(dev) ; + + if (intstatus & DNCOMPINT ) /* DnComplete */ + xl_dn_comp(dev) ; + + if (intstatus & HARDERRINT ) { /* Hardware error */ + writel(MMIO_WORD_READ | MACSTATUS, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + macstatus = readw(xl_mmio + MMIO_MACDATA) ; + printk(KERN_WARNING "%s: MacStatusError, details: ", dev->name); + if (macstatus & (1<<14)) + printk(KERN_WARNING "tchk error: Unrecoverable error \n") ; + if (macstatus & (1<<3)) + printk(KERN_WARNING "eint error: Internal watchdog timer expired \n") ; + if (macstatus & (1<<2)) + printk(KERN_WARNING "aint error: Host tried to perform illegal operation \n") ; + printk(KERN_WARNING "Instatus = %02x, macstatus = %02x\n",intstatus,macstatus) ; + printk(KERN_WARNING "%s: Resetting hardware: \n", dev->name); + netif_stop_queue(dev) ; + xl_freemem(dev) ; + free_irq(dev->irq,dev); + unregister_trdev(dev) ; + kfree(dev) ; + xl_reset(dev) ; + writel(ACK_INTERRUPT | LATCH_ACK, xl_mmio + MMIO_COMMAND) ; + spin_unlock(&xl_priv->xl_lock) ; + return ; + } + } else { + printk(KERN_WARNING "%s: Received Unknown interrupt : %04x \n", dev->name, intstatus) ; + writel(ACK_INTERRUPT | LATCH_ACK, xl_mmio + MMIO_COMMAND) ; + } + } + + /* Turn interrupts back on */ + + writel( SETINDENABLE | INT_MASK, xl_mmio + MMIO_COMMAND) ; + writel( SETINTENABLE | INT_MASK, xl_mmio + MMIO_COMMAND) ; + + spin_unlock(&xl_priv->xl_lock) ; +} + +/* + * Tx - Polling configuration + */ + +static int xl_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct xl_private *xl_priv=(struct xl_private *)dev->priv; + struct xl_tx_desc *txd ; + int tx_head, tx_tail, tx_prev ; + unsigned long flags ; + + spin_lock_irqsave(&xl_priv->xl_lock,flags) ; + + netif_stop_queue(dev) ; + + if (xl_priv->free_ring_entries > 1 ) { + /* + * Set up the descriptor for the packet + */ + tx_head = xl_priv->tx_ring_head ; + tx_tail = xl_priv->tx_ring_tail ; + + txd = &(xl_priv->xl_tx_ring[tx_head]) ; + txd->dnnextptr = 0 ; + txd->framestartheader = skb->len | TXDNINDICATE ; + txd->buffer = pci_map_single(xl_priv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE) ; + txd->buffer_length = skb->len | TXDNFRAGLAST ; + xl_priv->tx_ring_skb[tx_head] = skb ; + xl_priv->xl_stats.tx_packets++ ; + xl_priv->xl_stats.tx_bytes += skb->len ; + + /* + * Set the nextptr of the previous descriptor equal to this descriptor, add XL_TX_RING_SIZE -1 + * to ensure no negative numbers in unsigned locations. + */ + + tx_prev = (xl_priv->tx_ring_head + XL_TX_RING_SIZE - 1) & (XL_TX_RING_SIZE - 1) ; + + xl_priv->tx_ring_head++ ; + xl_priv->tx_ring_head &= (XL_TX_RING_SIZE - 1) ; + xl_priv->free_ring_entries-- ; + + xl_priv->xl_tx_ring[tx_prev].dnnextptr = xl_priv->tx_ring_dma_addr + (sizeof (struct xl_tx_desc) * tx_head) ; + + /* Sneaky, by doing a read on DnListPtr we can force the card to poll on the DnNextPtr */ + /* readl(xl_mmio + MMIO_DNLISTPTR) ; */ + + netif_wake_queue(dev) ; + + spin_unlock_irqrestore(&xl_priv->xl_lock,flags) ; + + return 0; + } else { + spin_unlock_irqrestore(&xl_priv->xl_lock,flags) ; + return 1; + } + +} + +/* + * The NIC has told us that a packet has been downloaded onto the card, we must + * find out which packet it has done, clear the skb and information for the packet + * then advance around the ring for all tranmitted packets + */ + +static void xl_dn_comp(struct net_device *dev) +{ + struct xl_private *xl_priv=(struct xl_private *)dev->priv; + u8 * xl_mmio = xl_priv->xl_mmio ; + struct xl_tx_desc *txd ; + + + if (xl_priv->tx_ring_tail == 255) {/* First time */ + xl_priv->xl_tx_ring[0].framestartheader = 0 ; + xl_priv->xl_tx_ring[0].dnnextptr = 0 ; + xl_priv->tx_ring_tail = 1 ; + } + + while (xl_priv->xl_tx_ring[xl_priv->tx_ring_tail].framestartheader & TXDNCOMPLETE ) { + txd = &(xl_priv->xl_tx_ring[xl_priv->tx_ring_tail]) ; + pci_unmap_single(xl_priv->pdev,txd->buffer, xl_priv->tx_ring_skb[xl_priv->tx_ring_tail]->len, PCI_DMA_TODEVICE) ; + txd->framestartheader = 0 ; + txd->buffer = 0xdeadbeef ; + txd->buffer_length = 0 ; + dev_kfree_skb_irq(xl_priv->tx_ring_skb[xl_priv->tx_ring_tail]) ; + xl_priv->tx_ring_tail++ ; + xl_priv->tx_ring_tail &= (XL_TX_RING_SIZE - 1) ; + xl_priv->free_ring_entries++ ; + } + + netif_wake_queue(dev) ; + + writel(ACK_INTERRUPT | DNCOMPACK | LATCH_ACK , xl_mmio + MMIO_COMMAND) ; +} + +/* + * Close the adapter properly. + * This srb reply cannot be handled from interrupt context as we have + * to free the interrupt from the driver. + */ + +static int xl_close(struct net_device *dev) +{ + struct xl_private *xl_priv = (struct xl_private *) dev->priv ; + u8 * xl_mmio = xl_priv->xl_mmio ; + unsigned long t ; + + netif_stop_queue(dev) ; + + /* + * Close the adapter, need to stall the rx and tx queues. + */ + + writew(DNSTALL, xl_mmio + MMIO_COMMAND) ; + t=jiffies; + while (readw(xl_mmio + MMIO_INTSTATUS) & INTSTAT_CMD_IN_PROGRESS) { + schedule(); + if(jiffies-t > 10*HZ) { + printk(KERN_ERR "%s: 3COM 3C359 Velocity XL-DNSTALL not responding.\n", dev->name); + break ; + } + } + writew(DNDISABLE, xl_mmio + MMIO_COMMAND) ; + t=jiffies; + while (readw(xl_mmio + MMIO_INTSTATUS) & INTSTAT_CMD_IN_PROGRESS) { + schedule(); + if(jiffies-t > 10*HZ) { + printk(KERN_ERR "%s: 3COM 3C359 Velocity XL-DNDISABLE not responding.\n", dev->name); + break ; + } + } + writew(UPSTALL, xl_mmio + MMIO_COMMAND) ; + t=jiffies; + while (readw(xl_mmio + MMIO_INTSTATUS) & INTSTAT_CMD_IN_PROGRESS) { + schedule(); + if(jiffies-t > 10*HZ) { + printk(KERN_ERR "%s: 3COM 3C359 Velocity XL-UPSTALL not responding.\n", dev->name); + break ; + } + } + + /* Turn off interrupts, we will still get the indication though + * so we can trap it + */ + + writel(SETINTENABLE, xl_mmio + MMIO_COMMAND) ; + + xl_srb_cmd(dev,CLOSE_NIC) ; + + t=jiffies; + while (!(readw(xl_mmio + MMIO_INTSTATUS) & INTSTAT_SRB)) { + schedule(); + if(jiffies-t > 10*HZ) { + printk(KERN_ERR "%s: 3COM 3C359 Velocity XL-CLOSENIC not responding.\n", dev->name); + break ; + } + } + /* Read the srb response from the adapter */ + + writel(MEM_BYTE_READ | 0xd0000 | xl_priv->srb, xl_mmio + MMIO_MAC_ACCESS_CMD); + if (readb(xl_mmio + MMIO_MACDATA) != CLOSE_NIC) { + printk(KERN_INFO "%s: CLOSE_NIC did not get a CLOSE_NIC response \n",dev->name) ; + } else { + writel((MEM_BYTE_READ | 0xd0000 | xl_priv->srb) +2, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + if (readb(xl_mmio + MMIO_MACDATA)==0) { + printk(KERN_INFO "%s: Adapter has been closed \n",dev->name) ; + writew(ACK_INTERRUPT | SRBRACK | LATCH_ACK, xl_mmio + MMIO_COMMAND) ; + + xl_freemem(dev) ; + free_irq(dev->irq,dev) ; + } else { + printk(KERN_INFO "%s: Close nic command returned error code %02x\n",dev->name, readb(xl_mmio + MMIO_MACDATA)) ; + } + } + + /* Reset the upload and download logic */ + + writew(UPRESET, xl_mmio + MMIO_COMMAND) ; + t=jiffies; + while (readw(xl_mmio + MMIO_INTSTATUS) & INTSTAT_CMD_IN_PROGRESS) { + schedule(); + if(jiffies-t > 10*HZ) { + printk(KERN_ERR "%s: 3COM 3C359 Velocity XL-UPRESET not responding.\n", dev->name); + break ; + } + } + writew(DNRESET, xl_mmio + MMIO_COMMAND) ; + t=jiffies; + while (readw(xl_mmio + MMIO_INTSTATUS) & INTSTAT_CMD_IN_PROGRESS) { + schedule(); + if(jiffies-t > 10*HZ) { + printk(KERN_ERR "%s: 3COM 3C359 Velocity XL-DNRESET not responding.\n", dev->name); + break ; + } + } + xl_hw_reset(dev) ; + return 0 ; +} + +static void xl_set_rx_mode(struct net_device *dev) +{ + struct xl_private *xl_priv = (struct xl_private *) dev->priv ; + struct dev_mc_list *dmi ; + unsigned char dev_mc_address[4] ; + u16 options ; + int i ; + + if (dev->flags & IFF_PROMISC) + options = 0x0004 ; + else + options = 0x0000 ; + + if (options ^ xl_priv->xl_copy_all_options) { /* Changed, must send command */ + xl_priv->xl_copy_all_options = options ; + xl_srb_cmd(dev, SET_RECEIVE_MODE) ; + return ; + } + + dev_mc_address[0] = dev_mc_address[1] = dev_mc_address[2] = dev_mc_address[3] = 0 ; + + for (i=0,dmi=dev->mc_list;i < dev->mc_count; i++,dmi = dmi->next) { + dev_mc_address[0] |= dmi->dmi_addr[2] ; + dev_mc_address[1] |= dmi->dmi_addr[3] ; + dev_mc_address[2] |= dmi->dmi_addr[4] ; + dev_mc_address[3] |= dmi->dmi_addr[5] ; + } + + if (memcmp(xl_priv->xl_functional_addr,dev_mc_address,4) != 0) { /* Options have changed, run the command */ + memcpy(xl_priv->xl_functional_addr, dev_mc_address,4) ; + xl_srb_cmd(dev, SET_FUNC_ADDRESS) ; + } + return ; +} + + +/* + * We issued an srb command and now we must read + * the response from the completed command. + */ + +static void xl_srb_bh(struct net_device *dev) +{ + struct xl_private *xl_priv = (struct xl_private *) dev->priv ; + u8 * xl_mmio = xl_priv->xl_mmio ; + u8 srb_cmd, ret_code ; + int i ; + + writel(MEM_BYTE_READ | 0xd0000 | xl_priv->srb, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + srb_cmd = readb(xl_mmio + MMIO_MACDATA) ; + writel((MEM_BYTE_READ | 0xd0000 | xl_priv->srb) +2, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + ret_code = readb(xl_mmio + MMIO_MACDATA) ; + + /* Ret_code is standard across all commands */ + + switch (ret_code) { + case 1: + printk(KERN_INFO "%s: Command: %d - Invalid Command code\n",dev->name,srb_cmd) ; + break ; + case 4: + printk(KERN_INFO "%s: Command: %d - Adapter is closed, must be open for this command \n",dev->name,srb_cmd) ; + break ; + + case 6: + printk(KERN_INFO "%s: Command: %d - Options Invalid for command \n",dev->name,srb_cmd) ; + break ; + + case 0: /* Successful command execution */ + switch (srb_cmd) { + case READ_LOG: /* Returns 14 bytes of data from the NIC */ + if(xl_priv->xl_message_level) + printk(KERN_INFO "%s: READ.LOG 14 bytes of data ",dev->name) ; + /* + * We still have to read the log even if message_level = 0 and we don't want + * to see it + */ + for (i=0;i<14;i++) { + writel(MEM_BYTE_READ | 0xd0000 | xl_priv->srb | i, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + if(xl_priv->xl_message_level) + printk("%02x:",readb(xl_mmio + MMIO_MACDATA)) ; + } + printk("\n") ; + break ; + case SET_FUNC_ADDRESS: + if(xl_priv->xl_message_level) + printk(KERN_INFO "%s: Functional Address Set \n",dev->name) ; + break ; + case CLOSE_NIC: + if(xl_priv->xl_message_level) + printk(KERN_INFO "%s: Received CLOSE_NIC interrupt in interrupt handler \n",dev->name) ; + break ; + case SET_MULTICAST_MODE: + if(xl_priv->xl_message_level) + printk(KERN_INFO "%s: Multicast options successfully changed\n",dev->name) ; + break ; + case SET_RECEIVE_MODE: + if(xl_priv->xl_message_level) { + if (xl_priv->xl_copy_all_options == 0x0004) + printk(KERN_INFO "%s: Entering promiscuous mode \n", dev->name) ; + else + printk(KERN_INFO "%s: Entering normal receive mode \n",dev->name) ; + } + break ; + + } /* switch */ + break ; + } /* switch */ + return ; +} + +static struct net_device_stats * xl_get_stats(struct net_device *dev) +{ + struct xl_private *xl_priv = (struct xl_private *) dev->priv ; + return (struct net_device_stats *) &xl_priv->xl_stats; +} + +static int xl_set_mac_address (struct net_device *dev, void *addr) +{ + struct sockaddr *saddr = addr ; + struct xl_private *xl_priv = (struct xl_private *)dev->priv ; + + if (netif_running(dev)) { + printk(KERN_WARNING "%s: Cannot set mac/laa address while card is open\n", dev->name) ; + return -EIO ; + } + + memcpy(xl_priv->xl_laa, saddr->sa_data,dev->addr_len) ; + + if (xl_priv->xl_message_level) { + printk(KERN_INFO "%s: MAC/LAA Set to = %x.%x.%x.%x.%x.%x\n",dev->name, xl_priv->xl_laa[0], + xl_priv->xl_laa[1], xl_priv->xl_laa[2], + xl_priv->xl_laa[3], xl_priv->xl_laa[4], + xl_priv->xl_laa[5]); + } + + return 0 ; +} + +static void xl_arb_cmd(struct net_device *dev) +{ + struct xl_private *xl_priv = (struct xl_private *) dev->priv; + u8 * xl_mmio = xl_priv->xl_mmio ; + u8 arb_cmd ; + u16 lan_status, lan_status_diff ; + + writel( ( MEM_BYTE_READ | 0xD0000 | xl_priv->arb), xl_mmio + MMIO_MAC_ACCESS_CMD) ; + arb_cmd = readb(xl_mmio + MMIO_MACDATA) ; + + if (arb_cmd == RING_STATUS_CHANGE) { /* Ring.Status.Change */ + writel( ( (MEM_WORD_READ | 0xD0000 | xl_priv->arb) + 6), xl_mmio + MMIO_MAC_ACCESS_CMD) ; + + printk(KERN_INFO "%s: Ring Status Change: New Status = %04x\n", dev->name, ntohs(readw(xl_mmio + MMIO_MACDATA) )) ; + + lan_status = ntohs(readw(xl_mmio + MMIO_MACDATA)); + + /* Acknowledge interrupt, this tells nic we are done with the arb */ + writel(ACK_INTERRUPT | ARBCACK | LATCH_ACK, xl_mmio + MMIO_COMMAND) ; + + lan_status_diff = xl_priv->xl_lan_status ^ lan_status ; + + if (lan_status_diff & (LSC_LWF | LSC_ARW | LSC_FPE | LSC_RR) ) { + if (lan_status_diff & LSC_LWF) + printk(KERN_WARNING "%s: Short circuit detected on the lobe\n",dev->name); + if (lan_status_diff & LSC_ARW) + printk(KERN_WARNING "%s: Auto removal error\n",dev->name); + if (lan_status_diff & LSC_FPE) + printk(KERN_WARNING "%s: FDX Protocol Error\n",dev->name); + if (lan_status_diff & LSC_RR) + printk(KERN_WARNING "%s: Force remove MAC frame received\n",dev->name); + + /* Adapter has been closed by the hardware */ + + netif_stop_queue(dev); + xl_freemem(dev) ; + free_irq(dev->irq,dev); + + printk(KERN_WARNING "%s: Adapter has been closed \n", dev->name) ; + } /* If serious error */ + + if (xl_priv->xl_message_level) { + if (lan_status_diff & LSC_SIG_LOSS) + printk(KERN_WARNING "%s: No receive signal detected \n", dev->name) ; + if (lan_status_diff & LSC_HARD_ERR) + printk(KERN_INFO "%s: Beaconing \n",dev->name); + if (lan_status_diff & LSC_SOFT_ERR) + printk(KERN_WARNING "%s: Adapter transmitted Soft Error Report Mac Frame \n",dev->name); + if (lan_status_diff & LSC_TRAN_BCN) + printk(KERN_INFO "%s: We are tranmitting the beacon, aaah\n",dev->name); + if (lan_status_diff & LSC_SS) + printk(KERN_INFO "%s: Single Station on the ring \n", dev->name); + if (lan_status_diff & LSC_RING_REC) + printk(KERN_INFO "%s: Ring recovery ongoing\n",dev->name); + if (lan_status_diff & LSC_FDX_MODE) + printk(KERN_INFO "%s: Operating in FDX mode\n",dev->name); + } + + if (lan_status_diff & LSC_CO) { + if (xl_priv->xl_message_level) + printk(KERN_INFO "%s: Counter Overflow \n", dev->name); + /* Issue READ.LOG command */ + xl_srb_cmd(dev, READ_LOG) ; + } + + /* There is no command in the tech docs to issue the read_sr_counters */ + if (lan_status_diff & LSC_SR_CO) { + if (xl_priv->xl_message_level) + printk(KERN_INFO "%s: Source routing counters overflow\n", dev->name); + } + + xl_priv->xl_lan_status = lan_status ; + + } /* Lan.change.status */ + else if ( arb_cmd == RECEIVE_DATA) { /* Received.Data */ +#if XL_DEBUG + printk(KERN_INFO "Received.Data \n") ; +#endif + writel( ((MEM_WORD_READ | 0xD0000 | xl_priv->arb) + 6), xl_mmio + MMIO_MAC_ACCESS_CMD) ; + xl_priv->mac_buffer = ntohs(readw(xl_mmio + MMIO_MACDATA)) ; + + /* Now we are going to be really basic here and not do anything + * with the data at all. The tech docs do not give me enough + * information to calculate the buffers properly so we're + * just going to tell the nic that we've dealt with the frame + * anyway. + */ + + dev->last_rx = jiffies ; + /* Acknowledge interrupt, this tells nic we are done with the arb */ + writel(ACK_INTERRUPT | ARBCACK | LATCH_ACK, xl_mmio + MMIO_COMMAND) ; + + /* Is the ASB free ? */ + + xl_priv->asb_queued = 0 ; + writel( ((MEM_BYTE_READ | 0xD0000 | xl_priv->asb) + 2), xl_mmio + MMIO_MAC_ACCESS_CMD) ; + if (readb(xl_mmio + MMIO_MACDATA) != 0xff) { + xl_priv->asb_queued = 1 ; + + xl_wait_misr_flags(dev) ; + + writel(MEM_BYTE_WRITE | MF_ASBFR, xl_mmio + MMIO_MAC_ACCESS_CMD); + writeb(0xff, xl_mmio + MMIO_MACDATA) ; + writel(MMIO_BYTE_WRITE | MISR_SET, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(MISR_ASBFR, xl_mmio + MMIO_MACDATA) ; + return ; + /* Drop out and wait for the bottom half to be run */ + } + + xl_asb_cmd(dev) ; + + } else { + printk(KERN_WARNING "%s: Received unknown arb (xl_priv) command: %02x \n",dev->name,arb_cmd) ; + } + + /* Acknowledge the arb interrupt */ + + writel(ACK_INTERRUPT | ARBCACK | LATCH_ACK , xl_mmio + MMIO_COMMAND) ; + + return ; +} + + +/* + * There is only one asb command, but we can get called from different + * places. + */ + +static void xl_asb_cmd(struct net_device *dev) +{ + struct xl_private *xl_priv = (struct xl_private *) dev->priv ; + u8 * xl_mmio = xl_priv->xl_mmio ; + + if (xl_priv->asb_queued == 1) + writel(ACK_INTERRUPT | LATCH_ACK | ASBFACK, xl_mmio + MMIO_COMMAND) ; + + writel(MEM_BYTE_WRITE | 0xd0000 | xl_priv->asb, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(0x81, xl_mmio + MMIO_MACDATA) ; + + writel(MEM_WORD_WRITE | 0xd0000 | xl_priv->asb | 6, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writew(ntohs(xl_priv->mac_buffer), xl_mmio + MMIO_MACDATA) ; + + xl_wait_misr_flags(dev) ; + + writel(MEM_BYTE_WRITE | MF_RASB, xl_mmio + MMIO_MAC_ACCESS_CMD); + writeb(0xff, xl_mmio + MMIO_MACDATA) ; + + writel(MMIO_BYTE_WRITE | MISR_SET, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(MISR_RASB, xl_mmio + MMIO_MACDATA) ; + + xl_priv->asb_queued = 2 ; + + return ; +} + +/* + * This will only get called if there was an error + * from the asb cmd. + */ +static void xl_asb_bh(struct net_device *dev) +{ + struct xl_private *xl_priv = (struct xl_private *) dev->priv ; + u8 * xl_mmio = xl_priv->xl_mmio ; + u8 ret_code ; + + writel(MMIO_BYTE_READ | 0xd0000 | xl_priv->asb | 2, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + ret_code = readb(xl_mmio + MMIO_MACDATA) ; + switch (ret_code) { + case 0x01: + printk(KERN_INFO "%s: ASB Command, unrecognized command code \n",dev->name) ; + break ; + case 0x26: + printk(KERN_INFO "%s: ASB Command, unexpected receive buffer \n", dev->name) ; + break ; + case 0x40: + printk(KERN_INFO "%s: ASB Command, Invalid Station ID \n", dev->name) ; + break ; + } + xl_priv->asb_queued = 0 ; + writel(ACK_INTERRUPT | LATCH_ACK | ASBFACK, xl_mmio + MMIO_COMMAND) ; + return ; +} + +/* + * Issue srb commands to the nic + */ + +static void xl_srb_cmd(struct net_device *dev, int srb_cmd) +{ + struct xl_private *xl_priv = (struct xl_private *) dev->priv ; + u8 * xl_mmio = xl_priv->xl_mmio ; + + switch (srb_cmd) { + case READ_LOG: + writel(MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(READ_LOG, xl_mmio + MMIO_MACDATA) ; + break; + + case CLOSE_NIC: + writel(MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(CLOSE_NIC, xl_mmio + MMIO_MACDATA) ; + break ; + + case SET_RECEIVE_MODE: + writel(MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(SET_RECEIVE_MODE, xl_mmio + MMIO_MACDATA) ; + writel(MEM_WORD_WRITE | 0xD0000 | xl_priv->srb | 4, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writew(xl_priv->xl_copy_all_options, xl_mmio + MMIO_MACDATA) ; + break ; + + case SET_FUNC_ADDRESS: + writel(MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(SET_FUNC_ADDRESS, xl_mmio + MMIO_MACDATA) ; + writel(MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb | 6 , xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(xl_priv->xl_functional_addr[0], xl_mmio + MMIO_MACDATA) ; + writel(MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb | 7 , xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(xl_priv->xl_functional_addr[1], xl_mmio + MMIO_MACDATA) ; + writel(MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb | 8 , xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(xl_priv->xl_functional_addr[2], xl_mmio + MMIO_MACDATA) ; + writel(MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb | 9 , xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(xl_priv->xl_functional_addr[3], xl_mmio + MMIO_MACDATA) ; + break ; + } /* switch */ + + + xl_wait_misr_flags(dev) ; + + /* Write 0xff to the CSRB flag */ + writel(MEM_BYTE_WRITE | MF_CSRB , xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(0xFF, xl_mmio + MMIO_MACDATA) ; + /* Set csrb bit in MISR register to process command */ + writel(MMIO_BYTE_WRITE | MISR_SET, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(MISR_CSRB, xl_mmio + MMIO_MACDATA) ; + xl_priv->srb_queued = 1 ; + + return ; +} + +/* + * This is nasty, to use the MISR command you have to wait for 6 memory locations + * to be zero. This is the way the driver does on other OS'es so we should be ok with + * the empty loop. + */ + +static void xl_wait_misr_flags(struct net_device *dev) +{ + struct xl_private *xl_priv = (struct xl_private *) dev->priv ; + u8 * xl_mmio = xl_priv->xl_mmio ; + + int i ; + + writel(MMIO_BYTE_READ | MISR_RW, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + if (readb(xl_mmio + MMIO_MACDATA) != 0) { /* Misr not clear */ + for (i=0; i<6; i++) { + writel(MEM_BYTE_READ | 0xDFFE0 | i, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + while (readb(xl_mmio + MMIO_MACDATA) != 0 ) {} ; /* Empty Loop */ + } + } + + writel(MMIO_BYTE_WRITE | MISR_AND, xl_mmio + MMIO_MAC_ACCESS_CMD) ; + writeb(0x80, xl_mmio + MMIO_MACDATA) ; + + return ; +} + +/* + * Change mtu size, this should work the same as olympic + */ + +static int xl_change_mtu(struct net_device *dev, int mtu) +{ + struct xl_private *xl_priv = (struct xl_private *) dev->priv; + u16 max_mtu ; + + if (xl_priv->xl_ring_speed == 4) + max_mtu = 4500 ; + else + max_mtu = 18000 ; + + if (mtu > max_mtu) + return -EINVAL ; + if (mtu < 100) + return -EINVAL ; + + dev->mtu = mtu ; + xl_priv->pkt_buf_sz = mtu + TR_HLEN ; + + return 0 ; +} + +static void __devexit xl_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pdev->driver_data; + struct xl_private *xl_priv=(struct xl_private *)dev->priv; + + unregister_trdev(dev); + iounmap(xl_priv->xl_mmio) ; + pci_release_regions(pdev) ; + pci_set_drvdata(pdev,NULL) ; + kfree(dev); + return ; +} + +static struct pci_driver xl_3c359_driver = { + name: "3c359", + id_table: xl_pci_tbl, + probe: xl_probe, + remove: __devexit_p(xl_remove_one), +}; + +static int __init xl_pci_init (void) +{ + return pci_module_init (&xl_3c359_driver); +} + + +static void __exit xl_pci_cleanup (void) +{ + pci_unregister_driver (&xl_3c359_driver); +} + +module_init(xl_pci_init); +module_exit(xl_pci_cleanup); + +MODULE_LICENSE("GPL") ; diff -Nru a/drivers/net/tokenring/3c359.h b/drivers/net/tokenring/3c359.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/tokenring/3c359.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,290 @@ +/* + * 3c359.h (c) 2000 Mike Phillips (mikep@linuxtr.net) All Rights Reserved + * + * Linux driver for 3Com 3C359 Token Link PCI XL cards. + * + * This software may be used and distributed according to the terms + * of the GNU General Public License Version 2 or (at your option) + * any later verion, incorporated herein by reference. + */ + +/* Memory Access Commands */ +#define IO_BYTE_READ 0x28 << 24 +#define IO_BYTE_WRITE 0x18 << 24 +#define IO_WORD_READ 0x20 << 24 +#define IO_WORD_WRITE 0x10 << 24 +#define MMIO_BYTE_READ 0x88 << 24 +#define MMIO_BYTE_WRITE 0x48 << 24 +#define MMIO_WORD_READ 0x80 << 24 +#define MMIO_WORD_WRITE 0x40 << 24 +#define MEM_BYTE_READ 0x8C << 24 +#define MEM_BYTE_WRITE 0x4C << 24 +#define MEM_WORD_READ 0x84 << 24 +#define MEM_WORD_WRITE 0x44 << 24 + +#define PMBAR 0x1C80 +#define PMB_CPHOLD (1<<10) + +#define CPATTENTION 0x180D +#define CPA_PMBARVIS (1<<7) +#define CPA_MEMWREN (1<<6) + +#define SWITCHSETTINGS 0x1C88 +#define EECONTROL 0x1C8A +#define EEDATA 0x1C8C +#define EEREAD 0x0080 +#define EEWRITE 0x0040 +#define EEERASE 0x0060 +#define EE_ENABLE_WRITE 0x0030 +#define EEBUSY (1<<15) + +#define WRBR 0xCDE02 +#define WWOR 0xCDE04 +#define WWCR 0xCDE06 +#define MACSTATUS 0xCDE08 +#define MISR_RW 0xCDE0B +#define MISR_AND 0xCDE2B +#define MISR_SET 0xCDE4B +#define RXBUFAREA 0xCDE10 +#define RXEARLYTHRESH 0xCDE12 +#define TXSTARTTHRESH 0x58 +#define DNPRIREQTHRESH 0x2C + +#define MISR_CSRB (1<<5) +#define MISR_RASB (1<<4) +#define MISR_SRBFR (1<<3) +#define MISR_ASBFR (1<<2) +#define MISR_ARBF (1<<1) + +/* MISR Flags memory locations */ +#define MF_SSBF 0xDFFE0 +#define MF_ARBF 0xDFFE1 +#define MF_ASBFR 0xDFFE2 +#define MF_SRBFR 0xDFFE3 +#define MF_RASB 0xDFFE4 +#define MF_CSRB 0xDFFE5 + +#define MMIO_MACDATA 0x10 +#define MMIO_MAC_ACCESS_CMD 0x14 +#define MMIO_TIMER 0x1A +#define MMIO_DMA_CTRL 0x20 +#define MMIO_DNLISTPTR 0x24 +#define MMIO_HASHFILTER 0x28 +#define MMIO_CONFIG 0x29 +#define MMIO_DNPRIREQTHRESH 0x2C +#define MMIO_DNPOLL 0x2D +#define MMIO_UPPKTSTATUS 0x30 +#define MMIO_FREETIMER 0x34 +#define MMIO_COUNTDOWN 0x36 +#define MMIO_UPLISTPTR 0x38 +#define MMIO_UPPOLL 0x3C +#define MMIO_UPBURSTTHRESH 0x40 +#define MMIO_DNBURSTTHRESH 0x41 +#define MMIO_INTSTATUS_AUTO 0x56 +#define MMIO_TXSTARTTHRESH 0x58 +#define MMIO_INTERRUPTENABLE 0x5A +#define MMIO_INDICATIONENABLE 0x5C +#define MMIO_COMMAND 0x5E /* These two are meant to be the same */ +#define MMIO_INTSTATUS 0x5E /* Makes the code more readable this way */ +#define INTSTAT_CMD_IN_PROGRESS (1<<12) +#define INTSTAT_SRB (1<<14) +#define INTSTAT_INTLATCH (1<<0) + +/* Indication / Interrupt Mask + * Annoyingly the bits to be set in the indication and interrupt enable + * do not match with the actual bits received in the interrupt, although + * they are in the same order. + * The mapping for the indication / interrupt are: + * Bit Indication / Interrupt + * 0 HostError + * 1 txcomplete + * 2 updneeded + * 3 rxcomplete + * 4 intrequested + * 5 macerror + * 6 dncomplete + * 7 upcomplete + * 8 txunderrun + * 9 asbf + * 10 srbr + * 11 arbc + * + * The only ones we don't want to receive are txcomplete and rxcomplete + * we use dncomplete and upcomplete instead. + */ + +#define INT_MASK 0xFF5 + +/* Note the subtle difference here, IND and INT */ + +#define SETINDENABLE (8<<12) +#define SETINTENABLE (7<<12) +#define SRBBIT (1<<10) +#define ASBBIT (1<<9) +#define ARBBIT (1<<11) + +#define SRB 0xDFE90 +#define ASB 0xDFED0 +#define ARB 0xD0000 +#define SCRATCH 0xDFEF0 + +#define INT_REQUEST 0x6000 /* (6 << 12) */ +#define ACK_INTERRUPT 0x6800 /* (13 <<11) */ +#define GLOBAL_RESET 0x00 +#define DNDISABLE 0x5000 +#define DNENABLE 0x4800 +#define DNSTALL 0x3002 +#define DNRESET 0x5800 +#define DNUNSTALL 0x3003 +#define UPRESET 0x2800 +#define UPSTALL 0x3000 +#define UPUNSTALL 0x3001 +#define SETCONFIG 0x4000 +#define SETTXSTARTTHRESH 0x9800 + +/* Received Interrupts */ +#define ASBFINT (1<<13) +#define SRBRINT (1<<14) +#define ARBCINT (1<<15) +#define TXUNDERRUN (1<<11) + +#define UPCOMPINT (1<<10) +#define DNCOMPINT (1<<9) +#define HARDERRINT (1<<7) +#define RXCOMPLETE (1<<4) +#define TXCOMPINT (1<<2) +#define HOSTERRINT (1<<1) + +/* Receive descriptor bits */ +#define RXOVERRUN (1<<19) +#define RXFC (1<<21) +#define RXAR (1<<22) +#define RXUPDCOMPLETE (1<<23) +#define RXUPDFULL (1<<24) +#define RXUPLASTFRAG (1<<31) + +/* Transmit descriptor bits */ +#define TXDNCOMPLETE (1<<16) +#define TXTXINDICATE (1<<27) +#define TXDPDEMPTY (1<<29) +#define TXDNINDICATE (1<<31) +#define TXDNFRAGLAST (1<<31) + +/* Interrupts to Acknowledge */ +#define LATCH_ACK 1 +#define TXCOMPACK (1<<1) +#define INTREQACK (1<<2) +#define DNCOMPACK (1<<3) +#define UPCOMPACK (1<<4) +#define ASBFACK (1<<5) +#define SRBRACK (1<<6) +#define ARBCACK (1<<7) + +#define XL_IO_SPACE 128 +#define SRB_COMMAND_SIZE 50 + +/* Adapter Commands */ +#define REQUEST_INT 0x00 +#define MODIFY_OPEN_PARMS 0x01 +#define RESTORE_OPEN_PARMS 0x02 +#define OPEN_NIC 0x03 +#define CLOSE_NIC 0x04 +#define SET_SLEEP_MODE 0x05 +#define SET_GROUP_ADDRESS 0x06 +#define SET_FUNC_ADDRESS 0x07 +#define READ_LOG 0x08 +#define SET_MULTICAST_MODE 0x0C +#define CHANGE_WAKEUP_PATTERN 0x0D +#define GET_STATISTICS 0x13 +#define SET_RECEIVE_MODE 0x1F + +/* ARB Commands */ +#define RECEIVE_DATA 0x81 +#define RING_STATUS_CHANGE 0x84 + +/* ASB Commands */ +#define ASB_RECEIVE_DATE 0x81 + +/* Defines for LAN STATUS CHANGE reports */ +#define LSC_SIG_LOSS 0x8000 +#define LSC_HARD_ERR 0x4000 +#define LSC_SOFT_ERR 0x2000 +#define LSC_TRAN_BCN 0x1000 +#define LSC_LWF 0x0800 +#define LSC_ARW 0x0400 +#define LSC_FPE 0x0200 +#define LSC_RR 0x0100 +#define LSC_CO 0x0080 +#define LSC_SS 0x0040 +#define LSC_RING_REC 0x0020 +#define LSC_SR_CO 0x0010 +#define LSC_FDX_MODE 0x0004 + +#define XL_MAX_ADAPTERS 8 /* 0x08 __MODULE_STRING can't hand 0xnn */ + +/* 3c359 defaults for buffers */ + +#define XL_RX_RING_SIZE 16 /* must be a power of 2 */ +#define XL_TX_RING_SIZE 16 /* must be a power of 2 */ + +#define PKT_BUF_SZ 4096 /* Default packet size */ + +/* 3c359 data structures */ + +struct xl_tx_desc { + u32 dnnextptr ; + u32 framestartheader ; + u32 buffer ; + u32 buffer_length ; +}; + +struct xl_rx_desc { + u32 upnextptr ; + u32 framestatus ; + u32 upfragaddr ; + u32 upfraglen ; +}; + +struct xl_private { + + + /* These two structures must be aligned on 8 byte boundaries */ + + /* struct xl_rx_desc xl_rx_ring[XL_RX_RING_SIZE]; */ + /* struct xl_tx_desc xl_tx_ring[XL_TX_RING_SIZE]; */ + struct xl_rx_desc *xl_rx_ring ; + struct xl_tx_desc *xl_tx_ring ; + struct sk_buff *tx_ring_skb[XL_TX_RING_SIZE], *rx_ring_skb[XL_RX_RING_SIZE]; + int tx_ring_head, tx_ring_tail ; + int rx_ring_tail, rx_ring_no ; + int free_ring_entries ; + + u16 srb; + u16 arb; + u16 asb; + + u8 *xl_mmio; + char *xl_card_name; + struct pci_dev *pdev ; + + spinlock_t xl_lock ; + + volatile int srb_queued; + struct wait_queue *srb_wait; + volatile int asb_queued; + + struct net_device_stats xl_stats ; + + u16 mac_buffer ; + u16 xl_lan_status ; + u8 xl_ring_speed ; + u16 pkt_buf_sz ; + u8 xl_message_level; + u16 xl_copy_all_options ; + unsigned char xl_functional_addr[4] ; + u16 xl_addr_table_addr, xl_parms_addr ; + u8 xl_laa[6] ; + u32 rx_ring_dma_addr ; + u32 tx_ring_dma_addr ; +}; + diff -Nru a/drivers/net/tokenring/3c359_microcode.h b/drivers/net/tokenring/3c359_microcode.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/tokenring/3c359_microcode.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,1585 @@ + +/* + * The firmware this driver downloads into the tokenring card is a + * separate program and is not GPL'd source code, even though the Linux + * side driver and the routine that loads this data into the card are. + * + * This firmware is licensed to you strictly for use in conjunction + * with the use of 3Com 3C359 TokenRing adapters. There is no + * waranty expressed or implied about its fitness for any purpose. + */ + +/* 3c359_microcode.mac: 3Com 3C359 Tokenring microcode. + * + * Notes: + * - Loaded from xl_init upon adapter initialization. + * + * Available from 3Com as part of their standard 3C359 driver. + * + * mc_size *must* must match the microcode being used, each version is a + * different length. + */ + + +#if defined(CONFIG_3C359) || defined(CONFIG_3C359_MODULE) + +static int mc_size = 24880 ; + +u8 microcode[] = { + 0xfe,0x3a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x33,0x2f,0x30,0x32,0x2f,0x39,0x39,0x20,0x31 +,0x37,0x3a,0x31,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46 +,0x00,0x00,0x07,0xff,0x02,0x00,0xfe,0x9f,0x06,0x00,0x00,0x7c,0x48,0x00,0x00,0x70 +,0x82,0x00,0xff,0xff,0x86,0x00,0xff,0xff,0x88,0x00,0xff,0xff,0x9a,0x00,0xff,0xff +,0xff,0xff,0x11,0x00,0xc0,0x00,0xff,0xff,0xff,0xff,0x11,0x22,0x33,0x44,0x55,0x66 +,0x33,0x43,0x4f,0x4d,0x20,0x42,0x41,0x42,0x45,0x11,0x40,0xc0,0x00,0xff,0xff,0xff +,0xff,0x11,0x22,0x33,0x44,0x55,0x66,0x53,0x74,0x61,0x72,0x74,0x20,0x6f,0x66,0x20 +,0x4c,0x4c,0x43,0x20,0x66,0x72,0x61,0x6d,0x65,0x2e,0x20,0x20,0x54,0x6f,0x74,0x61 +,0x6c,0x20,0x64,0x61,0x74,0x61,0x20,0x73,0x69,0x7a,0x65,0x20,0x69,0x73,0x20,0x78 +,0x78,0x78,0x20,0x20,0x20,0x42,0x41,0x42,0x45,0xe8,0xd2,0x01,0x83,0x3e,0xf7,0x34 +,0x00,0x75,0x21,0xe8,0x41,0x00,0x83,0x3e,0xf7,0x34,0x00,0x75,0x17,0xe8,0x82,0x00 +,0x83,0x3e,0xf7,0x34,0x00,0x75,0x0d,0xe8,0xbf,0x00,0x83,0x3e,0xf7,0x34,0x00,0x75 +,0x03,0xe8,0x41,0x02,0xc3,0x1e,0xb8,0x00,0xf0,0x8e,0xd8,0x33,0xf6,0xb9,0x00,0x80 +,0x33,0xdb,0xad,0x03,0xd8,0xe2,0xfb,0x1f,0xb8,0x00,0x00,0x83,0xfb,0x00,0x74,0x03 +,0xb8,0x22,0x00,0xa3,0xf7,0x34,0xc3,0xfa,0xba,0x56,0x00,0xb0,0xff,0xee,0x33,0xc0 +,0x8e,0xc0,0x33,0xf6,0xb9,0xff,0x7f,0x83,0x3e,0xff,0x34,0x00,0x74,0x08,0x8d,0x3e +,0x30,0x61,0xd1,0xef,0x2b,0xcf,0x26,0x8b,0x1c,0x26,0xc7,0x04,0xff,0xff,0x26,0x83 +,0x3c,0xff,0x75,0x17,0x26,0xc7,0x04,0x00,0x00,0x26,0x83,0x3c,0x00,0x75,0x0c,0x26 +,0x89,0x1c,0x46,0x46,0xe2,0xe0,0xb8,0x00,0x00,0xeb,0x03,0xb8,0x24,0x00,0xa3,0xf7 +,0x34,0xc3,0xfa,0xb4,0xd7,0x9e,0x73,0x3a,0x75,0x38,0x79,0x36,0x7b,0x34,0x9f,0xb1 +,0x05,0xd2,0xec,0x73,0x2d,0xb0,0x40,0xd0,0xe0,0x71,0x27,0x79,0x25,0xd0,0xe0,0x73 +,0x21,0x7b,0x1f,0x32,0xc0,0x75,0x1b,0x32,0xe4,0x9e,0x72,0x16,0x74,0x14,0x78,0x12 +,0x7a,0x10,0x9f,0xd2,0xec,0x72,0x0b,0xd0,0xe4,0x70,0x07,0x75,0x05,0xb8,0x00,0x00 +,0xeb,0x03,0xb8,0x26,0x00,0xa3,0xf7,0x34,0xc3,0xfa,0xba,0x5a,0x00,0x33,0xc0,0xef +,0xef,0xef,0xef,0xb0,0x00,0xe6,0x56,0xb0,0x00,0xe6,0x54,0xba,0x52,0x00,0xb8,0x01 +,0x01,0xef,0xe8,0xca,0x00,0x3c,0x01,0x75,0x7f,0xe8,0x83,0x00,0xba,0x52,0x00,0xb8 +,0x02,0x02,0xef,0xe8,0xb9,0x00,0x3c,0x02,0x75,0x6e,0xe8,0x7a,0x00,0xba,0x52,0x00 +,0xb8,0x04,0x04,0xef,0xe8,0xa8,0x00,0x3c,0x04,0x75,0x5d,0xe8,0x71,0x00,0xba,0x52 +,0x00,0xb8,0x08,0x08,0xef,0xe8,0x97,0x00,0x3c,0x08,0x75,0x4c,0xe8,0x68,0x00,0xba +,0x52,0x00,0xb8,0x10,0x10,0xef,0xe8,0x86,0x00,0x3c,0x10,0x75,0x3b,0xe8,0x5f,0x00 +,0xba,0x52,0x00,0xb8,0x20,0x20,0xef,0xe8,0x75,0x00,0x3c,0x20,0x75,0x2a,0xe8,0x56 +,0x00,0xba,0x52,0x00,0xb8,0x40,0x40,0xef,0xe8,0x64,0x00,0x3c,0x40,0x75,0x19,0xe8 +,0x4d,0x00,0xba,0x52,0x00,0xb8,0x80,0x80,0xef,0xe8,0x53,0x00,0x3c,0x80,0x75,0x08 +,0xe8,0x44,0x00,0xb8,0x00,0x00,0xeb,0x03,0xb8,0x28,0x00,0xa3,0xf7,0x34,0xc3,0xba +,0x5a,0x00,0xb8,0x00,0x80,0xef,0xc3,0xba,0x5a,0x00,0xb8,0x01,0x80,0xef,0xc3,0xba +,0x5a,0x00,0xb8,0x02,0x80,0xef,0xc3,0xba,0x5a,0x00,0xb8,0x03,0x80,0xef,0xc3,0xba +,0x5a,0x00,0xb8,0x04,0x80,0xef,0xc3,0xba,0x5a,0x00,0xb8,0x05,0x80,0xef,0xc3,0xba +,0x5a,0x00,0xb8,0x06,0x80,0xef,0xc3,0xba,0x5a,0x00,0xb8,0x07,0x80,0xef,0xc3,0xb9 +,0xff,0xff,0xe4,0x58,0xe4,0x54,0x3c,0x00,0x75,0x03,0x49,0x75,0xf7,0xc3,0xfa,0x32 +,0xc0,0xe6,0x56,0xe4,0x56,0x3c,0x00,0x74,0x03,0xe9,0x82,0x00,0xb0,0xff,0xe6,0x56 +,0xe4,0x56,0x3c,0xff,0x75,0x78,0xba,0x52,0x00,0xb8,0xff,0xff,0xef,0xed,0x3c,0xff +,0x75,0x6c,0xb8,0x00,0xff,0xef,0xed,0x3c,0x00,0x75,0x63,0xb0,0xff,0xe6,0x54,0xe4 +,0x54,0x3c,0xff,0x75,0x59,0x32,0xc0,0xe6,0x54,0xe4,0x54,0x3c,0x00,0x75,0x4f,0xb0 +,0x0f,0xe6,0x50,0xe4,0x50,0x24,0x0f,0x3c,0x0f,0x75,0x43,0xb0,0x00,0xe6,0x50,0xe4 +,0x50,0x24,0x0f,0x3c,0x00,0x75,0x37,0x8c,0xc8,0x8e,0xc0,0xbe,0x70,0x00,0x26,0x8b +,0x14,0x26,0x8b,0x5c,0x02,0xb8,0x00,0x00,0xef,0xed,0x23,0xc3,0x3d,0x00,0x00,0x75 +,0x1d,0xb8,0xff,0xff,0x23,0xc3,0xef,0x8b,0xc8,0xed,0x23,0xc3,0x3b,0xc1,0x75,0x0e +,0x83,0xc6,0x04,0x26,0x83,0x3c,0xff,0x75,0xd5,0xb8,0x00,0x00,0xeb,0x03,0xb8,0x2a +,0x00,0xa3,0xf7,0x34,0xc3,0xfa,0x33,0xc0,0xbf,0x00,0x20,0xb9,0x17,0x00,0xf3,0xab +,0xbf,0x00,0x30,0xb9,0x17,0x00,0xf3,0xab,0xbf,0x00,0x22,0xb9,0x40,0x00,0xf3,0xab +,0xbf,0x00,0x32,0xb9,0x40,0x00,0xf3,0xab,0xfc,0x1e,0x8c,0xc8,0x8e,0xd8,0x33,0xc0 +,0x8e,0xc0,0xbe,0x92,0x00,0xbf,0x00,0x20,0xb9,0x17,0x00,0xf3,0xa4,0xbe,0xa9,0x00 +,0xbf,0x00,0x22,0xb9,0x40,0x00,0xf3,0xa4,0x1f,0xc7,0x06,0xfb,0x34,0x64,0x00,0xba +,0x08,0x00,0xb8,0x0f,0x00,0xef,0xe8,0x82,0x01,0xe8,0x9b,0x01,0x72,0x0d,0xc7,0x06 +,0xf7,0x34,0x2c,0x00,0xc7,0x06,0xf9,0x34,0x04,0x00,0xc3,0xba,0x0a,0x00,0x33,0xc0 +,0xef,0xe8,0x98,0x01,0xe8,0xb5,0x01,0xb8,0x17,0x00,0xba,0x9c,0x00,0xef,0xb8,0x00 +,0x10,0xba,0x9a,0x00,0xef,0xb8,0x17,0x00,0xa9,0x01,0x00,0x74,0x01,0x40,0xba,0x8c +,0x00,0xef,0xb8,0x00,0x18,0xba,0x86,0x00,0xef,0xb8,0x0c,0x00,0xba,0x82,0x00,0xef +,0xba,0x02,0x00,0xed,0x25,0xf9,0xff,0x0d,0x02,0x00,0xef,0xba,0x06,0x00,0x33,0xc0 +,0xef,0xba,0x04,0x00,0xb8,0x60,0x00,0xef,0xba,0x00,0x00,0xb8,0x18,0x00,0xef,0xba +,0x80,0x00,0xb9,0xff,0xff,0xed,0xa9,0x01,0x00,0x75,0x04,0xe2,0xf8,0xeb,0x3e,0xba +,0x0a,0x00,0xed,0xa9,0x00,0x40,0x74,0x35,0xa9,0x00,0x20,0x74,0x30,0x33,0xc0,0xef +,0x51,0xb9,0xc8,0x00,0xe2,0xfe,0x59,0x1e,0x06,0x1f,0x26,0x8b,0x0e,0x02,0x30,0x83 +,0xf9,0x17,0x75,0x18,0x49,0x49,0xbe,0x02,0x20,0xbf,0x06,0x30,0xf3,0xa6,0x1f,0x23 +,0xc9,0x75,0x0a,0xff,0x0e,0xfb,0x34,0x74,0x12,0xe9,0x4d,0xff,0x1f,0xb8,0x2c,0x00 +,0xbb,0x00,0x00,0xa3,0xf7,0x34,0x89,0x1e,0xf9,0x34,0xc3,0xc7,0x06,0xfb,0x34,0x64 +,0x00,0xe8,0xd3,0x00,0x72,0x0d,0xc7,0x06,0xf7,0x34,0x2c,0x00,0xc7,0x06,0xf9,0x34 +,0x04,0x00,0xc3,0xe8,0xd6,0x00,0xe8,0xf3,0x00,0xb8,0x03,0x00,0xba,0x82,0x00,0xef +,0xb8,0x40,0x80,0xba,0x98,0x00,0xef,0xb8,0x00,0x11,0xba,0x96,0x00,0xef,0xb8,0x40 +,0x00,0xa9,0x01,0x00,0x74,0x01,0x40,0xba,0x92,0x00,0xef,0xb8,0x00,0x19,0xba,0x8e +,0x00,0xef,0xba,0x02,0x00,0xed,0x25,0xf9,0xff,0x0d,0x06,0x00,0xef,0xba,0x06,0x00 +,0x33,0xc0,0xef,0xba,0x00,0x00,0xb8,0x18,0x00,0xef,0xba,0x80,0x00,0xb9,0xff,0xff +,0xed,0xa9,0x20,0x00,0x75,0x04,0xe2,0xf8,0xeb,0x43,0xba,0x0a,0x00,0xed,0xa9,0x00 +,0x40,0x74,0x3a,0xa9,0x00,0x20,0x74,0x35,0x33,0xc0,0xef,0x51,0xb9,0xc8,0x00,0xe2 +,0xfe,0x59,0x1e,0x06,0x1f,0x26,0x8b,0x0e,0x02,0x32,0x83,0xf9,0x40,0x75,0x1d,0x49 +,0x49,0xbe,0x02,0x22,0xbf,0x06,0x32,0xf3,0xa6,0x1f,0x23,0xc9,0x75,0x0f,0xff,0x0e +,0xfb,0x34,0x74,0x03,0xe9,0x5a,0xff,0xb8,0x00,0x00,0xeb,0x0b,0x1f,0xb8,0x2c,0x00 +,0xbb,0x02,0x00,0x89,0x1e,0xf9,0x34,0xa3,0xf7,0x34,0xc3,0xba,0x02,0x00,0xb8,0x00 +,0x9c,0xef,0xba,0x00,0x00,0xb8,0x00,0x84,0xef,0x33,0xc0,0xef,0xba,0x0a,0x00,0xef +,0xba,0x0e,0x00,0x33,0xc0,0xef,0xc3,0xba,0x0a,0x00,0xb9,0xff,0xff,0xed,0x25,0x00 +,0x60,0x3d,0x00,0x60,0x74,0x04,0xe2,0xf5,0xf8,0xc3,0xf9,0xc3,0xb0,0x00,0xe6,0x56 +,0xb8,0x00,0xff,0xba,0x52,0x00,0xef,0xb9,0xff,0xff,0xba,0x58,0x00,0xed,0x25,0xef +,0x00,0x74,0x08,0xba,0x5a,0x00,0x33,0xc0,0xef,0xe2,0xef,0xc3,0xba,0x80,0x00,0xed +,0xba,0x84,0x00,0xef,0xba,0x80,0x00,0xed,0xc3,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0xc6,0x06,0xec,0x34,0x15,0x33,0xc0,0x8e,0xd8,0x8e,0xc0,0x1e,0x8c,0xc8,0xbe,0x40 +,0x54,0xbf,0x60,0xfe,0x8e,0xd8,0xb9,0x10,0x00,0xf3,0xa4,0x1f,0xc7,0x06,0x80,0x36 +,0x10,0x35,0xc7,0x06,0x8c,0x36,0x30,0x35,0x8d,0x06,0x38,0x35,0xa3,0x30,0x35,0xa3 +,0x32,0x35,0x05,0x33,0x01,0xa3,0x34,0x35,0xc7,0x06,0x36,0x35,0x50,0x01,0xc7,0x06 +,0x84,0x36,0x80,0xfe,0xc7,0x06,0x88,0x36,0xc0,0xfe,0xc6,0x06,0xc2,0xfe,0xff,0xc6 +,0x06,0x93,0x36,0x80,0xc6,0x06,0x92,0x36,0x00,0xc6,0x06,0x80,0xfe,0x80,0xc7,0x06 +,0x82,0xfe,0x54,0x50,0xc7,0x06,0x84,0xfe,0x2b,0x4d,0xe5,0xce,0xa9,0x02,0x00,0x75 +,0x08,0xc6,0x06,0x81,0xfe,0x23,0xe9,0x05,0x00,0xc6,0x06,0x81,0xfe,0x22,0xa1,0xf7 +,0x34,0xa3,0x86,0xfe,0xb8,0x48,0x34,0x86,0xe0,0xa3,0x88,0xfe,0x8d,0x06,0x4e,0x34 +,0x86,0xe0,0xa3,0x8a,0xfe,0xb8,0x58,0x34,0x86,0xe0,0xa3,0x8c,0xfe,0xb8,0x9c,0x34 +,0x86,0xe0,0xa3,0x8e,0xfe,0x8d,0x06,0x20,0x03,0x86,0xe0,0xa3,0x90,0xfe,0x33,0xc0 +,0xba,0x72,0x00,0xef,0x33,0xc0,0xba,0x74,0x00,0xef,0xba,0x76,0x00,0xef,0xb8,0x80 +,0xfe,0x86,0xe0,0xba,0x72,0x00,0xef,0xe8,0xbf,0x07,0xba,0x0c,0x01,0xb8,0x40,0x40 +,0xef,0xed,0xba,0x6a,0x00,0xb8,0x03,0x00,0xc1,0xe0,0x08,0x0d,0x03,0x00,0xef,0xb9 +,0x0a,0x00,0xe8,0x94,0x00,0xba,0x6a,0x00,0xb8,0x03,0x00,0xc1,0xe0,0x08,0xef,0xa1 +,0x32,0x34,0xa3,0xa2,0x33,0xc7,0x06,0xa6,0x33,0x04,0x00,0x8d,0x06,0xa0,0x33,0xc1 +,0xe8,0x04,0xcd,0x39,0xc7,0x06,0x90,0x36,0xff,0xff,0xe9,0xe3,0x00,0x63,0x0d,0x66 +,0x0d,0x66,0x0d,0x8a,0x0d,0xe6,0x0e,0x75,0x12,0x2e,0x0f,0x03,0x0f,0x50,0x0f,0x60 +,0x0d,0x60,0x0d,0x60,0x0d,0xed,0x0f,0xe9,0x12,0x60,0x0d,0x60,0x0d,0x60,0x0d,0x60 +,0x0d,0x60,0x0d,0x22,0x10,0x60,0x0d,0x60,0x0d,0x60,0x0d,0x60,0x0d,0xfe,0x10,0x60 +,0x0d,0x60,0x0d,0x60,0x0d,0x60,0x0d,0x60,0x0d,0x60,0x0d,0xaf,0x0f,0x32,0x10,0x37 +,0x0d,0x60,0x0d,0x60,0x0d,0x60,0x0d,0x60,0x0d,0x60,0x0d,0x60,0x0d,0x60,0x0d,0x60 +,0x0d,0x60,0x0d,0x60,0x0d,0x60,0x0d,0x60,0x0d,0x60,0x0d,0x60,0x0d,0x60,0x0d,0x60 +,0x0d,0x64,0x0e,0x00,0x0f,0x95,0x09,0x60,0x0a,0x49,0xbb,0xff,0xff,0xba,0x6a,0x00 +,0xed,0xa9,0x00,0x20,0x74,0x38,0x80,0x3e,0x80,0xfe,0x12,0x75,0x31,0xe8,0x4a,0x00 +,0xa1,0x32,0x34,0xa3,0xa2,0x33,0xc7,0x06,0xa6,0x33,0x04,0x00,0x8d,0x06,0xa0,0x33 +,0xc1,0xe8,0x04,0xcd,0x39,0xe8,0x22,0x00,0xc7,0x06,0xf3,0x34,0x46,0x00,0xc7,0x06 +,0xf5,0x34,0xff,0xff,0xc7,0x06,0x90,0x36,0xff,0xff,0x58,0xe9,0x32,0x00,0x4b,0x83 +,0xfb,0x00,0x75,0xb9,0x83,0xf9,0x00,0x75,0xb0,0xc3,0x52,0xba,0x6a,0x00,0xb8,0x03 +,0x00,0xc1,0xe0,0x08,0x0d,0x03,0x00,0xef,0x5a,0xc3,0x52,0xba,0x6a,0x00,0xb8,0x03 +,0x00,0xc1,0xe0,0x08,0xef,0x5a,0xc3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x68,0x80,0x07,0xa1,0x90,0x36,0xcd,0x35,0x8b,0x36,0x24,0x02,0x2e,0xff,0xa4,0x35 +,0x0a,0xfa,0x8a,0x26,0x94,0x36,0x88,0x26,0xe8,0x34,0xc6,0x06,0x94,0x36,0x00,0xfb +,0x22,0xe4,0x75,0x01,0xc3,0xf6,0xc4,0x20,0x74,0x7d,0xf6,0xc4,0x08,0x74,0x05,0x80 +,0x0e,0x92,0x36,0x04,0x80,0x26,0xe8,0x34,0xd7,0xc4,0x1e,0x84,0x36,0x26,0x8b,0x37 +,0x81,0xe6,0xff,0x00,0x83,0xfe,0x20,0x76,0x05,0xb0,0x01,0xe9,0x28,0x00,0x53,0x06 +,0xd1,0xe6,0x2e,0xff,0x94,0x9d,0x06,0x07,0x5b,0x26,0x88,0x47,0x02,0x3c,0xff,0x74 +,0x07,0x3c,0xfe,0x75,0x11,0xe9,0x3b,0x00,0xf6,0x06,0x92,0x36,0x08,0x75,0x34,0xf6 +,0x06,0x92,0x36,0x04,0x74,0x2d,0x80,0x26,0x92,0x36,0xf3,0x80,0x3e,0x95,0x36,0x00 +,0x75,0x21,0x26,0x80,0x3f,0x05,0x75,0x13,0xc6,0x06,0x95,0x36,0x00,0x26,0x80,0x7f +,0x06,0x00,0x74,0x07,0x26,0x8b,0x47,0x04,0xa2,0x95,0x36,0xba,0x0c,0x01,0xb8,0x40 +,0x40,0xef,0xed,0x8a,0x26,0xe8,0x34,0xf6,0xc4,0x10,0x75,0x03,0xe9,0x5b,0x00,0xf6 +,0xc4,0x04,0x74,0x05,0x80,0x0e,0x92,0x36,0x01,0x80,0x26,0xe8,0x34,0xeb,0xc4,0x3e +,0x88,0x36,0x26,0x8b,0x35,0x83,0xe6,0x7f,0x83,0xfe,0x12,0x72,0x08,0x26,0xc6,0x45 +,0x02,0x01,0xe9,0x24,0x00,0x83,0xc6,0x20,0xd1,0xe6,0x2e,0xff,0x94,0x9d,0x06,0xc4 +,0x3e,0x88,0x36,0x26,0x88,0x45,0x02,0x3c,0xff,0x75,0x0e,0xf6,0x06,0x92,0x36,0x01 +,0x74,0x14,0xf6,0x06,0x92,0x36,0x02,0x75,0x0d,0x80,0x26,0x92,0x36,0xfc,0xba,0x0c +,0x01,0xb8,0x20,0x20,0xef,0xed,0x8a,0x26,0xe8,0x34,0xf6,0xc4,0x08,0x74,0x22,0x80 +,0x26,0xe8,0x34,0xf7,0x80,0x0e,0x92,0x36,0x04,0xf6,0x06,0x92,0x36,0x08,0x74,0x11 +,0x80,0x26,0x92,0x36,0xf3,0xba,0x0c,0x01,0xb8,0x40,0x40,0xef,0xed,0x8a,0x26,0xe8 +,0x34,0xf6,0xc4,0x04,0x74,0x22,0x80,0x26,0xe8,0x34,0xfb,0x80,0x0e,0x92,0x36,0x01 +,0xf6,0x06,0x92,0x36,0x02,0x75,0x11,0x80,0x26,0x92,0x36,0xfe,0xba,0x0c,0x01,0xb8 +,0x20,0x20,0xef,0xed,0x8a,0x26,0xe8,0x34,0xf6,0xc4,0x01,0x74,0x67,0x80,0x26,0xe8 +,0x34,0xfe,0x80,0x3e,0xe8,0xff,0x00,0x74,0x39,0x80,0x3e,0xe8,0xff,0x04,0x74,0x32 +,0x80,0x3e,0xe8,0xff,0x01,0x75,0x21,0xe5,0x80,0xa9,0x00,0x07,0x74,0x0a,0xba,0x9e +,0x00,0xb8,0x00,0x02,0xef,0xe9,0xef,0xff,0xc6,0x06,0xe8,0xff,0x03,0xba,0x0c,0x01 +,0xb8,0x08,0x08,0xef,0xed,0xe9,0x28,0x00,0x80,0x3e,0xe8,0xff,0x03,0x74,0x06,0xe9 +,0x1e,0x00,0xe9,0x00,0x00,0xba,0x10,0x01,0xb8,0x02,0x02,0xef,0xed,0xe5,0x00,0x0d +,0x18,0x00,0xe7,0x00,0xe5,0x82,0x0d,0x02,0x00,0xe7,0x82,0xc6,0x06,0xe8,0xff,0x04 +,0x8a,0x26,0xe8,0x34,0xf6,0xc4,0x02,0x74,0x0d,0x80,0x26,0xe8,0x34,0xfd,0x80,0x26 +,0x92,0x36,0xbf,0xe8,0x4f,0x0b,0xfa,0xa0,0xe8,0x34,0x08,0x06,0x94,0x36,0xc6,0x06 +,0xe8,0x34,0x00,0xfb,0xc3,0xe8,0xe7,0x0f,0xc4,0x1e,0x84,0x36,0x2e,0xff,0x16,0x01 +,0x07,0x26,0x88,0x47,0x02,0xe9,0x7e,0xfe,0xe8,0x2d,0x10,0xc4,0x1e,0x84,0x36,0x2e +,0xff,0x16,0x03,0x07,0x26,0x88,0x47,0x02,0xe9,0x6b,0xfe,0x8e,0x06,0x26,0x02,0x2e +,0xff,0x16,0x07,0x07,0xc3,0xc3,0x83,0x3e,0xf5,0x34,0x00,0x74,0x0f,0xff,0x0e,0xf3 +,0x34,0x75,0x09,0xe8,0xc4,0xfd,0xc7,0x06,0xf5,0x34,0x00,0x00,0xf6,0x06,0x93,0x36 +,0x20,0x74,0x30,0xa1,0xc2,0x34,0x3b,0x06,0xe9,0x34,0xa3,0xe9,0x34,0x74,0x24,0x80 +,0x3e,0x95,0x36,0x00,0x75,0x1d,0xf7,0x06,0xe6,0x34,0x20,0x00,0x74,0x12,0xa9,0x20 +,0x00,0x74,0x0d,0x83,0x26,0xc2,0x34,0xdf,0x83,0x26,0xe9,0x34,0xdf,0xe9,0x03,0x00 +,0xe8,0xdd,0x09,0xba,0x06,0x01,0xed,0x8b,0xd0,0x81,0xe2,0x00,0xc0,0xc1,0xea,0x0e +,0x03,0x16,0x74,0x34,0xc1,0xe0,0x02,0x11,0x06,0x72,0x34,0x73,0x04,0xff,0x06,0x74 +,0x34,0xba,0x02,0x01,0xed,0x8b,0xd0,0x81,0xe2,0x00,0xc0,0xc1,0xea,0x0e,0x03,0x16 +,0x70,0x34,0xc1,0xe0,0x02,0x11,0x06,0x6e,0x34,0x73,0x04,0xff,0x06,0x70,0x34,0xc7 +,0x06,0xa6,0x33,0x04,0x00,0xc7,0x06,0xaa,0x33,0x00,0x00,0x8d,0x06,0xa0,0x33,0xc1 +,0xe8,0x04,0xcd,0x39,0xc3,0x95,0x09,0x95,0x09,0x65,0x09,0x78,0x09,0x95,0x09,0x95 +,0x09,0x91,0x07,0x95,0x09,0x96,0x09,0x8b,0x09,0x95,0x09,0x95,0x09,0x95,0x09,0x95 +,0x09,0x95,0x09,0x95,0x09,0x8b,0xc0,0x8b,0xc0,0x8b,0xc0,0x8b,0xc0,0x8b,0xc0,0x90 +,0xf6,0x06,0x93,0x36,0x20,0x75,0x03,0xe9,0xcc,0x00,0x8c,0xc0,0x40,0x8e,0xc0,0x26 +,0x8b,0x0e,0x06,0x00,0x86,0xe9,0x26,0x89,0x0e,0x06,0x00,0x8c,0xc2,0xc1,0xe2,0x04 +,0xbe,0x0e,0x00,0x26,0xa1,0x04,0x00,0xd0,0xe0,0x24,0xc0,0x8a,0xe0,0xc0,0xec,0x04 +,0x0a,0xc4,0x26,0xa2,0x05,0x00,0x26,0xa1,0x08,0x00,0xa9,0x00,0xc0,0x74,0x03,0xe9 +,0x9e,0x00,0x26,0xf6,0x06,0x10,0x00,0x80,0x75,0x03,0xe9,0x0a,0x00,0x26,0xa0,0x16 +,0x00,0x24,0x1f,0x32,0xe4,0x03,0xf0,0x80,0x3e,0xec,0x34,0x06,0x72,0x5c,0x80,0x3e +,0x95,0x36,0x00,0x75,0x66,0x8b,0xfa,0x33,0xdb,0x8e,0xc3,0x26,0x89,0x1d,0x26,0x88 +,0x5d,0x04,0x51,0x50,0xc4,0x1e,0x8c,0x36,0xb9,0x0f,0x00,0x33,0xc0,0xe8,0x21,0x09 +,0x58,0x59,0x0b,0xdb,0x74,0x34,0xfe,0x0e,0xe6,0x3a,0x26,0xc6,0x07,0x81,0x26,0xc6 +,0x47,0x01,0x00,0x26,0xc6,0x47,0x02,0xff,0x26,0xc7,0x47,0x04,0x00,0x00,0x26,0x89 +,0x4f,0x0a,0x86,0xf2,0x26,0x89,0x57,0x06,0x26,0x89,0x77,0x08,0x26,0xc6,0x47,0x09 +,0x00,0x26,0xc6,0x47,0x0c,0x02,0xe8,0x8c,0x09,0xc3,0xff,0x06,0xec,0x33,0x8c,0xc0 +,0x48,0x8e,0xc0,0xfa,0xe8,0x97,0x10,0xfb,0xe9,0xeb,0xff,0x8c,0xc0,0x48,0x8e,0xc0 +,0xfa,0xe8,0x8a,0x10,0xfb,0xc3,0x8c,0xc0,0x8e,0xc0,0xfa,0xe8,0x80,0x10,0xfb,0xc3 +,0x80,0x3e,0x95,0x36,0x00,0x75,0x03,0xe9,0xc2,0x00,0xbf,0x08,0x00,0x26,0xf6,0x06 +,0x10,0x00,0x80,0x75,0x05,0x03,0xfe,0xe9,0x0c,0x00,0x26,0xa0,0x16,0x00,0x24,0x1f +,0x32,0xe4,0x03,0xf0,0x03,0xfe,0xa0,0x95,0x36,0x3c,0x00,0x75,0x03,0xe9,0x9c,0x00 +,0x3c,0x01,0x74,0x0b,0x3c,0x02,0x74,0x14,0x3c,0x03,0x74,0x1d,0xe9,0x8d,0x00,0xc6 +,0x06,0x96,0x36,0x01,0xe8,0x3c,0x01,0x72,0x27,0xe9,0x80,0x00,0xc6,0x06,0x96,0x36 +,0x02,0xe8,0x83,0x00,0x72,0x1a,0xe9,0x73,0x00,0xc6,0x06,0x96,0x36,0x01,0xe8,0x22 +,0x01,0x72,0x0d,0xc6,0x06,0x96,0x36,0x02,0xe8,0x6c,0x00,0x72,0x03,0xe9,0x5c,0x00 +,0x53,0x06,0x50,0xc4,0x1e,0x8c,0x36,0xb9,0x0b,0x00,0x33,0xc0,0xe8,0x42,0x08,0x58 +,0x26,0xc6,0x07,0x82,0x26,0xc6,0x47,0x02,0xff,0x8d,0x06,0xe0,0xfe,0x86,0xc4,0x26 +,0x89,0x47,0x06,0xa0,0x96,0x36,0x26,0x88,0x47,0x08,0xe8,0xc8,0x08,0x07,0x5b,0x83 +,0x26,0xad,0x36,0xfe,0xa1,0xad,0x36,0xe7,0x04,0xba,0x10,0x01,0xb8,0x80,0x80,0xef +,0xed,0xba,0x10,0x01,0xb8,0x02,0x02,0xef,0xed,0x52,0xba,0xe0,0x00,0xb8,0x41,0x10 +,0xef,0x5a,0xb8,0x9c,0x03,0xcd,0x39,0xc6,0x06,0x95,0x36,0x00,0x8c,0xc0,0x48,0x8e +,0xc0,0xfa,0xe8,0xa9,0x0f,0xfb,0xc3,0x1e,0x06,0x1f,0x06,0x33,0xc0,0x8e,0xc0,0x8b +,0xf0,0x8d,0x3e,0x20,0xf3,0x51,0xb1,0x0a,0x26,0x83,0x7d,0x0c,0x01,0x75,0x2a,0x57 +,0x26,0x83,0x7d,0x0e,0x00,0x74,0x06,0xe8,0x2f,0x00,0xe9,0x03,0x00,0xe8,0x66,0x07 +,0x5f,0x73,0x16,0x33,0xc0,0x8e,0xd8,0x26,0x8b,0x4d,0x12,0x8d,0x75,0x20,0x8d,0x3e +,0xe0,0xfe,0xf3,0xa4,0x59,0x07,0x1f,0xf9,0xc3,0xfe,0xc9,0x74,0x07,0x81,0xc7,0x20 +,0x01,0xe9,0xc4,0xff,0x59,0x07,0x1f,0xf8,0xc3,0x51,0x50,0x53,0x56,0x52,0x57,0x33 +,0xdb,0x26,0x8a,0x5d,0x0e,0x26,0x8b,0x4d,0x12,0x8d,0x7d,0x20,0x5a,0x87,0xd7,0x26 +,0x8a,0x45,0x14,0x87,0xd7,0x42,0x32,0xff,0x80,0xff,0x08,0x75,0x08,0xfe,0xcb,0x22 +,0xdb,0x75,0xea,0x33,0xdb,0x23,0xdb,0x74,0x06,0xfe,0xc7,0xd0,0xc8,0x73,0x0c,0x50 +,0x26,0x8a,0x05,0x38,0x04,0x58,0x74,0x03,0xe9,0x0a,0x00,0x49,0x46,0x47,0x23,0xc9 +,0x74,0x0a,0xe9,0xd3,0xff,0x5a,0x5e,0x5b,0x58,0x59,0xf8,0xc3,0x5a,0x5e,0x5b,0x58 +,0x59,0xf9,0xc3,0x1e,0x06,0x1f,0x06,0x33,0xc0,0x8e,0xc0,0x86,0xcd,0x2b,0xce,0x8b +,0xf7,0x8b,0xc1,0x33,0xc9,0x80,0x3c,0xff,0x74,0x16,0x80,0xf9,0x06,0x73,0x09,0x32 +,0xc9,0x46,0x48,0x74,0x2e,0xe9,0xed,0xff,0x3d,0x60,0x00,0x73,0x0c,0xe9,0x23,0x00 +,0xfe,0xc1,0x46,0x48,0x74,0x1d,0xe9,0xdc,0xff,0xb8,0x10,0x00,0x8d,0x3e,0x18,0x34 +,0x32,0xed,0xb1,0x06,0xf3,0xa6,0x74,0x03,0xe9,0x08,0x00,0x48,0x23,0xc0,0x74,0x07 +,0xe9,0xe9,0xff,0x07,0x1f,0xf8,0xc3,0x8d,0x36,0x18,0x34,0x33,0xc0,0x8e,0xd8,0x8d +,0x3e,0xe0,0xfe,0xb8,0x10,0x00,0xb9,0x06,0x00,0x56,0xf3,0xa4,0x5e,0x48,0x3d,0x00 +,0x00,0x75,0xf3,0x07,0x1f,0xf9,0xc3,0xff,0x06,0xe4,0x33,0xc6,0x06,0xeb,0x34,0x00 +,0x26,0x8b,0x45,0x06,0x86,0xe0,0xc1,0xe8,0x04,0x48,0x06,0x8e,0xc0,0xfe,0x06,0xe6 +,0x3a,0xfa,0xe8,0x69,0x0e,0xfb,0x07,0xb0,0xff,0xc3,0x00,0x00,0x00,0x00,0x00,0x00 +,0xb0,0x01,0xc3,0xb0,0x00,0xc3,0xf6,0x06,0x93,0x36,0x20,0x75,0x03,0xb0,0x04,0xc3 +,0x8b,0x0e,0x97,0x36,0x81,0xe1,0x80,0x30,0x26,0x8b,0x47,0x04,0x25,0x7f,0xcf,0x0b +,0xc1,0xa3,0x97,0x36,0xa3,0xe6,0x34,0xb0,0x00,0xc3,0xf6,0x06,0x93,0x36,0x20,0x74 +,0x03,0xb0,0x03,0xc3,0x26,0x8b,0x47,0x08,0xa3,0x97,0x36,0xa3,0xe6,0x34,0x26,0x8a +,0x47,0x20,0xa2,0xfd,0x34,0x3c,0x01,0x75,0x06,0xc7,0x06,0xa1,0x36,0x00,0x00,0x26 +,0x8a,0x47,0x21,0xa2,0xfe,0x34,0x26,0x8b,0x47,0x0a,0xa3,0x18,0x34,0xa3,0x58,0x34 +,0x26,0x8b,0x47,0x0c,0xa3,0x1a,0x34,0xa3,0x5a,0x34,0x26,0x8b,0x47,0x0e,0xa3,0x1c +,0x34,0xa3,0x5c,0x34,0xc6,0x06,0x2a,0x34,0xc0,0x26,0x8b,0x47,0x14,0x25,0x7f,0xff +,0x09,0x06,0x2c,0x34,0x26,0x8b,0x47,0x16,0x25,0xff,0xfe,0x25,0xff,0xfc,0x09,0x06 +,0x2e,0x34,0xc6,0x06,0x00,0x34,0xc0,0x26,0x8b,0x47,0x10,0xa3,0x02,0x34,0x26,0x8b +,0x47,0x12,0xa3,0x04,0x34,0x06,0x53,0xe8,0x84,0x0a,0x5b,0x07,0x3d,0x00,0x00,0x75 +,0x07,0x80,0x0e,0x92,0x36,0x08,0xb0,0xfe,0xc3,0xb9,0x00,0x01,0xa1,0xac,0x33,0x33 +,0xd2,0xf7,0xf9,0xa3,0xae,0x33,0x91,0x49,0x33,0xd2,0xf7,0xe9,0x05,0x00,0x3b,0xa3 +,0x46,0x34,0xbf,0x00,0x3b,0x89,0x3e,0x44,0x34,0xba,0x68,0x00,0xb8,0xe0,0xe0,0xef +,0xa1,0xae,0x33,0xe7,0x62,0xa1,0xae,0x33,0xba,0x08,0x01,0xef,0xa1,0x44,0x34,0xe7 +,0x64,0xa1,0x44,0x34,0xba,0x0a,0x01,0xef,0xb8,0x00,0x01,0x2d,0x04,0x00,0x0d,0x00 +,0x10,0xe7,0x92,0xc3,0x3d,0x00,0x00,0x74,0x0a,0x26,0x89,0x47,0x07,0xe8,0x83,0x3a +,0xb0,0x07,0xc3,0xa1,0xae,0x33,0x26,0x89,0x47,0x2b,0xa1,0x44,0x34,0x26,0x89,0x47 +,0x2d,0xa1,0x46,0x34,0x26,0x89,0x47,0x2f,0x80,0x0e,0x93,0x36,0x20,0xa1,0x88,0x36 +,0x86,0xe0,0x26,0x89,0x47,0x08,0xa1,0x84,0x36,0x86,0xe0,0x26,0x89,0x47,0x0a,0xa1 +,0x80,0x36,0x86,0xe0,0x26,0x89,0x47,0x0c,0xb8,0x60,0xfe,0x86,0xe0,0x26,0x89,0x47 +,0x0e,0xa0,0xa1,0x36,0x26,0x88,0x47,0x10,0x8b,0x36,0x88,0x36,0x26,0xc6,0x44,0x02 +,0xff,0xe5,0x9e,0xa9,0x00,0x08,0x74,0x0c,0xba,0x84,0x00,0xed,0x0d,0x08,0x00,0xef +,0xba,0x8e,0x00,0xef,0xe5,0x02,0x25,0xf9,0xff,0xe7,0x02,0xba,0x10,0x01,0xb8,0x02 +,0x02,0xef,0xed,0xb0,0x00,0xc3,0xf6,0x06,0x93,0x36,0x20,0x75,0x03,0xb0,0x01,0xc3 +,0x80,0x26,0x93,0x36,0x9f,0xe8,0x8d,0x0a,0x80,0x0e,0x92,0x36,0x08,0xb0,0xfe,0xc3 +,0xb0,0x00,0xc3,0xf6,0x06,0x93,0x36,0x20,0x75,0x03,0xb0,0x04,0xc3,0xc6,0x06,0x2a +,0x34,0xc0,0x26,0x8b,0x47,0x06,0x25,0x7f,0xff,0xa3,0x2c,0x34,0x26,0x8b,0x47,0x08 +,0x25,0xff,0xfe,0x25,0xff,0xfc,0xa3,0x2e,0x34,0xcd,0x52,0xb0,0x00,0xc3,0xf6,0x06 +,0x93,0x36,0x20,0x75,0x03,0xb0,0x04,0xc3,0xc6,0x06,0x00,0x34,0xc0,0x26,0x8b,0x47 +,0x06,0xa3,0x02,0x34,0x26,0x8b,0x47,0x08,0xa3,0x04,0x34,0xcd,0x52,0xb0,0x00,0xc3 +,0xf6,0x06,0x93,0x36,0x20,0x75,0x03,0xb0,0x04,0xc3,0x57,0x8d,0x7f,0x06,0x51,0xb9 +,0x07,0x00,0x33,0xc0,0xf3,0xab,0x59,0x8d,0x7f,0x06,0xa1,0x7a,0x34,0x03,0x06,0x39 +,0x37,0x26,0x88,0x05,0xa1,0x95,0x37,0x26,0x88,0x45,0x02,0xa1,0x80,0x34,0x03,0x06 +,0x76,0x34,0x26,0x88,0x45,0x07,0xa1,0xc6,0x34,0x26,0x88,0x45,0x09,0xa1,0xd8,0x33 +,0x26,0x88,0x45,0x0a,0x33,0xc0,0xa3,0x7a,0x34,0xa3,0x39,0x37,0xa3,0x95,0x37,0xa3 +,0x80,0x34,0xa3,0x76,0x34,0xa3,0xc6,0x34,0xa3,0xd8,0x33,0x5f,0xb0,0x00,0xc3,0xf6 +,0x06,0x93,0x36,0x20,0x75,0x03,0xb0,0x04,0xc3,0x26,0x8b,0x4f,0x04,0x83,0xf9,0x06 +,0x74,0x12,0x83,0xf9,0x04,0x74,0x0d,0x83,0xf9,0x00,0x74,0x08,0x83,0xf9,0x02,0x74 +,0x03,0xb0,0x01,0xc3,0x89,0x0e,0xe8,0x3a,0x83,0x26,0xab,0x36,0xf9,0x09,0x0e,0xab +,0x36,0xe5,0x02,0x25,0xf9,0xff,0x0b,0xc1,0xe7,0x02,0xb0,0x00,0xc3,0xf6,0x06,0x93 +,0x36,0x20,0x75,0x03,0xb0,0x04,0xc3,0x26,0x8b,0x4f,0x04,0x80,0xf9,0xff,0x74,0x08 +,0x80,0xf9,0x00,0x74,0x10,0xb0,0x01,0xc3,0x83,0x0e,0xad,0x36,0x02,0xa1,0xad,0x36 +,0xe7,0x04,0xe9,0x0a,0x00,0x83,0x26,0xad,0x36,0xfd,0xa1,0xad,0x36,0xe7,0x04,0xb0 +,0x00,0xc3,0xf6,0x06,0x93,0x36,0x20,0x75,0x03,0xb0,0x04,0xc3,0xe8,0xd5,0x04,0xb0 +,0x00,0xc3,0xf6,0x06,0x93,0x36,0x80,0x75,0x03,0xb0,0x01,0xc3,0x26,0x83,0x7f,0x06 +,0x05,0x75,0x03,0xe9,0x9d,0x00,0x26,0x8b,0x57,0x04,0x26,0x8b,0x47,0x08,0x26,0x81 +,0x7f,0x06,0x00,0x80,0x75,0x08,0xed,0x26,0x89,0x47,0x0a,0xe9,0x9d,0x00,0x26,0x83 +,0x7f,0x06,0x01,0x75,0x04,0xef,0xe9,0x92,0x00,0x26,0x81,0x7f,0x06,0x01,0x80,0x75 +,0x09,0xef,0xed,0x26,0x89,0x47,0x0a,0xe9,0x81,0x00,0x26,0x83,0x7f,0x06,0x02,0x75 +,0x07,0x26,0x21,0x47,0x04,0xe9,0x73,0x00,0x26,0x81,0x7f,0x06,0x02,0x80,0x75,0x0c +,0x26,0x21,0x47,0x04,0xed,0x26,0x89,0x47,0x0a,0xe9,0x5f,0x00,0x26,0x83,0x7f,0x06 +,0x03,0x75,0x07,0x26,0x09,0x47,0x04,0xe9,0x51,0x00,0x26,0x81,0x7f,0x06,0x03,0x80 +,0x75,0x0c,0x26,0x09,0x47,0x04,0xed,0x26,0x89,0x47,0x0a,0xe9,0x3d,0x00,0x26,0x83 +,0x7f,0x06,0x04,0x75,0x07,0x26,0x31,0x47,0x04,0xe9,0x2f,0x00,0x26,0x81,0x7f,0x06 +,0x04,0x80,0x75,0x0c,0x26,0x31,0x47,0x04,0xed,0x26,0x89,0x47,0x0a,0xe9,0x1b,0x00 +,0xb0,0x01,0xc3,0xfa,0x53,0x26,0x8b,0x4f,0x08,0x0b,0xc9,0x74,0x0c,0x8d,0x1e,0xe0 +,0xfe,0xe8,0x52,0xff,0x83,0xc3,0x08,0xe2,0xf8,0x5b,0xfb,0xb0,0x00,0xc3,0xf6,0x06 +,0x93,0x36,0x80,0x75,0x0a,0xf6,0x06,0x93,0x36,0x20,0x75,0x03,0xb0,0x01,0xc3,0x8d +,0x3e,0xe0,0xfe,0xe5,0x00,0x26,0x89,0x05,0xe5,0x02,0x26,0x89,0x45,0x02,0xa1,0xad +,0x36,0x26,0x89,0x45,0x04,0xe5,0x06,0x26,0x89,0x45,0x06,0xe5,0x08,0x26,0x89,0x45 +,0x08,0xe5,0x0a,0x26,0x89,0x45,0x0a,0xe5,0x0e,0x26,0x89,0x45,0x0c,0xe5,0x48,0x26 +,0x89,0x45,0x0e,0xe5,0x4a,0x26,0x89,0x45,0x10,0xe5,0x4c,0x26,0x89,0x45,0x12,0xa1 +,0xb7,0x36,0x26,0x89,0x45,0x14,0xe5,0x50,0x26,0x89,0x45,0x16,0xe5,0x52,0x26,0x89 +,0x45,0x18,0xe5,0x54,0x26,0x89,0x45,0x1a,0xe5,0x56,0x26,0x89,0x45,0x1c,0xe5,0x58 +,0x26,0x89,0x45,0x1e,0xe5,0x62,0x26,0x89,0x45,0x20,0xe5,0x64,0x26,0x89,0x45,0x22 +,0xe5,0x66,0x26,0x89,0x45,0x24,0xe5,0x68,0x26,0x89,0x45,0x26,0xe5,0x6a,0x26,0x89 +,0x45,0x28,0xe5,0x6c,0x26,0x89,0x45,0x2a,0xe5,0x70,0x26,0x89,0x45,0x2c,0xe5,0x72 +,0x26,0x89,0x45,0x2e,0xe5,0x74,0x26,0x89,0x45,0x30,0xe5,0x76,0x26,0x89,0x45,0x32 +,0xe5,0x7c,0x26,0x89,0x45,0x34,0xe5,0x7e,0x26,0x89,0x45,0x36,0xe5,0x80,0x26,0x89 +,0x45,0x38,0xe5,0x82,0x26,0x89,0x45,0x3a,0xe5,0x86,0x26,0x89,0x45,0x3c,0xe5,0x88 +,0x26,0x89,0x45,0x3e,0xe5,0x9a,0x26,0x89,0x45,0x40,0xe5,0x9e,0x26,0x89,0x45,0x42 +,0xe5,0xcc,0x26,0x89,0x45,0x44,0xe5,0xce,0x26,0x89,0x45,0x46,0xe5,0xd0,0x26,0x89 +,0x45,0x48,0xe5,0xd2,0x26,0x89,0x45,0x4a,0xba,0x00,0x01,0xed,0x11,0x06,0x66,0x34 +,0x73,0x04,0xff,0x06,0x68,0x34,0x26,0x89,0x45,0x4c,0xba,0x02,0x01,0xed,0xc1,0xe0 +,0x02,0x11,0x06,0x6e,0x34,0x73,0x04,0xff,0x06,0x70,0x34,0x26,0x89,0x45,0x4e,0xba +,0x04,0x01,0xed,0x11,0x06,0x6a,0x34,0x73,0x04,0xff,0x06,0x6c,0x34,0x26,0x89,0x45 +,0x50,0xba,0x06,0x01,0xed,0xc1,0xe0,0x02,0x11,0x06,0x72,0x34,0x73,0x04,0xff,0x06 +,0x74,0x34,0x26,0x89,0x45,0x52,0xba,0x08,0x01,0xed,0x26,0x89,0x45,0x54,0xba,0x0a +,0x01,0xed,0x26,0x89,0x45,0x56,0xba,0x0c,0x01,0xed,0x26,0x89,0x45,0x58,0xba,0x0e +,0x01,0xed,0x01,0x06,0x7a,0x34,0x26,0x89,0x45,0x5e,0xba,0x10,0x01,0xed,0x26,0x89 +,0x45,0x5c,0xb0,0x00,0xc3,0xf6,0x06,0x93,0x36,0x80,0x74,0x07,0xf6,0x06,0x93,0x36 +,0x20,0x75,0x03,0xb0,0x01,0xc3,0x26,0x80,0x7f,0x06,0x00,0x75,0x30,0x80,0x3e,0x95 +,0x36,0x00,0x74,0x52,0xc6,0x06,0x95,0x36,0x00,0x83,0x26,0xad,0x36,0xfe,0xa1,0xad +,0x36,0xe7,0x04,0xba,0x10,0x01,0xb8,0x80,0x80,0xef,0xed,0xba,0x10,0x01,0xb8,0x02 +,0x02,0xef,0xed,0xba,0xe0,0x00,0xb8,0x00,0x10,0xef,0xb0,0x00,0xc3,0x26,0x8b,0x47 +,0x04,0x3d,0x00,0x00,0x74,0x20,0x3d,0x03,0x00,0x77,0x1b,0xba,0x10,0x01,0xb8,0x02 +,0x00,0xef,0xba,0xe0,0x00,0xb8,0x01,0x10,0xef,0x83,0x0e,0xad,0x36,0x01,0xa1,0xad +,0x36,0xe7,0x04,0xb0,0x00,0xc3,0xb0,0x06,0xc3,0xf6,0x06,0x93,0x36,0x80,0x75,0x03 +,0xb0,0x01,0xc3,0x26,0x83,0x7f,0x04,0x01,0x74,0x0a,0x26,0x83,0x7f,0x04,0x02,0x74 +,0x19,0xb0,0x06,0xc3,0x26,0x83,0x7f,0x06,0x0c,0x77,0xf6,0x26,0x83,0x7f,0x0a,0x60 +,0x77,0xef,0xe8,0x10,0x00,0x72,0x0b,0xb0,0x46,0xc3,0xe8,0x4e,0x00,0x72,0x03,0xb0 +,0x46,0xc3,0xb0,0x00,0xc3,0x51,0xb1,0x0a,0x8b,0x3e,0x20,0xf3,0x26,0x83,0x7d,0x0c +,0x02,0x75,0x03,0xe9,0x0e,0x00,0xfe,0xc9,0x74,0x07,0x81,0xc7,0x20,0x01,0xe9,0xeb +,0xff,0x59,0xf8,0xc3,0x57,0x8d,0x7d,0x0e,0x8d,0x77,0x06,0xb9,0x12,0x00,0xf3,0xa4 +,0x8d,0x7d,0x20,0x8d,0x36,0xe0,0xfe,0x26,0x8b,0x4d,0x12,0xf3,0xa4,0xff,0x06,0x01 +,0x35,0x5f,0x26,0xc7,0x45,0x0c,0x01,0x00,0x59,0xf9,0xc3,0x51,0xb1,0x0a,0x8d,0x3e +,0x20,0xf3,0x8d,0x36,0xe0,0xfe,0x26,0x83,0x7d,0x0c,0x01,0x75,0x1b,0x57,0xe8,0x25 +,0x00,0x5f,0x73,0x14,0x33,0xc0,0xb9,0x20,0x01,0xf3,0xaa,0x26,0xc7,0x45,0x0c,0x02 +,0x00,0xff,0x0e,0x01,0x35,0x59,0xf9,0xc3,0xfe,0xc9,0x74,0x07,0x81,0xc7,0x20,0x01 +,0xe9,0xd3,0xff,0x59,0xf8,0xc3,0x51,0x26,0x8b,0x4d,0x12,0x8d,0x7d,0x20,0xf3,0xa6 +,0x74,0x03,0x59,0xf8,0xc3,0x59,0xf9,0xc3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x80,0x3e,0xec,0x34,0x06,0x72,0x33,0xff,0x06,0xf0,0x33,0x50,0xc4,0x1e,0x8c,0x36 +,0xb9,0x0f,0x00,0x33,0xc0,0xe8,0x29,0x00,0x58,0x81,0x26,0xc2,0x34,0xdf,0x7f,0x81 +,0x26,0xe9,0x34,0xdf,0x7f,0x0b,0xdb,0x74,0x11,0x26,0xc6,0x07,0x84,0x26,0xc6,0x47 +,0x02,0xff,0x26,0x89,0x47,0x06,0xe8,0xac,0x00,0xc3,0xff,0x06,0xea,0x33,0xe9,0xf5 +,0xff,0x57,0x26,0x8b,0x3f,0x03,0xf9,0x26,0x3b,0x7f,0x02,0x74,0x16,0x26,0x3b,0x7f +,0x04,0x7c,0x2a,0x3d,0x00,0x00,0x75,0x13,0x8d,0x7f,0x08,0x03,0xf9,0x26,0x3b,0x7f +,0x02,0x7c,0x14,0xff,0x06,0xde,0x33,0x33,0xdb,0x5f,0xc3,0x26,0x8b,0x7f,0x02,0x26 +,0x89,0x3f,0x03,0xf9,0xe9,0x06,0x00,0x26,0x89,0x3f,0x26,0x29,0x0f,0x26,0xc7,0x05 +,0xff,0xff,0x26,0x87,0x3f,0x26,0x89,0x0d,0x8d,0x5d,0x02,0x50,0x8b,0xfb,0x83,0xe9 +,0x02,0x33,0xc0,0xf3,0xaa,0x58,0xfe,0x0e,0xec,0x34,0x5f,0xc3,0x8b,0x7c,0x02,0x3b +,0x3c,0x74,0x2f,0x83,0x3d,0xff,0x75,0x0b,0x8d,0x7c,0x08,0x89,0x7c,0x02,0x83,0x3d +,0xff,0x74,0x1e,0x8a,0x45,0x02,0x3c,0x81,0x75,0x0c,0x80,0x3e,0xeb,0x34,0x00,0x74 +,0x05,0x33,0xc0,0xe9,0x0b,0x00,0x8b,0x0d,0x01,0x4c,0x02,0x8d,0x75,0x02,0x83,0xe9 +,0x02,0xc3,0x80,0x3e,0xec,0x34,0x06,0x72,0x05,0x33,0xc0,0xe9,0xf3,0xff,0xff,0x06 +,0xee,0x33,0xe9,0xbe,0xff,0xf6,0x06,0x92,0x36,0x40,0x74,0x01,0xc3,0x57,0x56,0x51 +,0x52,0x8b,0x36,0x8c,0x36,0xe8,0xa4,0xff,0x75,0x03,0xe9,0x1a,0x00,0xe9,0x1c,0x00 +,0xfe,0x06,0xec,0x34,0xc4,0x3e,0x80,0x36,0xf3,0xa4,0x80,0x0e,0x92,0x36,0x40,0xba +,0x0c,0x01,0xb8,0x80,0x80,0xef,0xed,0x5a,0x59,0x5e,0x5f,0xc3,0xff,0x06,0xe0,0x33 +,0x80,0x3c,0x81,0x75,0x0c,0xff,0x06,0xe2,0x33,0xc6,0x06,0xeb,0x34,0x01,0xe9,0xcf +,0xff,0x80,0x3c,0x84,0x75,0x07,0xff,0x06,0xe6,0x33,0xe9,0xc3,0xff,0xff,0x06,0xe8 +,0x33,0xe9,0xbc,0xff,0x8d,0x3e,0xe0,0xfe,0xa1,0x72,0x34,0xc7,0x06,0x72,0x34,0x00 +,0x00,0x89,0x05,0xa1,0x74,0x34,0xc7,0x06,0x74,0x34,0x00,0x00,0x89,0x45,0x02,0xba +,0x04,0x01,0xed,0x89,0x45,0x04,0xc7,0x45,0x06,0x00,0x00,0xa1,0x6e,0x34,0xc7,0x06 +,0x6e,0x34,0x00,0x00,0x89,0x45,0x08,0xa1,0x70,0x34,0xc7,0x06,0x70,0x34,0x00,0x00 +,0x89,0x45,0x0a,0xba,0x00,0x01,0xed,0x89,0x45,0x0c,0xc7,0x45,0x0e,0x00,0x00,0x32 +,0xe4,0xba,0x0e,0x01,0xec,0x89,0x45,0x10,0xa1,0x7e,0x34,0xc7,0x06,0x7e,0x34,0x00 +,0x00,0x89,0x45,0x12,0xa1,0x8c,0x34,0xc7,0x06,0x8c,0x34,0x00,0x00,0x89,0x45,0x14 +,0xa1,0x8a,0x34,0xc7,0x06,0x8a,0x34,0x00,0x00,0x89,0x45,0x16,0xa1,0x7c,0x34,0xc7 +,0x06,0x7c,0x34,0x00,0x00,0x89,0x45,0x18,0xa1,0x88,0x34,0xc7,0x06,0x88,0x34,0x00 +,0x00,0x89,0x45,0x1a,0xa1,0xca,0x33,0xc7,0x06,0xca,0x33,0x00,0x00,0x89,0x45,0x1c +,0xa1,0x78,0x34,0xc7,0x06,0x78,0x34,0x00,0x00,0x89,0x45,0x1e,0xa1,0xc6,0x34,0xc7 +,0x06,0xc6,0x34,0x00,0x00,0x89,0x45,0x20,0xc3,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0xfa,0x33,0xc0,0x8e,0xd8,0x8e,0xc0,0xb8,0xa0,0x01,0xc1,0xe8,0x04,0x8e,0xd0,0x8d +,0x26,0x80,0x00,0xe8,0x00,0x01,0xe8,0x10,0xeb,0x8b,0x1e,0xf7,0x34,0x8b,0x16,0xf9 +,0x34,0x8b,0x36,0xff,0x34,0x33,0xc0,0xb9,0xef,0xff,0x8d,0x3e,0x14,0x00,0x2b,0xcf +,0x2b,0xce,0xd1,0xe9,0xf3,0xab,0x89,0x1e,0xf7,0x34,0x89,0x16,0xf9,0x34,0x83,0xfe +,0x00,0x74,0x0c,0xb9,0xef,0xff,0xbf,0x80,0xfe,0x2b,0xcf,0xd1,0xe9,0xf3,0xab,0xb9 +,0xff,0xff,0x81,0xe9,0x00,0x3b,0x83,0xfe,0x00,0x74,0x03,0xe9,0x1b,0x00,0x51,0x1e +,0xb8,0x00,0xe0,0x8e,0xd8,0x33,0xf6,0x8d,0x3e,0x00,0xd8,0xb9,0x00,0x0c,0xf3,0xa5 +,0x1f,0x59,0xbe,0xff,0xff,0x81,0xee,0x00,0xd8,0x2b,0xce,0x81,0xe1,0x00,0xff,0x89 +,0x0e,0xac,0x33,0x8d,0x06,0x20,0x02,0xc1,0xe8,0x04,0xa3,0x32,0x34,0x8e,0xd0,0x36 +,0xc7,0x06,0x1e,0x00,0x80,0x18,0x36,0xc7,0x06,0x22,0x00,0xff,0x7f,0x36,0xc7,0x06 +,0x0a,0x00,0xff,0xff,0x36,0xc7,0x06,0x1c,0x00,0x80,0x00,0x8d,0x06,0xa0,0x02,0xc1 +,0xe8,0x04,0xa3,0x30,0x34,0x8e,0xd0,0x36,0xc7,0x06,0x1e,0x00,0x50,0x28,0x36,0xc7 +,0x06,0x0a,0x00,0xff,0xff,0x36,0xc7,0x06,0x1c,0x00,0x80,0x00,0xb8,0xa0,0x01,0xc1 +,0xe8,0x04,0xa3,0x34,0x34,0xa3,0xf2,0x33,0x8e,0xd0,0x8d,0x26,0x80,0x00,0xb8,0x00 +,0x90,0xe7,0x02,0x8d,0x3e,0x70,0x01,0x8b,0xc7,0xc1,0xe8,0x04,0xb9,0x03,0x00,0x89 +,0x45,0x0e,0x89,0x45,0x02,0xc7,0x05,0xff,0xff,0x83,0xc7,0x10,0x05,0x01,0x00,0xe2 +,0xee,0xe8,0x5b,0x01,0xe5,0xce,0xa3,0xb5,0x36,0xe8,0x21,0x00,0xe8,0x45,0x01,0xa1 +,0x32,0x34,0x8c,0xcb,0xcd,0x37,0x0e,0x58,0xa9,0x00,0xf0,0x74,0x07,0x33,0xf6,0x89 +,0x36,0xff,0x34,0xc3,0x8d,0x36,0x30,0x61,0x89,0x36,0xff,0x34,0xc3,0x33,0xc0,0x8b +,0xd0,0x8b,0xf2,0xb9,0x68,0x00,0x2e,0x80,0xbc,0xac,0x17,0x80,0x75,0x01,0xef,0x83 +,0xc2,0x02,0x46,0xe2,0xf1,0xb8,0x02,0x00,0xe7,0x50,0xb9,0x5a,0x00,0x33,0xff,0xc7 +,0x05,0x65,0x18,0x8c,0x4d,0x02,0x83,0xc7,0x04,0xe2,0xf4,0x33,0xc0,0x8e,0xc0,0x8c +,0xc8,0x8e,0xd8,0x8d,0x3e,0x80,0x00,0x8d,0x36,0x9c,0x17,0xb9,0x08,0x00,0xe8,0x37 +,0x00,0x8d,0x36,0x20,0x21,0x8d,0x3e,0xc0,0x00,0xb9,0x0d,0x00,0xe8,0x29,0x00,0x8d +,0x3e,0x40,0x01,0xb9,0x0a,0x00,0xe8,0x1f,0x00,0xe8,0x4b,0x0e,0x33,0xc0,0x8e,0xd8 +,0xc7,0x06,0x4e,0x37,0x6f,0x17,0xe7,0x48,0xe7,0x4c,0xb8,0x40,0x9c,0xe7,0x4a,0xe5 +,0x48,0x90,0xb8,0x00,0x70,0xe7,0x48,0xc3,0xa5,0x83,0xc7,0x02,0xe2,0xfa,0xc3,0xe5 +,0x4c,0xc3,0x50,0x51,0x56,0x57,0x52,0x06,0x1e,0x33,0xc0,0x8e,0xd8,0xe5,0x58,0xd1 +,0xe0,0x73,0x11,0x8b,0xf0,0xd1,0xe6,0x33,0xc0,0x8e,0xd8,0x8b,0xb4,0x80,0x00,0x83 +,0xc6,0x0b,0xff,0xe6,0x1f,0x07,0x5a,0x5f,0x5e,0x59,0x58,0xcf,0x58,0x1c,0xe4,0x1c +,0x6c,0x1c,0x8e,0x1a,0xc0,0x1f,0x40,0x1a,0x44,0x1c,0x65,0x18,0x80,0x80,0x80,0xff +,0x80,0x03,0x02,0x80,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +,0x80,0x03,0x03,0x43,0x80,0x80,0x02,0x80,0x42,0x03,0x02,0xff,0x03,0x01,0x03,0x01 +,0x01,0x03,0x02,0x03,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x02,0x03,0x01,0x03 +,0x03,0xff,0x01,0x01,0xff,0x01,0xff,0x01,0x01,0x03,0x03,0x03,0xff,0xff,0xff,0xff +,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +,0xff,0xff,0xff,0x02,0xb8,0x0f,0x00,0xe7,0x84,0xb8,0x0f,0xf8,0xe7,0x82,0xc3,0xb9 +,0x08,0x00,0x89,0x0e,0xe6,0x3a,0x8d,0x06,0x20,0x03,0x8b,0xd0,0xc1,0xe8,0x04,0xa3 +,0x90,0x01,0x8b,0xc2,0x8b,0xd8,0xc1,0xe8,0x04,0x8e,0xc0,0x05,0x61,0x00,0x26,0xa3 +,0x00,0x00,0xa1,0x30,0x34,0x26,0xa3,0x02,0x00,0x83,0xc3,0x14,0xd1,0xeb,0x26,0x89 +,0x1e,0x08,0x00,0x81,0xc2,0x10,0x06,0xe2,0xd9,0x26,0xc7,0x06,0x00,0x00,0xff,0xff +,0x8c,0x06,0x92,0x01,0xc3,0x50,0x51,0x56,0x57,0x52,0x06,0x1e,0x33,0xc0,0x8e,0xd8 +,0xe7,0x5a,0xff,0x06,0xbe,0x33,0xba,0xd2,0x00,0xed,0xcf,0x00,0x00,0x00,0x00,0x00 +,0x8c,0xcb,0xa1,0x30,0x34,0xcd,0x37,0xe9,0x06,0xed,0xb8,0x32,0x00,0xc3,0xe8,0x8c +,0x01,0xfe,0x06,0xe2,0x34,0xe8,0x21,0x01,0x75,0xf0,0xe8,0x53,0x0e,0x81,0x0e,0xaf +,0x36,0x00,0xc0,0xc7,0x06,0xad,0x36,0x60,0x00,0xf7,0x06,0xe6,0x34,0x80,0x00,0x75 +,0x1a,0xf7,0x06,0xe6,0x34,0x00,0x08,0x74,0x09,0xc7,0x06,0xab,0x36,0x0b,0x00,0xe9 +,0x0f,0x00,0xc7,0x06,0xab,0x36,0x03,0x00,0xe9,0x06,0x00,0xc7,0x06,0xab,0x36,0x11 +,0x9c,0xc7,0x06,0xa9,0x36,0x18,0x00,0xf7,0x06,0xe6,0x34,0x80,0x00,0x75,0x0d,0xf7 +,0x06,0xb5,0x36,0x02,0x00,0x74,0x05,0x83,0x0e,0xa9,0x36,0x20,0xa1,0xa9,0x36,0xe7 +,0x00,0xa1,0xab,0x36,0xe7,0x02,0xf7,0x06,0xe6,0x34,0x80,0x00,0x74,0x2e,0xe8,0xf2 +,0x2f,0x33,0xc0,0x0d,0x41,0x00,0xe7,0x56,0xa1,0xb1,0x36,0x0d,0x00,0x10,0xe7,0x08 +,0xa1,0xb3,0x36,0xe7,0x0a,0xa1,0xaf,0x36,0xe7,0x06,0xb8,0x40,0x00,0xe7,0x4e,0x33 +,0xc0,0xe7,0x0e,0xc7,0x06,0x26,0x02,0x00,0x00,0xe9,0x23,0x00,0xc7,0x06,0x4e,0x37 +,0x3f,0x20,0x8e,0x06,0x30,0x34,0x26,0xf7,0x06,0x0a,0x00,0x00,0x80,0x74,0x07,0x26 +,0x81,0x0e,0x08,0x00,0x00,0x80,0xc6,0x06,0xe0,0x34,0x01,0xb8,0x00,0x00,0xc3,0xfe +,0x06,0xe1,0x34,0xc6,0x06,0xe0,0x34,0x00,0xa1,0x26,0x02,0x0b,0xc0,0x74,0x01,0xc3 +,0xe8,0x04,0x00,0xb8,0x00,0x00,0xc3,0xa1,0xa9,0x36,0xe7,0x00,0x8b,0x1e,0xab,0x36 +,0x83,0xe3,0x06,0xe5,0x02,0x25,0xf9,0xff,0x0b,0xc3,0x0d,0x10,0x00,0xe7,0x02,0xa1 +,0xad,0x36,0xe7,0x04,0xc3,0xb8,0x0a,0x00,0xe7,0x84,0xfe,0x06,0xe5,0x34,0xc6,0x06 +,0xe3,0x34,0x01,0x8e,0x06,0x30,0x34,0x26,0xf7,0x06,0x0a,0x00,0x00,0x40,0x74,0x07 +,0x26,0x81,0x0e,0x08,0x00,0x00,0x40,0xc3,0xc7,0x06,0x4e,0x37,0x6f,0x17,0xfe,0x06 +,0xe4,0x34,0xc6,0x06,0xe3,0x34,0x00,0xc3,0xc3,0xf6,0x06,0x18,0x34,0x80,0x75,0x0d +,0xa1,0x18,0x34,0x0b,0x06,0x1a,0x34,0x0b,0x06,0x1c,0x34,0x75,0x01,0xc3,0xa1,0x2e +,0x34,0x25,0xff,0xfe,0x8b,0x16,0xe7,0x36,0x81,0xe2,0x00,0x01,0x0b,0xc2,0xa3,0x2e +,0x34,0x8d,0x16,0x10,0x00,0xbf,0x00,0x00,0xb9,0x08,0x00,0x8b,0x85,0x00,0x34,0xef +,0x83,0xc2,0x10,0x8b,0x85,0x02,0x34,0xef,0x83,0xc2,0x10,0x8b,0x85,0x04,0x34,0xef +,0x83,0xc2,0xe2,0x83,0xc7,0x06,0x49,0x75,0xe2,0xb8,0x00,0x00,0x8e,0xc0,0xbe,0x00 +,0x34,0xbf,0xb9,0x36,0xb9,0x18,0x00,0xf3,0xa5,0xb8,0x00,0x00,0xc3,0x33,0xc0,0x8e +,0xc0,0x8d,0x3e,0xb0,0x33,0xb9,0x08,0x00,0xf3,0xab,0x8d,0x3e,0x3e,0x34,0xb9,0x03 +,0x00,0xf3,0xab,0xc3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x50,0x51,0x56,0x57,0x52,0x06,0x1e,0x33,0xc0,0x8e,0xd8,0xe7,0x5a,0xff,0x06,0xba +,0x33,0xe5,0x56,0x0d,0x20,0x00,0xe7,0x56,0xba,0x7a,0x00,0xed,0x08,0x26,0x94,0x36 +,0x33,0xc0,0xb1,0x08,0x32,0xed,0x06,0x8e,0xc0,0x8d,0x3e,0xe0,0xff,0xf3,0xaa,0x8e +,0x06,0x32,0x34,0x26,0x81,0x0e,0x08,0x00,0x00,0x02,0x07,0xe5,0x56,0x25,0xdf,0xff +,0xe7,0x56,0xe9,0xf8,0xfc,0x00,0xbd,0x1b,0x10,0x1b,0xd9,0x1a,0xf3,0x1a,0x50,0x51 +,0x56,0x57,0x52,0x06,0x1e,0x33,0xc0,0x8e,0xd8,0xe7,0x5a,0xff,0x06,0xb6,0x33,0x53 +,0x06,0x51,0xe5,0x80,0xa3,0xb4,0x33,0x8b,0xd8,0x8b,0xc8,0x25,0x10,0x00,0xa3,0xed +,0x34,0x0b,0xc0,0x74,0x14,0xff,0x06,0x80,0x34,0x80,0x3e,0xfe,0x34,0x00,0x74,0x03 +,0xe9,0x06,0x00,0xb8,0x80,0x00,0xe8,0x9d,0x04,0x83,0xe3,0x03,0xd1,0xe3,0x2e,0xff +,0x97,0x86,0x1a,0x59,0x07,0x5b,0xe9,0xa4,0xfc,0xba,0x20,0x00,0x8e,0x06,0x3c,0x34 +,0x83,0x3e,0x3c,0x34,0x00,0x75,0x03,0xe9,0xf0,0x00,0xc7,0x06,0x3c,0x34,0x00,0x00 +,0xe9,0x2a,0x00,0xba,0x10,0x00,0x8e,0x06,0x3a,0x34,0x83,0x3e,0x3a,0x34,0x00,0x75 +,0x03,0xe9,0xd5,0xff,0xc7,0x06,0x3a,0x34,0x00,0x00,0xe8,0x10,0x00,0xe9,0xc9,0xff +,0xba,0x10,0x00,0x8e,0x06,0x3a,0x34,0xc7,0x06,0x3a,0x34,0x00,0x00,0x26,0xa1,0x14 +,0x00,0x26,0xa3,0x0c,0x00,0x26,0xa1,0x16,0x00,0x26,0xa3,0x0e,0x00,0x26,0xc6,0x06 +,0x0a,0x00,0x00,0xc1,0xea,0x02,0x23,0xd1,0x74,0x1c,0xba,0x20,0x00,0x26,0xc7,0x06 +,0x0e,0x00,0xea,0x05,0x26,0x0b,0x16,0x0c,0x00,0x26,0x89,0x16,0x0c,0x00,0xff,0x06 +,0x86,0x34,0xff,0x06,0xdc,0x33,0x26,0xa1,0x0c,0x00,0xa9,0x00,0x37,0x74,0x16,0x26 +,0xc6,0x06,0x0a,0x00,0x02,0xa9,0x00,0x30,0x74,0x04,0xff,0x06,0x7a,0x34,0xff,0x06 +,0xda,0x33,0xe9,0x49,0x00,0xc0,0xec,0x07,0x83,0x16,0x8a,0x34,0x00,0x24,0x07,0x3c +,0x07,0x75,0x04,0xff,0x06,0x8c,0x34,0xff,0x06,0x7e,0x34,0xa1,0x30,0x34,0x8c,0xc3 +,0x8e,0xc0,0x8e,0xdb,0x26,0x83,0x0e,0x08,0x00,0x40,0x8c,0xd8,0x26,0x87,0x06,0x16 +,0x00,0x26,0x83,0x3e,0x14,0x00,0xff,0x74,0x0a,0x8e,0xc0,0x26,0x8c,0x1e,0x00,0x00 +,0xe9,0x05,0x00,0x26,0x8c,0x1e,0x14,0x00,0x33,0xc0,0x8e,0xd8,0xc3,0xc3,0x8c,0xc0 +,0x87,0x06,0x92,0x01,0x3d,0xff,0xff,0x74,0x0d,0x8e,0xd8,0x8c,0x06,0x00,0x00,0x33 +,0xc0,0x8e,0xd8,0xe9,0x04,0x00,0x8c,0x06,0x90,0x01,0xe8,0x01,0x00,0xc3,0x06,0x83 +,0x3e,0x90,0x01,0xff,0x74,0x29,0x83,0x3e,0x3a,0x34,0x00,0x75,0x11,0xba,0x86,0x00 +,0xe8,0x1e,0x00,0x8c,0x06,0x3a,0x34,0x83,0x3e,0x90,0x01,0xff,0x74,0x11,0x83,0x3e +,0x3c,0x34,0x00,0x75,0x0a,0xba,0x88,0x00,0xe8,0x06,0x00,0x8c,0x06,0x3c,0x34,0x07 +,0xc3,0xa1,0x90,0x01,0x8e,0xc0,0x26,0xa1,0x08,0x00,0xef,0x26,0xa1,0x00,0x00,0x26 +,0xc7,0x06,0x00,0x00,0xff,0xff,0xa3,0x90,0x01,0x3d,0xff,0xff,0x75,0x03,0xa3,0x92 +,0x01,0x83,0x3e,0xed,0x34,0x00,0x74,0x0b,0xb8,0x10,0x00,0xe7,0x84,0xc7,0x06,0xed +,0x34,0x00,0x00,0xc3,0x50,0x51,0x56,0x57,0x52,0x06,0x1e,0x33,0xc0,0x8e,0xd8,0xe7 +,0x5a,0xff,0x06,0xbc,0x33,0xe9,0x25,0xfb,0x50,0x51,0x56,0x57,0x52,0x06,0x1e,0x33 +,0xc0,0x8e,0xd8,0xe7,0x5a,0xff,0x06,0xb0,0x33,0xe9,0x11,0xfb,0x50,0x51,0x56,0x57 +,0x52,0x06,0x1e,0x33,0xc0,0x8e,0xd8,0xe7,0x5a,0xff,0x06,0xb4,0x33,0x06,0xff,0x06 +,0x76,0x34,0x80,0x3e,0xfe,0x34,0x00,0x74,0x04,0x07,0xe9,0xf0,0xfa,0xb8,0x80,0x00 +,0xe8,0xd3,0x02,0x07,0xe9,0xe6,0xfa,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0xc6,0x1d,0x08,0x1d,0x91,0x1e,0x5d,0x1e,0x73,0x1e,0x89,0x1e,0x91,0x1e,0xa8,0x1d +,0x91,0x1e,0x91,0x1e,0xaf,0x1e,0xaf,0x1e,0x15,0x1d,0x15,0x1d,0x91,0x1e,0x99,0x1f +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x02,0x00,0x00 +,0x00,0x01,0x00,0x10,0x00,0x01,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00 +,0x07,0xe9,0x99,0xfa,0x50,0x51,0x56,0x57,0x52,0x06,0x1e,0x33,0xc0,0x8e,0xd8,0xe7 +,0x5a,0xff,0x06,0xb2,0x33,0x06,0x68,0xf6,0x1c,0xe5,0x06,0xa3,0xb2,0x33,0x8b,0xf0 +,0x83,0xe6,0x1e,0x2e,0xff,0xa4,0xa0,0x1c,0xe5,0x0c,0xa9,0x80,0x00,0x74,0x06,0xe8 +,0xa4,0x01,0xe5,0x06,0xc3,0x53,0xe5,0x0c,0x8b,0xd8,0xa9,0x01,0x00,0x74,0x14,0x83 +,0x3e,0xe0,0x3a,0x00,0x74,0x0d,0x8e,0x06,0x38,0x34,0xe8,0xbf,0x06,0xc7,0x06,0xe0 +,0x3a,0x00,0x00,0xe5,0x00,0x0d,0x18,0x00,0xe7,0x00,0xe5,0x02,0x0d,0x11,0x00,0xe7 +,0x02,0x8b,0xc3,0x5b,0xa9,0x01,0x00,0x74,0x01,0xc3,0x8b,0xd0,0xb8,0x00,0x08,0xe7 +,0x84,0x8b,0xc2,0x8e,0x06,0x38,0x34,0x26,0xa3,0x0c,0x00,0x8b,0xd0,0xc1,0xe0,0x03 +,0x83,0x16,0x88,0x34,0x00,0xff,0x06,0x7c,0x34,0x26,0x83,0x3e,0x06,0x00,0x0a,0x75 +,0x21,0x8b,0xc2,0x25,0x40,0x18,0x3d,0x40,0x00,0x74,0x0c,0x3d,0x00,0x10,0x75,0x12 +,0x26,0xfe,0x0e,0x0a,0x00,0x74,0x0b,0xf7,0x06,0xef,0x34,0x20,0x00,0x75,0x03,0xe9 +,0x5a,0x06,0x8c,0xc0,0x26,0x8e,0x06,0x02,0x00,0x26,0x83,0x0e,0x08,0x00,0x20,0x26 +,0xa3,0x12,0x00,0x26,0xa3,0x10,0x00,0xc3,0xff,0x06,0xc4,0x33,0xe5,0x0c,0xa9,0x01 +,0x00,0x75,0x01,0xc3,0xa9,0xf0,0x07,0x74,0x01,0xc3,0xff,0x06,0xd4,0x33,0xe5,0x00 +,0x0d,0x18,0x00,0xe7,0x00,0xc3,0xff,0x06,0xca,0x33,0x80,0x3e,0xa0,0x36,0x08,0x75 +,0x14,0x8e,0x06,0x30,0x34,0x26,0xf7,0x06,0x0a,0x00,0x00,0x08,0x74,0x07,0x26,0x81 +,0x0e,0x08,0x00,0x00,0x08,0xe5,0x82,0x25,0xfd,0xff,0xe7,0x82,0xe5,0x0c,0x50,0xe5 +,0x80,0x25,0x00,0x07,0xa3,0xe4,0x3a,0xe5,0x8c,0x25,0x00,0x80,0xa3,0xe2,0x3a,0x58 +,0xa9,0x02,0x00,0x75,0x25,0x83,0x3e,0xe2,0x3a,0x00,0x75,0x1e,0x83,0x3e,0xe4,0x3a +,0x00,0x75,0x17,0xe5,0x08,0x0d,0x00,0x04,0x25,0xff,0x04,0xe7,0x08,0xe8,0x6a,0x01 +,0xe5,0x82,0x0d,0x02,0x00,0xe7,0x82,0xe9,0x21,0x00,0xe8,0x1a,0x06,0x80,0x3e,0xe8 +,0xff,0x00,0x74,0x0a,0x80,0x3e,0xe8,0xff,0x04,0x74,0x03,0xe9,0x0d,0x00,0xc6,0x06 +,0xe8,0xff,0x01,0xba,0x0c,0x01,0xb8,0x08,0x08,0xef,0xed,0x80,0x3e,0x9f,0x36,0x06 +,0x75,0x05,0x83,0x0e,0x99,0x36,0x40,0xb8,0x00,0x01,0xe9,0x09,0x01,0xff,0x06,0xcc +,0x33,0x81,0x26,0xaf,0x36,0xff,0xf7,0xa1,0xaf,0x36,0xe7,0x06,0xff,0x06,0xc6,0x34 +,0xe9,0x1e,0x00,0xff,0x06,0xce,0x33,0xff,0x06,0x95,0x37,0x81,0x26,0xaf,0x36,0xff +,0xef,0xa1,0xaf,0x36,0xe7,0x06,0xe9,0x08,0x00,0xff,0x06,0xd0,0x33,0xff,0x06,0x7a +,0x34,0xff,0x06,0xd2,0x33,0xd1,0xe6,0x8e,0x06,0x30,0x34,0x2e,0x8b,0x84,0xc0,0x1c +,0x26,0x09,0x06,0x08,0x00,0x2e,0x8b,0x84,0xc2,0x1c,0x09,0x06,0x66,0x37,0xc3,0xe5 +,0x0c,0xa9,0x80,0x00,0x74,0x56,0x50,0xe8,0xf0,0x00,0x58,0xa9,0x00,0x01,0x75,0x07 +,0xff,0x06,0xc6,0x33,0xe9,0x08,0x00,0xff,0x06,0x78,0x34,0xff,0x06,0xc8,0x33,0xe5 +,0x82,0x25,0xfd,0xff,0xe7,0x82,0xe8,0x6e,0x05,0xba,0x10,0x01,0xed,0x80,0x3e,0xe8 +,0xff,0x00,0x74,0x0a,0x80,0x3e,0xe8,0xff,0x04,0x74,0x03,0xe9,0x1d,0x00,0xc6,0x06 +,0xe8,0xff,0x01,0xba,0x0c,0x01,0xb8,0x08,0x08,0xef,0xed,0xe9,0x0d,0x00,0xc6,0x06 +,0xe8,0xff,0x03,0xba,0x0c,0x01,0xb8,0x08,0x08,0xef,0xed,0xc3,0xa9,0x01,0x00,0x74 +,0x1c,0xe8,0x2c,0x00,0x83,0x3e,0xe0,0x3a,0x00,0x74,0x0f,0x06,0x8e,0x06,0x38,0x34 +,0xe8,0xc9,0x04,0xc7,0x06,0xe0,0x3a,0x00,0x00,0x07,0xe9,0x5d,0x00,0x8b,0xd0,0x8e +,0x06,0x38,0x34,0x26,0xa3,0x0c,0x00,0xe8,0x06,0x00,0x68,0x69,0x1d,0xe9,0x4a,0x00 +,0xa9,0x00,0x04,0x74,0x0a,0xb8,0x00,0x04,0xff,0x06,0xd8,0x33,0xe9,0x17,0x00,0xa9 +,0x00,0x01,0x74,0x0a,0xff,0x06,0x39,0x37,0xb8,0x00,0x01,0xe9,0x08,0x00,0xa9,0x10 +,0x00,0xb8,0x10,0x00,0x74,0x1d,0x09,0x06,0x66,0x37,0x8c,0xc0,0x8e,0x06,0x30,0x34 +,0x26,0xf7,0x06,0x0a,0x00,0x00,0x01,0x74,0x07,0x26,0x81,0x0e,0x08,0x00,0x00,0x01 +,0x8e,0xc0,0xc3,0xff,0x06,0xc2,0x33,0xe9,0xf8,0xff,0xe5,0x00,0x0d,0x18,0x00,0xe7 +,0x00,0xe5,0x02,0x0d,0x11,0x00,0xe7,0x02,0xc3,0x58,0xe9,0x43,0xfd,0xe5,0x08,0x0d +,0x00,0x04,0x25,0xff,0x04,0xe7,0x08,0xe9,0xe0,0xff,0xe5,0x0e,0xa9,0x00,0x08,0x75 +,0x01,0xc3,0xe9,0xf5,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x50,0x51,0x56,0x57,0x52,0x06,0x1e,0x33,0xc0,0x8e,0xd8,0xe7,0x5a,0xff,0x06,0xb8 +,0x33,0xe5,0x48,0x06,0x53,0x57,0xff,0x16,0x4e,0x37,0x5f,0x5b,0x83,0x3e,0x80,0x01 +,0xff,0x74,0x58,0x8e,0x06,0x80,0x01,0x26,0xff,0x0e,0x08,0x00,0x75,0x4d,0x26,0xa1 +,0x00,0x00,0xa3,0x80,0x01,0x26,0xc7,0x06,0x00,0x00,0xff,0xff,0x8c,0xc0,0x26,0x8e +,0x06,0x02,0x00,0x26,0x81,0x0e,0x08,0x00,0x80,0x00,0x8b,0xd0,0x26,0x87,0x06,0x1a +,0x00,0x26,0x83,0x3e,0x18,0x00,0xff,0x74,0x0a,0x8e,0xc0,0x26,0x89,0x16,0x00,0x00 +,0xe9,0x05,0x00,0x26,0x89,0x16,0x18,0x00,0x83,0x3e,0x80,0x01,0xff,0x74,0x0c,0x8e +,0x06,0x80,0x01,0x26,0x83,0x3e,0x08,0x00,0x00,0x74,0xb3,0x07,0xe9,0x3e,0xf7,0xe5 +,0x4c,0x90,0xe5,0x02,0xa9,0x00,0x20,0x74,0x0d,0x25,0xff,0xdf,0x0d,0x01,0x00,0xe7 +,0x02,0x0d,0x00,0x20,0xe7,0x02,0xe5,0x0a,0x8b,0xd8,0xa3,0xf4,0x33,0x25,0xc3,0x57 +,0x0d,0x00,0x10,0xe7,0x0a,0xf7,0x06,0x9b,0x36,0x00,0x80,0x74,0x37,0xf7,0xc3,0x00 +,0x80,0x74,0x06,0xf7,0xc3,0x00,0x08,0x74,0x5d,0x81,0x26,0xc2,0x34,0x7f,0xff,0xc7 +,0x06,0x35,0x37,0x05,0x00,0xb8,0x80,0x03,0xcd,0x39,0x81,0x26,0x9b,0x36,0xff,0x7f +,0xc7,0x06,0x0f,0x37,0x04,0x00,0xf7,0x06,0x9b,0x36,0x40,0x00,0x75,0x06,0xc7,0x06 +,0x0f,0x37,0x03,0x00,0xf7,0x06,0x9b,0x36,0x00,0x20,0x74,0x2a,0xf7,0xc3,0x00,0x08 +,0x74,0x24,0x80,0x3e,0x9d,0x36,0x06,0x7c,0x1d,0xff,0x06,0x94,0x34,0x83,0x0e,0x66 +,0x37,0x20,0x8e,0x06,0x30,0x34,0x26,0xf7,0x06,0x0a,0x00,0x00,0x01,0x74,0x07,0x26 +,0x81,0x0e,0x08,0x00,0x00,0x01,0xf7,0xc3,0x00,0x20,0x75,0x3b,0xf7,0x06,0x9a,0x37 +,0x80,0x00,0x74,0x0b,0xff,0x06,0x89,0x37,0x33,0xc0,0xe7,0x0e,0xe9,0x04,0x00,0xff +,0x06,0x3b,0x37,0xf7,0x06,0x9b,0x36,0x00,0x20,0x74,0x1c,0x80,0x26,0x9e,0x36,0xff +,0x75,0x15,0x8e,0x06,0x30,0x34,0x26,0xf7,0x06,0x0a,0x00,0x00,0x08,0x74,0x07,0x26 +,0x81,0x0e,0x08,0x00,0x00,0x08,0xc3,0xc3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x02,0x23,0x02,0x23,0x02,0x23,0x02,0x23,0x03,0x23,0xdd,0x22,0x02,0x23,0xfd,0x21 +,0x02,0x23,0xa4,0x24,0xf3,0x24,0x02,0x23,0x8d,0x22,0x7a,0x23,0x02,0x23,0x97,0x24 +,0x1b,0x24,0x75,0x24,0x02,0x23,0x02,0x23,0x8e,0x25,0xfb,0x8e,0x06,0x7e,0x01,0xfb +,0x26,0x83,0x3e,0x00,0x00,0xff,0x74,0xf2,0x26,0x8e,0x06,0x00,0x00,0xfa,0x26,0x8b +,0x1e,0x08,0x00,0x26,0x23,0x1e,0x0a,0x00,0x74,0xe5,0x8c,0xc0,0x8e,0xd0,0x26,0x8b +,0x26,0x02,0x00,0x8c,0x16,0xf2,0x33,0x22,0xff,0x75,0x6a,0x26,0xa1,0x1c,0x00,0x8a +,0xe3,0x8a,0xdc,0x22,0xd8,0x75,0x0d,0xd0,0xe8,0x24,0xf8,0x0a,0xc0,0x75,0xf2,0xb0 +,0x80,0xe9,0xed,0xff,0xd0,0xe8,0x24,0xf8,0x0a,0xc0,0x75,0x02,0xb0,0x80,0x32,0xe4 +,0x26,0xa3,0x1c,0x00,0xf7,0xc3,0x08,0x00,0x75,0x47,0x2e,0x8a,0x9f,0xc5,0x25,0x2e +,0x8b,0xbf,0xc5,0x26,0x80,0xc3,0x10,0x26,0x8e,0x1d,0x26,0x8c,0x1e,0x06,0x00,0x8b +,0x16,0x00,0x00,0xc7,0x06,0x00,0x00,0xff,0xff,0x26,0x89,0x15,0x83,0xfa,0xff,0x75 +,0x0a,0x2e,0x8b,0x97,0xcd,0x26,0x26,0x21,0x16,0x08,0x00,0x33,0xc0,0x8e,0xd8,0x26 +,0x89,0x1e,0x04,0x00,0xc3,0x8a,0xdf,0xb7,0x00,0x2e,0x8a,0x9f,0xc5,0x25,0xe9,0xe0 +,0xff,0x26,0x83,0x26,0x08,0x00,0xf7,0x83,0xc3,0x10,0xe9,0xde,0xff,0x60,0x06,0x1e +,0x68,0x87,0x25,0x6a,0x00,0x1f,0x8e,0x06,0xf2,0x33,0x8b,0x0e,0x34,0x34,0x39,0x0e +,0xf2,0x33,0x74,0x0e,0x26,0x81,0x0e,0x0a,0x00,0x00,0x02,0x26,0x81,0x0e,0x08,0x00 +,0x00,0x02,0x26,0x89,0x26,0x02,0x00,0xa3,0xf2,0x33,0x8e,0xd0,0x8d,0x26,0x80,0x00 +,0x36,0x89,0x26,0x02,0x00,0x36,0x89,0x1e,0x20,0x00,0x36,0xc7,0x06,0x08,0x00,0x00 +,0x00,0xb9,0x04,0x00,0xbe,0x00,0x00,0x2e,0x8b,0xbc,0xc5,0x26,0x36,0xc7,0x05,0xff +,0xff,0x36,0xc7,0x45,0x02,0xff,0xff,0x83,0xc6,0x02,0xe2,0xeb,0x8e,0x06,0x7e,0x01 +,0x36,0x8b,0x0e,0x22,0x00,0x8c,0xc0,0x26,0x83,0x3e,0x00,0x00,0xff,0x26,0x8e,0x06 +,0x00,0x00,0x74,0x07,0x26,0x3b,0x0e,0x22,0x00,0x7d,0xea,0x36,0x8c,0x06,0x00,0x00 +,0x8e,0xc0,0x26,0x8c,0x16,0x00,0x00,0xfb,0x36,0xff,0x2e,0x1e,0x00,0x06,0x1e,0x68 +,0x8b,0x25,0x6a,0x00,0x1f,0x26,0x09,0x36,0x08,0x00,0xf7,0xc6,0x00,0xff,0x74,0x01 +,0xc3,0x56,0x52,0x2e,0x8b,0xb4,0xc5,0x25,0x81,0xe6,0xff,0x00,0x2e,0x8b,0xb4,0xc5 +,0x26,0x8c,0xc2,0x8e,0xc0,0x26,0xc7,0x06,0x00,0x00,0xff,0xff,0x8e,0xc2,0x26,0x83 +,0x3c,0xff,0x74,0x0f,0x8b,0xd0,0x26,0x87,0x54,0x02,0x8e,0xc2,0x26,0xa3,0x00,0x00 +,0xe9,0x07,0x00,0x26,0x89,0x44,0x02,0x26,0x89,0x04,0x5a,0x5e,0xc3,0x06,0x1e,0x68 +,0x8b,0x25,0x6a,0x00,0x1f,0x8e,0x06,0xf2,0x33,0x26,0xa3,0x0a,0x00,0x26,0x89,0x26 +,0x02,0x00,0xa1,0x34,0x34,0x8e,0xd0,0x8d,0x26,0x80,0x00,0x8c,0x16,0xf2,0x33,0xe9 +,0x4d,0xfe,0xcf,0x50,0x1e,0x52,0x53,0x33,0xc0,0x8e,0xd8,0x26,0x83,0x3e,0x04,0x00 +,0xff,0x26,0xc7,0x06,0x04,0x00,0x00,0x00,0x74,0x03,0xe9,0x1a,0x00,0x83,0x3e,0xe6 +,0x3a,0x02,0x76,0x13,0xff,0x06,0xd6,0x33,0x8c,0xc0,0x8e,0x06,0x32,0x34,0xbe,0x40 +,0x00,0x68,0x3a,0x23,0xe9,0x5e,0xff,0xe8,0x84,0xf8,0x5b,0x5a,0x1f,0x58,0xcf,0xe8 +,0xe1,0x00,0x26,0xc6,0x06,0x18,0x00,0x10,0x26,0x8a,0x1e,0x29,0x00,0x88,0x1e,0x1b +,0x37,0x26,0xc7,0x06,0x0c,0x00,0xff,0x7f,0x26,0xa1,0x0e,0x00,0xe7,0x9c,0x26,0xa1 +,0x08,0x00,0xe7,0x9a,0xe5,0x00,0x80,0xfb,0x08,0x74,0x09,0x0d,0x18,0xac,0xe7,0x00 +,0x07,0x1f,0x58,0xcf,0x0d,0x18,0x00,0xe9,0xf4,0xff,0x50,0x1e,0x06,0x33,0xc0,0x8e +,0xd8,0x83,0x3e,0xa1,0x36,0x00,0x75,0xb7,0x26,0x8b,0x36,0x06,0x00,0x2e,0xff,0x94 +,0xdc,0x23,0x07,0x1f,0x58,0xcf,0xe8,0x8a,0x00,0xe5,0x00,0x0d,0x18,0x00,0xe7,0x00 +,0xe8,0x49,0x00,0xc3,0x53,0xf7,0x06,0xef,0x34,0x20,0x00,0x75,0x2d,0xe5,0x8c,0x25 +,0x00,0x70,0x8b,0xd8,0xe5,0x8c,0x25,0x00,0x70,0x3b,0xc3,0x74,0x05,0x8b,0xd8,0xe9 +,0xf2,0xff,0x3d,0x00,0x30,0x75,0x10,0xe5,0x02,0x25,0xef,0xff,0xe7,0x02,0xc7,0x06 +,0xe0,0x3a,0xff,0xff,0xe9,0x03,0x00,0xe8,0x12,0x00,0x5b,0xc3,0xa3,0x23,0x96,0x23 +,0xa4,0x23,0xa4,0x23,0x96,0x23,0xa4,0x23,0x96,0x23,0x96,0x23,0x26,0xa0,0x29,0x00 +,0xa2,0x1b,0x37,0x26,0xc7,0x06,0x0c,0x00,0xff,0x7f,0x26,0xa1,0x0e,0x00,0xe7,0x9c +,0x26,0xa1,0x08,0x00,0xe7,0x9a,0xe5,0x00,0x25,0xff,0x53,0x26,0x8b,0x36,0x06,0x00 +,0x83,0xe6,0x0e,0x2e,0x0b,0x84,0xad,0x25,0xe7,0x00,0xc3,0x06,0x1e,0x68,0x8b,0x25 +,0x6a,0x00,0x1f,0x83,0x0e,0xef,0x34,0x20,0x83,0x0e,0x9b,0x36,0x08,0xe5,0x00,0x25 +,0xef,0xff,0x0d,0x08,0x00,0xe7,0x00,0xe5,0x00,0xa9,0x10,0x00,0x75,0x01,0xc3,0xe5 +,0x00,0xa9,0x10,0x00,0x75,0xf9,0xc3,0x50,0x53,0x51,0x56,0x06,0x1e,0x33,0xc0,0x8e +,0xd8,0xb8,0x05,0x00,0xe7,0x84,0xe5,0x08,0x0d,0x00,0x04,0x25,0xff,0x04,0xe7,0x08 +,0xe5,0x00,0x0d,0x18,0x00,0xe7,0x00,0xe5,0x02,0x0d,0x11,0x00,0xe7,0x02,0x1f,0x07 +,0x5e,0x59,0x5b,0x58,0xc3,0x50,0x1e,0x33,0xc0,0x8e,0xd8,0xc7,0x06,0xef,0x34,0x00 +,0x00,0x83,0x26,0x9b,0x36,0xf7,0xe5,0x00,0x0d,0x18,0x00,0xe7,0x00,0xe5,0x02,0x0d +,0x11,0x00,0xe7,0x02,0x1f,0x58,0xcf,0x60,0x06,0x1e,0x68,0x87,0x25,0x6a,0x00,0x1f +,0xe8,0x16,0xf5,0xc3,0x06,0x1e,0x68,0x8b,0x25,0x6a,0x00,0x1f,0x8e,0xc0,0x26,0x83 +,0x3e,0x0a,0x00,0x00,0x74,0x03,0xe8,0x43,0x00,0x26,0xc7,0x06,0x0a,0x00,0xff,0xff +,0x26,0x8b,0x16,0x06,0x00,0x8e,0x1e,0x8e,0x01,0x8c,0xd8,0x8b,0xca,0x83,0x3e,0x00 +,0x00,0xff,0x8e,0x1e,0x00,0x00,0x74,0x0a,0x2b,0x16,0x08,0x00,0x73,0xeb,0x29,0x0e +,0x08,0x00,0x26,0x89,0x0e,0x08,0x00,0x26,0x8c,0x1e,0x00,0x00,0x8e,0xd8,0x8c,0x06 +,0x00,0x00,0xc3,0x60,0x06,0x1e,0x68,0x87,0x25,0x6a,0x00,0x1f,0x8e,0xc0,0x8b,0xc8 +,0x8e,0x1e,0x8e,0x01,0x26,0xc7,0x06,0x0a,0x00,0x00,0x00,0x8c,0xd8,0x83,0x3e,0x00 +,0x00,0xff,0x74,0x25,0x3b,0x0e,0x00,0x00,0x8e,0x1e,0x00,0x00,0x75,0xed,0x8e,0xd8 +,0x26,0xa1,0x00,0x00,0xa3,0x00,0x00,0x3d,0xff,0xff,0x74,0x56,0x8e,0xd8,0x26,0xa1 +,0x08,0x00,0x01,0x06,0x08,0x00,0xe9,0x49,0x00,0x26,0x8e,0x1e,0x02,0x00,0xbe,0x18 +,0x00,0x83,0x3c,0xff,0x74,0x3c,0x39,0x0c,0x74,0x19,0x8e,0x1c,0xbe,0x00,0x00,0x83 +,0x3e,0x00,0x00,0xff,0x74,0x2c,0x39,0x0e,0x00,0x00,0x74,0x07,0x8e,0x1e,0x00,0x00 +,0xe9,0xec,0xff,0x26,0xa1,0x00,0x00,0x89,0x04,0x33,0xc9,0x8e,0xd9,0x3d,0xff,0xff +,0x75,0x10,0x83,0xfe,0x18,0x75,0x0b,0x26,0x8e,0x1e,0x02,0x00,0x81,0x26,0x08,0x00 +,0x7f,0xff,0x33,0xc0,0x8e,0xd8,0xc3,0x1f,0x07,0x61,0xcf,0x1f,0x07,0xcf,0x60,0x06 +,0x1e,0x68,0x87,0x25,0x6a,0x00,0x1f,0xe5,0x06,0x25,0x1e,0x00,0x3d,0x1e,0x00,0x75 +,0xf6,0xb9,0x08,0x00,0xe5,0x58,0xe7,0x5a,0x23,0xc0,0xe0,0xf8,0xc3,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0xac,0x00,0x00,0x00,0xa8,0x00,0x8c,0x02,0x04,0x00 +,0x00,0x08,0x10,0x20,0x00,0xff,0x0e,0x0c,0x0c,0x0a,0x0a,0x0a,0x0a,0x08,0x08,0x08 +,0x08,0x08,0x08,0x08,0x08,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06 +,0x06,0x06,0x06,0x06,0x06,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04 +,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04 +,0x04,0x04,0x04,0x04,0x04,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02 +,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02 +,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02 +,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02 +,0x02,0x02,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x14,0x00,0x10,0x00,0x0c,0x00,0xff,0x7f,0xff +,0xbf,0xff,0xdf,0xff,0xef,0xff,0xf7,0xff,0xfb,0xff,0xfd,0xff,0xfe,0x7f,0xff,0xbf +,0xff,0xdf,0xff,0xef,0xff,0xf7,0xff,0xfb,0xff,0xfd,0xff,0xfe,0xff,0x00,0x00,0x00 +,0x80,0x3e,0xe2,0x34,0x01,0x76,0x03,0xe9,0xa5,0x00,0xb8,0x00,0x00,0xe7,0x4e,0xb9 +,0x28,0x00,0xe2,0xfe,0xc6,0x06,0x45,0x37,0x02,0xbf,0x3f,0x28,0x2e,0x8b,0x45,0x08 +,0xe7,0x4e,0xb9,0x28,0x00,0xe2,0xfe,0x2e,0x8b,0x1d,0xc7,0x06,0xb3,0x36,0x40,0x11 +,0xc7,0x06,0xb1,0x36,0x27,0x00,0xc7,0x06,0x46,0x37,0x02,0x00,0xc7,0x06,0x48,0x37 +,0x64,0x00,0xf7,0x06,0xb5,0x36,0x02,0x00,0x75,0x1c,0x2e,0x0b,0x5d,0x02,0x81,0x26 +,0xb3,0x36,0xff,0xfe,0xc7,0x06,0xb1,0x36,0x9c,0x00,0xc7,0x06,0x46,0x37,0x08,0x00 +,0xc7,0x06,0x48,0x37,0x90,0x01,0x89,0x1e,0xb7,0x36,0x89,0x1e,0xfe,0x33,0xbe,0x20 +,0x00,0x8b,0xc3,0xe7,0x4e,0xb9,0x28,0x00,0xe2,0xfe,0x2e,0x8b,0x45,0x04,0xe7,0x4e +,0xb9,0x28,0x00,0xe2,0xfe,0xe5,0x4e,0x8b,0xcb,0x2e,0x23,0x45,0x06,0x2e,0x23,0x4d +,0x06,0x3a,0xc1,0x74,0x36,0x4e,0x75,0xd9,0x80,0x3e,0x45,0x37,0x00,0x74,0x0b,0xc6 +,0x06,0x45,0x37,0x00,0xbf,0x2f,0x28,0xe9,0x72,0xff,0xc6,0x06,0x45,0x37,0x01,0xf7 +,0x06,0xb5,0x36,0x02,0x00,0x74,0x14,0xe5,0xce,0x25,0xfd,0xff,0xe7,0xce,0xe8,0x43 +,0x00,0xe5,0xce,0x0d,0x02,0x00,0xe7,0xce,0xe8,0x39,0x00,0x80,0x3e,0xe2,0x34,0x01 +,0x76,0x01,0xc3,0xb8,0xea,0x05,0xe7,0x8c,0xfa,0xe8,0x12,0xf4,0xfb,0x8d,0x06,0xd0 +,0x39,0x8b,0xd8,0xc1,0xe8,0x04,0xa3,0x38,0x34,0x8e,0xc0,0xa1,0x30,0x34,0x26,0xa3 +,0x02,0x00,0x26,0xc7,0x06,0x00,0x00,0xff,0xff,0x83,0xc3,0x18,0xd1,0xeb,0x26,0x89 +,0x1e,0x08,0x00,0xc3,0xe5,0x02,0x0d,0x00,0x40,0xe7,0x02,0xe5,0x00,0x0d,0x04,0x00 +,0xe7,0x00,0xb8,0x00,0x00,0xe7,0x0a,0xe5,0x0a,0xa9,0x00,0x80,0x75,0x14,0xe5,0x08 +,0x0d,0x00,0x10,0xe7,0x08,0xe5,0x0a,0x0d,0x00,0x08,0xb9,0x05,0x00,0xe7,0x0a,0xe2 +,0xfc,0xc3,0xe5,0x08,0x0d,0x00,0x10,0xb9,0x05,0x00,0xe7,0x08,0xe2,0xfc,0xc3,0x04 +,0x0c,0x20,0x00,0x01,0x0c,0x7e,0xff,0x00,0x0c,0x02,0x00,0x10,0x00,0x40,0x00,0x0c +,0xc6,0x01,0x00,0x00,0xc0,0xf7,0xff,0x00,0xc0,0x02,0x00,0x10,0x00,0x40,0x00,0x00 +,0x33,0xc0,0x8e,0xd8,0x8d,0x3e,0x72,0x49,0x8d,0x36,0xb0,0x37,0xb9,0x14,0x00,0x8b +,0x1e,0x30,0x34,0x89,0x5c,0x02,0x2e,0x8b,0x45,0x02,0x89,0x44,0x06,0x2e,0x8b,0x05 +,0x89,0x44,0x04,0x83,0xc7,0x04,0x83,0xc6,0x10,0xe2,0xe8,0xc6,0x06,0x9e,0x36,0x0e +,0xe8,0xfd,0x26,0x68,0x83,0x28,0xa1,0xaa,0x02,0xcd,0x35,0x83,0x3e,0xa1,0x36,0x00 +,0x74,0x03,0xe9,0x3b,0x27,0x33,0xff,0x8e,0x06,0xa6,0x02,0x8b,0x36,0xa4,0x02,0x2e +,0xff,0xa4,0x2e,0x30,0x83,0x0e,0x99,0x36,0x04,0xc7,0x06,0x37,0x37,0x01,0x00,0xc6 +,0x06,0xca,0x34,0x01,0xe9,0x7d,0x19,0x80,0x3e,0xa0,0x36,0x08,0x74,0xe6,0x80,0x26 +,0x9e,0x36,0xff,0x75,0x1a,0xf7,0x06,0x9b,0x36,0x00,0x20,0x74,0x12,0xf7,0x06,0x9b +,0x36,0x03,0x00,0x75,0x0a,0x83,0x0e,0x66,0x37,0x10,0xc6,0x06,0xa0,0x36,0x08,0xe9 +,0xfb,0x01,0x80,0x3e,0x9e,0x36,0x02,0x75,0xce,0xc6,0x06,0xa0,0x36,0x06,0xe9,0xec +,0x01,0xc3,0xe9,0xe8,0x01,0x26,0xc7,0x06,0x0a,0x00,0x00,0x00,0x26,0xff,0x26,0x04 +,0x00,0xa1,0xd1,0x36,0x26,0x39,0x06,0x1a,0x00,0x75,0x22,0xa1,0xd3,0x36,0x26,0x39 +,0x06,0x1c,0x00,0x75,0x18,0xa1,0xd5,0x36,0x26,0x39,0x06,0x1e,0x00,0x75,0x0e,0x26 +,0xf7,0x06,0x0c,0x00,0x40,0x00,0x74,0x05,0x83,0x0e,0x66,0x37,0x40,0x81,0x0e,0xaf +,0x36,0x00,0x10,0xa1,0xaf,0x36,0xe7,0x06,0x80,0x3e,0x9d,0x36,0x02,0x75,0x06,0xcd +,0x34,0xe9,0xa2,0x1a,0xc3,0xf7,0x06,0x9b,0x36,0x10,0x00,0x75,0x54,0x26,0xf6,0x06 +,0x0a,0x00,0xff,0x75,0x4c,0x26,0xa0,0x19,0x00,0x24,0xc0,0x3c,0x40,0x75,0x11,0x80 +,0x3e,0x95,0x36,0x00,0x74,0x3b,0x26,0xc7,0x06,0x04,0x00,0xff,0xff,0xe9,0x31,0x00 +,0xe8,0xf1,0x04,0xf7,0x06,0x9b,0x36,0x03,0x00,0x74,0x2f,0x8b,0xd8,0xb8,0x7d,0x03 +,0xcd,0x3a,0x8b,0xc3,0xc6,0x06,0xa0,0x36,0x06,0xf7,0x06,0x9b,0x36,0x02,0x00,0x75 +,0x05,0xc6,0x06,0xa0,0x36,0x04,0x81,0x0e,0x9b,0x36,0x80,0x00,0x83,0x26,0x9b,0x36 +,0xfc,0xe9,0x23,0x01,0xe8,0x87,0x1d,0xe9,0x33,0x01,0x50,0x26,0xa1,0x0c,0x00,0x25 +,0x07,0x00,0x3d,0x07,0x00,0x75,0x03,0xe9,0x84,0x00,0x3d,0x05,0x00,0x75,0x03,0xe9 +,0x7c,0x00,0x83,0x3e,0xe8,0x3a,0x04,0x74,0x75,0x83,0x3e,0xe8,0x3a,0x02,0x74,0x6e +,0xf7,0x06,0xe6,0x34,0x18,0x80,0x75,0x03,0xe9,0x6a,0x00,0xf7,0x06,0xe6,0x34,0x00 +,0x80,0x74,0x35,0x26,0x80,0x3e,0x29,0x00,0x02,0x75,0x2d,0x51,0x56,0x57,0x8d,0x36 +,0x3e,0x34,0x8d,0x3e,0x20,0x00,0xb9,0x06,0x00,0xf3,0xa6,0x5f,0x5e,0x59,0x74,0x45 +,0x26,0xa1,0x20,0x00,0xa3,0x3e,0x34,0x26,0xa1,0x22,0x00,0xa3,0x40,0x34,0x26,0xa1 +,0x24,0x00,0xa3,0x42,0x34,0xe9,0x26,0x00,0xf7,0x06,0xe6,0x34,0x08,0x00,0x74,0x0b +,0x26,0x80,0x3e,0x19,0x00,0x00,0x74,0x03,0xe9,0x13,0x00,0xf7,0x06,0xe6,0x34,0x10 +,0x00,0x74,0x12,0x26,0xa0,0x28,0x00,0xc0,0xe8,0x04,0x22,0xc0,0x74,0x07,0x26,0xc7 +,0x06,0x04,0x00,0xff,0xff,0x58,0x23,0xc0,0x74,0x03,0xe9,0x57,0xff,0x81,0x26,0x9b +,0x36,0xff,0xfe,0x83,0xfe,0x06,0x7f,0x24,0x26,0xa1,0x20,0x00,0x3b,0x06,0xd1,0x36 +,0x75,0x1a,0x26,0xa1,0x22,0x00,0x3b,0x06,0xd3,0x36,0x75,0x10,0x26,0xa1,0x24,0x00 +,0x3b,0x06,0xd5,0x36,0x75,0x06,0x81,0x0e,0x9b,0x36,0x00,0x01,0x26,0xa1,0x20,0x00 +,0x25,0x7f,0xff,0xa3,0xb8,0x34,0x26,0xa1,0x22,0x00,0xa3,0xba,0x34,0x26,0xa1,0x24 +,0x00,0xa3,0xbc,0x34,0x8b,0xc6,0x86,0xc4,0xa3,0xc0,0x34,0xd1,0xe6,0x80,0xfc,0x09 +,0x74,0x03,0xe8,0xaa,0x1c,0x8b,0xc6,0x2e,0xff,0xa4,0x30,0x49,0x26,0xa1,0x0c,0x00 +,0x3d,0xff,0x7f,0x74,0x0f,0x26,0xff,0x26,0x04,0x00,0x8e,0x06,0x38,0x34,0xe8,0x36 +,0x06,0xcd,0x50,0xc3,0xe9,0x16,0x00,0xcd,0x34,0xe9,0x11,0x00,0xcd,0x34,0x89,0x36 +,0x3d,0x37,0xa1,0x9d,0x36,0xa3,0x3f,0x37,0xc6,0x06,0xa0,0x36,0x0c,0xe8,0x8e,0x00 +,0xa1,0x9f,0x36,0x22,0xe4,0x75,0x32,0xf7,0x06,0x4c,0x37,0x01,0x00,0x75,0x2a,0xf6 +,0x06,0x9d,0x36,0x80,0x74,0x07,0x88,0x26,0x9e,0x36,0xe9,0x31,0x00,0x3a,0x06,0x9d +,0x36,0xa3,0x9d,0x36,0x74,0x28,0x8b,0xf0,0x2e,0xff,0xa4,0x0d,0x2b,0x44,0x29,0xee +,0x42,0x19,0x44,0xcd,0x44,0x2f,0x45,0x5a,0x45,0x3a,0x26,0x9e,0x36,0x75,0x01,0xc3 +,0x32,0xc0,0x86,0xc4,0x8b,0xf0,0xa2,0x9e,0x36,0x2e,0xff,0xa4,0x20,0x49,0x8b,0x2e +,0x99,0x36,0x23,0xed,0x75,0x01,0xc3,0xbf,0x01,0x00,0xbe,0x00,0x00,0x85,0xfd,0x75 +,0x1a,0x46,0xd1,0xe7,0xe9,0xf6,0xff,0x2a,0x00,0x29,0x00,0x28,0x00,0x27,0x00,0x25 +,0x00,0x05,0x00,0x07,0x00,0x26,0x00,0x06,0x00,0x20,0x00,0xf7,0xd7,0x21,0x3e,0x99 +,0x36,0xd1,0xe6,0x2e,0x8b,0xb4,0x47,0x2b,0xe9,0x4f,0xff,0xe9,0x56,0xff,0x80,0x26 +,0x9e,0x36,0xff,0x75,0x17,0xf7,0x06,0x4c,0x37,0x01,0x00,0x75,0x0f,0xf6,0x06,0x9d +,0x36,0x80,0x74,0x08,0xf7,0x06,0x66,0x37,0xff,0xff,0x75,0x07,0xc7,0x06,0x66,0x37 +,0x00,0x00,0xc3,0xf7,0x06,0x41,0x37,0x01,0x00,0x75,0x0b,0xb8,0x7f,0x03,0xcd,0x39 +,0xc7,0x06,0x41,0x37,0x01,0x00,0x33,0xf6,0xb8,0x00,0x40,0x85,0x06,0x66,0x37,0x74 +,0x21,0x80,0xbc,0x54,0x37,0xff,0x74,0x04,0xfe,0x84,0x54,0x37,0x80,0xbc,0x96,0x34 +,0xff,0x74,0x04,0xfe,0x84,0x96,0x34,0x31,0x06,0x66,0x37,0x83,0x3e,0x66,0x37,0x00 +,0x74,0x05,0x46,0xd1,0xe8,0x73,0xd4,0xc3,0xa1,0xf4,0x33,0xa9,0x00,0x88,0x74,0x0b +,0xa9,0x00,0x10,0x75,0x09,0x8b,0x1e,0x43,0x37,0xff,0xe3,0xe9,0xd7,0x00,0xc7,0x06 +,0x35,0x37,0x05,0x00,0xc7,0x06,0x43,0x37,0x1e,0x2c,0xf7,0x06,0xf4,0x33,0x00,0x08 +,0x74,0x06,0xc7,0x06,0x43,0x37,0x10,0x2c,0xb8,0x80,0x03,0xcd,0x39,0xe9,0xcd,0xfe +,0xa9,0x00,0x08,0x74,0xd9,0xff,0x0e,0x35,0x37,0x75,0xed,0xe9,0x66,0x00,0xa9,0x00 +,0x08,0x75,0xcb,0xff,0x0e,0x35,0x37,0x75,0xdf,0x81,0x0e,0xc2,0x34,0xc0,0x00,0xf6 +,0x06,0x9d,0x36,0x80,0x74,0x48,0x81,0x0e,0x9b,0x36,0x00,0x80,0xf7,0x06,0x9b,0x36 +,0x01,0x00,0x74,0x1e,0xb8,0x7d,0x03,0xcd,0x3a,0x81,0x0e,0x9b,0x36,0x80,0x00,0x83 +,0x26,0x9b,0x36,0xfe,0xc7,0x06,0x0f,0x37,0x02,0x00,0xc6,0x06,0xa0,0x36,0x04,0xe9 +,0x7b,0xfe,0x80,0x3e,0xa0,0x36,0x04,0x75,0x07,0x83,0x3e,0x0f,0x37,0x01,0x75,0x05 +,0xc6,0x06,0xa0,0x36,0x06,0xc7,0x06,0x0f,0x37,0x02,0x00,0xe9,0x5f,0xfe,0xbe,0x02 +,0x00,0xe9,0x4a,0xfe,0x80,0x26,0x9e,0x36,0xff,0x75,0x3a,0xf6,0x06,0x9d,0x36,0x80 +,0x74,0x2d,0xf7,0x06,0x9b,0x36,0x00,0x20,0x75,0x2b,0xc6,0x06,0xa0,0x36,0x06,0xff +,0x06,0x94,0x34,0x83,0x0e,0x66,0x37,0x20,0x8e,0x06,0x30,0x34,0x26,0xf7,0x06,0x0a +,0x00,0x00,0x01,0x74,0x07,0x26,0x81,0x0e,0x08,0x00,0x00,0x01,0xe9,0x06,0x00,0xbe +,0x04,0x00,0xe9,0x09,0xfe,0x81,0x0e,0xaf,0x36,0x00,0x08,0xa1,0xaf,0x36,0xe7,0x06 +,0xe5,0x0a,0xa9,0x00,0x80,0x74,0x0e,0x81,0x26,0xaf,0x36,0xff,0xf7,0xa1,0xaf,0x36 +,0xe7,0x06,0xe9,0x09,0xff,0xe9,0xf5,0xfd,0xc7,0x06,0x41,0x37,0x00,0x00,0x83,0x0e +,0x99,0x36,0x02,0xe9,0xe7,0xfd,0x80,0x26,0x9e,0x36,0xff,0x75,0x1d,0xf7,0x06,0x9b +,0x36,0x00,0x40,0x75,0x05,0x83,0x0e,0x99,0x36,0x08,0x83,0x0e,0x99,0x36,0x20,0x81 +,0x26,0x9b,0x36,0xff,0xbf,0xb8,0x85,0x03,0xcd,0x39,0xe9,0xc0,0xfd,0x80,0x3e,0x9e +,0x36,0x06,0x74,0x07,0x80,0x3e,0x9e,0x36,0x0a,0x75,0x34,0xf6,0x06,0x9d,0x36,0x80 +,0x75,0x06,0xbe,0x07,0x00,0xe9,0x96,0xfd,0xc6,0x06,0xa0,0x36,0x04,0x83,0x3e,0x0f +,0x37,0x02,0x74,0x1b,0xc7,0x06,0x0f,0x37,0x04,0x00,0x80,0x3e,0x9e,0x36,0x06,0x75 +,0x0e,0xf7,0x06,0x9b,0x36,0x40,0x00,0x75,0x06,0xc7,0x06,0x0f,0x37,0x03,0x00,0xe9 +,0x7b,0xfd,0x80,0x3e,0x9d,0x36,0x04,0x75,0x12,0x81,0x0e,0xc2,0x34,0x00,0x40,0xff +,0x06,0x92,0x34,0xc6,0x06,0xa0,0x36,0x06,0xe9,0x62,0xfd,0xbe,0x05,0x00,0xe9,0x4d +,0xfd,0xf6,0x06,0x9d,0x36,0x80,0x75,0x19,0x83,0x0e,0xc2,0x34,0x04,0xbe,0x06,0x00 +,0xe9,0x3b,0xfd,0x80,0x26,0x9e,0x36,0xff,0x75,0xc5,0xff,0x06,0x31,0x37,0xe9,0x00 +,0x00,0x83,0x26,0xc2,0x34,0xbf,0xc6,0x06,0xa0,0x36,0x06,0xe9,0x2f,0xfd,0xe5,0x0a +,0x50,0x25,0xc3,0xbf,0xe7,0x0a,0x58,0x80,0x26,0x9e,0x36,0xff,0x75,0x0d,0xa9,0x00 +,0x40,0x75,0x08,0xc6,0x06,0xa0,0x36,0x06,0xe9,0x12,0xfd,0xb8,0x83,0x03,0xcd,0x39 +,0xc3,0xb8,0x7c,0x03,0xcd,0x39,0xf7,0x06,0xf4,0x33,0x00,0x10,0x75,0x09,0xc7,0x06 +,0x33,0x37,0x02,0x00,0xe9,0xf6,0xfc,0xff,0x0e,0x33,0x37,0x74,0x03,0xe9,0xed,0xfc +,0xff,0x06,0x8e,0x34,0xe8,0xf7,0x19,0x83,0x0e,0xc2,0x34,0x08,0xbe,0x03,0x00,0xe9 +,0xcc,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x04,0x04,0x05 +,0x04,0x04,0x04,0x00,0x03,0x00,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x04,0x00,0x08,0x08,0x05,0x08,0x08,0x08,0x00,0x03,0x00,0x03,0x03,0x00,0x00 +,0x02,0x04,0x04,0x04,0x04,0x00,0x00,0x08,0x00,0x00,0x0a,0x14,0x00,0x00,0x1a,0x00 +,0x1c,0x00,0x1e,0x20,0x00,0x00,0x04,0x41,0x06,0x0b,0x08,0xc2,0xff,0xe7,0x04,0x03 +,0x06,0x04,0x04,0x05,0x04,0x06,0x04,0x87,0x04,0x03,0x06,0x04,0x04,0x85,0x4e,0xa2 +,0x04,0xcf,0x04,0xcd,0xc7,0x06,0xa2,0x37,0x00,0x00,0xc7,0x06,0xa6,0x37,0x00,0x00 +,0x26,0xa1,0x20,0x00,0x25,0x7f,0xff,0xa3,0xf5,0x36,0x26,0xa1,0x22,0x00,0xa3,0xf7 +,0x36,0x26,0xa1,0x24,0x00,0xa3,0xf9,0x36,0xe8,0x3b,0x19,0x8b,0xf0,0x26,0x8b,0x0e +,0x0e,0x00,0x2b,0xc8,0x83,0xe9,0x0e,0xb8,0x01,0x80,0x83,0xf9,0x04,0x7c,0x51,0x26 +,0x8a,0x54,0x28,0x88,0x16,0x1c,0x37,0x40,0x26,0x8b,0x6c,0x26,0x86,0xcd,0x3b,0xcd +,0x86,0xcd,0x89,0x0e,0xa4,0x37,0x75,0x38,0x40,0x32,0xff,0x26,0x8a,0x5c,0x29,0x80 +,0xfb,0x15,0x77,0x25,0x80,0xfb,0x0a,0x74,0x20,0x80,0xfb,0x01,0x74,0x1b,0xb8,0x04 +,0x80,0x2e,0x3a,0x97,0x02,0x2e,0x74,0x07,0x2e,0x3a,0x97,0x18,0x2e,0x75,0x11,0x33 +,0xc0,0x80,0xfb,0x09,0x75,0x4f,0x8b,0xf3,0xc3,0x26,0xc7,0x06,0x04,0x00,0xff,0xff +,0x50,0x52,0xa1,0xa4,0x37,0x86,0xc4,0x26,0x3b,0x06,0x26,0x00,0x7c,0x32,0x26,0x81 +,0x3e,0x26,0x00,0x00,0x04,0x7e,0x29,0x8d,0x74,0x2a,0x26,0x8b,0x14,0x22,0xd2,0x74 +,0x1f,0x80,0xe6,0xbf,0x80,0xfe,0x09,0x75,0x17,0xc7,0x06,0xa2,0x37,0x01,0x00,0x80 +,0xfa,0x04,0x75,0x0c,0x26,0x8b,0x44,0x02,0xa3,0x03,0x37,0x86,0xc4,0xa3,0xd0,0x34 +,0x5a,0x58,0xe9,0xb1,0xff,0xbd,0x72,0x37,0x2e,0x8a,0x87,0x2e,0x2e,0x22,0xc0,0x74 +,0x16,0x05,0x44,0x2e,0x8b,0xf8,0x2e,0x8b,0x05,0x3e,0x89,0x46,0x00,0x83,0xc5,0x02 +,0x83,0xc7,0x02,0x22,0xe4,0x7d,0xef,0x8d,0x74,0x2a,0x83,0xe9,0x04,0x75,0x03,0xe9 +,0xa1,0x00,0x26,0x8b,0x14,0x22,0xd2,0x75,0x03,0xe9,0x7c,0x00,0xc7,0x06,0xa6,0x37 +,0x01,0x00,0xbf,0x72,0x37,0x8b,0x05,0x83,0xc7,0x02,0x80,0xe6,0xbf,0x80,0xe4,0x3f +,0x80,0xfe,0x09,0x75,0x22,0x80,0xfa,0x04,0x75,0x5e,0xc7,0x06,0xa2,0x37,0x01,0x00 +,0x26,0x8b,0x44,0x02,0xa3,0x03,0x37,0x86,0xc4,0xa3,0xd0,0x34,0x86,0xc4,0xc7,0x06 +,0xa6,0x37,0x00,0x00,0xe9,0x47,0x00,0x3b,0xfd,0x7e,0x15,0x26,0x8b,0x04,0xa8,0x40 +,0x74,0x06,0xb8,0x07,0x80,0xe9,0x38,0xff,0x32,0xc0,0x26,0x8b,0x04,0xe9,0x2e,0x00 +,0x3a,0xf4,0x75,0xb1,0xc7,0x45,0xfe,0x00,0x00,0x80,0xfe,0x22,0x75,0x0d,0x3a,0xd0 +,0x77,0x16,0xc7,0x06,0xa6,0x37,0x00,0x00,0xe9,0x13,0x00,0x3a,0xd0,0x75,0x09,0xc7 +,0x06,0xa6,0x37,0x00,0x00,0xe9,0x06,0x00,0xb8,0x05,0x80,0xe9,0x02,0xff,0x32,0xf6 +,0x03,0xf2,0x2b,0xca,0xb8,0x05,0x80,0x23,0xc9,0x76,0x03,0xe9,0x64,0xff,0x74,0x03 +,0xe9,0xed,0xfe,0x33,0xc0,0xbf,0x72,0x37,0x8b,0x15,0x47,0x47,0x3b,0xfd,0x7f,0x1b +,0xf6,0xc6,0x80,0x74,0x16,0xf7,0x06,0xa6,0x37,0x01,0x00,0x74,0x06,0xb8,0x08,0x80 +,0xe9,0xc3,0xfe,0xf6,0xc6,0x40,0x74,0xe0,0xb8,0x07,0x80,0xe9,0xb8,0xfe,0x7d,0x42 +,0xa3,0x45,0x44,0x29,0x44,0x29,0xb7,0x28,0xe2,0x28,0xee,0x2b,0xf2,0x28,0xf5,0x28 +,0x01,0x29,0xac,0x2a,0x44,0x29,0x44,0x29,0x44,0x29,0x44,0x29,0x44,0x29,0x00,0x00 +,0x73,0x36,0x00,0x00,0x03,0x36,0xc5,0x35,0x83,0x35,0x45,0x35,0x07,0x35,0xd2,0x34 +,0x45,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0xa6,0x38,0x00,0x00,0xe0,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0xf2,0x33,0x00,0x00,0xa6,0x33,0x60,0x33,0xfd,0x32,0xbc,0x32,0x77,0x32,0x3c,0x32 +,0xfb,0x31,0x6a,0x31,0x0a,0x31,0xe0,0xe0,0x10,0x10,0x10,0xe0,0xe0,0xe0,0xe0,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x00,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0 +,0xe0,0x33,0xff,0x26,0xf6,0x06,0x1a,0x00,0x80,0x74,0x1b,0x26,0x80,0x26,0x1a,0x00 +,0x7f,0x26,0x8b,0x3e,0x26,0x00,0x83,0xe7,0x1f,0x74,0x0b,0x26,0x80,0x0e,0x20,0x00 +,0x80,0x26,0x01,0x3e,0x0e,0x00,0xc3,0x60,0x2e,0x8b,0x84,0xa6,0x30,0x26,0xa3,0x18 +,0x00,0xd1,0xe6,0x2e,0xff,0x94,0x50,0x30,0x61,0xc3,0x26,0xc7,0x06,0x04,0x00,0xc4 +,0x2a,0x26,0xc7,0x06,0x0e,0x00,0x16,0x00,0x26,0xc7,0x06,0x06,0x00,0x06,0x00,0x26 +,0xc6,0x06,0x19,0x00,0x00,0xe8,0xbf,0x05,0xe8,0x98,0x05,0x26,0xc7,0x06,0x26,0x00 +,0x00,0x08,0x26,0xc6,0x06,0x28,0x00,0x40,0x26,0xc6,0x06,0x29,0x00,0x2a,0xbf,0x2a +,0x00,0x26,0xc6,0x05,0x04,0x26,0xc6,0x45,0x01,0x2a,0xa1,0x93,0x37,0x33,0xdb,0xa9 +,0x40,0x00,0x75,0x02,0xb3,0x01,0xa9,0x00,0x10,0x74,0x02,0xb7,0x88,0xa9,0x00,0x08 +,0x74,0x03,0x80,0xcf,0x44,0x26,0x89,0x5d,0x02,0xc3,0x83,0x0e,0xc2,0x34,0x20,0x26 +,0xc7,0x06,0x04,0x00,0x6b,0x2b,0x26,0xc7,0x06,0x0e,0x00,0x30,0x00,0x26,0xc7,0x06 +,0x06,0x00,0x0a,0x00,0x26,0xc7,0x06,0x0a,0x00,0x04,0x00,0x26,0xc6,0x06,0x19,0x00 +,0x00,0xe8,0x69,0x05,0xe8,0x2c,0x05,0x26,0xc7,0x06,0x26,0x00,0x00,0x22,0x26,0xc6 +,0x06,0x28,0x00,0x60,0x26,0xc6,0x06,0x29,0x00,0x29,0xbf,0x2a,0x00,0x26,0xc6,0x05 +,0x08,0x26,0xc6,0x45,0x01,0x2d,0x8d,0x7d,0x02,0xbe,0x54,0x37,0xb9,0x03,0x00,0xf3 +,0xa5,0x26,0xc6,0x05,0x08,0x26,0xc6,0x45,0x01,0x2e,0x8d,0x7d,0x02,0xbe,0x5a,0x37 +,0xb9,0x03,0x00,0xf3,0xa5,0xe8,0xd4,0x05,0xe8,0x64,0x05,0xb9,0x06,0x00,0xbe,0x54 +,0x37,0x8d,0x2e,0x2c,0x00,0x26,0x8b,0x46,0x00,0x29,0x04,0x83,0xc6,0x02,0x83,0xc5 +,0x02,0x83,0xf9,0x04,0x75,0x02,0x45,0x45,0xe2,0xeb,0xc3,0x26,0xc7,0x06,0x04,0x00 +,0xc4,0x2a,0x26,0xc7,0x06,0x0e,0x00,0x24,0x00,0x26,0xc7,0x06,0x06,0x00,0x06,0x00 +,0x26,0xc6,0x06,0x19,0x00,0x00,0xe8,0xe4,0x04,0xe8,0xa7,0x04,0x26,0xc7,0x06,0x26 +,0x00,0x00,0x16,0x26,0xc6,0x06,0x28,0x00,0x60,0x26,0xc6,0x06,0x29,0x00,0x28,0xbf +,0x2a,0x00,0xe8,0x5b,0x06,0xe8,0x74,0x05,0xe8,0x04,0x05,0xc3,0x26,0xc7,0x06,0x04 +,0x00,0xc4,0x2a,0x26,0xc7,0x06,0x0e,0x00,0x1a,0x00,0x26,0xc7,0x06,0x06,0x00,0x06 +,0x00,0x26,0xc6,0x06,0x19,0x00,0x00,0xe8,0xa3,0x04,0xe8,0x66,0x04,0x26,0xc7,0x06 +,0x26,0x00,0x00,0x0c,0x26,0xc6,0x06,0x28,0x00,0x60,0x26,0xc6,0x06,0x29,0x00,0x27 +,0xbf,0x2a,0x00,0xe8,0x21,0x05,0xc3,0x26,0xc7,0x06,0x04,0x00,0xc4,0x2a,0x26,0xc7 +,0x06,0x0e,0x00,0x20,0x00,0x26,0xc7,0x06,0x06,0x00,0x0a,0x00,0x26,0xc7,0x06,0x0a +,0x00,0x04,0x00,0x26,0xc6,0x06,0x19,0x00,0x00,0xe8,0x4b,0x04,0xe8,0x24,0x04,0x26 +,0xc7,0x06,0x26,0x00,0x00,0x12,0x26,0xc6,0x06,0x28,0x00,0x40,0x26,0xc6,0x06,0x29 +,0x00,0x26,0xbf,0x2a,0x00,0xe8,0xf4,0x04,0xe8,0x84,0x04,0xc3,0x26,0xc7,0x06,0x04 +,0x00,0xc4,0x2a,0x26,0xc7,0x06,0x0e,0x00,0x34,0x00,0x26,0xc7,0x06,0x06,0x00,0x06 +,0x00,0x26,0xc6,0x06,0x19,0x00,0x00,0xe8,0x0d,0x04,0xe8,0xe6,0x03,0x26,0xc7,0x06 +,0x26,0x00,0x00,0x26,0x26,0xc6,0x06,0x28,0x00,0x40,0x26,0xc6,0x06,0x29,0x00,0x25 +,0xbf,0x2a,0x00,0xe8,0xb6,0x04,0xe8,0x46,0x04,0xe8,0xfa,0x04,0xc3,0x26,0xc7,0x06 +,0x04,0x00,0xc4,0x2a,0x26,0xc7,0x06,0x0e,0x00,0x38,0x00,0xa1,0xa2,0x37,0x50,0x0b +,0xc0,0x75,0x07,0x26,0xc7,0x06,0x0e,0x00,0x34,0x00,0x26,0xc7,0x06,0x06,0x00,0x06 +,0x00,0x26,0xc6,0x06,0x19,0x00,0x00,0xe8,0x99,0x03,0xe8,0xa4,0xfd,0x26,0xc7,0x45 +,0x26,0x00,0x2a,0x58,0x0b,0xc0,0x75,0x06,0x26,0xc7,0x45,0x26,0x00,0x26,0xa1,0x1c +,0x37,0xc1,0xe0,0x04,0x26,0x88,0x45,0x28,0x26,0xc6,0x45,0x29,0x24,0x83,0xc7,0x2a +,0xe8,0x29,0x04,0xe8,0xa0,0x04,0xe8,0x22,0x05,0xe8,0xf8,0x03,0xe8,0x09,0x04,0xc3 +,0x26,0xc7,0x06,0x04,0x00,0xc4,0x2a,0x26,0xc7,0x06,0x0e,0x00,0x32,0x00,0x26,0xc7 +,0x06,0x06,0x00,0x06,0x00,0x26,0xc6,0x06,0x19,0x00,0x00,0xe8,0x45,0x03,0xe8,0x50 +,0xfd,0x26,0xc7,0x45,0x26,0x00,0x24,0xa1,0x1c,0x37,0xc1,0xe0,0x04,0x26,0x88,0x45 +,0x28,0x26,0xc6,0x45,0x29,0x23,0x83,0xc7,0x2a,0xe8,0xe0,0x03,0xe8,0x6c,0x04,0xe8 +,0x8a,0x04,0xe8,0x9c,0x04,0xc3,0x26,0xc7,0x06,0x04,0x00,0xc4,0x2a,0x26,0xc7,0x06 +,0x0e,0x00,0x34,0x00,0x26,0xc7,0x06,0x06,0x00,0x06,0x00,0x26,0xc6,0x06,0x19,0x00 +,0x00,0xe8,0xff,0x02,0xe8,0x0a,0xfd,0x26,0xc7,0x45,0x26,0x00,0x26,0xa1,0x1c,0x37 +,0xc1,0xe0,0x04,0x26,0x88,0x45,0x28,0x26,0xc6,0x45,0x29,0x22,0x83,0xc7,0x2a,0xe8 +,0x9a,0x03,0xe8,0xc7,0x03,0xe8,0x57,0x03,0xe8,0xf8,0x03,0xe8,0x78,0x04,0xe8,0x8a +,0x04,0xc3,0x26,0xc7,0x06,0x04,0x00,0x74,0x45,0x26,0xc7,0x06,0x0e,0x00,0x3e,0x00 +,0x26,0xc7,0x06,0x06,0x00,0x06,0x00,0x26,0xc7,0x06,0x0a,0x00,0x04,0x00,0x26,0xc6 +,0x06,0x19,0x00,0x00,0xe8,0xfc,0x02,0xe8,0xa9,0x02,0x83,0x3e,0x8d,0x37,0x03,0x75 +,0x01,0x90,0x26,0xc7,0x06,0x26,0x00,0x00,0x30,0x26,0xc6,0x06,0x28,0x00,0x50,0x26 +,0xc6,0x06,0x29,0x00,0x20,0xbf,0x2a,0x00,0xe8,0xd0,0x03,0xe8,0x01,0x03,0xe8,0xb5 +,0x03,0xe8,0x9f,0x03,0xc3,0x26,0xc7,0x06,0x04,0x00,0x61,0x43,0xb9,0xf0,0x00,0x83 +,0xe9,0x02,0x26,0x89,0x0e,0x0e,0x00,0x26,0xc7,0x06,0x06,0x00,0x02,0x00,0x26,0xc6 +,0x06,0x19,0x00,0x00,0x26,0xc7,0x06,0x1a,0x00,0x00,0x00,0x26,0xc7,0x06,0x1c,0x00 +,0x00,0x00,0x26,0xc7,0x06,0x1e,0x00,0x00,0x00,0xe8,0x47,0x02,0x83,0xe9,0x0e,0x86 +,0xcd,0x26,0x89,0x0e,0x26,0x00,0x86,0xcd,0x26,0xc6,0x06,0x28,0x00,0x00,0x26,0xc6 +,0x06,0x29,0x00,0x08,0xbf,0x2a,0x00,0x83,0xe9,0x04,0x26,0x89,0x0d,0x26,0xc6,0x45 +,0x01,0x26,0x8d,0x7d,0x02,0x83,0xe9,0x02,0xbb,0x01,0x00,0xb8,0x30,0x30,0x4b,0x75 +,0x17,0xbb,0x0a,0x00,0x8a,0xc4,0x26,0x88,0x05,0xb0,0x31,0x80,0xc4,0x01,0x80,0xfc +,0x3a,0x75,0x0a,0xb4,0x61,0xe9,0x05,0x00,0x26,0x88,0x05,0x04,0x01,0x47,0x49,0x75 +,0xdd,0xc3,0x26,0xc7,0x06,0x04,0x00,0x04,0x45,0x26,0xc7,0x06,0x0e,0x00,0x12,0x00 +,0x26,0xc7,0x06,0x06,0x00,0x06,0x00,0x26,0xc6,0x06,0x19,0x00,0x01,0xe8,0xe5,0x01 +,0xe8,0xd0,0x01,0x26,0xc7,0x06,0x26,0x00,0x00,0x04,0x26,0xc6,0x06,0x28,0x00,0x00 +,0x26,0xc6,0x06,0x29,0x00,0x07,0xc3,0x26,0xc7,0x06,0x04,0x00,0xc4,0x2a,0x26,0xc7 +,0x06,0x0e,0x00,0x20,0x00,0x26,0xc7,0x06,0x06,0x00,0x06,0x00,0x26,0xc6,0x06,0x19 +,0x00,0x06,0xe8,0x04,0x02,0xe8,0x9b,0x01,0x26,0xc7,0x06,0x26,0x00,0x00,0x12,0x26 +,0xc6,0x06,0x28,0x00,0x00,0x26,0xc6,0x06,0x29,0x00,0x06,0xbf,0x2a,0x00,0xe8,0x6b +,0x02,0xe8,0xfb,0x01,0xc3,0x26,0xc7,0x06,0x04,0x00,0xc4,0x2a,0x26,0xc7,0x06,0x0e +,0x00,0x20,0x00,0x26,0xc7,0x06,0x06,0x00,0x06,0x00,0x26,0xc6,0x06,0x19,0x00,0x05 +,0xe8,0xc6,0x01,0xe8,0x5d,0x01,0x26,0xc7,0x06,0x26,0x00,0x00,0x12,0x26,0xc6,0x06 +,0x28,0x00,0x00,0x26,0xc6,0x06,0x29,0x00,0x05,0xbf,0x2a,0x00,0xe8,0x2d,0x02,0xe8 +,0xbd,0x01,0xc3,0xff,0x06,0x82,0x34,0x26,0xc7,0x06,0x04,0x00,0x3d,0x41,0x26,0xc7 +,0x06,0x0e,0x00,0x20,0x00,0x26,0xc7,0x06,0x06,0x00,0x0e,0x00,0x26,0xc6,0x06,0x19 +,0x00,0x04,0xe8,0x84,0x01,0xe8,0x1b,0x01,0x26,0xc7,0x06,0x26,0x00,0x00,0x12,0x26 +,0xc6,0x06,0x28,0x00,0x00,0x26,0xc6,0x06,0x29,0x00,0x04,0xbf,0x2a,0x00,0xe8,0xeb +,0x01,0xe8,0x7b,0x01,0xc3,0x26,0xc7,0x06,0x04,0x00,0x67,0x42,0x26,0xc7,0x06,0x0e +,0x00,0x20,0x00,0x26,0xc7,0x06,0x06,0x00,0x08,0x00,0x26,0xc6,0x06,0x19,0x00,0x03 +,0xe8,0x46,0x01,0xe8,0xdd,0x00,0x26,0xc7,0x06,0x26,0x00,0x00,0x12,0x26,0xc6,0x06 +,0x28,0x00,0x00,0x26,0xc6,0x06,0x29,0x00,0x03,0xbf,0x2a,0x00,0xe8,0xad,0x01,0xe8 +,0x3d,0x01,0xc3,0xff,0x06,0x84,0x34,0x26,0xc7,0x06,0x04,0x00,0x67,0x42,0x26,0xc7 +,0x06,0x0e,0x00,0x24,0x00,0x26,0xc7,0x06,0x06,0x00,0x08,0x00,0x26,0xc6,0x06,0x19 +,0x00,0x02,0xe8,0x04,0x01,0xe8,0x9b,0x00,0x26,0xc7,0x06,0x26,0x00,0x00,0x16,0x26 +,0xc6,0x06,0x28,0x00,0x00,0x26,0xc6,0x06,0x29,0x00,0x02,0xbf,0x2a,0x00,0x26,0xc6 +,0x05,0x04,0x26,0xc6,0x45,0x01,0x01,0xa1,0x0f,0x37,0x86,0xe0,0xf6,0x06,0x6f,0x37 +,0x01,0x75,0x0f,0x39,0x06,0xcc,0x34,0x74,0x09,0x8b,0xd8,0xb8,0x89,0x03,0xcd,0x39 +,0x8b,0xc3,0xa3,0xcc,0x34,0x26,0x89,0x45,0x02,0x8d,0x7d,0x04,0xe8,0x3d,0x01,0xe8 +,0xcd,0x00,0xc3,0x26,0xc7,0x06,0x04,0x00,0xc4,0x2a,0x26,0xc7,0x06,0x0e,0x00,0x1c +,0x00,0xa1,0xa2,0x37,0x50,0x0b,0xc0,0x75,0x07,0x26,0xc7,0x06,0x0e,0x00,0x18,0x00 +,0x26,0xc7,0x06,0x06,0x00,0x06,0x00,0x26,0xc6,0x06,0x19,0x00,0x00,0xe8,0x23,0x00 +,0xe8,0x2e,0xfa,0x26,0xc7,0x45,0x26,0x00,0x0e,0x58,0x0b,0xc0,0x75,0x06,0x26,0xc7 +,0x45,0x26,0x00,0x0a,0x26,0xc6,0x45,0x29,0x00,0x83,0xc7,0x2a,0xe8,0xbd,0x00,0xe8 +,0xff,0x00,0xc3,0x56,0x57,0x51,0xb9,0x03,0x00,0xbe,0xd1,0x36,0xbf,0x20,0x00,0xf3 +,0xa5,0x59,0x5f,0x5e,0xc3,0x56,0x57,0x51,0xb9,0x03,0x00,0xbe,0xd1,0x36,0xbf,0x1a +,0x00,0xf3,0xa5,0x59,0x5f,0x5e,0xc3,0x26,0xc7,0x06,0x1a,0x00,0xc0,0x00,0x26,0xc7 +,0x06,0x1c,0x00,0x00,0x00,0x26,0xc7,0x06,0x1e,0x00,0x00,0x10,0xc3,0x26,0xc7,0x06 +,0x1a,0x00,0xc0,0x00,0x26,0xc7,0x06,0x1c,0x00,0x00,0x00,0x26,0xc7,0x06,0x1e,0x00 +,0x00,0x08,0xc3,0x26,0xc7,0x06,0x1a,0x00,0xc0,0x00,0x26,0xc7,0x06,0x1c,0x00,0x00 +,0x00,0x26,0xc7,0x06,0x1e,0x00,0x00,0x02,0xc3,0x26,0xc7,0x06,0x1a,0x00,0xc0,0x00 +,0x26,0xc7,0x06,0x1c,0x00,0xff,0xff,0x26,0xc7,0x06,0x1e,0x00,0xff,0xff,0xc3,0x26 +,0xc6,0x05,0x08,0x26,0xc6,0x45,0x01,0x02,0x8d,0x7d,0x02,0xbe,0x05,0x37,0xb9,0x03 +,0x00,0xf3,0xa5,0xc3,0x26,0xc6,0x05,0x04,0x26,0xc6,0x45,0x01,0x06,0xa1,0x0d,0x37 +,0x26,0x89,0x45,0x02,0x8d,0x7d,0x04,0xc3,0x26,0xc6,0x05,0x04,0x26,0xc6,0x45,0x01 +,0x07,0xa1,0x0b,0x37,0x26,0x89,0x45,0x02,0x83,0xc7,0x04,0xc3,0xa1,0xa2,0x37,0x0b +,0xc0,0x74,0x13,0x26,0xc6,0x05,0x04,0x26,0xc6,0x45,0x01,0x09,0xa1,0x03,0x37,0x26 +,0x89,0x45,0x02,0x83,0xc7,0x04,0xc3,0x26,0xc6,0x05,0x08,0x26,0xc6,0x45,0x01,0x02 +,0x8d,0x7d,0x02,0xbe,0x05,0x37,0xb9,0x03,0x00,0xf3,0xa5,0xc3,0x26,0xc6,0x05,0x06 +,0x26,0xc6,0x45,0x01,0x0b,0x8d,0x7d,0x02,0xbe,0xef,0x36,0xb9,0x02,0x00,0xf3,0xa5 +,0xc3,0x26,0xc6,0x05,0x06,0x26,0xc6,0x45,0x01,0x20,0xa1,0x68,0x37,0x26,0x89,0x45 +,0x02,0xa1,0x6a,0x37,0x26,0x88,0x65,0x05,0xc1,0xe0,0x04,0x26,0x88,0x45,0x04,0x83 +,0xc7,0x06,0xc3,0x26,0xc6,0x05,0x04,0x26,0xc6,0x45,0x01,0x21,0x26,0xc7,0x45,0x02 +,0x00,0x00,0x83,0xc7,0x04,0xc3,0x26,0xc6,0x05,0x14,0x26,0xc6,0x45,0x01,0x22,0x8d +,0x7d,0x02,0xbe,0x1f,0x37,0xb9,0x09,0x00,0xf3,0xa5,0xc3,0x26,0xc6,0x05,0x0c,0x26 +,0xc6,0x45,0x01,0x23,0x8d,0x7d,0x02,0x1e,0x0e,0x1f,0x8d,0x36,0x40,0x54,0xb9,0x03 +,0x00,0xf3,0xa5,0x33,0xc0,0xb9,0x02,0x00,0xf3,0xab,0x1f,0xc3,0x26,0xc6,0x05,0x08 +,0x26,0xc6,0x45,0x01,0x28,0x8d,0x7d,0x02,0xbe,0xd1,0x36,0xb9,0x03,0x00,0xf3,0xa5 +,0xc3,0x26,0xc6,0x05,0x08,0x26,0xc6,0x45,0x01,0x29,0xa1,0xc2,0x34,0x86,0xe0,0x26 +,0x89,0x45,0x02,0xa1,0x9b,0x36,0x26,0x89,0x45,0x04,0x26,0x88,0x45,0x06,0x26,0x88 +,0x45,0x07,0x8d,0x7d,0x08,0xc3,0x26,0xc6,0x05,0x06,0x26,0xc6,0x45,0x01,0x2b,0x8d +,0x7d,0x02,0xbe,0xbb,0x36,0xb9,0x02,0x00,0xf3,0xa5,0xc3,0x26,0xc6,0x05,0x06,0x26 +,0xc6,0x45,0x01,0x2c,0x8d,0x7d,0x02,0xbe,0xe5,0x36,0xb9,0x02,0x00,0xf3,0xa5,0xc3 +,0x26,0xc6,0x05,0x04,0x26,0xc6,0x45,0x01,0x30,0xa1,0x37,0x37,0x86,0xe0,0x26,0x89 +,0x45,0x02,0x8d,0x7d,0x04,0xc3,0x26,0xc7,0x06,0x0e,0x00,0x1e,0x00,0x26,0xc7,0x06 +,0x06,0x00,0x02,0x00,0x26,0xc6,0x06,0x19,0x00,0x00,0xe8,0x6c,0xfe,0xe8,0x03,0xfe +,0x26,0xc7,0x06,0x26,0x00,0x00,0x10,0x26,0xc6,0x06,0x28,0x00,0x30,0x26,0xc6,0x06 +,0x29,0x00,0x11,0xbf,0x2a,0x00,0xe8,0x35,0x00,0xe8,0x45,0x00,0xe8,0x55,0x00,0xc3 +,0x26,0xc7,0x06,0x0e,0x00,0x12,0x00,0x26,0xc7,0x06,0x06,0x00,0x02,0x00,0x26,0xc6 +,0x06,0x19,0x00,0x00,0xe8,0x32,0xfe,0xe8,0xc9,0xfd,0x26,0xc7,0x06,0x26,0x00,0x00 +,0x04,0x26,0xc6,0x06,0x28,0x00,0x30,0x26,0xc6,0x06,0x29,0x00,0x13,0xc3,0x26,0xc6 +,0x05,0x04,0x26,0xc6,0x45,0x01,0x0c,0x26,0xc7,0x45,0x02,0x00,0x01,0x83,0xc7,0x04 +,0xc3,0x26,0xc6,0x05,0x04,0x26,0xc6,0x45,0x01,0x0e,0x26,0xc7,0x45,0x02,0x00,0x02 +,0x83,0xc7,0x04,0xc3,0x26,0xc6,0x05,0x04,0x26,0xc6,0x45,0x01,0x21,0x26,0xc7,0x45 +,0x02,0x00,0x00,0x83,0xc7,0x04,0xc3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0xb3,0x39,0xc9,0x39,0x83,0x3a,0xb3,0x39,0xb3,0x39,0xb3,0x39,0x1c,0x3a,0x1c,0x3a +,0xa3,0xb6,0x34,0xa1,0xe9,0x36,0xa3,0x11,0x37,0xa3,0xd2,0x34,0xa1,0xeb,0x36,0xa3 +,0x13,0x37,0xa3,0xd4,0x34,0xa1,0xed,0x36,0xa3,0x15,0x37,0xa3,0xd6,0x34,0xa1,0x01 +,0x37,0xa3,0xce,0x34,0xa1,0xf7,0x36,0xa3,0x17,0x37,0xa3,0xdc,0x34,0xa1,0xf9,0x36 +,0xa3,0x19,0x37,0xa3,0xde,0x34,0xf7,0x06,0x9b,0x36,0x02,0x00,0x75,0x0c,0x33,0xc0 +,0xa0,0x9e,0x36,0x8b,0xf0,0x2e,0xff,0xa4,0x50,0x39,0xe9,0x0f,0x01,0xbe,0x07,0x00 +,0xe9,0x19,0xf1,0xf6,0x06,0x9d,0x36,0x80,0x74,0xf3,0xc6,0x06,0xa0,0x36,0x02,0xc6 +,0x06,0x6e,0x37,0x08,0xc6,0x06,0x70,0x37,0x02,0xb8,0x88,0x03,0xcd,0x39,0xf6,0x06 +,0x6f,0x37,0x01,0x75,0x4a,0xa1,0xd1,0x36,0x3a,0x06,0xe9,0x36,0x75,0x41,0x3a,0x26 +,0xea,0x36,0x75,0x3b,0xa1,0xd3,0x36,0x3a,0x06,0xeb,0x36,0x75,0x32,0x3a,0x26,0xec +,0x36,0x75,0x2c,0xa1,0xd5,0x36,0x3a,0x06,0xed,0x36,0x75,0x23,0x3a,0x26,0xee,0x36 +,0x75,0x1d,0xc6,0x06,0x70,0x37,0x02,0xfe,0x0e,0x6e,0x37,0x75,0x0f,0xb8,0x88,0x03 +,0xcd,0x3a,0x83,0x0e,0x9b,0x36,0x12,0xc6,0x06,0xa0,0x36,0x0c,0xe9,0xa8,0xf0,0xa1 +,0x05,0x37,0x26,0x3b,0x06,0x20,0x00,0x75,0x40,0xa1,0x07,0x37,0x26,0x3b,0x06,0x22 +,0x00,0x75,0x36,0xa1,0x09,0x37,0x26,0x3b,0x06,0x24,0x00,0x75,0x2c,0xa0,0x9e,0x36 +,0x3c,0x02,0x75,0x08,0x26,0xf6,0x06,0x18,0x00,0x08,0x75,0x47,0xc6,0x06,0x6e,0x37 +,0x08,0xfe,0x0e,0x70,0x37,0x75,0x1c,0xc6,0x06,0x70,0x37,0x02,0xe5,0x02,0x0d,0x01 +,0x04,0x25,0xef,0xff,0xe7,0x02,0xe9,0x5e,0xf0,0xc6,0x06,0x70,0x37,0x02,0xc6,0x06 +,0x6e,0x37,0x08,0xe5,0x02,0x25,0xff,0xfb,0x0d,0x01,0x00,0x25,0xef,0xff,0xe7,0x02 +,0xe9,0x44,0xf0,0xf7,0x06,0x9b,0x36,0x00,0x01,0x74,0x25,0x26,0xf6,0x06,0x18,0x00 +,0x08,0x75,0xed,0x81,0x26,0x9b,0x36,0x7f,0xff,0xb8,0x89,0x03,0xcd,0x3a,0xb8,0x84 +,0x03,0xcd,0x3a,0xc6,0x06,0xa0,0x36,0x06,0x83,0x26,0xc2,0x34,0xaf,0xe9,0x17,0xf0 +,0xa1,0x01,0x37,0x3a,0x26,0x0f,0x37,0x7f,0xc7,0xe9,0xf7,0xfe,0x83,0x26,0x9b,0x36 +,0xec,0xe8,0x2a,0x0d,0x81,0x0e,0x9b,0x36,0x80,0x00,0xbb,0xff,0x7f,0xcd,0x53,0xc6 +,0x06,0xa0,0x36,0x02,0xe9,0xf0,0xef,0x83,0x0e,0x9b,0x36,0x11,0xc6,0x06,0xa0,0x36 +,0x0c,0xe9,0xf9,0xef,0x44,0x3b,0x2c,0x3b,0xc7,0x2a,0x6b,0x3b,0x44,0x3b,0xc7,0x2a +,0xc7,0x2a,0xc7,0x2a,0xa3,0xb6,0x34,0x81,0x0e,0xc2,0x34,0x00,0x20,0xf7,0x06,0x41 +,0x37,0x01,0x00,0x74,0x1b,0x8c,0xc3,0xc7,0x06,0x41,0x37,0x00,0x00,0xb8,0x7f,0x03 +,0xcd,0x3a,0x33,0xc0,0x8e,0xc0,0xbf,0x54,0x37,0xb9,0x06,0x00,0xf3,0xab,0x8e,0xc3 +,0x33,0xc0,0xa0,0x9e,0x36,0x8b,0xf0,0x2e,0xff,0xa4,0xe4,0x3a,0xf7,0x06,0x9b,0x36 +,0x00,0x01,0x75,0x21,0x83,0x26,0xc2,0x34,0xbf,0xa1,0xa9,0x36,0xe7,0x00,0xa1,0x9b +,0x36,0xe9,0x09,0x00,0xa1,0x9b,0x36,0x81,0x26,0x9b,0x36,0xff,0xdf,0xa9,0x00,0x20 +,0x75,0x06,0xe9,0x6e,0x00,0xe9,0x6f,0xef,0x83,0x0e,0x99,0x36,0x04,0xc7,0x06,0x37 +,0x37,0x01,0x00,0xc6,0x06,0xca,0x34,0x01,0xe9,0x58,0x00,0x83,0x0e,0x9b,0x36,0x40 +,0xe8,0x58,0x00,0xa1,0x05,0x37,0x3b,0x06,0xe9,0x36,0x75,0x37,0xa1,0x07,0x37,0x3b +,0x06,0xeb,0x36,0x75,0x2e,0xa1,0x09,0x37,0x3b,0x06,0xed,0x36,0x75,0x25,0xfe,0x0e +,0x71,0x37,0x75,0x1c,0xb8,0x87,0x03,0xcd,0x3a,0x83,0x0e,0x99,0x36,0x10,0xa1,0x50 +,0x37,0xc7,0x06,0x50,0x37,0x00,0x00,0x09,0x06,0x99,0x36,0xc6,0x06,0xa0,0x36,0x08 +,0xe9,0x14,0xef,0x83,0x0e,0x99,0x36,0x04,0xc7,0x06,0x37,0x37,0x03,0x00,0xc6,0x06 +,0xca,0x34,0x03,0xc6,0x06,0xa0,0x36,0x0a,0xe9,0xfc,0xee,0xa1,0xd1,0x36,0x26,0x3b +,0x06,0x20,0x00,0x75,0x15,0xa1,0xd3,0x36,0x26,0x3b,0x06,0x22,0x00,0x75,0x12,0xa1 +,0xd5,0x36,0x26,0x3b,0x06,0x24,0x00,0x75,0x0f,0xc3,0x8d,0x36,0x20,0x00,0xe9,0x0b +,0x00,0x8d,0x36,0x22,0x00,0xe9,0x04,0x00,0x8d,0x36,0x24,0x00,0x83,0xc4,0x02,0xf7 +,0x06,0xe6,0x34,0x01,0x00,0x74,0x15,0x26,0x3a,0x04,0x77,0x08,0x72,0x0e,0x26,0x3a +,0x64,0x01,0x72,0x08,0xc6,0x06,0xa0,0x36,0x06,0xe9,0xab,0xee,0xe8,0x7c,0x0a,0x8c +,0xc0,0x3d,0xff,0xff,0x74,0x1b,0x26,0xc6,0x06,0x18,0x00,0x10,0x26,0xc7,0x06,0x04 +,0x00,0x49,0x3c,0x26,0xc7,0x06,0x06,0x00,0x0c,0x00,0xcd,0x50,0xb9,0x4e,0x00,0xe2 +,0xfe,0xc6,0x06,0xa0,0x36,0x0a,0xe9,0x94,0xee,0xe9,0x7b,0xee,0x8f,0x3c,0x06,0x3d +,0x06,0x3d,0x06,0x3d,0xd2,0x3c,0xea,0x3c,0x06,0x3d,0x06,0x3d,0xa3,0xb6,0x34,0x81 +,0x26,0xc2,0x34,0xaf,0xdf,0xc7,0x06,0x4c,0x37,0x00,0x00,0xb8,0x8a,0x03,0xcd,0x3a +,0x80,0x3e,0x9d,0x36,0x04,0x75,0x0c,0x80,0x3e,0x9e,0x36,0x06,0x74,0x05,0xc6,0x06 +,0x9f,0x36,0x06,0x33,0xc0,0xa0,0x9e,0x36,0x8b,0xf0,0x2e,0xff,0xa4,0x4c,0x3c,0xf7 +,0x06,0x9b,0x36,0x00,0x20,0x75,0x0e,0x81,0x26,0x9b,0x36,0xff,0xbf,0xb8,0x8b,0x03 +,0xcd,0x3a,0xe9,0x54,0x00,0xf7,0x06,0x9b,0x36,0x00,0x01,0x74,0x03,0xe9,0x17,0xee +,0xc7,0x06,0x37,0x37,0x02,0x00,0xc6,0x06,0xca,0x34,0x02,0x83,0x0e,0x99,0x36,0x04 +,0x83,0x0e,0x50,0x37,0x04,0xf6,0x06,0x9d,0x36,0x80,0x75,0x2a,0xe8,0x1f,0x0b,0xe9 +,0x27,0x00,0xf7,0x06,0x9b,0x36,0x00,0x01,0x75,0xd3,0xc7,0x06,0x37,0x37,0x02,0x00 +,0xc6,0x06,0xca,0x34,0x02,0x83,0x0e,0x99,0x36,0x04,0xc6,0x06,0xa0,0x36,0x00,0xf6 +,0x06,0x9d,0x36,0x80,0x74,0x03,0xe8,0xde,0x0a,0x81,0x26,0x9b,0x36,0x7c,0xff,0xbb +,0xff,0xff,0xcd,0x53,0xcd,0x54,0xe9,0xbe,0xed,0xa3,0xb6,0x34,0xe8,0xad,0x01,0xb8 +,0x86,0x03,0xcd,0x39,0xc7,0x06,0x4c,0x37,0x00,0x00,0x81,0x26,0xc2,0x34,0xaf,0xdf +,0xf6,0x06,0x9d,0x36,0x80,0x74,0x34,0xf7,0x06,0x9b,0x36,0x00,0x20,0x74,0x56,0xf7 +,0x06,0x9b,0x36,0x00,0x01,0x74,0x27,0xe8,0x35,0x01,0x72,0x1c,0xbe,0x00,0x40,0x85 +,0x36,0xc2,0x34,0x75,0x08,0x09,0x36,0xc2,0x34,0xff,0x06,0x92,0x34,0xe8,0x8b,0x01 +,0x73,0x06,0x81,0x0e,0x99,0x36,0x80,0x00,0xe9,0x6c,0xed,0xe9,0xb5,0x00,0xc7,0x06 +,0x37,0x37,0x02,0x00,0xc6,0x06,0xca,0x34,0x02,0x83,0x0e,0x99,0x36,0x04,0x83,0x0e +,0x50,0x37,0x04,0x80,0x3e,0x9e,0x36,0x08,0x74,0x03,0xe8,0x5a,0x0a,0xe8,0xef,0x00 +,0x72,0xd6,0xe9,0xc8,0xff,0x80,0x3e,0x9e,0x36,0x0a,0x75,0x12,0xc6,0x06,0xa0,0x36 +,0x00,0xf7,0x06,0x9b,0x36,0x08,0x00,0x74,0x02,0xcd,0x54,0xe8,0x39,0x0a,0x81,0x26 +,0x9b,0x36,0xff,0xbf,0xe8,0xc8,0x00,0x72,0xaf,0xb8,0x8b,0x03,0xcd,0x39,0xe9,0x9c +,0xff,0xf6,0x06,0x9e,0x36,0xff,0x75,0x58,0xa3,0xb6,0x34,0xe8,0xfe,0x00,0x81,0x26 +,0xc2,0x34,0xff,0xbf,0xf6,0x06,0x9d,0x36,0x80,0x74,0x48,0xf7,0x06,0x9b,0x36,0x00 +,0x20,0x74,0x22,0xf7,0x06,0x9b,0x36,0x00,0x40,0x75,0x08,0xe8,0x91,0x00,0x72,0x30 +,0xe9,0x22,0x00,0x26,0xa1,0x0c,0x00,0xa9,0x60,0x00,0x75,0x24,0x81,0x0e,0x66,0x37 +,0x00,0x08,0xe9,0xd2,0xec,0xc7,0x06,0x4c,0x37,0x00,0x00,0xe8,0x71,0x00,0x72,0x10 +,0xb8,0x8b,0x03,0xcd,0x39,0xe8,0xd3,0x00,0x73,0x06,0x81,0x0e,0x99,0x36,0x80,0x00 +,0xe9,0xb4,0xec,0x80,0x3e,0x9d,0x36,0x04,0x75,0x0c,0x80,0x3e,0x9e,0x36,0x06,0x74 +,0x46,0xc6,0x06,0x9f,0x36,0x06,0xf7,0x06,0x9b,0x36,0x00,0x01,0x74,0x0c,0x80,0x3e +,0x9d,0x36,0x08,0x75,0x05,0xc6,0x06,0x9f,0x36,0x0a,0xe8,0x32,0x00,0x72,0xd1,0xe8 +,0x99,0x00,0x80,0x3e,0x9d,0x36,0x08,0x75,0x13,0x81,0x0e,0x99,0x36,0x80,0x00,0xf7 +,0x06,0x9b,0x36,0x00,0x20,0x75,0x08,0xb8,0x8b,0x03,0xcd,0x39,0xe9,0x68,0xec,0xc6 +,0x06,0x9f,0x36,0x0a,0xe9,0x60,0xec,0xb8,0x86,0x03,0xcd,0x3a,0xe9,0x58,0xec,0x26 +,0xa1,0x0c,0x00,0xa9,0x60,0x00,0x74,0x08,0x81,0x26,0xc2,0x34,0xff,0xbf,0xf9,0xc3 +,0xf7,0x06,0x9b,0x36,0x00,0x40,0x74,0x13,0x81,0x0e,0x66,0x37,0x00,0x08,0xe8,0x4a +,0x00,0x73,0x06,0x81,0x0e,0x99,0x36,0x80,0x00,0xf9,0xc3,0x81,0x0e,0x9b,0x36,0x00 +,0x40,0x80,0x26,0x6f,0x37,0xfe,0x81,0x26,0x9b,0x36,0x7f,0xff,0xc6,0x06,0xa0,0x36 +,0x00,0xf8,0xc3,0x81,0x0e,0x99,0x36,0x00,0x01,0xe9,0x21,0xec,0x26,0xa1,0x20,0x00 +,0xa3,0xfb,0x36,0xa3,0xaa,0x34,0x26,0xa1,0x22,0x00,0xa3,0xfd,0x36,0xa3,0xac,0x34 +,0x26,0xa1,0x24,0x00,0xa3,0xff,0x36,0xa3,0xae,0x34,0xc3,0xa1,0x05,0x37,0x26,0x3b +,0x06,0x20,0x00,0x75,0x19,0xa1,0x07,0x37,0x26,0x3b,0x06,0x22,0x00,0x75,0x0f,0xa1 +,0x09,0x37,0x26,0x3b,0x06,0x24,0x00,0x75,0x05,0xe8,0x02,0x00,0xf8,0xc3,0x51,0x1e +,0x06,0x8b,0xc7,0x8d,0x36,0x20,0x00,0xbf,0x05,0x37,0xb9,0x03,0x00,0x1e,0x06,0x1f +,0x07,0xf3,0xa5,0x8b,0xf8,0x8d,0x36,0x20,0x00,0xbf,0xa0,0x34,0xb9,0x03,0x00,0xf3 +,0xa5,0x07,0x1f,0x59,0x8b,0xf8,0xa1,0x07,0x37,0xa3,0xa6,0x34,0xa1,0x09,0x37,0xa3 +,0xa8,0x34,0xf9,0xc3,0xc6,0x06,0xb6,0x34,0x01,0xe9,0x8b,0xeb,0xe8,0x87,0x08,0x8b +,0xf0,0x05,0x12,0x00,0x26,0x29,0x06,0x0e,0x00,0x26,0x8b,0x44,0x2a,0x26,0x3a,0x06 +,0x0e,0x00,0x75,0x5b,0x26,0x83,0x2e,0x0e,0x00,0x02,0x80,0xfc,0x27,0x75,0x50,0x26 +,0x8b,0x44,0x2c,0xa9,0xff,0xff,0x75,0x47,0x8b,0xfe,0x33,0xc0,0x26,0xf6,0x45,0x3c +,0x80,0x74,0x06,0x26,0x8a,0x45,0x3a,0x24,0x1f,0x03,0xf8,0x26,0x80,0x7d,0x45,0x09 +,0x75,0x2d,0x8c,0xc2,0x8e,0x06,0x38,0x34,0x8e,0xda,0x8b,0x0e,0x0e,0x00,0x26,0x89 +,0x0e,0x0e,0x00,0x8d,0x74,0x2c,0xbf,0x18,0x00,0xf3,0xa4,0x33,0xc0,0x8e,0xd8,0x26 +,0xc7,0x06,0x04,0x00,0xb5,0x3f,0x26,0xc7,0x06,0x06,0x00,0x06,0x00,0xcd,0x50,0xb8 +,0x06,0x80,0xe9,0xef,0xe9,0x26,0xa1,0x0c,0x00,0xa3,0x93,0x37,0x83,0x0e,0x99,0x36 +,0x01,0xe9,0x00,0xeb,0x26,0x80,0x3e,0x1c,0x00,0xff,0x75,0x2f,0x26,0x80,0x3e,0x1e +,0x00,0xff,0x75,0x27,0x26,0xf7,0x06,0x0c,0x00,0x40,0x00,0x75,0x1b,0xa1,0xd1,0x36 +,0x26,0xa3,0x1a,0x00,0xa1,0xd3,0x36,0x26,0xa3,0x1c,0x00,0xa1,0xd5,0x36,0x26,0xa3 +,0x1e,0x00,0xb8,0x0a,0x80,0xe8,0x36,0x07,0xe9,0xe2,0xea,0xff,0x06,0x90,0x34,0xbe +,0x0a,0x00,0xc6,0x06,0xb6,0x34,0x01,0xf6,0x06,0x9d,0x36,0x80,0x75,0x05,0x83,0x0e +,0xc2,0x34,0x01,0xe9,0xb6,0xea,0x80,0x3e,0x9d,0x36,0x0a,0x75,0x0f,0x26,0xa1,0x0c +,0x00,0x25,0x07,0x00,0x3d,0x04,0x00,0x75,0x03,0xe8,0x79,0x00,0xa1,0xf3,0x36,0x86 +,0xe0,0xe7,0x1e,0xa3,0xe3,0x36,0x81,0x26,0x0b,0x37,0x00,0x03,0x81,0x26,0x0d,0x37 +,0x7b,0x7f,0x83,0x0e,0x0d,0x37,0x48,0xe8,0x1e,0x00,0x26,0xa1,0x0c,0x00,0x25,0x07 +,0x00,0x3d,0x04,0x00,0x74,0x09,0x26,0xf7,0x06,0x0c,0x00,0x20,0x00,0x75,0x06,0xb8 +,0x01,0x00,0xe9,0x3f,0xe9,0xe9,0x5f,0xea,0xc7,0x06,0x41,0x37,0x00,0x00,0xb8,0x7f +,0x03,0xcd,0x3a,0xa1,0x1d,0x37,0xa3,0xc4,0x34,0x86,0xe0,0x68,0x7f,0x03,0x1f,0xa3 +,0x06,0x00,0x33,0xc0,0x8e,0xd8,0xa1,0x0b,0x37,0xa3,0xb2,0x34,0xa1,0x0d,0x37,0xa3 +,0xb4,0x34,0xa1,0xf3,0x36,0xa3,0xc8,0x34,0xa1,0xef,0x36,0xa3,0x9c,0x34,0xa1,0xf1 +,0x36,0xa3,0x9e,0x34,0xc3,0x80,0x0e,0x9d,0x36,0x80,0xbe,0x00,0x00,0xe8,0xb4,0x07 +,0xb8,0x7b,0x03,0xcd,0x3a,0xb8,0x7c,0x03,0xcd,0x39,0xc7,0x06,0x33,0x37,0x02,0x00 +,0xa1,0xe5,0x36,0xe7,0x2e,0xa1,0xe7,0x36,0xe7,0x3e,0xb8,0x82,0x03,0xcd,0x3a,0xf7 +,0x06,0x9b,0x36,0x00,0x20,0x75,0x03,0xe8,0xfd,0x06,0xa1,0xd3,0x36,0xa3,0xef,0x36 +,0xa3,0x9c,0x34,0xa1,0xd5,0x36,0xa3,0xf1,0x36,0xa3,0x9e,0x34,0xc3,0xf6,0x06,0x9d +,0x36,0x80,0x74,0x31,0xbe,0x22,0x00,0xe9,0x17,0x00,0xf6,0x06,0x9d,0x36,0x80,0x74 +,0x24,0xbe,0x23,0x00,0xe9,0x0a,0x00,0xf6,0x06,0x9d,0x36,0x80,0x74,0x17,0xbe,0x24 +,0x00,0x56,0xe8,0xa8,0x05,0x8c,0xc0,0x3d,0xff,0xff,0x5e,0x74,0x05,0xe8,0xd7,0xef +,0xcd,0x50,0xe9,0x1f,0xe8,0xe9,0x9f,0xe9,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0xb8,0x84,0x03,0xcd,0x3a,0xb8,0x8a,0x03,0xcd,0x39,0xe9,0xf7,0x00,0x80,0x3e,0xa0 +,0x36,0x08,0x75,0x2e,0xa9,0xd0,0x07,0x75,0x2c,0xa1,0xb1,0x36,0x0d,0x00,0x04,0xe7 +,0x08,0xe5,0x00,0x25,0xff,0x73,0xe7,0x00,0xb8,0x8a,0x03,0xcd,0x3a,0xe8,0xc3,0x06 +,0x33,0xc0,0xe7,0x0e,0xe5,0x0a,0x25,0xc3,0x17,0xe7,0x0a,0xcd,0x54,0xc6,0x06,0xa0 +,0x36,0x00,0xe9,0x68,0xe9,0xbe,0x04,0x00,0xe9,0x3f,0xe9,0x83,0x26,0x9b,0x36,0xbf +,0xc6,0x06,0x71,0x37,0x03,0xb8,0x86,0x03,0xcd,0x3a,0xb8,0x88,0x03,0xcd,0x3a,0xb8 +,0x83,0x03,0xcd,0x3a,0xb8,0x87,0x03,0xcd,0x39,0x81,0x0e,0xc2,0x34,0x00,0x20,0xe9 +,0x92,0x00,0xe8,0x49,0x06,0xb8,0x87,0x03,0xcd,0x39,0xbb,0xff,0x7f,0xcd,0x53,0xb8 +,0x84,0x03,0xcd,0x3a,0xb8,0x88,0x03,0xcd,0x3a,0xb8,0x8b,0x03,0xcd,0x3a,0xb8,0x83 +,0x03,0xcd,0x3a,0xb8,0x86,0x03,0xcd,0x3a,0xb8,0x85,0x03,0xcd,0x3a,0xc3,0xe5,0x00 +,0x25,0xff,0x53,0xe7,0x00,0x83,0x0e,0xc2,0x34,0x40,0x83,0x26,0xc2,0x34,0xef,0xe8 +,0x0c,0x06,0xbb,0xff,0x7f,0xcd,0x53,0xb8,0x8a,0x03,0xcd,0x3a,0xb8,0x85,0x03,0xcd +,0x3a,0xb8,0x86,0x03,0xcd,0x3a,0xb8,0x83,0x03,0xcd,0x3a,0xb8,0x87,0x03,0xcd,0x3a +,0xb8,0x8b,0x03,0xcd,0x3a,0xb8,0x84,0x03,0xcd,0x3a,0xb8,0x89,0x03,0xcd,0x3a,0xc3 +,0x83,0x0e,0xc2,0x34,0x50,0xe8,0x18,0x04,0xe8,0xd3,0x05,0xf6,0x06,0x6f,0x37,0x01 +,0x75,0x12,0xb8,0x89,0x03,0xcd,0x39,0x83,0x3e,0x0f,0x37,0x00,0x75,0x06,0xc7,0x06 +,0x0f,0x37,0x04,0x00,0xa1,0x9d,0x36,0x80,0xfc,0x08,0x74,0x05,0xb8,0x84,0x03,0xcd +,0x39,0xe5,0x02,0x0d,0x01,0x08,0x25,0xef,0xff,0xe7,0x02,0xa1,0x9d,0x36,0x86,0xe0 +,0x32,0xe4,0x8b,0xf0,0xd1,0xee,0x33,0xc0,0x0d,0x20,0x00,0x09,0x06,0xad,0x36,0xa1 +,0xad,0x36,0xe7,0x04,0xe9,0x53,0xe8,0xe9,0x5a,0xe8,0x33,0xc0,0xa0,0x1b,0x37,0xd1 +,0xe0,0x3a,0x06,0xa0,0x36,0x75,0x03,0xe9,0xba,0xff,0xe9,0x60,0xe8,0xc7,0x06,0x41 +,0x37,0x00,0x00,0xe8,0xc1,0xe1,0xe8,0x6a,0x06,0x33,0xc0,0x0d,0x41,0x00,0xe7,0x56 +,0xa1,0xb1,0x36,0x0d,0x00,0x10,0xe7,0x08,0xe5,0x02,0x25,0xf9,0xff,0x0d,0x03,0x00 +,0xe7,0x02,0xa1,0xb3,0x36,0xe7,0x0a,0xa1,0xaf,0x36,0xe7,0x06,0xa1,0xad,0x36,0xe7 +,0x04,0xe8,0x7c,0x03,0xe8,0x9f,0x03,0xc7,0x06,0x1d,0x37,0x00,0xc8,0xc7,0x06,0x0b +,0x37,0x00,0x03,0xc7,0x06,0x0d,0x37,0x7b,0x7f,0x33,0xc0,0xa3,0x99,0x36,0xa3,0x9b +,0x36,0xa3,0x9d,0x36,0xa3,0x9f,0x36,0xa3,0x4c,0x37,0xa3,0xf3,0x36,0xa3,0xef,0x36 +,0xa3,0xf1,0x36,0xe8,0x82,0xfd,0xc6,0x06,0x9f,0x36,0x02,0xe9,0xef,0xe7,0xe5,0x02 +,0x0d,0x01,0x88,0x25,0xef,0xff,0x0d,0x00,0x40,0x0d,0x00,0x04,0xe7,0x02,0xe8,0xf2 +,0x05,0xe5,0x0a,0x0d,0x40,0x00,0xe7,0x0a,0x33,0xc0,0xa3,0x81,0x37,0xa3,0x85,0x37 +,0xa3,0x83,0x37,0xa3,0x87,0x37,0xa3,0x89,0x37,0xe5,0x00,0x0d,0x00,0x84,0xe7,0x00 +,0xb8,0x8c,0x03,0xcd,0x39,0xb8,0x80,0x00,0xcd,0x35,0xc7,0x06,0xaa,0x02,0xff,0xff +,0xe5,0x00,0x25,0xff,0x7b,0xe7,0x00,0x81,0x0e,0x9a,0x37,0x80,0x00,0xb8,0x7e,0x03 +,0xcd,0x39,0x33,0xc0,0xe7,0x0e,0xbe,0x08,0x00,0x8e,0x06,0x38,0x34,0xe8,0xa7,0xed +,0x83,0x26,0xef,0x34,0xdf,0xff,0x06,0x81,0x37,0xcd,0x50,0x83,0x0e,0xef,0x34,0x20 +,0xc3,0xf7,0x06,0x9a,0x37,0x80,0x00,0x74,0x3d,0xa9,0xd0,0x07,0x74,0x10,0xa9,0x00 +,0x04,0x74,0x12,0x33,0xc0,0xe7,0x0e,0xff,0x06,0x87,0x37,0xe9,0xd2,0xff,0xff,0x06 +,0x85,0x37,0xe9,0xcb,0xff,0xff,0x06,0x83,0x37,0xe9,0xc4,0xff,0x83,0x26,0x9a,0x37 +,0x7f,0xa1,0x89,0x37,0x03,0x06,0x87,0x37,0x3d,0x05,0x00,0x7f,0x01,0xc3,0xbb,0xff +,0x7f,0xcd,0x53,0xe9,0x00,0x00,0xe5,0x02,0x25,0xff,0xfb,0x25,0xef,0xff,0x0d,0x01 +,0x00,0xe7,0x02,0xa1,0x83,0x37,0x3b,0x06,0x46,0x37,0x7f,0x2a,0xa1,0x85,0x37,0x3b +,0x06,0x48,0x37,0x7c,0x21,0xa1,0x89,0x37,0x03,0x06,0x87,0x37,0x3d,0x05,0x00,0x7f +,0x15,0xc6,0x06,0x9f,0x36,0x04,0xe5,0x02,0x25,0xff,0xf7,0x0d,0x01,0x00,0x25,0xef +,0xff,0xe7,0x02,0xe9,0xf7,0xe6,0xbe,0x01,0x00,0xf7,0x06,0x9b,0x36,0x03,0x00,0x74 +,0x0a,0x83,0x26,0x9b,0x36,0xfc,0x83,0x0e,0xc2,0x34,0x04,0xe9,0xd0,0xe6,0xb8,0x7b +,0x03,0xcd,0x39,0xe5,0x02,0x0d,0x01,0x60,0x25,0xef,0xff,0xe7,0x02,0xc7,0x06,0xf1 +,0x34,0x20,0x03,0xb8,0x8e,0x03,0xcd,0x39,0xc3,0x81,0x26,0xc2,0x34,0x7f,0xff,0x80 +,0x0e,0x6f,0x37,0x01,0xf7,0x06,0x9b,0x36,0x03,0x00,0x74,0xd2,0xb8,0x7b,0x03,0xcd +,0x3a,0xb8,0x7d,0x03,0xcd,0x39,0x83,0x26,0x9b,0x36,0xef,0x33,0xc0,0xb0,0x8a,0xa2 +,0x9f,0x36,0xa2,0x9d,0x36,0xc7,0x06,0x4c,0x37,0x01,0x00,0xc7,0x06,0x0f,0x37,0x04 +,0x00,0xf7,0x06,0x9b,0x36,0x40,0x00,0x75,0x06,0xc7,0x06,0x0f,0x37,0x03,0x00,0xb8 +,0x8d,0x03,0xcd,0x39,0xe8,0x00,0xd5,0xe5,0x02,0x0d,0x01,0x40,0x25,0xef,0xff,0x8b +,0xd8,0xb8,0x7c,0x03,0xcd,0x39,0xc7,0x06,0x33,0x37,0x02,0x00,0x8b,0xc3,0x0d,0x00 +,0x20,0x25,0xf9,0xff,0x0b,0x06,0xe8,0x3a,0xe7,0x02,0xc3,0xff,0x0e,0xf1,0x34,0x75 +,0x01,0xc3,0xe5,0x4e,0xa9,0x01,0x00,0x75,0x12,0xe5,0x00,0xa9,0x00,0x04,0x75,0x05 +,0x0d,0x00,0x04,0xe7,0x00,0xb8,0x8e,0x03,0xcd,0x39,0xc3,0xe5,0x00,0xa9,0x00,0x04 +,0x74,0xf3,0x25,0xff,0xfb,0xe7,0x00,0xe9,0xeb,0xff,0xc6,0x06,0xa0,0x36,0x04,0x83 +,0x26,0x9b,0x36,0xfc,0x81,0x0e,0x9b,0x36,0x80,0x00,0xe9,0x10,0xe6,0xb8,0x8e,0x03 +,0xcd,0x3a,0xcd,0x54,0x81,0x0e,0xaf,0x36,0x00,0x18,0xa1,0xaf,0x36,0xe7,0x06,0xb8 +,0x7b,0x03,0xcd,0x39,0xa1,0xd3,0x36,0xa3,0x8f,0x37,0xa1,0xd5,0x36,0xa3,0x91,0x37 +,0xc7,0x06,0x8b,0x37,0x02,0x00,0xc7,0x06,0x8d,0x37,0x02,0x00,0x83,0x0e,0x99,0x36 +,0x40,0xe9,0xd9,0xe5,0x80,0x3e,0x9f,0x36,0x06,0x75,0x15,0xa9,0xd0,0x07,0x75,0xec +,0x25,0x00,0x18,0x75,0x0e,0xff,0x0e,0x8b,0x37,0x75,0xe1,0xc6,0x06,0x9f,0x36,0x08 +,0xe9,0xba,0xe5,0xff,0x0e,0x8d,0x37,0x75,0xd3,0xbe,0x08,0x00,0xe9,0x9f,0xe5,0xb8 +,0x7b,0x03,0xcd,0x39,0xf7,0x06,0x9b,0x36,0x00,0x20,0x74,0x08,0xc6,0x06,0x9f,0x36 +,0x0a,0xe9,0x0d,0x00,0xf7,0x06,0x9b,0x36,0x00,0x40,0x74,0x0b,0xb8,0x8b,0x03,0xcd +,0x39,0x81,0x0e,0x99,0x36,0x80,0x00,0xe9,0x83,0xe5,0xb8,0x7b,0x03,0xcd,0x39,0xc7 +,0x06,0x8b,0x37,0x04,0x00,0xc7,0x06,0x8d,0x37,0x04,0x00,0x81,0x0e,0x99,0x36,0x00 +,0x02,0xe9,0x69,0xe5,0xf6,0x06,0x9d,0x36,0x80,0x75,0x1b,0xa9,0xd0,0x07,0x75,0xeb +,0xa9,0x00,0x18,0x75,0x0c,0xff,0x0e,0x8d,0x37,0x75,0xe0,0xe8,0x17,0xfb,0xe9,0x4c +,0xe5,0xb8,0x82,0x03,0xcd,0x39,0xc3,0xff,0x0e,0x8b,0x37,0x75,0xce,0xbe,0x09,0x00 +,0xe9,0x2b,0xe5,0xc7,0x06,0x3d,0x37,0x00,0x00,0xc7,0x06,0x9b,0x36,0x00,0x00,0xe8 +,0x3c,0x02,0x81,0x26,0xaf,0x36,0xff,0xe7,0xa1,0xaf,0x36,0xe7,0x06,0x81,0x26,0x9b +,0x36,0xff,0x7f,0xe5,0x02,0x0d,0x01,0x00,0x25,0xef,0xff,0x25,0xff,0xdf,0xe7,0x02 +,0xbb,0xff,0x7f,0xcd,0x53,0x33,0xc0,0xa3,0x9d,0x36,0xa3,0x9f,0x36,0xe8,0x50,0x00 +,0xe8,0x73,0x00,0xb8,0x81,0x03,0xcd,0x39,0xc3,0xf7,0x06,0x9b,0x36,0x03,0x00,0x74 +,0x0d,0xc6,0x06,0x9f,0x36,0x02,0xc6,0x06,0xa0,0x36,0x00,0xe9,0xdf,0xe4,0x83,0x0e +,0x9b,0x36,0x10,0xc7,0x06,0x99,0x36,0x00,0x00,0xe8,0xe7,0x02,0xe5,0x56,0x0d,0x02 +,0x00,0xe7,0x56,0xc7,0x06,0xa8,0x02,0x00,0x00,0x8b,0x36,0x3d,0x37,0xe8,0x44,0x02 +,0xc6,0x06,0xa0,0x36,0x0e,0xe9,0xb5,0xe4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x06,0xb8,0x8a,0x03,0xcd,0x3a,0xb8,0x85,0x03,0xcd,0x3a,0xb8,0x86,0x03,0xcd,0x3a +,0xb8,0x83,0x03,0xcd,0x3a,0xb8,0x87,0x03,0xcd,0x3a,0xb8,0x8b,0x03,0xcd,0x3a,0xb8 +,0x88,0x03,0xcd,0x3a,0x07,0xc3,0x06,0xb8,0x88,0x03,0xcd,0x3a,0xb8,0x7b,0x03,0xcd +,0x3a,0xb8,0x82,0x03,0xcd,0x3a,0xb8,0x7f,0x03,0xcd,0x3a,0xb8,0x7c,0x03,0xcd,0x3a +,0xb8,0x7e,0x03,0xcd,0x3a,0xb8,0x80,0x03,0xcd,0x3a,0xb8,0x81,0x03,0xcd,0x3a,0xb8 +,0x84,0x03,0xcd,0x3a,0xb8,0x89,0x03,0xcd,0x3a,0xb8,0x7d,0x03,0xcd,0x3a,0xb8,0x8d +,0x03,0xcd,0x3a,0xc7,0x06,0x41,0x37,0x00,0x00,0x07,0xc3,0x06,0x8e,0x06,0x38,0x34 +,0x1f,0x8b,0x0e,0x0e,0x00,0x26,0x89,0x0e,0x0e,0x00,0xbe,0x18,0x00,0xbf,0x18,0x00 +,0xf3,0xa4,0x06,0x1e,0x07,0xcd,0x34,0x07,0x33,0xc0,0x8e,0xd8,0xc3,0x26,0xf6,0x06 +,0x20,0x00,0x80,0x74,0x44,0x33,0xc0,0x26,0xa0,0x26,0x00,0x24,0x1f,0x8b,0xf0,0x26 +,0x8b,0x5c,0x28,0x89,0x1e,0x6a,0x37,0x06,0x8e,0x06,0x38,0x34,0x1f,0xc0,0xe3,0x04 +,0x26,0x88,0x5c,0x28,0x8b,0xc6,0xb9,0x06,0x00,0xbe,0x20,0x00,0xbf,0x1a,0x00,0xf3 +,0xa4,0x8b,0xc8,0x83,0xc7,0x06,0xf3,0xa4,0x26,0x81,0x26,0x26,0x00,0x1f,0x80,0x26 +,0x81,0x36,0x26,0x00,0x00,0x80,0xe9,0xa9,0xff,0x26,0x8b,0x1e,0x28,0x00,0x89,0x1e +,0x6a,0x37,0x06,0x8e,0x06,0x38,0x34,0x1f,0xc0,0xe3,0x04,0x26,0x88,0x1e,0x28,0x00 +,0xb9,0x06,0x00,0xbe,0x20,0x00,0xbf,0x1a,0x00,0xf3,0xa4,0xe9,0x84,0xff,0x86,0xc4 +,0xa3,0x68,0x37,0xe8,0x87,0xff,0xf7,0x06,0x6a,0x37,0x0f,0x00,0x74,0x10,0x80,0x3e +,0x9e,0x36,0x00,0x75,0x09,0xbe,0x00,0x00,0xe8,0xac,0xe9,0xcd,0x50,0xc3,0xc3,0x50 +,0x56,0x06,0x33,0xc0,0x26,0xf6,0x06,0x20,0x00,0x80,0x74,0x06,0x26,0xa0,0x26,0x00 +,0x24,0x1f,0x8b,0xf0,0x26,0x8b,0x5c,0x26,0x86,0xfb,0x83,0xeb,0x04,0x74,0x4f,0x83 +,0xc6,0x2a,0x8c,0xc0,0x8e,0xd8,0xb9,0x07,0x00,0x33,0xc0,0x8e,0xc0,0xbf,0x72,0x37 +,0xf3,0xab,0x33,0xc9,0x8a,0x0c,0x80,0xf9,0x00,0x75,0x03,0xe9,0x30,0x00,0x3b,0xd9 +,0x73,0x03,0xe9,0x29,0x00,0x2b,0xd9,0x8a,0x44,0x01,0x25,0x3f,0x00,0x74,0x19,0x3d +,0x0b,0x00,0x7d,0x14,0xd1,0xe0,0x8b,0xf8,0x2e,0x8b,0xbd,0x5c,0x49,0x8d,0x74,0x02 +,0x83,0xe9,0x02,0xf3,0xa4,0xe9,0x02,0x00,0x03,0xf1,0x23,0xdb,0x75,0xc4,0x33,0xc0 +,0x8e,0xd8,0x07,0x5e,0x58,0xc3,0x33,0xc0,0x26,0xf6,0x06,0x20,0x00,0x80,0x74,0x06 +,0x26,0xa0,0x26,0x00,0x24,0x1f,0xc3,0xe5,0x0a,0x25,0xc3,0xbf,0xe7,0x0a,0xb8,0x86 +,0x03,0xcd,0x39,0xb8,0x83,0x03,0xcd,0x39,0x81,0x26,0x9b,0x36,0x7c,0xdf,0xb8,0x85 +,0x03,0xcd,0x3a,0xe5,0x02,0x25,0xff,0xf3,0x0d,0x01,0x00,0x25,0xef,0xff,0xe7,0x02 +,0xe5,0x00,0x25,0xff,0x53,0xe7,0x00,0xa1,0xe7,0x36,0x25,0xff,0xfe,0xa3,0xe7,0x36 +,0xe7,0x3e,0x83,0x26,0x99,0x36,0xcf,0x81,0x0e,0xaf,0x36,0x00,0x10,0xa1,0xaf,0x36 +,0xe7,0x06,0xc3,0xe5,0x02,0x0d,0x01,0x0c,0x25,0xef,0xff,0xe7,0x02,0xa1,0xe7,0x36 +,0x0d,0x00,0x01,0xe7,0x3e,0xa3,0xe7,0x36,0x81,0x0e,0x9b,0x36,0x00,0x20,0x83,0x0e +,0x99,0x36,0x20,0x81,0x26,0x9b,0x36,0x7c,0xbf,0x81,0x0e,0xaf,0x36,0x00,0x10,0xa1 +,0xaf,0x36,0xe7,0x06,0xb8,0x86,0x03,0xcd,0x39,0xb8,0x85,0x03,0xcd,0x39,0xb8,0x83 +,0x03,0xcd,0x3a,0xc3,0x0b,0xf6,0x75,0x49,0x06,0x8e,0x06,0x32,0x34,0x80,0x3e,0xe0 +,0x34,0x01,0x75,0x1b,0x26,0x89,0x36,0x06,0x00,0x8e,0x06,0x32,0x34,0x26,0xf7,0x06 +,0x0a,0x00,0x00,0x20,0x74,0x07,0x26,0x81,0x0e,0x08,0x00,0x00,0x20,0x07,0xc3,0x80 +,0x3e,0xe3,0x34,0x01,0x75,0x19,0x26,0x89,0x36,0x06,0x00,0x8e,0x06,0x32,0x34,0x26 +,0xf7,0x06,0x0a,0x00,0x00,0x10,0x74,0x07,0x26,0x81,0x0e,0x08,0x00,0x00,0x10,0x07 +,0xc3,0xe9,0xb4,0xff,0x50,0x51,0x57,0x33,0xc0,0xb9,0x06,0x00,0x8e,0xc0,0xbf,0xd1 +,0x36,0xf3,0xae,0x5f,0x74,0x0c,0x26,0xf6,0x06,0x00,0x00,0xc0,0x75,0x04,0xf8,0x59 +,0x58,0xc3,0xf9,0xe9,0xf9,0xff,0x8b,0x05,0x0b,0x45,0x02,0x0b,0x45,0x04,0xc3,0x52 +,0x50,0xe5,0x06,0x25,0x1e,0x00,0x3d,0x1e,0x00,0x75,0xf6,0xb8,0x01,0x80,0xe7,0x5a +,0x58,0x5a,0xc3,0xe8,0xe9,0xff,0x50,0xe5,0x02,0x25,0xff,0x7f,0x0d,0x01,0x00,0x25 +,0xef,0xff,0xe7,0x02,0x0d,0x00,0x80,0xe7,0x02,0xa1,0xad,0x36,0xe7,0x04,0xa1,0xaf +,0x36,0xe7,0x06,0x58,0xc3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x2e,0x2b,0xce,0x41,0x10,0x42,0x7b,0x41,0x30,0x41,0xa2,0x41,0xaf,0x45,0x44,0x29 +,0xc7,0x2a,0xc7,0x2a,0x60,0x39,0xf4,0x3a,0x5c,0x3c,0x09,0x3d,0xb1,0x3d,0x34,0x3f +,0xc7,0x2a,0x3c,0x3f,0xc7,0x2a,0xc4,0x3f,0x16,0x40,0x16,0x40,0xed,0x40,0xfa,0x40 +,0x07,0x41,0xc7,0x2a,0xc7,0x2a,0xc7,0x2a,0xc7,0x2a,0xd6,0x52,0x00,0x00,0x01,0x37 +,0xe9,0x36,0xf3,0x36,0xef,0x36,0x1d,0x37,0x0d,0x37,0x0b,0x37,0x9c,0x37,0x03,0x37 +,0xfb,0x36,0x62,0x2d,0x40,0x06,0xd1,0x2d,0xf4,0x01,0xba,0x44,0x40,0x06,0x8c,0x43 +,0x64,0x00,0xe8,0x2c,0xc8,0x00,0xd8,0x2b,0x05,0x00,0xe9,0x45,0x50,0x00,0x97,0x45 +,0xfa,0x00,0xae,0x2d,0x04,0x01,0x6a,0x42,0x02,0x00,0xf6,0x2c,0xbc,0x02,0x93,0x2d +,0xdc,0x05,0x1d,0x2d,0x64,0x00,0xa1,0x2d,0x14,0x00,0xd7,0x3a,0x08,0x07,0x81,0x2d +,0x64,0x00,0xb3,0x3e,0x02,0x00,0x30,0x43,0x64,0x00,0xc5,0x2c,0xf4,0x01,0x8b,0x44 +,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x80,0x3e,0xfd,0x34,0x02,0x74,0x0c,0xe8,0x20,0x05,0xc7,0x06,0xa1,0x36,0x00,0x00 +,0xe9,0x9a,0xf8,0xff,0x06,0xc0,0x33,0xe8,0x10,0x05,0x8b,0x36,0x3d,0x37,0xe8,0x73 +,0xfe,0xc3,0xcd,0x34,0xe9,0xe8,0x05,0xc7,0x06,0xa3,0x36,0x00,0x00,0xc7,0x06,0x41 +,0x37,0x00,0x00,0xe8,0xed,0xfe,0x33,0xc0,0x0d,0x41,0x00,0xe7,0x56,0xa1,0xb1,0x36 +,0x0d,0x00,0x10,0xe7,0x08,0xa1,0xb3,0x36,0xe7,0x0a,0xa1,0xaf,0x36,0xe7,0x06,0xa1 +,0xad,0x36,0xe7,0x04,0xe8,0x2b,0x09,0xc7,0x06,0x1d,0x37,0x00,0xc8,0xc7,0x06,0x0b +,0x37,0x00,0x03,0xc7,0x06,0x0d,0x37,0x7b,0x7f,0x33,0xc0,0xa3,0x9b,0x36,0xa3,0x9d +,0x36,0xc7,0x06,0x4c,0x37,0x01,0x00,0xc6,0x06,0x9e,0x36,0xff,0xc7,0x06,0x05,0x37 +,0x00,0x00,0xc7,0x06,0x07,0x37,0x00,0x00,0xc7,0x06,0x09,0x37,0x00,0x00,0xa3,0xf3 +,0x36,0xa3,0xef,0x36,0xa3,0xf1,0x36,0xe8,0xfe,0xf5,0xe5,0x02,0x25,0xf9,0xff,0x0d +,0x03,0x00,0x0d,0x00,0x88,0x25,0xef,0xff,0x0d,0x00,0x40,0x0d,0x00,0x04,0xe7,0x02 +,0xb8,0x8f,0x03,0xcd,0x39,0xb8,0x80,0x00,0xcd,0x35,0xc7,0x06,0xaa,0x02,0xff,0xff +,0xa1,0xa9,0x36,0xa3,0xa7,0x36,0x0d,0x00,0xa4,0x0d,0x00,0x08,0xe7,0x00,0xa3,0xa9 +,0x36,0xc7,0x06,0xa3,0x36,0x01,0x00,0xc7,0x06,0xa5,0x36,0x0c,0x00,0x83,0x3e,0xa5 +,0x36,0x00,0x75,0x09,0xc7,0x06,0x3d,0x37,0x05,0x00,0xe9,0x13,0xff,0xff,0x0e,0xa5 +,0x36,0xbe,0x11,0x00,0xe8,0x22,0x05,0xb8,0x90,0x03,0xcd,0x39,0xc3,0x83,0x3e,0xa3 +,0x36,0x01,0x74,0xd9,0xc3,0xb8,0x90,0x03,0xcd,0x3a,0x26,0xa0,0x2b,0x00,0x26,0x8b +,0x1e,0x2c,0x00,0xcd,0x34,0x83,0x3e,0xa3,0x36,0x01,0x74,0x03,0xe9,0xf0,0x04,0x3c +,0x0f,0x75,0x1e,0x81,0xfb,0x00,0x02,0x75,0x18,0x26,0xa1,0x20,0x00,0xa3,0x05,0x37 +,0x26,0xa1,0x22,0x00,0xa3,0x07,0x37,0x26,0xa1,0x24,0x00,0xa3,0x09,0x37,0xe9,0x09 +,0x00,0xc7,0x06,0x3d,0x37,0x01,0x00,0xe9,0xb6,0xfe,0xc7,0x06,0xa3,0x36,0x02,0x00 +,0xc6,0x06,0x9e,0x36,0xff,0xe8,0xcb,0xfd,0xe8,0x1c,0xd9,0x33,0xc0,0xa3,0x85,0x37 +,0xa3,0x83,0x37,0xa3,0x87,0x37,0xa3,0x89,0x37,0xb8,0x91,0x03,0xcd,0x39,0xb8,0x80 +,0x00,0xcd,0x35,0xc7,0x06,0xaa,0x02,0xff,0xff,0xe5,0x00,0x25,0xff,0x53,0xe7,0x00 +,0x81,0x0e,0x9a,0x37,0x80,0x00,0xb8,0x92,0x03,0xcd,0x39,0x33,0xc0,0xe7,0x0e,0xbe +,0x08,0x00,0x8e,0x06,0x38,0x34,0xe8,0x8e,0xe5,0x26,0xc7,0x06,0x04,0x00,0x7d,0x4b +,0x83,0x26,0xef,0x34,0xdf,0xcd,0x50,0x83,0x0e,0xef,0x34,0x20,0xc3,0xf7,0x06,0x9a +,0x37,0x80,0x00,0x74,0x32,0xa9,0xd0,0x07,0x74,0x0c,0xa9,0x00,0x04,0x74,0x0e,0x33 +,0xc0,0xe7,0x0e,0xe9,0xda,0xff,0xff,0x06,0x85,0x37,0xe9,0xd3,0xff,0xff,0x06,0x83 +,0x37,0xe9,0xcc,0xff,0xc7,0x06,0x3d,0x37,0x01,0x00,0xe9,0x36,0xfe,0x83,0x26,0x9a +,0x37,0x7f,0xbb,0xff,0x7f,0xcd,0x53,0xe5,0x00,0x0d,0x00,0xac,0xe7,0x00,0xe5,0x02 +,0x25,0xff,0xfb,0x25,0xef,0xff,0x25,0xff,0xf7,0x0d,0x01,0x00,0xe7,0x02,0xa1,0x83 +,0x37,0x3b,0x06,0x46,0x37,0x7f,0xcd,0xa1,0x85,0x37,0x3b,0x06,0x48,0x37,0x7c,0xc4 +,0xc7,0x06,0xa3,0x36,0x03,0x00,0xbe,0x13,0x00,0xe8,0xfd,0x03,0xb8,0x93,0x03,0xcd +,0x39,0xb8,0x94,0x03,0xcd,0x39,0xb8,0x96,0x03,0xcd,0x39,0xb8,0x95,0x03,0xcd,0x39 +,0xbe,0x06,0x00,0xe8,0xe3,0x03,0xe9,0xd6,0x03,0x83,0x3e,0xa3,0x36,0x03,0x74,0x01 +,0xc3,0xbe,0x13,0x00,0xe8,0xd2,0x03,0xb8,0x94,0x03,0xcd,0x39,0xc3,0xb8,0x94,0x03 +,0xcd,0x3a,0x26,0xa0,0x2b,0x00,0x26,0x8b,0x1e,0x2c,0x00,0xcd,0x34,0x83,0x3e,0xa3 +,0x36,0x03,0x74,0x03,0xe9,0xa8,0x03,0x3c,0x0d,0x75,0x3e,0x83,0xfb,0x00,0x75,0x39 +,0xe5,0x02,0x0d,0x00,0x20,0xe7,0x02,0xb8,0x93,0x03,0xcd,0x3a,0xc7,0x06,0xa3,0x36 +,0x04,0x00,0xbe,0x00,0x00,0xe8,0x0c,0xfc,0xc6,0x06,0x9d,0x36,0x80,0xc6,0x06,0x9e +,0x36,0x00,0xc7,0x06,0x33,0x37,0x02,0x00,0xb8,0x9a,0x03,0xcd,0x39,0xe8,0xfc,0x00 +,0xc7,0x06,0x4c,0x37,0x00,0x00,0xe9,0x66,0x03,0xc7,0x06,0x3d,0x37,0x08,0x00,0xe9 +,0x61,0xfd,0x83,0x3e,0xa3,0x36,0x03,0x75,0x09,0xc7,0x06,0x3d,0x37,0x05,0x00,0xe9 +,0x51,0xfd,0xe9,0x4a,0x03,0x83,0x3e,0xa3,0x36,0x04,0x74,0x12,0x83,0x3e,0xa3,0x36 +,0x05,0x74,0x0b,0xcd,0x34,0xc7,0x06,0x3d,0x37,0x07,0x00,0xe9,0x35,0xfd,0xc7,0x06 +,0xa3,0x36,0x06,0x00,0xc6,0x06,0x9e,0x36,0xff,0xb8,0x9a,0x03,0xcd,0x3a,0xb8,0x99 +,0x03,0xcd,0x3a,0xb8,0x96,0x03,0xcd,0x3a,0xb8,0x97,0x03,0xcd,0x39,0xb8,0x98,0x03 +,0xcd,0x39,0xb8,0x9b,0x03,0xcd,0x39,0xe9,0x18,0xfd,0xcd,0x34,0x83,0x3e,0xa3,0x36 +,0x04,0x77,0x18,0x83,0x3e,0xa3,0x36,0x03,0x75,0x08,0xf7,0x06,0x9b,0x36,0x00,0x01 +,0x75,0x09,0xc7,0x06,0x3d,0x37,0x01,0x00,0xe9,0xe8,0xfc,0xe9,0xe1,0x02,0xcd,0x34 +,0x83,0x3e,0xa3,0x36,0x02,0x77,0x09,0xc7,0x06,0x3d,0x37,0x01,0x00,0xe9,0xd3,0xfc +,0x83,0x3e,0xa3,0x36,0x04,0x77,0x05,0xb8,0x96,0x03,0xcd,0x39,0xe9,0xc0,0x02,0x83 +,0x3e,0xa3,0x36,0x03,0x75,0x10,0x26,0xa1,0x0c,0x00,0x25,0x07,0x00,0x50,0x3d,0x04 +,0x00,0x75,0x03,0xe8,0x36,0x00,0xa1,0xf3,0x36,0x86,0xe0,0xe7,0x1e,0xa3,0xe3,0x36 +,0x81,0x26,0x0b,0x37,0x00,0x03,0x81,0x26,0x0d,0x37,0x7b,0x7f,0x83,0x0e,0x0d,0x37 +,0x48,0xe8,0x14,0xf3,0x58,0x3d,0x04,0x00,0x74,0x09,0x26,0xf7,0x06,0x0c,0x00,0x20 +,0x00,0x75,0x06,0xb8,0x01,0x00,0xe9,0x7a,0x02,0xe9,0x86,0xfc,0xa1,0xe5,0x36,0xe7 +,0x2e,0xa1,0xe7,0x36,0xe7,0x3e,0xa1,0xd3,0x36,0xa3,0x9c,0x34,0xa1,0xd5,0x36,0xa3 +,0x9e,0x34,0xc3,0x26,0x80,0x3e,0x1c,0x00,0xff,0x75,0x2f,0x26,0x80,0x3e,0x1e,0x00 +,0xff,0x75,0x27,0x26,0xf7,0x06,0x0c,0x00,0x40,0x00,0x75,0x1b,0xa1,0xd1,0x36,0x26 +,0xa3,0x1a,0x00,0xa1,0xd3,0x36,0x26,0xa3,0x1c,0x00,0xa1,0xd5,0x36,0x26,0xa3,0x1e +,0x00,0xb8,0x0a,0x80,0xe9,0x2c,0x02,0xe9,0x38,0xfc,0xff,0x06,0x90,0x34,0xbe,0x0a +,0x00,0xc6,0x06,0xb6,0x34,0x01,0xf6,0x06,0x9d,0x36,0x80,0x75,0x05,0x83,0x0e,0xc2 +,0x34,0x01,0xcd,0x34,0xe9,0x0c,0xfc,0x83,0x3e,0xa3,0x36,0x03,0x75,0x09,0xc7,0x06 +,0x3d,0x37,0x05,0x00,0xe9,0xfc,0xfb,0xe5,0x02,0x0d,0x03,0x00,0x0d,0x00,0x88,0x0d +,0x00,0x40,0x0d,0x00,0x04,0xe7,0x02,0xc7,0x06,0xa3,0x36,0x05,0x00,0xc6,0x06,0x9e +,0x36,0xff,0xbe,0x02,0x00,0xe8,0xe1,0x01,0xb8,0x89,0x03,0xcd,0x3a,0xb8,0x9a,0x03 +,0xcd,0x3a,0xb8,0x99,0x03,0xcd,0x39,0xb8,0x97,0x03,0xcd,0x39,0xb8,0x98,0x03,0xcd +,0x39,0xe9,0xbb,0x01,0x83,0x3e,0xa3,0x36,0x03,0x74,0x0a,0x83,0x3e,0xa3,0x36,0x04 +,0x74,0x03,0xe9,0xaa,0x01,0xbe,0x06,0x00,0xe8,0xae,0x01,0xb8,0x95,0x03,0xcd,0x39 +,0xe9,0x9c,0x01,0x83,0x3e,0xa3,0x36,0x05,0x74,0x03,0xe9,0x92,0x01,0xbe,0x02,0x00 +,0xe8,0x96,0x01,0xb8,0x99,0x03,0xcd,0x39,0xe9,0x84,0x01,0xc7,0x06,0x0f,0x37,0x05 +,0x00,0xe9,0x7b,0x01,0xe5,0x02,0x25,0xff,0xdf,0xe7,0x02,0xc7,0x06,0xa3,0x36,0x07 +,0x00,0xc7,0x06,0x0f,0x37,0x05,0x00,0xe9,0x65,0x01,0xe8,0xd5,0x04,0xc6,0x06,0x9d +,0x36,0x00,0xc7,0x06,0x9b,0x36,0x00,0x00,0xc7,0x06,0x0f,0x37,0x05,0x00,0xc7,0x06 +,0xa8,0x02,0x00,0x00,0xc7,0x06,0x4c,0x37,0x01,0x00,0xe5,0x02,0x25,0xf9,0xff,0x0d +,0x03,0x00,0x0d,0x00,0x88,0x25,0xef,0xff,0x0d,0x00,0x40,0x0d,0x00,0x04,0xe7,0x02 +,0xe9,0x67,0xfc,0xb8,0x9a,0x03,0xcd,0x39,0xf7,0x06,0xf4,0x33,0x00,0x10,0x75,0x09 +,0xc7,0x06,0x33,0x37,0x02,0x00,0xe9,0x16,0x01,0xff,0x0e,0x33,0x37,0x74,0x03,0xe9 +,0x0d,0x01,0xff,0x06,0x8e,0x34,0x83,0x0e,0xc2,0x34,0x08,0xc7,0x06,0x3d,0x37,0x03 +,0x00,0xe9,0xff,0xfa,0xc3,0x52,0x50,0xba,0xe0,0x00,0xb8,0x00,0x10,0xef,0x58,0x5a +,0xc3,0xc7,0x06,0x3d,0x37,0x00,0x00,0xe9,0xe9,0xfa,0xfa,0xe8,0x54,0x04,0xb8,0x80 +,0x03,0x8e,0xc0,0x26,0xc7,0x06,0x04,0x00,0xd8,0x2b,0xb8,0x7f,0x03,0x8e,0xc0,0x26 +,0xc7,0x06,0x04,0x00,0xe8,0x2c,0x33,0xc0,0x8e,0xc0,0xa1,0xa7,0x36,0xa3,0xa9,0x36 +,0xa1,0xa9,0x36,0xe7,0x00,0xa1,0xab,0x36,0xe7,0x02,0xc7,0x06,0x05,0x37,0x00,0x00 +,0xc7,0x06,0x07,0x37,0x00,0x00,0xc7,0x06,0x09,0x37,0x00,0x00,0xc6,0x06,0x9d,0x36 +,0x00,0xc6,0x06,0x9e,0x36,0xff,0xc7,0x06,0x9b,0x36,0x00,0x00,0xc7,0x06,0xa3,0x36 +,0x00,0x00,0xc7,0x06,0x0f,0x37,0x00,0x00,0xc7,0x06,0xa8,0x02,0x00,0x00,0xc7,0x06 +,0x4c,0x37,0x01,0x00,0x81,0x26,0xaf,0x36,0xff,0xe7,0xa1,0xaf,0x36,0xe7,0x06,0xbb +,0xff,0x7f,0xcd,0x53,0xe8,0x7c,0xf9,0xe5,0x56,0x0d,0x02,0x00,0xe7,0x56,0xfb,0xc3 +,0x8d,0x3e,0xc0,0x53,0x8d,0x36,0xf0,0x38,0xb9,0x0e,0x00,0x8b,0x1e,0x30,0x34,0x89 +,0x5c,0x02,0x2e,0x8b,0x45,0x02,0x89,0x44,0x06,0x2e,0x8b,0x05,0x89,0x44,0x04,0x83 +,0xc7,0x04,0x83,0xc6,0x10,0xe2,0xe8,0xb8,0x80,0x03,0x8e,0xc0,0x26,0xc7,0x06,0x04 +,0x00,0xe2,0x51,0xb8,0x7f,0x03,0x8e,0xc0,0x26,0xc7,0x06,0x04,0x00,0xb2,0x52,0x33 +,0xc0,0x8e,0xc0,0xc7,0x06,0xa1,0x36,0x01,0x00,0xc7,0x06,0x0f,0x37,0x05,0x00,0xc3 +,0x33,0xff,0x8e,0x06,0xa6,0x02,0x8b,0x36,0xa4,0x02,0x2e,0xff,0xa4,0xa0,0x53,0xe8 +,0x8c,0xdb,0xc3,0xe8,0x48,0xf7,0xe9,0xf6,0xff,0x8e,0x06,0x38,0x34,0xe8,0x07,0xe1 +,0x26,0xc7,0x06,0x04,0x00,0xdf,0x4f,0xcd,0x50,0xc3,0x26,0xc7,0x06,0x0a,0x00,0x00 +,0x00,0x26,0xff,0x26,0x04,0x00,0xcd,0x34,0xe9,0xd4,0xff,0xa1,0xd1,0x36,0x26,0x39 +,0x06,0x1a,0x00,0x75,0x22,0xa1,0xd3,0x36,0x26,0x39,0x06,0x1c,0x00,0x75,0x18,0xa1 +,0xd5,0x36,0x26,0x39,0x06,0x1e,0x00,0x75,0x0e,0x26,0xf7,0x06,0x0c,0x00,0x40,0x00 +,0x74,0x05,0x83,0x0e,0x66,0x37,0x40,0x81,0x0e,0xaf,0x36,0x00,0x10,0xa1,0xaf,0x36 +,0xe7,0x06,0x83,0x3e,0xa3,0x36,0x02,0x75,0x05,0xcd,0x34,0xe9,0x56,0xfb,0x83,0x3e +,0xa3,0x36,0x00,0x74,0xb1,0x83,0x3e,0xa3,0x36,0x05,0x77,0xaa,0x26,0xf6,0x06,0x0a +,0x00,0xff,0x75,0xa2,0xe8,0xfd,0xdd,0x50,0xf6,0x06,0x93,0x36,0x20,0x75,0x03,0xe9 +,0x8c,0x00,0x26,0xa1,0x0c,0x00,0x25,0x07,0x00,0x3d,0x07,0x00,0x75,0x03,0xe9,0x76 +,0x00,0x3d,0x05,0x00,0x75,0x03,0xe9,0x6e,0x00,0xf7,0x06,0xe6,0x34,0x18,0x80,0x75 +,0x03,0xe9,0x6a,0x00,0xf7,0x06,0xe6,0x34,0x00,0x80,0x74,0x35,0x26,0x80,0x3e,0x29 +,0x00,0x02,0x75,0x2d,0x51,0x56,0x57,0x8d,0x36,0x3e,0x34,0x8d,0x3e,0x20,0x00,0xb9 +,0x06,0x00,0xf3,0xa6,0x5f,0x5e,0x59,0x75,0x45,0x26,0xa1,0x20,0x00,0xa3,0x3e,0x34 +,0x26,0xa1,0x22,0x00,0xa3,0x40,0x34,0x26,0xa1,0x24,0x00,0xa3,0x42,0x34,0xe9,0x26 +,0x00,0xf7,0x06,0xe6,0x34,0x08,0x00,0x74,0x0b,0x26,0x80,0x3e,0x19,0x00,0x00,0x74 +,0x03,0xe9,0x13,0x00,0xf7,0x06,0xe6,0x34,0x10,0x00,0x74,0x12,0x26,0xa0,0x28,0x00 +,0xc0,0xe8,0x04,0x22,0xc0,0x74,0x07,0x26,0xc7,0x06,0x04,0x00,0xff,0xff,0x58,0x23 +,0xc0,0x74,0x03,0xe9,0xdd,0xfe,0x81,0x26,0x9b,0x36,0xff,0xfe,0x26,0xa1,0x20,0x00 +,0x3b,0x06,0xd1,0x36,0x75,0x1a,0x26,0xa1,0x22,0x00,0x3b,0x06,0xd3,0x36,0x75,0x10 +,0x26,0xa1,0x24,0x00,0x3b,0x06,0xd5,0x36,0x75,0x06,0x81,0x0e,0x9b,0x36,0x00,0x01 +,0x26,0xa1,0x20,0x00,0x25,0x7f,0xff,0xa3,0xb8,0x34,0x26,0xa1,0x22,0x00,0xa3,0xba +,0x34,0x26,0xa1,0x24,0x00,0xa3,0xbc,0x34,0x8b,0xc6,0x86,0xc4,0xa3,0xc0,0x34,0xd1 +,0xe6,0x80,0xfc,0x09,0x74,0x03,0xe8,0xf6,0xf5,0xa1,0x05,0x37,0x0b,0x06,0x07,0x37 +,0x0b,0x06,0x09,0x37,0x74,0x3e,0x26,0xa1,0x20,0x00,0x3b,0x06,0x05,0x37,0x75,0x17 +,0x26,0xa1,0x22,0x00,0x3b,0x06,0x07,0x37,0x75,0x0d,0x26,0xa1,0x24,0x00,0x3b,0x06 +,0x09,0x37,0x75,0x03,0xe9,0x1d,0x00,0x26,0xa0,0x28,0x00,0x24,0x0f,0x3c,0x03,0x74 +,0x1b,0x3c,0x00,0x75,0x0f,0x83,0x3e,0xa3,0x36,0x04,0x74,0x10,0xf7,0x06,0x9b,0x36 +,0x00,0x01,0x74,0x08,0x2e,0xff,0x94,0xf8,0x53,0xe9,0x33,0xfe,0xcd,0x34,0xc7,0x06 +,0x3d,0x37,0x01,0x00,0xe9,0x2c,0xf8,0x83,0x3e,0xa3,0x36,0x05,0x74,0x10,0x83,0x3e +,0xa3,0x36,0x01,0x7e,0x09,0x83,0xee,0x16,0x2e,0xff,0x94,0x24,0x54,0xc3,0xcd,0x34 +,0xc3,0x26,0xa1,0x0c,0x00,0x3d,0xff,0x7f,0x74,0x05,0x26,0xff,0x26,0x04,0x00,0xe9 +,0xfd,0xfd,0xa1,0xf4,0x33,0xa9,0x00,0x88,0x74,0x0b,0xa9,0x00,0x10,0x75,0x09,0x8b +,0x1e,0x43,0x37,0xff,0xe3,0xe9,0x97,0x00,0xc7,0x06,0x35,0x37,0x05,0x00,0xc7,0x06 +,0x43,0x37,0x28,0x52,0xf7,0x06,0xf4,0x33,0x00,0x08,0x74,0x06,0xc7,0x06,0x43,0x37 +,0x1a,0x52,0xb8,0x80,0x03,0xcd,0x39,0xe9,0xc5,0xfd,0xa9,0x00,0x08,0x74,0xd9,0xff +,0x0e,0x35,0x37,0x75,0xed,0xe9,0x30,0x00,0xa9,0x00,0x08,0x75,0xcb,0xff,0x0e,0x35 +,0x37,0x75,0xdf,0x81,0x0e,0xc2,0x34,0xc0,0x00,0xf6,0x06,0x9d,0x36,0x80,0x74,0x0f +,0x81,0x0e,0x9b,0x36,0x00,0x80,0xc7,0x06,0x0f,0x37,0x02,0x00,0xe9,0x90,0xfd,0xc7 +,0x06,0x3d,0x37,0x02,0x00,0xe9,0x8b,0xf7,0x80,0x26,0x9e,0x36,0xff,0x75,0x30,0xf6 +,0x06,0x9d,0x36,0x80,0x74,0x20,0xff,0x06,0x94,0x34,0x83,0x0e,0x66,0x37,0x20,0x8e +,0x06,0x30,0x34,0x26,0xf7,0x06,0x0a,0x00,0x00,0x01,0x74,0x07,0x26,0x81,0x0e,0x08 +,0x00,0x00,0x01,0xe9,0x09,0x00,0xc7,0x06,0x3d,0x37,0x04,0x00,0xe9,0x54,0xf7,0x81 +,0x0e,0xaf,0x36,0x00,0x08,0xa1,0xaf,0x36,0xe7,0x06,0xe5,0x0a,0xa9,0x00,0x80,0x74 +,0x0e,0x81,0x26,0xaf,0x36,0xff,0xf7,0xa1,0xaf,0x36,0xe7,0x06,0xe9,0x49,0xff,0xe9 +,0x2d,0xfd,0xc7,0x06,0x41,0x37,0x00,0x00,0xbe,0x29,0x00,0xe8,0x2b,0xfd,0xe9,0x1e +,0xfd,0xcd,0x34,0x83,0x3e,0xa3,0x36,0x04,0x77,0x09,0xc7,0x06,0x3d,0x37,0x01,0x00 +,0xe9,0x10,0xf7,0xe9,0x09,0xfd,0xcd,0x34,0xc3,0xc7,0x06,0x9b,0x36,0x00,0x00,0xe8 +,0x0c,0xf5,0x81,0x26,0xaf,0x36,0xff,0xe7,0xa1,0xaf,0x36,0xe7,0x06,0x81,0x26,0x9b +,0x36,0xff,0x7f,0xe5,0x02,0x0d,0x01,0x00,0x25,0xef,0xff,0x25,0xff,0xdf,0xe7,0x02 +,0xbb,0xff,0x7f,0xcd,0x53,0x33,0xc0,0xa3,0x9d,0x36,0xa3,0x9f,0x36,0xe8,0x20,0xf3 +,0xe8,0x43,0xf3,0x83,0x0e,0x9b,0x36,0x10,0xc7,0x06,0x99,0x36,0x00,0x00,0xe8,0xd2 +,0xf5,0xe5,0x56,0x0d,0x02,0x00,0xe7,0x56,0xc7,0x06,0xa8,0x02,0x00,0x00,0xbe,0x00 +,0x00,0xe8,0x30,0xf5,0xc6,0x06,0xa0,0x36,0x0e,0xb8,0x9c,0x03,0xcd,0x39,0xb8,0x80 +,0x00,0xcd,0x35,0xc7,0x06,0xaa,0x02,0xff,0xff,0xc7,0x06,0xa1,0x36,0x01,0x00,0xe9 +,0xa5,0xf6,0x06,0xb8,0x8f,0x03,0xcd,0x3a,0xb8,0x90,0x03,0xcd,0x3a,0xb8,0x91,0x03 +,0xcd,0x3a,0xb8,0x92,0x03,0xcd,0x3a,0xb8,0x93,0x03,0xcd,0x3a,0xb8,0x94,0x03,0xcd +,0x3a,0xb8,0x95,0x03,0xcd,0x3a,0xb8,0x96,0x03,0xcd,0x3a,0xb8,0x97,0x03,0xcd,0x3a +,0xb8,0x98,0x03,0xcd,0x3a,0xb8,0x99,0x03,0xcd,0x3a,0xb8,0x9a,0x03,0xcd,0x3a,0xb8 +,0x9b,0x03,0xcd,0x3a,0xb8,0x7f,0x03,0xcd,0x3a,0xb8,0x80,0x03,0xcd,0x3a,0x07,0xc3 +,0xf7,0x49,0xf1,0x4e,0xdf,0x4f,0xdf,0x4f,0xdf,0x4f,0xdf,0x4f,0xf8,0x51,0xdf,0x4f +,0xfa,0x4f,0x0b,0x50,0xd1,0x51,0xdf,0x4f,0xdf,0x4f,0xdf,0x4f,0xdf,0x4f,0xdf,0x4f +,0xe4,0x4e,0x06,0x00,0xcd,0x4a,0x04,0x00,0xe4,0x4e,0x19,0x00,0xad,0x4b,0xfa,0x00 +,0x82,0x4c,0x08,0x07,0x09,0x4c,0x14,0x00,0x24,0x4e,0x64,0x00,0xd7,0x4d,0xf4,0x01 +,0x64,0x4e,0xbc,0x02,0x7a,0x4e,0xe8,0x03,0x43,0x4e,0x02,0x00,0xb3,0x4e,0xf4,0x01 +,0x5b,0x4e,0xf4,0x01,0xe5,0x4e,0x14,0x00,0x06,0x50,0x06,0x50,0x95,0x4c,0xc1,0x52 +,0xc1,0x52,0xfe,0x4c,0xda,0x4c,0x06,0x50,0x06,0x50,0x06,0x50,0x06,0x50,0xb7,0x51 +,0xb7,0x51,0xb7,0x51,0xb7,0x51,0xb7,0x51,0xb7,0x51,0x06,0x50,0xd5,0x4a,0x06,0x50 +,0x1d,0x4c,0x06,0x50,0x83,0x4d,0x1f,0x4d,0x1f,0x4d,0xed,0x40,0xfa,0x40,0x07,0x41 +,0x37,0x37,0x2e,0x37,0x37,0x20,0x20,0x79,0x79,0x2f,0x79,0x79,0x2f,0x79,0x79,0x20 +,0x30,0x31,0x2e,0x39,0x30,0x20,0x20,0x30,0x32,0x2f,0x31,0x37,0x2f,0x39,0x39,0x20 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +,0x90,0xea,0xc0,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0x06 +} ; +#endif diff -Nru a/drivers/net/tokenring/Config.help b/drivers/net/tokenring/Config.help --- a/drivers/net/tokenring/Config.help Thu Mar 7 18:17:45 2002 +++ b/drivers/net/tokenring/Config.help Thu Mar 7 18:17:45 2002 @@ -129,3 +129,21 @@ The module will be called smctr.o. If you want to compile it as a module, say M here and read . +3COM 3C359 Token Link Velocity XL PCI adapter support +CONFIG_3C359 + This is support for the 3Com PCI Velocity XL cards, specifically + the 3Com 3C359, please note this is not for the 3C339 cards, you + should use the tms380 driver instead. + + If you have such an adapter, say Y and read the Token-Ring + mini-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). + The module will will be called 3c359.o. If you want to compile it + as a module, say M here and read Documentation/modules.txt. + + Also read the file or check the + Linux Token Ring Project site for the latest information at + + diff -Nru a/drivers/net/tokenring/Config.in b/drivers/net/tokenring/Config.in --- a/drivers/net/tokenring/Config.in Thu Mar 7 18:17:40 2002 +++ b/drivers/net/tokenring/Config.in Thu Mar 7 18:17:40 2002 @@ -18,6 +18,7 @@ fi dep_tristate ' IBM Olympic chipset PCI adapter support' CONFIG_IBMOL $CONFIG_TR $CONFIG_PCI dep_tristate ' IBM Lanstreamer chipset PCI adapter support' CONFIG_IBMLS $CONFIG_TR $CONFIG_PCI + dep_tristate ' 3Com 3C359 Token Link Velocity XL adapter support' CONFIG_3C359 $CONFIG_TR $CONFIG_PCI tristate ' Generic TMS380 Token Ring ISA/PCI adapter support' CONFIG_TMS380TR if [ "$CONFIG_TMS380TR" != "n" ]; then dep_tristate ' Generic TMS380 PCI support' CONFIG_TMSPCI $CONFIG_PCI diff -Nru a/drivers/net/tokenring/Makefile b/drivers/net/tokenring/Makefile --- a/drivers/net/tokenring/Makefile Thu Mar 7 18:17:39 2002 +++ b/drivers/net/tokenring/Makefile Thu Mar 7 18:17:39 2002 @@ -21,6 +21,7 @@ obj-$(CONFIG_TMSPCI) += tmspci.o obj-$(CONFIG_TMSISA) += tmsisa.o obj-$(CONFIG_SMCTR) += smctr.o +obj-$(CONFIG_3C359) += 3c359.o O_TARGET := tr.o diff -Nru a/drivers/net/tokenring/abyss.c b/drivers/net/tokenring/abyss.c --- a/drivers/net/tokenring/abyss.c Thu Mar 7 18:17:40 2002 +++ b/drivers/net/tokenring/abyss.c Thu Mar 7 18:17:40 2002 @@ -433,7 +433,7 @@ return 0; } -static void __exit abyss_detach (struct pci_dev *pdev) +static void __devexit abyss_detach (struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); @@ -451,7 +451,7 @@ name: "abyss", id_table: abyss_pci_tbl, probe: abyss_attach, - remove: abyss_detach, + remove: __devexit_p(abyss_detach), }; static int __init abyss_init (void) diff -Nru a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c --- a/drivers/net/tokenring/lanstreamer.c Thu Mar 7 18:17:43 2002 +++ b/drivers/net/tokenring/lanstreamer.c Thu Mar 7 18:17:43 2002 @@ -60,6 +60,11 @@ * malloc free checks, reviewed code. * 03/13/00 - Added spinlocks for smp * 03/08/01 - Added support for module_init() and module_exit() + * 08/15/01 - Added ioctl() functionality for debugging, changed netif_*_queue + * calls and other incorrectness - Kent Yoder + * 11/05/01 - Restructured the interrupt function, added delays, reduced the + * the number of TX descriptors to 1, which together can prevent + * the card from locking up the box - * * To Do: * @@ -84,6 +89,14 @@ #define STREAMER_NETWORK_MONITOR 0 +/* #define CONFIG_PROC_FS */ + +/* + * Allow or disallow ioctl's for debugging + */ + +#define STREAMER_IOCTL 0 + #include #include @@ -105,6 +118,7 @@ #include #include #include +#include #include #include @@ -121,7 +135,8 @@ * Official releases will only have an a.b.c version number format. */ -static char version[] = "LanStreamer.c v0.4.0 03/08/01 - Mike Sullivan"; +static char version[] = "LanStreamer.c v0.4.0 03/08/01 - Mike Sullivan\n" + " v0.5.1 03/04/02 - Kent Yoder"; static struct pci_device_id streamer_pci_tbl[] __initdata = { { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR, PCI_ANY_ID, PCI_ANY_ID,}, @@ -175,6 +190,10 @@ MODULE_PARM(message_level, "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i"); +#if STREAMER_IOCTL +static int streamer_ioctl(struct net_device *, struct ifreq *, int); +#endif + static int streamer_reset(struct net_device *dev); static int streamer_open(struct net_device *dev); static int streamer_xmit(struct sk_buff *skb, struct net_device *dev); @@ -206,6 +225,8 @@ __u32 mmio_start, mmio_end, mmio_flags, mmio_len; int rc=0; static int card_no=-1; + u16 pcr; + u8 cls = 0; #if STREAMER_DEBUG printk("lanstreamer::streamer_init_one, entry pdev %p\n",pdev); @@ -281,7 +302,11 @@ dev->hard_start_xmit = &streamer_xmit; dev->change_mtu = &streamer_change_mtu; dev->stop = &streamer_close; +#if STREAMER_IOCTL + dev->do_ioctl = &streamer_ioctl; +#else dev->do_ioctl = NULL; +#endif dev->set_multicast_list = &streamer_set_rx_mode; dev->get_stats = &streamer_get_stats; dev->set_mac_address = &streamer_set_mac_address; @@ -303,6 +328,27 @@ spin_lock_init(&streamer_priv->streamer_lock); + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cls); + cls <<= 2; + if (cls != SMP_CACHE_BYTES) { + printk(KERN_INFO " PCI cache line size set incorrectly " + "(%i bytes) by BIOS/FW, ", cls); + if (cls > SMP_CACHE_BYTES) + printk("expecting %i\n", SMP_CACHE_BYTES); + else { + printk("correcting to %i\n", SMP_CACHE_BYTES); + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, + SMP_CACHE_BYTES >> 2); + } + } + + pci_read_config_word (pdev, PCI_COMMAND, &pcr); + + pcr |= (PCI_COMMAND_INVALIDATE | PCI_COMMAND_SERR); + + pci_write_config_word (pdev, PCI_COMMAND, pcr); + pci_read_config_word (pdev, PCI_COMMAND, &pcr); + printk("%s \n", version); printk("%s: %s. I/O at %hx, MMIO at %p, using irq %d\n",dev->name, streamer_priv->streamer_card_name, @@ -403,6 +449,7 @@ printk("GPR: %x\n", readw(streamer_mmio + GPR)); printk("SISRMASK: %x\n", readw(streamer_mmio + SISR_MASK)); #endif + writew(readw(streamer_mmio + BCTL) | (BCTL_RX_FIFO_8 | BCTL_TX_FIFO_8), streamer_mmio + BCTL ); if (streamer_priv->streamer_ring_speed == 0) { /* Autosense */ writew(readw(streamer_mmio + GPR) | GPR_AUTOSENSE, @@ -558,8 +605,6 @@ do { int i; - save_flags(flags); - cli(); for (i = 0; i < SRB_COMMAND_SIZE; i += 2) { writew(0, streamer_mmio + LAPDINC); } @@ -599,11 +644,12 @@ } printk("\n"); #endif - + spin_lock_irqsave(&streamer_priv->streamer_lock, flags); streamer_priv->srb_queued = 1; /* signal solo that SRB command has been issued */ writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM); + spin_unlock_irqrestore(&streamer_priv->streamer_lock, flags); while (streamer_priv->srb_queued) { interruptible_sleep_on_timeout(&streamer_priv->srb_wait, 5 * HZ); @@ -617,7 +663,6 @@ break; } } - restore_flags(flags); #if STREAMER_DEBUG printk("SISR_MASK: %x\n", readw(streamer_mmio + SISR_MASK)); @@ -767,9 +812,12 @@ streamer_priv->streamer_tx_ring[i].bufcnt_framelen = 0; streamer_priv->streamer_tx_ring[i].buffer = 0; streamer_priv->streamer_tx_ring[i].buflen = 0; + streamer_priv->streamer_tx_ring[i].rsvd1 = 0; + streamer_priv->streamer_tx_ring[i].rsvd2 = 0; + streamer_priv->streamer_tx_ring[i].rsvd3 = 0; } streamer_priv->streamer_tx_ring[STREAMER_TX_RING_SIZE - 1].forward = - virt_to_bus(&streamer_priv->streamer_tx_ring[0]);; + virt_to_bus(&streamer_priv->streamer_tx_ring[0]); streamer_priv->free_tx_ring_entries = STREAMER_TX_RING_SIZE; streamer_priv->tx_ring_free = 0; /* next entry in tx ring to use */ @@ -941,37 +989,30 @@ __u8 *streamer_mmio = streamer_priv->streamer_mmio; __u16 sisr; __u16 misr; - __u16 sisrmask; + u8 max_intr = MAX_INTR; - sisrmask = SISR_MI; - writew(~sisrmask, streamer_mmio + SISR_MASK_RUM); + spin_lock(&streamer_priv->streamer_lock); sisr = readw(streamer_mmio + SISR); - writew(~sisr, streamer_mmio + SISR_RUM); - misr = readw(streamer_mmio + MISR_RUM); - writew(~misr, streamer_mmio + MISR_RUM); - if (!sisr) - { /* Interrupt isn't for us */ - writew(~misr,streamer_mmio+MISR_RUM); - return; - } + while((sisr & (SISR_MI | SISR_SRB_REPLY | SISR_ADAPTER_CHECK | SISR_ASB_FREE | + SISR_ARB_CMD | SISR_TRB_REPLY | SISR_PAR_ERR | SISR_SERR_ERR)) + && (max_intr > 0)) { - spin_lock(&streamer_priv->streamer_lock); + if(sisr & SISR_PAR_ERR) { + writew(~SISR_PAR_ERR, streamer_mmio + SISR_RUM); + (void)readw(streamer_mmio + SISR_RUM); + } - if ((sisr & (SISR_SRB_REPLY | SISR_ADAPTER_CHECK | SISR_ASB_FREE | SISR_ARB_CMD | SISR_TRB_REPLY)) - || (misr & (MISR_TX2_EOF | MISR_RX_NOBUF | MISR_RX_EOF))) { - if (sisr & SISR_SRB_REPLY) { - if (streamer_priv->srb_queued == 1) { - wake_up_interruptible(&streamer_priv->srb_wait); - } else if (streamer_priv->srb_queued == 2) { - streamer_srb_bh(dev); - } - streamer_priv->srb_queued = 0; + else if(sisr & SISR_SERR_ERR) { + writew(~SISR_SERR_ERR, streamer_mmio + SISR_RUM); + (void)readw(streamer_mmio + SISR_RUM); } - /* SISR_SRB_REPLY */ + + else if(sisr & SISR_MI) { + misr = readw(streamer_mmio + MISR_RUM); + if (misr & MISR_TX2_EOF) { - while (streamer_priv->streamer_tx_ring[(streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1)].status) - { + while(streamer_priv->streamer_tx_ring[(streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1)].status) { streamer_priv->tx_ring_last_status = (streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1); streamer_priv->free_tx_ring_entries++; streamer_priv->streamer_stats.tx_bytes += streamer_priv->tx_ring_skb[streamer_priv->tx_ring_last_status]->len; @@ -981,6 +1022,9 @@ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].status = 0; streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].bufcnt_framelen = 0; streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].buflen = 0; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].rsvd1 = 0; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].rsvd2 = 0; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].rsvd3 = 0; } netif_wake_queue(dev); } @@ -989,7 +1033,30 @@ streamer_rx(dev); } /* MISR_RX_EOF */ - if (sisr & SISR_ADAPTER_CHECK) { + + if (misr & MISR_RX_NOBUF) { + /* According to the documentation, we don't have to do anything, + * but trapping it keeps it out of /var/log/messages. + */ + } /* SISR_RX_NOBUF */ + + writew(~misr, streamer_mmio + MISR_RUM); + (void)readw(streamer_mmio + MISR_RUM); + } + + else if (sisr & SISR_SRB_REPLY) { + if (streamer_priv->srb_queued == 1) { + wake_up_interruptible(&streamer_priv->srb_wait); + } else if (streamer_priv->srb_queued == 2) { + streamer_srb_bh(dev); + } + streamer_priv->srb_queued = 0; + + writew(~SISR_SRB_REPLY, streamer_mmio + SISR_RUM); + (void)readw(streamer_mmio + SISR_RUM); + } + + else if (sisr & SISR_ADAPTER_CHECK) { printk(KERN_WARNING "%s: Adapter Check Interrupt Raised, 8 bytes of information follow:\n", dev->name); writel(readl(streamer_mmio + LAPWWO), streamer_mmio + LAPA); printk(KERN_WARNING "%s: Words %x:%x:%x:%x:\n", @@ -1001,38 +1068,37 @@ } /* SISR_ADAPTER_CHECK */ - if (sisr & SISR_ASB_FREE) { + else if (sisr & SISR_ASB_FREE) { /* Wake up anything that is waiting for the asb response */ if (streamer_priv->asb_queued) { streamer_asb_bh(dev); } + writew(~SISR_ASB_FREE, streamer_mmio + SISR_RUM); + (void)readw(streamer_mmio + SISR_RUM); } /* SISR_ASB_FREE */ - if (sisr & SISR_ARB_CMD) { + else if (sisr & SISR_ARB_CMD) { streamer_arb_cmd(dev); + writew(~SISR_ARB_CMD, streamer_mmio + SISR_RUM); + (void)readw(streamer_mmio + SISR_RUM); } /* SISR_ARB_CMD */ - if (sisr & SISR_TRB_REPLY) { + else if (sisr & SISR_TRB_REPLY) { /* Wake up anything that is waiting for the trb response */ if (streamer_priv->trb_queued) { wake_up_interruptible(&streamer_priv-> trb_wait); } streamer_priv->trb_queued = 0; + writew(~SISR_TRB_REPLY, streamer_mmio + SISR_RUM); + (void)readw(streamer_mmio + SISR_RUM); } /* SISR_TRB_REPLY */ - if (misr & MISR_RX_NOBUF) { - /* According to the documentation, we don't have to do anything, but trapping it keeps it out of - /var/log/messages. */ - } /* SISR_RX_NOBUF */ - } else { - printk(KERN_WARNING "%s: Unexpected interrupt: %x\n", - dev->name, sisr); - printk(KERN_WARNING "%s: SISR_MASK: %x\n", dev->name, - readw(streamer_mmio + SISR_MASK)); - } /* One if the interrupts we want */ - writew(SISR_MI, streamer_mmio + SISR_MASK_SUM); + sisr = readw(streamer_mmio + SISR); + max_intr--; + } /* while() */ + spin_unlock(&streamer_priv->streamer_lock) ; } @@ -1044,13 +1110,16 @@ unsigned long flags ; spin_lock_irqsave(&streamer_priv->streamer_lock, flags); - netif_stop_queue(dev); if (streamer_priv->free_tx_ring_entries) { streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].status = 0; - streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].bufcnt_framelen = 0x00010000 | skb->len; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].bufcnt_framelen = 0x00020000 | skb->len; streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buffer = virt_to_bus(skb->data); + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].rsvd1 = skb->len; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].rsvd2 = 0; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].rsvd3 = 0; streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buflen = skb->len; + streamer_priv->tx_ring_skb[streamer_priv->tx_ring_free] = skb; streamer_priv->free_tx_ring_entries--; #if STREAMER_DEBUG_PACKETS @@ -1067,12 +1136,13 @@ #endif writel(virt_to_bus (&streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free]),streamer_mmio + TX2LFDA); + (void)readl(streamer_mmio + TX2LFDA); streamer_priv->tx_ring_free = (streamer_priv->tx_ring_free + 1) & (STREAMER_TX_RING_SIZE - 1); - netif_wake_queue(dev); spin_unlock_irqrestore(&streamer_priv->streamer_lock,flags); return 0; } else { + netif_stop_queue(dev); spin_unlock_irqrestore(&streamer_priv->streamer_lock,flags); return 1; } @@ -1092,12 +1162,13 @@ writew(htons(SRB_CLOSE_ADAPTER << 8),streamer_mmio+LAPDINC); writew(htons(STREAMER_CLEAR_RET_CODE << 8), streamer_mmio+LAPDINC); - save_flags(flags); - cli(); + spin_lock_irqsave(&streamer_priv->streamer_lock, flags); streamer_priv->srb_queued = 1; writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM); + spin_unlock_irqrestore(&streamer_priv->streamer_lock, flags); + while (streamer_priv->srb_queued) { interruptible_sleep_on_timeout(&streamer_priv->srb_wait, @@ -1114,7 +1185,6 @@ } } - restore_flags(flags); streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1); for (i = 0; i < STREAMER_RX_RING_SIZE; i++) { @@ -1808,11 +1878,69 @@ #endif #endif +#if STREAMER_IOCTL && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +static int streamer_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + int i; + struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv; + u8 *streamer_mmio = streamer_priv->streamer_mmio; + + switch(cmd) { + case IOCTL_SISR_MASK: + writew(SISR_MI, streamer_mmio + SISR_MASK_SUM); + break; + case IOCTL_SPIN_LOCK_TEST: + printk(KERN_INFO "spin_lock() called.\n"); + spin_lock(&streamer_priv->streamer_lock); + spin_unlock(&streamer_priv->streamer_lock); + printk(KERN_INFO "spin_unlock() finished.\n"); + break; + case IOCTL_PRINT_BDAS: + printk(KERN_INFO "bdas: RXBDA: %x RXLBDA: %x TX2FDA: %x TX2LFDA: %x\n", + readw(streamer_mmio + RXBDA), + readw(streamer_mmio + RXLBDA), + readw(streamer_mmio + TX2FDA), + readw(streamer_mmio + TX2LFDA)); + break; + case IOCTL_PRINT_REGISTERS: + printk(KERN_INFO "registers:\n"); + printk(KERN_INFO "SISR: %04x MISR: %04x LISR: %04x BCTL: %04x BMCTL: %04x\nmask %04x mask %04x\n", + readw(streamer_mmio + SISR), + readw(streamer_mmio + MISR_RUM), + readw(streamer_mmio + LISR), + readw(streamer_mmio + BCTL), + readw(streamer_mmio + BMCTL_SUM), + readw(streamer_mmio + SISR_MASK), + readw(streamer_mmio + MISR_MASK)); + break; + case IOCTL_PRINT_RX_BUFS: + printk(KERN_INFO "Print rx bufs:\n"); + for(i=0; istreamer_rx_ring[i].status); + break; + case IOCTL_PRINT_TX_BUFS: + printk(KERN_INFO "Print tx bufs:\n"); + for(i=0; istreamer_tx_ring[i].status); + break; + case IOCTL_RX_CMD: + streamer_rx(dev); + printk(KERN_INFO "Sent rx command.\n"); + break; + default: + printk(KERN_INFO "Bad ioctl!\n"); + } + return 0; +} +#endif + static struct pci_driver streamer_pci_driver = { name: "lanstreamer", id_table: streamer_pci_tbl, probe: streamer_init_one, - remove: streamer_remove_one, + remove: __devexit_p(streamer_remove_one), }; static int __init streamer_init_module(void) { diff -Nru a/drivers/net/tokenring/lanstreamer.h b/drivers/net/tokenring/lanstreamer.h --- a/drivers/net/tokenring/lanstreamer.h Thu Mar 7 18:17:41 2002 +++ b/drivers/net/tokenring/lanstreamer.h Thu Mar 7 18:17:41 2002 @@ -56,11 +56,36 @@ * * 12/10/99 - Alpha Release 0.1.0 * First release to the public + * 08/15/01 - Added ioctl() definitions and others - Kent Yoder * */ +#if STREAMER_IOCTL && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +#include +#define IOCTL_PRINT_RX_BUFS SIOCDEVPRIVATE +#define IOCTL_PRINT_TX_BUFS SIOCDEVPRIVATE+1 +#define IOCTL_RX_CMD SIOCDEVPRIVATE+2 +#define IOCTL_TX_CMD SIOCDEVPRIVATE+3 +#define IOCTL_PRINT_REGISTERS SIOCDEVPRIVATE+4 +#define IOCTL_PRINT_BDAS SIOCDEVPRIVATE+5 +#define IOCTL_SPIN_LOCK_TEST SIOCDEVPRIVATE+6 +#define IOCTL_SISR_MASK SIOCDEVPRIVATE+7 +#endif + +/* MAX_INTR - the maximum number of times we can loop + * inside the interrupt function before returning + * control to the OS (maximum value is 256) + */ +#define MAX_INTR 5 + +#define CLS 0x0C +#define MLR 0x86 +#define LTR 0x0D + #define BCTL 0x60 #define BCTL_SOFTRESET (1<<15) +#define BCTL_RX_FIFO_8 (1<<1) +#define BCTL_TX_FIFO_8 (1<<3) #define GPR 0x4a #define GPR_AUTOSENSE (1<<2) @@ -89,6 +114,7 @@ #define SISR_MASK_RUM 0x58 #define SISR_MI (1<<15) +#define SISR_SERR_ERR (1<<14) #define SISR_TIMER (1<<11) #define SISR_LAP_PAR_ERR (1<<10) #define SISR_LAP_ACC_ERR (1<<9) @@ -218,7 +244,13 @@ /* Streamer defaults for buffers */ #define STREAMER_RX_RING_SIZE 16 /* should be a power of 2 */ -#define STREAMER_TX_RING_SIZE 8 /* should be a power of 2 */ +/* Setting the number of TX descriptors to 1 is a workaround for an + * undocumented hardware problem with the lanstreamer board. Setting + * this to something higher may slightly increase the throughput you + * can get from the card, but at the risk of locking up the box. - + * + */ +#define STREAMER_TX_RING_SIZE 1 /* should be a power of 2 */ #define PKT_BUF_SZ 4096 /* Default packet size */ diff -Nru a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c --- a/drivers/net/tokenring/olympic.c Thu Mar 7 18:17:40 2002 +++ b/drivers/net/tokenring/olympic.c Thu Mar 7 18:17:40 2002 @@ -433,8 +433,6 @@ do { int i; - save_flags(flags); - cli(); for(i=0;idev_addr,olympic_priv->olympic_laa,dev->addr_len) ; } writeb(1,init_srb+30); - + + spin_lock_irqsave(&olympic_priv->olympic_lock,flags); olympic_priv->srb_queued=1; writel(LISR_SRB_CMD,olympic_mmio+LISR_SUM); + spin_unlock_irqrestore(&olympic_priv->olympic_lock,flags); t = jiffies ; @@ -496,7 +496,6 @@ remove_wait_queue(&olympic_priv->srb_wait,&wait) ; set_current_state(TASK_RUNNING) ; - restore_flags(flags); #if OLYMPIC_DEBUG printk("init_srb(%p): ",init_srb); for(i=0;i<20;i++) @@ -1058,12 +1057,11 @@ writeb(0,srb+1); writeb(OLYMPIC_CLEAR_RET_CODE,srb+2); - save_flags(flags); - cli(); - + spin_lock_irqsave(&olympic_priv->olympic_lock,flags); olympic_priv->srb_queued=1; writel(LISR_SRB_CMD,olympic_mmio+LISR_SUM); + spin_unlock_irqrestore(&olympic_priv->olympic_lock,flags); t = jiffies ; @@ -1088,7 +1086,6 @@ remove_wait_queue(&olympic_priv->srb_wait,&wait) ; set_current_state(TASK_RUNNING) ; - restore_flags(flags) ; olympic_priv->rx_status_last_received++; olympic_priv->rx_status_last_received&=OLYMPIC_RX_RING_SIZE-1; @@ -1760,7 +1757,7 @@ name: "olympic", id_table: olympic_pci_tbl, probe: olympic_probe, - remove: olympic_remove_one + remove: __devexit_p(olympic_remove_one) }; static int __init olympic_pci_init(void) diff -Nru a/drivers/net/tokenring/tmspci.c b/drivers/net/tokenring/tmspci.c --- a/drivers/net/tokenring/tmspci.c Thu Mar 7 18:17:46 2002 +++ b/drivers/net/tokenring/tmspci.c Thu Mar 7 18:17:46 2002 @@ -220,7 +220,7 @@ return val; } -static void __exit tms_pci_detach (struct pci_dev *pdev) +static void __devexit tms_pci_detach (struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); @@ -238,7 +238,7 @@ name: "tmspci", id_table: tmspci_pci_tbl, probe: tms_pci_attach, - remove: tms_pci_detach, + remove: __devexit_p(tms_pci_detach), }; static int __init tms_pci_init (void) diff -Nru a/drivers/net/tulip/ChangeLog b/drivers/net/tulip/ChangeLog --- a/drivers/net/tulip/ChangeLog Thu Mar 7 18:17:39 2002 +++ b/drivers/net/tulip/ChangeLog Thu Mar 7 18:17:39 2002 @@ -1,3 +1,10 @@ +2002-03-07 Jeff Garzik + + * tulip_core (tulip_mwi_config): Use new PCI API functions + for enabling and disabled Memory-Write-Invalidate + PCI transaction. + Fix bugs in tulip MWI config also. + 2002-01-28 Stefan Rompf , Jeff Garzik diff -Nru a/drivers/net/tulip/Config.help b/drivers/net/tulip/Config.help --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/tulip/Config.help Thu Mar 7 18:17:46 2002 @@ -0,0 +1,110 @@ +CONFIG_PCMCIA_XIRCOM + This driver is for the Digital "Tulip" Ethernet CardBus adapters. + It should work with most DEC 21*4*-based chips/ethercards, as well + as with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and + ASIX. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called xircom_cb.o. If you want to compile + it as a module, say M here and read + . If unsure, say N. + +CONFIG_PCMCIA_XIRTULIP + This driver is for the Digital "Tulip" Ethernet CardBus adapters. + It should work with most DEC 21*4*-based chips/ethercards, as well + as with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and + ASIX. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called xircom_tulip_cb.o. If you want to compile + it as a module, say M here and read + . If unsure, say N. + +CONFIG_DE2104X + This driver is developed for the SMC EtherPower series Ethernet + cards and also works with cards based on the DECchip + 21040 (Tulip series) chips. Some LinkSys PCI cards are + of this type. (If your card is NOT SMC EtherPower 10/100 PCI + (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 + . More specific + information is contained in + . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called tulip.o. If you want to compile it as a + module, say M here and read as well + as . + +CONFIG_TULIP + This driver is developed for the SMC EtherPower series Ethernet + cards and also works with cards based on the DECchip + 21040/21041/21140 (Tulip series) chips. Some LinkSys PCI cards are + of this type. (If your card is NOT SMC EtherPower 10/100 PCI + (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 + . More specific + information is contained in + . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called tulip.o. If you want to compile it as a + module, say M here and read as well + as . + +CONFIG_TULIP_MWI + This configures your Tulip card specifically for the card and + system cache line size type you are using. + + This is experimental code, not yet tested on many boards. + + If unsure, say N. + +CONFIG_TULIP_MMIO + Use PCI shared memory for the NIC registers, rather than going through + the Tulip's PIO (programmed I/O ports). Faster, but could produce + obscure bugs if your mainboard has memory controller timing issues. + If in doubt, say N. + +CONFIG_NET_TULIP + This selects the "Tulip" family of EISA/PCI network cards. + +CONFIG_DE4X5 + This is support for the DIGITAL series of PCI/EISA Ethernet cards. + 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 + . More specific + information is contained in + . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called de4x5.o. If you want to compile it as a + module, say M here and read as well + as . + +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 + . + +CONFIG_DM9102 + This driver is for DM9102(A)/DM9132/DM9801 compatible PCI cards from + Davicom (). If you have such a network + (Ethernet) card, say Y. Some information is contained in the file + . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called dmfe.o. If you want to compile it as a + module, say M here and read as well + as . + diff -Nru a/drivers/net/tulip/Config.in b/drivers/net/tulip/Config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/tulip/Config.in Thu Mar 7 18:17:47 2002 @@ -0,0 +1,27 @@ +# +# Tulip family network device configuration +# + +mainmenu_option next_comment +comment '"Tulip" family network device support' + +bool '"Tulip" family network device support' CONFIG_NET_TULIP +if [ "$CONFIG_NET_TULIP" = "y" ]; then + dep_tristate ' Early DECchip Tulip (dc2104x) PCI support (EXPERIMENTAL)' CONFIG_DE2104X $CONFIG_PCI $CONFIG_EXPERIMENTAL + dep_tristate ' DECchip Tulip (dc2114x) PCI support' CONFIG_TULIP $CONFIG_PCI + if [ "$CONFIG_TULIP" = "y" -o "$CONFIG_TULIP" = "m" ]; then + dep_bool ' New bus configuration (EXPERIMENTAL)' CONFIG_TULIP_MWI $CONFIG_EXPERIMENTAL + bool ' Use PCI shared mem for NIC registers' CONFIG_TULIP_MMIO + fi + if [ "$CONFIG_PCI" = "y" -o "$CONFIG_EISA" = "y" ]; then + tristate ' Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5 + fi + dep_tristate ' Winbond W89c840 Ethernet support' CONFIG_WINBOND_840 $CONFIG_PCI + dep_tristate ' Davicom DM910x/DM980x support' CONFIG_DM9102 $CONFIG_PCI + if [ "$CONFIG_CARDBUS" = "y" ]; then + tristate ' Xircom CardBus support (new driver)' CONFIG_PCMCIA_XIRCOM + tristate ' Xircom Tulip-like CardBus support (old driver)' CONFIG_PCMCIA_XIRTULIP + fi +fi + +endmenu diff -Nru a/drivers/net/tulip/Makefile b/drivers/net/tulip/Makefile --- a/drivers/net/tulip/Makefile Thu Mar 7 18:17:46 2002 +++ b/drivers/net/tulip/Makefile Thu Mar 7 18:17:46 2002 @@ -1,17 +1,36 @@ # -# Makefile for the Tulip ethernet driver +# drivers/net/tulip/Makefile # -# 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). +# Makefile for the Linux "Tulip" family network device drivers. # -# Note 2! The CFLAGS definitions are now in the main makefile... -O_TARGET := tulip.o +O_TARGET := tulip_net.o -obj-y := eeprom.o interrupt.o media.o \ - timer.o tulip_core.o \ - 21142.o pnic.o pnic2.o -obj-m := $(O_TARGET) +obj-y := +obj-m := +obj-n := +obj- := + +# Things that need to export symbols +export-objs := + +obj-$(CONFIG_PCMCIA_XIRTULIP) += xircom_tulip_cb.o +obj-$(CONFIG_PCMCIA_XIRCOM) += xircom_cb.o +obj-$(CONFIG_DM9102) += dmfe.o +obj-$(CONFIG_WINBOND_840) += winbond-840.o +obj-$(CONFIG_DE2104X) += de2104x.o +obj-$(CONFIG_TULIP) += tulip.o +obj-$(CONFIG_DE4X5) += de4x5.o + +# Declare multi-part drivers. +list-multi := tulip.o + +tulip-objs := eeprom.o interrupt.o media.o \ + timer.o tulip_core.o \ + 21142.o pnic.o pnic2.o include $(TOPDIR)/Rules.make + +# Link rules for multi-part drivers. +tulip.o: $(tulip-objs) + $(LD) -r -o $@ $(tulip-objs) diff -Nru a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/tulip/de2104x.c Thu Mar 7 18:17:40 2002 @@ -0,0 +1,2240 @@ +/* de2104x.c: A Linux PCI Ethernet driver for Intel/Digital 21040/1 chips. */ +/* + Copyright 2001 Jeff Garzik + + Copyright 1994, 1995 Digital Equipment Corporation. [de4x5.c] + Written/copyright 1994-2001 by Donald Becker. [tulip.c] + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. + + See the file COPYING in this distribution for more information. + + TODO, in rough priority order: + * Support forcing media type with a module parameter, + like dl2k.c/sundance.c + * Constants (module parms?) for Rx work limit + * Complete reset on PciErr + * Jumbo frames / dev->change_mtu + * Adjust Rx FIFO threshold and Max Rx DMA burst on Rx FIFO error + * Adjust Tx FIFO threshold and Max Tx DMA burst on Tx FIFO error + * Implement Tx software interrupt mitigation via + Tx descriptor bit + + */ + +#define DRV_NAME "de2104x" +#define DRV_VERSION "0.5.4" +#define DRV_RELDATE "Jan 1, 2002" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* These identify the driver base version and may not be removed. */ +static char version[] __initdata = +KERN_INFO DRV_NAME " PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; + +MODULE_AUTHOR("Jeff Garzik "); +MODULE_DESCRIPTION("Intel/Digital 21040/1 series PCI Ethernet driver"); +MODULE_LICENSE("GPL"); + +static int debug = -1; +MODULE_PARM (debug, "i"); +MODULE_PARM_DESC (debug, "de2104x bitmapped message enable number"); + +/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ +#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ + || defined(__sparc_) || defined(__ia64__) \ + || defined(__sh__) || defined(__mips__) +static int rx_copybreak = 1518; +#else +static int rx_copybreak = 100; +#endif +MODULE_PARM (rx_copybreak, "i"); +MODULE_PARM_DESC (rx_copybreak, "de2104x Breakpoint at which Rx packets are copied"); + +#define PFX DRV_NAME ": " + +#define DE_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_TIMER | \ + NETIF_MSG_IFDOWN | \ + NETIF_MSG_IFUP | \ + NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR) + +#define DE_RX_RING_SIZE 64 +#define DE_TX_RING_SIZE 64 +#define DE_RING_BYTES \ + ((sizeof(struct de_desc) * DE_RX_RING_SIZE) + \ + (sizeof(struct de_desc) * DE_TX_RING_SIZE)) +#define NEXT_TX(N) (((N) + 1) & (DE_TX_RING_SIZE - 1)) +#define NEXT_RX(N) (((N) + 1) & (DE_RX_RING_SIZE - 1)) +#define TX_BUFFS_AVAIL(CP) \ + (((CP)->tx_tail <= (CP)->tx_head) ? \ + (CP)->tx_tail + (DE_TX_RING_SIZE - 1) - (CP)->tx_head : \ + (CP)->tx_tail - (CP)->tx_head - 1) + +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +#define RX_OFFSET 2 + +#define DE_SETUP_SKB ((struct sk_buff *) 1) +#define DE_DUMMY_SKB ((struct sk_buff *) 2) +#define DE_SETUP_FRAME_WORDS 96 +#define DE_EEPROM_WORDS 256 +#define DE_EEPROM_SIZE (DE_EEPROM_WORDS * sizeof(u16)) +#define DE_MAX_MEDIA 5 + +#define DE_MEDIA_TP_AUTO 0 +#define DE_MEDIA_BNC 1 +#define DE_MEDIA_AUI 2 +#define DE_MEDIA_TP 3 +#define DE_MEDIA_TP_FD 4 +#define DE_MEDIA_INVALID DE_MAX_MEDIA +#define DE_MEDIA_FIRST 0 +#define DE_MEDIA_LAST (DE_MAX_MEDIA - 1) +#define DE_AUI_BNC (SUPPORTED_AUI | SUPPORTED_BNC) + +#define DE_TIMER_LINK (60 * HZ) +#define DE_TIMER_NO_LINK (5 * HZ) + +#define DE_NUM_REGS 16 +#define DE_REGS_SIZE (DE_NUM_REGS * sizeof(u32)) +#define DE_REGS_VER 1 + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (6*HZ) + +#define DE_UNALIGNED_16(a) (u16)(get_unaligned((u16 *)(a))) + +/* This is a mysterious value that can be written to CSR11 in the 21040 (only) + to support a pre-NWay full-duplex signaling mechanism using short frames. + No one knows what it should be, but if left at its default value some + 10base2(!) packets trigger a full-duplex-request interrupt. */ +#define FULL_DUPLEX_MAGIC 0x6969 + +enum { + /* NIC registers */ + BusMode = 0x00, + TxPoll = 0x08, + RxPoll = 0x10, + RxRingAddr = 0x18, + TxRingAddr = 0x20, + MacStatus = 0x28, + MacMode = 0x30, + IntrMask = 0x38, + RxMissed = 0x40, + ROMCmd = 0x48, + CSR11 = 0x58, + SIAStatus = 0x60, + CSR13 = 0x68, + CSR14 = 0x70, + CSR15 = 0x78, + PCIPM = 0x40, + + /* BusMode bits */ + CmdReset = (1 << 0), + CacheAlign16 = 0x00008000, + BurstLen4 = 0x00000400, + + /* Rx/TxPoll bits */ + NormalTxPoll = (1 << 0), + NormalRxPoll = (1 << 0), + + /* Tx/Rx descriptor status bits */ + DescOwn = (1 << 31), + RxError = (1 << 15), + RxErrLong = (1 << 7), + RxErrCRC = (1 << 1), + RxErrFIFO = (1 << 0), + RxErrRunt = (1 << 11), + RxErrFrame = (1 << 14), + RingEnd = (1 << 25), + FirstFrag = (1 << 29), + LastFrag = (1 << 30), + TxError = (1 << 15), + TxFIFOUnder = (1 << 1), + TxLinkFail = (1 << 2) | (1 << 10) | (1 << 11), + TxMaxCol = (1 << 8), + TxOWC = (1 << 9), + TxJabber = (1 << 14), + SetupFrame = (1 << 27), + TxSwInt = (1 << 31), + + /* MacStatus bits */ + IntrOK = (1 << 16), + IntrErr = (1 << 15), + RxIntr = (1 << 6), + RxEmpty = (1 << 7), + TxIntr = (1 << 0), + TxEmpty = (1 << 2), + PciErr = (1 << 13), + TxState = (1 << 22) | (1 << 21) | (1 << 20), + RxState = (1 << 19) | (1 << 18) | (1 << 17), + LinkFail = (1 << 12), + LinkPass = (1 << 4), + RxStopped = (1 << 8), + TxStopped = (1 << 1), + + /* MacMode bits */ + TxEnable = (1 << 13), + RxEnable = (1 << 1), + RxTx = TxEnable | RxEnable, + FullDuplex = (1 << 9), + AcceptAllMulticast = (1 << 7), + AcceptAllPhys = (1 << 6), + BOCnt = (1 << 5), + MacModeClear = (1<<12) | (1<<11) | (1<<10) | (1<<8) | (1<<3) | + RxTx | BOCnt | AcceptAllPhys | AcceptAllMulticast, + + /* ROMCmd bits */ + EE_SHIFT_CLK = 0x02, /* EEPROM shift clock. */ + EE_CS = 0x01, /* EEPROM chip select. */ + EE_DATA_WRITE = 0x04, /* Data from the Tulip to EEPROM. */ + EE_WRITE_0 = 0x01, + EE_WRITE_1 = 0x05, + EE_DATA_READ = 0x08, /* Data from the EEPROM chip. */ + EE_ENB = (0x4800 | EE_CS), + + /* The EEPROM commands include the alway-set leading bit. */ + EE_READ_CMD = 6, + + /* RxMissed bits */ + RxMissedOver = (1 << 16), + RxMissedMask = 0xffff, + + /* SROM-related bits */ + SROMC0InfoLeaf = 27, + MediaBlockMask = 0x3f, + MediaCustomCSRs = (1 << 6), + + /* PCIPM bits */ + PM_Sleep = (1 << 31), + PM_Snooze = (1 << 30), + PM_Mask = PM_Sleep | PM_Snooze, + + /* SIAStatus bits */ + NWayState = (1 << 14) | (1 << 13) | (1 << 12), + NWayRestart = (1 << 12), + NonselPortActive = (1 << 9), + LinkFailStatus = (1 << 2), + NetCxnErr = (1 << 1), +}; + +static const u32 de_intr_mask = + IntrOK | IntrErr | RxIntr | RxEmpty | TxIntr | TxEmpty | + LinkPass | LinkFail | PciErr; + +/* + * Set the programmable burst length to 4 longwords for all: + * DMA errors result without these values. Cache align 16 long. + */ +static const u32 de_bus_mode = CacheAlign16 | BurstLen4; + +struct de_srom_media_block { + u8 opts; + u16 csr13; + u16 csr14; + u16 csr15; +} __attribute__((packed)); + +struct de_srom_info_leaf { + u16 default_media; + u8 n_blocks; + u8 unused; +} __attribute__((packed)); + +struct de_desc { + u32 opts1; + u32 opts2; + u32 addr1; + u32 addr2; +}; + +struct media_info { + u16 type; /* DE_MEDIA_xxx */ + u16 csr13; + u16 csr14; + u16 csr15; +}; + +struct ring_info { + struct sk_buff *skb; + dma_addr_t mapping; +}; + +struct de_private { + unsigned tx_head; + unsigned tx_tail; + unsigned rx_tail; + + void *regs; + struct net_device *dev; + spinlock_t lock; + + struct de_desc *rx_ring; + struct de_desc *tx_ring; + struct ring_info tx_skb[DE_TX_RING_SIZE]; + struct ring_info rx_skb[DE_RX_RING_SIZE]; + unsigned rx_buf_sz; + dma_addr_t ring_dma; + + u32 msg_enable; + + struct net_device_stats net_stats; + + struct pci_dev *pdev; + u32 macmode; + + u16 setup_frame[DE_SETUP_FRAME_WORDS]; + + u32 media_type; + u32 media_supported; + u32 media_advertise; + struct media_info media[DE_MAX_MEDIA]; + struct timer_list media_timer; + + u8 *ee_data; + unsigned board_idx; + unsigned de21040 : 1; + unsigned media_lock : 1; +}; + + +static void de_set_rx_mode (struct net_device *dev); +static void de_tx (struct de_private *de); +static void de_clean_rings (struct de_private *de); +static void de_media_interrupt (struct de_private *de, u32 status); +static void de21040_media_timer (unsigned long data); +static void de21041_media_timer (unsigned long data); +static unsigned int de_ok_to_advertise (struct de_private *de, u32 new_media); + + +static struct pci_device_id de_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { }, +}; +MODULE_DEVICE_TABLE(pci, de_pci_tbl); + +static const char * const media_name[DE_MAX_MEDIA] = { + "10baseT auto", + "BNC", + "AUI", + "10baseT-HD", + "10baseT-FD" +}; + +/* 21040 transceiver register settings: + * TP AUTO(unused), BNC(unused), AUI, TP, TP FD*/ +static u16 t21040_csr13[] = { 0, 0, 0x8F09, 0x8F01, 0x8F01, }; +static u16 t21040_csr14[] = { 0, 0, 0x0705, 0xFFFF, 0xFFFD, }; +static u16 t21040_csr15[] = { 0, 0, 0x0006, 0x0000, 0x0000, }; + +/* 21041 transceiver register settings: TP AUTO, BNC, AUI, TP, TP FD*/ +static u16 t21041_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; +static u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x6F3F, 0x6F3D, }; +static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; + + +static inline unsigned long +msec_to_jiffies(unsigned long ms) +{ + return (((ms)*HZ+999)/1000); +} + + +#define dr32(reg) readl(de->regs + (reg)) +#define dw32(reg,val) writel((val), de->regs + (reg)) + + +static void de_rx_err_acct (struct de_private *de, unsigned rx_tail, + u32 status, u32 len) +{ + if (netif_msg_rx_err (de)) + printk (KERN_DEBUG + "%s: rx err, slot %d status 0x%x len %d\n", + de->dev->name, rx_tail, status, len); + + if ((status & 0x38000300) != 0x0300) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + if (netif_msg_rx_err(de)) + printk(KERN_WARNING "%s: Oversized Ethernet frame " + "spanned multiple buffers, status %8.8x!\n", + de->dev->name, status); + de->net_stats.rx_length_errors++; + } + } else if (status & RxError) { + /* There was a fatal error. */ + de->net_stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) de->net_stats.rx_length_errors++; + if (status & RxErrCRC) de->net_stats.rx_crc_errors++; + if (status & RxErrFIFO) de->net_stats.rx_fifo_errors++; + } +} + +static void de_rx (struct de_private *de) +{ + unsigned rx_tail = de->rx_tail; + unsigned rx_work = DE_RX_RING_SIZE; + unsigned drop = 0; + int rc; + + while (rx_work--) { + u32 status, len; + dma_addr_t mapping; + struct sk_buff *skb, *copy_skb; + unsigned copying_skb, buflen; + + skb = de->rx_skb[rx_tail].skb; + if (!skb) + BUG(); + rmb(); + status = le32_to_cpu(de->rx_ring[rx_tail].opts1); + if (status & DescOwn) + break; + + len = ((status >> 16) & 0x7ff) - 4; + mapping = de->rx_skb[rx_tail].mapping; + + if (unlikely(drop)) { + de->net_stats.rx_dropped++; + goto rx_next; + } + + if (unlikely((status & 0x38008300) != 0x0300)) { + de_rx_err_acct(de, rx_tail, status, len); + goto rx_next; + } + + copying_skb = (len <= rx_copybreak); + + if (unlikely(netif_msg_rx_status(de))) + printk(KERN_DEBUG "%s: rx slot %d status 0x%x len %d copying? %d\n", + de->dev->name, rx_tail, status, len, + copying_skb); + + buflen = copying_skb ? (len + RX_OFFSET) : de->rx_buf_sz; + copy_skb = dev_alloc_skb (buflen); + if (unlikely(!copy_skb)) { + de->net_stats.rx_dropped++; + drop = 1; + rx_work = 100; + goto rx_next; + } + copy_skb->dev = de->dev; + + if (!copying_skb) { + pci_unmap_single(de->pdev, mapping, + buflen, PCI_DMA_FROMDEVICE); + skb_put(skb, len); + + mapping = + de->rx_skb[rx_tail].mapping = + pci_map_single(de->pdev, copy_skb->tail, + buflen, PCI_DMA_FROMDEVICE); + de->rx_skb[rx_tail].skb = copy_skb; + } else { + pci_dma_sync_single(de->pdev, mapping, len, PCI_DMA_FROMDEVICE); + skb_reserve(copy_skb, RX_OFFSET); + memcpy(skb_put(copy_skb, len), skb->tail, len); + + /* We'll reuse the original ring buffer. */ + skb = copy_skb; + } + + skb->protocol = eth_type_trans (skb, de->dev); + + de->net_stats.rx_packets++; + de->net_stats.rx_bytes += skb->len; + de->dev->last_rx = jiffies; + rc = netif_rx (skb); + if (rc == NET_RX_DROP) + drop = 1; + +rx_next: + de->rx_ring[rx_tail].opts1 = cpu_to_le32(DescOwn); + if (rx_tail == (DE_RX_RING_SIZE - 1)) + de->rx_ring[rx_tail].opts2 = + cpu_to_le32(RingEnd | de->rx_buf_sz); + else + de->rx_ring[rx_tail].opts2 = cpu_to_le32(de->rx_buf_sz); + de->rx_ring[rx_tail].addr1 = cpu_to_le32(mapping); + rx_tail = NEXT_RX(rx_tail); + } + + if (!rx_work) + printk(KERN_WARNING "%s: rx work limit reached\n", de->dev->name); + + de->rx_tail = rx_tail; +} + +static void de_interrupt (int irq, void *dev_instance, struct pt_regs *regs) +{ + struct net_device *dev = dev_instance; + struct de_private *de = dev->priv; + u32 status; + + status = dr32(MacStatus); + if ((!(status & (IntrOK|IntrErr))) || (status == 0xFFFF)) + return; + + if (netif_msg_intr(de)) + printk(KERN_DEBUG "%s: intr, status %08x mode %08x desc %u/%u/%u\n", + dev->name, status, dr32(MacMode), de->rx_tail, de->tx_head, de->tx_tail); + + dw32(MacStatus, status); + + if (status & (RxIntr | RxEmpty)) { + de_rx(de); + if (status & RxEmpty) + dw32(RxPoll, NormalRxPoll); + } + + spin_lock(&de->lock); + + if (status & (TxIntr | TxEmpty)) + de_tx(de); + + if (status & (LinkPass | LinkFail)) + de_media_interrupt(de, status); + + spin_unlock(&de->lock); + + if (status & PciErr) { + u16 pci_status; + + pci_read_config_word(de->pdev, PCI_STATUS, &pci_status); + pci_write_config_word(de->pdev, PCI_STATUS, pci_status); + printk(KERN_ERR "%s: PCI bus error, status=%08x, PCI status=%04x\n", + dev->name, status, pci_status); + } +} + +static void de_tx (struct de_private *de) +{ + unsigned tx_head = de->tx_head; + unsigned tx_tail = de->tx_tail; + + while (tx_tail != tx_head) { + struct sk_buff *skb; + u32 status; + + rmb(); + status = le32_to_cpu(de->tx_ring[tx_tail].opts1); + if (status & DescOwn) + break; + + skb = de->tx_skb[tx_tail].skb; + if (!skb) + BUG(); + if (unlikely(skb == DE_DUMMY_SKB)) + goto next; + + if (unlikely(skb == DE_SETUP_SKB)) { + pci_unmap_single(de->pdev, de->tx_skb[tx_tail].mapping, + sizeof(de->setup_frame), PCI_DMA_TODEVICE); + goto next; + } + + pci_unmap_single(de->pdev, de->tx_skb[tx_tail].mapping, + skb->len, PCI_DMA_TODEVICE); + + if (status & LastFrag) { + if (status & TxError) { + if (netif_msg_tx_err(de)) + printk(KERN_DEBUG "%s: tx err, status 0x%x\n", + de->dev->name, status); + de->net_stats.tx_errors++; + if (status & TxOWC) + de->net_stats.tx_window_errors++; + if (status & TxMaxCol) + de->net_stats.tx_aborted_errors++; + if (status & TxLinkFail) + de->net_stats.tx_carrier_errors++; + if (status & TxFIFOUnder) + de->net_stats.tx_fifo_errors++; + } else { + de->net_stats.tx_packets++; + de->net_stats.tx_bytes += skb->len; + if (netif_msg_tx_done(de)) + printk(KERN_DEBUG "%s: tx done, slot %d\n", de->dev->name, tx_tail); + } + dev_kfree_skb_irq(skb); + } + +next: + de->tx_skb[tx_tail].skb = NULL; + + tx_tail = NEXT_TX(tx_tail); + } + + de->tx_tail = tx_tail; + + if (netif_queue_stopped(de->dev) && (TX_BUFFS_AVAIL(de) > (DE_TX_RING_SIZE / 4))) + netif_wake_queue(de->dev); +} + +static int de_start_xmit (struct sk_buff *skb, struct net_device *dev) +{ + struct de_private *de = dev->priv; + unsigned int entry, tx_free; + u32 mapping, len, flags = FirstFrag | LastFrag; + struct de_desc *txd; + + spin_lock_irq(&de->lock); + + tx_free = TX_BUFFS_AVAIL(de); + if (tx_free == 0) { + netif_stop_queue(dev); + spin_unlock_irq(&de->lock); + return 1; + } + tx_free--; + + entry = de->tx_head; + + txd = &de->tx_ring[entry]; + + len = skb->len; + mapping = pci_map_single(de->pdev, skb->data, len, PCI_DMA_TODEVICE); + if (entry == (DE_TX_RING_SIZE - 1)) + flags |= RingEnd; + if (!tx_free || (tx_free == (DE_TX_RING_SIZE / 2))) + flags |= TxSwInt; + flags |= len; + txd->opts2 = cpu_to_le32(flags); + txd->addr1 = cpu_to_le32(mapping); + + de->tx_skb[entry].skb = skb; + de->tx_skb[entry].mapping = mapping; + wmb(); + + txd->opts1 = cpu_to_le32(DescOwn); + wmb(); + + de->tx_head = NEXT_TX(entry); + if (netif_msg_tx_queued(de)) + printk(KERN_DEBUG "%s: tx queued, slot %d, skblen %d\n", + dev->name, entry, skb->len); + + if (tx_free == 0) + netif_stop_queue(dev); + + spin_unlock_irq(&de->lock); + + /* Trigger an immediate transmit demand. */ + dw32(TxPoll, NormalTxPoll); + dev->trans_start = jiffies; + + return 0; +} + +/* Set or clear the multicast filter for this adaptor. + Note that we only use exclusion around actually queueing the + new frame, not around filling de->setup_frame. This is non-deterministic + when re-entered but still correct. */ + +#undef set_bit_le +#define set_bit_le(i,p) do { ((char *)(p))[(i)/8] |= (1<<((i)%8)); } while(0) + +static void build_setup_frame_hash(u16 *setup_frm, struct net_device *dev) +{ + struct de_private *de = dev->priv; + u16 hash_table[32]; + struct dev_mc_list *mclist; + int i; + u16 *eaddrs; + + memset(hash_table, 0, sizeof(hash_table)); + set_bit_le(255, hash_table); /* Broadcast entry */ + /* This should work on big-endian machines as well. */ + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + int index = ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff; + + set_bit_le(index, hash_table); + + for (i = 0; i < 32; i++) { + *setup_frm++ = hash_table[i]; + *setup_frm++ = hash_table[i]; + } + setup_frm = &de->setup_frame[13*6]; + } + + /* Fill the final entry with our physical address. */ + eaddrs = (u16 *)dev->dev_addr; + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; +} + +static void build_setup_frame_perfect(u16 *setup_frm, struct net_device *dev) +{ + struct de_private *de = dev->priv; + struct dev_mc_list *mclist; + int i; + u16 *eaddrs; + + /* We have <= 14 addresses so we can use the wonderful + 16 address perfect filtering of the Tulip. */ + for (i = 0, mclist = dev->mc_list; i < dev->mc_count; + i++, mclist = mclist->next) { + eaddrs = (u16 *)mclist->dmi_addr; + *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; + *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; + *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; + } + /* Fill the unused entries with the broadcast address. */ + memset(setup_frm, 0xff, (15-i)*12); + setup_frm = &de->setup_frame[15*6]; + + /* Fill the final entry with our physical address. */ + eaddrs = (u16 *)dev->dev_addr; + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; +} + + +static void __de_set_rx_mode (struct net_device *dev) +{ + struct de_private *de = dev->priv; + u32 macmode; + unsigned int entry; + u32 mapping; + struct de_desc *txd; + struct de_desc *dummy_txd = NULL; + + macmode = de->macmode & ~(AcceptAllMulticast | AcceptAllPhys); + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + macmode |= AcceptAllMulticast | AcceptAllPhys; + goto out; + } + + if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter well -- accept all multicasts. */ + macmode |= AcceptAllMulticast; + goto out; + } + + /* Note that only the low-address shortword of setup_frame is valid! + The values are doubled for big-endian architectures. */ + if (dev->mc_count > 14) /* Must use a multicast hash table. */ + build_setup_frame_hash (de->setup_frame, dev); + else + build_setup_frame_perfect (de->setup_frame, dev); + + /* + * Now add this frame to the Tx list. + */ + + entry = de->tx_head; + + /* Avoid a chip errata by prefixing a dummy entry. */ + if (entry != 0) { + de->tx_skb[entry].skb = DE_DUMMY_SKB; + + dummy_txd = &de->tx_ring[entry]; + dummy_txd->opts2 = (entry == (DE_TX_RING_SIZE - 1)) ? + cpu_to_le32(RingEnd) : 0; + dummy_txd->addr1 = 0; + + /* Must set DescOwned later to avoid race with chip */ + + entry = NEXT_TX(entry); + } + + de->tx_skb[entry].skb = DE_SETUP_SKB; + de->tx_skb[entry].mapping = mapping = + pci_map_single (de->pdev, de->setup_frame, + sizeof (de->setup_frame), PCI_DMA_TODEVICE); + + /* Put the setup frame on the Tx list. */ + txd = &de->tx_ring[entry]; + if (entry == (DE_TX_RING_SIZE - 1)) + txd->opts2 = cpu_to_le32(SetupFrame | RingEnd | sizeof (de->setup_frame)); + else + txd->opts2 = cpu_to_le32(SetupFrame | sizeof (de->setup_frame)); + txd->addr1 = cpu_to_le32(mapping); + wmb(); + + txd->opts1 = cpu_to_le32(DescOwn); + wmb(); + + if (dummy_txd) { + dummy_txd->opts1 = cpu_to_le32(DescOwn); + wmb(); + } + + de->tx_head = NEXT_TX(entry); + + if (TX_BUFFS_AVAIL(de) < 0) + BUG(); + if (TX_BUFFS_AVAIL(de) == 0) + netif_stop_queue(dev); + + /* Trigger an immediate transmit demand. */ + dw32(TxPoll, NormalTxPoll); + +out: + if (macmode != de->macmode) { + dw32 (MacMode, macmode); + de->macmode = macmode; + } +} + +static void de_set_rx_mode (struct net_device *dev) +{ + unsigned long flags; + struct de_private *de = dev->priv; + + spin_lock_irqsave (&de->lock, flags); + __de_set_rx_mode(dev); + spin_unlock_irqrestore (&de->lock, flags); +} + +static inline void de_rx_missed(struct de_private *de, u32 rx_missed) +{ + if (unlikely(rx_missed & RxMissedOver)) + de->net_stats.rx_missed_errors += RxMissedMask; + else + de->net_stats.rx_missed_errors += (rx_missed & RxMissedMask); +} + +static void __de_get_stats(struct de_private *de) +{ + u32 tmp = dr32(RxMissed); /* self-clearing */ + + de_rx_missed(de, tmp); +} + +static struct net_device_stats *de_get_stats(struct net_device *dev) +{ + struct de_private *de = dev->priv; + + /* The chip only need report frame silently dropped. */ + spin_lock_irq(&de->lock); + if (netif_running(dev) && netif_device_present(dev)) + __de_get_stats(de); + spin_unlock_irq(&de->lock); + + return &de->net_stats; +} + +static inline int de_is_running (struct de_private *de) +{ + return (dr32(MacStatus) & (RxState | TxState)) ? 1 : 0; +} + +static void de_stop_rxtx (struct de_private *de) +{ + u32 macmode; + unsigned int work = 1000; + + macmode = dr32(MacMode); + if (macmode & RxTx) { + dw32(MacMode, macmode & ~RxTx); + dr32(MacMode); + } + + while (--work > 0) { + if (!de_is_running(de)) + return; + cpu_relax(); + } + + printk(KERN_WARNING "%s: timeout expired stopping DMA\n", de->dev->name); +} + +static inline void de_start_rxtx (struct de_private *de) +{ + u32 macmode; + + macmode = dr32(MacMode); + if ((macmode & RxTx) != RxTx) { + dw32(MacMode, macmode | RxTx); + dr32(MacMode); + } +} + +static void de_stop_hw (struct de_private *de) +{ + + udelay(5); + dw32(IntrMask, 0); + + de_stop_rxtx(de); + + dw32(MacStatus, dr32(MacStatus)); + + udelay(10); + + de->rx_tail = 0; + de->tx_head = de->tx_tail = 0; +} + +static void de_link_up(struct de_private *de) +{ + if (!netif_carrier_ok(de->dev)) { + netif_carrier_on(de->dev); + if (netif_msg_link(de)) + printk(KERN_INFO "%s: link up, media %s\n", + de->dev->name, media_name[de->media_type]); + } +} + +static void de_link_down(struct de_private *de) +{ + if (netif_carrier_ok(de->dev)) { + netif_carrier_off(de->dev); + if (netif_msg_link(de)) + printk(KERN_INFO "%s: link down\n", de->dev->name); + } +} + +static void de_set_media (struct de_private *de) +{ + unsigned media = de->media_type; + + if (de_is_running(de)) + BUG(); + + if (de->de21040) + dw32(CSR11, FULL_DUPLEX_MAGIC); + dw32(CSR13, 0); /* Reset phy */ + dw32(CSR14, de->media[media].csr14); + dw32(CSR15, de->media[media].csr15); + dw32(CSR13, de->media[media].csr13); + + /* must delay 10ms before writing to other registers, + * especially CSR6 + */ + mdelay(10); + + if (media == DE_MEDIA_TP_FD) + de->macmode |= FullDuplex; + else + de->macmode &= ~FullDuplex; + + if (netif_msg_link(de)) { + printk(KERN_INFO "%s: set link %s\n" + KERN_INFO "%s: mode 0x%x, sia 0x%x,0x%x,0x%x,0x%x\n" + KERN_INFO "%s: set mode 0x%x, set sia 0x%x,0x%x,0x%x\n", + de->dev->name, media_name[media], + de->dev->name, dr32(MacMode), dr32(SIAStatus), + dr32(CSR13), dr32(CSR14), dr32(CSR15), + de->dev->name, de->macmode, de->media[media].csr13, + de->media[media].csr14, de->media[media].csr15); + } +} + +static void de_next_media (struct de_private *de, u32 *media, + unsigned int n_media) +{ + unsigned int i; + + for (i = 0; i < n_media; i++) { + if (de_ok_to_advertise(de, media[i])) { + de->media_type = media[i]; + return; + } + } +} + +static void de21040_media_timer (unsigned long data) +{ + struct de_private *de = (struct de_private *) data; + struct net_device *dev = de->dev; + u32 status = dr32(SIAStatus); + unsigned int carrier; + unsigned long flags; + + carrier = (status & NetCxnErr) ? 0 : 1; + + if (carrier) { + if (de->media_type != DE_MEDIA_AUI && (status & LinkFailStatus)) + goto no_link_yet; + + de->media_timer.expires = jiffies + DE_TIMER_LINK; + add_timer(&de->media_timer); + if (!netif_carrier_ok(dev)) + de_link_up(de); + else + if (netif_msg_timer(de)) + printk(KERN_INFO "%s: %s link ok, status %x\n", + dev->name, media_name[de->media_type], + status); + return; + } + + de_link_down(de); + + if (de->media_lock) + return; + + if (de->media_type == DE_MEDIA_AUI) { + u32 next_state = DE_MEDIA_TP; + de_next_media(de, &next_state, 1); + } else { + u32 next_state = DE_MEDIA_AUI; + de_next_media(de, &next_state, 1); + } + + spin_lock_irqsave(&de->lock, flags); + de_stop_rxtx(de); + spin_unlock_irqrestore(&de->lock, flags); + de_set_media(de); + de_start_rxtx(de); + +no_link_yet: + de->media_timer.expires = jiffies + DE_TIMER_NO_LINK; + add_timer(&de->media_timer); + + if (netif_msg_timer(de)) + printk(KERN_INFO "%s: no link, trying media %s, status %x\n", + dev->name, media_name[de->media_type], status); +} + +static unsigned int de_ok_to_advertise (struct de_private *de, u32 new_media) +{ + switch (new_media) { + case DE_MEDIA_TP_AUTO: + if (!(de->media_advertise & ADVERTISED_Autoneg)) + return 0; + if (!(de->media_advertise & (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full))) + return 0; + break; + case DE_MEDIA_BNC: + if (!(de->media_advertise & ADVERTISED_BNC)) + return 0; + break; + case DE_MEDIA_AUI: + if (!(de->media_advertise & ADVERTISED_AUI)) + return 0; + break; + case DE_MEDIA_TP: + if (!(de->media_advertise & ADVERTISED_10baseT_Half)) + return 0; + break; + case DE_MEDIA_TP_FD: + if (!(de->media_advertise & ADVERTISED_10baseT_Full)) + return 0; + break; + } + + return 1; +} + +static void de21041_media_timer (unsigned long data) +{ + struct de_private *de = (struct de_private *) data; + struct net_device *dev = de->dev; + u32 status = dr32(SIAStatus); + unsigned int carrier; + unsigned long flags; + + carrier = (status & NetCxnErr) ? 0 : 1; + + if (carrier) { + if ((de->media_type == DE_MEDIA_TP_AUTO || + de->media_type == DE_MEDIA_TP || + de->media_type == DE_MEDIA_TP_FD) && + (status & LinkFailStatus)) + goto no_link_yet; + + de->media_timer.expires = jiffies + DE_TIMER_LINK; + add_timer(&de->media_timer); + if (!netif_carrier_ok(dev)) + de_link_up(de); + else + if (netif_msg_timer(de)) + printk(KERN_INFO "%s: %s link ok, mode %x status %x\n", + dev->name, media_name[de->media_type], + dr32(MacMode), status); + return; + } + + de_link_down(de); + + /* if media type locked, don't switch media */ + if (de->media_lock) + goto set_media; + + /* if activity detected, use that as hint for new media type */ + if (status & NonselPortActive) { + unsigned int have_media = 1; + + /* if AUI/BNC selected, then activity is on TP port */ + if (de->media_type == DE_MEDIA_AUI || + de->media_type == DE_MEDIA_BNC) { + if (de_ok_to_advertise(de, DE_MEDIA_TP_AUTO)) + de->media_type = DE_MEDIA_TP_AUTO; + else + have_media = 0; + } + + /* TP selected. If there is only TP and BNC, then it's BNC */ + else if (((de->media_supported & DE_AUI_BNC) == SUPPORTED_BNC) && + de_ok_to_advertise(de, DE_MEDIA_BNC)) + de->media_type = DE_MEDIA_BNC; + + /* TP selected. If there is only TP and AUI, then it's AUI */ + else if (((de->media_supported & DE_AUI_BNC) == SUPPORTED_AUI) && + de_ok_to_advertise(de, DE_MEDIA_AUI)) + de->media_type = DE_MEDIA_AUI; + + /* otherwise, ignore the hint */ + else + have_media = 0; + + if (have_media) + goto set_media; + } + + /* + * Absent or ambiguous activity hint, move to next advertised + * media state. If de->media_type is left unchanged, this + * simply resets the PHY and reloads the current media settings. + */ + if (de->media_type == DE_MEDIA_AUI) { + u32 next_states[] = { DE_MEDIA_BNC, DE_MEDIA_TP_AUTO }; + de_next_media(de, next_states, ARRAY_SIZE(next_states)); + } else if (de->media_type == DE_MEDIA_BNC) { + u32 next_states[] = { DE_MEDIA_TP_AUTO, DE_MEDIA_AUI }; + de_next_media(de, next_states, ARRAY_SIZE(next_states)); + } else { + u32 next_states[] = { DE_MEDIA_AUI, DE_MEDIA_BNC, DE_MEDIA_TP_AUTO }; + de_next_media(de, next_states, ARRAY_SIZE(next_states)); + } + +set_media: + spin_lock_irqsave(&de->lock, flags); + de_stop_rxtx(de); + spin_unlock_irqrestore(&de->lock, flags); + de_set_media(de); + de_start_rxtx(de); + +no_link_yet: + de->media_timer.expires = jiffies + DE_TIMER_NO_LINK; + add_timer(&de->media_timer); + + if (netif_msg_timer(de)) + printk(KERN_INFO "%s: no link, trying media %s, status %x\n", + dev->name, media_name[de->media_type], status); +} + +static void de_media_interrupt (struct de_private *de, u32 status) +{ + if (status & LinkPass) { + de_link_up(de); + mod_timer(&de->media_timer, jiffies + DE_TIMER_LINK); + return; + } + + if (!(status & LinkFail)) + BUG(); + + if (netif_carrier_ok(de->dev)) { + de_link_down(de); + mod_timer(&de->media_timer, jiffies + DE_TIMER_NO_LINK); + } +} + +static int de_reset_mac (struct de_private *de) +{ + u32 status, tmp; + + /* + * Reset MAC. Copied from de4x5.c. + */ + + tmp = dr32 (BusMode); + if (tmp == 0xffffffff) + return -ENODEV; + mdelay (1); + + dw32 (BusMode, tmp | CmdReset); + mdelay (1); + + dw32 (BusMode, tmp); + mdelay (1); + + for (tmp = 0; tmp < 5; tmp++) { + dr32 (BusMode); + mdelay (1); + } + + mdelay (1); + + status = dr32(MacStatus); + if (status & (RxState | TxState)) + return -EBUSY; + if (status == 0xffffffff) + return -ENODEV; + return 0; +} + +static void de_adapter_wake (struct de_private *de) +{ + u32 pmctl; + + if (de->de21040) + return; + + pci_read_config_dword(de->pdev, PCIPM, &pmctl); + if (pmctl & PM_Mask) { + pmctl &= ~PM_Mask; + pci_write_config_dword(de->pdev, PCIPM, pmctl); + + /* de4x5.c delays, so we do too */ + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(msec_to_jiffies(10)); + } +} + +static void de_adapter_sleep (struct de_private *de) +{ + u32 pmctl; + + if (de->de21040) + return; + + pci_read_config_dword(de->pdev, PCIPM, &pmctl); + pmctl |= PM_Sleep; + pci_write_config_dword(de->pdev, PCIPM, pmctl); +} + +static int de_init_hw (struct de_private *de) +{ + struct net_device *dev = de->dev; + int rc; + + de_adapter_wake(de); + + de->macmode = dr32(MacMode) & ~MacModeClear; + + rc = de_reset_mac(de); + if (rc) + return rc; + + de_set_media(de); /* reset phy */ + + dw32(RxRingAddr, de->ring_dma); + dw32(TxRingAddr, de->ring_dma + (sizeof(struct de_desc) * DE_RX_RING_SIZE)); + + dw32(MacMode, RxTx | de->macmode); + + dr32(RxMissed); /* self-clearing */ + + dw32(IntrMask, de_intr_mask); + + de_set_rx_mode(dev); + + return 0; +} + +static int de_refill_rx (struct de_private *de) +{ + unsigned i; + + for (i = 0; i < DE_RX_RING_SIZE; i++) { + struct sk_buff *skb; + + skb = dev_alloc_skb(de->rx_buf_sz); + if (!skb) + goto err_out; + + skb->dev = de->dev; + + de->rx_skb[i].mapping = pci_map_single(de->pdev, + skb->tail, de->rx_buf_sz, PCI_DMA_FROMDEVICE); + de->rx_skb[i].skb = skb; + + de->rx_ring[i].opts1 = cpu_to_le32(DescOwn); + if (i == (DE_RX_RING_SIZE - 1)) + de->rx_ring[i].opts2 = + cpu_to_le32(RingEnd | de->rx_buf_sz); + else + de->rx_ring[i].opts2 = cpu_to_le32(de->rx_buf_sz); + de->rx_ring[i].addr1 = cpu_to_le32(de->rx_skb[i].mapping); + de->rx_ring[i].addr2 = 0; + } + + return 0; + +err_out: + de_clean_rings(de); + return -ENOMEM; +} + +static int de_init_rings (struct de_private *de) +{ + memset(de->tx_ring, 0, sizeof(struct de_desc) * DE_TX_RING_SIZE); + de->tx_ring[DE_TX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); + + de->rx_tail = 0; + de->tx_head = de->tx_tail = 0; + + return de_refill_rx (de); +} + +static int de_alloc_rings (struct de_private *de) +{ + de->rx_ring = pci_alloc_consistent(de->pdev, DE_RING_BYTES, &de->ring_dma); + if (!de->rx_ring) + return -ENOMEM; + de->tx_ring = &de->rx_ring[DE_RX_RING_SIZE]; + return de_init_rings(de); +} + +static void de_clean_rings (struct de_private *de) +{ + unsigned i; + + memset(de->rx_ring, 0, sizeof(struct de_desc) * DE_RX_RING_SIZE); + de->rx_ring[DE_RX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); + wmb(); + memset(de->tx_ring, 0, sizeof(struct de_desc) * DE_TX_RING_SIZE); + de->tx_ring[DE_TX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); + wmb(); + + for (i = 0; i < DE_RX_RING_SIZE; i++) { + if (de->rx_skb[i].skb) { + pci_unmap_single(de->pdev, de->rx_skb[i].mapping, + de->rx_buf_sz, PCI_DMA_FROMDEVICE); + dev_kfree_skb(de->rx_skb[i].skb); + } + } + + for (i = 0; i < DE_TX_RING_SIZE; i++) { + struct sk_buff *skb = de->tx_skb[i].skb; + if ((skb) && (skb != DE_DUMMY_SKB)) { + if (skb != DE_SETUP_SKB) { + dev_kfree_skb(skb); + de->net_stats.tx_dropped++; + pci_unmap_single(de->pdev, + de->tx_skb[i].mapping, + skb->len, PCI_DMA_TODEVICE); + } else { + pci_unmap_single(de->pdev, + de->tx_skb[i].mapping, + sizeof(de->setup_frame), + PCI_DMA_TODEVICE); + } + } + } + + memset(&de->rx_skb, 0, sizeof(struct ring_info) * DE_RX_RING_SIZE); + memset(&de->tx_skb, 0, sizeof(struct ring_info) * DE_TX_RING_SIZE); +} + +static void de_free_rings (struct de_private *de) +{ + de_clean_rings(de); + pci_free_consistent(de->pdev, DE_RING_BYTES, de->rx_ring, de->ring_dma); + de->rx_ring = NULL; + de->tx_ring = NULL; +} + +static int de_open (struct net_device *dev) +{ + struct de_private *de = dev->priv; + int rc; + unsigned long flags; + + if (netif_msg_ifup(de)) + printk(KERN_DEBUG "%s: enabling interface\n", dev->name); + + de->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); + + rc = de_alloc_rings(de); + if (rc) { + printk(KERN_ERR "%s: ring allocation failure, err=%d\n", + dev->name, rc); + return rc; + } + + rc = de_init_hw(de); + if (rc) { + printk(KERN_ERR "%s: h/w init failure, err=%d\n", + dev->name, rc); + goto err_out_free; + } + + rc = request_irq(dev->irq, de_interrupt, SA_SHIRQ, dev->name, dev); + if (rc) { + printk(KERN_ERR "%s: IRQ %d request failure, err=%d\n", + dev->name, dev->irq, rc); + goto err_out_hw; + } + + netif_start_queue(dev); + mod_timer(&de->media_timer, jiffies + DE_TIMER_NO_LINK); + + return 0; + +err_out_hw: + spin_lock_irqsave(&de->lock, flags); + de_stop_hw(de); + spin_unlock_irqrestore(&de->lock, flags); + +err_out_free: + de_free_rings(de); + return rc; +} + +static int de_close (struct net_device *dev) +{ + struct de_private *de = dev->priv; + unsigned long flags; + + if (netif_msg_ifdown(de)) + printk(KERN_DEBUG "%s: disabling interface\n", dev->name); + + del_timer_sync(&de->media_timer); + + spin_lock_irqsave(&de->lock, flags); + de_stop_hw(de); + netif_stop_queue(dev); + netif_carrier_off(dev); + spin_unlock_irqrestore(&de->lock, flags); + + free_irq(dev->irq, dev); + + de_free_rings(de); + de_adapter_sleep(de); + pci_disable_device(de->pdev); + return 0; +} + +static void de_tx_timeout (struct net_device *dev) +{ + struct de_private *de = dev->priv; + + printk(KERN_DEBUG "%s: NIC status %08x mode %08x sia %08x desc %u/%u/%u\n", + dev->name, dr32(MacStatus), dr32(MacMode), dr32(SIAStatus), + de->rx_tail, de->tx_head, de->tx_tail); + + del_timer_sync(&de->media_timer); + + disable_irq(dev->irq); + spin_lock_irq(&de->lock); + + de_stop_hw(de); + netif_stop_queue(dev); + netif_carrier_off(dev); + + spin_unlock_irq(&de->lock); + enable_irq(dev->irq); + + /* Update the error counts. */ + __de_get_stats(de); + + synchronize_irq(); + de_clean_rings(de); + + de_init_hw(de); + + netif_wake_queue(dev); +} + +static int de_get_regs(struct de_private *de, u8 *buf) +{ + int i; + u32 *rbuf = (u32 *)buf; + + /* read all CSRs */ + for (i = 0; i < DE_NUM_REGS; i++) + rbuf[i] = dr32(i * 8); + + /* handle self-clearing RxMissed counter, CSR8 */ + de_rx_missed(de, rbuf[8]); + + return 0; +} + +static int de_ethtool_gset(struct de_private *de, struct ethtool_cmd *ecmd) +{ + ecmd->supported = de->media_supported; + ecmd->transceiver = XCVR_INTERNAL; + ecmd->phy_address = 0; + ecmd->advertising = de->media_advertise; + + switch (de->media_type) { + case DE_MEDIA_AUI: + ecmd->port = PORT_AUI; + ecmd->speed = 5; + break; + case DE_MEDIA_BNC: + ecmd->port = PORT_BNC; + ecmd->speed = 2; + break; + default: + ecmd->port = PORT_TP; + ecmd->speed = SPEED_10; + break; + } + + if (de->macmode & FullDuplex) + ecmd->duplex = DUPLEX_FULL; + else + ecmd->duplex = DUPLEX_HALF; + + if (de->media_lock) + ecmd->autoneg = AUTONEG_DISABLE; + else + ecmd->autoneg = AUTONEG_ENABLE; + + /* ignore maxtxpkt, maxrxpkt for now */ + + return 0; +} + +static int de_ethtool_sset(struct de_private *de, struct ethtool_cmd *ecmd) +{ + u32 new_media; + unsigned int media_lock; + + if (ecmd->speed != SPEED_10 && ecmd->speed != 5 && ecmd->speed != 2) + return -EINVAL; + if (de->de21040 && ecmd->speed == 2) + return -EINVAL; + if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL) + return -EINVAL; + if (ecmd->port != PORT_TP && ecmd->port != PORT_AUI && ecmd->port != PORT_BNC) + return -EINVAL; + if (de->de21040 && ecmd->port == PORT_BNC) + return -EINVAL; + if (ecmd->transceiver != XCVR_INTERNAL) + return -EINVAL; + if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE) + return -EINVAL; + if (ecmd->advertising & ~de->media_supported) + return -EINVAL; + if (ecmd->autoneg == AUTONEG_ENABLE && + (!(ecmd->advertising & ADVERTISED_Autoneg))) + return -EINVAL; + + switch (ecmd->port) { + case PORT_AUI: + new_media = DE_MEDIA_AUI; + if (!(ecmd->advertising & ADVERTISED_AUI)) + return -EINVAL; + break; + case PORT_BNC: + new_media = DE_MEDIA_BNC; + if (!(ecmd->advertising & ADVERTISED_BNC)) + return -EINVAL; + break; + default: + if (ecmd->autoneg == AUTONEG_ENABLE) + new_media = DE_MEDIA_TP_AUTO; + else if (ecmd->duplex == DUPLEX_FULL) + new_media = DE_MEDIA_TP_FD; + else + new_media = DE_MEDIA_TP; + if (!(ecmd->advertising & ADVERTISED_TP)) + return -EINVAL; + if (!(ecmd->advertising & (ADVERTISED_10baseT_Full | ADVERTISED_10baseT_Half))) + return -EINVAL; + break; + } + + media_lock = (ecmd->autoneg == AUTONEG_ENABLE) ? 0 : 1; + + if ((new_media == de->media_type) && + (media_lock == de->media_lock) && + (ecmd->advertising == de->media_advertise)) + return 0; /* nothing to change */ + + de_link_down(de); + de_stop_rxtx(de); + + de->media_type = new_media; + de->media_lock = media_lock; + de->media_advertise = ecmd->advertising; + de_set_media(de); + + return 0; +} + +static int de_ethtool_ioctl (struct de_private *de, void *useraddr) +{ + u32 ethcmd; + + /* dev_ioctl() in ../../net/core/dev.c has already checked + capable(CAP_NET_ADMIN), so don't bother with that here. */ + + if (get_user(ethcmd, (u32 *)useraddr)) + return -EFAULT; + + switch (ethcmd) { + + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strcpy (info.driver, DRV_NAME); + strcpy (info.version, DRV_VERSION); + strcpy (info.bus_info, de->pdev->slot_name); + info.eedump_len = DE_EEPROM_SIZE; + info.regdump_len = DE_REGS_SIZE; + if (copy_to_user (useraddr, &info, sizeof (info))) + return -EFAULT; + return 0; + } + + /* get settings */ + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd = { ETHTOOL_GSET }; + spin_lock_irq(&de->lock); + de_ethtool_gset(de, &ecmd); + spin_unlock_irq(&de->lock); + if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + } + /* set settings */ + case ETHTOOL_SSET: { + struct ethtool_cmd ecmd; + int r; + if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) + return -EFAULT; + spin_lock_irq(&de->lock); + r = de_ethtool_sset(de, &ecmd); + spin_unlock_irq(&de->lock); + return r; + } + + /* restart autonegotiation */ + case ETHTOOL_NWAY_RST: { + u32 status; + + if (de->media_type != DE_MEDIA_TP_AUTO) + return -EINVAL; + if (netif_carrier_ok(de->dev)) + de_link_down(de); + + status = dr32(SIAStatus); + dw32(SIAStatus, (status & ~NWayState) | NWayRestart); + if (netif_msg_link(de)) + printk(KERN_INFO "%s: link nway restart, status %x,%x\n", + de->dev->name, status, dr32(SIAStatus)); + return 0; + } + + /* get link status */ + case ETHTOOL_GLINK: { + struct ethtool_value edata = {ETHTOOL_GLINK}; + edata.data = (netif_carrier_ok(de->dev)) ? 1 : 0; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + + /* get message-level */ + case ETHTOOL_GMSGLVL: { + struct ethtool_value edata = {ETHTOOL_GMSGLVL}; + edata.data = de->msg_enable; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + /* set message-level */ + case ETHTOOL_SMSGLVL: { + struct ethtool_value edata; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + de->msg_enable = edata.data; + return 0; + } + + /* get registers */ + case ETHTOOL_GREGS: { + struct ethtool_regs regs; + u8 regbuf[DE_REGS_SIZE]; + int r; + + if (copy_from_user(®s, useraddr, sizeof(regs))) + return -EFAULT; + + if (regs.len > DE_REGS_SIZE) { + regs.len = DE_REGS_SIZE; + } + regs.version = (DE_REGS_VER << 2) | de->de21040; + if (copy_to_user(useraddr, ®s, sizeof(regs))) + return -EFAULT; + + useraddr += offsetof(struct ethtool_regs, data); + + spin_lock_irq(&de->lock); + r = de_get_regs(de, regbuf); + spin_unlock_irq(&de->lock); + + if (r) + return r; + if (copy_to_user(useraddr, regbuf, regs.len)) + return -EFAULT; + return 0; + } + + /* get SROM dump */ + case ETHTOOL_GEEPROM: { + struct ethtool_eeprom eeprom; + + if (!de->ee_data) + break; + if (copy_from_user(&eeprom, useraddr, sizeof(eeprom))) + return -EFAULT; + if ((eeprom.offset != 0) || (eeprom.magic != 0) || + (eeprom.len != DE_EEPROM_SIZE)) + return -EINVAL; + + useraddr += offsetof(struct ethtool_regs, data); + if (copy_to_user(useraddr, de->ee_data, DE_EEPROM_SIZE)) + return -EFAULT; + } + + default: + break; + } + + return -EOPNOTSUPP; +} + + +static int de_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct de_private *de = dev->priv; + int rc = 0; + + if (!netif_running(dev)) + return -EINVAL; + + switch (cmd) { + case SIOCETHTOOL: + return de_ethtool_ioctl(de, (void *) rq->ifr_data); + + default: + rc = -EOPNOTSUPP; + break; + } + + return rc; +} + +static void __init de21040_get_mac_address (struct de_private *de) +{ + unsigned i; + + dw32 (ROMCmd, 0); /* Reset the pointer with a dummy write. */ + + for (i = 0; i < 6; i++) { + int value, boguscnt = 100000; + do + value = dr32(ROMCmd); + while (value < 0 && --boguscnt > 0); + de->dev->dev_addr[i] = value; + if (boguscnt <= 0) + printk(KERN_WARNING PFX "timeout reading 21040 MAC address byte %u\n", i); + } +} + +static void __init de21040_get_media_info(struct de_private *de) +{ + unsigned int i; + + de->media_type = DE_MEDIA_TP; + de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Full | + SUPPORTED_10baseT_Half | SUPPORTED_AUI; + de->media_advertise = de->media_supported; + + for (i = 0; i < DE_MAX_MEDIA; i++) { + switch (i) { + case DE_MEDIA_AUI: + case DE_MEDIA_TP: + case DE_MEDIA_TP_FD: + de->media[i].type = i; + de->media[i].csr13 = t21040_csr13[i]; + de->media[i].csr14 = t21040_csr14[i]; + de->media[i].csr15 = t21040_csr15[i]; + break; + default: + de->media[i].type = DE_MEDIA_INVALID; + break; + } + } +} + +/* Note: this routine returns extra data bits for size detection. */ +static unsigned __init tulip_read_eeprom(void *regs, int location, int addr_len) +{ + int i; + unsigned retval = 0; + void *ee_addr = regs + ROMCmd; + int read_cmd = location | (EE_READ_CMD << addr_len); + + writel(EE_ENB & ~EE_CS, ee_addr); + writel(EE_ENB, ee_addr); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + writel(EE_ENB | dataval, ee_addr); + readl(ee_addr); + writel(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + readl(ee_addr); + retval = (retval << 1) | ((readl(ee_addr) & EE_DATA_READ) ? 1 : 0); + } + writel(EE_ENB, ee_addr); + readl(ee_addr); + + for (i = 16; i > 0; i--) { + writel(EE_ENB | EE_SHIFT_CLK, ee_addr); + readl(ee_addr); + retval = (retval << 1) | ((readl(ee_addr) & EE_DATA_READ) ? 1 : 0); + writel(EE_ENB, ee_addr); + readl(ee_addr); + } + + /* Terminate the EEPROM access. */ + writel(EE_ENB & ~EE_CS, ee_addr); + return retval; +} + +static void __init de21041_get_srom_info (struct de_private *de) +{ + unsigned i, sa_offset = 0, ofs; + u8 ee_data[DE_EEPROM_SIZE + 6] = {}; + unsigned ee_addr_size = tulip_read_eeprom(de->regs, 0xff, 8) & 0x40000 ? 8 : 6; + struct de_srom_info_leaf *il; + void *bufp; + + /* download entire eeprom */ + for (i = 0; i < DE_EEPROM_WORDS; i++) + ((u16 *)ee_data)[i] = + le16_to_cpu(tulip_read_eeprom(de->regs, i, ee_addr_size)); + + /* DEC now has a specification but early board makers + just put the address in the first EEPROM locations. */ + /* This does memcmp(eedata, eedata+16, 8) */ + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + sa_offset = 20; + + /* store MAC address */ + for (i = 0; i < 6; i ++) + de->dev->dev_addr[i] = ee_data[i + sa_offset]; + + /* get offset of controller 0 info leaf. ignore 2nd byte. */ + ofs = ee_data[SROMC0InfoLeaf]; + if (ofs >= (sizeof(ee_data) - sizeof(struct de_srom_info_leaf) - sizeof(struct de_srom_media_block))) + goto bad_srom; + + /* get pointer to info leaf */ + il = (struct de_srom_info_leaf *) &ee_data[ofs]; + + /* paranoia checks */ + if (il->n_blocks == 0) + goto bad_srom; + if ((sizeof(ee_data) - ofs) < + (sizeof(struct de_srom_info_leaf) + (sizeof(struct de_srom_media_block) * il->n_blocks))) + goto bad_srom; + + /* get default media type */ + switch (DE_UNALIGNED_16(&il->default_media)) { + case 0x0001: de->media_type = DE_MEDIA_BNC; break; + case 0x0002: de->media_type = DE_MEDIA_AUI; break; + case 0x0204: de->media_type = DE_MEDIA_TP_FD; break; + default: de->media_type = DE_MEDIA_TP_AUTO; break; + } + + if (netif_msg_probe(de)) + printk(KERN_INFO "de%d: SROM leaf offset %u, default media %s\n", + de->board_idx, ofs, + media_name[de->media_type]); + + /* init SIA register values to defaults */ + for (i = 0; i < DE_MAX_MEDIA; i++) { + de->media[i].type = DE_MEDIA_INVALID; + de->media[i].csr13 = 0xffff; + de->media[i].csr14 = 0xffff; + de->media[i].csr15 = 0xffff; + } + + /* parse media blocks to see what medias are supported, + * and if any custom CSR values are provided + */ + bufp = ((void *)il) + sizeof(*il); + for (i = 0; i < il->n_blocks; i++) { + struct de_srom_media_block *ib = bufp; + unsigned idx; + + /* index based on media type in media block */ + switch(ib->opts & MediaBlockMask) { + case 0: /* 10baseT */ + de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half + | SUPPORTED_Autoneg; + idx = DE_MEDIA_TP; + de->media[DE_MEDIA_TP_AUTO].type = DE_MEDIA_TP_AUTO; + break; + case 1: /* BNC */ + de->media_supported |= SUPPORTED_BNC; + idx = DE_MEDIA_BNC; + break; + case 2: /* AUI */ + de->media_supported |= SUPPORTED_AUI; + idx = DE_MEDIA_AUI; + break; + case 4: /* 10baseT-FD */ + de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Full + | SUPPORTED_Autoneg; + idx = DE_MEDIA_TP_FD; + de->media[DE_MEDIA_TP_AUTO].type = DE_MEDIA_TP_AUTO; + break; + default: + goto bad_srom; + } + + de->media[idx].type = idx; + + if (netif_msg_probe(de)) + printk(KERN_INFO "de%d: media block #%u: %s", + de->board_idx, i, + media_name[de->media[idx].type]); + + bufp += sizeof (ib->opts); + + if (ib->opts & MediaCustomCSRs) { + de->media[idx].csr13 = DE_UNALIGNED_16(&ib->csr13); + de->media[idx].csr14 = DE_UNALIGNED_16(&ib->csr14); + de->media[idx].csr15 = DE_UNALIGNED_16(&ib->csr15); + bufp += sizeof(ib->csr13) + sizeof(ib->csr14) + + sizeof(ib->csr15); + + if (netif_msg_probe(de)) + printk(" (%x,%x,%x)\n", + de->media[idx].csr13, + de->media[idx].csr14, + de->media[idx].csr15); + + } else if (netif_msg_probe(de)) + printk("\n"); + + if (bufp > ((void *)&ee_data[DE_EEPROM_SIZE - 3])) + break; + } + + de->media_advertise = de->media_supported; + +fill_defaults: + /* fill in defaults, for cases where custom CSRs not used */ + for (i = 0; i < DE_MAX_MEDIA; i++) { + if (de->media[i].csr13 == 0xffff) + de->media[i].csr13 = t21041_csr13[i]; + if (de->media[i].csr14 == 0xffff) + de->media[i].csr14 = t21041_csr14[i]; + if (de->media[i].csr15 == 0xffff) + de->media[i].csr15 = t21041_csr15[i]; + } + + de->ee_data = kmalloc(DE_EEPROM_SIZE, GFP_KERNEL); + if (de->ee_data) + memcpy(de->ee_data, &ee_data[0], DE_EEPROM_SIZE); + + return; + +bad_srom: + /* for error cases, it's ok to assume we support all these */ + for (i = 0; i < DE_MAX_MEDIA; i++) + de->media[i].type = i; + de->media_supported = + SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP | + SUPPORTED_AUI | + SUPPORTED_BNC; + goto fill_defaults; +} + +static int __init de_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev; + struct de_private *de; + int rc; + void *regs; + long pciaddr; + static int board_idx = -1; + + board_idx++; + +#ifndef MODULE + if (board_idx == 0) + printk("%s", version); +#endif + + /* allocate a new ethernet device structure, and fill in defaults */ + dev = alloc_etherdev(sizeof(struct de_private)); + if (!dev) + return -ENOMEM; + + SET_MODULE_OWNER(dev); + dev->open = de_open; + dev->stop = de_close; + dev->set_multicast_list = de_set_rx_mode; + dev->hard_start_xmit = de_start_xmit; + dev->get_stats = de_get_stats; + dev->do_ioctl = de_ioctl; + dev->tx_timeout = de_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + + dev->irq = pdev->irq; + + de = dev->priv; + de->de21040 = ent->driver_data == 0 ? 1 : 0; + de->pdev = pdev; + de->dev = dev; + de->msg_enable = (debug < 0 ? DE_DEF_MSG_ENABLE : debug); + de->board_idx = board_idx; + spin_lock_init (&de->lock); + init_timer(&de->media_timer); + if (de->de21040) + de->media_timer.function = de21040_media_timer; + else + de->media_timer.function = de21041_media_timer; + de->media_timer.data = (unsigned long) de; + + netif_carrier_off(dev); + netif_stop_queue(dev); + + /* wake up device, assign resources */ + rc = pci_enable_device(pdev); + if (rc) + goto err_out_free; + + /* reserve PCI resources to ensure driver atomicity */ + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out_disable; + + /* check for invalid IRQ value */ + if (pdev->irq < 2) { + rc = -EIO; + printk(KERN_ERR PFX "invalid irq (%d) for pci dev %s\n", + pdev->irq, pdev->slot_name); + goto err_out_res; + } + + /* obtain and check validity of PCI I/O address */ + pciaddr = pci_resource_start(pdev, 1); + if (!pciaddr) { + rc = -EIO; + printk(KERN_ERR PFX "no MMIO resource for pci dev %s\n", + pdev->slot_name); + goto err_out_res; + } + if (pci_resource_len(pdev, 1) < DE_REGS_SIZE) { + rc = -EIO; + printk(KERN_ERR PFX "MMIO resource (%lx) too small on pci dev %s\n", + pci_resource_len(pdev, 1), pdev->slot_name); + goto err_out_res; + } + + /* remap CSR registers */ + regs = ioremap_nocache(pciaddr, DE_REGS_SIZE); + if (!regs) { + rc = -EIO; + printk(KERN_ERR PFX "Cannot map PCI MMIO (%lx@%lx) on pci dev %s\n", + pci_resource_len(pdev, 1), pciaddr, pdev->slot_name); + goto err_out_res; + } + dev->base_addr = (unsigned long) regs; + de->regs = regs; + + de_adapter_wake(de); + + /* make sure hardware is not running */ + rc = de_reset_mac(de); + if (rc) { + printk(KERN_ERR PFX "Cannot reset MAC, pci dev %s\n", + pdev->slot_name); + goto err_out_iomap; + } + + /* get MAC address, initialize default media type and + * get list of supported media + */ + if (de->de21040) { + de21040_get_mac_address(de); + de21040_get_media_info(de); + } else { + de21041_get_srom_info(de); + } + + /* register new network interface with kernel */ + rc = register_netdev(dev); + if (rc) + goto err_out_iomap; + + /* print info about board and interface just registered */ + printk (KERN_INFO "%s: %s at 0x%lx, " + "%02x:%02x:%02x:%02x:%02x:%02x, " + "IRQ %d\n", + dev->name, + de->de21040 ? "21040" : "21041", + dev->base_addr, + dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5], + dev->irq); + + pci_set_drvdata(pdev, dev); + + /* enable busmastering */ + pci_set_master(pdev); + + /* put adapter to sleep */ + de_adapter_sleep(de); + + return 0; + +err_out_iomap: + if (de->ee_data) + kfree(de->ee_data); + iounmap(regs); +err_out_res: + pci_release_regions(pdev); +err_out_disable: + pci_disable_device(pdev); +err_out_free: + kfree(dev); + return rc; +} + +static void __exit de_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct de_private *de = dev->priv; + + if (!dev) + BUG(); + unregister_netdev(dev); + if (de->ee_data) + kfree(de->ee_data); + iounmap(de->regs); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + kfree(dev); +} + +#ifdef CONFIG_PM + +static int de_suspend (struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct de_private *de = dev->priv; + + rtnl_lock(); + if (netif_running (dev)) { + del_timer_sync(&de->media_timer); + + disable_irq(dev->irq); + spin_lock_irq(&de->lock); + + de_stop_hw(de); + netif_stop_queue(dev); + netif_device_detach(dev); + netif_carrier_off(dev); + + spin_unlock_irq(&de->lock); + enable_irq(dev->irq); + + /* Update the error counts. */ + __de_get_stats(de); + + synchronize_irq(); + de_clean_rings(de); + + de_adapter_sleep(de); + pci_disable_device(pdev); + } else { + netif_device_detach(dev); + } + rtnl_unlock(); + return 0; +} + +static int de_resume (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct de_private *de = dev->priv; + + rtnl_lock(); + if (netif_device_present(dev)) + goto out; + if (netif_running(dev)) { + pci_enable_device(pdev); + de_init_hw(de); + netif_device_attach(dev); + } else { + netif_device_attach(dev); + } +out: + rtnl_unlock(); + return 0; +} + +#endif /* CONFIG_PM */ + +static struct pci_driver de_driver = { + name: DRV_NAME, + id_table: de_pci_tbl, + probe: de_init_one, + remove: de_remove_one, +#ifdef CONFIG_PM + suspend: de_suspend, + resume: de_resume, +#endif +}; + +static int __init de_init (void) +{ +#ifdef MODULE + printk("%s", version); +#endif + return pci_module_init (&de_driver); +} + +static void __exit de_exit (void) +{ + pci_unregister_driver (&de_driver); +} + +module_init(de_init); +module_exit(de_exit); diff -Nru a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/tulip/de4x5.c Thu Mar 7 18:17:37 2002 @@ -0,0 +1,5918 @@ +/* de4x5.c: A DIGITAL DC21x4x DECchip and DE425/DE434/DE435/DE450/DE500 + ethernet driver for Linux. + + Copyright 1994, 1995 Digital Equipment Corporation. + + Testing resources for this driver have been made available + in part by NASA Ames Research Center (mjacob@nas.nasa.gov). + + The author may be reached at davies@maniac.ultranet.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. + + Originally, this driver was written for the Digital Equipment + Corporation series of EtherWORKS ethernet cards: + + DE425 TP/COAX EISA + DE434 TP PCI + DE435 TP/COAX/AUI PCI + DE450 TP/COAX/AUI PCI + DE500 10/100 PCI Fasternet + + but it will now attempt to support all cards which conform to the + Digital Semiconductor SROM Specification. The driver currently + recognises the following chips: + + DC21040 (no SROM) + DC21041[A] + DC21140[A] + DC21142 + DC21143 + + So far the driver is known to work with the following cards: + + KINGSTON + Linksys + ZNYX342 + SMC8432 + SMC9332 (w/new SROM) + ZNYX31[45] + ZNYX346 10/100 4 port (can act as a 10/100 bridge!) + + The driver has been tested on a relatively busy network using the DE425, + DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred + 16M of data to a DECstation 5000/200 as follows: + + TCP UDP + TX RX TX RX + DE425 1030k 997k 1170k 1128k + DE434 1063k 995k 1170k 1125k + DE435 1063k 995k 1170k 1125k + DE500 1063k 998k 1170k 1125k in 10Mb/s mode + + All values are typical (in kBytes/sec) from a sample of 4 for each + measurement. Their error is +/-20k on a quiet (private) network and also + depend on what load the CPU has. + + ========================================================================= + This driver has been written substantially from scratch, although its + inheritance of style and stack interface from 'ewrk3.c' and in turn from + Donald Becker's 'lance.c' should be obvious. With the module autoload of + every usable DECchip board, I pinched Donald's 'next_module' field to + link my modules together. + + Upto 15 EISA cards can be supported under this driver, limited primarily + by the available IRQ lines. I have checked different configurations of + multiple depca, EtherWORKS 3 cards and de4x5 cards and have not found a + problem yet (provided you have at least depca.c v0.38) ... + + PCI support has been added to allow the driver to work with the DE434, + DE435, DE450 and DE500 cards. The I/O accesses are a bit of a kludge due + to the differences in the EISA and PCI CSR address offsets from the base + address. + + The ability to load this driver as a loadable module has been included + and used extensively during the driver development (to save those long + reboot sequences). Loadable module support under PCI and EISA has been + achieved by letting the driver autoprobe as if it were compiled into the + kernel. Do make sure you're not sharing interrupts with anything that + cannot accommodate interrupt sharing! + + To utilise this ability, you have to do 8 things: + + 0) have a copy of the loadable modules code installed on your system. + 1) copy de4x5.c from the /linux/drivers/net directory to your favourite + temporary directory. + 2) for fixed autoprobes (not recommended), edit the source code near + line 5594 to reflect the I/O address you're using, or assign these when + loading by: + + insmod de4x5 io=0xghh where g = bus number + hh = device number + + NB: autoprobing for modules is now supported by default. You may just + use: + + insmod de4x5 + + to load all available boards. For a specific board, still use + the 'io=?' above. + 3) compile de4x5.c, but include -DMODULE in the command line to ensure + that the correct bits are compiled (see end of source code). + 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a + kernel with the de4x5 configuration turned off and reboot. + 5) insmod de4x5 [io=0xghh] + 6) run the net startup bits for your new eth?? interface(s) manually + (usually /etc/rc.inet[12] at boot time). + 7) enjoy! + + To unload a module, turn off the associated interface(s) + 'ifconfig eth?? down' then 'rmmod de4x5'. + + Automedia detection is included so that in principal you can disconnect + from, e.g. TP, reconnect to BNC and things will still work (after a + pause whilst the driver figures out where its media went). My tests + using ping showed that it appears to work.... + + By default, the driver will now autodetect any DECchip based card. + Should you have a need to restrict the driver to DIGITAL only cards, you + can compile with a DEC_ONLY define, or if loading as a module, use the + 'dec_only=1' parameter. + + I've changed the timing routines to use the kernel timer and scheduling + functions so that the hangs and other assorted problems that occurred + while autosensing the media should be gone. A bonus for the DC21040 + auto media sense algorithm is that it can now use one that is more in + line with the rest (the DC21040 chip doesn't have a hardware timer). + The downside is the 1 'jiffies' (10ms) resolution. + + IEEE 802.3u MII interface code has been added in anticipation that some + products may use it in the future. + + The SMC9332 card has a non-compliant SROM which needs fixing - I have + patched this driver to detect it because the SROM format used complies + to a previous DEC-STD format. + + I have removed the buffer copies needed for receive on Intels. I cannot + remove them for Alphas since the Tulip hardware only does longword + aligned DMA transfers and the Alphas get alignment traps with non + longword aligned data copies (which makes them really slow). No comment. + + I have added SROM decoding routines to make this driver work with any + card that supports the Digital Semiconductor SROM spec. This will help + all cards running the dc2114x series chips in particular. Cards using + the dc2104x chips should run correctly with the basic driver. I'm in + debt to for the testing and feedback that helped get + this feature working. So far we have tested KINGSTON, SMC8432, SMC9332 + (with the latest SROM complying with the SROM spec V3: their first was + broken), ZNYX342 and LinkSys. ZYNX314 (dual 21041 MAC) and ZNYX 315 + (quad 21041 MAC) cards also appear to work despite their incorrectly + wired IRQs. + + I have added a temporary fix for interrupt problems when some SCSI cards + share the same interrupt as the DECchip based cards. The problem occurs + because the SCSI card wants to grab the interrupt as a fast interrupt + (runs the service routine with interrupts turned off) vs. this card + which really needs to run the service routine with interrupts turned on. + This driver will now add the interrupt service routine as a fast + interrupt if it is bounced from the slow interrupt. THIS IS NOT A + RECOMMENDED WAY TO RUN THE DRIVER and has been done for a limited time + until people sort out their compatibility issues and the kernel + interrupt service code is fixed. YOU SHOULD SEPARATE OUT THE FAST + INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not + run on the same interrupt. PCMCIA/CardBus is another can of worms... + + Finally, I think I have really fixed the module loading problem with + more than one DECchip based card. As a side effect, I don't mess with + the device structure any more which means that if more than 1 card in + 2.0.x is installed (4 in 2.1.x), the user will have to edit + linux/drivers/net/Space.c to make room for them. Hence, module loading + is the preferred way to use this driver, since it doesn't have this + limitation. + + Where SROM media detection is used and full duplex is specified in the + SROM, the feature is ignored unless lp->params.fdx is set at compile + time OR during a module load (insmod de4x5 args='eth??:fdx' [see + below]). This is because there is no way to automatically detect full + duplex links except through autonegotiation. When I include the + autonegotiation feature in the SROM autoconf code, this detection will + occur automatically for that case. + + Command line arguments are now allowed, similar to passing arguments + through LILO. This will allow a per adapter board set up of full duplex + and media. The only lexical constraints are: the board name (dev->name) + appears in the list before its parameters. The list of parameters ends + either at the end of the parameter list or with another board name. The + following parameters are allowed: + + fdx for full duplex + autosense to set the media/speed; with the following + sub-parameters: + TP, TP_NW, BNC, AUI, BNC_AUI, 100Mb, 10Mb, AUTO + + Case sensitivity is important for the sub-parameters. They *must* be + upper case. Examples: + + insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. + + For a compiled in driver, at or above line 548, place e.g. + #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP" + + Yes, I know full duplex isn't permissible on BNC or AUI; they're just + examples. By default, full duplex is turned off and AUTO is the default + autosense setting. In reality, I expect only the full duplex option to + be used. Note the use of single quotes in the two examples above and the + lack of commas to separate items. ALSO, you must get the requested media + correct in relation to what the adapter SROM says it has. There's no way + to determine this in advance other than by trial and error and common + sense, e.g. call a BNC connectored port 'BNC', not '10Mb'. + + Changed the bus probing. EISA used to be done first, followed by PCI. + Most people probably don't even know what a de425 is today and the EISA + probe has messed up some SCSI cards in the past, so now PCI is always + probed first followed by EISA if a) the architecture allows EISA and + either b) there have been no PCI cards detected or c) an EISA probe is + forced by the user. To force a probe include "force_eisa" in your + insmod "args" line; for built-in kernels either change the driver to do + this automatically or include #define DE4X5_FORCE_EISA on or before + line 1040 in the driver. + + TO DO: + ------ + + Revision History + ---------------- + + Version Date Description + + 0.1 17-Nov-94 Initial writing. ALPHA code release. + 0.2 13-Jan-95 Added PCI support for DE435's. + 0.21 19-Jan-95 Added auto media detection. + 0.22 10-Feb-95 Fix interrupt handler call . + Fix recognition bug reported by . + Add request/release_region code. + Add loadable modules support for PCI. + Clean up loadable modules support. + 0.23 28-Feb-95 Added DC21041 and DC21140 support. + Fix missed frame counter value and initialisation. + Fixed EISA probe. + 0.24 11-Apr-95 Change delay routine to use . + Change TX_BUFFS_AVAIL macro. + Change media autodetection to allow manual setting. + Completed DE500 (DC21140) support. + 0.241 18-Apr-95 Interim release without DE500 Autosense Algorithm. + 0.242 10-May-95 Minor changes. + 0.30 12-Jun-95 Timer fix for DC21140. + Portability changes. + Add ALPHA changes from . + Add DE500 semi automatic autosense. + Add Link Fail interrupt TP failure detection. + Add timer based link change detection. + Plugged a memory leak in de4x5_queue_pkt(). + 0.31 13-Jun-95 Fixed PCI stuff for 1.3.1. + 0.32 26-Jun-95 Added verify_area() calls in de4x5_ioctl() from a + suggestion by . + 0.33 8-Aug-95 Add shared interrupt support (not released yet). + 0.331 21-Aug-95 Fix de4x5_open() with fast CPUs. + Fix de4x5_interrupt(). + Fix dc21140_autoconf() mess. + No shared interrupt support. + 0.332 11-Sep-95 Added MII management interface routines. + 0.40 5-Mar-96 Fix setup frame timeout . + Add kernel timer code (h/w is too flaky). + Add MII based PHY autosense. + Add new multicasting code. + Add new autosense algorithms for media/mode + selection using kernel scheduling/timing. + Re-formatted. + Made changes suggested by : + Change driver to detect all DECchip based cards + with DEC_ONLY restriction a special case. + Changed driver to autoprobe as a module. No irq + checking is done now - assume BIOS is good! + Added SMC9332 detection + 0.41 21-Mar-96 Don't check for get_hw_addr checksum unless DEC card + only + Fix for multiple PCI cards reported by + Duh, put the SA_SHIRQ flag into request_interrupt(). + Fix SMC ethernet address in enet_det[]. + Print chip name instead of "UNKNOWN" during boot. + 0.42 26-Apr-96 Fix MII write TA bit error. + Fix bug in dc21040 and dc21041 autosense code. + Remove buffer copies on receive for Intels. + Change sk_buff handling during media disconnects to + eliminate DUP packets. + Add dynamic TX thresholding. + Change all chips to use perfect multicast filtering. + Fix alloc_device() bug + 0.43 21-Jun-96 Fix unconnected media TX retry bug. + Add Accton to the list of broken cards. + Fix TX under-run bug for non DC21140 chips. + Fix boot command probe bug in alloc_device() as + reported by and + . + Add cache locks to prevent a race condition as + reported by and + . + Upgraded alloc_device() code. + 0.431 28-Jun-96 Fix potential bug in queue_pkt() from discussion + with + 0.44 13-Aug-96 Fix RX overflow bug in 2114[023] chips. + Fix EISA probe bugs reported by + and . + 0.441 9-Sep-96 Change dc21041_autoconf() to probe quiet BNC media + with a loopback packet. + 0.442 9-Sep-96 Include AUI in dc21041 media printout. Bug reported + by + 0.45 8-Dec-96 Include endian functions for PPC use, from work + by and . + 0.451 28-Dec-96 Added fix to allow autoprobe for modules after + suggestion from . + 0.5 30-Jan-97 Added SROM decoding functions. + Updated debug flags. + Fix sleep/wakeup calls for PCI cards, bug reported + by . + Added multi-MAC, one SROM feature from discussion + with . + Added full module autoprobe capability. + Added attempt to use an SMC9332 with broken SROM. + Added fix for ZYNX multi-mac cards that didn't + get their IRQs wired correctly. + 0.51 13-Feb-97 Added endian fixes for the SROM accesses from + + Fix init_connection() to remove extra device reset. + Fix MAC/PHY reset ordering in dc21140m_autoconf(). + Fix initialisation problem with lp->timeout in + typeX_infoblock() from . + Fix MII PHY reset problem from work done by + . + 0.52 26-Apr-97 Some changes may not credit the right people - + a disk crash meant I lost some mail. + Change RX interrupt routine to drop rather than + defer packets to avoid hang reported by + . + Fix srom_exec() to return for COMPACT and type 1 + infoblocks. + Added DC21142 and DC21143 functions. + Added byte counters from + Added SA_INTERRUPT temporary fix from + . + 0.53 12-Nov-97 Fix the *_probe() to include 'eth??' name during + module load: bug reported by + + Fix multi-MAC, one SROM, to work with 2114x chips: + bug reported by . + Make above search independent of BIOS device scan + direction. + Completed DC2114[23] autosense functions. + 0.531 21-Dec-97 Fix DE500-XA 100Mb/s bug reported by + and + . + Added argument list to set up each board from either + a module's command line or a compiled in #define. + Added generic MII PHY functionality to deal with + newer PHY chips. + Fix the mess in 2.1.67. + 0.532 5-Jan-98 Fix bug in mii_get_phy() reported by + . + Fix bug in pci_probe() for 64 bit systems reported + by . + 0.533 9-Jan-98 Fix more 64 bit bugs reported by . + 0.534 24-Jan-98 Fix last (?) endian bug from + 0.535 21-Feb-98 Fix Ethernet Address PROM reset bug for DC21040. + 0.536 21-Mar-98 Change pci_probe() to use the pci_dev structure. + **Incompatible with 2.0.x from here.** + 0.540 5-Jul-98 Atomicize assertion of dev->interrupt for SMP + from + Add TP, AUI and BNC cases to 21140m_autoconf() for + case where a 21140 under SROM control uses, e.g. AUI + from problem report by + Add MII parallel detection to 2114x_autoconf() for + case where no autonegotiation partner exists from + problem report by . + Add ability to force connection type directly even + when using SROM control from problem report by + . + Updated the PCI interface to conform with the latest + version. I hope nothing is broken... + Add TX done interrupt modification from suggestion + by . + Fix is_anc_capable() bug reported by + . + Fix type[13]_infoblock() bug: during MII search, PHY + lp->rst not run because lp->ibn not initialised - + from report & fix by . + Fix probe bug with EISA & PCI cards present from + report by . + 0.541 24-Aug-98 Fix compiler problems associated with i386-string + ops from multiple bug reports and temporary fix + from . + Fix pci_probe() to correctly emulate the old + pcibios_find_class() function. + Add an_exception() for old ZYNX346 and fix compile + warning on PPC & SPARC, from . + Fix lastPCI to correctly work with compiled in + kernels and modules from bug report by + et al. + 0.542 15-Sep-98 Fix dc2114x_autoconf() to stop multiple messages + when media is unconnected. + Change dev->interrupt to lp->interrupt to ensure + alignment for Alpha's and avoid their unaligned + access traps. This flag is merely for log messages: + should do something more definitive though... + 0.543 30-Dec-98 Add SMP spin locking. + 0.544 8-May-99 Fix for buggy SROM in Motorola embedded boards using + a 21143 by . + Change PCI/EISA bus probing order. + 0.545 28-Nov-99 Further Moto SROM bug fix from + + Remove double checking for DEBUG_RX in de4x5_dbg_rx() + from report by + 0.546 22-Feb-01 Fixes Alpha XP1000 oops. The srom_search function + was causing a page fault when initializing the + variable 'pb', on a non de4x5 PCI device, in this + case a PCI bridge (DEC chip 21152). The value of + 'pb' is now only initialized if a de4x5 chip is + present. + + 0.547 08-Nov-01 Use library crc32 functions by + ========================================================================= +*/ + +static const char *version = "de4x5.c:V0.546 2001/02/22 davies@maniac.ultranet.com\n"; + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PPC +#include +#endif /* CONFIG_PPC */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "de4x5.h" + +#define c_char const char +#define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a))) + +/* +** MII Information +*/ +struct phy_table { + int reset; /* Hard reset required? */ + int id; /* IEEE OUI */ + int ta; /* One cycle TA time - 802.3u is confusing here */ + struct { /* Non autonegotiation (parallel) speed det. */ + int reg; + int mask; + int value; + } spd; +}; + +struct mii_phy { + int reset; /* Hard reset required? */ + int id; /* IEEE OUI */ + int ta; /* One cycle TA time */ + struct { /* Non autonegotiation (parallel) speed det. */ + int reg; + int mask; + int value; + } spd; + int addr; /* MII address for the PHY */ + u_char *gep; /* Start of GEP sequence block in SROM */ + u_char *rst; /* Start of reset sequence in SROM */ + u_int mc; /* Media Capabilities */ + u_int ana; /* NWay Advertisement */ + u_int fdx; /* Full DupleX capabilites for each media */ + u_int ttm; /* Transmit Threshold Mode for each media */ + u_int mci; /* 21142 MII Connector Interrupt info */ +}; + +#define DE4X5_MAX_PHY 8 /* Allow upto 8 attached PHY devices per board */ + +struct sia_phy { + u_char mc; /* Media Code */ + u_char ext; /* csr13-15 valid when set */ + int csr13; /* SIA Connectivity Register */ + int csr14; /* SIA TX/RX Register */ + int csr15; /* SIA General Register */ + int gepc; /* SIA GEP Control Information */ + int gep; /* SIA GEP Data */ +}; + +/* +** Define the know universe of PHY devices that can be +** recognised by this driver. +*/ +static struct phy_table phy_info[] = { + {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}}, /* National TX */ + {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}}, /* Broadcom T4 */ + {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */ + {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}, /* Cypress T4 */ + {0, 0x7810 , 1, {0x14, 0x0800, 0x0800}} /* Level One LTX970 */ +}; + +/* +** These GENERIC values assumes that the PHY devices follow 802.3u and +** allow parallel detection to set the link partner ability register. +** Detection of 100Base-TX [H/F Duplex] and 100Base-T4 is supported. +*/ +#define GENERIC_REG 0x05 /* Autoneg. Link Partner Advertisement Reg. */ +#define GENERIC_MASK MII_ANLPA_100M /* All 100Mb/s Technologies */ +#define GENERIC_VALUE MII_ANLPA_100M /* 100B-TX, 100B-TX FDX, 100B-T4 */ + +/* +** Define special SROM detection cases +*/ +static c_char enet_det[][ETH_ALEN] = { + {0x00, 0x00, 0xc0, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xe8, 0x00, 0x00, 0x00} +}; + +#define SMC 1 +#define ACCTON 2 + +/* +** SROM Repair definitions. If a broken SROM is detected a card may +** use this information to help figure out what to do. This is a +** "stab in the dark" and so far for SMC9332's only. +*/ +static c_char srom_repair_info[][100] = { + {0x00,0x1e,0x00,0x00,0x00,0x08, /* SMC9332 */ + 0x1f,0x01,0x8f,0x01,0x00,0x01,0x00,0x02, + 0x01,0x00,0x00,0x78,0xe0,0x01,0x00,0x50, + 0x00,0x18,} +}; + + +#ifdef DE4X5_DEBUG +static int de4x5_debug = DE4X5_DEBUG; +#else +/*static int de4x5_debug = (DEBUG_MII | DEBUG_SROM | DEBUG_PCICFG | DEBUG_MEDIA | DEBUG_VERSION);*/ +static int de4x5_debug = (DEBUG_MEDIA | DEBUG_VERSION); +#endif + +/* +** Allow per adapter set up. For modules this is simply a command line +** parameter, e.g.: +** insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. +** +** For a compiled in driver, place e.g. +** #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP" +** here +*/ +#ifdef DE4X5_PARM +static char *args = DE4X5_PARM; +#else +static char *args; +#endif + +struct parameters { + int fdx; + int autosense; +}; + +#define DE4X5_AUTOSENSE_MS 250 /* msec autosense tick (DE500) */ + +#define DE4X5_NDA 0xffe0 /* No Device (I/O) Address */ + +/* +** Ethernet PROM defines +*/ +#define PROBE_LENGTH 32 +#define ETH_PROM_SIG 0xAA5500FFUL + +/* +** Ethernet Info +*/ +#define PKT_BUF_SZ 1536 /* Buffer size for each Tx/Rx buffer */ +#define IEEE802_3_SZ 1518 /* Packet + CRC */ +#define MAX_PKT_SZ 1514 /* Maximum ethernet packet length */ +#define MAX_DAT_SZ 1500 /* Maximum ethernet data length */ +#define MIN_DAT_SZ 1 /* Minimum ethernet data length */ +#define PKT_HDR_LEN 14 /* Addresses and data length info */ +#define FAKE_FRAME_LEN (MAX_PKT_SZ + 1) +#define QUEUE_PKT_TIMEOUT (3*HZ) /* 3 second timeout */ + + +/* +** EISA bus defines +*/ +#define DE4X5_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */ +#define DE4X5_EISA_TOTAL_SIZE 0x100 /* I/O address extent */ + +#define MAX_EISA_SLOTS 16 +#define EISA_SLOT_INC 0x1000 +#define EISA_ALLOWED_IRQ_LIST {5, 9, 10, 11} + +#define DE4X5_SIGNATURE {"DE425","DE434","DE435","DE450","DE500"} +#define DE4X5_NAME_LENGTH 8 + +/* +** Ethernet PROM defines for DC21040 +*/ +#define PROBE_LENGTH 32 +#define ETH_PROM_SIG 0xAA5500FFUL + +/* +** PCI Bus defines +*/ +#define PCI_MAX_BUS_NUM 8 +#define DE4X5_PCI_TOTAL_SIZE 0x80 /* I/O address extent */ +#define DE4X5_CLASS_CODE 0x00020000 /* Network controller, Ethernet */ +#define NO_MORE_PCI -2 /* PCI bus search all done */ + +/* +** Memory Alignment. Each descriptor is 4 longwords long. To force a +** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and +** DESC_ALIGN. ALIGN aligns the start address of the private memory area +** and hence the RX descriptor ring's first entry. +*/ +#define DE4X5_ALIGN4 ((u_long)4 - 1) /* 1 longword align */ +#define DE4X5_ALIGN8 ((u_long)8 - 1) /* 2 longword align */ +#define DE4X5_ALIGN16 ((u_long)16 - 1) /* 4 longword align */ +#define DE4X5_ALIGN32 ((u_long)32 - 1) /* 8 longword align */ +#define DE4X5_ALIGN64 ((u_long)64 - 1) /* 16 longword align */ +#define DE4X5_ALIGN128 ((u_long)128 - 1) /* 32 longword align */ + +#define DE4X5_ALIGN DE4X5_ALIGN32 /* Keep the DC21040 happy... */ +#define DE4X5_CACHE_ALIGN CAL_16LONG +#define DESC_SKIP_LEN DSL_0 /* Must agree with DESC_ALIGN */ +/*#define DESC_ALIGN u32 dummy[4]; / * Must agree with DESC_SKIP_LEN */ +#define DESC_ALIGN + +#ifndef DEC_ONLY /* See README.de4x5 for using this */ +static int dec_only; +#else +static int dec_only = 1; +#endif + +/* +** DE4X5 IRQ ENABLE/DISABLE +*/ +#define ENABLE_IRQs { \ + imr |= lp->irq_en;\ + outl(imr, DE4X5_IMR); /* Enable the IRQs */\ +} + +#define DISABLE_IRQs {\ + imr = inl(DE4X5_IMR);\ + imr &= ~lp->irq_en;\ + outl(imr, DE4X5_IMR); /* Disable the IRQs */\ +} + +#define UNMASK_IRQs {\ + imr |= lp->irq_mask;\ + outl(imr, DE4X5_IMR); /* Unmask the IRQs */\ +} + +#define MASK_IRQs {\ + imr = inl(DE4X5_IMR);\ + imr &= ~lp->irq_mask;\ + outl(imr, DE4X5_IMR); /* Mask the IRQs */\ +} + +/* +** DE4X5 START/STOP +*/ +#define START_DE4X5 {\ + omr = inl(DE4X5_OMR);\ + omr |= OMR_ST | OMR_SR;\ + outl(omr, DE4X5_OMR); /* Enable the TX and/or RX */\ +} + +#define STOP_DE4X5 {\ + omr = inl(DE4X5_OMR);\ + omr &= ~(OMR_ST|OMR_SR);\ + outl(omr, DE4X5_OMR); /* Disable the TX and/or RX */ \ +} + +/* +** DE4X5 SIA RESET +*/ +#define RESET_SIA outl(0, DE4X5_SICR); /* Reset SIA connectivity regs */ + +/* +** DE500 AUTOSENSE TIMER INTERVAL (MILLISECS) +*/ +#define DE4X5_AUTOSENSE_MS 250 + +/* +** SROM Structure +*/ +struct de4x5_srom { + char sub_vendor_id[2]; + char sub_system_id[2]; + char reserved[12]; + char id_block_crc; + char reserved2; + char version; + char num_controllers; + char ieee_addr[6]; + char info[100]; + short chksum; +}; +#define SUB_VENDOR_ID 0x500a + +/* +** DE4X5 Descriptors. Make sure that all the RX buffers are contiguous +** and have sizes of both a power of 2 and a multiple of 4. +** A size of 256 bytes for each buffer could be chosen because over 90% of +** all packets in our network are <256 bytes long and 64 longword alignment +** is possible. 1536 showed better 'ttcp' performance. Take your pick. 32 TX +** descriptors are needed for machines with an ALPHA CPU. +*/ +#define NUM_RX_DESC 8 /* Number of RX descriptors */ +#define NUM_TX_DESC 32 /* Number of TX descriptors */ +#define RX_BUFF_SZ 1536 /* Power of 2 for kmalloc and */ + /* Multiple of 4 for DC21040 */ + /* Allows 512 byte alignment */ +struct de4x5_desc { + volatile s32 status; + u32 des1; + u32 buf; + u32 next; + DESC_ALIGN +}; + +/* +** The DE4X5 private structure +*/ +#define DE4X5_PKT_STAT_SZ 16 +#define DE4X5_PKT_BIN_SZ 128 /* Should be >=100 unless you + increase DE4X5_PKT_STAT_SZ */ + +struct pkt_stats { + u_int bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */ + u_int unicast; + u_int multicast; + u_int broadcast; + u_int excessive_collisions; + u_int tx_underruns; + u_int excessive_underruns; + u_int rx_runt_frames; + u_int rx_collision; + u_int rx_dribble; + u_int rx_overflow; +}; + +struct de4x5_private { + char adapter_name[80]; /* Adapter name */ + u_long interrupt; /* Aligned ISR flag */ + struct de4x5_desc *rx_ring; /* RX descriptor ring */ + struct de4x5_desc *tx_ring; /* TX descriptor ring */ + struct sk_buff *tx_skb[NUM_TX_DESC]; /* TX skb for freeing when sent */ + struct sk_buff *rx_skb[NUM_RX_DESC]; /* RX skb's */ + int rx_new, rx_old; /* RX descriptor ring pointers */ + int tx_new, tx_old; /* TX descriptor ring pointers */ + char setup_frame[SETUP_FRAME_LEN]; /* Holds MCA and PA info. */ + char frame[64]; /* Min sized packet for loopback*/ + spinlock_t lock; /* Adapter specific spinlock */ + struct net_device_stats stats; /* Public stats */ + struct pkt_stats pktStats; /* Private stats counters */ + char rxRingSize; + char txRingSize; + int bus; /* EISA or PCI */ + int bus_num; /* PCI Bus number */ + int device; /* Device number on PCI bus */ + int state; /* Adapter OPENED or CLOSED */ + int chipset; /* DC21040, DC21041 or DC21140 */ + s32 irq_mask; /* Interrupt Mask (Enable) bits */ + s32 irq_en; /* Summary interrupt bits */ + int media; /* Media (eg TP), mode (eg 100B)*/ + int c_media; /* Remember the last media conn */ + int fdx; /* media full duplex flag */ + int linkOK; /* Link is OK */ + int autosense; /* Allow/disallow autosensing */ + int tx_enable; /* Enable descriptor polling */ + int setup_f; /* Setup frame filtering type */ + int local_state; /* State within a 'media' state */ + struct mii_phy phy[DE4X5_MAX_PHY]; /* List of attached PHY devices */ + struct sia_phy sia; /* SIA PHY Information */ + int active; /* Index to active PHY device */ + int mii_cnt; /* Number of attached PHY's */ + int timeout; /* Scheduling counter */ + struct timer_list timer; /* Timer info for kernel */ + int tmp; /* Temporary global per card */ + struct { + void *priv; /* Original kmalloc'd mem addr */ + u_long lock; /* Lock the cache accesses */ + s32 csr0; /* Saved Bus Mode Register */ + s32 csr6; /* Saved Operating Mode Reg. */ + s32 csr7; /* Saved IRQ Mask Register */ + s32 gep; /* Saved General Purpose Reg. */ + s32 gepc; /* Control info for GEP */ + s32 csr13; /* Saved SIA Connectivity Reg. */ + s32 csr14; /* Saved SIA TX/RX Register */ + s32 csr15; /* Saved SIA General Register */ + int save_cnt; /* Flag if state already saved */ + struct sk_buff *skb; /* Save the (re-ordered) skb's */ + } cache; + struct de4x5_srom srom; /* A copy of the SROM */ + struct net_device *next_module; /* Link to the next module */ + int rx_ovf; /* Check for 'RX overflow' tag */ + int useSROM; /* For non-DEC card use SROM */ + int useMII; /* Infoblock using the MII */ + int asBitValid; /* Autosense bits in GEP? */ + int asPolarity; /* 0 => asserted high */ + int asBit; /* Autosense bit number in GEP */ + int defMedium; /* SROM default medium */ + int tcount; /* Last infoblock number */ + int infoblock_init; /* Initialised this infoblock? */ + int infoleaf_offset; /* SROM infoleaf for controller */ + s32 infoblock_csr6; /* csr6 value in SROM infoblock */ + int infoblock_media; /* infoblock media */ + int (*infoleaf_fn)(struct net_device *); /* Pointer to infoleaf function */ + u_char *rst; /* Pointer to Type 5 reset info */ + u_char ibn; /* Infoblock number */ + struct parameters params; /* Command line/ #defined params */ + struct pci_dev *pdev; /* Device cookie for DMA alloc */ + dma_addr_t dma_rings; /* DMA handle for rings */ + int dma_size; /* Size of the DMA area */ + char *rx_bufs; /* rx bufs on alpha, sparc, ... */ +}; + +/* +** Kludge to get around the fact that the CSR addresses have different +** offsets in the PCI and EISA boards. Also note that the ethernet address +** PROM is accessed differently. +*/ +static struct bus_type { + int bus; + int bus_num; + int device; + int chipset; + struct de4x5_srom srom; + int autosense; + int useSROM; +} bus; + +/* +** To get around certain poxy cards that don't provide an SROM +** for the second and more DECchip, I have to key off the first +** chip's address. I'll assume there's not a bad SROM iff: +** +** o the chipset is the same +** o the bus number is the same and > 0 +** o the sum of all the returned hw address bytes is 0 or 0x5fa +** +** Also have to save the irq for those cards whose hardware designers +** can't follow the PCI to PCI Bridge Architecture spec. +*/ +static struct { + int chipset; + int bus; + int irq; + u_char addr[ETH_ALEN]; +} last = {0,}; + +/* +** The transmit ring full condition is described by the tx_old and tx_new +** pointers by: +** tx_old = tx_new Empty ring +** tx_old = tx_new+1 Full ring +** tx_old+txRingSize = tx_new+1 Full ring (wrapped condition) +*/ +#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ + lp->tx_old+lp->txRingSize-lp->tx_new-1:\ + lp->tx_old -lp->tx_new-1) + +#define TX_PKT_PENDING (lp->tx_old != lp->tx_new) + +/* +** Public Functions +*/ +static int de4x5_open(struct net_device *dev); +static int de4x5_queue_pkt(struct sk_buff *skb, struct net_device *dev); +static void de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int de4x5_close(struct net_device *dev); +static struct net_device_stats *de4x5_get_stats(struct net_device *dev); +static void de4x5_local_stats(struct net_device *dev, char *buf, int pkt_len); +static void set_multicast_list(struct net_device *dev); +static int de4x5_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); + +/* +** Private functions +*/ +static int de4x5_hw_init(struct net_device *dev, u_long iobase, struct pci_dev *pdev); +static int de4x5_init(struct net_device *dev); +static int de4x5_sw_reset(struct net_device *dev); +static int de4x5_rx(struct net_device *dev); +static int de4x5_tx(struct net_device *dev); +static int de4x5_ast(struct net_device *dev); +static int de4x5_txur(struct net_device *dev); +static int de4x5_rx_ovfc(struct net_device *dev); + +static int autoconf_media(struct net_device *dev); +static void create_packet(struct net_device *dev, char *frame, int len); +static void load_packet(struct net_device *dev, char *buf, u32 flags, struct sk_buff *skb); +static int dc21040_autoconf(struct net_device *dev); +static int dc21041_autoconf(struct net_device *dev); +static int dc21140m_autoconf(struct net_device *dev); +static int dc2114x_autoconf(struct net_device *dev); +static int srom_autoconf(struct net_device *dev); +static int de4x5_suspect_state(struct net_device *dev, int timeout, int prev_state, int (*fn)(struct net_device *, int), int (*asfn)(struct net_device *)); +static int dc21040_state(struct net_device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct net_device *, int)); +static int test_media(struct net_device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec); +static int test_for_100Mb(struct net_device *dev, int msec); +static int wait_for_link(struct net_device *dev); +static int test_mii_reg(struct net_device *dev, int reg, int mask, int pol, long msec); +static int is_spd_100(struct net_device *dev); +static int is_100_up(struct net_device *dev); +static int is_10_up(struct net_device *dev); +static int is_anc_capable(struct net_device *dev); +static int ping_media(struct net_device *dev, int msec); +static struct sk_buff *de4x5_alloc_rx_buff(struct net_device *dev, int index, int len); +static void de4x5_free_rx_buffs(struct net_device *dev); +static void de4x5_free_tx_buffs(struct net_device *dev); +static void de4x5_save_skbs(struct net_device *dev); +static void de4x5_rst_desc_ring(struct net_device *dev); +static void de4x5_cache_state(struct net_device *dev, int flag); +static void de4x5_put_cache(struct net_device *dev, struct sk_buff *skb); +static void de4x5_putb_cache(struct net_device *dev, struct sk_buff *skb); +static struct sk_buff *de4x5_get_cache(struct net_device *dev); +static void de4x5_setup_intr(struct net_device *dev); +static void de4x5_init_connection(struct net_device *dev); +static int de4x5_reset_phy(struct net_device *dev); +static void reset_init_sia(struct net_device *dev, s32 sicr, s32 strr, s32 sigr); +static int test_ans(struct net_device *dev, s32 irqs, s32 irq_mask, s32 msec); +static int test_tp(struct net_device *dev, s32 msec); +static int EISA_signature(char *name, s32 eisa_id); +static int PCI_signature(char *name, struct bus_type *lp); +static void DevicePresent(u_long iobase); +static void enet_addr_rst(u_long aprom_addr); +static int de4x5_bad_srom(struct bus_type *lp); +static short srom_rd(u_long address, u_char offset); +static void srom_latch(u_int command, u_long address); +static void srom_command(u_int command, u_long address); +static void srom_address(u_int command, u_long address, u_char offset); +static short srom_data(u_int command, u_long address); +/*static void srom_busy(u_int command, u_long address);*/ +static void sendto_srom(u_int command, u_long addr); +static int getfrom_srom(u_long addr); +static int srom_map_media(struct net_device *dev); +static int srom_infoleaf_info(struct net_device *dev); +static void srom_init(struct net_device *dev); +static void srom_exec(struct net_device *dev, u_char *p); +static int mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr); +static void mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr); +static int mii_rdata(u_long ioaddr); +static void mii_wdata(int data, int len, u_long ioaddr); +static void mii_ta(u_long rw, u_long ioaddr); +static int mii_swap(int data, int len); +static void mii_address(u_char addr, u_long ioaddr); +static void sendto_mii(u32 command, int data, u_long ioaddr); +static int getfrom_mii(u32 command, u_long ioaddr); +static int mii_get_oui(u_char phyaddr, u_long ioaddr); +static int mii_get_phy(struct net_device *dev); +static void SetMulticastFilter(struct net_device *dev); +static int get_hw_addr(struct net_device *dev); +static void srom_repair(struct net_device *dev, int card); +static int test_bad_enet(struct net_device *dev, int status); +static int an_exception(struct bus_type *lp); +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) +static void eisa_probe(struct net_device *dev, u_long iobase); +#endif +static void pci_probe(struct net_device *dev, u_long iobase); +static void srom_search(struct pci_dev *pdev); +static char *build_setup_frame(struct net_device *dev, int mode); +static void disable_ast(struct net_device *dev); +static void enable_ast(struct net_device *dev, u32 time_out); +static long de4x5_switch_mac_port(struct net_device *dev); +static int gep_rd(struct net_device *dev); +static void gep_wr(s32 data, struct net_device *dev); +static void timeout(struct net_device *dev, void (*fn)(u_long data), u_long data, u_long msec); +static void yawn(struct net_device *dev, int state); +static void link_modules(struct net_device *dev, struct net_device *tmp); +static void de4x5_parse_params(struct net_device *dev); +static void de4x5_dbg_open(struct net_device *dev); +static void de4x5_dbg_mii(struct net_device *dev, int k); +static void de4x5_dbg_media(struct net_device *dev); +static void de4x5_dbg_srom(struct de4x5_srom *p); +static void de4x5_dbg_rx(struct sk_buff *skb, int len); +static int de4x5_strncmp(char *a, char *b, int n); +static int dc21041_infoleaf(struct net_device *dev); +static int dc21140_infoleaf(struct net_device *dev); +static int dc21142_infoleaf(struct net_device *dev); +static int dc21143_infoleaf(struct net_device *dev); +static int type0_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type1_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type2_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type3_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type4_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type5_infoblock(struct net_device *dev, u_char count, u_char *p); +static int compact_infoblock(struct net_device *dev, u_char count, u_char *p); + +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +static struct net_device *unlink_modules(struct net_device *p); +static struct net_device *insert_device(struct net_device *dev, u_long iobase, + int (*init)(struct net_device *)); +static int count_adapters(void); +static int loading_module = 1; +MODULE_PARM(de4x5_debug, "i"); +MODULE_PARM(dec_only, "i"); +MODULE_PARM(args, "s"); +MODULE_PARM_DESC(de4x5_debug, "de4x5 debug mask"); +MODULE_PARM_DESC(dec_only, "de4x5 probe only for Digital boards (0-1)"); +MODULE_PARM_DESC(args, "de4x5 full duplex and media type settings; see de4x5.c for details"); +MODULE_LICENSE("GPL"); + +# else +static int loading_module; +#endif /* MODULE */ + +static char name[DE4X5_NAME_LENGTH + 1]; +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) +static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST; +static int lastEISA; +# ifdef DE4X5_FORCE_EISA /* Force an EISA bus probe or not */ +static int forceEISA = 1; +# else +static int forceEISA; +# endif +#endif +static int num_de4x5s; +static int cfrv, useSROM; +static int lastPCI = -1; +static struct net_device *lastModule; +static struct pci_dev *pdev; + +/* +** List the SROM infoleaf functions and chipsets +*/ +struct InfoLeaf { + int chipset; + int (*fn)(struct net_device *); +}; +static struct InfoLeaf infoleaf_array[] = { + {DC21041, dc21041_infoleaf}, + {DC21140, dc21140_infoleaf}, + {DC21142, dc21142_infoleaf}, + {DC21143, dc21143_infoleaf} +}; +#define INFOLEAF_SIZE (sizeof(infoleaf_array)/(sizeof(int)+sizeof(int *))) + +/* +** List the SROM info block functions +*/ +static int (*dc_infoblock[])(struct net_device *dev, u_char, u_char *) = { + type0_infoblock, + type1_infoblock, + type2_infoblock, + type3_infoblock, + type4_infoblock, + type5_infoblock, + compact_infoblock +}; + +#define COMPACT (sizeof(dc_infoblock)/sizeof(int *) - 1) + +/* +** Miscellaneous defines... +*/ +#define RESET_DE4X5 {\ + int i;\ + i=inl(DE4X5_BMR);\ + mdelay(1);\ + outl(i | BMR_SWR, DE4X5_BMR);\ + mdelay(1);\ + outl(i, DE4X5_BMR);\ + mdelay(1);\ + for (i=0;i<5;i++) {inl(DE4X5_BMR); mdelay(1);}\ + mdelay(1);\ +} + +#define PHY_HARD_RESET {\ + outl(GEP_HRST, DE4X5_GEP); /* Hard RESET the PHY dev. */\ + mdelay(1); /* Assert for 1ms */\ + outl(0x00, DE4X5_GEP);\ + mdelay(2); /* Wait for 2ms */\ +} + + +/* +** Autoprobing in modules is allowed here. See the top of the file for +** more info. +*/ +int __init +de4x5_probe(struct net_device *dev) +{ + u_long iobase = dev->base_addr; + + pci_probe(dev, iobase); +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) + if ((lastPCI == NO_MORE_PCI) && ((num_de4x5s == 0) || forceEISA)) { + eisa_probe(dev, iobase); + } +#endif + + return (dev->priv ? 0 : -ENODEV); +} + +static int __init +de4x5_hw_init(struct net_device *dev, u_long iobase, struct pci_dev *pdev) +{ + struct bus_type *lp = &bus; + int i, status=0; + char *tmp; + + /* Ensure we're not sleeping */ + if (lp->bus == EISA) { + outb(WAKEUP, PCI_CFPM); + } else { + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, WAKEUP); + } + mdelay(10); + + RESET_DE4X5; + + if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) { + return -ENXIO; /* Hardware could not reset */ + } + + /* + ** Now find out what kind of DC21040/DC21041/DC21140 board we have. + */ + useSROM = FALSE; + if (lp->bus == PCI) { + PCI_signature(name, lp); + } else { + EISA_signature(name, EISA_ID0); + } + + if (*name == '\0') { /* Not found a board signature */ + return -ENXIO; + } + + dev->base_addr = iobase; + if (lp->bus == EISA) { + printk("%s: %s at 0x%04lx (EISA slot %ld)", + dev->name, name, iobase, ((iobase>>12)&0x0f)); + } else { /* PCI port address */ + printk("%s: %s at 0x%04lx (PCI bus %d, device %d)", dev->name, name, + iobase, lp->bus_num, lp->device); + } + + printk(", h/w address "); + status = get_hw_addr(dev); + for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */ + printk("%2.2x:", dev->dev_addr[i]); + } + printk("%2.2x,\n", dev->dev_addr[i]); + + if (status != 0) { + printk(" which has an Ethernet PROM CRC error.\n"); + return -ENXIO; + } else { + struct de4x5_private *lp; + + /* + ** Reserve a section of kernel memory for the adapter + ** private area and the TX/RX descriptor rings. + */ + dev->priv = (void *) kmalloc(sizeof(struct de4x5_private) + DE4X5_ALIGN, + GFP_KERNEL); + if (dev->priv == NULL) { + return -ENOMEM; + } + + /* + ** Align to a longword boundary + */ + tmp = dev->priv; + dev->priv = (void *)(((u_long)dev->priv + DE4X5_ALIGN) & ~DE4X5_ALIGN); + lp = (struct de4x5_private *)dev->priv; + memset(dev->priv, 0, sizeof(struct de4x5_private)); + lp->bus = bus.bus; + lp->bus_num = bus.bus_num; + lp->device = bus.device; + lp->chipset = bus.chipset; + lp->cache.priv = tmp; + lp->cache.gepc = GEP_INIT; + lp->asBit = GEP_SLNK; + lp->asPolarity = GEP_SLNK; + lp->asBitValid = TRUE; + lp->timeout = -1; + lp->useSROM = useSROM; + lp->pdev = pdev; + memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom)); + lp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; + de4x5_parse_params(dev); + + /* + ** Choose correct autosensing in case someone messed up + */ + lp->autosense = lp->params.autosense; + if (lp->chipset != DC21140) { + if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) { + lp->params.autosense = TP; + } + if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) { + lp->params.autosense = BNC; + } + } + lp->fdx = lp->params.fdx; + sprintf(lp->adapter_name,"%s (%s)", name, dev->name); + + lp->dma_size = (NUM_RX_DESC + NUM_TX_DESC) * sizeof(struct de4x5_desc); +#if defined(__alpha__) || defined(__powerpc__) || defined(__sparc_v9__) || defined(DE4X5_DO_MEMCPY) + lp->dma_size += RX_BUFF_SZ * NUM_RX_DESC + DE4X5_ALIGN; +#endif + lp->rx_ring = pci_alloc_consistent(pdev, lp->dma_size, &lp->dma_rings); + if (lp->rx_ring == NULL) { + kfree(lp->cache.priv); + lp->cache.priv = NULL; + return -ENOMEM; + } + + lp->tx_ring = lp->rx_ring + NUM_RX_DESC; + + /* + ** Set up the RX descriptor ring (Intels) + ** Allocate contiguous receive buffers, long word aligned (Alphas) + */ +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) + for (i=0; irx_ring[i].status = 0; + lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); + lp->rx_ring[i].buf = 0; + lp->rx_ring[i].next = 0; + lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */ + } + +#else + { + dma_addr_t dma_rx_bufs; + + dma_rx_bufs = lp->dma_rings + (NUM_RX_DESC + NUM_TX_DESC) + * sizeof(struct de4x5_desc); + dma_rx_bufs = (dma_rx_bufs + DE4X5_ALIGN) & ~DE4X5_ALIGN; + lp->rx_bufs = (char *)(((long)(lp->rx_ring + NUM_RX_DESC + + NUM_TX_DESC) + DE4X5_ALIGN) & ~DE4X5_ALIGN); + for (i=0; irx_ring[i].status = 0; + lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); + lp->rx_ring[i].buf = + cpu_to_le32(dma_rx_bufs+i*RX_BUFF_SZ); + lp->rx_ring[i].next = 0; + lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */ + } + + } +#endif + + barrier(); + + request_region(iobase, (lp->bus == PCI ? DE4X5_PCI_TOTAL_SIZE : + DE4X5_EISA_TOTAL_SIZE), + lp->adapter_name); + + lp->rxRingSize = NUM_RX_DESC; + lp->txRingSize = NUM_TX_DESC; + + /* Write the end of list marker to the descriptor lists */ + lp->rx_ring[lp->rxRingSize - 1].des1 |= cpu_to_le32(RD_RER); + lp->tx_ring[lp->txRingSize - 1].des1 |= cpu_to_le32(TD_TER); + + /* Tell the adapter where the TX/RX rings are located. */ + outl(lp->dma_rings, DE4X5_RRBA); + outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), + DE4X5_TRBA); + + /* Initialise the IRQ mask and Enable/Disable */ + lp->irq_mask = IMR_RIM | IMR_TIM | IMR_TUM | IMR_UNM; + lp->irq_en = IMR_NIM | IMR_AIM; + + /* Create a loopback packet frame for later media probing */ + create_packet(dev, lp->frame, sizeof(lp->frame)); + + /* Check if the RX overflow bug needs testing for */ + i = cfrv & 0x000000fe; + if ((lp->chipset == DC21140) && (i == 0x20)) { + lp->rx_ovf = 1; + } + + /* Initialise the SROM pointers if possible */ + if (lp->useSROM) { + lp->state = INITIALISED; + if (srom_infoleaf_info(dev)) { + return -ENXIO; + } + srom_init(dev); + } + + lp->state = CLOSED; + + /* + ** Check for an MII interface + */ + if ((lp->chipset != DC21040) && (lp->chipset != DC21041)) { + mii_get_phy(dev); + } + +#ifndef __sparc_v9__ + printk(" and requires IRQ%d (provided by %s).\n", dev->irq, +#else + printk(" and requires IRQ%x (provided by %s).\n", dev->irq, +#endif + ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG")); + } + + if (de4x5_debug & DEBUG_VERSION) { + printk(version); + } + + /* The DE4X5-specific entries in the device structure. */ + dev->open = &de4x5_open; + dev->hard_start_xmit = &de4x5_queue_pkt; + dev->stop = &de4x5_close; + dev->get_stats = &de4x5_get_stats; + dev->set_multicast_list = &set_multicast_list; + dev->do_ioctl = &de4x5_ioctl; + + dev->mem_start = 0; + + /* Fill in the generic fields of the device structure. */ + ether_setup(dev); + + /* Let the adapter sleep to save power */ + yawn(dev, SLEEP); + + return status; +} + + +static int +de4x5_open(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int i, status = 0; + s32 omr; + + /* Allocate the RX buffers */ + for (i=0; irxRingSize; i++) { + if (de4x5_alloc_rx_buff(dev, i, 0) == NULL) { + de4x5_free_rx_buffs(dev); + return -EAGAIN; + } + } + + /* + ** Wake up the adapter + */ + yawn(dev, WAKEUP); + + /* + ** Re-initialize the DE4X5... + */ + status = de4x5_init(dev); + lp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; + lp->state = OPEN; + de4x5_dbg_open(dev); + + if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ, + lp->adapter_name, dev)) { + printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq); + if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ, + lp->adapter_name, dev)) { + printk("\n Cannot get IRQ- reconfigure your hardware.\n"); + disable_ast(dev); + de4x5_free_rx_buffs(dev); + de4x5_free_tx_buffs(dev); + yawn(dev, SLEEP); + lp->state = CLOSED; + return -EAGAIN; + } else { + printk("\n Succeeded, but you should reconfigure your hardware to avoid this.\n"); + printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n"); + } + } + + lp->interrupt = UNMASK_INTERRUPTS; + dev->trans_start = jiffies; + + START_DE4X5; + + de4x5_setup_intr(dev); + + if (de4x5_debug & DEBUG_OPEN) { + printk("\tsts: 0x%08x\n", inl(DE4X5_STS)); + printk("\tbmr: 0x%08x\n", inl(DE4X5_BMR)); + printk("\timr: 0x%08x\n", inl(DE4X5_IMR)); + printk("\tomr: 0x%08x\n", inl(DE4X5_OMR)); + printk("\tsisr: 0x%08x\n", inl(DE4X5_SISR)); + printk("\tsicr: 0x%08x\n", inl(DE4X5_SICR)); + printk("\tstrr: 0x%08x\n", inl(DE4X5_STRR)); + printk("\tsigr: 0x%08x\n", inl(DE4X5_SIGR)); + } + + MOD_INC_USE_COUNT; + + return status; +} + +/* +** Initialize the DE4X5 operating conditions. NB: a chip problem with the +** DC21140 requires using perfect filtering mode for that chip. Since I can't +** see why I'd want > 14 multicast addresses, I have changed all chips to use +** the perfect filtering mode. Keep the DMA burst length at 8: there seems +** to be data corruption problems if it is larger (UDP errors seen from a +** ttcp source). +*/ +static int +de4x5_init(struct net_device *dev) +{ + /* Lock out other processes whilst setting up the hardware */ + netif_stop_queue(dev); + + de4x5_sw_reset(dev); + + /* Autoconfigure the connected port */ + autoconf_media(dev); + + return 0; +} + +static int +de4x5_sw_reset(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int i, j, status = 0; + s32 bmr, omr; + + /* Select the MII or SRL port now and RESET the MAC */ + if (!lp->useSROM) { + if (lp->phy[lp->active].id != 0) { + lp->infoblock_csr6 = OMR_SDP | OMR_PS | OMR_HBD; + } else { + lp->infoblock_csr6 = OMR_SDP | OMR_TTM; + } + de4x5_switch_mac_port(dev); + } + + /* + ** Set the programmable burst length to 8 longwords for all the DC21140 + ** Fasternet chips and 4 longwords for all others: DMA errors result + ** without these values. Cache align 16 long. + */ + bmr = (lp->chipset==DC21140 ? PBL_8 : PBL_4) | DESC_SKIP_LEN | DE4X5_CACHE_ALIGN; + bmr |= ((lp->chipset & ~0x00ff)==DC2114x ? BMR_RML : 0); + outl(bmr, DE4X5_BMR); + + omr = inl(DE4X5_OMR) & ~OMR_PR; /* Turn off promiscuous mode */ + if (lp->chipset == DC21140) { + omr |= (OMR_SDP | OMR_SB); + } + lp->setup_f = PERFECT; + outl(lp->dma_rings, DE4X5_RRBA); + outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), + DE4X5_TRBA); + + lp->rx_new = lp->rx_old = 0; + lp->tx_new = lp->tx_old = 0; + + for (i = 0; i < lp->rxRingSize; i++) { + lp->rx_ring[i].status = cpu_to_le32(R_OWN); + } + + for (i = 0; i < lp->txRingSize; i++) { + lp->tx_ring[i].status = cpu_to_le32(0); + } + + barrier(); + + /* Build the setup frame depending on filtering mode */ + SetMulticastFilter(dev); + + load_packet(dev, lp->setup_frame, PERFECT_F|TD_SET|SETUP_FRAME_LEN, (struct sk_buff *)1); + outl(omr|OMR_ST, DE4X5_OMR); + + /* Poll for setup frame completion (adapter interrupts are disabled now) */ + sti(); /* Ensure timer interrupts */ + for (j=0, i=0;(i<500) && (j==0);i++) { /* Upto 500ms delay */ + mdelay(1); + if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1; + } + outl(omr, DE4X5_OMR); /* Stop everything! */ + + if (j == 0) { + printk("%s: Setup frame timed out, status %08x\n", dev->name, + inl(DE4X5_STS)); + status = -EIO; + } + + lp->tx_new = (++lp->tx_new) % lp->txRingSize; + lp->tx_old = lp->tx_new; + + return status; +} + +/* +** Writes a socket buffer address to the next available transmit descriptor. +*/ +static int +de4x5_queue_pkt(struct sk_buff *skb, struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int status = 0; + u_long flags = 0; + + netif_stop_queue(dev); + if (lp->tx_enable == NO) { /* Cannot send for now */ + return -1; + } + + /* + ** Clean out the TX ring asynchronously to interrupts - sometimes the + ** interrupts are lost by delayed descriptor status updates relative to + ** the irq assertion, especially with a busy PCI bus. + */ + spin_lock_irqsave(&lp->lock, flags); + de4x5_tx(dev); + spin_unlock_irqrestore(&lp->lock, flags); + + /* Test if cache is already locked - requeue skb if so */ + if (test_and_set_bit(0, (void *)&lp->cache.lock) && !lp->interrupt) + return -1; + + /* Transmit descriptor ring full or stale skb */ + if (netif_queue_stopped(dev) || (u_long) lp->tx_skb[lp->tx_new] > 1) { + if (lp->interrupt) { + de4x5_putb_cache(dev, skb); /* Requeue the buffer */ + } else { + de4x5_put_cache(dev, skb); + } + if (de4x5_debug & DEBUG_TX) { + printk("%s: transmit busy, lost media or stale skb found:\n STS:%08x\n tbusy:%d\n IMR:%08x\n OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), netif_queue_stopped(dev), inl(DE4X5_IMR), inl(DE4X5_OMR), ((u_long) lp->tx_skb[lp->tx_new] > 1) ? "YES" : "NO"); + } + } else if (skb->len > 0) { + /* If we already have stuff queued locally, use that first */ + if (lp->cache.skb && !lp->interrupt) { + de4x5_put_cache(dev, skb); + skb = de4x5_get_cache(dev); + } + + while (skb && !netif_queue_stopped(dev) && + (u_long) lp->tx_skb[lp->tx_new] <= 1) { + spin_lock_irqsave(&lp->lock, flags); + netif_stop_queue(dev); + load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb); + lp->stats.tx_bytes += skb->len; + outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */ + + lp->tx_new = (++lp->tx_new) % lp->txRingSize; + dev->trans_start = jiffies; + + if (TX_BUFFS_AVAIL) { + netif_start_queue(dev); /* Another pkt may be queued */ + } + skb = de4x5_get_cache(dev); + spin_unlock_irqrestore(&lp->lock, flags); + } + if (skb) de4x5_putb_cache(dev, skb); + } + + lp->cache.lock = 0; + + return status; +} + +/* +** The DE4X5 interrupt handler. +** +** I/O Read/Writes through intermediate PCI bridges are never 'posted', +** so that the asserted interrupt always has some real data to work with - +** if these I/O accesses are ever changed to memory accesses, ensure the +** STS write is read immediately to complete the transaction if the adapter +** is not on bus 0. Lost interrupts can still occur when the PCI bus load +** is high and descriptor status bits cannot be set before the associated +** interrupt is asserted and this routine entered. +*/ +static void +de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct de4x5_private *lp; + s32 imr, omr, sts, limit; + u_long iobase; + + if (dev == NULL) { + printk ("de4x5_interrupt(): irq %d for unknown device.\n", irq); + return; + } + lp = (struct de4x5_private *)dev->priv; + spin_lock(&lp->lock); + iobase = dev->base_addr; + + DISABLE_IRQs; /* Ensure non re-entrancy */ + + if (test_and_set_bit(MASK_INTERRUPTS, (void*) &lp->interrupt)) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + + synchronize_irq(); + + for (limit=0; limit<8; limit++) { + sts = inl(DE4X5_STS); /* Read IRQ status */ + outl(sts, DE4X5_STS); /* Reset the board interrupts */ + + if (!(sts & lp->irq_mask)) break;/* All done */ + + if (sts & (STS_RI | STS_RU)) /* Rx interrupt (packet[s] arrived) */ + de4x5_rx(dev); + + if (sts & (STS_TI | STS_TU)) /* Tx interrupt (packet sent) */ + de4x5_tx(dev); + + if (sts & STS_LNF) { /* TP Link has failed */ + lp->irq_mask &= ~IMR_LFM; + } + + if (sts & STS_UNF) { /* Transmit underrun */ + de4x5_txur(dev); + } + + if (sts & STS_SE) { /* Bus Error */ + STOP_DE4X5; + printk("%s: Fatal bus error occurred, sts=%#8x, device stopped.\n", + dev->name, sts); + spin_unlock(&lp->lock); + return; + } + } + + /* Load the TX ring with any locally stored packets */ + if (!test_and_set_bit(0, (void *)&lp->cache.lock)) { + while (lp->cache.skb && !netif_queue_stopped(dev) && lp->tx_enable) { + de4x5_queue_pkt(de4x5_get_cache(dev), dev); + } + lp->cache.lock = 0; + } + + lp->interrupt = UNMASK_INTERRUPTS; + ENABLE_IRQs; + spin_unlock(&lp->lock); + + return; +} + +static int +de4x5_rx(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int entry; + s32 status; + + for (entry=lp->rx_new; (s32)le32_to_cpu(lp->rx_ring[entry].status)>=0; + entry=lp->rx_new) { + status = (s32)le32_to_cpu(lp->rx_ring[entry].status); + + if (lp->rx_ovf) { + if (inl(DE4X5_MFC) & MFC_FOCM) { + de4x5_rx_ovfc(dev); + break; + } + } + + if (status & RD_FS) { /* Remember the start of frame */ + lp->rx_old = entry; + } + + if (status & RD_LS) { /* Valid frame status */ + if (lp->tx_enable) lp->linkOK++; + if (status & RD_ES) { /* There was an error. */ + lp->stats.rx_errors++; /* Update the error stats. */ + if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++; + if (status & RD_CE) lp->stats.rx_crc_errors++; + if (status & RD_OF) lp->stats.rx_fifo_errors++; + if (status & RD_TL) lp->stats.rx_length_errors++; + if (status & RD_RF) lp->pktStats.rx_runt_frames++; + if (status & RD_CS) lp->pktStats.rx_collision++; + if (status & RD_DB) lp->pktStats.rx_dribble++; + if (status & RD_OF) lp->pktStats.rx_overflow++; + } else { /* A valid frame received */ + struct sk_buff *skb; + short pkt_len = (short)(le32_to_cpu(lp->rx_ring[entry].status) + >> 16) - 4; + + if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) { + printk("%s: Insufficient memory; nuking packet.\n", + dev->name); + lp->stats.rx_dropped++; + } else { + de4x5_dbg_rx(skb, pkt_len); + + /* Push up the protocol stack */ + skb->protocol=eth_type_trans(skb,dev); + de4x5_local_stats(dev, skb->data, pkt_len); + netif_rx(skb); + + /* Update stats */ + dev->last_rx = jiffies; + lp->stats.rx_packets++; + lp->stats.rx_bytes += pkt_len; + } + } + + /* Change buffer ownership for this frame, back to the adapter */ + for (;lp->rx_old!=entry;lp->rx_old=(++lp->rx_old)%lp->rxRingSize) { + lp->rx_ring[lp->rx_old].status = cpu_to_le32(R_OWN); + barrier(); + } + lp->rx_ring[entry].status = cpu_to_le32(R_OWN); + barrier(); + } + + /* + ** Update entry information + */ + lp->rx_new = (++lp->rx_new) % lp->rxRingSize; + } + + return 0; +} + +static inline void +de4x5_free_tx_buff(struct de4x5_private *lp, int entry) +{ + pci_unmap_single(lp->pdev, le32_to_cpu(lp->tx_ring[entry].buf), + le32_to_cpu(lp->tx_ring[entry].des1) & TD_TBS1, + PCI_DMA_TODEVICE); + if ((u_long) lp->tx_skb[entry] > 1) + dev_kfree_skb_irq(lp->tx_skb[entry]); + lp->tx_skb[entry] = NULL; +} + +/* +** Buffer sent - check for TX buffer errors. +*/ +static int +de4x5_tx(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int entry; + s32 status; + + for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) { + status = (s32)le32_to_cpu(lp->tx_ring[entry].status); + if (status < 0) { /* Buffer not sent yet */ + break; + } else if (status != 0x7fffffff) { /* Not setup frame */ + if (status & TD_ES) { /* An error happened */ + lp->stats.tx_errors++; + if (status & TD_NC) lp->stats.tx_carrier_errors++; + if (status & TD_LC) lp->stats.tx_window_errors++; + if (status & TD_UF) lp->stats.tx_fifo_errors++; + if (status & TD_EC) lp->pktStats.excessive_collisions++; + if (status & TD_DE) lp->stats.tx_aborted_errors++; + + if (TX_PKT_PENDING) { + outl(POLL_DEMAND, DE4X5_TPD);/* Restart a stalled TX */ + } + } else { /* Packet sent */ + lp->stats.tx_packets++; + if (lp->tx_enable) lp->linkOK++; + } + /* Update the collision counter */ + lp->stats.collisions += ((status & TD_EC) ? 16 : + ((status & TD_CC) >> 3)); + + /* Free the buffer. */ + if (lp->tx_skb[entry] != NULL) + de4x5_free_tx_buff(lp, entry); + } + + /* Update all the pointers */ + lp->tx_old = (++lp->tx_old) % lp->txRingSize; + } + + /* Any resources available? */ + if (TX_BUFFS_AVAIL && netif_queue_stopped(dev)) { + if (lp->interrupt) + netif_wake_queue(dev); + else + netif_start_queue(dev); + } + + return 0; +} + +static int +de4x5_ast(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int next_tick = DE4X5_AUTOSENSE_MS; + + disable_ast(dev); + + if (lp->useSROM) { + next_tick = srom_autoconf(dev); + } else if (lp->chipset == DC21140) { + next_tick = dc21140m_autoconf(dev); + } else if (lp->chipset == DC21041) { + next_tick = dc21041_autoconf(dev); + } else if (lp->chipset == DC21040) { + next_tick = dc21040_autoconf(dev); + } + lp->linkOK = 0; + enable_ast(dev, next_tick); + + return 0; +} + +static int +de4x5_txur(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int omr; + + omr = inl(DE4X5_OMR); + if (!(omr & OMR_SF) || (lp->chipset==DC21041) || (lp->chipset==DC21040)) { + omr &= ~(OMR_ST|OMR_SR); + outl(omr, DE4X5_OMR); + while (inl(DE4X5_STS) & STS_TS); + if ((omr & OMR_TR) < OMR_TR) { + omr += 0x4000; + } else { + omr |= OMR_SF; + } + outl(omr | OMR_ST | OMR_SR, DE4X5_OMR); + } + + return 0; +} + +static int +de4x5_rx_ovfc(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int omr; + + omr = inl(DE4X5_OMR); + outl(omr & ~OMR_SR, DE4X5_OMR); + while (inl(DE4X5_STS) & STS_RS); + + for (; (s32)le32_to_cpu(lp->rx_ring[lp->rx_new].status)>=0;) { + lp->rx_ring[lp->rx_new].status = cpu_to_le32(R_OWN); + lp->rx_new = (++lp->rx_new % lp->rxRingSize); + } + + outl(omr, DE4X5_OMR); + + return 0; +} + +static int +de4x5_close(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 imr, omr; + + disable_ast(dev); + + netif_stop_queue(dev); + + if (de4x5_debug & DEBUG_CLOSE) { + printk("%s: Shutting down ethercard, status was %8.8x.\n", + dev->name, inl(DE4X5_STS)); + } + + /* + ** We stop the DE4X5 here... mask interrupts and stop TX & RX + */ + DISABLE_IRQs; + STOP_DE4X5; + + /* Free the associated irq */ + free_irq(dev->irq, dev); + lp->state = CLOSED; + + /* Free any socket buffers */ + de4x5_free_rx_buffs(dev); + de4x5_free_tx_buffs(dev); + + MOD_DEC_USE_COUNT; + + /* Put the adapter to sleep to save power */ + yawn(dev, SLEEP); + + return 0; +} + +static struct net_device_stats * +de4x5_get_stats(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + lp->stats.rx_missed_errors = (int)(inl(DE4X5_MFC) & (MFC_OVFL | MFC_CNTR)); + + return &lp->stats; +} + +static void +de4x5_local_stats(struct net_device *dev, char *buf, int pkt_len) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int i; + + for (i=1; ipktStats.bins[i]++; + i = DE4X5_PKT_STAT_SZ; + } + } + if (buf[0] & 0x01) { /* Multicast/Broadcast */ + if ((*(s32 *)&buf[0] == -1) && (*(s16 *)&buf[4] == -1)) { + lp->pktStats.broadcast++; + } else { + lp->pktStats.multicast++; + } + } else if ((*(s32 *)&buf[0] == *(s32 *)&dev->dev_addr[0]) && + (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) { + lp->pktStats.unicast++; + } + + lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */ + if (lp->pktStats.bins[0] == 0) { /* Reset counters */ + memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats)); + } + + return; +} + +/* +** Removes the TD_IC flag from previous descriptor to improve TX performance. +** If the flag is changed on a descriptor that is being read by the hardware, +** I assume PCI transaction ordering will mean you are either successful or +** just miss asserting the change to the hardware. Anyway you're messing with +** a descriptor you don't own, but this shouldn't kill the chip provided +** the descriptor register is read only to the hardware. +*/ +static void +load_packet(struct net_device *dev, char *buf, u32 flags, struct sk_buff *skb) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int entry = (lp->tx_new ? lp->tx_new-1 : lp->txRingSize-1); + dma_addr_t buf_dma = pci_map_single(lp->pdev, buf, flags & TD_TBS1, PCI_DMA_TODEVICE); + + lp->tx_ring[lp->tx_new].buf = cpu_to_le32(buf_dma); + lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER); + lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags); + lp->tx_skb[lp->tx_new] = skb; + lp->tx_ring[entry].des1 &= cpu_to_le32(~TD_IC); + barrier(); + + lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN); + barrier(); +} + +/* +** Set or clear the multicast filter for this adaptor. +*/ +static void +set_multicast_list(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + /* First, double check that the adapter is open */ + if (lp->state == OPEN) { + if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ + u32 omr; + omr = inl(DE4X5_OMR); + omr |= OMR_PR; + outl(omr, DE4X5_OMR); + } else { + SetMulticastFilter(dev); + load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | + SETUP_FRAME_LEN, (struct sk_buff *)1); + + lp->tx_new = (++lp->tx_new) % lp->txRingSize; + outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */ + dev->trans_start = jiffies; + } + } + + return; +} + +/* +** Calculate the hash code and update the logical address filter +** from a list of ethernet multicast addresses. +** Little endian crc one liner from Matt Thomas, DEC. +*/ +static void +SetMulticastFilter(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + struct dev_mc_list *dmi=dev->mc_list; + u_long iobase = dev->base_addr; + int i, j, bit, byte; + u16 hashcode; + u32 omr, crc; + char *pa; + unsigned char *addrs; + + omr = inl(DE4X5_OMR); + omr &= ~(OMR_PR | OMR_PM); + pa = build_setup_frame(dev, ALL); /* Build the basic frame */ + + if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 14)) { + omr |= OMR_PM; /* Pass all multicasts */ + } else if (lp->setup_f == HASH_PERF) { /* Hash Filtering */ + for (i=0;imc_count;i++) { /* for each address in the list */ + addrs=dmi->dmi_addr; + dmi=dmi->next; + if ((*addrs & 0x01) == 1) { /* multicast address? */ + crc = ether_crc_le(ETH_ALEN, addrs); + hashcode = crc & HASH_BITS; /* hashcode is 9 LSb of CRC */ + + byte = hashcode >> 3; /* bit[3-8] -> byte in filter */ + bit = 1 << (hashcode & 0x07);/* bit[0-2] -> bit in byte */ + + byte <<= 1; /* calc offset into setup frame */ + if (byte & 0x02) { + byte -= 1; + } + lp->setup_frame[byte] |= bit; + } + } + } else { /* Perfect filtering */ + for (j=0; jmc_count; j++) { + addrs=dmi->dmi_addr; + dmi=dmi->next; + for (i=0; ibus = EISA; + + if (ioaddr == 0) { /* Autoprobing */ + iobase = EISA_SLOT_INC; /* Get the first slot address */ + i = 1; + maxSlots = MAX_EISA_SLOTS; + } else { /* Probe a specific location */ + iobase = ioaddr; + i = (ioaddr >> 12); + maxSlots = i + 1; + } + + for (status = -ENODEV; (i> 8) & 0x00ffff00; + vendor = (u_short) cfid; + + /* Read the EISA Configuration Registers */ + irq = inb(EISA_REG0); + irq = de4x5_irq[(irq >> 1) & 0x03]; + + if (is_DC2114x) { + device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143); + } + lp->chipset = device; + + /* Write the PCI Configuration Registers */ + outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS); + outl(0x00006000, PCI_CFLT); + outl(iobase, PCI_CBIO); + + DevicePresent(EISA_APROM); + + dev->irq = irq; + if ((status = de4x5_hw_init(dev, iobase, NULL)) == 0) { + num_de4x5s++; + if (loading_module) link_modules(lastModule, dev); + lastEISA = i; + return; + } + } + + if (ioaddr == 0) lastEISA = i; + + return; +} +#endif /* !(__sparc_v9__) && !(__powerpc__) && !defined(__alpha__) */ + +/* +** PCI bus I/O device probe +** NB: PCI I/O accesses and Bus Mastering are enabled by the PCI BIOS, not +** the driver. Some PCI BIOS's, pre V2.1, need the slot + features to be +** enabled by the user first in the set up utility. Hence we just check for +** enabled features and silently ignore the card if they're not. +** +** STOP PRESS: Some BIOS's __require__ the driver to enable the bus mastering +** bit. Here, check for I/O accesses and then set BM. If you put the card in +** a non BM slot, you're on your own (and complain to the PC vendor that your +** PC doesn't conform to the PCI standard)! +** +** This function is only compatible with the *latest* 2.1.x kernels. For 2.0.x +** kernels use the V0.535[n] drivers. +*/ +#define PCI_LAST_DEV 32 + +static void __init +pci_probe(struct net_device *dev, u_long ioaddr) +{ + u_char pb, pbus, dev_num, dnum, timer; + u_short vendor, index, status; + u_int irq = 0, device, class = DE4X5_CLASS_CODE; + u_long iobase = 0; /* Clear upper 32 bits in Alphas */ + struct bus_type *lp = &bus; + + if (lastPCI == NO_MORE_PCI) return; + + if (!pcibios_present()) { + lastPCI = NO_MORE_PCI; + return; /* No PCI bus in this machine! */ + } + + lp->bus = PCI; + lp->bus_num = 0; + + if ((ioaddr < 0x1000) && loading_module) { + pbus = (u_short)(ioaddr >> 8); + dnum = (u_short)(ioaddr & 0xff); + } else { + pbus = 0; + dnum = 0; + } + + for (index=lastPCI+1;(pdev = pci_find_class(class, pdev))!=NULL;index++) { + dev_num = PCI_SLOT(pdev->devfn); + pb = pdev->bus->number; + if ((pbus || dnum) && ((pbus != pb) || (dnum != dev_num))) continue; + + vendor = pdev->vendor; + device = pdev->device << 8; + if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue; + + /* Search for an SROM on this bus */ + if (lp->bus_num != pb) { + lp->bus_num = pb; + srom_search(pdev); + } + + /* Get the chip configuration revision register */ + pcibios_read_config_dword(pb, pdev->devfn, PCI_REVISION_ID, &cfrv); + + /* Set the device number information */ + lp->device = dev_num; + lp->bus_num = pb; + + /* Set the chipset information */ + if (is_DC2114x) { + device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143); + } + lp->chipset = device; + + /* Get the board I/O address (64 bits on sparc64) */ + iobase = pci_resource_start(pdev, 0); + + /* Fetch the IRQ to be used */ + irq = pdev->irq; + if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; + + /* Check if I/O accesses and Bus Mastering are enabled */ + pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status); +#ifdef __powerpc__ + if (!(status & PCI_COMMAND_IO)) { + status |= PCI_COMMAND_IO; + pcibios_write_config_word(pb, pdev->devfn, PCI_COMMAND, status); + pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status); + } +#endif /* __powerpc__ */ + if (!(status & PCI_COMMAND_IO)) continue; + + if (!(status & PCI_COMMAND_MASTER)) { + status |= PCI_COMMAND_MASTER; + pcibios_write_config_word(pb, pdev->devfn, PCI_COMMAND, status); + pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status); + } + if (!(status & PCI_COMMAND_MASTER)) continue; + + /* Check the latency timer for values >= 0x60 */ + pcibios_read_config_byte(pb, pdev->devfn, PCI_LATENCY_TIMER, &timer); + if (timer < 0x60) { + pcibios_write_config_byte(pb, pdev->devfn, PCI_LATENCY_TIMER, 0x60); + } + + DevicePresent(DE4X5_APROM); + if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) { + dev->irq = irq; + if ((status = de4x5_hw_init(dev, iobase, pdev)) == 0) { + num_de4x5s++; + lastPCI = index; + if (loading_module) link_modules(lastModule, dev); + return; + } + } else if (ioaddr != 0) { + printk("%s: region already allocated at 0x%04lx.\n", dev->name, + iobase); + } + } + + lastPCI = NO_MORE_PCI; + + return; +} + +/* +** This function searches the current bus (which is >0) for a DECchip with an +** SROM, so that in multiport cards that have one SROM shared between multiple +** DECchips, we can find the base SROM irrespective of the BIOS scan direction. +** For single port cards this is a time waster... +*/ +static void __init +srom_search(struct pci_dev *dev) +{ + u_char pb; + u_short vendor, status; + u_int irq = 0, device; + u_long iobase = 0; /* Clear upper 32 bits in Alphas */ + int i, j; + struct bus_type *lp = &bus; + struct list_head *walk = &dev->bus_list; + + for (walk = walk->next; walk != &dev->bus_list; walk = walk->next) { + struct pci_dev *this_dev = pci_dev_b(walk); + + /* Skip the pci_bus list entry */ + if (list_entry(walk, struct pci_bus, devices) == dev->bus) continue; + + vendor = this_dev->vendor; + device = this_dev->device << 8; + if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue; + + /* Get the chip configuration revision register */ + pb = this_dev->bus->number; + pcibios_read_config_dword(pb, this_dev->devfn, PCI_REVISION_ID, &cfrv); + + /* Set the device number information */ + lp->device = PCI_SLOT(this_dev->devfn); + lp->bus_num = pb; + + /* Set the chipset information */ + if (is_DC2114x) { + device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143); + } + lp->chipset = device; + + /* Get the board I/O address (64 bits on sparc64) */ + iobase = pci_resource_start(this_dev, 0); + + /* Fetch the IRQ to be used */ + irq = this_dev->irq; + if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; + + /* Check if I/O accesses are enabled */ + pcibios_read_config_word(pb, this_dev->devfn, PCI_COMMAND, &status); + if (!(status & PCI_COMMAND_IO)) continue; + + /* Search for a valid SROM attached to this DECchip */ + DevicePresent(DE4X5_APROM); + for (j=0, i=0; isrom + SROM_HWADD + i); + } + if ((j != 0) && (j != 0x5fa)) { + last.chipset = device; + last.bus = pb; + last.irq = irq; + for (i=0; isrom + SROM_HWADD + i); + } + return; + } + } + + return; +} + +static void __init +link_modules(struct net_device *dev, struct net_device *tmp) +{ + struct net_device *p=dev; + + if (p) { + while (((struct de4x5_private *)(p->priv))->next_module) { + p = ((struct de4x5_private *)(p->priv))->next_module; + } + + if (dev != tmp) { + ((struct de4x5_private *)(p->priv))->next_module = tmp; + } else { + ((struct de4x5_private *)(p->priv))->next_module = NULL; + } + } + + return; +} + +/* +** Auto configure the media here rather than setting the port at compile +** time. This routine is called by de4x5_init() and when a loss of media is +** detected (excessive collisions, loss of carrier, no carrier or link fail +** [TP] or no recent receive activity) to check whether the user has been +** sneaky and changed the port on us. +*/ +static int +autoconf_media(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int next_tick = DE4X5_AUTOSENSE_MS; + + lp->linkOK = 0; + lp->c_media = AUTO; /* Bogus last media */ + disable_ast(dev); + inl(DE4X5_MFC); /* Zero the lost frames counter */ + lp->media = INIT; + lp->tcount = 0; + + if (lp->useSROM) { + next_tick = srom_autoconf(dev); + } else if (lp->chipset == DC21040) { + next_tick = dc21040_autoconf(dev); + } else if (lp->chipset == DC21041) { + next_tick = dc21041_autoconf(dev); + } else if (lp->chipset == DC21140) { + next_tick = dc21140m_autoconf(dev); + } + + enable_ast(dev, next_tick); + + return (lp->media); +} + +/* +** Autoconfigure the media when using the DC21040. AUI cannot be distinguished +** from BNC as the port has a jumper to set thick or thin wire. When set for +** BNC, the BNC port will indicate activity if it's not terminated correctly. +** The only way to test for that is to place a loopback packet onto the +** network and watch for errors. Since we're messing with the interrupt mask +** register, disable the board interrupts and do not allow any more packets to +** be queued to the hardware. Re-enable everything only when the media is +** found. +** I may have to "age out" locally queued packets so that the higher layer +** timeouts don't effectively duplicate packets on the network. +*/ +static int +dc21040_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int next_tick = DE4X5_AUTOSENSE_MS; + s32 imr; + + switch (lp->media) { + case INIT: + DISABLE_IRQs; + lp->tx_enable = NO; + lp->timeout = -1; + de4x5_save_skbs(dev); + if ((lp->autosense == AUTO) || (lp->autosense == TP)) { + lp->media = TP; + } else if ((lp->autosense == BNC) || (lp->autosense == AUI) || (lp->autosense == BNC_AUI)) { + lp->media = BNC_AUI; + } else if (lp->autosense == EXT_SIA) { + lp->media = EXT_SIA; + } else { + lp->media = NC; + } + lp->local_state = 0; + next_tick = dc21040_autoconf(dev); + break; + + case TP: + next_tick = dc21040_state(dev, 0x8f01, 0xffff, 0x0000, 3000, BNC_AUI, + TP_SUSPECT, test_tp); + break; + + case TP_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21040_autoconf); + break; + + case BNC: + case AUI: + case BNC_AUI: + next_tick = dc21040_state(dev, 0x8f09, 0x0705, 0x0006, 3000, EXT_SIA, + BNC_AUI_SUSPECT, ping_media); + break; + + case BNC_AUI_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, BNC_AUI, ping_media, dc21040_autoconf); + break; + + case EXT_SIA: + next_tick = dc21040_state(dev, 0x3041, 0x0000, 0x0006, 3000, + NC, EXT_SIA_SUSPECT, ping_media); + break; + + case EXT_SIA_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, EXT_SIA, ping_media, dc21040_autoconf); + break; + + case NC: + /* default to TP for all */ + reset_init_sia(dev, 0x8f01, 0xffff, 0x0000); + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tx_enable = NO; + break; + } + + return next_tick; +} + +static int +dc21040_state(struct net_device *dev, int csr13, int csr14, int csr15, int timeout, + int next_state, int suspect_state, + int (*fn)(struct net_device *, int)) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int next_tick = DE4X5_AUTOSENSE_MS; + int linkBad; + + switch (lp->local_state) { + case 0: + reset_init_sia(dev, csr13, csr14, csr15); + lp->local_state++; + next_tick = 500; + break; + + case 1: + if (!lp->tx_enable) { + linkBad = fn(dev, timeout); + if (linkBad < 0) { + next_tick = linkBad & ~TIMER_CB; + } else { + if (linkBad && (lp->autosense == AUTO)) { + lp->local_state = 0; + lp->media = next_state; + } else { + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = suspect_state; + next_tick = 3000; + } + break; + } + + return next_tick; +} + +static int +de4x5_suspect_state(struct net_device *dev, int timeout, int prev_state, + int (*fn)(struct net_device *, int), + int (*asfn)(struct net_device *)) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int next_tick = DE4X5_AUTOSENSE_MS; + int linkBad; + + switch (lp->local_state) { + case 1: + if (lp->linkOK) { + lp->media = prev_state; + } else { + lp->local_state++; + next_tick = asfn(dev); + } + break; + + case 2: + linkBad = fn(dev, timeout); + if (linkBad < 0) { + next_tick = linkBad & ~TIMER_CB; + } else if (!linkBad) { + lp->local_state--; + lp->media = prev_state; + } else { + lp->media = INIT; + lp->tcount++; + } + } + + return next_tick; +} + +/* +** Autoconfigure the media when using the DC21041. AUI needs to be tested +** before BNC, because the BNC port will indicate activity if it's not +** terminated correctly. The only way to test for that is to place a loopback +** packet onto the network and watch for errors. Since we're messing with +** the interrupt mask register, disable the board interrupts and do not allow +** any more packets to be queued to the hardware. Re-enable everything only +** when the media is found. +*/ +static int +dc21041_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 sts, irqs, irq_mask, imr, omr; + int next_tick = DE4X5_AUTOSENSE_MS; + + switch (lp->media) { + case INIT: + DISABLE_IRQs; + lp->tx_enable = NO; + lp->timeout = -1; + de4x5_save_skbs(dev); /* Save non transmitted skb's */ + if ((lp->autosense == AUTO) || (lp->autosense == TP_NW)) { + lp->media = TP; /* On chip auto negotiation is broken */ + } else if (lp->autosense == TP) { + lp->media = TP; + } else if (lp->autosense == BNC) { + lp->media = BNC; + } else if (lp->autosense == AUI) { + lp->media = AUI; + } else { + lp->media = NC; + } + lp->local_state = 0; + next_tick = dc21041_autoconf(dev); + break; + + case TP_NW: + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */ + outl(omr | OMR_FDX, DE4X5_OMR); + } + irqs = STS_LNF | STS_LNP; + irq_mask = IMR_LFM | IMR_LPM; + sts = test_media(dev, irqs, irq_mask, 0xef01, 0xffff, 0x0008, 2400); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (sts & STS_LNP) { + lp->media = ANS; + } else { + lp->media = AUI; + } + next_tick = dc21041_autoconf(dev); + } + break; + + case ANS: + if (!lp->tx_enable) { + irqs = STS_LNP; + irq_mask = IMR_LPM; + sts = test_ans(dev, irqs, irq_mask, 3000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (!(sts & STS_LNP) && (lp->autosense == AUTO)) { + lp->media = TP; + next_tick = dc21041_autoconf(dev); + } else { + lp->local_state = 1; + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = ANS_SUSPECT; + next_tick = 3000; + } + break; + + case ANS_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, ANS, test_tp, dc21041_autoconf); + break; + + case TP: + if (!lp->tx_enable) { + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for TP */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = STS_LNF | STS_LNP; + irq_mask = IMR_LFM | IMR_LPM; + sts = test_media(dev,irqs, irq_mask, 0xef01, 0xff3f, 0x0008, 2400); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (!(sts & STS_LNP) && (lp->autosense == AUTO)) { + if (inl(DE4X5_SISR) & SISR_NRA) { + lp->media = AUI; /* Non selected port activity */ + } else { + lp->media = BNC; + } + next_tick = dc21041_autoconf(dev); + } else { + lp->local_state = 1; + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = TP_SUSPECT; + next_tick = 3000; + } + break; + + case TP_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21041_autoconf); + break; + + case AUI: + if (!lp->tx_enable) { + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0xef09, 0xf73d, 0x000e, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { + lp->media = BNC; + next_tick = dc21041_autoconf(dev); + } else { + lp->local_state = 1; + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = AUI_SUSPECT; + next_tick = 3000; + } + break; + + case AUI_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc21041_autoconf); + break; + + case BNC: + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0xef09, 0xf73d, 0x0006, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + lp->local_state++; /* Ensure media connected */ + next_tick = dc21041_autoconf(dev); + } + break; + + case 1: + if (!lp->tx_enable) { + if ((sts = ping_media(dev, 3000)) < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (sts) { + lp->local_state = 0; + lp->media = NC; + } else { + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = BNC_SUSPECT; + next_tick = 3000; + } + break; + } + break; + + case BNC_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc21041_autoconf); + break; + + case NC: + omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */ + outl(omr | OMR_FDX, DE4X5_OMR); + reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */ + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tx_enable = NO; + break; + } + + return next_tick; +} + +/* +** Some autonegotiation chips are broken in that they do not return the +** acknowledge bit (anlpa & MII_ANLPA_ACK) in the link partner advertisement +** register, except at the first power up negotiation. +*/ +static int +dc21140m_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int ana, anlpa, cap, cr, slnk, sr; + int next_tick = DE4X5_AUTOSENSE_MS; + u_long imr, omr, iobase = dev->base_addr; + + switch(lp->media) { + case INIT: + if (lp->timeout < 0) { + DISABLE_IRQs; + lp->tx_enable = FALSE; + lp->linkOK = 0; + de4x5_save_skbs(dev); /* Save non transmitted skb's */ + } + if ((next_tick = de4x5_reset_phy(dev)) < 0) { + next_tick &= ~TIMER_CB; + } else { + if (lp->useSROM) { + if (srom_map_media(dev) < 0) { + lp->tcount++; + return next_tick; + } + srom_exec(dev, lp->phy[lp->active].gep); + if (lp->infoblock_media == ANS) { + ana = lp->phy[lp->active].ana | MII_ANA_CSMA; + mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + } + } else { + lp->tmp = MII_SR_ASSC; /* Fake out the MII speed set */ + SET_10Mb; + if (lp->autosense == _100Mb) { + lp->media = _100Mb; + } else if (lp->autosense == _10Mb) { + lp->media = _10Mb; + } else if ((lp->autosense == AUTO) && + ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { + ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); + ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); + mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + lp->media = ANS; + } else if (lp->autosense == AUTO) { + lp->media = SPD_DET; + } else if (is_spd_100(dev) && is_100_up(dev)) { + lp->media = _100Mb; + } else { + lp->media = NC; + } + } + lp->local_state = 0; + next_tick = dc21140m_autoconf(dev); + } + break; + + case ANS: + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + } + cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500); + if (cr < 0) { + next_tick = cr & ~TIMER_CB; + } else { + if (cr) { + lp->local_state = 0; + lp->media = SPD_DET; + } else { + lp->local_state++; + } + next_tick = dc21140m_autoconf(dev); + } + break; + + case 1: + if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) { + next_tick = sr & ~TIMER_CB; + } else { + lp->media = SPD_DET; + lp->local_state = 0; + if (sr) { /* Success! */ + lp->tmp = MII_SR_ASSC; + anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII); + ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + if (!(anlpa & MII_ANLPA_RF) && + (cap = anlpa & MII_ANLPA_TAF & ana)) { + if (cap & MII_ANA_100M) { + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); + lp->media = _100Mb; + } else if (cap & MII_ANA_10M) { + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); + + lp->media = _10Mb; + } + } + } /* Auto Negotiation failed to finish */ + next_tick = dc21140m_autoconf(dev); + } /* Auto Negotiation failed to start */ + break; + } + break; + + case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ + if (lp->timeout < 0) { + lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS : + (~gep_rd(dev) & GEP_LNP)); + SET_100Mb_PDET; + } + if ((slnk = test_for_100Mb(dev, 6500)) < 0) { + next_tick = slnk & ~TIMER_CB; + } else { + if (is_spd_100(dev) && is_100_up(dev)) { + lp->media = _100Mb; + } else if ((!is_spd_100(dev) && (is_10_up(dev) & lp->tmp))) { + lp->media = _10Mb; + } else { + lp->media = NC; + } + next_tick = dc21140m_autoconf(dev); + } + break; + + case _100Mb: /* Set 100Mb/s */ + next_tick = 3000; + if (!lp->tx_enable) { + SET_100Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + case BNC: + case AUI: + case _10Mb: /* Set 10Mb/s */ + next_tick = 3000; + if (!lp->tx_enable) { + SET_10Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + case NC: + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tx_enable = FALSE; + break; + } + + return next_tick; +} + +/* +** This routine may be merged into dc21140m_autoconf() sometime as I'm +** changing how I figure out the media - but trying to keep it backwards +** compatible with the de500-xa and de500-aa. +** Whether it's BNC, AUI, SYM or MII is sorted out in the infoblock +** functions and set during de4x5_mac_port() and/or de4x5_reset_phy(). +** This routine just has to figure out whether 10Mb/s or 100Mb/s is +** active. +** When autonegotiation is working, the ANS part searches the SROM for +** the highest common speed (TP) link that both can run and if that can +** be full duplex. That infoblock is executed and then the link speed set. +** +** Only _10Mb and _100Mb are tested here. +*/ +static int +dc2114x_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 cr, anlpa, ana, cap, irqs, irq_mask, imr, omr, slnk, sr, sts; + int next_tick = DE4X5_AUTOSENSE_MS; + + switch (lp->media) { + case INIT: + if (lp->timeout < 0) { + DISABLE_IRQs; + lp->tx_enable = FALSE; + lp->linkOK = 0; + lp->timeout = -1; + de4x5_save_skbs(dev); /* Save non transmitted skb's */ + if (lp->params.autosense & ~AUTO) { + srom_map_media(dev); /* Fixed media requested */ + if (lp->media != lp->params.autosense) { + lp->tcount++; + lp->media = INIT; + return next_tick; + } + lp->media = INIT; + } + } + if ((next_tick = de4x5_reset_phy(dev)) < 0) { + next_tick &= ~TIMER_CB; + } else { + if (lp->autosense == _100Mb) { + lp->media = _100Mb; + } else if (lp->autosense == _10Mb) { + lp->media = _10Mb; + } else if (lp->autosense == TP) { + lp->media = TP; + } else if (lp->autosense == BNC) { + lp->media = BNC; + } else if (lp->autosense == AUI) { + lp->media = AUI; + } else { + lp->media = SPD_DET; + if ((lp->infoblock_media == ANS) && + ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { + ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); + ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); + mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + lp->media = ANS; + } + } + lp->local_state = 0; + next_tick = dc2114x_autoconf(dev); + } + break; + + case ANS: + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + } + cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500); + if (cr < 0) { + next_tick = cr & ~TIMER_CB; + } else { + if (cr) { + lp->local_state = 0; + lp->media = SPD_DET; + } else { + lp->local_state++; + } + next_tick = dc2114x_autoconf(dev); + } + break; + + case 1: + if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) { + next_tick = sr & ~TIMER_CB; + } else { + lp->media = SPD_DET; + lp->local_state = 0; + if (sr) { /* Success! */ + lp->tmp = MII_SR_ASSC; + anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII); + ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + if (!(anlpa & MII_ANLPA_RF) && + (cap = anlpa & MII_ANLPA_TAF & ana)) { + if (cap & MII_ANA_100M) { + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); + lp->media = _100Mb; + } else if (cap & MII_ANA_10M) { + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); + lp->media = _10Mb; + } + } + } /* Auto Negotiation failed to finish */ + next_tick = dc2114x_autoconf(dev); + } /* Auto Negotiation failed to start */ + break; + } + break; + + case AUI: + if (!lp->tx_enable) { + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { + lp->media = BNC; + next_tick = dc2114x_autoconf(dev); + } else { + lp->local_state = 1; + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = AUI_SUSPECT; + next_tick = 3000; + } + break; + + case AUI_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc2114x_autoconf); + break; + + case BNC: + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + lp->local_state++; /* Ensure media connected */ + next_tick = dc2114x_autoconf(dev); + } + break; + + case 1: + if (!lp->tx_enable) { + if ((sts = ping_media(dev, 3000)) < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (sts) { + lp->local_state = 0; + lp->tcount++; + lp->media = INIT; + } else { + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = BNC_SUSPECT; + next_tick = 3000; + } + break; + } + break; + + case BNC_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc2114x_autoconf); + break; + + case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ + if (srom_map_media(dev) < 0) { + lp->tcount++; + lp->media = INIT; + return next_tick; + } + if (lp->media == _100Mb) { + if ((slnk = test_for_100Mb(dev, 6500)) < 0) { + lp->media = SPD_DET; + return (slnk & ~TIMER_CB); + } + } else { + if (wait_for_link(dev) < 0) { + lp->media = SPD_DET; + return PDET_LINK_WAIT; + } + } + if (lp->media == ANS) { /* Do MII parallel detection */ + if (is_spd_100(dev)) { + lp->media = _100Mb; + } else { + lp->media = _10Mb; + } + next_tick = dc2114x_autoconf(dev); + } else if (((lp->media == _100Mb) && is_100_up(dev)) || + (((lp->media == _10Mb) || (lp->media == TP) || + (lp->media == BNC) || (lp->media == AUI)) && + is_10_up(dev))) { + next_tick = dc2114x_autoconf(dev); + } else { + lp->tcount++; + lp->media = INIT; + } + break; + + case _10Mb: + next_tick = 3000; + if (!lp->tx_enable) { + SET_10Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + case _100Mb: + next_tick = 3000; + if (!lp->tx_enable) { + SET_100Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + default: + lp->tcount++; +printk("Huh?: media:%02x\n", lp->media); + lp->media = INIT; + break; + } + + return next_tick; +} + +static int +srom_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + return lp->infoleaf_fn(dev); +} + +/* +** This mapping keeps the original media codes and FDX flag unchanged. +** While it isn't strictly necessary, it helps me for the moment... +** The early return avoids a media state / SROM media space clash. +*/ +static int +srom_map_media(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + lp->fdx = 0; + if (lp->infoblock_media == lp->media) + return 0; + + switch(lp->infoblock_media) { + case SROM_10BASETF: + if (!lp->params.fdx) return -1; + lp->fdx = TRUE; + case SROM_10BASET: + if (lp->params.fdx && !lp->fdx) return -1; + if ((lp->chipset == DC21140) || ((lp->chipset & ~0x00ff) == DC2114x)) { + lp->media = _10Mb; + } else { + lp->media = TP; + } + break; + + case SROM_10BASE2: + lp->media = BNC; + break; + + case SROM_10BASE5: + lp->media = AUI; + break; + + case SROM_100BASETF: + if (!lp->params.fdx) return -1; + lp->fdx = TRUE; + case SROM_100BASET: + if (lp->params.fdx && !lp->fdx) return -1; + lp->media = _100Mb; + break; + + case SROM_100BASET4: + lp->media = _100Mb; + break; + + case SROM_100BASEFF: + if (!lp->params.fdx) return -1; + lp->fdx = TRUE; + case SROM_100BASEF: + if (lp->params.fdx && !lp->fdx) return -1; + lp->media = _100Mb; + break; + + case ANS: + lp->media = ANS; + lp->fdx = lp->params.fdx; + break; + + default: + printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, + lp->infoblock_media); + return -1; + break; + } + + return 0; +} + +static void +de4x5_init_connection(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + u_long flags = 0; + + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; /* Stop scrolling media messages */ + } + + spin_lock_irqsave(&lp->lock, flags); + de4x5_rst_desc_ring(dev); + de4x5_setup_intr(dev); + lp->tx_enable = YES; + spin_unlock_irqrestore(&lp->lock, flags); + outl(POLL_DEMAND, DE4X5_TPD); + + netif_wake_queue(dev); + + return; +} + +/* +** General PHY reset function. Some MII devices don't reset correctly +** since their MII address pins can float at voltages that are dependent +** on the signal pin use. Do a double reset to ensure a reset. +*/ +static int +de4x5_reset_phy(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int next_tick = 0; + + if ((lp->useSROM) || (lp->phy[lp->active].id)) { + if (lp->timeout < 0) { + if (lp->useSROM) { + if (lp->phy[lp->active].rst) { + srom_exec(dev, lp->phy[lp->active].rst); + srom_exec(dev, lp->phy[lp->active].rst); + } else if (lp->rst) { /* Type 5 infoblock reset */ + srom_exec(dev, lp->rst); + srom_exec(dev, lp->rst); + } + } else { + PHY_HARD_RESET; + } + if (lp->useMII) { + mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + } + } + if (lp->useMII) { + next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500); + } + } else if (lp->chipset == DC21140) { + PHY_HARD_RESET; + } + + return next_tick; +} + +static int +test_media(struct net_device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 sts, csr12; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + if (!lp->useSROM) { /* Already done if by SROM, else dc2104[01] */ + reset_init_sia(dev, csr13, csr14, csr15); + } + + /* set up the interrupt mask */ + outl(irq_mask, DE4X5_IMR); + + /* clear all pending interrupts */ + sts = inl(DE4X5_STS); + outl(sts, DE4X5_STS); + + /* clear csr12 NRA and SRA bits */ + if ((lp->chipset == DC21041) || lp->useSROM) { + csr12 = inl(DE4X5_SISR); + outl(csr12, DE4X5_SISR); + } + } + + sts = inl(DE4X5_STS) & ~TIMER_CB; + + if (!(sts & irqs) && --lp->timeout) { + sts = 100 | TIMER_CB; + } else { + lp->timeout = -1; + } + + return sts; +} + +static int +test_tp(struct net_device *dev, s32 msec) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int sisr; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + } + + sisr = (inl(DE4X5_SISR) & ~TIMER_CB) & (SISR_LKF | SISR_NCR); + + if (sisr && --lp->timeout) { + sisr = 100 | TIMER_CB; + } else { + lp->timeout = -1; + } + + return sisr; +} + +/* +** Samples the 100Mb Link State Signal. The sample interval is important +** because too fast a rate can give erroneous results and confuse the +** speed sense algorithm. +*/ +#define SAMPLE_INTERVAL 500 /* ms */ +#define SAMPLE_DELAY 2000 /* ms */ +static int +test_for_100Mb(struct net_device *dev, int msec) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int gep = 0, ret = ((lp->chipset & ~0x00ff)==DC2114x? -1 :GEP_SLNK); + + if (lp->timeout < 0) { + if ((msec/SAMPLE_INTERVAL) <= 0) return 0; + if (msec > SAMPLE_DELAY) { + lp->timeout = (msec - SAMPLE_DELAY)/SAMPLE_INTERVAL; + gep = SAMPLE_DELAY | TIMER_CB; + return gep; + } else { + lp->timeout = msec/SAMPLE_INTERVAL; + } + } + + if (lp->phy[lp->active].id || lp->useSROM) { + gep = is_100_up(dev) | is_spd_100(dev); + } else { + gep = (~gep_rd(dev) & (GEP_SLNK | GEP_LNP)); + } + if (!(gep & ret) && --lp->timeout) { + gep = SAMPLE_INTERVAL | TIMER_CB; + } else { + lp->timeout = -1; + } + + return gep; +} + +static int +wait_for_link(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + if (lp->timeout < 0) { + lp->timeout = 1; + } + + if (lp->timeout--) { + return TIMER_CB; + } else { + lp->timeout = -1; + } + + return 0; +} + +/* +** +** +*/ +static int +test_mii_reg(struct net_device *dev, int reg, int mask, int pol, long msec) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int test; + u_long iobase = dev->base_addr; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + } + + if (pol) pol = ~0; + reg = mii_rd((u_char)reg, lp->phy[lp->active].addr, DE4X5_MII) & mask; + test = (reg ^ pol) & mask; + + if (test && --lp->timeout) { + reg = 100 | TIMER_CB; + } else { + lp->timeout = -1; + } + + return reg; +} + +static int +is_spd_100(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int spd; + + if (lp->useMII) { + spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII); + spd = ~(spd ^ lp->phy[lp->active].spd.value); + spd &= lp->phy[lp->active].spd.mask; + } else if (!lp->useSROM) { /* de500-xa */ + spd = ((~gep_rd(dev)) & GEP_SLNK); + } else { + if ((lp->ibn == 2) || !lp->asBitValid) + return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0); + + spd = (lp->asBitValid & (lp->asPolarity ^ (gep_rd(dev) & lp->asBit))) | + (lp->linkOK & ~lp->asBitValid); + } + + return spd; +} + +static int +is_100_up(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (lp->useMII) { + /* Double read for sticky bits & temporary drops */ + mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); + return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); + } else if (!lp->useSROM) { /* de500-xa */ + return ((~gep_rd(dev)) & GEP_SLNK); + } else { + if ((lp->ibn == 2) || !lp->asBitValid) + return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0); + + return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | + (lp->linkOK & ~lp->asBitValid)); + } +} + +static int +is_10_up(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (lp->useMII) { + /* Double read for sticky bits & temporary drops */ + mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); + return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); + } else if (!lp->useSROM) { /* de500-xa */ + return ((~gep_rd(dev)) & GEP_LNP); + } else { + if ((lp->ibn == 2) || !lp->asBitValid) + return (((lp->chipset & ~0x00ff) == DC2114x) ? + (~inl(DE4X5_SISR)&SISR_LS10): + 0); + + return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | + (lp->linkOK & ~lp->asBitValid)); + } +} + +static int +is_anc_capable(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { + return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII)); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + return (inl(DE4X5_SISR) & SISR_LPN) >> 12; + } else { + return 0; + } +} + +/* +** Send a packet onto the media and watch for send errors that indicate the +** media is bad or unconnected. +*/ +static int +ping_media(struct net_device *dev, int msec) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int sisr; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + + lp->tmp = lp->tx_new; /* Remember the ring position */ + load_packet(dev, lp->frame, TD_LS | TD_FS | sizeof(lp->frame), (struct sk_buff *)1); + lp->tx_new = (++lp->tx_new) % lp->txRingSize; + outl(POLL_DEMAND, DE4X5_TPD); + } + + sisr = inl(DE4X5_SISR); + + if ((!(sisr & SISR_NCR)) && + ((s32)le32_to_cpu(lp->tx_ring[lp->tmp].status) < 0) && + (--lp->timeout)) { + sisr = 100 | TIMER_CB; + } else { + if ((!(sisr & SISR_NCR)) && + !(le32_to_cpu(lp->tx_ring[lp->tmp].status) & (T_OWN | TD_ES)) && + lp->timeout) { + sisr = 0; + } else { + sisr = 1; + } + lp->timeout = -1; + } + + return sisr; +} + +/* +** This function does 2 things: on Intels it kmalloc's another buffer to +** replace the one about to be passed up. On Alpha's it kmallocs a buffer +** into which the packet is copied. +*/ +static struct sk_buff * +de4x5_alloc_rx_buff(struct net_device *dev, int index, int len) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + struct sk_buff *p; + +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) + struct sk_buff *ret; + u_long i=0, tmp; + + p = dev_alloc_skb(IEEE802_3_SZ + DE4X5_ALIGN + 2); + if (!p) return NULL; + + p->dev = dev; + tmp = virt_to_bus(p->data); + i = ((tmp + DE4X5_ALIGN) & ~DE4X5_ALIGN) - tmp; + skb_reserve(p, i); + lp->rx_ring[index].buf = cpu_to_le32(tmp + i); + + ret = lp->rx_skb[index]; + lp->rx_skb[index] = p; + + if ((u_long) ret > 1) { + skb_put(ret, len); + } + + return ret; + +#else + if (lp->state != OPEN) return (struct sk_buff *)1; /* Fake out the open */ + + p = dev_alloc_skb(len + 2); + if (!p) return NULL; + + p->dev = dev; + skb_reserve(p, 2); /* Align */ + if (index < lp->rx_old) { /* Wrapped buffer */ + short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ; + memcpy(skb_put(p,tlen),lp->rx_bufs + lp->rx_old * RX_BUFF_SZ,tlen); + memcpy(skb_put(p,len-tlen),lp->rx_bufs,len-tlen); + } else { /* Linear buffer */ + memcpy(skb_put(p,len),lp->rx_bufs + lp->rx_old * RX_BUFF_SZ,len); + } + + return p; +#endif +} + +static void +de4x5_free_rx_buffs(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int i; + + for (i=0; irxRingSize; i++) { + if ((u_long) lp->rx_skb[i] > 1) { + dev_kfree_skb(lp->rx_skb[i]); + } + lp->rx_ring[i].status = 0; + lp->rx_skb[i] = (struct sk_buff *)1; /* Dummy entry */ + } + + return; +} + +static void +de4x5_free_tx_buffs(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int i; + + for (i=0; itxRingSize; i++) { + if (lp->tx_skb[i]) + de4x5_free_tx_buff(lp, i); + lp->tx_ring[i].status = 0; + } + + /* Unload the locally queued packets */ + while (lp->cache.skb) { + dev_kfree_skb(de4x5_get_cache(dev)); + } + + return; +} + +/* +** When a user pulls a connection, the DECchip can end up in a +** 'running - waiting for end of transmission' state. This means that we +** have to perform a chip soft reset to ensure that we can synchronize +** the hardware and software and make any media probes using a loopback +** packet meaningful. +*/ +static void +de4x5_save_skbs(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 omr; + + if (!lp->cache.save_cnt) { + STOP_DE4X5; + de4x5_tx(dev); /* Flush any sent skb's */ + de4x5_free_tx_buffs(dev); + de4x5_cache_state(dev, DE4X5_SAVE_STATE); + de4x5_sw_reset(dev); + de4x5_cache_state(dev, DE4X5_RESTORE_STATE); + lp->cache.save_cnt++; + START_DE4X5; + } + + return; +} + +static void +de4x5_rst_desc_ring(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int i; + s32 omr; + + if (lp->cache.save_cnt) { + STOP_DE4X5; + outl(lp->dma_rings, DE4X5_RRBA); + outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), + DE4X5_TRBA); + + lp->rx_new = lp->rx_old = 0; + lp->tx_new = lp->tx_old = 0; + + for (i = 0; i < lp->rxRingSize; i++) { + lp->rx_ring[i].status = cpu_to_le32(R_OWN); + } + + for (i = 0; i < lp->txRingSize; i++) { + lp->tx_ring[i].status = cpu_to_le32(0); + } + + barrier(); + lp->cache.save_cnt--; + START_DE4X5; + } + + return; +} + +static void +de4x5_cache_state(struct net_device *dev, int flag) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + switch(flag) { + case DE4X5_SAVE_STATE: + lp->cache.csr0 = inl(DE4X5_BMR); + lp->cache.csr6 = (inl(DE4X5_OMR) & ~(OMR_ST | OMR_SR)); + lp->cache.csr7 = inl(DE4X5_IMR); + break; + + case DE4X5_RESTORE_STATE: + outl(lp->cache.csr0, DE4X5_BMR); + outl(lp->cache.csr6, DE4X5_OMR); + outl(lp->cache.csr7, DE4X5_IMR); + if (lp->chipset == DC21140) { + gep_wr(lp->cache.gepc, dev); + gep_wr(lp->cache.gep, dev); + } else { + reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, + lp->cache.csr15); + } + break; + } + + return; +} + +static void +de4x5_put_cache(struct net_device *dev, struct sk_buff *skb) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + struct sk_buff *p; + + if (lp->cache.skb) { + for (p=lp->cache.skb; p->next; p=p->next); + p->next = skb; + } else { + lp->cache.skb = skb; + } + skb->next = NULL; + + return; +} + +static void +de4x5_putb_cache(struct net_device *dev, struct sk_buff *skb) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + struct sk_buff *p = lp->cache.skb; + + lp->cache.skb = skb; + skb->next = p; + + return; +} + +static struct sk_buff * +de4x5_get_cache(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + struct sk_buff *p = lp->cache.skb; + + if (p) { + lp->cache.skb = p->next; + p->next = NULL; + } + + return p; +} + +/* +** Check the Auto Negotiation State. Return OK when a link pass interrupt +** is received and the auto-negotiation status is NWAY OK. +*/ +static int +test_ans(struct net_device *dev, s32 irqs, s32 irq_mask, s32 msec) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 sts, ans; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + outl(irq_mask, DE4X5_IMR); + + /* clear all pending interrupts */ + sts = inl(DE4X5_STS); + outl(sts, DE4X5_STS); + } + + ans = inl(DE4X5_SISR) & SISR_ANS; + sts = inl(DE4X5_STS) & ~TIMER_CB; + + if (!(sts & irqs) && (ans ^ ANS_NWOK) && --lp->timeout) { + sts = 100 | TIMER_CB; + } else { + lp->timeout = -1; + } + + return sts; +} + +static void +de4x5_setup_intr(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 imr, sts; + + if (inl(DE4X5_OMR) & OMR_SR) { /* Only unmask if TX/RX is enabled */ + imr = 0; + UNMASK_IRQs; + sts = inl(DE4X5_STS); /* Reset any pending (stale) interrupts */ + outl(sts, DE4X5_STS); + ENABLE_IRQs; + } + + return; +} + +/* +** +*/ +static void +reset_init_sia(struct net_device *dev, s32 csr13, s32 csr14, s32 csr15) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + RESET_SIA; + if (lp->useSROM) { + if (lp->ibn == 3) { + srom_exec(dev, lp->phy[lp->active].rst); + srom_exec(dev, lp->phy[lp->active].gep); + outl(1, DE4X5_SICR); + return; + } else { + csr15 = lp->cache.csr15; + csr14 = lp->cache.csr14; + csr13 = lp->cache.csr13; + outl(csr15 | lp->cache.gepc, DE4X5_SIGR); + outl(csr15 | lp->cache.gep, DE4X5_SIGR); + } + } else { + outl(csr15, DE4X5_SIGR); + } + outl(csr14, DE4X5_STRR); + outl(csr13, DE4X5_SICR); + + mdelay(10); + + return; +} + +/* +** Create a loopback ethernet packet +*/ +static void +create_packet(struct net_device *dev, char *frame, int len) +{ + int i; + char *buf = frame; + + for (i=0; idev_addr[i]; + } + for (i=0; idev_addr[i]; + } + + *buf++ = 0; /* Packet length (2 bytes) */ + *buf++ = 1; + + return; +} + +/* +** Look for a particular board name in the EISA configuration space +*/ +static int +EISA_signature(char *name, s32 eisa_id) +{ + static c_char *signatures[] = DE4X5_SIGNATURE; + char ManCode[DE4X5_STRLEN]; + union { + s32 ID; + char Id[4]; + } Eisa; + int i, status = 0, siglen = sizeof(signatures)/sizeof(c_char *); + + *name = '\0'; + Eisa.ID = inl(eisa_id); + + ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40); + ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40); + ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30); + ManCode[3]=((Eisa.Id[2]&0x0f)+0x30); + ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30); + ManCode[5]='\0'; + + for (i=0;ichipset == DC21040) { + strcpy(name, "DE434/5"); + return status; + } else { /* Search for a DEC name in the SROM */ + int i = *((char *)&lp->srom + 19) * 3; + strncpy(name, (char *)&lp->srom + 26 + i, 8); + } + name[8] = '\0'; + for (i=0; ichipset == DC21040) ? "DC21040" : + ((lp->chipset == DC21041) ? "DC21041" : + ((lp->chipset == DC21140) ? "DC21140" : + ((lp->chipset == DC21142) ? "DC21142" : + ((lp->chipset == DC21143) ? "DC21143" : "UNKNOWN" + ))))))); + } + if (lp->chipset != DC21041) { + useSROM = TRUE; /* card is not recognisably DEC */ + } + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + useSROM = TRUE; + } + + return status; +} + +/* +** Set up the Ethernet PROM counter to the start of the Ethernet address on +** the DC21040, else read the SROM for the other chips. +** The SROM may not be present in a multi-MAC card, so first read the +** MAC address and check for a bad address. If there is a bad one then exit +** immediately with the prior srom contents intact (the h/w address will +** be fixed up later). +*/ +static void +DevicePresent(u_long aprom_addr) +{ + int i, j=0; + struct bus_type *lp = &bus; + + if (lp->chipset == DC21040) { + if (lp->bus == EISA) { + enet_addr_rst(aprom_addr); /* Reset Ethernet Address ROM Pointer */ + } else { + outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */ + } + } else { /* Read new srom */ + u_short tmp, *p = (short *)((char *)&lp->srom + SROM_HWADD); + for (i=0; i<(ETH_ALEN>>1); i++) { + tmp = srom_rd(aprom_addr, (SROM_HWADD>>1) + i); + *p = le16_to_cpu(tmp); + j += *p++; + } + if ((j == 0) || (j == 0x2fffd)) { + return; + } + + p=(short *)&lp->srom; + for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) { + tmp = srom_rd(aprom_addr, i); + *p++ = le16_to_cpu(tmp); + } + de4x5_dbg_srom((struct de4x5_srom *)&lp->srom); + } + + return; +} + +/* +** Since the write on the Enet PROM register doesn't seem to reset the PROM +** pointer correctly (at least on my DE425 EISA card), this routine should do +** it...from depca.c. +*/ +static void +enet_addr_rst(u_long aprom_addr) +{ + union { + struct { + u32 a; + u32 b; + } llsig; + char Sig[sizeof(u32) << 1]; + } dev; + short sigLength=0; + s8 data; + int i, j; + + dev.llsig.a = ETH_PROM_SIG; + dev.llsig.b = ETH_PROM_SIG; + sigLength = sizeof(u32) << 1; + + for (i=0,j=0;jbase_addr; + int broken, i, k, tmp, status = 0; + u_short j,chksum; + struct bus_type *lp = &bus; + + broken = de4x5_bad_srom(lp); + + for (i=0,k=0,j=0;j<3;j++) { + k <<= 1; + if (k > 0xffff) k-=0xffff; + + if (lp->bus == PCI) { + if (lp->chipset == DC21040) { + while ((tmp = inl(DE4X5_APROM)) < 0); + k += (u_char) tmp; + dev->dev_addr[i++] = (u_char) tmp; + while ((tmp = inl(DE4X5_APROM)) < 0); + k += (u_short) (tmp << 8); + dev->dev_addr[i++] = (u_char) tmp; + } else if (!broken) { + dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++; + dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++; + } else if ((broken == SMC) || (broken == ACCTON)) { + dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++; + dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++; + } + } else { + k += (u_char) (tmp = inb(EISA_APROM)); + dev->dev_addr[i++] = (u_char) tmp; + k += (u_short) ((tmp = inb(EISA_APROM)) << 8); + dev->dev_addr[i++] = (u_char) tmp; + } + + if (k > 0xffff) k-=0xffff; + } + if (k == 0xffff) k=0; + + if (lp->bus == PCI) { + if (lp->chipset == DC21040) { + while ((tmp = inl(DE4X5_APROM)) < 0); + chksum = (u_char) tmp; + while ((tmp = inl(DE4X5_APROM)) < 0); + chksum |= (u_short) (tmp << 8); + if ((k != chksum) && (dec_only)) status = -1; + } + } else { + chksum = (u_char) inb(EISA_APROM); + chksum |= (u_short) (inb(EISA_APROM) << 8); + if ((k != chksum) && (dec_only)) status = -1; + } + + /* If possible, try to fix a broken card - SMC only so far */ + srom_repair(dev, broken); + +#ifdef CONFIG_PPC + /* + ** If the address starts with 00 a0, we have to bit-reverse + ** each byte of the address. + */ + if ( (ppc_md.ppc_machine & _MACH_Pmac) && + (dev->dev_addr[0] == 0) && + (dev->dev_addr[1] == 0xa0) ) + { + for (i = 0; i < ETH_ALEN; ++i) + { + int x = dev->dev_addr[i]; + x = ((x & 0xf) << 4) + ((x & 0xf0) >> 4); + x = ((x & 0x33) << 2) + ((x & 0xcc) >> 2); + dev->dev_addr[i] = ((x & 0x55) << 1) + ((x & 0xaa) >> 1); + } + } +#endif /* CONFIG_PPC */ + + /* Test for a bad enet address */ + status = test_bad_enet(dev, status); + + return status; +} + +/* +** Test for enet addresses in the first 32 bytes. The built-in strncmp +** didn't seem to work here...? +*/ +static int +de4x5_bad_srom(struct bus_type *lp) +{ + int i, status = 0; + + for (i=0; isrom, (char *)&enet_det[i], 3) && + !de4x5_strncmp((char *)&lp->srom+0x10, (char *)&enet_det[i], 3)) { + if (i == 0) { + status = SMC; + } else if (i == 1) { + status = ACCTON; + } + break; + } + } + + return status; +} + +static int +de4x5_strncmp(char *a, char *b, int n) +{ + int ret=0; + + for (;n && !ret;n--) { + ret = *a++ - *b++; + } + + return ret; +} + +static void +srom_repair(struct net_device *dev, int card) +{ + struct bus_type *lp = &bus; + + switch(card) { + case SMC: + memset((char *)&bus.srom, 0, sizeof(struct de4x5_srom)); + memcpy(lp->srom.ieee_addr, (char *)dev->dev_addr, ETH_ALEN); + memcpy(lp->srom.info, (char *)&srom_repair_info[SMC-1], 100); + useSROM = TRUE; + break; + } + + return; +} + +/* +** Assume that the irq's do not follow the PCI spec - this is seems +** to be true so far (2 for 2). +*/ +static int +test_bad_enet(struct net_device *dev, int status) +{ + struct bus_type *lp = &bus; + int i, tmp; + + for (tmp=0,i=0; idev_addr[i]; + if ((tmp == 0) || (tmp == 0x5fa)) { + if ((lp->chipset == last.chipset) && + (lp->bus_num == last.bus) && (lp->bus_num > 0)) { + for (i=0; idev_addr[i] = last.addr[i]; + for (i=ETH_ALEN-1; i>2; --i) { + dev->dev_addr[i] += 1; + if (dev->dev_addr[i] != 0) break; + } + for (i=0; idev_addr[i]; + if (!an_exception(lp)) { + dev->irq = last.irq; + } + + status = 0; + } + } else if (!status) { + last.chipset = lp->chipset; + last.bus = lp->bus_num; + last.irq = dev->irq; + for (i=0; idev_addr[i]; + } + + return status; +} + +/* +** List of board exceptions with correctly wired IRQs +*/ +static int +an_exception(struct bus_type *lp) +{ + if ((*(u_short *)lp->srom.sub_vendor_id == 0x00c0) && + (*(u_short *)lp->srom.sub_system_id == 0x95e0)) { + return -1; + } + + return 0; +} + +/* +** SROM Read +*/ +static short +srom_rd(u_long addr, u_char offset) +{ + sendto_srom(SROM_RD | SROM_SR, addr); + + srom_latch(SROM_RD | SROM_SR | DT_CS, addr); + srom_command(SROM_RD | SROM_SR | DT_IN | DT_CS, addr); + srom_address(SROM_RD | SROM_SR | DT_CS, addr, offset); + + return srom_data(SROM_RD | SROM_SR | DT_CS, addr); +} + +static void +srom_latch(u_int command, u_long addr) +{ + sendto_srom(command, addr); + sendto_srom(command | DT_CLK, addr); + sendto_srom(command, addr); + + return; +} + +static void +srom_command(u_int command, u_long addr) +{ + srom_latch(command, addr); + srom_latch(command, addr); + srom_latch((command & 0x0000ff00) | DT_CS, addr); + + return; +} + +static void +srom_address(u_int command, u_long addr, u_char offset) +{ + int i, a; + + a = offset << 2; + for (i=0; i<6; i++, a <<= 1) { + srom_latch(command | ((a & 0x80) ? DT_IN : 0), addr); + } + udelay(1); + + i = (getfrom_srom(addr) >> 3) & 0x01; + + return; +} + +static short +srom_data(u_int command, u_long addr) +{ + int i; + short word = 0; + s32 tmp; + + for (i=0; i<16; i++) { + sendto_srom(command | DT_CLK, addr); + tmp = getfrom_srom(addr); + sendto_srom(command, addr); + + word = (word << 1) | ((tmp >> 3) & 0x01); + } + + sendto_srom(command & 0x0000ff00, addr); + + return word; +} + +/* +static void +srom_busy(u_int command, u_long addr) +{ + sendto_srom((command & 0x0000ff00) | DT_CS, addr); + + while (!((getfrom_srom(addr) >> 3) & 0x01)) { + mdelay(1); + } + + sendto_srom(command & 0x0000ff00, addr); + + return; +} +*/ + +static void +sendto_srom(u_int command, u_long addr) +{ + outl(command, addr); + udelay(1); + + return; +} + +static int +getfrom_srom(u_long addr) +{ + s32 tmp; + + tmp = inl(addr); + udelay(1); + + return tmp; +} + +static int +srom_infoleaf_info(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int i, count; + u_char *p; + + /* Find the infoleaf decoder function that matches this chipset */ + for (i=0; ichipset == infoleaf_array[i].chipset) break; + } + if (i == INFOLEAF_SIZE) { + lp->useSROM = FALSE; + printk("%s: Cannot find correct chipset for SROM decoding!\n", + dev->name); + return -ENXIO; + } + + lp->infoleaf_fn = infoleaf_array[i].fn; + + /* Find the information offset that this function should use */ + count = *((u_char *)&lp->srom + 19); + p = (u_char *)&lp->srom + 26; + + if (count > 1) { + for (i=count; i; --i, p+=3) { + if (lp->device == *p) break; + } + if (i == 0) { + lp->useSROM = FALSE; + printk("%s: Cannot find correct PCI device [%d] for SROM decoding!\n", + dev->name, lp->device); + return -ENXIO; + } + } + + lp->infoleaf_offset = TWIDDLE(p+1); + + return 0; +} + +/* +** This routine loads any type 1 or 3 MII info into the mii device +** struct and executes any type 5 code to reset PHY devices for this +** controller. +** The info for the MII devices will be valid since the index used +** will follow the discovery process from MII address 1-31 then 0. +*/ +static void +srom_init(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + u_char count; + + p+=2; + if (lp->chipset == DC21140) { + lp->cache.gepc = (*p++ | GEP_CTRL); + gep_wr(lp->cache.gepc, dev); + } + + /* Block count */ + count = *p++; + + /* Jump the infoblocks to find types */ + for (;count; --count) { + if (*p < 128) { + p += COMPACT_LEN; + } else if (*(p+1) == 5) { + type5_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 4) { + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 3) { + type3_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 2) { + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 1) { + type1_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else { + p += ((*p & BLOCK_LEN) + 1); + } + } + + return; +} + +/* +** A generic routine that writes GEP control, data and reset information +** to the GEP register (21140) or csr15 GEP portion (2114[23]). +*/ +static void +srom_exec(struct net_device *dev, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char count = (p ? *p++ : 0); + u_short *w = (u_short *)p; + + if (((lp->ibn != 1) && (lp->ibn != 3) && (lp->ibn != 5)) || !count) return; + + if (lp->chipset != DC21140) RESET_SIA; + + while (count--) { + gep_wr(((lp->chipset==DC21140) && (lp->ibn!=5) ? + *p++ : TWIDDLE(w++)), dev); + mdelay(2); /* 2ms per action */ + } + + if (lp->chipset != DC21140) { + outl(lp->cache.csr14, DE4X5_STRR); + outl(lp->cache.csr13, DE4X5_SICR); + } + + return; +} + +/* +** Basically this function is a NOP since it will never be called, +** unless I implement the DC21041 SROM functions. There's no need +** since the existing code will be satisfactory for all boards. +*/ +static int +dc21041_infoleaf(struct net_device *dev) +{ + return DE4X5_AUTOSENSE_MS; +} + +static int +dc21140_infoleaf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* GEP control */ + lp->cache.gepc = (*p++ | GEP_CTRL); + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = FALSE; + } + + return next_tick & ~TIMER_CB; +} + +static int +dc21142_infoleaf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = FALSE; + } + + return next_tick & ~TIMER_CB; +} + +static int +dc21143_infoleaf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = FALSE; + } + + return next_tick & ~TIMER_CB; +} + +/* +** The compact infoblock is only designed for DC21140[A] chips, so +** we'll reuse the dc21140m_autoconf function. Non MII media only. +*/ +static int +compact_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char flags, csr6; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+COMPACT_LEN) < 128) { + return dc_infoblock[COMPACT](dev, count, p+COMPACT_LEN); + } else { + return dc_infoblock[*(p+COMPACT_LEN+1)](dev, count, p+COMPACT_LEN); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = COMPACT; + lp->active = 0; + gep_wr(lp->cache.gepc, dev); + lp->infoblock_media = (*p++) & COMPACT_MC; + lp->cache.gep = *p++; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); + lp->useMII = FALSE; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +/* +** This block describes non MII media for the DC21140[A] only. +*/ +static int +type0_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 0; + lp->active = 0; + gep_wr(lp->cache.gepc, dev); + p+=2; + lp->infoblock_media = (*p++) & BLOCK0_MC; + lp->cache.gep = *p++; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); + lp->useMII = FALSE; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +/* These functions are under construction! */ + +static int +type1_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + p += 2; + if (lp->state == INITIALISED) { + lp->ibn = 1; + lp->active = *p++; + lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1); + lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1); + lp->phy[lp->active].mc = TWIDDLE(p); p += 2; + lp->phy[lp->active].ana = TWIDDLE(p); p += 2; + lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; + lp->phy[lp->active].ttm = TWIDDLE(p); + return 0; + } else if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 1; + lp->active = *p; + lp->infoblock_csr6 = OMR_MII_100; + lp->useMII = TRUE; + lp->infoblock_media = ANS; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +static int +type2_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 2; + lp->active = 0; + p += 2; + lp->infoblock_media = (*p) & MEDIA_CODE; + + if ((*p++) & EXT_FIELD) { + lp->cache.csr13 = TWIDDLE(p); p += 2; + lp->cache.csr14 = TWIDDLE(p); p += 2; + lp->cache.csr15 = TWIDDLE(p); p += 2; + } else { + lp->cache.csr13 = CSR13; + lp->cache.csr14 = CSR14; + lp->cache.csr15 = CSR15; + } + lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2; + lp->cache.gep = ((s32)(TWIDDLE(p)) << 16); + lp->infoblock_csr6 = OMR_SIA; + lp->useMII = FALSE; + + de4x5_switch_mac_port(dev); + } + + return dc2114x_autoconf(dev); +} + +static int +type3_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + p += 2; + if (lp->state == INITIALISED) { + lp->ibn = 3; + lp->active = *p++; + if (MOTO_SROM_BUG) lp->active = 0; + lp->phy[lp->active].gep = (*p ? p : 0); p += (2 * (*p) + 1); + lp->phy[lp->active].rst = (*p ? p : 0); p += (2 * (*p) + 1); + lp->phy[lp->active].mc = TWIDDLE(p); p += 2; + lp->phy[lp->active].ana = TWIDDLE(p); p += 2; + lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; + lp->phy[lp->active].ttm = TWIDDLE(p); p += 2; + lp->phy[lp->active].mci = *p; + return 0; + } else if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 3; + lp->active = *p; + if (MOTO_SROM_BUG) lp->active = 0; + lp->infoblock_csr6 = OMR_MII_100; + lp->useMII = TRUE; + lp->infoblock_media = ANS; + + de4x5_switch_mac_port(dev); + } + + return dc2114x_autoconf(dev); +} + +static int +type4_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 4; + lp->active = 0; + p+=2; + lp->infoblock_media = (*p++) & MEDIA_CODE; + lp->cache.csr13 = CSR13; /* Hard coded defaults */ + lp->cache.csr14 = CSR14; + lp->cache.csr15 = CSR15; + lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2; + lp->cache.gep = ((s32)(TWIDDLE(p)) << 16); p += 2; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); + lp->useMII = FALSE; + + de4x5_switch_mac_port(dev); + } + + return dc2114x_autoconf(dev); +} + +/* +** This block type provides information for resetting external devices +** (chips) through the General Purpose Register. +*/ +static int +type5_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + /* Must be initializing to run this code */ + if ((lp->state == INITIALISED) || (lp->media == INIT)) { + p+=2; + lp->rst = p; + srom_exec(dev, lp->rst); + } + + return DE4X5_AUTOSENSE_MS; +} + +/* +** MII Read/Write +*/ + +static int +mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr) +{ + mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */ + mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */ + mii_wdata(MII_STRD, 4, ioaddr); /* SFD and Read operation */ + mii_address(phyaddr, ioaddr); /* PHY address to be accessed */ + mii_address(phyreg, ioaddr); /* PHY Register to read */ + mii_ta(MII_STRD, ioaddr); /* Turn around time - 2 MDC */ + + return mii_rdata(ioaddr); /* Read data */ +} + +static void +mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr) +{ + mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */ + mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */ + mii_wdata(MII_STWR, 4, ioaddr); /* SFD and Write operation */ + mii_address(phyaddr, ioaddr); /* PHY address to be accessed */ + mii_address(phyreg, ioaddr); /* PHY Register to write */ + mii_ta(MII_STWR, ioaddr); /* Turn around time - 2 MDC */ + data = mii_swap(data, 16); /* Swap data bit ordering */ + mii_wdata(data, 16, ioaddr); /* Write data */ + + return; +} + +static int +mii_rdata(u_long ioaddr) +{ + int i; + s32 tmp = 0; + + for (i=0; i<16; i++) { + tmp <<= 1; + tmp |= getfrom_mii(MII_MRD | MII_RD, ioaddr); + } + + return tmp; +} + +static void +mii_wdata(int data, int len, u_long ioaddr) +{ + int i; + + for (i=0; i>= 1; + } + + return; +} + +static void +mii_address(u_char addr, u_long ioaddr) +{ + int i; + + addr = mii_swap(addr, 5); + for (i=0; i<5; i++) { + sendto_mii(MII_MWR | MII_WR, addr, ioaddr); + addr >>= 1; + } + + return; +} + +static void +mii_ta(u_long rw, u_long ioaddr) +{ + if (rw == MII_STWR) { + sendto_mii(MII_MWR | MII_WR, 1, ioaddr); + sendto_mii(MII_MWR | MII_WR, 0, ioaddr); + } else { + getfrom_mii(MII_MRD | MII_RD, ioaddr); /* Tri-state MDIO */ + } + + return; +} + +static int +mii_swap(int data, int len) +{ + int i, tmp = 0; + + for (i=0; i>= 1; + } + + return tmp; +} + +static void +sendto_mii(u32 command, int data, u_long ioaddr) +{ + u32 j; + + j = (data & 1) << 17; + outl(command | j, ioaddr); + udelay(1); + outl(command | MII_MDC | j, ioaddr); + udelay(1); + + return; +} + +static int +getfrom_mii(u32 command, u_long ioaddr) +{ + outl(command, ioaddr); + udelay(1); + outl(command | MII_MDC, ioaddr); + udelay(1); + + return ((inl(ioaddr) >> 19) & 1); +} + +/* +** Here's 3 ways to calculate the OUI from the ID registers. +*/ +static int +mii_get_oui(u_char phyaddr, u_long ioaddr) +{ +/* + union { + u_short reg; + u_char breg[2]; + } a; + int i, r2, r3, ret=0;*/ + int r2, r3; + + /* Read r2 and r3 */ + r2 = mii_rd(MII_ID0, phyaddr, ioaddr); + r3 = mii_rd(MII_ID1, phyaddr, ioaddr); + /* SEEQ and Cypress way * / + / * Shuffle r2 and r3 * / + a.reg=0; + r3 = ((r3>>10)|(r2<<6))&0x0ff; + r2 = ((r2>>2)&0x3fff); + + / * Bit reverse r3 * / + for (i=0;i<8;i++) { + ret<<=1; + ret |= (r3&1); + r3>>=1; + } + + / * Bit reverse r2 * / + for (i=0;i<16;i++) { + a.reg<<=1; + a.reg |= (r2&1); + r2>>=1; + } + + / * Swap r2 bytes * / + i=a.breg[0]; + a.breg[0]=a.breg[1]; + a.breg[1]=i; + + return ((a.reg<<8)|ret); */ /* SEEQ and Cypress way */ +/* return ((r2<<6)|(u_int)(r3>>10)); */ /* NATIONAL and BROADCOM way */ + return r2; /* (I did it) My way */ +} + +/* +** The SROM spec forces us to search addresses [1-31 0]. Bummer. +*/ +static int +mii_get_phy(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table); + int id; + + lp->active = 0; + lp->useMII = TRUE; + + /* Search the MII address space for possible PHY devices */ + for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(++i)%DE4X5_MAX_MII) { + lp->phy[lp->active].addr = i; + if (i==0) n++; /* Count cycles */ + while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */ + id = mii_get_oui(i, DE4X5_MII); + if ((id == 0) || (id == 65535)) continue; /* Valid ID? */ + for (j=0; jphy[k].id && (k < DE4X5_MAX_PHY); k++); + if (k < DE4X5_MAX_PHY) { + memcpy((char *)&lp->phy[k], + (char *)&phy_info[j], sizeof(struct phy_table)); + lp->phy[k].addr = i; + lp->mii_cnt++; + lp->active++; + } else { + goto purgatory; /* Stop the search */ + } + break; + } + if ((j == limit) && (i < DE4X5_MAX_MII)) { + for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++); + lp->phy[k].addr = i; + lp->phy[k].id = id; + lp->phy[k].spd.reg = GENERIC_REG; /* ANLPA register */ + lp->phy[k].spd.mask = GENERIC_MASK; /* 100Mb/s technologies */ + lp->phy[k].spd.value = GENERIC_VALUE; /* TX & T4, H/F Duplex */ + lp->mii_cnt++; + lp->active++; + printk("%s: Using generic MII device control. If the board doesn't operate, \nplease mail the following dump to the author:\n", dev->name); + j = de4x5_debug; + de4x5_debug |= DEBUG_MII; + de4x5_dbg_mii(dev, k); + de4x5_debug = j; + printk("\n"); + } + } + purgatory: + lp->active = 0; + if (lp->phy[0].id) { /* Reset the PHY devices */ + for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/ + mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII); + while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST); + + de4x5_dbg_mii(dev, k); + } + } + if (!lp->mii_cnt) lp->useMII = FALSE; + + return lp->mii_cnt; +} + +static char * +build_setup_frame(struct net_device *dev, int mode) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int i; + char *pa = lp->setup_frame; + + /* Initialise the setup frame */ + if (mode == ALL) { + memset(lp->setup_frame, 0, SETUP_FRAME_LEN); + } + + if (lp->setup_f == HASH_PERF) { + for (pa=lp->setup_frame+IMPERF_PA_OFFSET, i=0; idev_addr[i]; /* Host address */ + if (i & 0x01) pa += 2; + } + *(lp->setup_frame + (HASH_TABLE_LEN >> 3) - 3) = 0x80; + } else { + for (i=0; idev_addr[i]; + if (i & 0x01) pa += 4; + } + for (i=0; ipriv; + + del_timer(&lp->timer); + + return; +} + +static long +de4x5_switch_mac_port(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 omr; + + STOP_DE4X5; + + /* Assert the OMR_PS bit in CSR6 */ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | + OMR_FDX)); + omr |= lp->infoblock_csr6; + if (omr & OMR_PS) omr |= OMR_HBD; + outl(omr, DE4X5_OMR); + + /* Soft Reset */ + RESET_DE4X5; + + /* Restore the GEP - especially for COMPACT and Type 0 Infoblocks */ + if (lp->chipset == DC21140) { + gep_wr(lp->cache.gepc, dev); + gep_wr(lp->cache.gep, dev); + } else if ((lp->chipset & ~0x0ff) == DC2114x) { + reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15); + } + + /* Restore CSR6 */ + outl(omr, DE4X5_OMR); + + /* Reset CSR8 */ + inl(DE4X5_MFC); + + return omr; +} + +static void +gep_wr(s32 data, struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (lp->chipset == DC21140) { + outl(data, DE4X5_GEP); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + outl((data<<16) | lp->cache.csr15, DE4X5_SIGR); + } + + return; +} + +static int +gep_rd(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (lp->chipset == DC21140) { + return inl(DE4X5_GEP); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + return (inl(DE4X5_SIGR) & 0x000fffff); + } + + return 0; +} + +static void +timeout(struct net_device *dev, void (*fn)(u_long data), u_long data, u_long msec) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int dt; + + /* First, cancel any pending timer events */ + del_timer(&lp->timer); + + /* Convert msec to ticks */ + dt = (msec * HZ) / 1000; + if (dt==0) dt=1; + + /* Set up timer */ + lp->timer.expires = jiffies + dt; + lp->timer.function = fn; + lp->timer.data = data; + add_timer(&lp->timer); + + return; +} + +static void +yawn(struct net_device *dev, int state) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return; + + if(lp->bus == EISA) { + switch(state) { + case WAKEUP: + outb(WAKEUP, PCI_CFPM); + mdelay(10); + break; + + case SNOOZE: + outb(SNOOZE, PCI_CFPM); + break; + + case SLEEP: + outl(0, DE4X5_SICR); + outb(SLEEP, PCI_CFPM); + break; + } + } else { + switch(state) { + case WAKEUP: + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, WAKEUP); + mdelay(10); + break; + + case SNOOZE: + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, SNOOZE); + break; + + case SLEEP: + outl(0, DE4X5_SICR); + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, SLEEP); + break; + } + } + + return; +} + +static void +de4x5_parse_params(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + char *p, *q, t; + + lp->params.fdx = 0; + lp->params.autosense = AUTO; + + if (args == NULL) return; + + if ((p = strstr(args, dev->name))) { + if (!(q = strstr(p+strlen(dev->name), "eth"))) q = p + strlen(p); + t = *q; + *q = '\0'; + +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) + if (strstr(p, "force_eisa") || strstr(p, "FORCE_EISA")) forceEISA = 1; +#endif + if (strstr(p, "fdx") || strstr(p, "FDX")) lp->params.fdx = 1; + + if (strstr(p, "autosense") || strstr(p, "AUTOSENSE")) { + if (strstr(p, "TP")) { + lp->params.autosense = TP; + } else if (strstr(p, "TP_NW")) { + lp->params.autosense = TP_NW; + } else if (strstr(p, "BNC")) { + lp->params.autosense = BNC; + } else if (strstr(p, "AUI")) { + lp->params.autosense = AUI; + } else if (strstr(p, "BNC_AUI")) { + lp->params.autosense = BNC; + } else if (strstr(p, "10Mb")) { + lp->params.autosense = _10Mb; + } else if (strstr(p, "100Mb")) { + lp->params.autosense = _100Mb; + } else if (strstr(p, "AUTO")) { + lp->params.autosense = AUTO; + } + } + *q = t; + } + + return; +} + +static void +de4x5_dbg_open(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int i; + + if (de4x5_debug & DEBUG_OPEN) { + printk("%s: de4x5 opening with irq %d\n",dev->name,dev->irq); + printk("\tphysical address: "); + for (i=0;i<6;i++) { + printk("%2.2x:",(short)dev->dev_addr[i]); + } + printk("\n"); + printk("Descriptor head addresses:\n"); + printk("\t0x%8.8lx 0x%8.8lx\n",(u_long)lp->rx_ring,(u_long)lp->tx_ring); + printk("Descriptor addresses:\nRX: "); + for (i=0;irxRingSize-1;i++){ + if (i < 3) { + printk("0x%8.8lx ",(u_long)&lp->rx_ring[i].status); + } + } + printk("...0x%8.8lx\n",(u_long)&lp->rx_ring[i].status); + printk("TX: "); + for (i=0;itxRingSize-1;i++){ + if (i < 3) { + printk("0x%8.8lx ", (u_long)&lp->tx_ring[i].status); + } + } + printk("...0x%8.8lx\n", (u_long)&lp->tx_ring[i].status); + printk("Descriptor buffers:\nRX: "); + for (i=0;irxRingSize-1;i++){ + if (i < 3) { + printk("0x%8.8x ",le32_to_cpu(lp->rx_ring[i].buf)); + } + } + printk("...0x%8.8x\n",le32_to_cpu(lp->rx_ring[i].buf)); + printk("TX: "); + for (i=0;itxRingSize-1;i++){ + if (i < 3) { + printk("0x%8.8x ", le32_to_cpu(lp->tx_ring[i].buf)); + } + } + printk("...0x%8.8x\n", le32_to_cpu(lp->tx_ring[i].buf)); + printk("Ring size: \nRX: %d\nTX: %d\n", + (short)lp->rxRingSize, + (short)lp->txRingSize); + } + + return; +} + +static void +de4x5_dbg_mii(struct net_device *dev, int k) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (de4x5_debug & DEBUG_MII) { + printk("\nMII device address: %d\n", lp->phy[k].addr); + printk("MII CR: %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII)); + printk("MII SR: %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII)); + printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII)); + printk("MII ID1: %x\n",mii_rd(MII_ID1,lp->phy[k].addr,DE4X5_MII)); + if (lp->phy[k].id != BROADCOM_T4) { + printk("MII ANA: %x\n",mii_rd(0x04,lp->phy[k].addr,DE4X5_MII)); + printk("MII ANC: %x\n",mii_rd(0x05,lp->phy[k].addr,DE4X5_MII)); + } + printk("MII 16: %x\n",mii_rd(0x10,lp->phy[k].addr,DE4X5_MII)); + if (lp->phy[k].id != BROADCOM_T4) { + printk("MII 17: %x\n",mii_rd(0x11,lp->phy[k].addr,DE4X5_MII)); + printk("MII 18: %x\n",mii_rd(0x12,lp->phy[k].addr,DE4X5_MII)); + } else { + printk("MII 20: %x\n",mii_rd(0x14,lp->phy[k].addr,DE4X5_MII)); + } + } + + return; +} + +static void +de4x5_dbg_media(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + if (lp->media != lp->c_media) { + if (de4x5_debug & DEBUG_MEDIA) { + printk("%s: media is %s%s\n", dev->name, + (lp->media == NC ? "unconnected, link down or incompatible connection" : + (lp->media == TP ? "TP" : + (lp->media == ANS ? "TP/Nway" : + (lp->media == BNC ? "BNC" : + (lp->media == AUI ? "AUI" : + (lp->media == BNC_AUI ? "BNC/AUI" : + (lp->media == EXT_SIA ? "EXT SIA" : + (lp->media == _100Mb ? "100Mb/s" : + (lp->media == _10Mb ? "10Mb/s" : + "???" + ))))))))), (lp->fdx?" full duplex.":".")); + } + lp->c_media = lp->media; + } + + return; +} + +static void +de4x5_dbg_srom(struct de4x5_srom *p) +{ + int i; + + if (de4x5_debug & DEBUG_SROM) { + printk("Sub-system Vendor ID: %04x\n", *((u_short *)p->sub_vendor_id)); + printk("Sub-system ID: %04x\n", *((u_short *)p->sub_system_id)); + printk("ID Block CRC: %02x\n", (u_char)(p->id_block_crc)); + printk("SROM version: %02x\n", (u_char)(p->version)); + printk("# controllers: %02x\n", (u_char)(p->num_controllers)); + + printk("Hardware Address: "); + for (i=0;iieee_addr+i)); + } + printk("%02x\n", (u_char)*(p->ieee_addr+i)); + printk("CRC checksum: %04x\n", (u_short)(p->chksum)); + for (i=0; i<64; i++) { + printk("%3d %04x\n", i<<1, (u_short)*((u_short *)p+i)); + } + } + + return; +} + +static void +de4x5_dbg_rx(struct sk_buff *skb, int len) +{ + int i, j; + + if (de4x5_debug & DEBUG_RX) { + printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n", + (u_char)skb->data[0], + (u_char)skb->data[1], + (u_char)skb->data[2], + (u_char)skb->data[3], + (u_char)skb->data[4], + (u_char)skb->data[5], + (u_char)skb->data[6], + (u_char)skb->data[7], + (u_char)skb->data[8], + (u_char)skb->data[9], + (u_char)skb->data[10], + (u_char)skb->data[11], + (u_char)skb->data[12], + (u_char)skb->data[13], + len); + for (j=0; len>0;j+=16, len-=16) { + printk(" %03x: ",j); + for (i=0; i<16 && idata[i+j]); + } + printk("\n"); + } + } + + return; +} + +/* +** Perform IOCTL call functions here. Some are privileged operations and the +** effective uid is checked in those cases. In the normal course of events +** this function is only used for my testing. +*/ +static int +de4x5_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + struct de4x5_ioctl *ioc = (struct de4x5_ioctl *) &rq->ifr_data; + u_long iobase = dev->base_addr; + int i, j, status = 0; + s32 omr; + union { + u8 addr[144]; + u16 sval[72]; + u32 lval[36]; + } tmp; + u_long flags = 0; + + switch(ioc->cmd) { + case DE4X5_GET_HWADDR: /* Get the hardware address */ + ioc->len = ETH_ALEN; + for (i=0; idev_addr[i]; + } + if (copy_to_user(ioc->data, tmp.addr, ioc->len)) return -EFAULT; + break; + + case DE4X5_SET_HWADDR: /* Set the hardware address */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (copy_from_user(tmp.addr, ioc->data, ETH_ALEN)) return -EFAULT; + if (netif_queue_stopped(dev)) + return -EBUSY; + netif_stop_queue(dev); + for (i=0; idev_addr[i] = tmp.addr[i]; + } + build_setup_frame(dev, PHYS_ADDR_ONLY); + /* Set up the descriptor and give ownership to the card */ + load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | + SETUP_FRAME_LEN, (struct sk_buff *)1); + lp->tx_new = (++lp->tx_new) % lp->txRingSize; + outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */ + netif_wake_queue(dev); /* Unlock the TX ring */ + break; + + case DE4X5_SET_PROM: /* Set Promiscuous Mode */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + omr = inl(DE4X5_OMR); + omr |= OMR_PR; + outl(omr, DE4X5_OMR); + dev->flags |= IFF_PROMISC; + break; + + case DE4X5_CLR_PROM: /* Clear Promiscuous Mode */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + omr = inl(DE4X5_OMR); + omr &= ~OMR_PR; + outl(omr, DE4X5_OMR); + dev->flags &= ~IFF_PROMISC; + break; + + case DE4X5_SAY_BOO: /* Say "Boo!" to the kernel log file */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + printk("%s: Boo!\n", dev->name); + break; + + case DE4X5_MCA_EN: /* Enable pass all multicast addressing */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + omr = inl(DE4X5_OMR); + omr |= OMR_PM; + outl(omr, DE4X5_OMR); + break; + + case DE4X5_GET_STATS: /* Get the driver statistics */ + { + struct pkt_stats statbuf; + ioc->len = sizeof(statbuf); + spin_lock_irqsave(&lp->lock, flags); + memcpy(&statbuf, &lp->pktStats, ioc->len); + spin_unlock_irqrestore(&lp->lock, flags); + if (copy_to_user(ioc->data, &statbuf, ioc->len)) + return -EFAULT; + break; + } + case DE4X5_CLR_STATS: /* Zero out the driver statistics */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + spin_lock_irqsave(&lp->lock, flags); + memset(&lp->pktStats, 0, sizeof(lp->pktStats)); + spin_unlock_irqrestore(&lp->lock, flags); + break; + + case DE4X5_GET_OMR: /* Get the OMR Register contents */ + tmp.addr[0] = inl(DE4X5_OMR); + if (copy_to_user(ioc->data, tmp.addr, 1)) return -EFAULT; + break; + + case DE4X5_SET_OMR: /* Set the OMR Register contents */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (copy_from_user(tmp.addr, ioc->data, 1)) return -EFAULT; + outl(tmp.addr[0], DE4X5_OMR); + break; + + case DE4X5_GET_REG: /* Get the DE4X5 Registers */ + j = 0; + tmp.lval[0] = inl(DE4X5_STS); j+=4; + tmp.lval[1] = inl(DE4X5_BMR); j+=4; + tmp.lval[2] = inl(DE4X5_IMR); j+=4; + tmp.lval[3] = inl(DE4X5_OMR); j+=4; + tmp.lval[4] = inl(DE4X5_SISR); j+=4; + tmp.lval[5] = inl(DE4X5_SICR); j+=4; + tmp.lval[6] = inl(DE4X5_STRR); j+=4; + tmp.lval[7] = inl(DE4X5_SIGR); j+=4; + ioc->len = j; + if (copy_to_user(ioc->data, tmp.addr, ioc->len)) return -EFAULT; + break; + +#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */ +/* + case DE4X5_DUMP: + j = 0; + tmp.addr[j++] = dev->irq; + for (i=0; idev_addr[i]; + } + tmp.addr[j++] = lp->rxRingSize; + tmp.lval[j>>2] = (long)lp->rx_ring; j+=4; + tmp.lval[j>>2] = (long)lp->tx_ring; j+=4; + + for (i=0;irxRingSize-1;i++){ + if (i < 3) { + tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4; + } + } + tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4; + for (i=0;itxRingSize-1;i++){ + if (i < 3) { + tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4; + } + } + tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4; + + for (i=0;irxRingSize-1;i++){ + if (i < 3) { + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; + } + } + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; + for (i=0;itxRingSize-1;i++){ + if (i < 3) { + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; + } + } + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; + + for (i=0;irxRingSize;i++){ + tmp.lval[j>>2] = le32_to_cpu(lp->rx_ring[i].status); j+=4; + } + for (i=0;itxRingSize;i++){ + tmp.lval[j>>2] = le32_to_cpu(lp->tx_ring[i].status); j+=4; + } + + tmp.lval[j>>2] = inl(DE4X5_BMR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_TPD); j+=4; + tmp.lval[j>>2] = inl(DE4X5_RPD); j+=4; + tmp.lval[j>>2] = inl(DE4X5_RRBA); j+=4; + tmp.lval[j>>2] = inl(DE4X5_TRBA); j+=4; + tmp.lval[j>>2] = inl(DE4X5_STS); j+=4; + tmp.lval[j>>2] = inl(DE4X5_OMR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_IMR); j+=4; + tmp.lval[j>>2] = lp->chipset; j+=4; + if (lp->chipset == DC21140) { + tmp.lval[j>>2] = gep_rd(dev); j+=4; + } else { + tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_STRR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4; + } + tmp.lval[j>>2] = lp->phy[lp->active].id; j+=4; + if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { + tmp.lval[j>>2] = lp->active; j+=4; + tmp.lval[j>>2]=mii_rd(MII_CR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(MII_SR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(MII_ID0,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(MII_ID1,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + if (lp->phy[lp->active].id != BROADCOM_T4) { + tmp.lval[j>>2]=mii_rd(MII_ANA,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(MII_ANLPA,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + } + tmp.lval[j>>2]=mii_rd(0x10,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + if (lp->phy[lp->active].id != BROADCOM_T4) { + tmp.lval[j>>2]=mii_rd(0x11,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(0x12,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + } else { + tmp.lval[j>>2]=mii_rd(0x14,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + } + } + + tmp.addr[j++] = lp->txRingSize; + tmp.addr[j++] = netif_queue_stopped(dev); + + ioc->len = j; + if (copy_to_user(ioc->data, tmp.addr, ioc->len)) return -EFAULT; + break; + +*/ + default: + return -EOPNOTSUPP; + } + + return status; +} + +#ifdef MODULE +/* +** Note now that module autoprobing is allowed under EISA and PCI. The +** IRQ lines will not be auto-detected; instead I'll rely on the BIOSes +** to "do the right thing". +*/ +#define LP(a) ((struct de4x5_private *)(a)) +static struct net_device *mdev = NULL; +static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED */ +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "de4x5 I/O base address"); + +int +init_module(void) +{ + int i, num, status = -EIO; + struct net_device *p; + + num = count_adapters(); + + for (i=0; ipriv; + if (lp) { + release_region(p->base_addr, (lp->bus == PCI ? + DE4X5_PCI_TOTAL_SIZE : + DE4X5_EISA_TOTAL_SIZE)); + if (lp->cache.priv) { /* Private area allocated? */ + kfree(lp->cache.priv); /* Free the private area */ + } + if (lp->rx_ring) { + pci_free_consistent(lp->pdev, lp->dma_size, lp->rx_ring, + lp->dma_rings); + } + } + kfree(p); + } else { + status = 0; /* At least one adapter will work */ + lastModule = p; + } + } + + return status; +} + +void +cleanup_module(void) +{ + while (mdev != NULL) { + mdev = unlink_modules(mdev); + } + + return; +} + +static struct net_device * +unlink_modules(struct net_device *p) +{ + struct net_device *next = NULL; + + if (p->priv) { /* Private areas allocated? */ + struct de4x5_private *lp = (struct de4x5_private *)p->priv; + + next = lp->next_module; + if (lp->rx_ring) { + pci_free_consistent(lp->pdev, lp->dma_size, lp->rx_ring, + lp->dma_rings); + } + release_region(p->base_addr, (lp->bus == PCI ? + DE4X5_PCI_TOTAL_SIZE : + DE4X5_EISA_TOTAL_SIZE)); + kfree(lp->cache.priv); /* Free the private area */ + } + unregister_netdev(p); + kfree(p); /* Free the device structure */ + + return next; +} + +static int +count_adapters(void) +{ + int i, j=0; + u_short vendor; + u_int class = DE4X5_CLASS_CODE; + u_int device; + +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) + char name[DE4X5_STRLEN]; + u_long iobase = 0x1000; + + for (i=1; ivendor; + device = pdev->device << 8; + if (is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x) j++; + } + + return j; +} + +/* +** If at end of eth device list and can't use current entry, malloc +** one up. If memory could not be allocated, print an error message. +*/ +static struct net_device * __init +insert_device(struct net_device *dev, u_long iobase, int (*init)(struct net_device *)) +{ + struct net_device *new; + + new = (struct net_device *)kmalloc(sizeof(struct net_device), GFP_KERNEL); + if (new == NULL) { + printk("de4x5.c: Device not initialised, insufficient memory\n"); + return NULL; + } else { + memset((char *)new, 0, sizeof(struct net_device)); + new->base_addr = iobase; /* assign the io address */ + new->init = init; /* initialisation routine */ + } + + return new; +} + +#endif /* MODULE */ + + +/* + * Local variables: + * + * Delete -DMODVERSIONS below if you didn't define this in your kernel + * + * compile-command: "gcc -D__KERNEL__ -DMODULE -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -DMODVERSIONS -include /linux/include/linux/modversions.h -c de4x5.c" + * End: + */ diff -Nru a/drivers/net/tulip/de4x5.h b/drivers/net/tulip/de4x5.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/tulip/de4x5.h Thu Mar 7 18:17:45 2002 @@ -0,0 +1,1031 @@ +/* + Copyright 1994 Digital Equipment Corporation. + + This software may be used and distributed according to the terms of the + GNU General Public License, incorporated herein by reference. + + The author may be reached as davies@wanton.lkg.dec.com or Digital + Equipment Corporation, 550 King Street, Littleton MA 01460. + + ========================================================================= +*/ + +/* +** DC21040 CSR<1..15> Register Address Map +*/ +#define DE4X5_BMR iobase+(0x000 << lp->bus) /* Bus Mode Register */ +#define DE4X5_TPD iobase+(0x008 << lp->bus) /* Transmit Poll Demand Reg */ +#define DE4X5_RPD iobase+(0x010 << lp->bus) /* Receive Poll Demand Reg */ +#define DE4X5_RRBA iobase+(0x018 << lp->bus) /* RX Ring Base Address Reg */ +#define DE4X5_TRBA iobase+(0x020 << lp->bus) /* TX Ring Base Address Reg */ +#define DE4X5_STS iobase+(0x028 << lp->bus) /* Status Register */ +#define DE4X5_OMR iobase+(0x030 << lp->bus) /* Operation Mode Register */ +#define DE4X5_IMR iobase+(0x038 << lp->bus) /* Interrupt Mask Register */ +#define DE4X5_MFC iobase+(0x040 << lp->bus) /* Missed Frame Counter */ +#define DE4X5_APROM iobase+(0x048 << lp->bus) /* Ethernet Address PROM */ +#define DE4X5_BROM iobase+(0x048 << lp->bus) /* Boot ROM Register */ +#define DE4X5_SROM iobase+(0x048 << lp->bus) /* Serial ROM Register */ +#define DE4X5_MII iobase+(0x048 << lp->bus) /* MII Interface Register */ +#define DE4X5_DDR iobase+(0x050 << lp->bus) /* Data Diagnostic Register */ +#define DE4X5_FDR iobase+(0x058 << lp->bus) /* Full Duplex Register */ +#define DE4X5_GPT iobase+(0x058 << lp->bus) /* General Purpose Timer Reg.*/ +#define DE4X5_GEP iobase+(0x060 << lp->bus) /* General Purpose Register */ +#define DE4X5_SISR iobase+(0x060 << lp->bus) /* SIA Status Register */ +#define DE4X5_SICR iobase+(0x068 << lp->bus) /* SIA Connectivity Register */ +#define DE4X5_STRR iobase+(0x070 << lp->bus) /* SIA TX/RX Register */ +#define DE4X5_SIGR iobase+(0x078 << lp->bus) /* SIA General Register */ + +/* +** EISA Register Address Map +*/ +#define EISA_ID iobase+0x0c80 /* EISA ID Registers */ +#define EISA_ID0 iobase+0x0c80 /* EISA ID Register 0 */ +#define EISA_ID1 iobase+0x0c81 /* EISA ID Register 1 */ +#define EISA_ID2 iobase+0x0c82 /* EISA ID Register 2 */ +#define EISA_ID3 iobase+0x0c83 /* EISA ID Register 3 */ +#define EISA_CR iobase+0x0c84 /* EISA Control Register */ +#define EISA_REG0 iobase+0x0c88 /* EISA Configuration Register 0 */ +#define EISA_REG1 iobase+0x0c89 /* EISA Configuration Register 1 */ +#define EISA_REG2 iobase+0x0c8a /* EISA Configuration Register 2 */ +#define EISA_REG3 iobase+0x0c8f /* EISA Configuration Register 3 */ +#define EISA_APROM iobase+0x0c90 /* Ethernet Address PROM */ + +/* +** PCI/EISA Configuration Registers Address Map +*/ +#define PCI_CFID iobase+0x0008 /* PCI Configuration ID Register */ +#define PCI_CFCS iobase+0x000c /* PCI Command/Status Register */ +#define PCI_CFRV iobase+0x0018 /* PCI Revision Register */ +#define PCI_CFLT iobase+0x001c /* PCI Latency Timer Register */ +#define PCI_CBIO iobase+0x0028 /* PCI Base I/O Register */ +#define PCI_CBMA iobase+0x002c /* PCI Base Memory Address Register */ +#define PCI_CBER iobase+0x0030 /* PCI Expansion ROM Base Address Reg. */ +#define PCI_CFIT iobase+0x003c /* PCI Configuration Interrupt Register */ +#define PCI_CFDA iobase+0x0040 /* PCI Driver Area Register */ +#define PCI_CFDD iobase+0x0041 /* PCI Driver Dependent Area Register */ +#define PCI_CFPM iobase+0x0043 /* PCI Power Management Area Register */ + +/* +** EISA Configuration Register 0 bit definitions +*/ +#define ER0_BSW 0x80 /* EISA Bus Slave Width, 1: 32 bits */ +#define ER0_BMW 0x40 /* EISA Bus Master Width, 1: 32 bits */ +#define ER0_EPT 0x20 /* EISA PREEMPT Time, 0: 23 BCLKs */ +#define ER0_ISTS 0x10 /* Interrupt Status (X) */ +#define ER0_LI 0x08 /* Latch Interrupts */ +#define ER0_INTL 0x06 /* INTerrupt Level */ +#define ER0_INTT 0x01 /* INTerrupt Type, 0: Level, 1: Edge */ + +/* +** EISA Configuration Register 1 bit definitions +*/ +#define ER1_IAM 0xe0 /* ISA Address Mode */ +#define ER1_IAE 0x10 /* ISA Addressing Enable */ +#define ER1_UPIN 0x0f /* User Pins */ + +/* +** EISA Configuration Register 2 bit definitions +*/ +#define ER2_BRS 0xc0 /* Boot ROM Size */ +#define ER2_BRA 0x3c /* Boot ROM Address <16:13> */ + +/* +** EISA Configuration Register 3 bit definitions +*/ +#define ER3_BWE 0x40 /* Burst Write Enable */ +#define ER3_BRE 0x04 /* Burst Read Enable */ +#define ER3_LSR 0x02 /* Local Software Reset */ + +/* +** PCI Configuration ID Register (PCI_CFID). The Device IDs are left +** shifted 8 bits to allow detection of DC21142 and DC21143 variants with +** the configuration revision register step number. +*/ +#define CFID_DID 0xff00 /* Device ID */ +#define CFID_VID 0x00ff /* Vendor ID */ +#define DC21040_DID 0x0200 /* Unique Device ID # */ +#define DC21040_VID 0x1011 /* DC21040 Manufacturer */ +#define DC21041_DID 0x1400 /* Unique Device ID # */ +#define DC21041_VID 0x1011 /* DC21041 Manufacturer */ +#define DC21140_DID 0x0900 /* Unique Device ID # */ +#define DC21140_VID 0x1011 /* DC21140 Manufacturer */ +#define DC2114x_DID 0x1900 /* Unique Device ID # */ +#define DC2114x_VID 0x1011 /* DC2114[23] Manufacturer */ + +/* +** Chipset defines +*/ +#define DC21040 DC21040_DID +#define DC21041 DC21041_DID +#define DC21140 DC21140_DID +#define DC2114x DC2114x_DID +#define DC21142 (DC2114x_DID | 0x0010) +#define DC21143 (DC2114x_DID | 0x0030) +#define DC2114x_BRK 0x0020 /* CFRV break between DC21142 & DC21143 */ + +#define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID)) +#define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID)) +#define is_DC21140 ((vendor == DC21140_VID) && (device == DC21140_DID)) +#define is_DC2114x ((vendor == DC2114x_VID) && (device == DC2114x_DID)) +#define is_DC21142 ((vendor == DC2114x_VID) && (device == DC21142)) +#define is_DC21143 ((vendor == DC2114x_VID) && (device == DC21143)) + +/* +** PCI Configuration Command/Status Register (PCI_CFCS) +*/ +#define CFCS_DPE 0x80000000 /* Detected Parity Error (S) */ +#define CFCS_SSE 0x40000000 /* Signal System Error (S) */ +#define CFCS_RMA 0x20000000 /* Receive Master Abort (S) */ +#define CFCS_RTA 0x10000000 /* Receive Target Abort (S) */ +#define CFCS_DST 0x06000000 /* DEVSEL Timing (S) */ +#define CFCS_DPR 0x01000000 /* Data Parity Report (S) */ +#define CFCS_FBB 0x00800000 /* Fast Back-To-Back (S) */ +#define CFCS_SEE 0x00000100 /* System Error Enable (C) */ +#define CFCS_PER 0x00000040 /* Parity Error Response (C) */ +#define CFCS_MO 0x00000004 /* Master Operation (C) */ +#define CFCS_MSA 0x00000002 /* Memory Space Access (C) */ +#define CFCS_IOSA 0x00000001 /* I/O Space Access (C) */ + +/* +** PCI Configuration Revision Register (PCI_CFRV) +*/ +#define CFRV_BC 0xff000000 /* Base Class */ +#define CFRV_SC 0x00ff0000 /* Subclass */ +#define CFRV_RN 0x000000f0 /* Revision Number */ +#define CFRV_SN 0x0000000f /* Step Number */ +#define BASE_CLASS 0x02000000 /* Indicates Network Controller */ +#define SUB_CLASS 0x00000000 /* Indicates Ethernet Controller */ +#define STEP_NUMBER 0x00000020 /* Increments for future chips */ +#define REV_NUMBER 0x00000003 /* 0x00, 0x01, 0x02, 0x03: Rev in Step */ +#define CFRV_MASK 0xffff0000 /* Register mask */ + +/* +** PCI Configuration Latency Timer Register (PCI_CFLT) +*/ +#define CFLT_BC 0x0000ff00 /* Latency Timer bits */ + +/* +** PCI Configuration Base I/O Address Register (PCI_CBIO) +*/ +#define CBIO_MASK -128 /* Base I/O Address Mask */ +#define CBIO_IOSI 0x00000001 /* I/O Space Indicator (RO, value is 1) */ + +/* +** PCI Configuration Card Information Structure Register (PCI_CCIS) +*/ +#define CCIS_ROMI 0xf0000000 /* ROM Image */ +#define CCIS_ASO 0x0ffffff8 /* Address Space Offset */ +#define CCIS_ASI 0x00000007 /* Address Space Indicator */ + +/* +** PCI Configuration Subsystem ID Register (PCI_SSID) +*/ +#define SSID_SSID 0xffff0000 /* Subsystem ID */ +#define SSID_SVID 0x0000ffff /* Subsystem Vendor ID */ + +/* +** PCI Configuration Expansion ROM Base Address Register (PCI_CBER) +*/ +#define CBER_MASK 0xfffffc00 /* Expansion ROM Base Address Mask */ +#define CBER_ROME 0x00000001 /* ROM Enable */ + +/* +** PCI Configuration Interrupt Register (PCI_CFIT) +*/ +#define CFIT_MXLT 0xff000000 /* MAX_LAT Value (0.25us periods) */ +#define CFIT_MNGT 0x00ff0000 /* MIN_GNT Value (0.25us periods) */ +#define CFIT_IRQP 0x0000ff00 /* Interrupt Pin */ +#define CFIT_IRQL 0x000000ff /* Interrupt Line */ + +/* +** PCI Configuration Power Management Area Register (PCI_CFPM) +*/ +#define SLEEP 0x80 /* Power Saving Sleep Mode */ +#define SNOOZE 0x40 /* Power Saving Snooze Mode */ +#define WAKEUP 0x00 /* Power Saving Wakeup */ + +#define PCI_CFDA_DSU 0x41 /* 8 bit Configuration Space Address */ +#define PCI_CFDA_PSM 0x43 /* 8 bit Configuration Space Address */ + +/* +** DC21040 Bus Mode Register (DE4X5_BMR) +*/ +#define BMR_RML 0x00200000 /* [Memory] Read Multiple */ +#define BMR_DBO 0x00100000 /* Descriptor Byte Ordering (Endian) */ +#define BMR_TAP 0x000e0000 /* Transmit Automatic Polling */ +#define BMR_DAS 0x00010000 /* Diagnostic Address Space */ +#define BMR_CAL 0x0000c000 /* Cache Alignment */ +#define BMR_PBL 0x00003f00 /* Programmable Burst Length */ +#define BMR_BLE 0x00000080 /* Big/Little Endian */ +#define BMR_DSL 0x0000007c /* Descriptor Skip Length */ +#define BMR_BAR 0x00000002 /* Bus ARbitration */ +#define BMR_SWR 0x00000001 /* Software Reset */ + + /* Timings here are for 10BASE-T/AUI only*/ +#define TAP_NOPOLL 0x00000000 /* No automatic polling */ +#define TAP_200US 0x00020000 /* TX automatic polling every 200us */ +#define TAP_800US 0x00040000 /* TX automatic polling every 800us */ +#define TAP_1_6MS 0x00060000 /* TX automatic polling every 1.6ms */ +#define TAP_12_8US 0x00080000 /* TX automatic polling every 12.8us */ +#define TAP_25_6US 0x000a0000 /* TX automatic polling every 25.6us */ +#define TAP_51_2US 0x000c0000 /* TX automatic polling every 51.2us */ +#define TAP_102_4US 0x000e0000 /* TX automatic polling every 102.4us */ + +#define CAL_NOUSE 0x00000000 /* Not used */ +#define CAL_8LONG 0x00004000 /* 8-longword alignment */ +#define CAL_16LONG 0x00008000 /* 16-longword alignment */ +#define CAL_32LONG 0x0000c000 /* 32-longword alignment */ + +#define PBL_0 0x00000000 /* DMA burst length = amount in RX FIFO */ +#define PBL_1 0x00000100 /* 1 longword DMA burst length */ +#define PBL_2 0x00000200 /* 2 longwords DMA burst length */ +#define PBL_4 0x00000400 /* 4 longwords DMA burst length */ +#define PBL_8 0x00000800 /* 8 longwords DMA burst length */ +#define PBL_16 0x00001000 /* 16 longwords DMA burst length */ +#define PBL_32 0x00002000 /* 32 longwords DMA burst length */ + +#define DSL_0 0x00000000 /* 0 longword / descriptor */ +#define DSL_1 0x00000004 /* 1 longword / descriptor */ +#define DSL_2 0x00000008 /* 2 longwords / descriptor */ +#define DSL_4 0x00000010 /* 4 longwords / descriptor */ +#define DSL_8 0x00000020 /* 8 longwords / descriptor */ +#define DSL_16 0x00000040 /* 16 longwords / descriptor */ +#define DSL_32 0x00000080 /* 32 longwords / descriptor */ + +/* +** DC21040 Transmit Poll Demand Register (DE4X5_TPD) +*/ +#define TPD 0x00000001 /* Transmit Poll Demand */ + +/* +** DC21040 Receive Poll Demand Register (DE4X5_RPD) +*/ +#define RPD 0x00000001 /* Receive Poll Demand */ + +/* +** DC21040 Receive Ring Base Address Register (DE4X5_RRBA) +*/ +#define RRBA 0xfffffffc /* RX Descriptor List Start Address */ + +/* +** DC21040 Transmit Ring Base Address Register (DE4X5_TRBA) +*/ +#define TRBA 0xfffffffc /* TX Descriptor List Start Address */ + +/* +** Status Register (DE4X5_STS) +*/ +#define STS_GPI 0x04000000 /* General Purpose Port Interrupt */ +#define STS_BE 0x03800000 /* Bus Error Bits */ +#define STS_TS 0x00700000 /* Transmit Process State */ +#define STS_RS 0x000e0000 /* Receive Process State */ +#define STS_NIS 0x00010000 /* Normal Interrupt Summary */ +#define STS_AIS 0x00008000 /* Abnormal Interrupt Summary */ +#define STS_ER 0x00004000 /* Early Receive */ +#define STS_FBE 0x00002000 /* Fatal Bus Error */ +#define STS_SE 0x00002000 /* System Error */ +#define STS_LNF 0x00001000 /* Link Fail */ +#define STS_FD 0x00000800 /* Full-Duplex Short Frame Received */ +#define STS_TM 0x00000800 /* Timer Expired (DC21041) */ +#define STS_ETI 0x00000400 /* Early Transmit Interrupt */ +#define STS_AT 0x00000400 /* AUI/TP Pin */ +#define STS_RWT 0x00000200 /* Receive Watchdog Time-Out */ +#define STS_RPS 0x00000100 /* Receive Process Stopped */ +#define STS_RU 0x00000080 /* Receive Buffer Unavailable */ +#define STS_RI 0x00000040 /* Receive Interrupt */ +#define STS_UNF 0x00000020 /* Transmit Underflow */ +#define STS_LNP 0x00000010 /* Link Pass */ +#define STS_ANC 0x00000010 /* Autonegotiation Complete */ +#define STS_TJT 0x00000008 /* Transmit Jabber Time-Out */ +#define STS_TU 0x00000004 /* Transmit Buffer Unavailable */ +#define STS_TPS 0x00000002 /* Transmit Process Stopped */ +#define STS_TI 0x00000001 /* Transmit Interrupt */ + +#define EB_PAR 0x00000000 /* Parity Error */ +#define EB_MA 0x00800000 /* Master Abort */ +#define EB_TA 0x01000000 /* Target Abort */ +#define EB_RES0 0x01800000 /* Reserved */ +#define EB_RES1 0x02000000 /* Reserved */ + +#define TS_STOP 0x00000000 /* Stopped */ +#define TS_FTD 0x00100000 /* Fetch Transmit Descriptor */ +#define TS_WEOT 0x00200000 /* Wait for End Of Transmission */ +#define TS_QDAT 0x00300000 /* Queue skb data into TX FIFO */ +#define TS_RES 0x00400000 /* Reserved */ +#define TS_SPKT 0x00500000 /* Setup Packet */ +#define TS_SUSP 0x00600000 /* Suspended */ +#define TS_CLTD 0x00700000 /* Close Transmit Descriptor */ + +#define RS_STOP 0x00000000 /* Stopped */ +#define RS_FRD 0x00020000 /* Fetch Receive Descriptor */ +#define RS_CEOR 0x00040000 /* Check for End of Receive Packet */ +#define RS_WFRP 0x00060000 /* Wait for Receive Packet */ +#define RS_SUSP 0x00080000 /* Suspended */ +#define RS_CLRD 0x000a0000 /* Close Receive Descriptor */ +#define RS_FLUSH 0x000c0000 /* Flush RX FIFO */ +#define RS_QRFS 0x000e0000 /* Queue RX FIFO into RX Skb */ + +#define INT_CANCEL 0x0001ffff /* For zeroing all interrupt sources */ + +/* +** Operation Mode Register (DE4X5_OMR) +*/ +#define OMR_SC 0x80000000 /* Special Capture Effect Enable */ +#define OMR_RA 0x40000000 /* Receive All */ +#define OMR_SDP 0x02000000 /* SD Polarity - MUST BE ASSERTED */ +#define OMR_SCR 0x01000000 /* Scrambler Mode */ +#define OMR_PCS 0x00800000 /* PCS Function */ +#define OMR_TTM 0x00400000 /* Transmit Threshold Mode */ +#define OMR_SF 0x00200000 /* Store and Forward */ +#define OMR_HBD 0x00080000 /* HeartBeat Disable */ +#define OMR_PS 0x00040000 /* Port Select */ +#define OMR_CA 0x00020000 /* Capture Effect Enable */ +#define OMR_BP 0x00010000 /* Back Pressure */ +#define OMR_TR 0x0000c000 /* Threshold Control Bits */ +#define OMR_ST 0x00002000 /* Start/Stop Transmission Command */ +#define OMR_FC 0x00001000 /* Force Collision Mode */ +#define OMR_OM 0x00000c00 /* Operating Mode */ +#define OMR_FDX 0x00000200 /* Full Duplex Mode */ +#define OMR_FKD 0x00000100 /* Flaky Oscillator Disable */ +#define OMR_PM 0x00000080 /* Pass All Multicast */ +#define OMR_PR 0x00000040 /* Promiscuous Mode */ +#define OMR_SB 0x00000020 /* Start/Stop Backoff Counter */ +#define OMR_IF 0x00000010 /* Inverse Filtering */ +#define OMR_PB 0x00000008 /* Pass Bad Frames */ +#define OMR_HO 0x00000004 /* Hash Only Filtering Mode */ +#define OMR_SR 0x00000002 /* Start/Stop Receive */ +#define OMR_HP 0x00000001 /* Hash/Perfect Receive Filtering Mode */ + +#define TR_72 0x00000000 /* Threshold set to 72 (128) bytes */ +#define TR_96 0x00004000 /* Threshold set to 96 (256) bytes */ +#define TR_128 0x00008000 /* Threshold set to 128 (512) bytes */ +#define TR_160 0x0000c000 /* Threshold set to 160 (1024) bytes */ + +#define OMR_DEF (OMR_SDP) +#define OMR_SIA (OMR_SDP | OMR_TTM) +#define OMR_SYM (OMR_SDP | OMR_SCR | OMR_PCS | OMR_HBD | OMR_PS) +#define OMR_MII_10 (OMR_SDP | OMR_TTM | OMR_PS) +#define OMR_MII_100 (OMR_SDP | OMR_HBD | OMR_PS) + +/* +** DC21040 Interrupt Mask Register (DE4X5_IMR) +*/ +#define IMR_GPM 0x04000000 /* General Purpose Port Mask */ +#define IMR_NIM 0x00010000 /* Normal Interrupt Summary Mask */ +#define IMR_AIM 0x00008000 /* Abnormal Interrupt Summary Mask */ +#define IMR_ERM 0x00004000 /* Early Receive Mask */ +#define IMR_FBM 0x00002000 /* Fatal Bus Error Mask */ +#define IMR_SEM 0x00002000 /* System Error Mask */ +#define IMR_LFM 0x00001000 /* Link Fail Mask */ +#define IMR_FDM 0x00000800 /* Full-Duplex (Short Frame) Mask */ +#define IMR_TMM 0x00000800 /* Timer Expired Mask (DC21041) */ +#define IMR_ETM 0x00000400 /* Early Transmit Interrupt Mask */ +#define IMR_ATM 0x00000400 /* AUI/TP Switch Mask */ +#define IMR_RWM 0x00000200 /* Receive Watchdog Time-Out Mask */ +#define IMR_RSM 0x00000100 /* Receive Stopped Mask */ +#define IMR_RUM 0x00000080 /* Receive Buffer Unavailable Mask */ +#define IMR_RIM 0x00000040 /* Receive Interrupt Mask */ +#define IMR_UNM 0x00000020 /* Underflow Interrupt Mask */ +#define IMR_ANM 0x00000010 /* Autonegotiation Complete Mask */ +#define IMR_LPM 0x00000010 /* Link Pass */ +#define IMR_TJM 0x00000008 /* Transmit Time-Out Jabber Mask */ +#define IMR_TUM 0x00000004 /* Transmit Buffer Unavailable Mask */ +#define IMR_TSM 0x00000002 /* Transmission Stopped Mask */ +#define IMR_TIM 0x00000001 /* Transmit Interrupt Mask */ + +/* +** Missed Frames and FIFO Overflow Counters (DE4X5_MFC) +*/ +#define MFC_FOCO 0x10000000 /* FIFO Overflow Counter Overflow Bit */ +#define MFC_FOC 0x0ffe0000 /* FIFO Overflow Counter Bits */ +#define MFC_OVFL 0x00010000 /* Missed Frames Counter Overflow Bit */ +#define MFC_CNTR 0x0000ffff /* Missed Frames Counter Bits */ +#define MFC_FOCM 0x1ffe0000 /* FIFO Overflow Counter Mask */ + +/* +** DC21040 Ethernet Address PROM (DE4X5_APROM) +*/ +#define APROM_DN 0x80000000 /* Data Not Valid */ +#define APROM_DT 0x000000ff /* Address Byte */ + +/* +** DC21041 Boot/Ethernet Address ROM (DE4X5_BROM) +*/ +#define BROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */ +#define BROM_RD 0x00004000 /* Read from Boot ROM */ +#define BROM_WR 0x00002000 /* Write to Boot ROM */ +#define BROM_BR 0x00001000 /* Select Boot ROM when set */ +#define BROM_SR 0x00000800 /* Select Serial ROM when set */ +#define BROM_REG 0x00000400 /* External Register Select */ +#define BROM_DT 0x000000ff /* Data Byte */ + +/* +** DC21041 Serial/Ethernet Address ROM (DE4X5_SROM, DE4X5_MII) +*/ +#define MII_MDI 0x00080000 /* MII Management Data In */ +#define MII_MDO 0x00060000 /* MII Management Mode/Data Out */ +#define MII_MRD 0x00040000 /* MII Management Define Read Mode */ +#define MII_MWR 0x00000000 /* MII Management Define Write Mode */ +#define MII_MDT 0x00020000 /* MII Management Data Out */ +#define MII_MDC 0x00010000 /* MII Management Clock */ +#define MII_RD 0x00004000 /* Read from MII */ +#define MII_WR 0x00002000 /* Write to MII */ +#define MII_SEL 0x00000800 /* Select MII when RESET */ + +#define SROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */ +#define SROM_RD 0x00004000 /* Read from Boot ROM */ +#define SROM_WR 0x00002000 /* Write to Boot ROM */ +#define SROM_BR 0x00001000 /* Select Boot ROM when set */ +#define SROM_SR 0x00000800 /* Select Serial ROM when set */ +#define SROM_REG 0x00000400 /* External Register Select */ +#define SROM_DT 0x000000ff /* Data Byte */ + +#define DT_OUT 0x00000008 /* Serial Data Out */ +#define DT_IN 0x00000004 /* Serial Data In */ +#define DT_CLK 0x00000002 /* Serial ROM Clock */ +#define DT_CS 0x00000001 /* Serial ROM Chip Select */ + +#define MII_PREAMBLE 0xffffffff /* MII Management Preamble */ +#define MII_TEST 0xaaaaaaaa /* MII Test Signal */ +#define MII_STRD 0x06 /* Start of Frame+Op Code: use low nibble */ +#define MII_STWR 0x0a /* Start of Frame+Op Code: use low nibble */ + +#define MII_CR 0x00 /* MII Management Control Register */ +#define MII_SR 0x01 /* MII Management Status Register */ +#define MII_ID0 0x02 /* PHY Identifier Register 0 */ +#define MII_ID1 0x03 /* PHY Identifier Register 1 */ +#define MII_ANA 0x04 /* Auto Negotiation Advertisement */ +#define MII_ANLPA 0x05 /* Auto Negotiation Link Partner Ability */ +#define MII_ANE 0x06 /* Auto Negotiation Expansion */ +#define MII_ANP 0x07 /* Auto Negotiation Next Page TX */ + +#define DE4X5_MAX_MII 32 /* Maximum address of MII PHY devices */ + +/* +** MII Management Control Register +*/ +#define MII_CR_RST 0x8000 /* RESET the PHY chip */ +#define MII_CR_LPBK 0x4000 /* Loopback enable */ +#define MII_CR_SPD 0x2000 /* 0: 10Mb/s; 1: 100Mb/s */ +#define MII_CR_10 0x0000 /* Set 10Mb/s */ +#define MII_CR_100 0x2000 /* Set 100Mb/s */ +#define MII_CR_ASSE 0x1000 /* Auto Speed Select Enable */ +#define MII_CR_PD 0x0800 /* Power Down */ +#define MII_CR_ISOL 0x0400 /* Isolate Mode */ +#define MII_CR_RAN 0x0200 /* Restart Auto Negotiation */ +#define MII_CR_FDM 0x0100 /* Full Duplex Mode */ +#define MII_CR_CTE 0x0080 /* Collision Test Enable */ + +/* +** MII Management Status Register +*/ +#define MII_SR_T4C 0x8000 /* 100BASE-T4 capable */ +#define MII_SR_TXFD 0x4000 /* 100BASE-TX Full Duplex capable */ +#define MII_SR_TXHD 0x2000 /* 100BASE-TX Half Duplex capable */ +#define MII_SR_TFD 0x1000 /* 10BASE-T Full Duplex capable */ +#define MII_SR_THD 0x0800 /* 10BASE-T Half Duplex capable */ +#define MII_SR_ASSC 0x0020 /* Auto Speed Selection Complete*/ +#define MII_SR_RFD 0x0010 /* Remote Fault Detected */ +#define MII_SR_ANC 0x0008 /* Auto Negotiation capable */ +#define MII_SR_LKS 0x0004 /* Link Status */ +#define MII_SR_JABD 0x0002 /* Jabber Detect */ +#define MII_SR_XC 0x0001 /* Extended Capabilities */ + +/* +** MII Management Auto Negotiation Advertisement Register +*/ +#define MII_ANA_TAF 0x03e0 /* Technology Ability Field */ +#define MII_ANA_T4AM 0x0200 /* T4 Technology Ability Mask */ +#define MII_ANA_TXAM 0x0180 /* TX Technology Ability Mask */ +#define MII_ANA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ +#define MII_ANA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ +#define MII_ANA_100M 0x0380 /* 100Mb Technology Ability Mask */ +#define MII_ANA_10M 0x0060 /* 10Mb Technology Ability Mask */ +#define MII_ANA_CSMA 0x0001 /* CSMA-CD Capable */ + +/* +** MII Management Auto Negotiation Remote End Register +*/ +#define MII_ANLPA_NP 0x8000 /* Next Page (Enable) */ +#define MII_ANLPA_ACK 0x4000 /* Remote Acknowledge */ +#define MII_ANLPA_RF 0x2000 /* Remote Fault */ +#define MII_ANLPA_TAF 0x03e0 /* Technology Ability Field */ +#define MII_ANLPA_T4AM 0x0200 /* T4 Technology Ability Mask */ +#define MII_ANLPA_TXAM 0x0180 /* TX Technology Ability Mask */ +#define MII_ANLPA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ +#define MII_ANLPA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ +#define MII_ANLPA_100M 0x0380 /* 100Mb Technology Ability Mask */ +#define MII_ANLPA_10M 0x0060 /* 10Mb Technology Ability Mask */ +#define MII_ANLPA_CSMA 0x0001 /* CSMA-CD Capable */ + +/* +** SROM Media Definitions (ABG SROM Section) +*/ +#define MEDIA_NWAY 0x0080 /* Nway (Auto Negotiation) on PHY */ +#define MEDIA_MII 0x0040 /* MII Present on the adapter */ +#define MEDIA_FIBRE 0x0008 /* Fibre Media present */ +#define MEDIA_AUI 0x0004 /* AUI Media present */ +#define MEDIA_TP 0x0002 /* TP Media present */ +#define MEDIA_BNC 0x0001 /* BNC Media present */ + +/* +** SROM Definitions (Digital Semiconductor Format) +*/ +#define SROM_SSVID 0x0000 /* Sub-system Vendor ID offset */ +#define SROM_SSID 0x0002 /* Sub-system ID offset */ +#define SROM_CISPL 0x0004 /* CardBus CIS Pointer low offset */ +#define SROM_CISPH 0x0006 /* CardBus CIS Pointer high offset */ +#define SROM_IDCRC 0x0010 /* ID Block CRC offset*/ +#define SROM_RSVD2 0x0011 /* ID Reserved 2 offset */ +#define SROM_SFV 0x0012 /* SROM Format Version offset */ +#define SROM_CCNT 0x0013 /* Controller Count offset */ +#define SROM_HWADD 0x0014 /* Hardware Address offset */ +#define SROM_MRSVD 0x007c /* Manufacturer Reserved offset*/ +#define SROM_CRC 0x007e /* SROM CRC offset */ + +/* +** SROM Media Connection Definitions +*/ +#define SROM_10BT 0x0000 /* 10BASE-T half duplex */ +#define SROM_10BTN 0x0100 /* 10BASE-T with Nway */ +#define SROM_10BTF 0x0204 /* 10BASE-T full duplex */ +#define SROM_10BTNLP 0x0400 /* 10BASE-T without Link Pass test */ +#define SROM_10B2 0x0001 /* 10BASE-2 (BNC) */ +#define SROM_10B5 0x0002 /* 10BASE-5 (AUI) */ +#define SROM_100BTH 0x0003 /* 100BASE-T half duplex */ +#define SROM_100BTF 0x0205 /* 100BASE-T full duplex */ +#define SROM_100BT4 0x0006 /* 100BASE-T4 */ +#define SROM_100BFX 0x0007 /* 100BASE-FX half duplex (Fiber) */ +#define SROM_M10BT 0x0009 /* MII 10BASE-T half duplex */ +#define SROM_M10BTF 0x020a /* MII 10BASE-T full duplex */ +#define SROM_M100BT 0x000d /* MII 100BASE-T half duplex */ +#define SROM_M100BTF 0x020e /* MII 100BASE-T full duplex */ +#define SROM_M100BT4 0x000f /* MII 100BASE-T4 */ +#define SROM_M100BF 0x0010 /* MII 100BASE-FX half duplex */ +#define SROM_M100BFF 0x0211 /* MII 100BASE-FX full duplex */ +#define SROM_PDA 0x0800 /* Powerup & Dynamic Autosense */ +#define SROM_PAO 0x8800 /* Powerup Autosense Only */ +#define SROM_NSMI 0xffff /* No Selected Media Information */ + +/* +** SROM Media Definitions +*/ +#define SROM_10BASET 0x0000 /* 10BASE-T half duplex */ +#define SROM_10BASE2 0x0001 /* 10BASE-2 (BNC) */ +#define SROM_10BASE5 0x0002 /* 10BASE-5 (AUI) */ +#define SROM_100BASET 0x0003 /* 100BASE-T half duplex */ +#define SROM_10BASETF 0x0004 /* 10BASE-T full duplex */ +#define SROM_100BASETF 0x0005 /* 100BASE-T full duplex */ +#define SROM_100BASET4 0x0006 /* 100BASE-T4 */ +#define SROM_100BASEF 0x0007 /* 100BASE-FX half duplex */ +#define SROM_100BASEFF 0x0008 /* 100BASE-FX full duplex */ + +#define BLOCK_LEN 0x7f /* Extended blocks length mask */ +#define EXT_FIELD 0x40 /* Extended blocks extension field bit */ +#define MEDIA_CODE 0x3f /* Extended blocks media code mask */ + +/* +** SROM Compact Format Block Masks +*/ +#define COMPACT_FI 0x80 /* Format Indicator */ +#define COMPACT_LEN 0x04 /* Length */ +#define COMPACT_MC 0x3f /* Media Code */ + +/* +** SROM Extended Format Block Type 0 Masks +*/ +#define BLOCK0_FI 0x80 /* Format Indicator */ +#define BLOCK0_MCS 0x80 /* Media Code byte Sign */ +#define BLOCK0_MC 0x3f /* Media Code */ + +/* +** DC21040 Full Duplex Register (DE4X5_FDR) +*/ +#define FDR_FDACV 0x0000ffff /* Full Duplex Auto Configuration Value */ + +/* +** DC21041 General Purpose Timer Register (DE4X5_GPT) +*/ +#define GPT_CON 0x00010000 /* One shot: 0, Continuous: 1 */ +#define GPT_VAL 0x0000ffff /* Timer Value */ + +/* +** DC21140 General Purpose Register (DE4X5_GEP) (hardware dependent bits) +*/ +/* Valid ONLY for DE500 hardware */ +#define GEP_LNP 0x00000080 /* Link Pass (input) */ +#define GEP_SLNK 0x00000040 /* SYM LINK (input) */ +#define GEP_SDET 0x00000020 /* Signal Detect (input) */ +#define GEP_HRST 0x00000010 /* Hard RESET (to PHY) (output) */ +#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */ +#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */ +#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */ +#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */ +#define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */ +#define GEP_CTRL 0x00000100 /* GEP control bit */ + +/* +** SIA Register Defaults +*/ +#define CSR13 0x00000001 +#define CSR14 0x0003ff7f /* Autonegotiation disabled */ +#define CSR15 0x00000008 + +/* +** SIA Status Register (DE4X5_SISR) +*/ +#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */ +#define SISR_LPN 0x00008000 /* Link Partner Negotiable */ +#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */ +#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */ +#define SISR_TRF 0x00000800 /* Transmit Remote Fault */ +#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */ +#define SISR_ANR_FDS 0x00000400 /* Auto Negotiate Restart/Full Duplex Sel.*/ +#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */ +#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */ +#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */ +#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */ +#define SISR_DAO 0x00000080 /* PLL All One */ +#define SISR_DAZ 0x00000040 /* PLL All Zero */ +#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */ +#define SISR_DSD 0x00000010 /* PLL Self-Test Done */ +#define SISR_APS 0x00000008 /* Auto Polarity State */ +#define SISR_LKF 0x00000004 /* Link Fail Status */ +#define SISR_LS10 0x00000004 /* 10Mb/s Link Fail Status */ +#define SISR_NCR 0x00000002 /* Network Connection Error */ +#define SISR_LS100 0x00000002 /* 100Mb/s Link Fail Status */ +#define SISR_PAUI 0x00000001 /* AUI_TP Indication */ +#define SISR_MRA 0x00000001 /* MII Receive Port Activity */ + +#define ANS_NDIS 0x00000000 /* Nway disable */ +#define ANS_TDIS 0x00001000 /* Transmit Disable */ +#define ANS_ADET 0x00002000 /* Ability Detect */ +#define ANS_ACK 0x00003000 /* Acknowledge */ +#define ANS_CACK 0x00004000 /* Complete Acknowledge */ +#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */ +#define ANS_LCHK 0x00006000 /* Link Check */ + +#define SISR_RST 0x00000301 /* CSR12 reset */ +#define SISR_ANR 0x00001301 /* Autonegotiation restart */ + +/* +** SIA Connectivity Register (DE4X5_SICR) +*/ +#define SICR_SDM 0xffff0000 /* SIA Diagnostics Mode */ +#define SICR_OE57 0x00008000 /* Output Enable 5 6 7 */ +#define SICR_OE24 0x00004000 /* Output Enable 2 4 */ +#define SICR_OE13 0x00002000 /* Output Enable 1 3 */ +#define SICR_IE 0x00001000 /* Input Enable */ +#define SICR_EXT 0x00000000 /* SIA MUX Select External SIA Mode */ +#define SICR_D_SIA 0x00000400 /* SIA MUX Select Diagnostics - SIA Sigs */ +#define SICR_DPLL 0x00000800 /* SIA MUX Select Diagnostics - DPLL Sigs*/ +#define SICR_APLL 0x00000a00 /* SIA MUX Select Diagnostics - DPLL Sigs*/ +#define SICR_D_RxM 0x00000c00 /* SIA MUX Select Diagnostics - RxM Sigs */ +#define SICR_M_RxM 0x00000d00 /* SIA MUX Select Diagnostics - RxM Sigs */ +#define SICR_LNKT 0x00000e00 /* SIA MUX Select Diagnostics - Link Test*/ +#define SICR_SEL 0x00000f00 /* SIA MUX Select AUI or TP with LEDs */ +#define SICR_ASE 0x00000080 /* APLL Start Enable*/ +#define SICR_SIM 0x00000040 /* Serial Interface Input Multiplexer */ +#define SICR_ENI 0x00000020 /* Encoder Input Multiplexer */ +#define SICR_EDP 0x00000010 /* SIA PLL External Input Enable */ +#define SICR_AUI 0x00000008 /* 10Base-T (0) or AUI (1) */ +#define SICR_CAC 0x00000004 /* CSR Auto Configuration */ +#define SICR_PS 0x00000002 /* Pin AUI/TP Selection */ +#define SICR_SRL 0x00000001 /* SIA Reset */ +#define SIA_RESET 0x00000000 /* SIA Reset Value */ + +/* +** SIA Transmit and Receive Register (DE4X5_STRR) +*/ +#define STRR_TAS 0x00008000 /* 10Base-T/AUI Autosensing Enable */ +#define STRR_SPP 0x00004000 /* Set Polarity Plus */ +#define STRR_APE 0x00002000 /* Auto Polarity Enable */ +#define STRR_LTE 0x00001000 /* Link Test Enable */ +#define STRR_SQE 0x00000800 /* Signal Quality Enable */ +#define STRR_CLD 0x00000400 /* Collision Detect Enable */ +#define STRR_CSQ 0x00000200 /* Collision Squelch Enable */ +#define STRR_RSQ 0x00000100 /* Receive Squelch Enable */ +#define STRR_ANE 0x00000080 /* Auto Negotiate Enable */ +#define STRR_HDE 0x00000040 /* Half Duplex Enable */ +#define STRR_CPEN 0x00000030 /* Compensation Enable */ +#define STRR_LSE 0x00000008 /* Link Pulse Send Enable */ +#define STRR_DREN 0x00000004 /* Driver Enable */ +#define STRR_LBK 0x00000002 /* Loopback Enable */ +#define STRR_ECEN 0x00000001 /* Encoder Enable */ +#define STRR_RESET 0xffffffff /* Reset value for STRR */ + +/* +** SIA General Register (DE4X5_SIGR) +*/ +#define SIGR_RMI 0x40000000 /* Receive Match Interrupt */ +#define SIGR_GI1 0x20000000 /* General Port Interrupt 1 */ +#define SIGR_GI0 0x10000000 /* General Port Interrupt 0 */ +#define SIGR_CWE 0x08000000 /* Control Write Enable */ +#define SIGR_RME 0x04000000 /* Receive Match Enable */ +#define SIGR_GEI1 0x02000000 /* GEP Interrupt Enable on Port 1 */ +#define SIGR_GEI0 0x01000000 /* GEP Interrupt Enable on Port 0 */ +#define SIGR_LGS3 0x00800000 /* LED/GEP3 Select */ +#define SIGR_LGS2 0x00400000 /* LED/GEP2 Select */ +#define SIGR_LGS1 0x00200000 /* LED/GEP1 Select */ +#define SIGR_LGS0 0x00100000 /* LED/GEP0 Select */ +#define SIGR_MD 0x000f0000 /* General Purpose Mode and Data */ +#define SIGR_LV2 0x00008000 /* General Purpose LED2 value */ +#define SIGR_LE2 0x00004000 /* General Purpose LED2 enable */ +#define SIGR_FRL 0x00002000 /* Force Receiver Low */ +#define SIGR_DPST 0x00001000 /* PLL Self Test Start */ +#define SIGR_LSD 0x00000800 /* LED Stretch Disable */ +#define SIGR_FLF 0x00000400 /* Force Link Fail */ +#define SIGR_FUSQ 0x00000200 /* Force Unsquelch */ +#define SIGR_TSCK 0x00000100 /* Test Clock */ +#define SIGR_LV1 0x00000080 /* General Purpose LED1 value */ +#define SIGR_LE1 0x00000040 /* General Purpose LED1 enable */ +#define SIGR_RWR 0x00000020 /* Receive Watchdog Release */ +#define SIGR_RWD 0x00000010 /* Receive Watchdog Disable */ +#define SIGR_ABM 0x00000008 /* BNC: 0, AUI:1 */ +#define SIGR_JCK 0x00000004 /* Jabber Clock */ +#define SIGR_HUJ 0x00000002 /* Host Unjab */ +#define SIGR_JBD 0x00000001 /* Jabber Disable */ +#define SIGR_RESET 0xffff0000 /* Reset value for SIGR */ + +/* +** Receive Descriptor Bit Summary +*/ +#define R_OWN 0x80000000 /* Own Bit */ +#define RD_FF 0x40000000 /* Filtering Fail */ +#define RD_FL 0x3fff0000 /* Frame Length */ +#define RD_ES 0x00008000 /* Error Summary */ +#define RD_LE 0x00004000 /* Length Error */ +#define RD_DT 0x00003000 /* Data Type */ +#define RD_RF 0x00000800 /* Runt Frame */ +#define RD_MF 0x00000400 /* Multicast Frame */ +#define RD_FS 0x00000200 /* First Descriptor */ +#define RD_LS 0x00000100 /* Last Descriptor */ +#define RD_TL 0x00000080 /* Frame Too Long */ +#define RD_CS 0x00000040 /* Collision Seen */ +#define RD_FT 0x00000020 /* Frame Type */ +#define RD_RJ 0x00000010 /* Receive Watchdog */ +#define RD_RE 0x00000008 /* Report on MII Error */ +#define RD_DB 0x00000004 /* Dribbling Bit */ +#define RD_CE 0x00000002 /* CRC Error */ +#define RD_OF 0x00000001 /* Overflow */ + +#define RD_RER 0x02000000 /* Receive End Of Ring */ +#define RD_RCH 0x01000000 /* Second Address Chained */ +#define RD_RBS2 0x003ff800 /* Buffer 2 Size */ +#define RD_RBS1 0x000007ff /* Buffer 1 Size */ + +/* +** Transmit Descriptor Bit Summary +*/ +#define T_OWN 0x80000000 /* Own Bit */ +#define TD_ES 0x00008000 /* Error Summary */ +#define TD_TO 0x00004000 /* Transmit Jabber Time-Out */ +#define TD_LO 0x00000800 /* Loss Of Carrier */ +#define TD_NC 0x00000400 /* No Carrier */ +#define TD_LC 0x00000200 /* Late Collision */ +#define TD_EC 0x00000100 /* Excessive Collisions */ +#define TD_HF 0x00000080 /* Heartbeat Fail */ +#define TD_CC 0x00000078 /* Collision Counter */ +#define TD_LF 0x00000004 /* Link Fail */ +#define TD_UF 0x00000002 /* Underflow Error */ +#define TD_DE 0x00000001 /* Deferred */ + +#define TD_IC 0x80000000 /* Interrupt On Completion */ +#define TD_LS 0x40000000 /* Last Segment */ +#define TD_FS 0x20000000 /* First Segment */ +#define TD_FT1 0x10000000 /* Filtering Type */ +#define TD_SET 0x08000000 /* Setup Packet */ +#define TD_AC 0x04000000 /* Add CRC Disable */ +#define TD_TER 0x02000000 /* Transmit End Of Ring */ +#define TD_TCH 0x01000000 /* Second Address Chained */ +#define TD_DPD 0x00800000 /* Disabled Padding */ +#define TD_FT0 0x00400000 /* Filtering Type */ +#define TD_TBS2 0x003ff800 /* Buffer 2 Size */ +#define TD_TBS1 0x000007ff /* Buffer 1 Size */ + +#define PERFECT_F 0x00000000 +#define HASH_F TD_FT0 +#define INVERSE_F TD_FT1 +#define HASH_O_F (TD_FT1 | TD_F0) + +/* +** Media / mode state machine definitions +** User selectable: +*/ +#define TP 0x0040 /* 10Base-T (now equiv to _10Mb) */ +#define TP_NW 0x0002 /* 10Base-T with Nway */ +#define BNC 0x0004 /* Thinwire */ +#define AUI 0x0008 /* Thickwire */ +#define BNC_AUI 0x0010 /* BNC/AUI on DC21040 indistinguishable */ +#define _10Mb 0x0040 /* 10Mb/s Ethernet */ +#define _100Mb 0x0080 /* 100Mb/s Ethernet */ +#define AUTO 0x4000 /* Auto sense the media or speed */ + +/* +** Internal states +*/ +#define NC 0x0000 /* No Connection */ +#define ANS 0x0020 /* Intermediate AutoNegotiation State */ +#define SPD_DET 0x0100 /* Parallel speed detection */ +#define INIT 0x0200 /* Initial state */ +#define EXT_SIA 0x0400 /* External SIA for motherboard chip */ +#define ANS_SUSPECT 0x0802 /* Suspect the ANS (TP) port is down */ +#define TP_SUSPECT 0x0803 /* Suspect the TP port is down */ +#define BNC_AUI_SUSPECT 0x0804 /* Suspect the BNC or AUI port is down */ +#define EXT_SIA_SUSPECT 0x0805 /* Suspect the EXT SIA port is down */ +#define BNC_SUSPECT 0x0806 /* Suspect the BNC port is down */ +#define AUI_SUSPECT 0x0807 /* Suspect the AUI port is down */ +#define MII 0x1000 /* MII on the 21143 */ + +#define TIMER_CB 0x80000000 /* Timer callback detection */ + +/* +** DE4X5 DEBUG Options +*/ +#define DEBUG_NONE 0x0000 /* No DEBUG messages */ +#define DEBUG_VERSION 0x0001 /* Print version message */ +#define DEBUG_MEDIA 0x0002 /* Print media messages */ +#define DEBUG_TX 0x0004 /* Print TX (queue_pkt) messages */ +#define DEBUG_RX 0x0008 /* Print RX (de4x5_rx) messages */ +#define DEBUG_SROM 0x0010 /* Print SROM messages */ +#define DEBUG_MII 0x0020 /* Print MII messages */ +#define DEBUG_OPEN 0x0040 /* Print de4x5_open() messages */ +#define DEBUG_CLOSE 0x0080 /* Print de4x5_close() messages */ +#define DEBUG_PCICFG 0x0100 +#define DEBUG_ALL 0x01ff + +/* +** Miscellaneous +*/ +#define PCI 0 +#define EISA 1 + +#define HASH_TABLE_LEN 512 /* Bits */ +#define HASH_BITS 0x01ff /* 9 LS bits */ + +#define SETUP_FRAME_LEN 192 /* Bytes */ +#define IMPERF_PA_OFFSET 156 /* Bytes */ + +#define POLL_DEMAND 1 + +#define LOST_MEDIA_THRESHOLD 3 + +#define MASK_INTERRUPTS 1 +#define UNMASK_INTERRUPTS 0 + +#define DE4X5_STRLEN 8 + +#define DE4X5_INIT 0 /* Initialisation time */ +#define DE4X5_RUN 1 /* Run time */ + +#define DE4X5_SAVE_STATE 0 +#define DE4X5_RESTORE_STATE 1 + +/* +** Address Filtering Modes +*/ +#define PERFECT 0 /* 16 perfect physical addresses */ +#define HASH_PERF 1 /* 1 perfect, 512 multicast addresses */ +#define PERFECT_REJ 2 /* Reject 16 perfect physical addresses */ +#define ALL_HASH 3 /* Hashes all physical & multicast addrs */ + +#define ALL 0 /* Clear out all the setup frame */ +#define PHYS_ADDR_ONLY 1 /* Update the physical address only */ + +/* +** Booleans +*/ +#define NO 0 +#define FALSE 0 + +#define YES ~0 +#define TRUE ~0 + +/* +** Adapter state +*/ +#define INITIALISED 0 /* After h/w initialised and mem alloc'd */ +#define CLOSED 1 /* Ready for opening */ +#define OPEN 2 /* Running */ + +/* +** Various wait times +*/ +#define PDET_LINK_WAIT 1200 /* msecs to wait for link detect bits */ +#define ANS_FINISH_WAIT 1000 /* msecs to wait for link detect bits */ + +/* +** IEEE OUIs for various PHY vendor/chip combos - Reg 2 values only. Since +** the vendors seem split 50-50 on how to calculate the OUI register values +** anyway, just reading Reg2 seems reasonable for now [see de4x5_get_oui()]. +*/ +#define NATIONAL_TX 0x2000 +#define BROADCOM_T4 0x03e0 +#define SEEQ_T4 0x0016 +#define CYPRESS_T4 0x0014 + +/* +** Speed Selection stuff +*/ +#define SET_10Mb {\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ + omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ + if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ + mii_wr(MII_CR_10|(lp->fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ + }\ + omr |= ((lp->fdx ? OMR_FDX : 0) | OMR_TTM);\ + outl(omr, DE4X5_OMR);\ + if (!lp->useSROM) lp->cache.gep = 0;\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | (lp->infoblock_csr6 & ~(OMR_SCR | OMR_HBD)), DE4X5_OMR);\ + } else {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | OMR_SDP | OMR_TTM, DE4X5_OMR);\ + lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD);\ + gep_wr(lp->cache.gep, dev);\ + }\ +} + +#define SET_100Mb {\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ + int fdx=0;\ + if (lp->phy[lp->active].id == NATIONAL_TX) {\ + mii_wr(mii_rd(0x18, lp->phy[lp->active].addr, DE4X5_MII) & ~0x2000,\ + 0x18, lp->phy[lp->active].addr, DE4X5_MII);\ + }\ + omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ + sr = mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);\ + if (!(sr & MII_ANA_T4AM) && lp->fdx) fdx=1;\ + if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ + mii_wr(MII_CR_100|(fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ + }\ + if (fdx) omr |= OMR_FDX;\ + outl(omr, DE4X5_OMR);\ + if (!lp->useSROM) lp->cache.gep = 0;\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | lp->infoblock_csr6, DE4X5_OMR);\ + } else {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ + lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD) | GEP_MODE;\ + gep_wr(lp->cache.gep, dev);\ + }\ +} + +/* FIX ME so I don't jam 10Mb networks */ +#define SET_100Mb_PDET {\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ + mii_wr(MII_CR_100|MII_CR_ASSE, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ + omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + outl(omr, DE4X5_OMR);\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + outl(omr, DE4X5_OMR);\ + } else {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS, DE4X5_OMR);\ + lp->cache.gep = (GEP_FDXD | GEP_MODE);\ + gep_wr(lp->cache.gep, dev);\ + }\ +} + +/* +** Include the IOCTL stuff +*/ +#include + +#define DE4X5IOCTL SIOCDEVPRIVATE + +struct de4x5_ioctl { + unsigned short cmd; /* Command to run */ + unsigned short len; /* Length of the data buffer */ + unsigned char *data; /* Pointer to the data buffer */ +}; + +/* +** Recognised commands for the driver +*/ +#define DE4X5_GET_HWADDR 0x01 /* Get the hardware address */ +#define DE4X5_SET_HWADDR 0x02 /* Set the hardware address */ +#define DE4X5_SET_PROM 0x03 /* Set Promiscuous Mode */ +#define DE4X5_CLR_PROM 0x04 /* Clear Promiscuous Mode */ +#define DE4X5_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */ +#define DE4X5_GET_MCA 0x06 /* Get a multicast address */ +#define DE4X5_SET_MCA 0x07 /* Set a multicast address */ +#define DE4X5_CLR_MCA 0x08 /* Clear a multicast address */ +#define DE4X5_MCA_EN 0x09 /* Enable a multicast address group */ +#define DE4X5_GET_STATS 0x0a /* Get the driver statistics */ +#define DE4X5_CLR_STATS 0x0b /* Zero out the driver statistics */ +#define DE4X5_GET_OMR 0x0c /* Get the OMR Register contents */ +#define DE4X5_SET_OMR 0x0d /* Set the OMR Register contents */ +#define DE4X5_GET_REG 0x0e /* Get the DE4X5 Registers */ + +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) + +#define MOTO_SROM_BUG ((lp->active == 8) && (((le32_to_cpu(get_unaligned(((s32 *)dev->dev_addr))))&0x00ffffff)==0x3e0008)) diff -Nru a/drivers/net/tulip/dmfe.c b/drivers/net/tulip/dmfe.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/tulip/dmfe.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,2070 @@ +/* + A Davicom DM9102/DM9102A/DM9102A+DM9801/DM9102A+DM9802 NIC fast + ethernet driver for Linux. + Copyright (C) 1997 Sten Wang + + 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. + + DAVICOM Web-Site: www.davicom.com.tw + + Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw + Maintainer: Tobias Ringstrom + + (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved. + + Marcelo Tosatti : + Made it compile in 2.3 (device to net_device) + + Alan Cox : + Cleaned up for kernel merge. + Removed the back compatibility support + Reformatted, fixing spelling etc as I went + Removed IRQ 0-15 assumption + + Jeff Garzik : + Updated to use new PCI driver API. + Resource usage cleanups. + Report driver version to user. + + Tobias Ringstrom : + Cleaned up and added SMP safety. Thanks go to Jeff Garzik, + Andrew Morton and Frank Davis for the SMP safety fixes. + + Vojtech Pavlik : + Cleaned up pointer arithmetics. + Fixed a lot of 64bit issues. + Cleaned up printk()s a bit. + Fixed some obvious big endian problems. + + Tobias Ringstrom : + Use time_after for jiffies calculation. Added ethtool + support. Updated PCI resource allocation. Do not + forget to unmap PCI mapped skbs. + + TODO + + Implement pci_driver::suspend() and pci_driver::resume() + power management methods. + + Check on 64 bit boxes. + Check and fix on big endian boxes. + + Test and make sure PCI latency is now correct for all cases. +*/ + +#define DRV_NAME "dmfe" +#define DRV_VERSION "1.36.4" +#define DRV_RELDATE "2002-01-17" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +/* Board/System/Debug information/definition ---------------- */ +#define PCI_DM9132_ID 0x91321282 /* Davicom DM9132 ID */ +#define PCI_DM9102_ID 0x91021282 /* Davicom DM9102 ID */ +#define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */ +#define PCI_DM9009_ID 0x90091282 /* Davicom DM9009 ID */ + +#define DM9102_IO_SIZE 0x80 +#define DM9102A_IO_SIZE 0x100 +#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */ +#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */ +#define RX_DESC_CNT 0x20 /* Allocated Rx descriptors */ +#define TX_FREE_DESC_CNT (TX_DESC_CNT - 2) /* Max TX packet count */ +#define TX_WAKE_DESC_CNT (TX_DESC_CNT - 3) /* TX wakeup count */ +#define DESC_ALL_CNT (TX_DESC_CNT + RX_DESC_CNT) +#define TX_BUF_ALLOC 0x600 +#define RX_ALLOC_SIZE 0x620 +#define DM910X_RESET 1 +#define CR0_DEFAULT 0x00E00000 /* TX & RX burst mode */ +#define CR6_DEFAULT 0x00080000 /* HD */ +#define CR7_DEFAULT 0x180c1 +#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */ +#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */ +#define MAX_PACKET_SIZE 1514 +#define DMFE_MAX_MULTICAST 14 +#define RX_COPY_SIZE 100 +#define MAX_CHECK_PACKET 0x8000 +#define DM9801_NOISE_FLOOR 8 +#define DM9802_NOISE_FLOOR 5 + +#define DMFE_10MHF 0 +#define DMFE_100MHF 1 +#define DMFE_10MFD 4 +#define DMFE_100MFD 5 +#define DMFE_AUTO 8 +#define DMFE_1M_HPNA 0x10 + +#define DMFE_TXTH_72 0x400000 /* TX TH 72 byte */ +#define DMFE_TXTH_96 0x404000 /* TX TH 96 byte */ +#define DMFE_TXTH_128 0x0000 /* TX TH 128 byte */ +#define DMFE_TXTH_256 0x4000 /* TX TH 256 byte */ +#define DMFE_TXTH_512 0x8000 /* TX TH 512 byte */ +#define DMFE_TXTH_1K 0xC000 /* TX TH 1K byte */ + +#define DMFE_TIMER_WUT (jiffies + HZ * 1)/* timer wakeup time : 1 second */ +#define DMFE_TX_TIMEOUT ((3*HZ)/2) /* tx packet time-out time 1.5 s" */ +#define DMFE_TX_KICK (HZ/2) /* tx packet Kick-out time 0.5 s" */ + +#define DMFE_DBUG(dbug_now, msg, value) if (dmfe_debug || (dbug_now)) printk(KERN_ERR DRV_NAME ": %s %lx\n", (msg), (long) (value)) + +#define SHOW_MEDIA_TYPE(mode) printk(KERN_ERR DRV_NAME ": Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half"); + + +/* CR9 definition: SROM/MII */ +#define CR9_SROM_READ 0x4800 +#define CR9_SRCS 0x1 +#define CR9_SRCLK 0x2 +#define CR9_CRDOUT 0x8 +#define SROM_DATA_0 0x0 +#define SROM_DATA_1 0x4 +#define PHY_DATA_1 0x20000 +#define PHY_DATA_0 0x00000 +#define MDCLKH 0x10000 + +#define PHY_POWER_DOWN 0x800 + +#define SROM_V41_CODE 0x14 + +#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5); + +#define __CHK_IO_SIZE(pci_id, dev_rev) ( ((pci_id)==PCI_DM9132_ID) || ((dev_rev) >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE +#define CHK_IO_SIZE(pci_dev, dev_rev) __CHK_IO_SIZE(((pci_dev)->device << 16) | (pci_dev)->vendor, dev_rev) + +/* Sten Check */ +#define DEVICE net_device + +/* Structure/enum declaration ------------------------------- */ +struct tx_desc { + u32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */ + char *tx_buf_ptr; /* Data for us */ + struct tx_desc *next_tx_desc; +} __attribute__(( aligned(32) )); + +struct rx_desc { + u32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */ + struct sk_buff *rx_skb_ptr; /* Data for us */ + struct rx_desc *next_rx_desc; +} __attribute__(( aligned(32) )); + +struct dmfe_board_info { + u32 chip_id; /* Chip vendor/Device ID */ + u32 chip_revision; /* Chip revision */ + struct DEVICE *next_dev; /* next device */ + struct pci_dev *pdev; /* PCI device */ + spinlock_t lock; + + long ioaddr; /* I/O base address */ + u32 cr0_data; + u32 cr5_data; + u32 cr6_data; + u32 cr7_data; + u32 cr15_data; + + /* pointer for memory physical address */ + dma_addr_t buf_pool_dma_ptr; /* Tx buffer pool memory */ + dma_addr_t buf_pool_dma_start; /* Tx buffer pool align dword */ + dma_addr_t desc_pool_dma_ptr; /* descriptor pool memory */ + dma_addr_t first_tx_desc_dma; + dma_addr_t first_rx_desc_dma; + + /* descriptor pointer */ + unsigned char *buf_pool_ptr; /* Tx buffer pool memory */ + unsigned char *buf_pool_start; /* Tx buffer pool align dword */ + unsigned char *desc_pool_ptr; /* descriptor pool memory */ + struct tx_desc *first_tx_desc; + struct tx_desc *tx_insert_ptr; + struct tx_desc *tx_remove_ptr; + struct rx_desc *first_rx_desc; + struct rx_desc *rx_insert_ptr; + struct rx_desc *rx_ready_ptr; /* packet come pointer */ + unsigned long tx_packet_cnt; /* transmitted packet count */ + unsigned long tx_queue_cnt; /* wait to send packet count */ + unsigned long rx_avail_cnt; /* available rx descriptor count */ + unsigned long interval_rx_cnt; /* rx packet count a callback time */ + + u16 HPNA_command; /* For HPNA register 16 */ + u16 HPNA_timer; /* For HPNA remote device check */ + u16 dbug_cnt; + u16 NIC_capability; /* NIC media capability */ + u16 PHY_reg4; /* Saved Phyxcer register 4 value */ + + u8 HPNA_present; /* 0:none, 1:DM9801, 2:DM9802 */ + u8 chip_type; /* Keep DM9102A chip type */ + u8 media_mode; /* user specify media mode */ + u8 op_mode; /* real work media mode */ + u8 phy_addr; + u8 link_failed; /* Ever link failed */ + u8 wait_reset; /* Hardware failed, need to reset */ + u8 dm910x_chk_mode; /* Operating mode check */ + u8 first_in_callback; /* Flag to record state */ + struct timer_list timer; + + /* System defined statistic counter */ + struct net_device_stats stats; + + /* Driver defined statistic counter */ + unsigned long tx_fifo_underrun; + unsigned long tx_loss_carrier; + unsigned long tx_no_carrier; + unsigned long tx_late_collision; + unsigned long tx_excessive_collision; + unsigned long tx_jabber_timeout; + unsigned long reset_count; + unsigned long reset_cr8; + unsigned long reset_fatal; + unsigned long reset_TXtimeout; + + /* NIC SROM data */ + unsigned char srom[128]; +}; + +enum dmfe_offsets { + DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20, + DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48, + DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70, + DCR15 = 0x78 +}; + +enum dmfe_CR6_bits { + CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, + CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000, + CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000 +}; + +/* Global variable declaration ----------------------------- */ +static int __devinitdata printed_version; +static char version[] __devinitdata = + KERN_INFO DRV_NAME ": Davicom DM9xxx net driver, version " + DRV_VERSION " (" DRV_RELDATE ")\n"; + +static int dmfe_debug; +static unsigned char dmfe_media_mode = DMFE_AUTO; +static u32 dmfe_cr6_user_set; + +/* For module input parameter */ +static int debug; +static u32 cr6set; +static unsigned char mode = 8; +static u8 chkmode = 1; +static u8 HPNA_mode; /* Default: Low Power/High Speed */ +static u8 HPNA_rx_cmd; /* Default: Disable Rx remote command */ +static u8 HPNA_tx_cmd; /* Default: Don't issue remote command */ +static u8 HPNA_NoiseFloor; /* Default: HPNA NoiseFloor */ +static u8 SF_mode; /* Special Function: 1:VLAN, 2:RX Flow Control + 4: TX pause packet */ + + +/* function declaration ------------------------------------- */ +static int dmfe_open(struct DEVICE *); +static int dmfe_start_xmit(struct sk_buff *, struct DEVICE *); +static int dmfe_stop(struct DEVICE *); +static struct net_device_stats * dmfe_get_stats(struct DEVICE *); +static void dmfe_set_filter_mode(struct DEVICE *); +static int dmfe_do_ioctl(struct DEVICE *, struct ifreq *, int); +static u16 read_srom_word(long ,int); +static void dmfe_interrupt(int , void *, struct pt_regs *); +static void dmfe_descriptor_init(struct dmfe_board_info *, unsigned long); +static void allocate_rx_buffer(struct dmfe_board_info *); +static void update_cr6(u32, unsigned long); +static void send_filter_frame(struct DEVICE * ,int); +static void dm9132_id_table(struct DEVICE * ,int); +static u16 phy_read(unsigned long, u8, u8, u32); +static void phy_write(unsigned long, u8, u8, u16, u32); +static void phy_write_1bit(unsigned long, u32); +static u16 phy_read_1bit(unsigned long); +static u8 dmfe_sense_speed(struct dmfe_board_info *); +static void dmfe_process_mode(struct dmfe_board_info *); +static void dmfe_timer(unsigned long); +static void dmfe_rx_packet(struct DEVICE *, struct dmfe_board_info *); +static void dmfe_free_tx_pkt(struct DEVICE *, struct dmfe_board_info *); +static void dmfe_reuse_skb(struct dmfe_board_info *, struct sk_buff *); +static void dmfe_dynamic_reset(struct DEVICE *); +static void dmfe_free_rxbuffer(struct dmfe_board_info *); +static void dmfe_init_dm910x(struct DEVICE *); +static inline u32 cal_CRC(unsigned char *, unsigned int, u8); +static void dmfe_parse_srom(struct dmfe_board_info *); +static void dmfe_program_DM9801(struct dmfe_board_info *, int); +static void dmfe_program_DM9802(struct dmfe_board_info *); +static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info * ); +static void dmfe_set_phyxcer(struct dmfe_board_info *); + +/* DM910X network baord routine ---------------------------- */ + +/* + * Search DM910X board ,allocate space and register it + */ + +static int __devinit dmfe_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct dmfe_board_info *db; /* board information structure */ + struct net_device *dev; + u32 dev_rev, pci_pmr; + int i, err; + + DMFE_DBUG(0, "dmfe_init_one()", 0); + + if (!printed_version++) + printk(version); + + /* Init network device */ + dev = alloc_etherdev(sizeof(*db)); + if (dev == NULL) + return -ENOMEM; + SET_MODULE_OWNER(dev); + + if (pci_set_dma_mask(pdev, 0xffffffff)) { + printk(KERN_WARNING DRV_NAME ": 32-bit PCI DMA not available.\n"); + err = -ENODEV; + goto err_out_free; + } + + /* Enable Master/IO access, Disable memory access */ + err = pci_enable_device(pdev); + if (err) + goto err_out_free; + + if (!pci_resource_start(pdev, 0)) { + printk(KERN_ERR DRV_NAME ": I/O base is zero\n"); + err = -ENODEV; + goto err_out_disable; + } + + /* Read Chip revision */ + pci_read_config_dword(pdev, PCI_REVISION_ID, &dev_rev); + + if (pci_resource_len(pdev, 0) < (CHK_IO_SIZE(pdev, dev_rev)) ) { + printk(KERN_ERR DRV_NAME ": Allocated I/O size too small\n"); + err = -ENODEV; + goto err_out_disable; + } + +#if 0 /* pci_{enable_device,set_master} sets minimum latency for us now */ + + /* Set Latency Timer 80h */ + /* FIXME: setting values > 32 breaks some SiS 559x stuff. + Need a PCI quirk.. */ + + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x80); +#endif + + if (pci_request_regions(pdev, DRV_NAME)) { + printk(KERN_ERR DRV_NAME ": Failed to request PCI regions\n"); + err = -ENODEV; + goto err_out_disable; + } + + /* Init system & device */ + db = dev->priv; + + /* Allocate Tx/Rx descriptor memory */ + db->desc_pool_ptr = pci_alloc_consistent(pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, &db->desc_pool_dma_ptr); + db->buf_pool_ptr = pci_alloc_consistent(pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, &db->buf_pool_dma_ptr); + + db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr; + db->first_tx_desc_dma = db->desc_pool_dma_ptr; + db->buf_pool_start = db->buf_pool_ptr; + db->buf_pool_dma_start = db->buf_pool_dma_ptr; + + db->chip_id = ent->driver_data; + db->ioaddr = pci_resource_start(pdev, 0); + db->chip_revision = dev_rev; + + db->pdev = pdev; + + dev->base_addr = db->ioaddr; + dev->irq = pdev->irq; + pci_set_drvdata(pdev, dev); + dev->open = &dmfe_open; + dev->hard_start_xmit = &dmfe_start_xmit; + dev->stop = &dmfe_stop; + dev->get_stats = &dmfe_get_stats; + dev->set_multicast_list = &dmfe_set_filter_mode; + dev->do_ioctl = &dmfe_do_ioctl; + spin_lock_init(&db->lock); + + pci_read_config_dword(pdev, 0x50, &pci_pmr); + pci_pmr &= 0x70000; + if ( (pci_pmr == 0x10000) && (dev_rev == 0x02000031) ) + db->chip_type = 1; /* DM9102A E3 */ + else + db->chip_type = 0; + + /* read 64 word srom data */ + for (i = 0; i < 64; i++) + ((u16 *) db->srom)[i] = cpu_to_le16(read_srom_word(db->ioaddr, i)); + + /* Set Node address */ + for (i = 0; i < 6; i++) + dev->dev_addr[i] = db->srom[20 + i]; + + err = register_netdev (dev); + if (err) + goto err_out_res; + + printk(KERN_INFO "%s: Davicom DM%04lx at pci%s,", + dev->name, + ent->driver_data >> 16, + pdev->slot_name); + for (i = 0; i < 6; i++) + printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]); + printk(", irq %d.\n", dev->irq); + + pci_set_master(pdev); + + return 0; + +err_out_res: + pci_release_regions(pdev); +err_out_disable: + pci_disable_device(pdev); +err_out_free: + pci_set_drvdata(pdev, NULL); + kfree(dev); + + return err; +} + + +static void __exit dmfe_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct dmfe_board_info *db = dev->priv; + + DMFE_DBUG(0, "dmfe_remove_one()", 0); + + if (dev) { + pci_free_consistent(db->pdev, sizeof(struct tx_desc) * + DESC_ALL_CNT + 0x20, db->desc_pool_ptr, + db->desc_pool_dma_ptr); + pci_free_consistent(db->pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, + db->buf_pool_ptr, db->buf_pool_dma_ptr); + unregister_netdev(dev); + pci_release_regions(pdev); + kfree(dev); /* free board information */ + pci_set_drvdata(pdev, NULL); + } + + DMFE_DBUG(0, "dmfe_remove_one() exit", 0); +} + + +/* + * Open the interface. + * The interface is opened whenever "ifconfig" actives it. + */ + +static int dmfe_open(struct DEVICE *dev) +{ + int ret; + struct dmfe_board_info *db = dev->priv; + + DMFE_DBUG(0, "dmfe_open", 0); + + ret = request_irq(dev->irq, &dmfe_interrupt, SA_SHIRQ, dev->name, dev); + if (ret) + return ret; + + /* system variable init */ + db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set; + db->tx_packet_cnt = 0; + db->tx_queue_cnt = 0; + db->rx_avail_cnt = 0; + db->link_failed = 1; + db->wait_reset = 0; + + db->first_in_callback = 0; + db->NIC_capability = 0xf; /* All capability*/ + db->PHY_reg4 = 0x1e0; + + /* CR6 operation mode decision */ + if ( !chkmode || (db->chip_id == PCI_DM9132_ID) || + (db->chip_revision >= 0x02000030) ) { + db->cr6_data |= DMFE_TXTH_256; + db->cr0_data = CR0_DEFAULT; + db->dm910x_chk_mode=4; /* Enter the normal mode */ + } else { + db->cr6_data |= CR6_SFT; /* Store & Forward mode */ + db->cr0_data = 0; + db->dm910x_chk_mode = 1; /* Enter the check mode */ + } + + /* Initilize DM910X board */ + dmfe_init_dm910x(dev); + + /* Active System Interface */ + netif_wake_queue(dev); + + /* set and active a timer process */ + init_timer(&db->timer); + db->timer.expires = DMFE_TIMER_WUT + HZ * 2; + db->timer.data = (unsigned long)dev; + db->timer.function = &dmfe_timer; + add_timer(&db->timer); + + return 0; +} + + +/* Initilize DM910X board + * Reset DM910X board + * Initilize TX/Rx descriptor chain structure + * Send the set-up frame + * Enable Tx/Rx machine + */ + +static void dmfe_init_dm910x(struct DEVICE *dev) +{ + struct dmfe_board_info *db = dev->priv; + unsigned long ioaddr = db->ioaddr; + + DMFE_DBUG(0, "dmfe_init_dm910x()", 0); + + /* Reset DM910x MAC controller */ + outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */ + udelay(100); + outl(db->cr0_data, ioaddr + DCR0); + udelay(5); + + /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */ + db->phy_addr = 1; + + /* Parser SROM and media mode */ + dmfe_parse_srom(db); + db->media_mode = dmfe_media_mode; + + /* RESET Phyxcer Chip by GPR port bit 7 */ + outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */ + if (db->chip_id == PCI_DM9009_ID) { + outl(0x80, ioaddr + DCR12); /* Issue RESET signal */ + mdelay(300); /* Delay 300 ms */ + } + outl(0x0, ioaddr + DCR12); /* Clear RESET signal */ + + /* Process Phyxcer Media Mode */ + if ( !(db->media_mode & 0x10) ) /* Force 1M mode */ + dmfe_set_phyxcer(db); + + /* Media Mode Process */ + if ( !(db->media_mode & DMFE_AUTO) ) + db->op_mode = db->media_mode; /* Force Mode */ + + /* Initiliaze Transmit/Receive decriptor and CR3/4 */ + dmfe_descriptor_init(db, ioaddr); + + /* Init CR6 to program DM910x operation */ + update_cr6(db->cr6_data, ioaddr); + + /* Send setup frame */ + if (db->chip_id == PCI_DM9132_ID) + dm9132_id_table(dev, dev->mc_count); /* DM9132 */ + else + send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */ + + /* Init CR7, interrupt active bit */ + db->cr7_data = CR7_DEFAULT; + outl(db->cr7_data, ioaddr + DCR7); + + /* Init CR15, Tx jabber and Rx watchdog timer */ + outl(db->cr15_data, ioaddr + DCR15); + + /* Enable DM910X Tx/Rx function */ + db->cr6_data |= CR6_RXSC | CR6_TXSC | 0x40000; + update_cr6(db->cr6_data, ioaddr); +} + + +/* + * Hardware start transmission. + * Send a packet to media from the upper layer. + */ + +static int dmfe_start_xmit(struct sk_buff *skb, struct DEVICE *dev) +{ + struct dmfe_board_info *db = dev->priv; + struct tx_desc *txptr; + unsigned long flags; + + DMFE_DBUG(0, "dmfe_start_xmit", 0); + + /* Resource flag check */ + netif_stop_queue(dev); + + /* Too large packet check */ + if (skb->len > MAX_PACKET_SIZE) { + printk(KERN_ERR DRV_NAME ": big packet = %d\n", (u16)skb->len); + dev_kfree_skb(skb); + return 0; + } + + spin_lock_irqsave(&db->lock, flags); + + /* No Tx resource check, it never happen nromally */ + if (db->tx_queue_cnt >= TX_FREE_DESC_CNT) { + spin_unlock_irqrestore(&db->lock, flags); + printk(KERN_ERR DRV_NAME ": No Tx resource %ld\n", db->tx_queue_cnt); + return 1; + } + + /* Disable NIC interrupt */ + outl(0, dev->base_addr + DCR7); + + /* transmit this packet */ + txptr = db->tx_insert_ptr; + memcpy(txptr->tx_buf_ptr, skb->data, skb->len); + txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len); + + /* Point to next transmit free descriptor */ + db->tx_insert_ptr = txptr->next_tx_desc; + + /* Transmit Packet Process */ + if ( (!db->tx_queue_cnt) && (db->tx_packet_cnt < TX_MAX_SEND_CNT) ) { + txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ + db->tx_packet_cnt++; /* Ready to send */ + outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ + dev->trans_start = jiffies; /* saved time stamp */ + } else { + db->tx_queue_cnt++; /* queue TX packet */ + outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ + } + + /* Tx resource check */ + if ( db->tx_queue_cnt < TX_FREE_DESC_CNT ) + netif_wake_queue(dev); + + /* free this SKB */ + dev_kfree_skb(skb); + + /* Restore CR7 to enable interrupt */ + spin_unlock_irqrestore(&db->lock, flags); + outl(db->cr7_data, dev->base_addr + DCR7); + + return 0; +} + + +/* + * Stop the interface. + * The interface is stopped when it is brought. + */ + +static int dmfe_stop(struct DEVICE *dev) +{ + struct dmfe_board_info *db = dev->priv; + unsigned long ioaddr = dev->base_addr; + + DMFE_DBUG(0, "dmfe_stop", 0); + + /* disable system */ + netif_stop_queue(dev); + + /* deleted timer */ + del_timer_sync(&db->timer); + + /* Reset & stop DM910X board */ + outl(DM910X_RESET, ioaddr + DCR0); + udelay(5); + phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); + + /* free interrupt */ + free_irq(dev->irq, dev); + + /* free allocated rx buffer */ + dmfe_free_rxbuffer(db); + +#if 0 + /* show statistic counter */ + printk(DRV_NAME ": FU:%lx EC:%lx LC:%lx NC:%lx LOC:%lx TXJT:%lx RESET:%lx RCR8:%lx FAL:%lx TT:%lx\n", + db->tx_fifo_underrun, db->tx_excessive_collision, + db->tx_late_collision, db->tx_no_carrier, db->tx_loss_carrier, + db->tx_jabber_timeout, db->reset_count, db->reset_cr8, + db->reset_fatal, db->reset_TXtimeout); +#endif + + return 0; +} + + +/* + * DM9102 insterrupt handler + * receive the packet to upper layer, free the transmitted packet + */ + +static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct DEVICE *dev = dev_id; + struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv; + unsigned long ioaddr = dev->base_addr; + unsigned long flags; + + DMFE_DBUG(0, "dmfe_interrupt()", 0); + + if (!dev) { + DMFE_DBUG(1, "dmfe_interrupt() without DEVICE arg", 0); + return; + } + + spin_lock_irqsave(&db->lock, flags); + + /* Got DM910X status */ + db->cr5_data = inl(ioaddr + DCR5); + outl(db->cr5_data, ioaddr + DCR5); + if ( !(db->cr5_data & 0xc1) ) { + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + /* Disable all interrupt in CR7 to solve the interrupt edge problem */ + outl(0, ioaddr + DCR7); + + /* Check system status */ + if (db->cr5_data & 0x2000) { + /* system bus error happen */ + DMFE_DBUG(1, "System bus error happen. CR5=", db->cr5_data); + db->reset_fatal++; + db->wait_reset = 1; /* Need to RESET */ + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + /* Received the coming packet */ + if ( (db->cr5_data & 0x40) && db->rx_avail_cnt ) + dmfe_rx_packet(dev, db); + + /* reallocate rx descriptor buffer */ + if (db->rx_avail_cntcr5_data & 0x01) + dmfe_free_tx_pkt(dev, db); + + /* Mode Check */ + if (db->dm910x_chk_mode & 0x2) { + db->dm910x_chk_mode = 0x4; + db->cr6_data |= 0x100; + update_cr6(db->cr6_data, db->ioaddr); + } + + /* Restore CR7 to enable interrupt mask */ + outl(db->cr7_data, ioaddr + DCR7); + + spin_unlock_irqrestore(&db->lock, flags); +} + + +/* + * Free TX resource after TX complete + */ + +static void dmfe_free_tx_pkt(struct DEVICE *dev, struct dmfe_board_info * db) +{ + struct tx_desc *txptr; + unsigned long ioaddr = dev->base_addr; + u32 tdes0; + + txptr = db->tx_remove_ptr; + while(db->tx_packet_cnt) { + tdes0 = le32_to_cpu(txptr->tdes0); + /* printk(DRV_NAME ": tdes0=%x\n", tdes0); */ + if (tdes0 & 0x80000000) + break; + + /* A packet sent completed */ + db->tx_packet_cnt--; + db->stats.tx_packets++; + + /* Transmit statistic counter */ + if ( tdes0 != 0x7fffffff ) { + /* printk(DRV_NAME ": tdes0=%x\n", tdes0); */ + db->stats.collisions += (tdes0 >> 3) & 0xf; + db->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff; + if (tdes0 & TDES0_ERR_MASK) { + db->stats.tx_errors++; + + if (tdes0 & 0x0002) { /* UnderRun */ + db->tx_fifo_underrun++; + if ( !(db->cr6_data & CR6_SFT) ) { + db->cr6_data = db->cr6_data | CR6_SFT; + update_cr6(db->cr6_data, db->ioaddr); + } + } + if (tdes0 & 0x0100) + db->tx_excessive_collision++; + if (tdes0 & 0x0200) + db->tx_late_collision++; + if (tdes0 & 0x0400) + db->tx_no_carrier++; + if (tdes0 & 0x0800) + db->tx_loss_carrier++; + if (tdes0 & 0x4000) + db->tx_jabber_timeout++; + } + } + + txptr = txptr->next_tx_desc; + }/* End of while */ + + /* Update TX remove pointer to next */ + db->tx_remove_ptr = txptr; + + /* Send the Tx packet in queue */ + if ( (db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt ) { + txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ + db->tx_packet_cnt++; /* Ready to send */ + db->tx_queue_cnt--; + outl(0x1, ioaddr + DCR1); /* Issue Tx polling */ + dev->trans_start = jiffies; /* saved time stamp */ + } + + /* Resource available check */ + if ( db->tx_queue_cnt < TX_WAKE_DESC_CNT ) + netif_wake_queue(dev); /* Active upper layer, send again */ +} + + +/* + * Receive the come packet and pass to upper layer + */ + +static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db) +{ + struct rx_desc *rxptr; + struct sk_buff *skb; + int rxlen; + u32 rdes0; + + rxptr = db->rx_ready_ptr; + + while(db->rx_avail_cnt) { + rdes0 = le32_to_cpu(rxptr->rdes0); + if (rdes0 & 0x80000000) /* packet owner check */ + break; + + db->rx_avail_cnt--; + db->interval_rx_cnt++; + + pci_unmap_single(db->pdev, le32_to_cpu(rxptr->rdes2), RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE); + if ( (rdes0 & 0x300) != 0x300) { + /* A packet without First/Last flag */ + /* reuse this SKB */ + DMFE_DBUG(0, "Reuse SK buffer, rdes0", rdes0); + dmfe_reuse_skb(db, rxptr->rx_skb_ptr); + } else { + /* A packet with First/Last flag */ + rxlen = ( (rdes0 >> 16) & 0x3fff) - 4; + + /* error summary bit check */ + if (rdes0 & 0x8000) { + /* This is a error packet */ + //printk(DRV_NAME ": rdes0: %lx\n", rdes0); + db->stats.rx_errors++; + if (rdes0 & 1) + db->stats.rx_fifo_errors++; + if (rdes0 & 2) + db->stats.rx_crc_errors++; + if (rdes0 & 0x80) + db->stats.rx_length_errors++; + } + + if ( !(rdes0 & 0x8000) || + ((db->cr6_data & CR6_PM) && (rxlen>6)) ) { + skb = rxptr->rx_skb_ptr; + + /* Received Packet CRC check need or not */ + if ( (db->dm910x_chk_mode & 1) && + (cal_CRC(skb->tail, rxlen, 1) != + (*(u32 *) (skb->tail+rxlen) ))) { /* FIXME (?) */ + /* Found a error received packet */ + dmfe_reuse_skb(db, rxptr->rx_skb_ptr); + db->dm910x_chk_mode = 3; + } else { + /* Good packet, send to upper layer */ + /* Shorst packet used new SKB */ + if ( (rxlen < RX_COPY_SIZE) && + ( (skb = dev_alloc_skb(rxlen + 2) ) + != NULL) ) { + /* size less than COPY_SIZE, allocate a rxlen SKB */ + skb->dev = dev; + skb_reserve(skb, 2); /* 16byte align */ + memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->tail, rxlen); + dmfe_reuse_skb(db, rxptr->rx_skb_ptr); + } else { + skb->dev = dev; + skb_put(skb, rxlen); + } + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + db->stats.rx_packets++; + db->stats.rx_bytes += rxlen; + } + } else { + /* Reuse SKB buffer when the packet is error */ + DMFE_DBUG(0, "Reuse SK buffer, rdes0", rdes0); + dmfe_reuse_skb(db, rxptr->rx_skb_ptr); + } + } + + rxptr = rxptr->next_rx_desc; + } + + db->rx_ready_ptr = rxptr; +} + + +/* + * Get statistics from driver. + */ + +static struct net_device_stats * dmfe_get_stats(struct DEVICE *dev) +{ + struct dmfe_board_info *db = (struct dmfe_board_info *)dev->priv; + + DMFE_DBUG(0, "dmfe_get_stats", 0); + return &db->stats; +} + + +/* + * Set DM910X multicast address + */ + +static void dmfe_set_filter_mode(struct DEVICE * dev) +{ + struct dmfe_board_info *db = dev->priv; + unsigned long flags; + + DMFE_DBUG(0, "dmfe_set_filter_mode()", 0); + spin_lock_irqsave(&db->lock, flags); + + if (dev->flags & IFF_PROMISC) { + DMFE_DBUG(0, "Enable PROM Mode", 0); + db->cr6_data |= CR6_PM | CR6_PBF; + update_cr6(db->cr6_data, db->ioaddr); + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + if (dev->flags & IFF_ALLMULTI || dev->mc_count > DMFE_MAX_MULTICAST) { + DMFE_DBUG(0, "Pass all multicast address", dev->mc_count); + db->cr6_data &= ~(CR6_PM | CR6_PBF); + db->cr6_data |= CR6_PAM; + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + DMFE_DBUG(0, "Set multicast address", dev->mc_count); + if (db->chip_id == PCI_DM9132_ID) + dm9132_id_table(dev, dev->mc_count); /* DM9132 */ + else + send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */ + spin_unlock_irqrestore(&db->lock, flags); +} + + +/* + * Process the ethtool ioctl command + */ + +static int dmfe_ethtool_ioctl(struct net_device *dev, void *useraddr) +{ + struct dmfe_board_info *db = dev->priv; + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + u32 ethcmd; + + if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) + return -EFAULT; + + switch (ethcmd) { + case ETHTOOL_GDRVINFO: + strcpy(info.driver, DRV_NAME); + strcpy(info.version, DRV_VERSION); + if (db->pdev) + strcpy(info.bus_info, db->pdev->slot_name); + else + sprintf(info.bus_info, "EISA 0x%lx %d", + dev->base_addr, dev->irq); + if (copy_to_user(useraddr, &info, sizeof(info))) + return -EFAULT; + return 0; + } + + return -EOPNOTSUPP; +} + + +/* + * Process the upper socket ioctl command + */ + +static int dmfe_do_ioctl(struct DEVICE *dev, struct ifreq *ifr, int cmd) +{ + int retval = -EOPNOTSUPP; + DMFE_DBUG(0, "dmfe_do_ioctl()", 0); + + switch(cmd) { + case SIOCETHTOOL: + return dmfe_ethtool_ioctl(dev, (void*)ifr->ifr_data); + } + + return retval; +} + + +/* + * A periodic timer routine + * Dynamic media sense, allocate Rx buffer... + */ + +static void dmfe_timer(unsigned long data) +{ + u32 tmp_cr8; + unsigned char tmp_cr12; + struct DEVICE *dev = (struct DEVICE *) data; + struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv; + unsigned long flags; + + DMFE_DBUG(0, "dmfe_timer()", 0); + spin_lock_irqsave(&db->lock, flags); + + /* Media mode process when Link OK before enter this route */ + if (db->first_in_callback == 0) { + db->first_in_callback = 1; + if (db->chip_type && (db->chip_id==PCI_DM9102_ID)) { + db->cr6_data &= ~0x40000; + update_cr6(db->cr6_data, db->ioaddr); + phy_write(db->ioaddr, db->phy_addr, 0, 0x1000, db->chip_id); + db->cr6_data |= 0x40000; + update_cr6(db->cr6_data, db->ioaddr); + db->timer.expires = DMFE_TIMER_WUT + HZ * 2; + add_timer(&db->timer); + spin_unlock_irqrestore(&db->lock, flags); + return; + } + } + + + /* Operating Mode Check */ + if ( (db->dm910x_chk_mode & 0x1) && + (db->stats.rx_packets > MAX_CHECK_PACKET) ) + db->dm910x_chk_mode = 0x4; + + /* Dynamic reset DM910X : system error or transmit time-out */ + tmp_cr8 = inl(db->ioaddr + DCR8); + if ( (db->interval_rx_cnt==0) && (tmp_cr8) ) { + db->reset_cr8++; + db->wait_reset = 1; + } + db->interval_rx_cnt = 0; + + /* TX polling kick monitor */ + if ( db->tx_packet_cnt && + time_after(jiffies, dev->trans_start + DMFE_TX_KICK) ) { + outl(0x1, dev->base_addr + DCR1); /* Tx polling again */ + + /* TX Timeout */ + if ( time_after(jiffies, dev->trans_start + DMFE_TX_TIMEOUT) ) { + db->reset_TXtimeout++; + db->wait_reset = 1; + printk(KERN_WARNING "%s: Tx timeout - resetting\n", + dev->name); + } + } + + if (db->wait_reset) { + DMFE_DBUG(0, "Dynamic Reset device", db->tx_packet_cnt); + db->reset_count++; + dmfe_dynamic_reset(dev); + db->first_in_callback = 0; + db->timer.expires = DMFE_TIMER_WUT; + add_timer(&db->timer); + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + /* Link status check, Dynamic media type change */ + if (db->chip_id == PCI_DM9132_ID) + tmp_cr12 = inb(db->ioaddr + DCR9 + 3); /* DM9132 */ + else + tmp_cr12 = inb(db->ioaddr + DCR12); /* DM9102/DM9102A */ + + if ( ((db->chip_id == PCI_DM9102_ID) && + (db->chip_revision == 0x02000030)) || + ((db->chip_id == PCI_DM9132_ID) && + (db->chip_revision == 0x02000010)) ) { + /* DM9102A Chip */ + if (tmp_cr12 & 2) + tmp_cr12 = 0x0; /* Link failed */ + else + tmp_cr12 = 0x3; /* Link OK */ + } + + if ( !(tmp_cr12 & 0x3) && !db->link_failed ) { + /* Link Failed */ + DMFE_DBUG(0, "Link Failed", tmp_cr12); + db->link_failed = 1; + + /* For Force 10/100M Half/Full mode: Enable Auto-Nego mode */ + /* AUTO or force 1M Homerun/Longrun don't need */ + if ( !(db->media_mode & 0x38) ) + phy_write(db->ioaddr, db->phy_addr, 0, 0x1000, db->chip_id); + + /* AUTO mode, if INT phyxcer link failed, select EXT device */ + if (db->media_mode & DMFE_AUTO) { + /* 10/100M link failed, used 1M Home-Net */ + db->cr6_data|=0x00040000; /* bit18=1, MII */ + db->cr6_data&=~0x00000200; /* bit9=0, HD mode */ + update_cr6(db->cr6_data, db->ioaddr); + } + } else + if ((tmp_cr12 & 0x3) && db->link_failed) { + DMFE_DBUG(0, "Link link OK", tmp_cr12); + db->link_failed = 0; + + /* Auto Sense Speed */ + if ( (db->media_mode & DMFE_AUTO) && + dmfe_sense_speed(db) ) + db->link_failed = 1; + dmfe_process_mode(db); + /* SHOW_MEDIA_TYPE(db->op_mode); */ + } + + /* HPNA remote command check */ + if (db->HPNA_command & 0xf00) { + db->HPNA_timer--; + if (!db->HPNA_timer) + dmfe_HPNA_remote_cmd_chk(db); + } + + /* Timer active again */ + db->timer.expires = DMFE_TIMER_WUT; + add_timer(&db->timer); + spin_unlock_irqrestore(&db->lock, flags); +} + + +/* + * Dynamic reset the DM910X board + * Stop DM910X board + * Free Tx/Rx allocated memory + * Reset DM910X board + * Re-initilize DM910X board + */ + +static void dmfe_dynamic_reset(struct DEVICE *dev) +{ + struct dmfe_board_info *db = dev->priv; + + DMFE_DBUG(0, "dmfe_dynamic_reset()", 0); + + /* Sopt MAC controller */ + db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); /* Disable Tx/Rx */ + update_cr6(db->cr6_data, dev->base_addr); + outl(0, dev->base_addr + DCR7); /* Disable Interrupt */ + outl(inl(dev->base_addr + DCR5), dev->base_addr + DCR5); + + /* Disable upper layer interface */ + netif_stop_queue(dev); + + /* Free Rx Allocate buffer */ + dmfe_free_rxbuffer(db); + + /* system variable init */ + db->tx_packet_cnt = 0; + db->tx_queue_cnt = 0; + db->rx_avail_cnt = 0; + db->link_failed = 1; + db->wait_reset = 0; + + /* Re-initilize DM910X board */ + dmfe_init_dm910x(dev); + + /* Restart upper layer interface */ + netif_wake_queue(dev); +} + + +/* + * free all allocated rx buffer + */ + +static void dmfe_free_rxbuffer(struct dmfe_board_info * db) +{ + DMFE_DBUG(0, "dmfe_free_rxbuffer()", 0); + + /* free allocated rx buffer */ + while (db->rx_avail_cnt) { + dev_kfree_skb(db->rx_ready_ptr->rx_skb_ptr); + db->rx_ready_ptr = db->rx_ready_ptr->next_rx_desc; + db->rx_avail_cnt--; + } +} + + +/* + * Reuse the SK buffer + */ + +static void dmfe_reuse_skb(struct dmfe_board_info *db, struct sk_buff * skb) +{ + struct rx_desc *rxptr = db->rx_insert_ptr; + + if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) { + rxptr->rx_skb_ptr = skb; + rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); + wmb(); + rxptr->rdes0 = cpu_to_le32(0x80000000); + db->rx_avail_cnt++; + db->rx_insert_ptr = rxptr->next_rx_desc; + } else + DMFE_DBUG(0, "SK Buffer reuse method error", db->rx_avail_cnt); +} + + +/* + * Initialize transmit/Receive descriptor + * Using Chain structure, and allocate Tx/Rx buffer + */ + +static void dmfe_descriptor_init(struct dmfe_board_info *db, unsigned long ioaddr) +{ + struct tx_desc *tmp_tx; + struct rx_desc *tmp_rx; + unsigned char *tmp_buf; + dma_addr_t tmp_tx_dma, tmp_rx_dma; + dma_addr_t tmp_buf_dma; + int i; + + DMFE_DBUG(0, "dmfe_descriptor_init()", 0); + + /* tx descriptor start pointer */ + db->tx_insert_ptr = db->first_tx_desc; + db->tx_remove_ptr = db->first_tx_desc; + outl(db->first_tx_desc_dma, ioaddr + DCR4); /* TX DESC address */ + + /* rx descriptor start pointer */ + db->first_rx_desc = (void *)db->first_tx_desc + sizeof(struct tx_desc) * TX_DESC_CNT; + db->first_rx_desc_dma = db->first_tx_desc_dma + sizeof(struct tx_desc) * TX_DESC_CNT; + db->rx_insert_ptr = db->first_rx_desc; + db->rx_ready_ptr = db->first_rx_desc; + outl(db->first_rx_desc_dma, ioaddr + DCR3); /* RX DESC address */ + + /* Init Transmit chain */ + tmp_buf = db->buf_pool_start; + tmp_buf_dma = db->buf_pool_dma_start; + tmp_tx_dma = db->first_tx_desc_dma; + for (tmp_tx = db->first_tx_desc, i = 0; i < TX_DESC_CNT; i++, tmp_tx++) { + tmp_tx->tx_buf_ptr = tmp_buf; + tmp_tx->tdes0 = cpu_to_le32(0); + tmp_tx->tdes1 = cpu_to_le32(0x81000000); /* IC, chain */ + tmp_tx->tdes2 = cpu_to_le32(tmp_buf_dma); + tmp_tx_dma += sizeof(struct tx_desc); + tmp_tx->tdes3 = cpu_to_le32(tmp_tx_dma); + tmp_tx->next_tx_desc = tmp_tx + 1; + tmp_buf = tmp_buf + TX_BUF_ALLOC; + tmp_buf_dma = tmp_buf_dma + TX_BUF_ALLOC; + } + (--tmp_tx)->tdes3 = cpu_to_le32(db->first_tx_desc_dma); + tmp_tx->next_tx_desc = db->first_tx_desc; + + /* Init Receive descriptor chain */ + tmp_rx_dma=db->first_rx_desc_dma; + for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT; i++, tmp_rx++) { + tmp_rx->rdes0 = cpu_to_le32(0); + tmp_rx->rdes1 = cpu_to_le32(0x01000600); + tmp_rx_dma += sizeof(struct rx_desc); + tmp_rx->rdes3 = cpu_to_le32(tmp_rx_dma); + tmp_rx->next_rx_desc = tmp_rx + 1; + } + (--tmp_rx)->rdes3 = cpu_to_le32(db->first_rx_desc_dma); + tmp_rx->next_rx_desc = db->first_rx_desc; + + /* pre-allocate Rx buffer */ + allocate_rx_buffer(db); +} + + +/* + * Update CR6 value + * Firstly stop DM910X , then written value and start + */ + +static void update_cr6(u32 cr6_data, unsigned long ioaddr) +{ + u32 cr6_tmp; + + cr6_tmp = cr6_data & ~0x2002; /* stop Tx/Rx */ + outl(cr6_tmp, ioaddr + DCR6); + udelay(5); + outl(cr6_data, ioaddr + DCR6); + udelay(5); +} + + +/* + * Send a setup frame for DM9132 + * This setup frame initilize DM910X addres filter mode +*/ + +static void dm9132_id_table(struct DEVICE *dev, int mc_cnt) +{ + struct dev_mc_list *mcptr; + u16 * addrptr; + unsigned long ioaddr = dev->base_addr+0xc0; /* ID Table */ + u32 hash_val; + u16 i, hash_table[4]; + + DMFE_DBUG(0, "dm9132_id_table()", 0); + + /* Node address */ + addrptr = (u16 *) dev->dev_addr; + outw(addrptr[0], ioaddr); + ioaddr += 4; + outw(addrptr[1], ioaddr); + ioaddr += 4; + outw(addrptr[2], ioaddr); + ioaddr += 4; + + /* Clear Hash Table */ + for (i = 0; i < 4; i++) + hash_table[i] = 0x0; + + /* broadcast address */ + hash_table[3] = 0x8000; + + /* the multicast address in Hash Table : 64 bits */ + for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) { + hash_val = cal_CRC( (char *) mcptr->dmi_addr, 6, 0) & 0x3f; + hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16); + } + + /* Write the hash table to MAC MD table */ + for (i = 0; i < 4; i++, ioaddr += 4) + outw(hash_table[i], ioaddr); +} + + +/* + * Send a setup frame for DM9102/DM9102A + * This setup frame initilize DM910X addres filter mode + */ + +static void send_filter_frame(struct DEVICE *dev, int mc_cnt) +{ + struct dmfe_board_info *db = dev->priv; + struct dev_mc_list *mcptr; + struct tx_desc *txptr; + u16 * addrptr; + u32 * suptr; + int i; + + DMFE_DBUG(0, "send_filter_frame()", 0); + + txptr = db->tx_insert_ptr; + suptr = (u32 *) txptr->tx_buf_ptr; + + /* Node address */ + addrptr = (u16 *) dev->dev_addr; + *suptr++ = addrptr[0]; + *suptr++ = addrptr[1]; + *suptr++ = addrptr[2]; + + /* broadcast address */ + *suptr++ = 0xffff; + *suptr++ = 0xffff; + *suptr++ = 0xffff; + + /* fit the multicast address */ + for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) { + addrptr = (u16 *) mcptr->dmi_addr; + *suptr++ = addrptr[0]; + *suptr++ = addrptr[1]; + *suptr++ = addrptr[2]; + } + + for (; i<14; i++) { + *suptr++ = 0xffff; + *suptr++ = 0xffff; + *suptr++ = 0xffff; + } + + /* prepare the setup frame */ + db->tx_insert_ptr = txptr->next_tx_desc; + txptr->tdes1 = cpu_to_le32(0x890000c0); + + /* Resource Check and Send the setup packet */ + if (!db->tx_packet_cnt) { + /* Resource Empty */ + db->tx_packet_cnt++; + txptr->tdes0 = cpu_to_le32(0x80000000); + update_cr6(db->cr6_data | 0x2000, dev->base_addr); + outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ + update_cr6(db->cr6_data, dev->base_addr); + dev->trans_start = jiffies; + } else + db->tx_queue_cnt++; /* Put in TX queue */ +} + + +/* + * Allocate rx buffer, + * As possible as allocate maxiumn Rx buffer + */ + +static void allocate_rx_buffer(struct dmfe_board_info *db) +{ + struct rx_desc *rxptr; + struct sk_buff *skb; + + rxptr = db->rx_insert_ptr; + + while(db->rx_avail_cnt < RX_DESC_CNT) { + if ( ( skb = dev_alloc_skb(RX_ALLOC_SIZE) ) == NULL ) + break; + rxptr->rx_skb_ptr = skb; /* FIXME (?) */ + rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); + wmb(); + rxptr->rdes0 = cpu_to_le32(0x80000000); + rxptr = rxptr->next_rx_desc; + db->rx_avail_cnt++; + } + + db->rx_insert_ptr = rxptr; +} + + +/* + * Read one word data from the serial ROM + */ + +static u16 read_srom_word(long ioaddr, int offset) +{ + int i; + u16 srom_data = 0; + long cr9_ioaddr = ioaddr + DCR9; + + outl(CR9_SROM_READ, cr9_ioaddr); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + /* Send the Read Command 110b */ + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr); + + /* Send the offset */ + for (i = 5; i >= 0; i--) { + srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0; + SROM_CLK_WRITE(srom_data, cr9_ioaddr); + } + + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + for (i = 16; i > 0; i--) { + outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr); + udelay(5); + srom_data = (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1 : 0); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + udelay(5); + } + + outl(CR9_SROM_READ, cr9_ioaddr); + return srom_data; +} + + +/* + * Auto sense the media mode + */ + +static u8 dmfe_sense_speed(struct dmfe_board_info * db) +{ + u8 ErrFlag = 0; + u16 phy_mode; + + /* CR6 bit18=0, select 10/100M */ + update_cr6( (db->cr6_data & ~0x40000), db->ioaddr); + + phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id); + phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id); + + if ( (phy_mode & 0x24) == 0x24 ) { + if (db->chip_id == PCI_DM9132_ID) /* DM9132 */ + phy_mode = phy_read(db->ioaddr, db->phy_addr, 7, db->chip_id) & 0xf000; + else /* DM9102/DM9102A */ + phy_mode = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0xf000; + /* printk(DRV_NAME ": Phy_mode %x ",phy_mode); */ + switch (phy_mode) { + case 0x1000: db->op_mode = DMFE_10MHF; break; + case 0x2000: db->op_mode = DMFE_10MFD; break; + case 0x4000: db->op_mode = DMFE_100MHF; break; + case 0x8000: db->op_mode = DMFE_100MFD; break; + default: db->op_mode = DMFE_10MHF; + ErrFlag = 1; + break; + } + } else { + db->op_mode = DMFE_10MHF; + DMFE_DBUG(0, "Link Failed :", phy_mode); + ErrFlag = 1; + } + + return ErrFlag; +} + + +/* + * Set 10/100 phyxcer capability + * AUTO mode : phyxcer register4 is NIC capability + * Force mode: phyxcer register4 is the force media + */ + +static void dmfe_set_phyxcer(struct dmfe_board_info *db) +{ + u16 phy_reg; + + /* Select 10/100M phyxcer */ + db->cr6_data &= ~0x40000; + update_cr6(db->cr6_data, db->ioaddr); + + /* DM9009 Chip: Phyxcer reg18 bit12=0 */ + if (db->chip_id == PCI_DM9009_ID) { + phy_reg = phy_read(db->ioaddr, db->phy_addr, 18, db->chip_id) & ~0x1000; + phy_write(db->ioaddr, db->phy_addr, 18, phy_reg, db->chip_id); + } + + /* Phyxcer capability setting */ + phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x01e0; + + if (db->media_mode & DMFE_AUTO) { + /* AUTO Mode */ + phy_reg |= db->PHY_reg4; + } else { + /* Force Mode */ + switch(db->media_mode) { + case DMFE_10MHF: phy_reg |= 0x20; break; + case DMFE_10MFD: phy_reg |= 0x40; break; + case DMFE_100MHF: phy_reg |= 0x80; break; + case DMFE_100MFD: phy_reg |= 0x100; break; + } + if (db->chip_id == PCI_DM9009_ID) phy_reg &= 0x61; + } + + /* Write new capability to Phyxcer Reg4 */ + if ( !(phy_reg & 0x01e0)) { + phy_reg|=db->PHY_reg4; + db->media_mode|=DMFE_AUTO; + } + phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id); + + /* Restart Auto-Negotiation */ + if ( db->chip_type && (db->chip_id == PCI_DM9102_ID) ) + phy_write(db->ioaddr, db->phy_addr, 0, 0x1800, db->chip_id); + if ( !db->chip_type ) + phy_write(db->ioaddr, db->phy_addr, 0, 0x1200, db->chip_id); +} + + +/* + * Process op-mode + * AUTO mode : PHY controller in Auto-negotiation Mode + * Force mode: PHY controller in force mode with HUB + * N-way force capability with SWITCH + */ + +static void dmfe_process_mode(struct dmfe_board_info *db) +{ + u16 phy_reg; + + /* Full Duplex Mode Check */ + if (db->op_mode & 0x4) + db->cr6_data |= CR6_FDM; /* Set Full Duplex Bit */ + else + db->cr6_data &= ~CR6_FDM; /* Clear Full Duplex Bit */ + + /* Transciver Selection */ + if (db->op_mode & 0x10) /* 1M HomePNA */ + db->cr6_data |= 0x40000;/* External MII select */ + else + db->cr6_data &= ~0x40000;/* Internal 10/100 transciver */ + + update_cr6(db->cr6_data, db->ioaddr); + + /* 10/100M phyxcer force mode need */ + if ( !(db->media_mode & 0x18)) { + /* Forece Mode */ + phy_reg = phy_read(db->ioaddr, db->phy_addr, 6, db->chip_id); + if ( !(phy_reg & 0x1) ) { + /* parter without N-Way capability */ + phy_reg = 0x0; + switch(db->op_mode) { + case DMFE_10MHF: phy_reg = 0x0; break; + case DMFE_10MFD: phy_reg = 0x100; break; + case DMFE_100MHF: phy_reg = 0x2000; break; + case DMFE_100MFD: phy_reg = 0x2100; break; + } + phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id); + if ( db->chip_type && (db->chip_id == PCI_DM9102_ID) ) + mdelay(20); + phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id); + } + } +} + + +/* + * Write a word to Phy register + */ + +static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, u16 phy_data, u32 chip_id) +{ + u16 i; + unsigned long ioaddr; + + if (chip_id == PCI_DM9132_ID) { + ioaddr = iobase + 0x80 + offset * 4; + outw(phy_data, ioaddr); + } else { + /* DM9102/DM9102A Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send write command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send Phy addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); + + /* Send register addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); + + /* written trasnition */ + phy_write_1bit(ioaddr, PHY_DATA_1); + phy_write_1bit(ioaddr, PHY_DATA_0); + + /* Write a word data to PHY controller */ + for ( i = 0x8000; i > 0; i >>= 1) + phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0); + } +} + + +/* + * Read a word data from phy register + */ + +static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset, u32 chip_id) +{ + int i; + u16 phy_data; + unsigned long ioaddr; + + if (chip_id == PCI_DM9132_ID) { + /* DM9132 Chip */ + ioaddr = iobase + 0x80 + offset * 4; + phy_data = inw(ioaddr); + } else { + /* DM9102/DM9102A Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send read command(10) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_1); + phy_write_1bit(ioaddr, PHY_DATA_0); + + /* Send Phy addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); + + /* Send register addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); + + /* Skip transition state */ + phy_read_1bit(ioaddr); + + /* read 16bit data */ + for (phy_data = 0, i = 0; i < 16; i++) { + phy_data <<= 1; + phy_data |= phy_read_1bit(ioaddr); + } + } + + return phy_data; +} + + +/* + * Write one bit data to Phy Controller + */ + +static void phy_write_1bit(unsigned long ioaddr, u32 phy_data) +{ + outl(phy_data, ioaddr); /* MII Clock Low */ + udelay(1); + outl(phy_data | MDCLKH, ioaddr); /* MII Clock High */ + udelay(1); + outl(phy_data, ioaddr); /* MII Clock Low */ + udelay(1); +} + + +/* + * Read one bit phy data from PHY controller + */ + +static u16 phy_read_1bit(unsigned long ioaddr) +{ + u16 phy_data; + + outl(0x50000, ioaddr); + udelay(1); + phy_data = ( inl(ioaddr) >> 19 ) & 0x1; + outl(0x40000, ioaddr); + udelay(1); + + return phy_data; +} + + +/* + * Calculate the CRC valude of the Rx packet + * flag = 1 : return the reverse CRC (for the received packet CRC) + * 0 : return the normal CRC (for Hash Table index) + */ + +static inline u32 cal_CRC(unsigned char * Data, unsigned int Len, u8 flag) +{ + u32 crc = crc32(~0, Data, Len); + if (flag) crc = ~crc; + return crc; +} + + +/* + * Parser SROM and media mode + */ + +static void dmfe_parse_srom(struct dmfe_board_info * db) +{ + char * srom = db->srom; + int dmfe_mode, tmp_reg; + + DMFE_DBUG(0, "dmfe_parse_srom() ", 0); + + /* Init CR15 */ + db->cr15_data = CR15_DEFAULT; + + /* Check SROM Version */ + if ( ( (int) srom[18] & 0xff) == SROM_V41_CODE) { + /* SROM V4.01 */ + /* Get NIC support media mode */ + db->NIC_capability = le16_to_cpup(srom + 34); + db->PHY_reg4 = 0; + for (tmp_reg = 1; tmp_reg < 0x10; tmp_reg <<= 1) { + switch( db->NIC_capability & tmp_reg ) { + case 0x1: db->PHY_reg4 |= 0x0020; break; + case 0x2: db->PHY_reg4 |= 0x0040; break; + case 0x4: db->PHY_reg4 |= 0x0080; break; + case 0x8: db->PHY_reg4 |= 0x0100; break; + } + } + + /* Media Mode Force or not check */ + dmfe_mode = le32_to_cpup(srom + 34) & le32_to_cpup(srom + 36); + switch(dmfe_mode) { + case 0x4: dmfe_media_mode = DMFE_100MHF; break; /* 100MHF */ + case 0x2: dmfe_media_mode = DMFE_10MFD; break; /* 10MFD */ + case 0x8: dmfe_media_mode = DMFE_100MFD; break; /* 100MFD */ + case 0x100: + case 0x200: dmfe_media_mode = DMFE_1M_HPNA; break;/* HomePNA */ + } + + /* Special Function setting */ + /* VLAN function */ + if ( (SF_mode & 0x1) || (srom[43] & 0x80) ) + db->cr15_data |= 0x40; + + /* Flow Control */ + if ( (SF_mode & 0x2) || (srom[40] & 0x1) ) + db->cr15_data |= 0x400; + + /* TX pause packet */ + if ( (SF_mode & 0x4) || (srom[40] & 0xe) ) + db->cr15_data |= 0x9800; + } + + /* Parse HPNA parameter */ + db->HPNA_command = 1; + + /* Accept remote command or not */ + if (HPNA_rx_cmd == 0) + db->HPNA_command |= 0x8000; + + /* Issue remote command & operation mode */ + if (HPNA_tx_cmd == 1) + switch(HPNA_mode) { /* Issue Remote Command */ + case 0: db->HPNA_command |= 0x0904; break; + case 1: db->HPNA_command |= 0x0a00; break; + case 2: db->HPNA_command |= 0x0506; break; + case 3: db->HPNA_command |= 0x0602; break; + } + else + switch(HPNA_mode) { /* Don't Issue */ + case 0: db->HPNA_command |= 0x0004; break; + case 1: db->HPNA_command |= 0x0000; break; + case 2: db->HPNA_command |= 0x0006; break; + case 3: db->HPNA_command |= 0x0002; break; + } + + /* Check DM9801 or DM9802 present or not */ + db->HPNA_present = 0; + update_cr6(db->cr6_data|0x40000, db->ioaddr); + tmp_reg = phy_read(db->ioaddr, db->phy_addr, 3, db->chip_id); + if ( ( tmp_reg & 0xfff0 ) == 0xb900 ) { + /* DM9801 or DM9802 present */ + db->HPNA_timer = 8; + if ( phy_read(db->ioaddr, db->phy_addr, 31, db->chip_id) == 0x4404) { + /* DM9801 HomeRun */ + db->HPNA_present = 1; + dmfe_program_DM9801(db, tmp_reg); + } else { + /* DM9802 LongRun */ + db->HPNA_present = 2; + dmfe_program_DM9802(db); + } + } + +} + + +/* + * Init HomeRun DM9801 + */ + +static void dmfe_program_DM9801(struct dmfe_board_info * db, int HPNA_rev) +{ + uint reg17, reg25; + + if ( !HPNA_NoiseFloor ) HPNA_NoiseFloor = DM9801_NOISE_FLOOR; + switch(HPNA_rev) { + case 0xb900: /* DM9801 E3 */ + db->HPNA_command |= 0x1000; + reg25 = phy_read(db->ioaddr, db->phy_addr, 24, db->chip_id); + reg25 = ( (reg25 + HPNA_NoiseFloor) & 0xff) | 0xf000; + reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); + break; + case 0xb901: /* DM9801 E4 */ + reg25 = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); + reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor; + reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); + reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor + 3; + break; + case 0xb902: /* DM9801 E5 */ + case 0xb903: /* DM9801 E6 */ + default: + db->HPNA_command |= 0x1000; + reg25 = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); + reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor - 5; + reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); + reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor; + break; + } + phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); + phy_write(db->ioaddr, db->phy_addr, 17, reg17, db->chip_id); + phy_write(db->ioaddr, db->phy_addr, 25, reg25, db->chip_id); +} + + +/* + * Init HomeRun DM9802 + */ + +static void dmfe_program_DM9802(struct dmfe_board_info * db) +{ + uint phy_reg; + + if ( !HPNA_NoiseFloor ) HPNA_NoiseFloor = DM9802_NOISE_FLOOR; + phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); + phy_reg = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); + phy_reg = ( phy_reg & 0xff00) + HPNA_NoiseFloor; + phy_write(db->ioaddr, db->phy_addr, 25, phy_reg, db->chip_id); +} + + +/* + * Check remote HPNA power and speed status. If not correct, + * issue command again. +*/ + +static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info * db) +{ + uint phy_reg; + + /* Got remote device status */ + phy_reg = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0x60; + switch(phy_reg) { + case 0x00: phy_reg = 0x0a00;break; /* LP/LS */ + case 0x20: phy_reg = 0x0900;break; /* LP/HS */ + case 0x40: phy_reg = 0x0600;break; /* HP/LS */ + case 0x60: phy_reg = 0x0500;break; /* HP/HS */ + } + + /* Check remote device status match our setting ot not */ + if ( phy_reg != (db->HPNA_command & 0x0f00) ) { + phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); + db->HPNA_timer=8; + } else + db->HPNA_timer=600; /* Match, every 10 minutes, check */ +} + + + +static struct pci_device_id dmfe_pci_tbl[] __devinitdata = { + { 0x1282, 0x9132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9132_ID }, + { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9102_ID }, + { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9100_ID }, + { 0x1282, 0x9009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9009_ID }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, dmfe_pci_tbl); + + +static struct pci_driver dmfe_driver = { + name: "dmfe", + id_table: dmfe_pci_tbl, + probe: dmfe_init_one, + remove: __devexit_p(dmfe_remove_one), +}; + +MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw"); +MODULE_DESCRIPTION("Davicom DM910X fast ethernet driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(debug, "i"); +MODULE_PARM(mode, "i"); +MODULE_PARM(cr6set, "i"); +MODULE_PARM(chkmode, "i"); +MODULE_PARM(HPNA_mode, "i"); +MODULE_PARM(HPNA_rx_cmd, "i"); +MODULE_PARM(HPNA_tx_cmd, "i"); +MODULE_PARM(HPNA_NoiseFloor, "i"); +MODULE_PARM(SF_mode, "i"); +MODULE_PARM_DESC(debug, "Davicom DM9xxx enable debugging (0-1)"); +MODULE_PARM_DESC(mode, "Davicom DM9xxx: Bit 0: 10/100Mbps, bit 2: duplex, bit 8: HomePNA"); +MODULE_PARM_DESC(SF_mode, "Davicom DM9xxx special function (bit 0: VLAN, bit 1 Flow Control, bit 2: TX pause packet)"); + +/* Description: + * when user used insmod to add module, system invoked init_module() + * to initilize and register. + */ + +static int __init dmfe_init_module(void) +{ + int rc; + + printk(version); + printed_version = 1; + + DMFE_DBUG(0, "init_module() ", debug); + + if (debug) + dmfe_debug = debug; /* set debug flag */ + if (cr6set) + dmfe_cr6_user_set = cr6set; + + switch(mode) { + case DMFE_10MHF: + case DMFE_100MHF: + case DMFE_10MFD: + case DMFE_100MFD: + case DMFE_1M_HPNA: + dmfe_media_mode = mode; + break; + default:dmfe_media_mode = DMFE_AUTO; + break; + } + + if (HPNA_mode > 4) + HPNA_mode = 0; /* Default: LP/HS */ + if (HPNA_rx_cmd > 1) + HPNA_rx_cmd = 0; /* Default: Ignored remote cmd */ + if (HPNA_tx_cmd > 1) + HPNA_tx_cmd = 0; /* Default: Don't issue remote cmd */ + if (HPNA_NoiseFloor > 15) + HPNA_NoiseFloor = 0; + + rc = pci_module_init(&dmfe_driver); + if (rc < 0) + return rc; + + return 0; +} + + +/* + * Description: + * when user used rmmod to delete module, system invoked clean_module() + * to un-register all registered services. + */ + +static void __exit dmfe_cleanup_module(void) +{ + DMFE_DBUG(0, "dmfe_clean_module() ", debug); + pci_unregister_driver(&dmfe_driver); +} + +module_init(dmfe_init_module); +module_exit(dmfe_cleanup_module); diff -Nru a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c --- a/drivers/net/tulip/tulip_core.c Thu Mar 7 18:17:45 2002 +++ b/drivers/net/tulip/tulip_core.c Thu Mar 7 18:17:45 2002 @@ -15,8 +15,8 @@ */ #define DRV_NAME "tulip" -#define DRV_VERSION "1.1.11" -#define DRV_RELDATE "Feb 08, 2002" +#define DRV_VERSION "1.1.12" +#define DRV_RELDATE "Mar 07, 2002" #include #include @@ -1164,7 +1164,7 @@ { struct tulip_private *tp = dev->priv; u8 cache; - u16 pci_command, new_command; + u16 pci_command; u32 csr0; if (tulip_debug > 3) @@ -1172,24 +1172,6 @@ tp->csr0 = csr0 = 0; - /* check for sane cache line size. from acenic.c. */ - pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache); - if ((cache << 2) != SMP_CACHE_BYTES) { - printk(KERN_WARNING "%s: PCI cache line size set incorrectly " - "(%i bytes) by BIOS/FW, correcting to %i\n", - pdev->slot_name, (cache << 2), SMP_CACHE_BYTES); - pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, - SMP_CACHE_BYTES >> 2); - udelay(5); - } - - /* read cache line size again, hardware may not have accepted - * our cache line size change - */ - pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache); - if (!cache) - goto out; - /* if we have any cache line size at all, we can do MRM */ csr0 |= MRM; @@ -1200,15 +1182,19 @@ /* set or disable MWI in the standard PCI command bit. * Check for the case where mwi is desired but not available */ + if (csr0 & MWI) pci_set_mwi(pdev); + else pci_clear_mwi(pdev); + + /* read result from hardware (in case bit refused to enable) */ pci_read_config_word(pdev, PCI_COMMAND, &pci_command); - if (csr0 & MWI) new_command = pci_command | PCI_COMMAND_INVALIDATE; - else new_command = pci_command & ~PCI_COMMAND_INVALIDATE; - if (new_command != pci_command) { - pci_write_config_word(pdev, PCI_COMMAND, new_command); - udelay(5); - pci_read_config_word(pdev, PCI_COMMAND, &pci_command); - if ((csr0 & MWI) && (!(pci_command & PCI_COMMAND_INVALIDATE))) - csr0 &= ~MWI; + if ((csr0 & MWI) && (!(pci_command & PCI_COMMAND_INVALIDATE))) + csr0 &= ~MWI; + + /* if cache line size hardwired to zero, no MWI */ + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache); + if ((csr0 & MWI) && (cache == 0)) { + csr0 &= ~MWI; + pci_clear_mwi(pdev); } /* assign per-cacheline-size cache alignment and @@ -1225,20 +1211,29 @@ csr0 |= MRL | (3 << CALShift) | (32 << BurstLenShift); break; default: - goto out; + cache = 0; + break; } - tp->csr0 = csr0; - goto out; + /* if we have a good cache line size, we by now have a good + * csr0, so save it and exit + */ + if (cache) + goto out; + /* we don't have a good csr0 or cache line size, disable MWI */ if (csr0 & MWI) { - pci_command &= ~PCI_COMMAND_INVALIDATE; - pci_write_config_word(pdev, PCI_COMMAND, pci_command); + pci_clear_mwi(pdev); csr0 &= ~MWI; } - tp->csr0 = csr0 | (8 << BurstLenShift) | (1 << CALShift); + + /* sane defaults for burst length and cache alignment + * originally from de4x5 driver + */ + csr0 |= (8 << BurstLenShift) | (1 << CALShift); out: + tp->csr0 = csr0; if (tulip_debug > 2) printk(KERN_DEBUG "%s: MWI config cacheline=%d, csr0=%08x\n", pdev->slot_name, cache, csr0); diff -Nru a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/tulip/winbond-840.c Thu Mar 7 18:17:39 2002 @@ -0,0 +1,1757 @@ +/* winbond-840.c: A Linux PCI network adapter device driver. */ +/* + Written 1998-2001 by Donald Becker. + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + Support and updates available at + http://www.scyld.com/network/drivers.html + + Do not remove the copyright infomation. + Do not change the version information unless an improvement has been made. + Merely removing my name, as Compex has done in the past, does not count + as an improvement. + + Changelog: + * ported to 2.4 + ??? + * spin lock update, memory barriers, new style dma mappings + limit each tx buffer to < 1024 bytes + remove DescIntr from Rx descriptors (that's an Tx flag) + remove next pointer from Tx descriptors + synchronize tx_q_bytes + software reset in tx_timeout + Copyright (C) 2000 Manfred Spraul + * further cleanups + power management. + support for big endian descriptors + Copyright (C) 2001 Manfred Spraul + * ethtool support (jgarzik) + * Replace some MII-related magic numbers with constants (jgarzik) + + TODO: + * enable pci_power_off + * Wake-On-LAN +*/ + +#define DRV_NAME "winbond-840" +#define DRV_VERSION "1.01-d" +#define DRV_RELDATE "Nov-17-2001" + + +/* Automatically extracted configuration info: +probe-func: winbond840_probe +config-in: tristate 'Winbond W89c840 Ethernet support' CONFIG_WINBOND_840 + +c-help-name: Winbond W89c840 PCI Ethernet support +c-help-symbol: CONFIG_WINBOND_840 +c-help: This driver is for the Winbond W89c840 chip. It also works with +c-help: the TX9882 chip on the Compex RL100-ATX board. +c-help: More specific information and updates are available from +c-help: http://www.scyld.com/network/drivers.html +*/ + +/* The user-configurable values. + These may be modified when a driver module is loaded.*/ + +static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ +static int max_interrupt_work = 20; +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). + The '840 uses a 64 element hash table based on the Ethernet CRC. */ +static int multicast_filter_limit = 32; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. + Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak; + +/* Used to pass the media type, etc. + Both 'options[]' and 'full_duplex[]' should exist for driver + interoperability. + The media type is usually passed in 'options[]'. +*/ +#define MAX_UNITS 8 /* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two for compile efficiency. + The compiler will convert '%'<2^N> into a bit mask. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE 16 +#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */ +#define TX_QUEUE_LEN_RESTART 5 +#define RX_RING_SIZE 32 + +#define TX_BUFLIMIT (1024-128) + +/* The presumed FIFO size for working around the Tx-FIFO-overflow bug. + To avoid overflowing we don't queue again until we have room for a + full-size packet. + */ +#define TX_FIFO_SIZE (2048) +#define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16) + + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (2*HZ) + +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning You must compile this file with the correct options! +#warning See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +/* Include files, designed to support most kernel versions 2.0.0 and later. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Processor type for cache alignment. */ +#include +#include +#include + +/* These identify the driver base version and may not be removed. */ +static char version[] __devinitdata = +KERN_INFO DRV_NAME ".c:v" DRV_VERSION " (2.4 port) " DRV_RELDATE " Donald Becker \n" +KERN_INFO " http://www.scyld.com/network/drivers.html\n"; + +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("Winbond W89c840 Ethernet driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(debug, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM_DESC(max_interrupt_work, "winbond-840 maximum events handled per interrupt"); +MODULE_PARM_DESC(debug, "winbond-840 debug level (0-6)"); +MODULE_PARM_DESC(rx_copybreak, "winbond-840 copy breakpoint for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, "winbond-840 maximum number of filtered multicast addresses"); +MODULE_PARM_DESC(options, "winbond-840: Bits 0-3: media type, bit 17: full duplex"); +MODULE_PARM_DESC(full_duplex, "winbond-840 full duplex setting(s) (1)"); + +/* + Theory of Operation + +I. Board Compatibility + +This driver is for the Winbond w89c840 chip. + +II. Board-specific settings + +None. + +III. Driver operation + +This chip is very similar to the Digital 21*4* "Tulip" family. The first +twelve registers and the descriptor format are nearly identical. Read a +Tulip manual for operational details. + +A significant difference is that the multicast filter and station address are +stored in registers rather than loaded through a pseudo-transmit packet. + +Unlike the Tulip, transmit buffers are limited to 1KB. To transmit a +full-sized packet we must use both data buffers in a descriptor. Thus the +driver uses ring mode where descriptors are implicitly sequential in memory, +rather than using the second descriptor address as a chain pointer to +subsequent descriptors. + +IV. Notes + +If you are going to almost clone a Tulip, why not go all the way and avoid +the need for a new driver? + +IVb. References + +http://www.scyld.com/expert/100mbps.html +http://www.scyld.com/expert/NWay.html +http://www.winbond.com.tw/ + +IVc. Errata + +A horrible bug exists in the transmit FIFO. Apparently the chip doesn't +correctly detect a full FIFO, and queuing more than 2048 bytes may result in +silent data corruption. + +Test with 'ping -s 10000' on a fast computer. + +*/ + + + +/* + PCI probe table. +*/ +enum pci_id_flags_bits { + /* Set PCI command register bits before calling probe1(). */ + PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, + /* Read and map the single following PCI BAR. */ + PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4, + PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400, +}; +enum chip_capability_flags { + CanHaveMII=1, HasBrokenTx=2, AlwaysFDX=4, FDXOnNoMII=8,}; +#ifdef USE_IO_OPS +#define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER) +#else +#define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER) +#endif + +static struct pci_device_id w840_pci_tbl[] __devinitdata = { + { 0x1050, 0x0840, PCI_ANY_ID, 0x8153, 0, 0, 0 }, + { 0x1050, 0x0840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { 0x11f6, 0x2011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, w840_pci_tbl); + +struct pci_id_info { + const char *name; + struct match_info { + int pci, pci_mask, subsystem, subsystem_mask; + int revision, revision_mask; /* Only 8 bits. */ + } id; + enum pci_id_flags_bits pci_flags; + int io_size; /* Needed for I/O region check or ioremap(). */ + int drv_flags; /* Driver use, intended as capability flags. */ +}; +static struct pci_id_info pci_id_tbl[] = { + {"Winbond W89c840", /* Sometime a Level-One switch card. */ + { 0x08401050, 0xffffffff, 0x81530000, 0xffff0000 }, + W840_FLAGS, 128, CanHaveMII | HasBrokenTx | FDXOnNoMII}, + {"Winbond W89c840", { 0x08401050, 0xffffffff, }, + W840_FLAGS, 128, CanHaveMII | HasBrokenTx}, + {"Compex RL100-ATX", { 0x201111F6, 0xffffffff,}, + W840_FLAGS, 128, CanHaveMII | HasBrokenTx}, + {0,}, /* 0 terminated list. */ +}; + +/* This driver was written to use PCI memory space, however some x86 systems + work only with I/O space accesses. Pass -DUSE_IO_OPS to use PCI I/O space + accesses instead of memory space. */ + +#ifdef USE_IO_OPS +#undef readb +#undef readw +#undef readl +#undef writeb +#undef writew +#undef writel +#define readb inb +#define readw inw +#define readl inl +#define writeb outb +#define writew outw +#define writel outl +#endif + +/* Offsets to the Command and Status Registers, "CSRs". + While similar to the Tulip, these registers are longword aligned. + Note: It's not useful to define symbolic names for every register bit in + the device. The name can only partially document the semantics and make + the driver longer and more difficult to read. +*/ +enum w840_offsets { + PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08, + RxRingPtr=0x0C, TxRingPtr=0x10, + IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C, + RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C, + CurRxDescAddr=0x30, CurRxBufAddr=0x34, /* Debug use */ + MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40, + CurTxDescAddr=0x4C, CurTxBufAddr=0x50, +}; + +/* Bits in the interrupt status/enable registers. */ +/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */ +enum intr_status_bits { + NormalIntr=0x10000, AbnormalIntr=0x8000, + IntrPCIErr=0x2000, TimerInt=0x800, + IntrRxDied=0x100, RxNoBuf=0x80, IntrRxDone=0x40, + TxFIFOUnderflow=0x20, RxErrIntr=0x10, + TxIdle=0x04, IntrTxStopped=0x02, IntrTxDone=0x01, +}; + +/* Bits in the NetworkConfig register. */ +enum rx_mode_bits { + AcceptErr=0x80, AcceptRunt=0x40, + AcceptBroadcast=0x20, AcceptMulticast=0x10, + AcceptAllPhys=0x08, AcceptMyPhys=0x02, +}; + +enum mii_reg_bits { + MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000, + MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000, +}; + +/* The Tulip Rx and Tx buffer descriptors. */ +struct w840_rx_desc { + s32 status; + s32 length; + u32 buffer1; + u32 buffer2; +}; + +struct w840_tx_desc { + s32 status; + s32 length; + u32 buffer1, buffer2; +}; + +/* Bits in network_desc.status */ +enum desc_status_bits { + DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000, + DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000, + DescIntr=0x80000000, +}; + +#define PRIV_ALIGN 15 /* Required alignment mask */ +#define MII_CNT 1 /* winbond only supports one MII */ +struct netdev_private { + struct w840_rx_desc *rx_ring; + dma_addr_t rx_addr[RX_RING_SIZE]; + struct w840_tx_desc *tx_ring; + dma_addr_t tx_addr[TX_RING_SIZE]; + dma_addr_t ring_dma_addr; + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + /* The saved address of a sent-in-place packet/buffer, for later free(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + struct net_device_stats stats; + struct timer_list timer; /* Media monitoring timer. */ + /* Frequently used values: keep some adjacent for cache effect. */ + spinlock_t lock; + int chip_id, drv_flags; + struct pci_dev *pci_dev; + int csr6; + struct w840_rx_desc *rx_head_desc; + unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ + unsigned int rx_buf_sz; /* Based on MTU+slack. */ + unsigned int cur_tx, dirty_tx; + unsigned int tx_q_bytes; + unsigned int tx_full; /* The Tx queue is full. */ + /* MII transceiver section. */ + int mii_cnt; /* MII device addresses. */ + unsigned char phys[MII_CNT]; /* MII device addresses, but only the first is used */ + u32 mii; + struct mii_if_info mii_if; +}; + +static int eeprom_read(long ioaddr, int location); +static int mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int location, int value); +static int netdev_open(struct net_device *dev); +static int update_link(struct net_device *dev); +static void netdev_timer(unsigned long data); +static void init_rxtx_rings(struct net_device *dev); +static void free_rxtx_rings(struct netdev_private *np); +static void init_registers(struct net_device *dev); +static void tx_timeout(struct net_device *dev); +static int alloc_ringdesc(struct net_device *dev); +static void free_ringdesc(struct netdev_private *np); +static int start_tx(struct sk_buff *skb, struct net_device *dev); +static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); +static void netdev_error(struct net_device *dev, int intr_status); +static int netdev_rx(struct net_device *dev); +static u32 __set_rx_mode(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); +static struct net_device_stats *get_stats(struct net_device *dev); +static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static int netdev_close(struct net_device *dev); + + + +static int __devinit w840_probe1 (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev; + struct netdev_private *np; + static int find_cnt; + int chip_idx = ent->driver_data; + int irq; + int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0; + long ioaddr; + + i = pci_enable_device(pdev); + if (i) return i; + + pci_set_master(pdev); + + irq = pdev->irq; + + if (pci_set_dma_mask(pdev,0xFFFFffff)) { + printk(KERN_WARNING "Winbond-840: Device %s disabled due to DMA limitations.\n", + pdev->slot_name); + return -EIO; + } + dev = alloc_etherdev(sizeof(*np)); + if (!dev) + return -ENOMEM; + SET_MODULE_OWNER(dev); + + if (pci_request_regions(pdev, DRV_NAME)) + goto err_out_netdev; + +#ifdef USE_IO_OPS + ioaddr = pci_resource_start(pdev, 0); +#else + ioaddr = pci_resource_start(pdev, 1); + ioaddr = (long) ioremap (ioaddr, pci_id_tbl[chip_idx].io_size); + if (!ioaddr) + goto err_out_free_res; +#endif + + for (i = 0; i < 3; i++) + ((u16 *)dev->dev_addr)[i] = le16_to_cpu(eeprom_read(ioaddr, i)); + + /* Reset the chip to erase previous misconfiguration. + No hold time required! */ + writel(0x00000001, ioaddr + PCIBusCfg); + + dev->base_addr = ioaddr; + dev->irq = irq; + + np = dev->priv; + np->pci_dev = pdev; + np->chip_id = chip_idx; + np->drv_flags = pci_id_tbl[chip_idx].drv_flags; + spin_lock_init(&np->lock); + np->mii_if.dev = dev; + np->mii_if.mdio_read = mdio_read; + np->mii_if.mdio_write = mdio_write; + + pci_set_drvdata(pdev, dev); + + if (dev->mem_start) + option = dev->mem_start; + + /* The lower four bits are the media type. */ + if (option > 0) { + if (option & 0x200) + np->mii_if.full_duplex = 1; + if (option & 15) + printk(KERN_INFO "%s: ignoring user supplied media type %d", + dev->name, option & 15); + } + if (find_cnt < MAX_UNITS && full_duplex[find_cnt] > 0) + np->mii_if.full_duplex = 1; + + if (np->mii_if.full_duplex) + np->mii_if.duplex_lock = 1; + + /* The chip-specific entries in the device structure. */ + dev->open = &netdev_open; + dev->hard_start_xmit = &start_tx; + dev->stop = &netdev_close; + dev->get_stats = &get_stats; + dev->set_multicast_list = &set_rx_mode; + dev->do_ioctl = &netdev_ioctl; + dev->tx_timeout = &tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + + i = register_netdev(dev); + if (i) + goto err_out_cleardev; + + printk(KERN_INFO "%s: %s at 0x%lx, ", + dev->name, pci_id_tbl[chip_idx].name, ioaddr); + for (i = 0; i < 5; i++) + printk("%2.2x:", dev->dev_addr[i]); + printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); + + if (np->drv_flags & CanHaveMII) { + int phy, phy_idx = 0; + for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) { + int mii_status = mdio_read(dev, phy, MII_BMSR); + if (mii_status != 0xffff && mii_status != 0x0000) { + np->phys[phy_idx++] = phy; + np->mii_if.advertising = mdio_read(dev, phy, MII_ADVERTISE); + np->mii = (mdio_read(dev, phy, MII_PHYSID1) << 16)+ + mdio_read(dev, phy, MII_PHYSID2); + printk(KERN_INFO "%s: MII PHY %8.8xh found at address %d, status " + "0x%4.4x advertising %4.4x.\n", + dev->name, np->mii, phy, mii_status, np->mii_if.advertising); + } + } + np->mii_cnt = phy_idx; + np->mii_if.phy_id = np->phys[0]; + if (phy_idx == 0) { + printk(KERN_WARNING "%s: MII PHY not found -- this device may " + "not operate correctly.\n", dev->name); + } + } + + find_cnt++; + return 0; + +err_out_cleardev: + pci_set_drvdata(pdev, NULL); +#ifndef USE_IO_OPS + iounmap((void *)ioaddr); +err_out_free_res: +#endif + pci_release_regions(pdev); +err_out_netdev: + kfree (dev); + return -ENODEV; +} + + +/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. These are + often serial bit streams generated by the host processor. + The example below is for the common 93c46 EEPROM, 64 16 bit words. */ + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need + a delay. Note that pre-2.0.34 kernels had a cache-alignment bug that + made udelay() unreliable. + The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is + depricated. +*/ +#define eeprom_delay(ee_addr) readl(ee_addr) + +enum EEPROM_Ctrl_Bits { + EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805, + EE_ChipSelect=0x801, EE_DataIn=0x08, +}; + +/* The EEPROM commands include the alway-set leading bit. */ +enum EEPROM_Cmds { + EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6), +}; + +static int eeprom_read(long addr, int location) +{ + int i; + int retval = 0; + long ee_addr = addr + EECtrl; + int read_cmd = location | EE_ReadCmd; + writel(EE_ChipSelect, ee_addr); + + /* Shift the read command bits out. */ + for (i = 10; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0; + writel(dataval, ee_addr); + eeprom_delay(ee_addr); + writel(dataval | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + } + writel(EE_ChipSelect, ee_addr); + eeprom_delay(ee_addr); + + for (i = 16; i > 0; i--) { + writel(EE_ChipSelect | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0); + writel(EE_ChipSelect, ee_addr); + eeprom_delay(ee_addr); + } + + /* Terminate the EEPROM access. */ + writel(0, ee_addr); + return retval; +} + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. + + The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back 33Mhz PCI cycles. */ +#define mdio_delay(mdio_addr) readl(mdio_addr) + +/* Set iff a MII transceiver on any interface requires mdio preamble. + This only set with older tranceivers, so the extra + code size of a per-interface flag is not worthwhile. */ +static char mii_preamble_required = 1; + +#define MDIO_WRITE0 (MDIO_EnbOutput) +#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput) + +/* Generate the preamble required for initial synchronization and + a few older transceivers. */ +static void mdio_sync(long mdio_addr) +{ + int bits = 32; + + /* Establish sync by sending at least 32 logic ones. */ + while (--bits >= 0) { + writel(MDIO_WRITE1, mdio_addr); + mdio_delay(mdio_addr); + writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } +} + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ + long mdio_addr = dev->base_addr + MIICtrl; + int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int i, retval = 0; + + if (mii_preamble_required) + mdio_sync(mdio_addr); + + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + + writel(dataval, mdio_addr); + mdio_delay(mdio_addr); + writel(dataval | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 20; i > 0; i--) { + writel(MDIO_EnbIn, mdio_addr); + mdio_delay(mdio_addr); + retval = (retval << 1) | ((readl(mdio_addr) & MDIO_DataIn) ? 1 : 0); + writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + return (retval>>1) & 0xffff; +} + +static void mdio_write(struct net_device *dev, int phy_id, int location, int value) +{ + struct netdev_private *np = dev->priv; + long mdio_addr = dev->base_addr + MIICtrl; + int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; + int i; + + if (location == 4 && phy_id == np->phys[0]) + np->mii_if.advertising = value; + + if (mii_preamble_required) + mdio_sync(mdio_addr); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + + writel(dataval, mdio_addr); + mdio_delay(mdio_addr); + writel(dataval | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + writel(MDIO_EnbIn, mdio_addr); + mdio_delay(mdio_addr); + writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + return; +} + + +static int netdev_open(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + int i; + + writel(0x00000001, ioaddr + PCIBusCfg); /* Reset */ + + netif_device_detach(dev); + i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev); + if (i) + goto out_err; + + if (debug > 1) + printk(KERN_DEBUG "%s: w89c840_open() irq %d.\n", + dev->name, dev->irq); + + if((i=alloc_ringdesc(dev))) + goto out_err; + + spin_lock_irq(&np->lock); + netif_device_attach(dev); + init_registers(dev); + spin_unlock_irq(&np->lock); + + netif_start_queue(dev); + if (debug > 2) + printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name); + + /* Set the timer to check for link beat. */ + init_timer(&np->timer); + np->timer.expires = jiffies + 1*HZ; + np->timer.data = (unsigned long)dev; + np->timer.function = &netdev_timer; /* timer handler */ + add_timer(&np->timer); + return 0; +out_err: + netif_device_attach(dev); + return i; +} + +#define MII_DAVICOM_DM9101 0x0181b800 + +static int update_link(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + int duplex, fasteth, result, mii_reg; + + /* BSMR */ + mii_reg = mdio_read(dev, np->phys[0], MII_BMSR); + + if (mii_reg == 0xffff) + return np->csr6; + /* reread: the link status bit is sticky */ + mii_reg = mdio_read(dev, np->phys[0], MII_BMSR); + if (!(mii_reg & 0x4)) { + if (netif_carrier_ok(dev)) { + if (debug) + printk(KERN_INFO "%s: MII #%d reports no link. Disabling watchdog.\n", + dev->name, np->phys[0]); + netif_carrier_off(dev); + } + return np->csr6; + } + if (!netif_carrier_ok(dev)) { + if (debug) + printk(KERN_INFO "%s: MII #%d link is back. Enabling watchdog.\n", + dev->name, np->phys[0]); + netif_carrier_on(dev); + } + + if ((np->mii & ~0xf) == MII_DAVICOM_DM9101) { + /* If the link partner doesn't support autonegotiation + * the MII detects it's abilities with the "parallel detection". + * Some MIIs update the LPA register to the result of the parallel + * detection, some don't. + * The Davicom PHY [at least 0181b800] doesn't. + * Instead bit 9 and 13 of the BMCR are updated to the result + * of the negotiation.. + */ + mii_reg = mdio_read(dev, np->phys[0], MII_BMCR); + duplex = mii_reg & BMCR_FULLDPLX; + fasteth = mii_reg & BMCR_SPEED100; + } else { + int negotiated; + mii_reg = mdio_read(dev, np->phys[0], MII_LPA); + negotiated = mii_reg & np->mii_if.advertising; + + duplex = (negotiated & LPA_100FULL) || ((negotiated & 0x02C0) == LPA_10FULL); + fasteth = negotiated & 0x380; + } + duplex |= np->mii_if.duplex_lock; + /* remove fastether and fullduplex */ + result = np->csr6 & ~0x20000200; + if (duplex) + result |= 0x200; + if (fasteth) + result |= 0x20000000; + if (result != np->csr6 && debug) + printk(KERN_INFO "%s: Setting %dMBit-%s-duplex based on MII#%d\n", + dev->name, fasteth ? 100 : 10, + duplex ? "full" : "half", np->phys[0]); + return result; +} + +#define RXTX_TIMEOUT 2000 +static inline void update_csr6(struct net_device *dev, int new) +{ + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + int limit = RXTX_TIMEOUT; + + if (!netif_device_present(dev)) + new = 0; + if (new==np->csr6) + return; + /* stop both Tx and Rx processes */ + writel(np->csr6 & ~0x2002, ioaddr + NetworkConfig); + /* wait until they have really stopped */ + for (;;) { + int csr5 = readl(ioaddr + IntrStatus); + int t; + + t = (csr5 >> 17) & 0x07; + if (t==0||t==1) { + /* rx stopped */ + t = (csr5 >> 20) & 0x07; + if (t==0||t==1) + break; + } + + limit--; + if(!limit) { + printk(KERN_INFO "%s: couldn't stop rxtx, IntrStatus %xh.\n", + dev->name, csr5); + break; + } + udelay(1); + } + np->csr6 = new; + /* and restart them with the new configuration */ + writel(np->csr6, ioaddr + NetworkConfig); + if (new & 0x200) + np->mii_if.full_duplex = 1; +} + +static void netdev_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + + if (debug > 2) + printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x " + "config %8.8x.\n", + dev->name, (int)readl(ioaddr + IntrStatus), + (int)readl(ioaddr + NetworkConfig)); + spin_lock_irq(&np->lock); + update_csr6(dev, update_link(dev)); + spin_unlock_irq(&np->lock); + np->timer.expires = jiffies + 10*HZ; + add_timer(&np->timer); +} + +static void init_rxtx_rings(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + int i; + + np->rx_head_desc = &np->rx_ring[0]; + np->tx_ring = (struct w840_tx_desc*)&np->rx_ring[RX_RING_SIZE]; + + /* Initial all Rx descriptors. */ + for (i = 0; i < RX_RING_SIZE; i++) { + np->rx_ring[i].length = np->rx_buf_sz; + np->rx_ring[i].status = 0; + np->rx_skbuff[i] = 0; + } + /* Mark the last entry as wrapping the ring. */ + np->rx_ring[i-1].length |= DescEndRing; + + /* Fill in the Rx buffers. Handle allocation failure gracefully. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); + np->rx_skbuff[i] = skb; + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + np->rx_addr[i] = pci_map_single(np->pci_dev,skb->tail, + skb->len,PCI_DMA_FROMDEVICE); + + np->rx_ring[i].buffer1 = np->rx_addr[i]; + np->rx_ring[i].status = DescOwn; + } + + np->cur_rx = 0; + np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + + /* Initialize the Tx descriptors */ + for (i = 0; i < TX_RING_SIZE; i++) { + np->tx_skbuff[i] = 0; + np->tx_ring[i].status = 0; + } + np->tx_full = 0; + np->tx_q_bytes = np->dirty_tx = np->cur_tx = 0; + + writel(np->ring_dma_addr, dev->base_addr + RxRingPtr); + writel(np->ring_dma_addr+sizeof(struct w840_rx_desc)*RX_RING_SIZE, + dev->base_addr + TxRingPtr); + +} + +static void free_rxtx_rings(struct netdev_private* np) +{ + int i; + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + np->rx_ring[i].status = 0; + if (np->rx_skbuff[i]) { + pci_unmap_single(np->pci_dev, + np->rx_addr[i], + np->rx_skbuff[i]->len, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(np->rx_skbuff[i]); + } + np->rx_skbuff[i] = 0; + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (np->tx_skbuff[i]) { + pci_unmap_single(np->pci_dev, + np->tx_addr[i], + np->tx_skbuff[i]->len, + PCI_DMA_TODEVICE); + dev_kfree_skb(np->tx_skbuff[i]); + } + np->tx_skbuff[i] = 0; + } +} + +static void init_registers(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + int i; + + for (i = 0; i < 6; i++) + writeb(dev->dev_addr[i], ioaddr + StationAddr + i); + + /* Initialize other registers. */ +#ifdef __BIG_ENDIAN + i = (1<<20); /* Big-endian descriptors */ +#else + i = 0; +#endif + i |= (0x04<<2); /* skip length 4 u32 */ + i |= 0x02; /* give Rx priority */ + + /* Configure the PCI bus bursts and FIFO thresholds. + 486: Set 8 longword cache alignment, 8 longword burst. + 586: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 0000 align to cache 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords */ + +#if defined (__i386__) && !defined(MODULE) + /* When not a module we can work around broken '486 PCI boards. */ + if (boot_cpu_data.x86 <= 4) { + i |= 0x4800; + printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache " + "alignment to 8 longwords.\n", dev->name); + } else { + i |= 0xE000; + } +#elif defined(__powerpc__) || defined(__i386__) || defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) + i |= 0xE000; +#elif defined(__sparc__) + i |= 0x4800; +#else +#warning Processor architecture undefined + i |= 0x4800; +#endif + writel(i, ioaddr + PCIBusCfg); + + np->csr6 = 0; + /* 128 byte Tx threshold; + Transmit on; Receive on; */ + update_csr6(dev, 0x00022002 | update_link(dev) | __set_rx_mode(dev)); + + /* Clear and Enable interrupts by setting the interrupt mask. */ + writel(0x1A0F5, ioaddr + IntrStatus); + writel(0x1A0F5, ioaddr + IntrEnable); + + writel(0, ioaddr + RxStartDemand); +} + +static void tx_timeout(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + + printk(KERN_WARNING "%s: Transmit timed out, status %8.8x," + " resetting...\n", dev->name, (int)readl(ioaddr + IntrStatus)); + + { + int i; + printk(KERN_DEBUG " Rx ring %p: ", np->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)np->rx_ring[i].status); + printk("\n"KERN_DEBUG" Tx ring %p: ", np->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %8.8x", np->tx_ring[i].status); + printk("\n"); + } + printk(KERN_DEBUG "Tx cur %d Tx dirty %d Tx Full %d, q bytes %d.\n", + np->cur_tx, np->dirty_tx, np->tx_full, np->tx_q_bytes); + printk(KERN_DEBUG "Tx Descriptor addr %xh.\n",readl(ioaddr+0x4C)); + + disable_irq(dev->irq); + spin_lock_irq(&np->lock); + /* + * Under high load dirty_tx and the internal tx descriptor pointer + * come out of sync, thus perform a software reset and reinitialize + * everything. + */ + + writel(1, dev->base_addr+PCIBusCfg); + udelay(1); + + free_rxtx_rings(np); + init_rxtx_rings(dev); + init_registers(dev); + spin_unlock_irq(&np->lock); + enable_irq(dev->irq); + + netif_wake_queue(dev); + dev->trans_start = jiffies; + np->stats.tx_errors++; + return; +} + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static int alloc_ringdesc(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + + np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); + + np->rx_ring = pci_alloc_consistent(np->pci_dev, + sizeof(struct w840_rx_desc)*RX_RING_SIZE + + sizeof(struct w840_tx_desc)*TX_RING_SIZE, + &np->ring_dma_addr); + if(!np->rx_ring) + return -ENOMEM; + init_rxtx_rings(dev); + return 0; +} + +static void free_ringdesc(struct netdev_private *np) +{ + pci_free_consistent(np->pci_dev, + sizeof(struct w840_rx_desc)*RX_RING_SIZE + + sizeof(struct w840_tx_desc)*TX_RING_SIZE, + np->rx_ring, np->ring_dma_addr); + +} + +static int start_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + unsigned entry; + + /* Caution: the write order is important here, set the field + with the "ownership" bits last. */ + + /* Calculate the next Tx descriptor entry. */ + entry = np->cur_tx % TX_RING_SIZE; + + np->tx_addr[entry] = pci_map_single(np->pci_dev, + skb->data,skb->len, PCI_DMA_TODEVICE); + np->tx_skbuff[entry] = skb; + + np->tx_ring[entry].buffer1 = np->tx_addr[entry]; + if (skb->len < TX_BUFLIMIT) { + np->tx_ring[entry].length = DescWholePkt | skb->len; + } else { + int len = skb->len - TX_BUFLIMIT; + + np->tx_ring[entry].buffer2 = np->tx_addr[entry]+TX_BUFLIMIT; + np->tx_ring[entry].length = DescWholePkt | (len << 11) | TX_BUFLIMIT; + } + if(entry == TX_RING_SIZE-1) + np->tx_ring[entry].length |= DescEndRing; + + /* Now acquire the irq spinlock. + * The difficult race is the the ordering between + * increasing np->cur_tx and setting DescOwn: + * - if np->cur_tx is increased first the interrupt + * handler could consider the packet as transmitted + * since DescOwn is cleared. + * - If DescOwn is set first the NIC could report the + * packet as sent, but the interrupt handler would ignore it + * since the np->cur_tx was not yet increased. + */ + spin_lock_irq(&np->lock); + np->cur_tx++; + + wmb(); /* flush length, buffer1, buffer2 */ + np->tx_ring[entry].status = DescOwn; + wmb(); /* flush status and kick the hardware */ + writel(0, dev->base_addr + TxStartDemand); + np->tx_q_bytes += skb->len; + /* Work around horrible bug in the chip by marking the queue as full + when we do not have FIFO room for a maximum sized packet. */ + if (np->cur_tx - np->dirty_tx > TX_QUEUE_LEN || + ((np->drv_flags & HasBrokenTx) && np->tx_q_bytes > TX_BUG_FIFO_LIMIT)) { + netif_stop_queue(dev); + wmb(); + np->tx_full = 1; + } + spin_unlock_irq(&np->lock); + + dev->trans_start = jiffies; + + if (debug > 4) { + printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n", + dev->name, np->cur_tx, entry); + } + return 0; +} + +static void netdev_tx_done(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { + int entry = np->dirty_tx % TX_RING_SIZE; + int tx_status = np->tx_ring[entry].status; + + if (tx_status < 0) + break; + if (tx_status & 0x8000) { /* There was an error, log it. */ +#ifndef final_version + if (debug > 1) + printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", + dev->name, tx_status); +#endif + np->stats.tx_errors++; + if (tx_status & 0x0104) np->stats.tx_aborted_errors++; + if (tx_status & 0x0C80) np->stats.tx_carrier_errors++; + if (tx_status & 0x0200) np->stats.tx_window_errors++; + if (tx_status & 0x0002) np->stats.tx_fifo_errors++; + if ((tx_status & 0x0080) && np->mii_if.full_duplex == 0) + np->stats.tx_heartbeat_errors++; +#ifdef ETHER_STATS + if (tx_status & 0x0100) np->stats.collisions16++; +#endif + } else { +#ifdef ETHER_STATS + if (tx_status & 0x0001) np->stats.tx_deferred++; +#endif +#ifndef final_version + if (debug > 3) + printk(KERN_DEBUG "%s: Transmit slot %d ok, Tx status %8.8x.\n", + dev->name, entry, tx_status); +#endif + np->stats.tx_bytes += np->tx_skbuff[entry]->len; + np->stats.collisions += (tx_status >> 3) & 15; + np->stats.tx_packets++; + } + /* Free the original skb. */ + pci_unmap_single(np->pci_dev,np->tx_addr[entry], + np->tx_skbuff[entry]->len, + PCI_DMA_TODEVICE); + np->tx_q_bytes -= np->tx_skbuff[entry]->len; + dev_kfree_skb_irq(np->tx_skbuff[entry]); + np->tx_skbuff[entry] = 0; + } + if (np->tx_full && + np->cur_tx - np->dirty_tx < TX_QUEUE_LEN_RESTART && + np->tx_q_bytes < TX_BUG_FIFO_LIMIT) { + /* The ring is no longer full, clear tbusy. */ + np->tx_full = 0; + wmb(); + netif_wake_queue(dev); + } +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) +{ + struct net_device *dev = (struct net_device *)dev_instance; + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + int work_limit = max_interrupt_work; + + if (!netif_device_present(dev)) + return; + do { + u32 intr_status = readl(ioaddr + IntrStatus); + + /* Acknowledge all of the current interrupt sources ASAP. */ + writel(intr_status & 0x001ffff, ioaddr + IntrStatus); + + if (debug > 4) + printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", + dev->name, intr_status); + + if ((intr_status & (NormalIntr|AbnormalIntr)) == 0) + break; + + if (intr_status & (IntrRxDone | RxNoBuf)) + netdev_rx(dev); + if (intr_status & RxNoBuf) + writel(0, ioaddr + RxStartDemand); + + if (intr_status & (TxIdle | IntrTxDone) && + np->cur_tx != np->dirty_tx) { + spin_lock(&np->lock); + netdev_tx_done(dev); + spin_unlock(&np->lock); + } + + /* Abnormal error summary/uncommon events handlers. */ + if (intr_status & (AbnormalIntr | TxFIFOUnderflow | IntrPCIErr | + TimerInt | IntrTxStopped)) + netdev_error(dev, intr_status); + + if (--work_limit < 0) { + printk(KERN_WARNING "%s: Too much work at interrupt, " + "status=0x%4.4x.\n", dev->name, intr_status); + /* Set the timer to re-enable the other interrupts after + 10*82usec ticks. */ + spin_lock(&np->lock); + if (netif_device_present(dev)) { + writel(AbnormalIntr | TimerInt, ioaddr + IntrEnable); + writel(10, ioaddr + GPTimer); + } + spin_unlock(&np->lock); + break; + } + } while (1); + + if (debug > 3) + printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", + dev->name, (int)readl(ioaddr + IntrStatus)); +} + +/* This routine is logically part of the interrupt handler, but separated + for clarity and better register allocation. */ +static int netdev_rx(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + int entry = np->cur_rx % RX_RING_SIZE; + int work_limit = np->dirty_rx + RX_RING_SIZE - np->cur_rx; + + if (debug > 4) { + printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n", + entry, np->rx_ring[entry].status); + } + + /* If EOP is set on the next entry, it's a new packet. Send it up. */ + while (--work_limit >= 0) { + struct w840_rx_desc *desc = np->rx_head_desc; + s32 status = desc->status; + + if (debug > 4) + printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n", + status); + if (status < 0) + break; + if ((status & 0x38008300) != 0x0300) { + if ((status & 0x38000300) != 0x0300) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " + "multiple buffers, entry %#x status %4.4x!\n", + dev->name, np->cur_rx, status); + np->stats.rx_length_errors++; + } + } else if (status & 0x8000) { + /* There was a fatal error. */ + if (debug > 2) + printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", + dev->name, status); + np->stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) np->stats.rx_length_errors++; + if (status & 0x004C) np->stats.rx_frame_errors++; + if (status & 0x0002) np->stats.rx_crc_errors++; + } + } else { + struct sk_buff *skb; + /* Omit the four octet CRC from the length. */ + int pkt_len = ((status >> 16) & 0x7ff) - 4; + +#ifndef final_version + if (debug > 4) + printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d" + " status %x.\n", pkt_len, status); +#endif + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ + if (pkt_len < rx_copybreak + && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align the IP header */ + pci_dma_sync_single(np->pci_dev,np->rx_addr[entry], + np->rx_skbuff[entry]->len, + PCI_DMA_FROMDEVICE); + /* Call copy + cksum if available. */ +#if HAS_IP_COPYSUM + eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); + skb_put(skb, pkt_len); +#else + memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail, + pkt_len); +#endif + } else { + pci_unmap_single(np->pci_dev,np->rx_addr[entry], + np->rx_skbuff[entry]->len, + PCI_DMA_FROMDEVICE); + skb_put(skb = np->rx_skbuff[entry], pkt_len); + np->rx_skbuff[entry] = NULL; + } +#ifndef final_version /* Remove after testing. */ + /* You will want this info for the initial debug. */ + if (debug > 5) + printk(KERN_DEBUG " Rx data %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:" + "%2.2x %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x %2.2x%2.2x " + "%d.%d.%d.%d.\n", + skb->data[0], skb->data[1], skb->data[2], skb->data[3], + skb->data[4], skb->data[5], skb->data[6], skb->data[7], + skb->data[8], skb->data[9], skb->data[10], + skb->data[11], skb->data[12], skb->data[13], + skb->data[14], skb->data[15], skb->data[16], + skb->data[17]); +#endif + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + np->stats.rx_packets++; + np->stats.rx_bytes += pkt_len; + } + entry = (++np->cur_rx) % RX_RING_SIZE; + np->rx_head_desc = &np->rx_ring[entry]; + } + + /* Refill the Rx ring buffers. */ + for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { + struct sk_buff *skb; + entry = np->dirty_rx % RX_RING_SIZE; + if (np->rx_skbuff[entry] == NULL) { + skb = dev_alloc_skb(np->rx_buf_sz); + np->rx_skbuff[entry] = skb; + if (skb == NULL) + break; /* Better luck next round. */ + skb->dev = dev; /* Mark as being used by this device. */ + np->rx_addr[entry] = pci_map_single(np->pci_dev, + skb->tail, + skb->len, PCI_DMA_FROMDEVICE); + np->rx_ring[entry].buffer1 = np->rx_addr[entry]; + } + wmb(); + np->rx_ring[entry].status = DescOwn; + } + + return 0; +} + +static void netdev_error(struct net_device *dev, int intr_status) +{ + long ioaddr = dev->base_addr; + struct netdev_private *np = dev->priv; + + if (debug > 2) + printk(KERN_DEBUG "%s: Abnormal event, %8.8x.\n", + dev->name, intr_status); + if (intr_status == 0xffffffff) + return; + spin_lock(&np->lock); + if (intr_status & TxFIFOUnderflow) { + int new; + /* Bump up the Tx threshold */ +#if 0 + /* This causes lots of dropped packets, + * and under high load even tx_timeouts + */ + new = np->csr6 + 0x4000; +#else + new = (np->csr6 >> 14)&0x7f; + if (new < 64) + new *= 2; + else + new = 127; /* load full packet before starting */ + new = (np->csr6 & ~(0x7F << 14)) | (new<<14); +#endif + printk(KERN_DEBUG "%s: Tx underflow, new csr6 %8.8x.\n", + dev->name, new); + update_csr6(dev, new); + } + if (intr_status & IntrRxDied) { /* Missed a Rx frame. */ + np->stats.rx_errors++; + } + if (intr_status & TimerInt) { + /* Re-enable other interrupts. */ + if (netif_device_present(dev)) + writel(0x1A0F5, ioaddr + IntrEnable); + } + np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; + writel(0, ioaddr + RxStartDemand); + spin_unlock(&np->lock); +} + +static struct net_device_stats *get_stats(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + struct netdev_private *np = dev->priv; + + /* The chip only need report frame silently dropped. */ + spin_lock_irq(&np->lock); + if (netif_running(dev) && netif_device_present(dev)) + np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; + spin_unlock_irq(&np->lock); + + return &np->stats; +} + + +static u32 __set_rx_mode(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + u32 mc_filter[2]; /* Multicast hash filter */ + u32 rx_mode; + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + /* Unconditionally log net taps. */ + printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); + memset(mc_filter, 0xff, sizeof(mc_filter)); + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAllPhys + | AcceptMyPhys; + } else if ((dev->mc_count > multicast_filter_limit) + || (dev->flags & IFF_ALLMULTI)) { + /* Too many to match, or accept all multicasts. */ + memset(mc_filter, 0xff, sizeof(mc_filter)); + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + } else { + struct dev_mc_list *mclist; + int i; + memset(mc_filter, 0, sizeof(mc_filter)); + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + set_bit((ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26) ^ 0x3F, + mc_filter); + } + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + } + writel(mc_filter[0], ioaddr + MulticastFilter0); + writel(mc_filter[1], ioaddr + MulticastFilter1); + return rx_mode; +} + +static void set_rx_mode(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + u32 rx_mode = __set_rx_mode(dev); + spin_lock_irq(&np->lock); + update_csr6(dev, (np->csr6 & ~0x00F8) | rx_mode); + spin_unlock_irq(&np->lock); +} + +static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) +{ + struct netdev_private *np = dev->priv; + u32 ethcmd; + + if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) + return -EFAULT; + + switch (ethcmd) { + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; + strcpy(info.driver, DRV_NAME); + strcpy(info.version, DRV_VERSION); + strcpy(info.bus_info, np->pci_dev->slot_name); + if (copy_to_user(useraddr, &info, sizeof(info))) + return -EFAULT; + return 0; + } + + /* get settings */ + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd = { ETHTOOL_GSET }; + spin_lock_irq(&np->lock); + mii_ethtool_gset(&np->mii_if, &ecmd); + spin_unlock_irq(&np->lock); + if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + } + /* set settings */ + case ETHTOOL_SSET: { + int r; + struct ethtool_cmd ecmd; + if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) + return -EFAULT; + spin_lock_irq(&np->lock); + r = mii_ethtool_sset(&np->mii_if, &ecmd); + spin_unlock_irq(&np->lock); + return r; + } + /* restart autonegotiation */ + case ETHTOOL_NWAY_RST: { + return mii_nway_restart(&np->mii_if); + } + /* get link status */ + case ETHTOOL_GLINK: { + struct ethtool_value edata = {ETHTOOL_GLINK}; + edata.data = mii_link_ok(&np->mii_if); + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + + /* get message-level */ + case ETHTOOL_GMSGLVL: { + struct ethtool_value edata = {ETHTOOL_GMSGLVL}; + edata.data = debug; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + /* set message-level */ + case ETHTOOL_SMSGLVL: { + struct ethtool_value edata; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + debug = edata.data; + return 0; + } + } + + return -EOPNOTSUPP; +} + +static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data; + struct netdev_private *np = dev->priv; + + switch(cmd) { + case SIOCETHTOOL: + return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); + case SIOCGMIIPHY: /* Get address of MII PHY in use. */ + data->phy_id = ((struct netdev_private *)dev->priv)->phys[0] & 0x1f; + /* Fall Through */ + + case SIOCGMIIREG: /* Read MII PHY register. */ + spin_lock_irq(&np->lock); + data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f); + spin_unlock_irq(&np->lock); + return 0; + + case SIOCSMIIREG: /* Write MII PHY register. */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + spin_lock_irq(&np->lock); + mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in); + spin_unlock_irq(&np->lock); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int netdev_close(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + struct netdev_private *np = dev->priv; + + netif_stop_queue(dev); + + if (debug > 1) { + printk(KERN_DEBUG "%s: Shutting down ethercard, status was %8.8x " + "Config %8.8x.\n", dev->name, (int)readl(ioaddr + IntrStatus), + (int)readl(ioaddr + NetworkConfig)); + printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n", + dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx); + } + + /* Stop the chip's Tx and Rx processes. */ + spin_lock_irq(&np->lock); + netif_device_detach(dev); + update_csr6(dev, 0); + writel(0x0000, ioaddr + IntrEnable); + spin_unlock_irq(&np->lock); + + free_irq(dev->irq, dev); + wmb(); + netif_device_attach(dev); + + if (readl(ioaddr + NetworkConfig) != 0xffffffff) + np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; + +#ifdef __i386__ + if (debug > 2) { + int i; + + printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n", + (int)np->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" #%d desc. %4.4x %4.4x %8.8x.\n", + i, np->tx_ring[i].length, + np->tx_ring[i].status, np->tx_ring[i].buffer1); + printk("\n"KERN_DEBUG " Rx ring %8.8x:\n", + (int)np->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) { + printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", + i, np->rx_ring[i].length, + np->rx_ring[i].status, np->rx_ring[i].buffer1); + } + } +#endif /* __i386__ debugging only */ + + del_timer_sync(&np->timer); + + free_rxtx_rings(np); + free_ringdesc(np); + + return 0; +} + +static void __devexit w840_remove1 (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + if (dev) { + unregister_netdev(dev); + pci_release_regions(pdev); +#ifndef USE_IO_OPS + iounmap((char *)(dev->base_addr)); +#endif + kfree(dev); + } + + pci_set_drvdata(pdev, NULL); +} + +#ifdef CONFIG_PM + +/* + * suspend/resume synchronization: + * - open, close, do_ioctl: + * rtnl_lock, & netif_device_detach after the rtnl_unlock. + * - get_stats: + * spin_lock_irq(np->lock), doesn't touch hw if not present + * - hard_start_xmit: + * netif_stop_queue + spin_unlock_wait(&dev->xmit_lock); + * - tx_timeout: + * netif_device_detach + spin_unlock_wait(&dev->xmit_lock); + * - set_multicast_list + * netif_device_detach + spin_unlock_wait(&dev->xmit_lock); + * - interrupt handler + * doesn't touch hw if not present, synchronize_irq waits for + * running instances of the interrupt handler. + * + * Disabling hw requires clearing csr6 & IntrEnable. + * update_csr6 & all function that write IntrEnable check netif_device_present + * before settings any bits. + * + * Detach must occur under spin_unlock_irq(), interrupts from a detached + * device would cause an irq storm. + */ +static int w840_suspend (struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + + rtnl_lock(); + if (netif_running (dev)) { + del_timer_sync(&np->timer); + + spin_lock_irq(&np->lock); + netif_device_detach(dev); + update_csr6(dev, 0); + writel(0, ioaddr + IntrEnable); + netif_stop_queue(dev); + spin_unlock_irq(&np->lock); + + spin_unlock_wait(&dev->xmit_lock); + synchronize_irq(); + + np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; + + /* no more hardware accesses behind this line. */ + + if (np->csr6) BUG(); + if (readl(ioaddr + IntrEnable)) BUG(); + + /* pci_power_off(pdev, -1); */ + + free_rxtx_rings(np); + } else { + netif_device_detach(dev); + } + rtnl_unlock(); + return 0; +} + + +static int w840_resume (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct netdev_private *np = dev->priv; + + rtnl_lock(); + if (netif_device_present(dev)) + goto out; /* device not suspended */ + if (netif_running(dev)) { + pci_enable_device(pdev); + /* pci_power_on(pdev); */ + + spin_lock_irq(&np->lock); + writel(1, dev->base_addr+PCIBusCfg); + readl(dev->base_addr+PCIBusCfg); + udelay(1); + netif_device_attach(dev); + init_rxtx_rings(dev); + init_registers(dev); + spin_unlock_irq(&np->lock); + + netif_wake_queue(dev); + + np->timer.expires = jiffies + 1*HZ; + add_timer(&np->timer); + } else { + netif_device_attach(dev); + } +out: + rtnl_unlock(); + return 0; +} +#endif + +static struct pci_driver w840_driver = { + name: DRV_NAME, + id_table: w840_pci_tbl, + probe: w840_probe1, + remove: __devexit_p(w840_remove1), +#ifdef CONFIG_PM + suspend: w840_suspend, + resume: w840_resume, +#endif +}; + +static int __init w840_init(void) +{ +/* when a module, this is printed whether or not devices are found in probe */ +#ifdef MODULE + printk(version); +#endif + return pci_module_init(&w840_driver); +} + +static void __exit w840_exit(void) +{ + pci_unregister_driver(&w840_driver); +} + +module_init(w840_init); +module_exit(w840_exit); diff -Nru a/drivers/net/tulip/xircom_cb.c b/drivers/net/tulip/xircom_cb.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/tulip/xircom_cb.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,1245 @@ +/* + * xircom_cb: A driver for the (tulip-like) Xircom Cardbus ethernet cards + * + * This software is (C) 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.33 2001/03/19 14:02:07 arjanv Exp $ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#ifdef DEBUG +#define enter(x) printk("Enter: %s, %s line %i\n",x,__FILE__,__LINE__) +#define leave(x) printk("Leave: %s, %s line %i\n",x,__FILE__,__LINE__) +#else +#define enter(x) do {} while (0) +#define leave(x) do {} while (0) +#endif + + +MODULE_DESCRIPTION("Xircom Cardbus ethernet driver"); +MODULE_AUTHOR("Arjan van de Ven "); +MODULE_LICENSE("GPL"); + + + +/* 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 + +static int bufferoffsets[NUMDESCRIPTORS] = {128,2048,4096,6144}; + + +struct xircom_private { + /* Send and receive buffers, kernel-addressable and dma addressable forms */ + + unsigned int *rx_buffer; + unsigned int *tx_buffer; + + dma_addr_t rx_dma_handle; + dma_addr_t tx_dma_handle; + + struct sk_buff *tx_skb[4]; + + unsigned long io_port; + int open; + + /* transmit_used is the rotating counter that indicates which transmit + descriptor has to be used next */ + 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_read_descriptor(struct net_device *dev,struct xircom_private *card, int descnr, unsigned int bufferoffset); +static void investigate_write_descriptor(struct net_device *dev, struct xircom_private *card, 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 void trigger_transmit(struct xircom_private *card); +static void trigger_receive(struct xircom_private *card); +static void setup_descriptors(struct xircom_private *card); +static void remove_descriptors(struct xircom_private *card); +static 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 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, + suspend:NULL, + resume:NULL +}; + + +#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<rx_buffer = pci_alloc_consistent(pdev,8192,&private->rx_dma_handle); + + if (private->rx_buffer == NULL) { + printk(KERN_ERR "xircom_probe: no memory for rx buffer \n"); + kfree(private); + return -ENODEV; + } + private->tx_buffer = pci_alloc_consistent(pdev,8192,&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; + } + dev = init_etherdev(dev, 0); + if (dev == NULL) { + printk(KERN_ERR "xircom_probe: failed to allocate etherdev\n"); + kfree(private->rx_buffer); + kfree(private->tx_buffer); + kfree(private); + return -ENODEV; + } + SET_MODULE_OWNER(dev); + 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; + pdev->driver_data = dev; + + + /* start the transmitter to get a heartbeat */ + /* TODO: send 2 dummy packets here */ + tranceiver_voodoo(private); + + spin_lock_irqsave(&private->lock,flags); + activate_transmitter(private); + activate_receiver(private); + spin_unlock_irqrestore(&private->lock,flags); + + trigger_receive(private); + + leave("xircom_probe"); + 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("xircom_remove"); + if (dev!=NULL) { + card=dev->priv; + if (card!=NULL) { + if (card->rx_buffer!=NULL) + pci_free_consistent(pdev,8192,card->rx_buffer,card->rx_dma_handle); + card->rx_buffer = NULL; + if (card->tx_buffer!=NULL) + pci_free_consistent(pdev,8192,card->tx_buffer,card->tx_dma_handle); + card->tx_buffer = NULL; + } + kfree(card); + } + release_region(dev->base_addr, 128); + unregister_netdev(dev); + kfree(dev); + leave("xircom_remove"); +} + +static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_instance; + struct xircom_private *card = (struct xircom_private *) dev->priv; + unsigned int status; + int i; + + enter("xircom_interrupt\n"); + + spin_lock(&card->lock); + status = inl(card->io_port+CSR5); + +#if DEBUG + print_binary(status); + printk("tx status 0x%08x 0x%08x \n",card->tx_buffer[0],card->tx_buffer[4]); + printk("rx status 0x%08x 0x%08x \n",card->rx_buffer[0],card->rx_buffer[4]); +#endif + + if (link_status_changed(card)) { + int newlink; + printk(KERN_DEBUG "xircom_cb: Link status has changed \n"); + newlink = link_status(card); + printk(KERN_INFO "xircom_cb: Link is %i mbit \n",newlink); + if (newlink) + netif_carrier_on(dev); + else + netif_carrier_off(dev); + + } + + /* Clear all remaining interrupts */ + status |= 0xffffffff; /* FIXME: make this clear only the + real existing bits */ + outl(status,card->io_port+CSR5); + + + for (i=0;ilock); + leave("xircom_interrupt"); +} + +static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct xircom_private *card; + unsigned long flags; + int nextdescriptor; + int desc; + enter("xircom_start_xmit"); + + card = (struct xircom_private*)dev->priv; + spin_lock_irqsave(&card->lock,flags); + + /* First see if we can free some descriptors */ + for (desc=0;desctransmit_used +1) % (NUMDESCRIPTORS); + desc = card->transmit_used; + + /* only send the packet if the descriptor is free */ + if (card->tx_buffer[4*desc]==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]/4],0,1536); + memcpy(&(card->tx_buffer[bufferoffsets[desc]/4]),skb->data,skb->len); + + + /* FIXME: The specification tells us that the length we send HAS to be a multiple of + 4 bytes. */ + + card->tx_buffer[4*desc+1] = skb->len; + if (desc == NUMDESCRIPTORS-1) + card->tx_buffer[4*desc+1] |= (1<<25); /* bit 25: last descriptor of the ring */ + + card->tx_buffer[4*desc+1] |= 0xF0000000; + /* 0xF0... means want interrupts*/ + card->tx_skb[desc] = skb; + + wmb(); + /* This gives the descriptor to the card */ + card->tx_buffer[4*desc] = 0x80000000; + trigger_transmit(card); + if (((int)card->tx_buffer[nextdescriptor*4])<0) { /* next descriptor is occupied... */ + netif_stop_queue(dev); + } + card->transmit_used = nextdescriptor; + leave("xircom-start_xmit - sent"); + spin_unlock_irqrestore(&card->lock,flags); + return 0; + } + + + + /* Uh oh... no free descriptor... drop the packet */ + netif_stop_queue(dev); + spin_unlock_irqrestore(&card->lock,flags); + trigger_transmit(card); + + return -EIO; +} + + + + +static int xircom_open(struct net_device *dev) +{ + struct xircom_private *xp = (struct xircom_private *) dev->priv; + int retval; + enter("xircom_open"); + 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) { + leave("xircom_open - No IRQ"); + return retval; + } + + xircom_up(xp); + xp->open = 1; + leave("xircom_open"); + return 0; +} + +static int xircom_close(struct net_device *dev) +{ + struct xircom_private *card; + unsigned long flags; + + enter("xircom_close"); + card = dev->priv; + netif_stop_queue(dev); /* we don't want 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); + + card->open = 0; + free_irq(dev->irq,dev); + + leave("xircom_close"); + + 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("initialize_card"); + + + 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("initialize_card"); +} + +/* +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 void trigger_transmit(struct xircom_private *card) +{ + unsigned int val; + enter("trigger_transmit"); + + val = 0; + outl(val, card->io_port + CSR1); + + leave("trigger_transmit"); +} + +/* +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 void trigger_receive(struct xircom_private *card) +{ + unsigned int val; + enter("trigger_receive"); + + val = 0; + outl(val, card->io_port + CSR2); + + leave("trigger_receive"); +} + +/* +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; + unsigned int address; + int i; + enter("setup_descriptors"); + + + if (card->rx_buffer == NULL) + BUG(); + if (card->tx_buffer == NULL) + BUG(); + + /* Receive descriptors */ + memset(card->rx_buffer, 0, 128); /* clear the descriptors */ + for (i=0;i 0x80000000 */ + card->rx_buffer[i*4 + 0] = 0x80000000; + /* Rx Descr1: buffer 1 is 1536 bytes, buffer 2 is 0 bytes */ + card->rx_buffer[i*4 + 1] = 1536; + if (i==NUMDESCRIPTORS-1) + card->rx_buffer[i*4 + 1] |= (1 << 25); /* bit 25 is "last descriptor" */ + + /* Rx Descr2: address of the buffer + we store the buffer at the 2nd half of the page */ + + address = (unsigned long) card->rx_dma_handle; + card->rx_buffer[i*4 + 2] = cpu_to_le32(address + bufferoffsets[i]); + /* Rx Desc3: address of 2nd buffer -> 0 */ + card->rx_buffer[i*4 + 3] = 0; + } + + wmb(); + /* Write the receive descriptor ring address to the card */ + address = (unsigned long) card->rx_dma_handle; + val = cpu_to_le32(address); + outl(val, card->io_port + CSR3); /* Receive descr list address */ + + + /* transmit descriptors */ + memset(card->tx_buffer, 0, 128); /* clear the descriptors */ + + for (i=0;i 0x00000000 */ + card->tx_buffer[i*4 + 0] = 0x00000000; + /* Tx Descr1: buffer 1 is 1536 bytes, buffer 2 is 0 bytes */ + card->tx_buffer[i*4 + 1] = 1536; + if (i==NUMDESCRIPTORS-1) + card->tx_buffer[i*4 + 1] |= (1 << 25); /* bit 25 is "last descriptor" */ + + /* Tx Descr2: address of the buffer + we store the buffer at the 2nd half of the page */ + address = (unsigned long) card->tx_dma_handle; + card->tx_buffer[i*4 + 2] = cpu_to_le32(address + bufferoffsets[i]); + /* Tx Desc3: address of 2nd buffer -> 0 */ + card->tx_buffer[i*4 + 3] = 0; + } + + wmb(); + /* wite the transmit descriptor ring to the card */ + address = (unsigned long) card->tx_dma_handle; + val =cpu_to_le32(address); + outl(val, card->io_port + CSR4); /* xmit descr list address */ + + leave("setup_descriptors"); +} + +/* +remove_descriptors informs the card the descriptors are no longer +valid by setting the address in the card to 0x00. +*/ +static void remove_descriptors(struct xircom_private *card) +{ + unsigned int val; + enter("remove_descriptors"); + + val = 0; + outl(val, card->io_port + CSR3); /* Receive descriptor address */ + outl(val, card->io_port + CSR4); /* Send descriptor address */ + + leave("remove_descriptors"); +} + +/* +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 int link_status_changed(struct xircom_private *card) +{ + unsigned int val; + enter("link_status_changed"); + + val = inl(card->io_port + CSR5); /* Status register */ + + if ((val & (1 << 27)) == 0) { /* no change */ + leave("link_status_changed - nochange"); + return 0; + } + + /* clear the event by writing a 1 to the bit in the + status register. */ + val = (1 << 27); + outl(val, card->io_port + CSR5); + + leave("link_status_changed - changed"); + return 1; +} + + +/* +transmit_active returns 1 if the transmitter on the card is +in a non-stopped state. +*/ +static int transmit_active(struct xircom_private *card) +{ + unsigned int val; + enter("transmit_active"); + + val = inl(card->io_port + CSR5); /* Status register */ + + if ((val & (7 << 20)) == 0) { /* transmitter disabled */ + leave("transmit_active - inactive"); + return 0; + } + + leave("transmit_active - active"); + return 1; +} + +/* +receive_active returns 1 if the receiver on the card is +in a non-stopped state. +*/ +static int receive_active(struct xircom_private *card) +{ + unsigned int val; + enter("receive_active"); + + + val = inl(card->io_port + CSR5); /* Status register */ + + if ((val & (7 << 17)) == 0) { /* receiver disabled */ + leave("receive_active - inactive"); + return 0; + } + + leave("receive_active - active"); + 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("activate_receiver"); + + + val = inl(card->io_port + CSR6); /* Operation mode */ + + /* If the "active" bit is set and the receiver is already + active, no need to do the expensive thing */ + if ((val&2) && (receive_active(card))) + return; + + + val = val & ~2; /* 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 | 2; /* 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("activate_receiver"); +} + +/* +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("deactivate_receiver"); + + val = inl(card->io_port + CSR6); /* Operation mode */ + val = val & ~2; /* 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("deactivate_receiver"); +} + + +/* +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("activate_transmitter"); + + + val = inl(card->io_port + CSR6); /* Operation mode */ + + /* If the "active" bit is set and the receiver is already + active, no need to do the expensive thing */ + if ((val&(1<<13)) && (transmit_active(card))) + return; + + val = val & ~(1 << 13); /* 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 | (1 << 13); /* 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("activate_transmitter"); +} + +/* +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("deactivate_transmitter"); + + val = inl(card->io_port + CSR6); /* Operation mode */ + val = val & ~2; /* 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("deactivate_transmitter"); +} + + +/* +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("enable_transmit_interrupt"); + + val = inl(card->io_port + CSR7); /* Interrupt enable register */ + val |= 1; /* enable the transmit interrupt */ + outl(val, card->io_port + CSR7); + + leave("enable_transmit_interrupt"); +} + + +/* +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("enable_receive_interrupt"); + + 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_receive_interrupt"); +} + +/* +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("enable_link_interrupt"); + + val = inl(card->io_port + CSR7); /* Interrupt enable register */ + val = val | (1 << 27); /* enable the link status chage interrupt */ + outl(val, card->io_port + CSR7); + + leave("enable_link_interrupt"); +} + + + +/* +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("enable_all_interrupts"); + + val = 0; /* disable all interrupts */ + outl(val, card->io_port + CSR7); + + leave("disable_all_interrupts"); +} + +/* +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("enable_link_interrupt"); + + 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_link_interrupt"); +} + +/* +enable_promisc starts promisc mode + +must be called with the lock held and interrupts disabled. +*/ +static int enable_promisc(struct xircom_private *card) +{ + unsigned int val; + enter("enable_promisc"); + + val = inl(card->io_port + CSR6); + val = val | (1 << 6); + outl(val, card->io_port + CSR6); + + leave("enable_promisc"); + return 1; +} + + + + +/* +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 int link_status(struct xircom_private *card) +{ + unsigned int val; + enter("link_status"); + + 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("link_status"); + return 0; +} + + + + + +/* + 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("read_mac_address"); + + 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("read_mac_address"); +} + + +/* + 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; + + enter("tranceiver_voodoo"); + + /* disable all powermanagement */ + pci_write_config_dword(card->pdev, PCI_POWERMGMT, 0x0000); + + 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("tranceiver_voodoo"); +} + + +static void xircom_up(struct xircom_private *card) +{ + unsigned long flags; + int i; + + enter("xircom_up"); + + /* disable all powermanagement */ + pci_write_config_dword(card->pdev, PCI_POWERMGMT, 0x0000); + + 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]); + + + spin_unlock_irqrestore(&card->lock, flags); + trigger_receive(card); + trigger_transmit(card); + netif_start_queue(card->dev); + leave("xircom_up"); +} + +/* Bufferoffset is in BYTES */ +static void investigate_read_descriptor(struct net_device *dev,struct xircom_private *card, int descnr, unsigned int bufferoffset) +{ + int status; + + enter("investigate_read_descriptor"); + status = card->rx_buffer[4*descnr]; + + 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, (unsigned char*)&card->rx_buffer[bufferoffset / 4], 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_buffer[4*descnr] = 0x80000000; + trigger_receive(card); + } + + leave("investigate_read_descriptor"); + +} + + +/* Bufferoffset is in BYTES */ +static void investigate_write_descriptor(struct net_device *dev, struct xircom_private *card, int descnr, unsigned int bufferoffset) +{ + int status; + + enter("investigate_write_descriptor"); + + status = card->tx_buffer[4*descnr]; +#if 0 + if (status & 0x8000) { /* Major error */ + printk(KERN_ERR "Major transmit error status %x \n", status); + card->tx_buffer[4*descnr] = 0; + netif_wake_queue (dev); + } +#endif + if (status > 0) { /* bit 31 is 0 when done */ + 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&(1<<8)) + card->stats.collisions++; + card->tx_buffer[4*descnr] = 0; /* descriptor is free again */ + netif_wake_queue (dev); + card->stats.tx_packets++; + } + + leave("investigate_write_descriptor"); + +} + + +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 -Nru a/drivers/net/tulip/xircom_tulip_cb.c b/drivers/net/tulip/xircom_tulip_cb.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/tulip/xircom_tulip_cb.c Thu Mar 7 18:17:39 2002 @@ -0,0 +1,1744 @@ +/* xircom_tulip_cb.c: A Xircom CBE-100 ethernet driver for Linux. */ +/* + Written/copyright 1994-1999 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + ----------------------------------------------------------- + + Linux kernel-specific changes: + + LK1.0 (Ion Badulescu) + - Major cleanup + - Use 2.4 PCI API + - Support ethtool + - Rewrite perfect filter/hash code + - Use interrupts for media changes + + LK1.1 (Ion Badulescu) + - Disallow negotiation of unsupported full-duplex modes +*/ + +#define DRV_NAME "xircom_tulip_cb" +#define DRV_VERSION "0.91+LK1.1" +#define DRV_RELDATE "October 11, 2001" + +#define CARDBUS 1 + +/* A few user-configurable values. */ + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 25; + +#define MAX_UNITS 4 +/* Used to pass the full-duplex flag, etc. */ +static int full_duplex[MAX_UNITS]; +static int options[MAX_UNITS]; +static int mtu[MAX_UNITS]; /* Jumbo MTU for interfaces. */ + +/* Keep the ring sizes a power of two for efficiency. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE 16 +#define RX_RING_SIZE 32 + +/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ +#ifdef __alpha__ +static int rx_copybreak = 1518; +#else +static int rx_copybreak = 100; +#endif + +/* + Set the bus performance register. + Typical: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 No alignment 0x00000000 unlimited 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords + Warning: many older 486 systems are broken and require setting 0x00A04800 + 8 longword cache alignment, 8 longword burst. + ToDo: Non-Intel setting could be better. +*/ + +#if defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) +static int csr0 = 0x01A00000 | 0xE000; +#elif defined(__powerpc__) +static int csr0 = 0x01B00000 | 0x8000; +#elif defined(__sparc__) +static int csr0 = 0x01B00080 | 0x8000; +#elif defined(__i386__) +static int csr0 = 0x01A00000 | 0x8000; +#else +#warning Processor architecture undefined! +static int csr0 = 0x00A00000 | 0x4800; +#endif + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4 * HZ) +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +#define PKT_SETUP_SZ 192 /* Size of the setup frame */ + +/* PCI registers */ +#define PCI_POWERMGMT 0x40 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* Processor type for cache alignment. */ +#include + + +/* These identify the driver base version and may not be removed. */ +static char version[] __devinitdata = +KERN_INFO DRV_NAME ".c derived from tulip.c:v0.91 4/14/99 becker@scyld.com\n" +KERN_INFO " unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE "\n"; + +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("Xircom CBE-100 ethernet driver"); +MODULE_LICENSE("GPL v2"); + +MODULE_PARM(debug, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(csr0, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); + +#define RUN_AT(x) (jiffies + (x)) + +#define xircom_debug debug +#ifdef XIRCOM_DEBUG +static int xircom_debug = XIRCOM_DEBUG; +#else +static int xircom_debug = 1; +#endif + +/* + Theory of Operation + +I. Board Compatibility + +This device driver was forked from the driver for the DECchip "Tulip", +Digital's single-chip ethernet controllers for PCI. It supports Xircom's +almost-Tulip-compatible CBE-100 CardBus adapters. + +II. Board-specific settings + +PCI bus devices are configured by the system at boot time, so no jumpers +need to be set on the board. The system BIOS preferably should assign the +PCI INTA signal to an otherwise unused system IRQ line. + +III. Driver operation + +IIIa. Ring buffers + +The Xircom can use either ring buffers or lists of Tx and Rx descriptors. +This driver uses statically allocated rings of Rx and Tx descriptors, set at +compile time by RX/TX_RING_SIZE. This version of the driver allocates skbuffs +for the Rx ring buffers at open() time and passes the skb->data field to the +Xircom as receive data buffers. When an incoming frame is less than +RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is +copied to the new skbuff. When the incoming frame is larger, the skbuff is +passed directly up the protocol stack and replaced by a newly allocated +skbuff. + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames. For small frames the copying cost is negligible (esp. considering +that we are pre-loading the cache with immediately useful header +information). For large frames the copying cost is non-trivial, and the +larger copy might flush the cache of useful data. A subtle aspect of this +choice is that the Xircom only receives into longword aligned buffers, thus +the IP header at offset 14 isn't longword aligned for further processing. +Copied frames are put into the new skbuff at an offset of "+2", thus copying +has the beneficial effect of aligning the IP header and preloading the +cache. + +IIIC. Synchronization +The driver runs as two independent, single-threaded flows of control. One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag. The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'tp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so +we can't avoid the interrupt overhead by having the Tx routine reap the Tx +stats.) After reaping the stats, it marks the queue entry as empty by setting +the 'base' to zero. Iff the 'tp->tx_full' flag is set, it clears both the +tx_full and tbusy flags. + +IV. Notes + +IVb. References + +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html +http://www.digital.com (search for current 21*4* datasheets and "21X4 SROM") +http://www.national.com/pf/DP/DP83840A.html + +IVc. Errata + +*/ + +/* A full-duplex map for media types. */ +enum MediaIs { + MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, + MediaIs100=16}; +static const char media_cap[] = +{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20 }; + +/* Offsets to the Command and Status Registers, "CSRs". All accesses + must be longword instructions and quadword aligned. */ +enum xircom_offsets { + CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, + CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, + CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x04, }; + +/* The bits in the CSR5 status registers, mostly interrupt sources. */ +enum status_bits { + LinkChange=0x08000000, + NormalIntr=0x10000, NormalIntrMask=0x00014045, + AbnormalIntr=0x8000, AbnormalIntrMask=0x0a00a5a2, + ReservedIntrMask=0xe0001a18, + EarlyRxIntr=0x4000, BusErrorIntr=0x2000, + EarlyTxIntr=0x400, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40, + TxFIFOUnderflow=0x20, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01, +}; + +enum csr0_control_bits { + EnableMWI=0x01000000, EnableMRL=0x00800000, + EnableMRM=0x00200000, EqualBusPrio=0x02, + SoftwareReset=0x01, +}; + +enum csr6_control_bits { + ReceiveAllBit=0x40000000, AllMultiBit=0x80, PromiscBit=0x40, + HashFilterBit=0x01, FullDuplexBit=0x0200, + TxThresh10=0x400000, TxStoreForw=0x200000, + TxThreshMask=0xc000, TxThreshShift=14, + EnableTx=0x2000, EnableRx=0x02, + ReservedZeroMask=0x8d930134, ReservedOneMask=0x320c0000, + EnableTxRx=(EnableTx | EnableRx), +}; + + +enum tbl_flag { + HAS_MII=1, HAS_ACPI=2, +}; +static struct xircom_chip_table { + char *chip_name; + int valid_intrs; /* CSR7 interrupt enable settings */ + int flags; +} xircom_tbl[] = { + { "Xircom Cardbus Adapter", + LinkChange | NormalIntr | AbnormalIntr | BusErrorIntr | + RxDied | RxNoBuf | RxIntr | TxFIFOUnderflow | TxNoBuf | TxDied | TxIntr, + HAS_MII | HAS_ACPI, }, + { NULL, }, +}; +/* This matches the table above. */ +enum chips { + X3201_3, +}; + + +/* The Xircom Rx and Tx buffer descriptors. */ +struct xircom_rx_desc { + s32 status; + s32 length; + u32 buffer1, buffer2; +}; + +struct xircom_tx_desc { + s32 status; + s32 length; + u32 buffer1, buffer2; /* We use only buffer 1. */ +}; + +enum tx_desc0_status_bits { + Tx0DescOwned=0x80000000, Tx0DescError=0x8000, Tx0NoCarrier=0x0800, + Tx0LateColl=0x0200, Tx0ManyColl=0x0100, Tx0Underflow=0x02, +}; +enum tx_desc1_status_bits { + Tx1ComplIntr=0x80000000, Tx1LastSeg=0x40000000, Tx1FirstSeg=0x20000000, + Tx1SetupPkt=0x08000000, Tx1DisableCRC=0x04000000, Tx1RingWrap=0x02000000, + Tx1ChainDesc=0x01000000, Tx1NoPad=0x800000, Tx1HashSetup=0x400000, + Tx1WholePkt=(Tx1FirstSeg | Tx1LastSeg), +}; +enum rx_desc0_status_bits { + Rx0DescOwned=0x80000000, Rx0DescError=0x8000, Rx0NoSpace=0x4000, + Rx0Runt=0x0800, Rx0McastPkt=0x0400, Rx0FirstSeg=0x0200, Rx0LastSeg=0x0100, + Rx0HugeFrame=0x80, Rx0CRCError=0x02, + Rx0WholePkt=(Rx0FirstSeg | Rx0LastSeg), +}; +enum rx_desc1_status_bits { + Rx1RingWrap=0x02000000, Rx1ChainDesc=0x01000000, +}; + +struct xircom_private { + struct xircom_rx_desc rx_ring[RX_RING_SIZE]; + struct xircom_tx_desc tx_ring[TX_RING_SIZE]; + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; +#ifdef CARDBUS + /* The X3201-3 requires 4-byte aligned tx bufs */ + struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE]; +#endif + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + u16 setup_frame[PKT_SETUP_SZ / sizeof(u16)]; /* Pseudo-Tx frame to init address table. */ + int chip_id; + struct net_device_stats stats; + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + unsigned int tx_full:1; /* The Tx queue is full. */ + unsigned int speed100:1; + unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int autoneg:1; + unsigned int default_port:4; /* Last dev->if_port value. */ + unsigned int open:1; + unsigned int csr0; /* CSR0 setting. */ + unsigned int csr6; /* Current CSR6 control settings. */ + u16 to_advertise; /* NWay capabilities advertised. */ + u16 advertising[4]; + signed char phys[4], mii_cnt; /* MII device addresses. */ + int saved_if_port; + struct pci_dev *pdev; + spinlock_t lock; +}; + +static int mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int location, int value); +static void xircom_up(struct net_device *dev); +static void xircom_down(struct net_device *dev); +static int xircom_open(struct net_device *dev); +static void xircom_tx_timeout(struct net_device *dev); +static void xircom_init_ring(struct net_device *dev); +static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int xircom_rx(struct net_device *dev); +static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int xircom_close(struct net_device *dev); +static struct net_device_stats *xircom_get_stats(struct net_device *dev); +static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void set_rx_mode(struct net_device *dev); +static void check_duplex(struct net_device *dev); + + +/* The Xircom cards are picky about when certain bits in CSR6 can be + manipulated. Keith Owens . */ +static void outl_CSR6(u32 newcsr6, long ioaddr) +{ + const int strict_bits = + TxThresh10 | TxStoreForw | TxThreshMask | EnableTxRx | FullDuplexBit; + int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200; + long flags; + save_flags(flags); + cli(); + /* mask out the reserved bits that always read 0 on the Xircom cards */ + newcsr6 &= ~ReservedZeroMask; + /* or in the reserved bits that always read 1 */ + newcsr6 |= ReservedOneMask; + currcsr6 = inl(ioaddr + CSR6); + if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) || + ((currcsr6 & ~EnableTxRx) == 0)) { + outl(newcsr6, ioaddr + CSR6); /* safe */ + restore_flags(flags); + return; + } + /* make sure the transmitter and receiver are stopped first */ + currcsr6 &= ~EnableTxRx; + while (1) { + csr5 = inl(ioaddr + CSR5); + if (csr5 == 0xffffffff) + break; /* cannot read csr5, card removed? */ + csr5_22_20 = csr5 & 0x700000; + csr5_19_17 = csr5 & 0x0e0000; + if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) && + (csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000)) + break; /* both are stopped or suspended */ + if (!--attempts) { + printk(KERN_INFO DRV_NAME ": outl_CSR6 too many attempts," + "csr5=0x%08x\n", csr5); + outl(newcsr6, ioaddr + CSR6); /* unsafe but do it anyway */ + restore_flags(flags); + return; + } + outl(currcsr6, ioaddr + CSR6); + udelay(1); + } + /* now it is safe to change csr6 */ + outl(newcsr6, ioaddr + CSR6); + restore_flags(flags); +} + + +static void __devinit read_mac_address(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + int i, j; + unsigned char tuple, link, data_id, data_count; + + /* Xircom has its address stored in the CIS; + * we access it through the boot rom interface for now + * this might not work, as the CIS is not parsed but I + * (danilo) use the offset I found on my card's CIS !!! + * + * Doug Ledford: I changed this routine around so that it + * walks the CIS memory space, parsing the config items, and + * finds the proper lan_node_id tuple and uses the data + * stored there. + */ + outl(1 << 12, ioaddr + CSR9); /* enable boot rom access */ + for (i = 0x100; i < 0x1f7; i += link+2) { + outl(i, ioaddr + CSR10); + tuple = inl(ioaddr + CSR9) & 0xff; + outl(i + 1, ioaddr + CSR10); + link = inl(ioaddr + CSR9) & 0xff; + outl(i + 2, ioaddr + CSR10); + data_id = inl(ioaddr + CSR9) & 0xff; + outl(i + 3, ioaddr + CSR10); + data_count = inl(ioaddr + 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, ioaddr + CSR10); + dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff; + } + break; + } else if (link == 0) { + break; + } + } +} + + +/* + * locate the MII interfaces and initialize them. + * we disable full-duplex modes here, + * because we don't know how to handle them. + */ +static void find_mii_transceivers(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + int phy, phy_idx; + + if (media_cap[tp->default_port] & MediaIsMII) { + u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 }; + tp->to_advertise = media2advert[tp->default_port - 9]; + } else + tp->to_advertise = + /*ADVERTISE_100BASE4 | ADVERTISE_100FULL |*/ ADVERTISE_100HALF | + /*ADVERTISE_10FULL |*/ ADVERTISE_10HALF | ADVERTISE_CSMA; + + /* Find the connected MII xcvrs. + Doing this in open() would allow detecting external xcvrs later, + but takes much time. */ + for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { + int mii_status = mdio_read(dev, phy, MII_BMSR); + if ((mii_status & (BMSR_100BASE4 | BMSR_100HALF | BMSR_10HALF)) == BMSR_100BASE4 || + ((mii_status & BMSR_100BASE4) == 0 && + (mii_status & (BMSR_100FULL | BMSR_100HALF | BMSR_10FULL | BMSR_10HALF)) != 0)) { + int mii_reg0 = mdio_read(dev, phy, MII_BMCR); + int mii_advert = mdio_read(dev, phy, MII_ADVERTISE); + int reg4 = ((mii_status >> 6) & tp->to_advertise) | ADVERTISE_CSMA; + tp->phys[phy_idx] = phy; + tp->advertising[phy_idx++] = reg4; + printk(KERN_INFO "%s: MII transceiver #%d " + "config %4.4x status %4.4x advertising %4.4x.\n", + dev->name, phy, mii_reg0, mii_status, mii_advert); + } + } + tp->mii_cnt = phy_idx; + if (phy_idx == 0) { + printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n", + dev->name); + tp->phys[0] = 0; + } +} + + +/* + * To quote Arjan van de Ven: + * 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. + * Actually it seems to go like this: + * - GPIO2 enables the MII itself so we can talk to it. The MII gets reset + * so any prior MII settings are lost. + * - GPIO0 enables the TP port so the MII can talk to the network. + * - a software reset will reset both GPIO pins. + * I also moved the software reset here, because doing it in xircom_up() + * required enabling the GPIO pins each time, which reset the MII each time. + * Thus we couldn't control the MII -- which sucks because we don't know + * how to handle full-duplex modes so we *must* disable them. + */ +static void transceiver_voodoo(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + long ioaddr = dev->base_addr; + + /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ + outl(SoftwareReset, ioaddr + CSR0); + udelay(2); + + /* Deassert reset. */ + outl(tp->csr0, ioaddr + CSR0); + + /* Reset the xcvr interface and turn on heartbeat. */ + outl(0x0008, ioaddr + CSR15); + udelay(5); /* The delays are Xircom-recommended to give the + * chipset time to reset the actual hardware + * on the PCMCIA card + */ + outl(0xa8050000, ioaddr + CSR15); + udelay(5); + outl(0xa00f0000, ioaddr + CSR15); + udelay(5); + + outl_CSR6(0, ioaddr); + //outl_CSR6(FullDuplexBit, ioaddr); +} + + +static int __devinit xircom_init_one(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct net_device *dev; + struct xircom_private *tp; + static int board_idx = -1; + int chip_idx = id->driver_data; + long ioaddr; + int i; + u8 chip_rev; + +/* 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 + + //printk(KERN_INFO "xircom_init_one(%s)\n", pdev->slot_name); + + board_idx++; + + if (pci_enable_device(pdev)) + return -ENODEV; + + pci_set_master(pdev); + + ioaddr = pci_resource_start(pdev, 0); + dev = alloc_etherdev(sizeof(*tp)); + if (!dev) { + printk (KERN_ERR DRV_NAME "%d: cannot alloc etherdev, aborting\n", board_idx); + return -ENOMEM; + } + SET_MODULE_OWNER(dev); + + dev->base_addr = ioaddr; + dev->irq = pdev->irq; + + if (pci_request_regions(pdev, dev->name)) { + printk (KERN_ERR DRV_NAME " %d: cannot reserve PCI resources, aborting\n", board_idx); + goto err_out_free_netdev; + } + + /* Bring the chip out of sleep mode. + Caution: Snooze mode does not work with some boards! */ + if (xircom_tbl[chip_idx].flags & HAS_ACPI) + pci_write_config_dword(pdev, PCI_POWERMGMT, 0); + + /* Stop the chip's Tx and Rx processes. */ + outl_CSR6(inl(ioaddr + CSR6) & ~EnableTxRx, ioaddr); + /* Clear the missed-packet counter. */ + (volatile int)inl(ioaddr + CSR8); + + tp = dev->priv; + + tp->lock = SPIN_LOCK_UNLOCKED; + tp->pdev = pdev; + tp->chip_id = chip_idx; + /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles. */ + /* XXX: is this necessary for Xircom? */ + tp->csr0 = csr0 & ~EnableMWI; + + pci_set_drvdata(pdev, dev); + + /* The lower four bits are the media type. */ + if (board_idx >= 0 && board_idx < MAX_UNITS) { + tp->default_port = options[board_idx] & 15; + if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0) + tp->full_duplex = 1; + if (mtu[board_idx] > 0) + dev->mtu = mtu[board_idx]; + } + if (dev->mem_start) + tp->default_port = dev->mem_start; + if (tp->default_port) { + if (media_cap[tp->default_port] & MediaAlwaysFD) + tp->full_duplex = 1; + } + if (tp->full_duplex) + tp->autoneg = 0; + else + tp->autoneg = 1; + tp->speed100 = 1; + + /* The Xircom-specific entries in the device structure. */ + dev->open = &xircom_open; + dev->hard_start_xmit = &xircom_start_xmit; + dev->stop = &xircom_close; + dev->get_stats = &xircom_get_stats; + dev->do_ioctl = &xircom_ioctl; +#ifdef HAVE_MULTICAST + dev->set_multicast_list = &set_rx_mode; +#endif + dev->tx_timeout = xircom_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + + transceiver_voodoo(dev); + + read_mac_address(dev); + + if (register_netdev(dev)) + goto err_out_cleardev; + + pci_read_config_byte(pdev, PCI_REVISION_ID, &chip_rev); + printk(KERN_INFO "%s: %s rev %d at %#3lx,", + dev->name, xircom_tbl[chip_idx].chip_name, chip_rev, ioaddr); + for (i = 0; i < 6; i++) + printk("%c%2.2X", i ? ':' : ' ', dev->dev_addr[i]); + printk(", IRQ %d.\n", dev->irq); + + if (xircom_tbl[chip_idx].flags & HAS_MII) { + find_mii_transceivers(dev); + check_duplex(dev); + } + + return 0; + +err_out_cleardev: + pci_set_drvdata(pdev, NULL); + pci_release_regions(pdev); +err_out_free_netdev: + unregister_netdev(dev); + kfree(dev); + return -ENODEV; +} + + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. */ + +/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back PCI I/O cycles, but we insert a delay to avoid + "overclocking" issues or future 66Mhz PCI. */ +#define mdio_delay() inl(mdio_addr) + +/* Read and write the MII registers using software-generated serial + MDIO protocol. It is just different enough from the EEPROM protocol + to not share code. The maxium data clock rate is 2.5 Mhz. */ +#define MDIO_SHIFT_CLK 0x10000 +#define MDIO_DATA_WRITE0 0x00000 +#define MDIO_DATA_WRITE1 0x20000 +#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */ +#define MDIO_ENB_IN 0x40000 +#define MDIO_DATA_READ 0x80000 + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ + int i; + int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int retval = 0; + long ioaddr = dev->base_addr; + long mdio_addr = ioaddr + CSR9; + + /* Establish sync by sending at least 32 logic ones. */ + for (i = 32; i >= 0; i--) { + outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + + outl(MDIO_ENB | dataval, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + outl(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + return (retval>>1) & 0xffff; +} + + +static void mdio_write(struct net_device *dev, int phy_id, int location, int value) +{ + int i; + int cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; + long ioaddr = dev->base_addr; + long mdio_addr = ioaddr + CSR9; + + /* Establish sync by sending 32 logic ones. */ + for (i = 32; i >= 0; i--) { + outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + outl(MDIO_ENB | dataval, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + outl(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + return; +} + + +static void +xircom_up(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + long ioaddr = dev->base_addr; + int i; + + /* Clear the tx ring */ + for (i = 0; i < TX_RING_SIZE; i++) { + tp->tx_skbuff[i] = 0; + tp->tx_ring[i].status = 0; + } + + if (xircom_debug > 1) + printk(KERN_DEBUG "%s: xircom_up() irq %d.\n", dev->name, dev->irq); + + outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3); + outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); + + tp->saved_if_port = dev->if_port; + if (dev->if_port == 0) + dev->if_port = tp->default_port; + + tp->csr6 = TxThresh10 /*| FullDuplexBit*/; /* XXX: why 10 and not 100? */ + + set_rx_mode(dev); + + /* Start the chip's Tx to process setup frame. */ + outl_CSR6(tp->csr6, ioaddr); + outl_CSR6(tp->csr6 | EnableTx, ioaddr); + + /* Acknowledge all outstanding interrupts sources */ + outl(xircom_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5); + /* Enable interrupts by setting the interrupt mask. */ + outl(xircom_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); + /* Enable Rx */ + outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); + /* Rx poll demand */ + outl(0, ioaddr + CSR2); + + /* Tell the net layer we're ready */ + netif_start_queue (dev); + + if (xircom_debug > 2) { + printk(KERN_DEBUG "%s: Done xircom_up(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n", + dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5), + inl(ioaddr + CSR6)); + } +} + + +static int +xircom_open(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + + if (request_irq(dev->irq, &xircom_interrupt, SA_SHIRQ, dev->name, dev)) + return -EAGAIN; + + xircom_init_ring(dev); + + xircom_up(dev); + tp->open = 1; + + return 0; +} + + +static void xircom_tx_timeout(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + long ioaddr = dev->base_addr; + + if (media_cap[dev->if_port] & MediaIsMII) { + /* Do nothing -- the media monitor should handle this. */ + if (xircom_debug > 1) + printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", + dev->name); + } + +#if defined(way_too_many_messages) + if (xircom_debug > 3) { + int i; + for (i = 0; i < RX_RING_SIZE; i++) { + u8 *buf = (u8 *)(tp->rx_ring[i].buffer1); + int j; + printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x " + "%2.2x %2.2x %2.2x.\n", + i, (unsigned int)tp->rx_ring[i].status, + (unsigned int)tp->rx_ring[i].length, + (unsigned int)tp->rx_ring[i].buffer1, + (unsigned int)tp->rx_ring[i].buffer2, + buf[0], buf[1], buf[2]); + for (j = 0; buf[j] != 0xee && j < 1600; j++) + if (j < 100) printk(" %2.2x", buf[j]); + printk(" j=%d.\n", j); + } + printk(KERN_DEBUG " Rx ring %8.8x: ", (int)tp->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); + printk("\n" KERN_DEBUG " Tx ring %8.8x: ", (int)tp->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); + printk("\n"); + } +#endif + + /* Stop and restart the chip's Tx/Rx processes . */ + outl_CSR6(tp->csr6 | EnableRx, ioaddr); + outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); + + dev->trans_start = jiffies; + netif_wake_queue (dev); + tp->stats.tx_errors++; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void xircom_init_ring(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + int i; + + tp->tx_full = 0; + tp->cur_rx = tp->cur_tx = 0; + tp->dirty_rx = tp->dirty_tx = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + tp->rx_ring[i].status = 0; + tp->rx_ring[i].length = PKT_BUF_SZ; + tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]); + tp->rx_skbuff[i] = NULL; + } + /* Mark the last entry as wrapping the ring. */ + tp->rx_ring[i-1].length = PKT_BUF_SZ | Rx1RingWrap; + tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]); + + for (i = 0; i < RX_RING_SIZE; i++) { + /* Note the receive buffer must be longword aligned. + dev_alloc_skb() provides 16 byte alignment. But do *not* + use skb_reserve() to align the IP header! */ + struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ); + tp->rx_skbuff[i] = skb; + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + tp->rx_ring[i].status = Rx0DescOwned; /* Owned by Xircom chip */ + tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail); + } + tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + + /* The Tx buffer descriptor is filled in as needed, but we + do need to clear the ownership bit. */ + for (i = 0; i < TX_RING_SIZE; i++) { + tp->tx_skbuff[i] = 0; + tp->tx_ring[i].status = 0; + tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]); +#ifdef CARDBUS + if (tp->chip_id == X3201_3) + tp->tx_aligned_skbuff[i] = dev_alloc_skb(PKT_BUF_SZ); +#endif /* CARDBUS */ + } + tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]); +} + + +static int +xircom_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + int entry; + u32 flag; + + /* Caution: the write order is important here, set the base address + with the "ownership" bits last. */ + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % TX_RING_SIZE; + + tp->tx_skbuff[entry] = skb; +#ifdef CARDBUS + if (tp->chip_id == X3201_3) { + memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len); + tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data); + } else +#endif + tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); + + if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ + flag = Tx1WholePkt; /* No interrupt */ + } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { + flag = Tx1WholePkt | Tx1ComplIntr; /* Tx-done intr. */ + } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { + flag = Tx1WholePkt; /* No Tx-done intr. */ + } else { + /* Leave room for set_rx_mode() to fill entries. */ + flag = Tx1WholePkt | Tx1ComplIntr; /* Tx-done intr. */ + tp->tx_full = 1; + } + if (entry == TX_RING_SIZE - 1) + flag |= Tx1WholePkt | Tx1ComplIntr | Tx1RingWrap; + + tp->tx_ring[entry].length = skb->len | flag; + tp->tx_ring[entry].status = Tx0DescOwned; /* Pass ownership to the chip. */ + tp->cur_tx++; + if (tp->tx_full) + netif_stop_queue (dev); + else + netif_wake_queue (dev); + + /* Trigger an immediate transmit demand. */ + outl(0, dev->base_addr + CSR1); + + dev->trans_start = jiffies; + + return 0; +} + + +static void xircom_media_change(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + long ioaddr = dev->base_addr; + u16 reg0, reg1, reg4, reg5; + u32 csr6 = inl(ioaddr + CSR6), newcsr6; + + /* reset status first */ + mdio_read(dev, tp->phys[0], MII_BMCR); + mdio_read(dev, tp->phys[0], MII_BMSR); + + reg0 = mdio_read(dev, tp->phys[0], MII_BMCR); + reg1 = mdio_read(dev, tp->phys[0], MII_BMSR); + + if (reg1 & BMSR_LSTATUS) { + /* link is up */ + if (reg0 & BMCR_ANENABLE) { + /* autonegotiation is enabled */ + reg4 = mdio_read(dev, tp->phys[0], MII_ADVERTISE); + reg5 = mdio_read(dev, tp->phys[0], MII_LPA); + if (reg4 & ADVERTISE_100FULL && reg5 & LPA_100FULL) { + tp->speed100 = 1; + tp->full_duplex = 1; + } else if (reg4 & ADVERTISE_100HALF && reg5 & LPA_100HALF) { + tp->speed100 = 1; + tp->full_duplex = 0; + } else if (reg4 & ADVERTISE_10FULL && reg5 & LPA_10FULL) { + tp->speed100 = 0; + tp->full_duplex = 1; + } else { + tp->speed100 = 0; + tp->full_duplex = 0; + } + } else { + /* autonegotiation is disabled */ + if (reg0 & BMCR_SPEED100) + tp->speed100 = 1; + else + tp->speed100 = 0; + if (reg0 & BMCR_FULLDPLX) + tp->full_duplex = 1; + else + tp->full_duplex = 0; + } + printk(KERN_DEBUG "%s: Link is up, running at %sMbit %s-duplex\n", + dev->name, + tp->speed100 ? "100" : "10", + tp->full_duplex ? "full" : "half"); + newcsr6 = csr6 & ~FullDuplexBit; + if (tp->full_duplex) + newcsr6 |= FullDuplexBit; + if (newcsr6 != csr6) + outl_CSR6(newcsr6, ioaddr + CSR6); + } else { + printk(KERN_DEBUG "%s: Link is down\n", dev->name); + } +} + + +static void check_duplex(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + u16 reg0; + + mdio_write(dev, tp->phys[0], MII_BMCR, BMCR_RESET); + udelay(500); + while (mdio_read(dev, tp->phys[0], MII_BMCR) & BMCR_RESET); + + reg0 = mdio_read(dev, tp->phys[0], MII_BMCR); + mdio_write(dev, tp->phys[0], MII_ADVERTISE, tp->advertising[0]); + + if (tp->autoneg) { + reg0 &= ~(BMCR_SPEED100 | BMCR_FULLDPLX); + reg0 |= BMCR_ANENABLE | BMCR_ANRESTART; + } else { + reg0 &= ~(BMCR_ANENABLE | BMCR_ANRESTART); + if (tp->speed100) + reg0 |= BMCR_SPEED100; + if (tp->full_duplex) + reg0 |= BMCR_FULLDPLX; + printk(KERN_DEBUG "%s: Link forced to %sMbit %s-duplex\n", + dev->name, + tp->speed100 ? "100" : "10", + tp->full_duplex ? "full" : "half"); + } + mdio_write(dev, tp->phys[0], MII_BMCR, reg0); +} + + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct net_device *dev = dev_instance; + struct xircom_private *tp = dev->priv; + long ioaddr = dev->base_addr; + int csr5, work_budget = max_interrupt_work; + + spin_lock (&tp->lock); + + do { + csr5 = inl(ioaddr + CSR5); + /* Acknowledge all of the current interrupt sources ASAP. */ + outl(csr5 & 0x0001ffff, ioaddr + CSR5); + + if (xircom_debug > 4) + printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", + dev->name, csr5, inl(dev->base_addr + CSR5)); + + if (csr5 == 0xffffffff) + break; /* all bits set, assume PCMCIA card removed */ + + if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) + break; + + if (csr5 & (RxIntr | RxNoBuf)) + work_budget -= xircom_rx(dev); + + if (csr5 & (TxNoBuf | TxDied | TxIntr)) { + unsigned int dirty_tx; + + for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; + dirty_tx++) { + int entry = dirty_tx % TX_RING_SIZE; + int status = tp->tx_ring[entry].status; + + if (status < 0) + break; /* It still hasn't been Txed */ + /* Check for Rx filter setup frames. */ + if (tp->tx_skbuff[entry] == NULL) + continue; + + if (status & Tx0DescError) { + /* There was an major error, log it. */ +#ifndef final_version + if (xircom_debug > 1) + printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", + dev->name, status); +#endif + tp->stats.tx_errors++; + if (status & Tx0ManyColl) { + tp->stats.tx_aborted_errors++; +#ifdef ETHER_STATS + tp->stats.collisions16++; +#endif + } + if (status & Tx0NoCarrier) tp->stats.tx_carrier_errors++; + if (status & Tx0LateColl) tp->stats.tx_window_errors++; + if (status & Tx0Underflow) tp->stats.tx_fifo_errors++; + } else { + tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff; + tp->stats.collisions += (status >> 3) & 15; + tp->stats.tx_packets++; + } + + /* Free the original skb. */ + dev_kfree_skb_irq(tp->tx_skbuff[entry]); + tp->tx_skbuff[entry] = 0; + } + +#ifndef final_version + if (tp->cur_tx - dirty_tx > TX_RING_SIZE) { + printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dev->name, dirty_tx, tp->cur_tx, tp->tx_full); + dirty_tx += TX_RING_SIZE; + } +#endif + + if (tp->tx_full && + tp->cur_tx - dirty_tx < TX_RING_SIZE - 2) + /* The ring is no longer full */ + tp->tx_full = 0; + + if (tp->tx_full) + netif_stop_queue (dev); + else + netif_wake_queue (dev); + + tp->dirty_tx = dirty_tx; + if (csr5 & TxDied) { + if (xircom_debug > 2) + printk(KERN_WARNING "%s: The transmitter stopped." + " CSR5 is %x, CSR6 %x, new CSR6 %x.\n", + dev->name, csr5, inl(ioaddr + CSR6), tp->csr6); + outl_CSR6(tp->csr6 | EnableRx, ioaddr); + outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); + } + } + + /* Log errors. */ + if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */ + if (csr5 & LinkChange) + xircom_media_change(dev); + if (csr5 & TxFIFOUnderflow) { + if ((tp->csr6 & TxThreshMask) != TxThreshMask) + tp->csr6 += (1 << TxThreshShift); /* Bump up the Tx threshold */ + else + tp->csr6 |= TxStoreForw; /* Store-n-forward. */ + /* Restart the transmit process. */ + outl_CSR6(tp->csr6 | EnableRx, ioaddr); + outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); + } + if (csr5 & RxDied) { /* Missed a Rx frame. */ + tp->stats.rx_errors++; + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); + } + /* Clear all error sources, included undocumented ones! */ + outl(0x0800f7ba, ioaddr + CSR5); + } + if (--work_budget < 0) { + if (xircom_debug > 1) + printk(KERN_WARNING "%s: Too much work during an interrupt, " + "csr5=0x%8.8x.\n", dev->name, csr5); + /* Acknowledge all interrupt sources. */ + outl(0x8001ffff, ioaddr + CSR5); + break; + } + } while (1); + + if (xircom_debug > 3) + printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", + dev->name, inl(ioaddr + CSR5)); + + spin_unlock (&tp->lock); +} + + +static int +xircom_rx(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + int entry = tp->cur_rx % RX_RING_SIZE; + int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; + int work_done = 0; + + if (xircom_debug > 4) + printk(KERN_DEBUG " In xircom_rx(), entry %d %8.8x.\n", entry, + tp->rx_ring[entry].status); + /* If we own the next entry, it's a new packet. Send it up. */ + while (tp->rx_ring[entry].status >= 0) { + s32 status = tp->rx_ring[entry].status; + + if (xircom_debug > 5) + printk(KERN_DEBUG " In xircom_rx(), entry %d %8.8x.\n", entry, + tp->rx_ring[entry].status); + if (--rx_work_limit < 0) + break; + if ((status & 0x38008300) != 0x0300) { + if ((status & 0x38000300) != 0x0300) { + /* Ignore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + if (xircom_debug > 1) + printk(KERN_WARNING "%s: Oversized Ethernet frame " + "spanned multiple buffers, status %8.8x!\n", + dev->name, status); + tp->stats.rx_length_errors++; + } + } else if (status & Rx0DescError) { + /* There was a fatal error. */ + if (xircom_debug > 2) + printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", + dev->name, status); + tp->stats.rx_errors++; /* end of a packet.*/ + if (status & (Rx0Runt | Rx0HugeFrame)) tp->stats.rx_length_errors++; + if (status & Rx0CRCError) tp->stats.rx_crc_errors++; + } + } else { + /* Omit the four octet CRC from the length. */ + short pkt_len = ((status >> 16) & 0x7ff) - 4; + struct sk_buff *skb; + +#ifndef final_version + if (pkt_len > 1518) { + printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n", + dev->name, pkt_len, pkt_len); + pkt_len = 1518; + tp->stats.rx_length_errors++; + } +#endif + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ + if (pkt_len < rx_copybreak + && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align the IP header */ +#if ! defined(__alpha__) + eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), + pkt_len, 0); + skb_put(skb, pkt_len); +#else + memcpy(skb_put(skb, pkt_len), + bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); +#endif + work_done++; + } else { /* Pass up the skb already on the Rx ring. */ + skb_put(skb = tp->rx_skbuff[entry], pkt_len); + tp->rx_skbuff[entry] = NULL; + } + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + tp->stats.rx_packets++; + tp->stats.rx_bytes += pkt_len; + } + entry = (++tp->cur_rx) % RX_RING_SIZE; + } + + /* Refill the Rx ring buffers. */ + for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { + entry = tp->dirty_rx % RX_RING_SIZE; + if (tp->rx_skbuff[entry] == NULL) { + struct sk_buff *skb; + skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ); + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail); + work_done++; + } + tp->rx_ring[entry].status = Rx0DescOwned; + } + + return work_done; +} + + +static void +xircom_down(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + struct xircom_private *tp = dev->priv; + + /* Disable interrupts by clearing the interrupt mask. */ + outl(0, ioaddr + CSR7); + /* Stop the chip's Tx and Rx processes. */ + outl_CSR6(inl(ioaddr + CSR6) & ~EnableTxRx, ioaddr); + + if (inl(ioaddr + CSR6) != 0xffffffff) + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + + dev->if_port = tp->saved_if_port; +} + + +static int +xircom_close(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + struct xircom_private *tp = dev->priv; + int i; + + if (xircom_debug > 1) + printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, inl(ioaddr + CSR5)); + + netif_stop_queue(dev); + + if (netif_device_present(dev)) + xircom_down(dev); + + free_irq(dev->irq, dev); + + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = tp->rx_skbuff[i]; + tp->rx_skbuff[i] = 0; + tp->rx_ring[i].status = 0; /* Not owned by Xircom chip. */ + tp->rx_ring[i].length = 0; + tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */ + if (skb) { + dev_kfree_skb(skb); + } + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (tp->tx_skbuff[i]) + dev_kfree_skb(tp->tx_skbuff[i]); + tp->tx_skbuff[i] = 0; + } + + tp->open = 0; + return 0; +} + + +static struct net_device_stats *xircom_get_stats(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + long ioaddr = dev->base_addr; + + if (netif_device_present(dev)) + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + + return &tp->stats; +} + + +static int xircom_ethtool_ioctl(struct net_device *dev, void *useraddr) +{ + struct ethtool_cmd ecmd; + struct xircom_private *tp = dev->priv; + + if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) + return -EFAULT; + + switch (ecmd.cmd) { + case ETHTOOL_GSET: + ecmd.supported = + SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_MII; + + ecmd.advertising = ADVERTISED_MII; + if (tp->advertising[0] & ADVERTISE_10HALF) + ecmd.advertising |= ADVERTISED_10baseT_Half; + if (tp->advertising[0] & ADVERTISE_10FULL) + ecmd.advertising |= ADVERTISED_10baseT_Full; + if (tp->advertising[0] & ADVERTISE_100HALF) + ecmd.advertising |= ADVERTISED_100baseT_Half; + if (tp->advertising[0] & ADVERTISE_100FULL) + ecmd.advertising |= ADVERTISED_100baseT_Full; + if (tp->autoneg) { + ecmd.advertising |= ADVERTISED_Autoneg; + ecmd.autoneg = AUTONEG_ENABLE; + } else + ecmd.autoneg = AUTONEG_DISABLE; + + ecmd.port = PORT_MII; + ecmd.transceiver = XCVR_INTERNAL; + ecmd.phy_address = tp->phys[0]; + ecmd.speed = tp->speed100 ? SPEED_100 : SPEED_10; + ecmd.duplex = tp->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + ecmd.maxtxpkt = TX_RING_SIZE / 2; + ecmd.maxrxpkt = 0; + + if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + + case ETHTOOL_SSET: { + u16 autoneg, speed100, full_duplex; + + autoneg = (ecmd.autoneg == AUTONEG_ENABLE); + speed100 = (ecmd.speed == SPEED_100); + full_duplex = (ecmd.duplex == DUPLEX_FULL); + + tp->autoneg = autoneg; + if (speed100 != tp->speed100 || + full_duplex != tp->full_duplex) { + tp->speed100 = speed100; + tp->full_duplex = full_duplex; + /* change advertising bits */ + tp->advertising[0] &= ~(ADVERTISE_10HALF | + ADVERTISE_10FULL | + ADVERTISE_100HALF | + ADVERTISE_100FULL | + ADVERTISE_100BASE4); + if (speed100) { + if (full_duplex) + tp->advertising[0] |= ADVERTISE_100FULL; + else + tp->advertising[0] |= ADVERTISE_100HALF; + } else { + if (full_duplex) + tp->advertising[0] |= ADVERTISE_10FULL; + else + tp->advertising[0] |= ADVERTISE_10HALF; + } + } + check_duplex(dev); + return 0; + } + + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info; + memset(&info, 0, sizeof(info)); + info.cmd = ecmd.cmd; + strcpy(info.driver, DRV_NAME); + strcpy(info.version, DRV_VERSION); + *info.fw_version = 0; + strcpy(info.bus_info, tp->pdev->slot_name); + if (copy_to_user(useraddr, &info, sizeof(info))) + return -EFAULT; + return 0; + } + + default: + return -EOPNOTSUPP; + } +} + + +/* Provide ioctl() calls to examine the MII xcvr state. */ +static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct xircom_private *tp = dev->priv; + u16 *data = (u16 *)&rq->ifr_data; + int phy = tp->phys[0] & 0x1f; + long flags; + + switch(cmd) { + case SIOCETHTOOL: + return xircom_ethtool_ioctl(dev, (void *) rq->ifr_data); + + /* Legacy mii-diag interface */ + case SIOCGMIIPHY: /* Get address of MII PHY in use. */ + if (tp->mii_cnt) + data[0] = phy; + else + return -ENODEV; + return 0; + case SIOCGMIIREG: /* Read MII PHY register. */ + save_flags(flags); + cli(); + data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); + restore_flags(flags); + return 0; + case SIOCSMIIREG: /* Write MII PHY register. */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + save_flags(flags); + cli(); + if (data[0] == tp->phys[0]) { + u16 value = data[2]; + switch (data[1]) { + case 0: + if (value & (BMCR_RESET | BMCR_ANENABLE)) + /* Autonegotiation. */ + tp->autoneg = 1; + else { + tp->full_duplex = (value & BMCR_FULLDPLX) ? 1 : 0; + tp->autoneg = 0; + } + break; + case 4: + tp->advertising[0] = value; + break; + } + check_duplex(dev); + } + mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); + restore_flags(flags); + return 0; + default: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} + +/* Set or clear the multicast filter for this adaptor. + Note that we only use exclusion around actually queueing the + new frame, not around filling tp->setup_frame. This is non-deterministic + when re-entered but still correct. */ +static void set_rx_mode(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + struct dev_mc_list *mclist; + long ioaddr = dev->base_addr; + int csr6 = inl(ioaddr + CSR6); + u16 *eaddrs, *setup_frm; + u32 tx_flags; + int i; + + tp->csr6 &= ~(AllMultiBit | PromiscBit | HashFilterBit); + csr6 &= ~(AllMultiBit | PromiscBit | HashFilterBit); + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + tp->csr6 |= PromiscBit; + csr6 |= PromiscBit; + goto out; + } + + if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter well -- accept all multicasts. */ + tp->csr6 |= AllMultiBit; + csr6 |= AllMultiBit; + goto out; + } + + tx_flags = Tx1WholePkt | Tx1SetupPkt | PKT_SETUP_SZ; + + /* Note that only the low-address shortword of setup_frame is valid! */ + setup_frm = tp->setup_frame; + mclist = dev->mc_list; + + /* Fill the first entry with our physical address. */ + eaddrs = (u16 *)dev->dev_addr; + *setup_frm = cpu_to_le16(eaddrs[0]); setup_frm += 2; + *setup_frm = cpu_to_le16(eaddrs[1]); setup_frm += 2; + *setup_frm = cpu_to_le16(eaddrs[2]); setup_frm += 2; + + if (dev->mc_count > 14) { /* Must use a multicast hash table. */ + u32 *hash_table = (u32 *)(tp->setup_frame + 4 * 12); + u32 hash, hash2; + + tx_flags |= Tx1HashSetup; + tp->csr6 |= HashFilterBit; + csr6 |= HashFilterBit; + + /* Fill the unused 3 entries with the broadcast address. + At least one entry *must* contain the broadcast address!!!*/ + for (i = 0; i < 3; i++) { + *setup_frm = 0xffff; setup_frm += 2; + *setup_frm = 0xffff; setup_frm += 2; + *setup_frm = 0xffff; setup_frm += 2; + } + + /* Truly brain-damaged hash filter layout */ + /* XXX: not sure if I should take the last or the first 9 bits */ + for (i = 0; i < dev->mc_count; i++, mclist = mclist->next) { + u32 *hptr; + hash = ether_crc(ETH_ALEN, mclist->dmi_addr) & 0x1ff; + if (hash < 384) { + hash2 = hash + ((hash >> 4) << 4) + + ((hash >> 5) << 5); + } else { + hash -= 384; + hash2 = 64 + hash + (hash >> 4) * 80; + } + hptr = &hash_table[hash2 & ~0x1f]; + *hptr |= cpu_to_le32(1 << (hash2 & 0x1f)); + } + } else { + /* We have <= 14 mcast addresses so we can use Xircom's + wonderful 16-address perfect filter. */ + for (i = 0; i < dev->mc_count; i++, mclist = mclist->next) { + eaddrs = (u16 *)mclist->dmi_addr; + *setup_frm = cpu_to_le16(eaddrs[0]); setup_frm += 2; + *setup_frm = cpu_to_le16(eaddrs[1]); setup_frm += 2; + *setup_frm = cpu_to_le16(eaddrs[2]); setup_frm += 2; + } + /* Fill the unused entries with the broadcast address. + At least one entry *must* contain the broadcast address!!!*/ + for (; i < 15; i++) { + *setup_frm = 0xffff; setup_frm += 2; + *setup_frm = 0xffff; setup_frm += 2; + *setup_frm = 0xffff; setup_frm += 2; + } + } + + /* Now add this frame to the Tx list. */ + if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { + /* Same setup recently queued, we need not add it. */ + /* XXX: Huh? All it means is that the Tx list is full...*/ + } else { + unsigned long flags; + unsigned int entry; + int dummy = -1; + + save_flags(flags); cli(); + entry = tp->cur_tx++ % TX_RING_SIZE; + + if (entry != 0) { + /* Avoid a chip errata by prefixing a dummy entry. */ + tp->tx_skbuff[entry] = 0; + tp->tx_ring[entry].length = + (entry == TX_RING_SIZE - 1) ? Tx1RingWrap : 0; + tp->tx_ring[entry].buffer1 = 0; + /* race with chip, set Tx0DescOwned later */ + dummy = entry; + entry = tp->cur_tx++ % TX_RING_SIZE; + } + + tp->tx_skbuff[entry] = 0; + /* Put the setup frame on the Tx list. */ + if (entry == TX_RING_SIZE - 1) + tx_flags |= Tx1RingWrap; /* Wrap ring. */ + tp->tx_ring[entry].length = tx_flags; + tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame); + tp->tx_ring[entry].status = Tx0DescOwned; + if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) { + tp->tx_full = 1; + netif_stop_queue (dev); + } + if (dummy >= 0) + tp->tx_ring[dummy].status = Tx0DescOwned; + restore_flags(flags); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); + } + +out: + outl_CSR6(csr6, ioaddr); +} + + +static struct pci_device_id xircom_pci_table[] __devinitdata = { + { 0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 }, + {0}, +}; +MODULE_DEVICE_TABLE(pci, xircom_pci_table); + + +#ifdef CONFIG_PM +static int xircom_suspend(struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct xircom_private *tp = dev->priv; + printk(KERN_INFO "xircom_suspend(%s)\n", dev->name); + if (tp->open) + xircom_down(dev); + return 0; +} + + +static int xircom_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct xircom_private *tp = dev->priv; + printk(KERN_INFO "xircom_resume(%s)\n", dev->name); + + /* Bring the chip out of sleep mode. + Caution: Snooze mode does not work with some boards! */ + if (xircom_tbl[tp->chip_id].flags & HAS_ACPI) + pci_write_config_dword(tp->pdev, PCI_POWERMGMT, 0); + + transceiver_voodoo(dev); + if (xircom_tbl[tp->chip_id].flags & HAS_MII) + check_duplex(dev); + + if (tp->open) + xircom_up(dev); + return 0; +} +#endif /* CONFIG_PM */ + + +static void __devexit xircom_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + printk(KERN_INFO "xircom_remove_one(%s)\n", dev->name); + unregister_netdev(dev); + pci_release_regions(pdev); + kfree(dev); + pci_set_drvdata(pdev, NULL); +} + + +static struct pci_driver xircom_driver = { + name: DRV_NAME, + id_table: xircom_pci_table, + probe: xircom_init_one, + remove: __devexit_p(xircom_remove_one), +#ifdef CONFIG_PM + suspend: xircom_suspend, + resume: xircom_resume +#endif /* CONFIG_PM */ +}; + + +static int __init xircom_init(void) +{ +/* when a module, this is printed whether or not devices are found in probe */ +#ifdef MODULE + printk(version); +#endif + return pci_module_init(&xircom_driver); +} + + +static void __exit xircom_exit(void) +{ + pci_unregister_driver(&xircom_driver); +} + +module_init(xircom_init) +module_exit(xircom_exit) + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -Nru a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c --- a/drivers/net/via-rhine.c Thu Mar 7 18:17:43 2002 +++ b/drivers/net/via-rhine.c Thu Mar 7 18:17:43 2002 @@ -321,6 +321,7 @@ VT86C100A = 0, VT6102, VT3043, + VT6105, }; struct via_rhine_chip_info { @@ -349,7 +350,7 @@ { "VIA VT6102 Rhine-II", RHINE_IOTYPE, 256, CanHaveMII | HasWOL }, { "VIA VT3043 Rhine", RHINE_IOTYPE, 128, - CanHaveMII | ReqTxAlign } + CanHaveMII | ReqTxAlign }, { "VIA VT6105 Rhine-III", RHINE_IOTYPE, 256, CanHaveMII | HasWOL }, }; @@ -1753,7 +1754,7 @@ name: "via-rhine", id_table: via_rhine_pci_tbl, probe: via_rhine_init_one, - remove: via_rhine_remove_one, + remove: __devexit_p(via_rhine_remove_one), }; diff -Nru a/drivers/net/wan/Config.help b/drivers/net/wan/Config.help --- a/drivers/net/wan/Config.help Thu Mar 7 18:17:38 2002 +++ b/drivers/net/wan/Config.help Thu Mar 7 18:17:38 2002 @@ -41,14 +41,15 @@ This driver supports the FarSync T-Series X.21 (and V.35/V.24) cards from FarSite Communications Ltd. Synchronous communication is supported on all ports at speeds up to - 8Mb/s (128K on V.24) using synchronous PPP or Cisco HDLC. + 8Mb/s (128K on V.24) using synchronous PPP, Cisco HDLC, raw HDLC, + Frame Relay or X.25/LAPB. 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 . The module will be called farsync.o and if you want the module to be automatically loaded when the interface is referenced then you - should add "alias syncX farsync" to /etc/modules.conf for each + should add "alias hdlcX farsync" to /etc/modules.conf for each interface, where X is 0, 1, 2, ... CONFIG_DLCI diff -Nru a/drivers/net/wan/Config.in b/drivers/net/wan/Config.in --- a/drivers/net/wan/Config.in Thu Mar 7 18:17:38 2002 +++ b/drivers/net/wan/Config.in Thu Mar 7 18:17:38 2002 @@ -39,9 +39,6 @@ dep_tristate ' Etinc PCISYNC serial board support (EXPERIMENTAL)' CONFIG_DSCC4 m -# FarSite Communications' cards - - tristate ' FarSync T-Series X.21 (and V.35/V.24) cards' CONFIG_FARSYNC # # Lan Media's board. Currently 1000, 1200, 5200, 5245 @@ -55,8 +52,13 @@ tristate ' SyncLink HDLC/SYNCPPP support' CONFIG_SYNCLINK_SYNCPPP - tristate ' Generic HDLC driver' CONFIG_HDLC +# Generic HDLC + + tristate ' Generic HDLC layer' CONFIG_HDLC if [ "$CONFIG_HDLC" != "n" ]; then + bool ' Raw HDLC support' CONFIG_HDLC_RAW + bool ' Cisco HDLC support' CONFIG_HDLC_CISCO + bool ' Frame Relay support' CONFIG_HDLC_FR bool ' Synchronous Point-to-Point Protocol (PPP) support' CONFIG_HDLC_PPP if [ "$CONFIG_LAPB" = "m" -a "$CONFIG_HDLC" = "m" -o "$CONFIG_LAPB" = "y" ]; then bool ' X.25 protocol support' CONFIG_HDLC_X25 @@ -65,6 +67,11 @@ fi dep_tristate ' SDL RISCom/N2 support' CONFIG_N2 $CONFIG_HDLC dep_tristate ' Moxa C101 support' CONFIG_C101 $CONFIG_HDLC + dep_tristate ' FarSync T-Series support' CONFIG_FARSYNC $CONFIG_HDLC + bool ' Debug received/transmitted packets' CONFIG_HDLC_DEBUG_PKT + bool ' Debug hard_header routines' CONFIG_HDLC_DEBUG_HARD_HEADER + bool ' Debug FECN/BECN conditions' CONFIG_HDLC_DEBUG_ECN + bool ' Debug RX/TX packet rings' CONFIG_HDLC_DEBUG_RINGS fi tristate ' Frame relay DLCI support' CONFIG_DLCI diff -Nru a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile --- a/drivers/net/wan/Makefile Thu Mar 7 18:17:37 2002 +++ b/drivers/net/wan/Makefile Thu Mar 7 18:17:37 2002 @@ -1,7 +1,7 @@ # # Makefile for the Linux network (wan) device drivers. # -# 3 Aug 2000, Christoph Hellwig +# 3 Aug 2000, Christoph Hellwig # Rewritten to use lists instead of if-statements. # @@ -9,7 +9,7 @@ O_TARGET := wan.o -export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc.o +export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc_generic.o list-multi = wanpipe.o cyclomx.o wanpipe-objs = sdlamain.o sdla_ft1.o $(wanpipe-y) @@ -22,6 +22,11 @@ cyclomx-objs = cycx_main.o $(cyclomx-y) cyclomx-$(CONFIG_CYCLOMX_X25) += cycx_x25.o +hdlc-$(CONFIG_HDLC_RAW) += hdlc_raw.o +hdlc-$(CONFIG_HDLC_CISCO) += hdlc_cisco.o +hdlc-$(CONFIG_HDLC_FR) += hdlc_fr.o +hdlc-$(CONFIG_HDLC_PPP) += hdlc_ppp.o +hdlc-$(CONFIG_HDLC_X25) += hdlc_x25.o obj-$(CONFIG_HOSTESS_SV11) += z85230.o syncppp.o hostess_sv11.o obj-$(CONFIG_SEALEVEL_4021) += z85230.o syncppp.o sealevel.o @@ -56,11 +61,16 @@ obj-$(CONFIG_LAPBETHER) += lapbether.o obj-$(CONFIG_SBNI) += sbni.o obj-$(CONFIG_HDLC) += hdlc.o -obj-$(CONFIG_HDLC_PPP) += syncppp.o +ifeq ($(CONFIG_HDLC_PPP),y) + obj-$(CONFIG_HDLC) += syncppp.o +endif obj-$(CONFIG_N2) += n2.o obj-$(CONFIG_C101) += c101.o include $(TOPDIR)/Rules.make + +hdlc.o: hdlc_generic.o $(hdlc-y) + $(LD) -r -o $@ hdlc_generic.o $(hdlc-y) wanpipe.o: $(wanpipe-objs) $(LD) -r -o $@ $(wanpipe-objs) diff -Nru a/drivers/net/wan/c101.c b/drivers/net/wan/c101.c --- a/drivers/net/wan/c101.c Thu Mar 7 18:17:39 2002 +++ b/drivers/net/wan/c101.c Thu Mar 7 18:17:39 2002 @@ -1,7 +1,7 @@ /* * Moxa C101 synchronous serial card driver for Linux * - * Copyright (C) 2000 Krzysztof Halasa + * Copyright (C) 2000-2001 Krzysztof Halasa * * 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 @@ -15,6 +15,7 @@ * Moxa C101 User's Manual */ +#include #include #include #include @@ -29,10 +30,8 @@ #include "hd64570.h" -#define DEBUG_RINGS -/* #define DEBUG_PKT */ -static const char* version = "Moxa C101 driver revision: 1.02 for Linux 2.4"; +static const char* version = "Moxa C101 driver version: 1.09"; static const char* devname = "C101"; #define C101_PAGE 0x1D00 @@ -51,12 +50,12 @@ typedef struct card_s { hdlc_device hdlc; /* HDLC device struct - must be first */ spinlock_t lock; /* TX lock */ - int clkmode; /* clock mode */ - int clkrate; /* clock speed */ - int line; /* loopback only */ u8 *win0base; /* ISA window base address */ u32 phy_winbase; /* ISA physical base address */ u16 buff_offset; /* offset of first buffer of first channel */ + sync_serial_settings settings; + unsigned short encoding; + unsigned short parity; u8 rxs, txs, tmc; /* SCA registers */ u8 irq; /* IRQ (3-15) */ u8 ring_buffers; /* number of buffers in a ring */ @@ -72,6 +71,9 @@ typedef card_t port_t; +static card_t *first_card; +static card_t **new_card = &first_card; + #define sca_in(reg, card) readb((card)->win0base + C101_SCA + (reg)) #define sca_out(value, reg, card) writeb(value, (card)->win0base + C101_SCA + (reg)) @@ -105,13 +107,13 @@ #include "hd6457x.c" -static int c101_set_clock(port_t *port, int value) +static int c101_set_iface(port_t *port) { u8 msci = get_msci(port); u8 rxs = port->rxs & CLK_BRG_MASK; u8 txs = port->txs & CLK_BRG_MASK; - switch(value) { + switch(port->settings.clock_type) { case CLOCK_EXT: rxs |= CLK_LINE_RX; /* RXC input */ txs |= CLK_LINE_TX; /* TXC input */ @@ -140,76 +142,76 @@ port->txs = txs; sca_out(rxs, msci + RXS, port); sca_out(txs, msci + TXS, port); - port->clkmode = value; + sca_set_port(port); return 0; } -static int c101_open(hdlc_device *hdlc) +static int c101_open(struct net_device *dev) { + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); + int result = hdlc_open(hdlc); + if (result) + return result; MOD_INC_USE_COUNT; writeb(1, port->win0base + C101_DTR); sca_out(0, MSCI1_OFFSET + CTL, port); /* RTS uses ch#2 output */ sca_open(hdlc); - c101_set_clock(port, port->clkmode); - return 0; + return c101_set_iface(port); } -static void c101_close(hdlc_device *hdlc) +static int c101_close(struct net_device *dev) { + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); sca_close(hdlc); writeb(0, port->win0base + C101_DTR); sca_out(CTL_NORTS, MSCI1_OFFSET + CTL, port); + hdlc_close(hdlc); MOD_DEC_USE_COUNT; + return 0; } -static int c101_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd) +static int c101_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - int value = ifr->ifr_ifru.ifru_ivalue; - int result = 0; + union line_settings *line = &ifr->ifr_settings->ifs_line; + const size_t size = sizeof(sync_serial_settings); + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - - switch(cmd) { - case HDLCSCLOCK: - result = c101_set_clock(port, value); - case HDLCGCLOCK: - value = port->clkmode; - break; - - case HDLCSCLOCKRATE: - port->clkrate = value; - sca_set_clock(port); - case HDLCGCLOCKRATE: - value = port->clkrate; - break; - - case HDLCSLINE: - result = sca_set_loopback(port, value); - case HDLCGLINE: - value = port->line; - break; - -#ifdef DEBUG_RINGS - case HDLCRUN: +#ifdef CONFIG_HDLC_DEBUG_RINGS + if (cmd == SIOCDEVPRIVATE) { sca_dump_rings(hdlc); return 0; -#endif /* DEBUG_RINGS */ + } +#endif + if (cmd != SIOCWANDEV) + return hdlc_ioctl(dev, ifr, cmd); + + switch(ifr->ifr_settings->type) { + case IF_GET_IFACE: + ifr->ifr_settings->type = IF_IFACE_SYNC_SERIAL; + if (copy_to_user(&line->sync, &port->settings, size)) + return -EFAULT; + return 0; + + case IF_IFACE_SYNC_SERIAL: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (copy_from_user(&port->settings, &line->sync, size)) + return -EFAULT; + /* FIXME - put sanity checks here */ + return c101_set_iface(port); default: - return -EINVAL; + return hdlc_ioctl(dev, ifr, cmd); } - - ifr->ifr_ifru.ifru_ivalue = value; - return result; } @@ -231,6 +233,7 @@ static int c101_run(unsigned long irq, unsigned long winbase) { + struct net_device *dev; card_t *card; int result; @@ -284,15 +287,19 @@ sca_init(card, 0); + dev = hdlc_to_dev(&card->hdlc); + spin_lock_init(&card->lock); - hdlc_to_dev(&card->hdlc)->irq = irq; - hdlc_to_dev(&card->hdlc)->mem_start = winbase; - hdlc_to_dev(&card->hdlc)->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1; - hdlc_to_dev(&card->hdlc)->tx_queue_len = 50; - card->hdlc.ioctl = c101_ioctl; - card->hdlc.open = c101_open; - card->hdlc.close = c101_close; + dev->irq = irq; + dev->mem_start = winbase; + dev->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1; + dev->tx_queue_len = 50; + dev->do_ioctl = c101_ioctl; + dev->open = c101_open; + dev->stop = c101_close; + card->hdlc.attach = sca_attach; card->hdlc.xmit = sca_xmit; + card->settings.clock_type = CLOCK_EXT; result = register_hdlc_device(&card->hdlc); if (result) { @@ -319,7 +326,7 @@ return -ENOSYS; /* no parameters specified, abort */ } - printk(KERN_INFO "%s\n", version); + printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version); do { unsigned long irq, ram; diff -Nru a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c --- a/drivers/net/wan/dscc4.c Thu Mar 7 18:17:40 2002 +++ b/drivers/net/wan/dscc4.c Thu Mar 7 18:17:40 2002 @@ -72,7 +72,7 @@ * the documentation/chipset releases. An on-line errata would be welcome. * * TODO: - * - syncppp oopses. X25 untested. + * - test X25. * - use polling at high irq/s, * - performance analysis, * - endianness. @@ -91,6 +91,7 @@ #include #include #include +#include #include #include #include @@ -114,7 +115,7 @@ #include /* Version */ -static const char version[] = "$Id: dscc4.c,v 1.157 2002/01/28 01:54:19 romieu Exp $\n"; +static const char version[] = "$Id: dscc4.c,v 1.158 2002/01/30 00:40:37 romieu Exp $\n"; static int debug; static int quartz; @@ -168,9 +169,9 @@ #define BRR_DIVIDER_MAX 64*0x00008000 #define dev_per_card 4 -#define SOURCE_ID(flags) ((flags >> 28 ) & 0x03) -#define TO_SIZE(state) ((state >> 16) & 0x1fff) -#define TO_STATE(len) cpu_to_le32((len & TxSizeMax) << 16) +#define SOURCE_ID(flags) (((flags) >> 28 ) & 0x03) +#define TO_SIZE(state) (((state) >> 16) & 0x1fff) +#define TO_STATE(len) cpu_to_le32(((len) & TxSizeMax) << 16) #define RX_MAX(len) ((((len) >> 5) + 1)<< 5) #define SCC_REG_START(id) SCC_START+(id)*SCC_OFFSET @@ -343,6 +344,11 @@ static int dscc4_tx_poll(struct dscc4_dev_priv *, struct net_device *); #endif +static inline struct dscc4_dev_priv *dscc4_priv(struct net_device *dev) +{ + return list_entry(dev, struct dscc4_dev_priv, hdlc.netdev); +} + static inline void dscc4_patch_register(u32 ioaddr, u32 mask, u32 value) { u32 state; @@ -479,52 +485,49 @@ static inline int dscc4_xpr_ack(struct dscc4_dev_priv *dpriv) { - int cur, ret = 0; - s16 i; + int cur = dpriv->iqtx_current%IRQ_RING_SIZE; + s16 i = 0; - cur = dpriv->iqtx_current%IRQ_RING_SIZE; - for (i = 0; i >= 0; i++) { + do { if (!(dpriv->flags & (NeedIDR | NeedIDT)) || (dpriv->iqtx[cur] & Xpr)) break; smp_rmb(); - } - if (i < 0) { - printk(KERN_ERR "%s: %s timeout\n", "dscc4", "XPR"); - ret = -1; - } - return ret; + } while (i++ >= 0); + + return i; } static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, int cur, struct RxFD *rx_fd, struct net_device *dev) { + struct net_device_stats *stats = &dev_to_hdlc(dev)->stats; struct pci_dev *pdev = dpriv->pci_priv->pdev; struct sk_buff *skb; int pkt_len; skb = dpriv->rx_skbuff[cur]; - pkt_len = TO_SIZE(rx_fd->state2) - 1; - pci_dma_sync_single(pdev, rx_fd->data, pkt_len + 1, PCI_DMA_FROMDEVICE); - if((skb->data[pkt_len] & FrameOk) == FrameOk) { + pkt_len = TO_SIZE(rx_fd->state2); + pci_dma_sync_single(pdev, rx_fd->data, pkt_len, PCI_DMA_FROMDEVICE); + if((skb->data[--pkt_len] & FrameOk) == FrameOk) { pci_unmap_single(pdev, rx_fd->data, skb->len, PCI_DMA_FROMDEVICE); - dev_to_hdlc(dev)->stats.rx_packets++; - dev_to_hdlc(dev)->stats.rx_bytes += pkt_len; + stats->rx_packets++; + stats->rx_bytes += pkt_len; skb->tail += pkt_len; skb->len = pkt_len; - if (netif_running(hdlc_to_dev(&dpriv->hdlc))) + if (netif_running(dev)) skb->protocol = htons(ETH_P_HDLC); netif_rx(skb); try_get_rx_skb(dpriv, cur, dev); } else { if(skb->data[pkt_len] & FrameRdo) - dev_to_hdlc(dev)->stats.rx_fifo_errors++; + stats->rx_fifo_errors++; else if(!(skb->data[pkt_len] | ~FrameCrc)) - dev_to_hdlc(dev)->stats.rx_crc_errors++; + stats->rx_crc_errors++; else if(!(skb->data[pkt_len] | ~(FrameVfr | FrameRab))) - dev_to_hdlc(dev)->stats.rx_length_errors++; + stats->rx_length_errors++; else - dev_to_hdlc(dev)->stats.rx_errors++; + stats->rx_errors++; } rx_fd->state1 |= Hold; rx_fd->state2 = 0x00000000; @@ -612,7 +615,7 @@ * SCC 0-3 private rx/tx irq structures * IQRX/TXi needs to be set soon. Learned it the hard way... */ - for(i = 0; i < dev_per_card; i++) { + for (i = 0; i < dev_per_card; i++) { dpriv = priv->root + i; dpriv->iqtx = (u32 *) pci_alloc_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), &dpriv->iqtx_dma); @@ -620,7 +623,7 @@ goto err_out_free_iqtx; writel(dpriv->iqtx_dma, ioaddr + IQTX0 + i*4); } - for(i = 0; i < dev_per_card; i++) { + for (i = 0; i < dev_per_card; i++) { dpriv = priv->root + i; dpriv->iqrx = (u32 *) pci_alloc_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), &dpriv->iqrx_dma); @@ -740,7 +743,6 @@ dpriv->dev_id = i; dpriv->pci_priv = ppriv; spin_lock_init(&dpriv->lock); - d->priv = dpriv; hdlc->xmit = dscc4_start_xmit; hdlc->attach = dscc4_hdlc_attach; @@ -778,7 +780,7 @@ struct dscc4_dev_priv *dpriv; struct dscc4_pci_priv *ppriv; - dpriv = dev->priv; + dpriv = dscc4_priv(dev); if (netif_queue_stopped(dev) && ((jiffies - dev->trans_start) > TX_TIMEOUT)) { ppriv = dpriv->pci_priv; @@ -844,7 +846,7 @@ static int dscc4_open(struct net_device *dev) { - struct dscc4_dev_priv *dpriv = dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); hdlc_device *hdlc = &dpriv->hdlc; struct dscc4_pci_priv *ppriv; u32 ioaddr; @@ -893,8 +895,10 @@ * WARNING, a really missing XPR usually means a hardware * reset is needed. Suggestions anyone ? */ - if (dscc4_xpr_ack(dpriv)) + if (dscc4_xpr_ack(dpriv) < 0) { + printk(KERN_ERR "%s: %s timeout\n", DRV_NAME, "XPR"); goto err_free_ring; + } netif_start_queue(dev); @@ -925,7 +929,7 @@ static int dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev) { - struct dscc4_dev_priv *dpriv = dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); struct dscc4_pci_priv *ppriv; struct TxFD *tx_fd; int cur, next; @@ -935,7 +939,6 @@ next = dpriv->tx_current%TX_RING_SIZE; dpriv->tx_skbuff[next] = skb; tx_fd = dpriv->tx_fd + next; - printk(KERN_DEBUG "%s: %d sent\n", dev->name, skb->len); tx_fd->state = FrameEnd | Hold | TO_STATE(skb->len); tx_fd->data = pci_map_single(ppriv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); @@ -972,7 +975,7 @@ static int dscc4_close(struct net_device *dev) { - struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); u32 ioaddr = dev->base_addr; int dev_id; hdlc_device *hdlc = dev_to_hdlc(dev); @@ -1011,7 +1014,7 @@ static int dscc4_set_clock(struct net_device *dev, u32 *bps, u32 *state) { - struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); u32 brr; *state &= ~Ccr0ClockMask; @@ -1062,37 +1065,29 @@ static int dscc4_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct dscc4_dev_priv *dpriv = dev->priv; - struct if_settings *if_s = &ifr->ifr_settings; + union line_settings *line = &ifr->ifr_settings->ifs_line; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); const size_t size = sizeof(dpriv->settings); int ret = 0; if (dev->flags & IFF_UP) return -EBUSY; - if (cmd != SIOCDEVICE) + if (cmd != SIOCWANDEV) return -EOPNOTSUPP; - switch(ifr->ifr_settings.type) { + switch(ifr->ifr_settings->type) { case IF_GET_IFACE: - if_s->type = IF_IFACE_SYNC_SERIAL; - if (if_s->data_length == 0) - return 0; - if (if_s->data_length < size) - return -ENOMEM; - if (copy_to_user(if_s->data, &dpriv->settings, size)) + ifr->ifr_settings->type = IF_IFACE_SYNC_SERIAL; + if (copy_to_user(&line->sync, &dpriv->settings, size)) return -EFAULT; - if_s->data_length = size; break; case IF_IFACE_SYNC_SERIAL: if(!capable(CAP_NET_ADMIN)) return -EPERM; - if (if_s->data_length != size) - return -ENOMEM; - - if (copy_from_user(&dpriv->settings, if_s->data, size)) + if (copy_from_user(&dpriv->settings, &line->sync, size)) return -EFAULT; ret = dscc4_set_iface(dev); break; @@ -1133,7 +1128,7 @@ static int dscc4_clock_setting(struct net_device *dev) { - struct dscc4_dev_priv *dpriv = dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); sync_serial_settings *settings = &dpriv->settings; u32 bps, state; u32 ioaddr; @@ -1160,7 +1155,7 @@ static int dscc4_encoding_setting(struct net_device *dev) { - struct dscc4_dev_priv *dpriv = dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); struct thingie encoding[] = { { ENCODING_NRZ, 0x00000000 }, { ENCODING_NRZI, 0x00200000 }, @@ -1184,7 +1179,7 @@ static int dscc4_loopback_setting(struct net_device *dev) { - struct dscc4_dev_priv *dpriv = dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); sync_serial_settings *settings = &dpriv->settings; u32 ioaddr, state; @@ -1203,7 +1198,7 @@ static int dscc4_crc_setting(struct net_device *dev) { - struct dscc4_dev_priv *dpriv = dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); struct thingie crc[] = { { PARITY_CRC16_PR0_CCITT, 0x00000010 }, { PARITY_CRC16_PR1_CCITT, 0x00000000 }, @@ -1516,7 +1511,6 @@ } } else { /* ! SccEvt */ #ifdef DEBUG_PARANOIA - int i; static struct { u32 mask; const char *irq_name; @@ -1528,21 +1522,21 @@ { 0x00000008, "PLLA"}, { 0x00000004, "CDSC"}, { 0, NULL} - }; + }, *evt; #endif /* DEBUG_PARANOIA */ state &= 0x00ffffff; #ifdef DEBUG_PARANOIA - for (i = 0; evts[i].irq_name; i++) { - if (state & evts[i].mask) { + for (evt = evts; evt->irq_name; evt++) { + if (state & evt->mask) { printk(KERN_DEBUG "%s: %s\n", dev->name, - evts[i].irq_name); - if (!(state &= ~evts[i].mask)) + evt->irq_name); + if (!(state &= ~evt->mask)) goto try; } } #endif /* DEBUG_PARANOIA */ /* - * Receive Data Overflow (FIXME: untested) + * Receive Data Overflow (FIXME: fscked) */ if (state & Rdo) { u32 ioaddr, scc_offset, scc_addr; @@ -1633,7 +1627,7 @@ static int dscc4_init_ring(struct net_device *dev) { - struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); struct TxFD *tx_fd; struct RxFD *rx_fd; int i; @@ -1654,7 +1648,7 @@ dpriv->tx_dirty = 0; /* the dma core of the dscc4 will be locked on the first desc */ - for(i = 0; i < TX_RING_SIZE; ) { + for (i = 0; i < TX_RING_SIZE; ) { reset_TxFD(tx_fd); /* FIXME: NULL should be ok - to be tried */ tx_fd->data = dpriv->tx_fd_dma; @@ -1689,7 +1683,7 @@ skb->len, PCI_DMA_TODEVICE); dpriv->tx_skbuff[0] = skb; } - for (i = 0; i < RX_RING_SIZE;) { + for (i = 0; i < RX_RING_SIZE; ) { /* size set by the host. Multiple of 4 bytes please */ rx_fd->state1 = HiDesc; /* Hi, no Hold */ rx_fd->state2 = 0x00000000; @@ -1753,7 +1747,8 @@ static int dscc4_hdlc_attach(hdlc_device *hdlc, unsigned short encoding, unsigned short parity) { - struct dscc4_dev_priv *dpriv = hdlc_to_dev(hdlc)->priv; + struct net_device *dev = hdlc_to_dev(hdlc); + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); if (encoding != ENCODING_NRZ && encoding != ENCODING_NRZI && @@ -1782,7 +1777,7 @@ MODULE_DEVICE_TABLE(pci, dscc4_pci_tbl); static struct pci_driver dscc4_driver = { - name: "dscc4", + name: DRV_NAME, id_table: dscc4_pci_tbl, probe: dscc4_init_one, remove: dscc4_remove_one, diff -Nru a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c --- a/drivers/net/wan/farsync.c Thu Mar 7 18:17:39 2002 +++ b/drivers/net/wan/farsync.c Thu Mar 7 18:17:39 2002 @@ -1,5 +1,5 @@ /* - * FarSync X21 driver for Linux (2.4.x kernel version) + * FarSync X21 driver for Linux (generic HDLC version) * * Actually sync driver for X.21, V.35 and V.24 on FarSync T-series cards * @@ -18,11 +18,10 @@ #include #include #include -#include #include -#include #include -#include +#include +#include #include "farsync.h" @@ -32,6 +31,7 @@ */ MODULE_AUTHOR("R.J.Dunlop "); MODULE_DESCRIPTION("FarSync T-Series X21 driver. FarSite Communications Ltd."); +MODULE_LICENSE("GPL"); EXPORT_NO_SYMBOLS; @@ -331,26 +331,15 @@ /* Per port (line or channel) information */ struct fst_port_info { - void *if_ptr; /* Some drivers describe this as a - * general purpose pointer. However if - * using syncPPP it has a very specific - * purpose: it must be the first item in - * the structure pointed to by dev->priv - * and must in turn point to the - * associated ppp_device structure. - */ + hdlc_device hdlc; /* HDLC device struct - must be first */ struct fst_card_info *card; /* Card we're associated with */ int index; /* Port index on the card */ - int proto; /* Protocol we are running */ int hwif; /* Line hardware (lineInterface copy) */ int run; /* Port is running */ int rxpos; /* Next Rx buffer to use */ int txpos; /* Next Tx buffer to use */ int txipos; /* Next Tx buffer to check for free */ int txcnt; /* Count of Tx buffers in use */ - struct net_device *dev; /* Kernel network device entry */ - struct net_device_stats stats; /* Standard statistics */ - struct ppp_device pppdev; /* Link to syncPPP */ }; /* Per card information @@ -370,6 +359,11 @@ struct fst_port_info ports[ FST_MAX_PORTS ]; }; +/* Convert an HDLC device pointer into a port info pointer and similar */ +#define hdlc_to_port(H) ((struct fst_port_info *)(H)) +#define dev_to_port(D) hdlc_to_port(dev_to_hdlc(D)) +#define port_to_dev(P) hdlc_to_dev(&(P)->hdlc) + /* * Shared memory window access macros @@ -632,25 +626,18 @@ if ( signals & (( port->hwif == X21 ) ? IPSTS_INDICATE : IPSTS_DCD )) { - if ( ! netif_carrier_ok ( port->dev )) + if ( ! netif_carrier_ok ( port_to_dev ( port ))) { dbg ( DBG_INTR,"DCD active\n"); - - /* Poke sPPP to renegotiate */ - if ( port->proto == FST_HDLC || port->proto == FST_PPP ) - { - sppp_reopen ( port->dev ); - } - - netif_carrier_on ( port->dev ); + netif_carrier_on ( port_to_dev ( port )); } } else { - if ( netif_carrier_ok ( port->dev )) + if ( netif_carrier_ok ( port_to_dev ( port ))) { dbg ( DBG_INTR,"DCD lost\n"); - netif_carrier_off ( port->dev ); + netif_carrier_off ( port_to_dev ( port )); } } } @@ -693,24 +680,24 @@ len ); if ( dmabits != ( RX_STP | RX_ENP ) || len > LEN_RX_BUFFER - 2 ) { - port->stats.rx_errors++; + port->hdlc.stats.rx_errors++; /* Update error stats and discard buffer */ if ( dmabits & RX_OFLO ) { - port->stats.rx_fifo_errors++; + port->hdlc.stats.rx_fifo_errors++; } if ( dmabits & RX_CRC ) { - port->stats.rx_crc_errors++; + port->hdlc.stats.rx_crc_errors++; } if ( dmabits & RX_FRAM ) { - port->stats.rx_frame_errors++; + port->hdlc.stats.rx_frame_errors++; } if ( dmabits == ( RX_STP | RX_ENP )) { - port->stats.rx_length_errors++; + port->hdlc.stats.rx_length_errors++; } /* Discard buffer descriptors until we see the end of packet @@ -747,7 +734,7 @@ { dbg ( DBG_RX,"intr_rx: can't allocate buffer\n"); - port->stats.rx_dropped++; + port->hdlc.stats.rx_dropped++; /* Return descriptor to card */ FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); @@ -771,29 +758,16 @@ port->rxpos = rxp; /* Update stats */ - port->stats.rx_packets++; - port->stats.rx_bytes += len; + port->hdlc.stats.rx_packets++; + port->hdlc.stats.rx_bytes += len; /* Push upstream */ - if ( port->proto == FST_HDLC || port->proto == FST_PPP ) - { - /* Mark for further processing by sPPP module */ - skb->protocol = htons ( ETH_P_WAN_PPP ); - } - else - { - /* DEC customer specific protocol (since nothing defined for - * marking raw data), at least one other driver uses this value - * for this purpose. - */ - skb->protocol = htons ( ETH_P_CUST ); - skb->pkt_type = PACKET_HOST; - } skb->mac.raw = skb->data; - skb->dev = port->dev; + skb->dev = hdlc_to_dev ( &port->hdlc ); + skb->protocol = htons ( ETH_P_HDLC ); netif_rx ( skb ); - port->dev->last_rx = jiffies; + port_to_dev ( port )->last_rx = jiffies; } @@ -844,7 +818,7 @@ case CTLB_CHG: case CTLC_CHG: case CTLD_CHG: - if ( port->run && port->dev != NULL ) + if ( port->run ) fst_intr_ctlchg ( card, port ); break; @@ -863,9 +837,8 @@ * always load up the entire packet for DMA. */ dbg ( DBG_TX,"Tx underflow port %d\n", event & 0x03 ); - - port->stats.tx_errors++; - port->stats.tx_fifo_errors++; + port->hdlc.stats.tx_errors++; + port->hdlc.stats.tx_fifo_errors++; break; case INIT_CPLT: @@ -890,7 +863,7 @@ for ( pi = 0, port = card->ports ; pi < card->nports ; pi++, port++ ) { - if ( port->dev == NULL || ! port->run ) + if ( ! port->run ) continue; /* Check for rx completions */ @@ -907,7 +880,7 @@ --port->txcnt; if ( ++port->txipos >= NUM_TX_BUFFER ) port->txipos = 0; - netif_wake_queue ( port->dev ); + netif_wake_queue ( port_to_dev ( port )); } } @@ -969,145 +942,28 @@ static int -fst_change_mtu ( struct net_device *dev, int new_mtu ) -{ - if ( new_mtu < 128 || new_mtu > FST_MAX_MTU ) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - - -/* Sooner or later you can't avoid a forward declaration */ -static int fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd ); - -static int -switch_proto ( struct fst_port_info *port, int new_proto ) -{ - int err; - int orig_mtu; - struct net_device *dev; - - dev = port->dev; - - /* Turn off sPPP module ? */ - if (( new_proto != FST_HDLC && new_proto != FST_PPP ) - && ( port->proto == FST_HDLC || port->proto == FST_PPP )) - { - sppp_close ( port->pppdev.dev ); - sppp_detach ( port->pppdev.dev ); - - /* Reset some fields overwritten by sPPP */ - dev->hard_header = NULL; - dev->rebuild_header = NULL; - dev->tx_queue_len = FST_TX_QUEUE_LEN; - dev->type = ARPHRD_MYTYPE; - dev->addr_len = 0; - dev->hard_header_len = 0; - dev->do_ioctl = fst_ioctl; - dev->change_mtu = fst_change_mtu; - dev->flags = IFF_POINTOPOINT|IFF_NOARP; - - return 0; - } - - /* Turn on sPPP ? */ - if (( new_proto == FST_HDLC || new_proto == FST_PPP ) - && ( port->proto != FST_HDLC && port->proto != FST_PPP )) - { - orig_mtu = dev->mtu; - - /* Attach to sync PPP module */ - port->pppdev.dev = dev; - sppp_attach ( &port->pppdev ); - - if ( orig_mtu < dev->mtu ) - dev->change_mtu ( dev, orig_mtu ); - - /* Claw back the ioctl routine. We promise to be good and call - * the sync PPP routines once we've eliminated our functions. - */ - dev->do_ioctl = fst_ioctl; - - /* Set the mode */ - if ( new_proto == FST_HDLC ) - { - err = sppp_do_ioctl ( port->pppdev.dev, NULL, - SPPPIOCCISCO ); - } - else - { - err = sppp_do_ioctl ( port->pppdev.dev, NULL, - SPPPIOCPPP ); - } - - /* Open the device */ - if ( err == 0 ) - { - (void) sppp_open ( port->dev ); - } - - return err; - } - - /* Switch sPPP mode to that desired */ - err = 0; - if ( new_proto == FST_HDLC && port->pppdev.dev != NULL ) - { - err = sppp_do_ioctl ( port->pppdev.dev, NULL, SPPPIOCCISCO ); - } - else if ( new_proto == FST_PPP && port->pppdev.dev != NULL ) - { - err = sppp_do_ioctl ( port->pppdev.dev, NULL, SPPPIOCPPP ); - } - - /* Anything else is switching from one raw mode to another which is - * basically a NOP - */ - - return err; -} - - -static int set_conf_from_info ( struct fst_card_info *card, struct fst_port_info *port, struct fstioc_info *info ) { int err; - /* Set things according to the user set valid flags */ + /* Set things according to the user set valid flags. + * Several of the old options have been invalidated/replaced by the + * generic HDLC package. + */ err = 0; if ( info->valid & FSTVAL_PROTO ) - { - if ( port->proto != info->proto ) - { - err = switch_proto ( port, info->proto ); - if ( err == 0 ) - port->proto = info->proto; - } - } + err = -EINVAL; if ( info->valid & FSTVAL_CABLE ) - { - FST_WRB ( card, portConfig[port->index].lineInterface, - info->lineInterface ); - port->hwif = info->lineInterface; - } + err = -EINVAL; if ( info->valid & FSTVAL_SPEED ) - { - FST_WRL ( card, portConfig[port->index].lineSpeed, - info->lineSpeed ); - FST_WRB ( card, portConfig[port->index].internalClock, - info->internalClock ); - } + err = -EINVAL; + if ( info->valid & FSTVAL_MODE ) - { FST_WRW ( card, cardMode, info->cardMode ); - } #if FST_DEBUG if ( info->valid & FSTVAL_DEBUG ) - { fst_debug_mask = info->debug; - } #endif return err; @@ -1125,7 +981,7 @@ info->nports = card->nports; info->type = card->type; info->state = card->state; - info->proto = port->proto; + info->proto = FST_GEN_HDLC; info->index = i; #if FST_DEBUG info->debug = fst_debug_mask; @@ -1154,6 +1010,102 @@ static int +fst_set_iface ( struct fst_card_info *card, struct fst_port_info *port, + struct ifreq *ifr ) +{ + union line_settings *line = &ifr->ifr_settings->ifs_line; + sync_serial_settings sync; + int i; + + if ( copy_from_user ( &sync, &line->sync, sizeof ( sync ))) + return -EFAULT; + + if ( sync.loopback ) + return -EINVAL; + + i = port->index; + + switch ( ifr->ifr_settings->type ) + { + case IF_IFACE_V35: + FST_WRW ( card, portConfig[i].lineInterface, V35 ); + port->hwif = V35; + break; + + case IF_IFACE_V24: + FST_WRW ( card, portConfig[i].lineInterface, V24 ); + port->hwif = V24; + break; + + case IF_IFACE_X21: + FST_WRW ( card, portConfig[i].lineInterface, X21 ); + port->hwif = X21; + break; + + case IF_IFACE_SYNC_SERIAL: + break; + + default: + return -EINVAL; + } + + switch ( sync.clock_type ) + { + case CLOCK_EXT: + FST_WRB ( card, portConfig[i].internalClock, EXTCLK ); + break; + + case CLOCK_INT: + FST_WRB ( card, portConfig[i].internalClock, INTCLK ); + break; + + default: + return -EINVAL; + } + FST_WRL ( card, portConfig[i].lineSpeed, sync.clock_rate ); + return 0; +} + +static int +fst_get_iface ( struct fst_card_info *card, struct fst_port_info *port, + struct ifreq *ifr ) +{ + union line_settings *line = &ifr->ifr_settings->ifs_line; + sync_serial_settings sync; + int i; + + /* First check what line type is set, we'll default to reporting X.21 + * if nothing is set as IF_IFACE_SYNC_SERIAL implies it can't be + * changed + */ + switch ( port->hwif ) + { + case V35: + ifr->ifr_settings->type = IF_IFACE_V35; + break; + case V24: + ifr->ifr_settings->type = IF_IFACE_V24; + break; + case X21: + default: + ifr->ifr_settings->type = IF_IFACE_X21; + break; + } + + i = port->index; + sync.clock_rate = FST_RDL ( card, portConfig[i].lineSpeed ); + /* Lucky card and linux use same encoding here */ + sync.clock_type = FST_RDB ( card, portConfig[i].internalClock ); + sync.loopback = 0; + + if ( copy_to_user (&line->sync, &sync, sizeof ( sync ))) + return -EFAULT; + + return 0; +} + + +static int fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd ) { struct fst_card_info *card; @@ -1164,7 +1116,7 @@ dbg ( DBG_IOCTL,"ioctl: %x, %p\n", cmd, ifr->ifr_data ); - port = dev->priv; + port = dev_to_port ( dev ); card = port->card; if ( !capable ( CAP_NET_ADMIN )) @@ -1201,7 +1153,7 @@ * when going over the top */ if ( wrthdr.size > FST_MEMSIZE || wrthdr.offset > FST_MEMSIZE - || wrthdr.size + wrthdr.offset > FST_MEMSIZE ) + || wrthdr.size + wrthdr.offset > FST_MEMSIZE ) { return -ENXIO; } @@ -1261,6 +1213,9 @@ case FSTSETCONF: + /* Most of the setting have been moved to the generic ioctls + * this just covers debug and board ident mode now + */ if ( copy_from_user ( &info, ifr->ifr_data, sizeof ( info ))) { return -EFAULT; @@ -1268,12 +1223,25 @@ return set_conf_from_info ( card, port, &info ); + case SIOCWANDEV: + switch ( ifr->ifr_settings->type ) + { + case IF_GET_IFACE: + return fst_get_iface ( card, port, ifr ); + + case IF_IFACE_SYNC_SERIAL: + case IF_IFACE_V35: + case IF_IFACE_V24: + case IF_IFACE_X21: + return fst_set_iface ( card, port, ifr ); + + default: + return hdlc_ioctl ( dev, ifr, cmd ); + } + default: - /* Not one of ours. Pass it through to sPPP package */ - if ( port->proto == FST_HDLC || port->proto == FST_PPP ) - return sppp_do_ioctl ( dev, ifr, cmd ); - else - return -EINVAL; + /* Not one of ours. Pass through to HDLC package */ + return hdlc_ioctl ( dev, ifr, cmd ); } } @@ -1306,9 +1274,9 @@ signals = FST_RDL ( port->card, v24DebouncedSts[port->index]); if ( signals & (( port->hwif == X21 ) ? IPSTS_INDICATE : IPSTS_DCD )) - netif_carrier_on ( port->dev ); + netif_carrier_on ( port_to_dev ( port )); else - netif_carrier_off ( port->dev ); + netif_carrier_off ( port_to_dev ( port )); } } @@ -1335,64 +1303,15 @@ static int fst_open ( struct net_device *dev ) { - struct fst_card_info *card; - struct fst_port_info *port; - int orig_mtu; int err; - MOD_INC_USE_COUNT; - - port = dev->priv; - card = port->card; - - switch ( port->proto ) - { - case FST_HDLC: - case FST_PPP: - - orig_mtu = dev->mtu; - - /* Attach to sync PPP module */ - port->pppdev.dev = dev; - sppp_attach ( &port->pppdev ); - - if ( orig_mtu < dev->mtu ) - dev->change_mtu ( dev, orig_mtu ); - - /* Claw back the ioctl routine. We promise to be good and call - * the sync PPP routines once we've eliminated our functions. - */ - dev->do_ioctl = fst_ioctl; - - err = sppp_do_ioctl ( dev, NULL, port->proto == FST_HDLC - ? SPPPIOCCISCO : SPPPIOCPPP ); - if ( err ) - { - sppp_detach ( dev ); - MOD_DEC_USE_COUNT; - return err; - } - - err = sppp_open ( dev ); - if ( err ) - { - sppp_detach ( dev ); - MOD_DEC_USE_COUNT; - return err; - } - break; - - case FST_MONITOR: - case FST_RAW: - break; - - default: - dbg ( DBG_OPEN,"open: Unknown proto %d\n", port->proto ); - break; - } + err = hdlc_open ( dev_to_hdlc ( dev )); + if ( err ) + return err; - fst_openport ( port ); + MOD_INC_USE_COUNT; + fst_openport ( dev_to_port ( dev )); netif_wake_queue ( dev ); return 0; } @@ -1400,34 +1319,22 @@ static int fst_close ( struct net_device *dev ) { - struct fst_port_info *port; - - port = dev->priv; - netif_stop_queue ( dev ); - switch ( port->proto ) - { - case FST_HDLC: - case FST_PPP: - sppp_close ( dev ); - sppp_detach ( dev ); - break; - - case FST_MONITOR: - case FST_RAW: - break; - - default: - dbg ( DBG_OPEN,"close: Unknown proto %d\n", port->proto ); - break; - } - - fst_closeport ( port ); - + fst_closeport ( dev_to_port ( dev )); + hdlc_close ( dev_to_hdlc ( dev )); MOD_DEC_USE_COUNT; return 0; } +static int +fst_attach ( hdlc_device *hdlc, unsigned short encoding, unsigned short parity ) +{ + /* Setting currently fixed in FarSync card so we check and forget */ + if ( encoding != ENCODING_NRZ || parity != PARITY_CRC16_PR1_CCITT ) + return -EINVAL; + return 0; +} + static void fst_tx_timeout ( struct net_device *dev ) @@ -1436,10 +1343,10 @@ dbg ( DBG_INTR | DBG_TX,"tx_timeout\n"); - port = dev->priv; + port = dev_to_port ( dev ); - port->stats.tx_errors++; - port->stats.tx_aborted_errors++; + port->hdlc.stats.tx_errors++; + port->hdlc.stats.tx_aborted_errors++; if ( port->txcnt > 0 ) fst_issue_cmd ( port, ABORTTX ); @@ -1459,22 +1366,15 @@ int pi; int txp; - port = dev->priv; + port = dev_to_port ( dev ); card = port->card; - /* Drop packet if in monitor (rx only) mode */ - if ( port->proto == FST_MONITOR ) - { - dev_kfree_skb ( skb ); - return 0; - } - /* Drop packet with error if we don't have carrier */ if ( ! netif_carrier_ok ( dev )) { dev_kfree_skb ( skb ); - port->stats.tx_errors++; - port->stats.tx_carrier_errors++; + port->hdlc.stats.tx_errors++; + port->hdlc.stats.tx_carrier_errors++; return 0; } @@ -1484,7 +1384,7 @@ dbg ( DBG_TX,"Packet too large %d vs %d\n", skb->len, LEN_TX_BUFFER ); dev_kfree_skb ( skb ); - port->stats.tx_errors++; + port->hdlc.stats.tx_errors++; return 0; } @@ -1498,7 +1398,7 @@ spin_unlock_irqrestore ( &card->card_lock, flags ); dbg ( DBG_TX,"Out of Tx buffers\n"); dev_kfree_skb ( skb ); - port->stats.tx_errors++; + port->hdlc.stats.tx_errors++; return 0; } if ( ++port->txpos >= NUM_TX_BUFFER ) @@ -1518,8 +1418,8 @@ FST_WRW ( card, txDescrRing[pi][txp].bcnt, cnv_bcnt ( skb->len )); FST_WRB ( card, txDescrRing[pi][txp].bits, DMA_OWN | TX_STP | TX_ENP ); - port->stats.tx_packets++; - port->stats.tx_bytes += skb->len; + port->hdlc.stats.tx_packets++; + port->hdlc.stats.tx_bytes += skb->len; dev_kfree_skb ( skb ); @@ -1528,18 +1428,6 @@ } -static struct net_device_stats * -fst_get_stats ( struct net_device *dev ) -{ - struct fst_port_info *port; - - if (( port = dev->priv ) != NULL ) - return &port->stats; - else - return NULL; -} - - /* * Card setup having checked hardware resources. * Should be pretty bizarre if we get an error here (kernel memory @@ -1566,34 +1454,13 @@ */ for ( i = 0 ; i < card->nports ; i++ ) { - card->ports[i].if_ptr = &card->ports[i].pppdev; card->ports[i].card = card; card->ports[i].index = i; - card->ports[i].proto = FST_HDLC; card->ports[i].run = 0; - dev = kmalloc ( sizeof ( struct net_device ), GFP_KERNEL ); - if ( dev == NULL ) - { - printk_err ("Cannot allocate net_device for port %d\n", - i ); - /* No point going any further */ - card->nports = i; - break; - } - memset ( dev, 0, sizeof ( struct net_device )); - card->ports[i].dev = dev; - - if ( dev_alloc_name ( dev, FST_NDEV_NAME "%d") < 0 ) - { - printk_err ("Cannot allocate i/f name for port %d\n", - i ); - kfree ( dev ); - card->nports = i; - break; - } + dev = hdlc_to_dev ( &card->ports[i].hdlc ); - /* Fill in remainder of the net device info */ + /* Fill in the net device info */ /* Since this is a PCI setup this is purely * informational. Give them the buffer addresses * and basic card I/O. @@ -1609,26 +1476,19 @@ dev->base_addr = card->pci_conf; dev->irq = card->irq; - dev->get_stats = fst_get_stats; - dev->mtu = FST_DEF_MTU; - dev->change_mtu = fst_change_mtu; - dev->priv = &card->ports[i]; - dev->tx_queue_len = FST_TX_QUEUE_LEN; - dev->type = ARPHRD_MYTYPE; - dev->addr_len = 0; - dev->open = fst_open; - dev->stop = fst_close; - dev->hard_start_xmit = fst_start_xmit; - dev->do_ioctl = fst_ioctl; - dev->watchdog_timeo = FST_TX_TIMEOUT; - dev->tx_timeout = fst_tx_timeout; - dev->flags = IFF_POINTOPOINT|IFF_NOARP; - - if (( err = register_netdev ( dev )) < 0 ) - { - printk_err ("Cannot register %s (errno %d)\n", - dev->name, -err ); - kfree ( dev ); + dev->tx_queue_len = FST_TX_QUEUE_LEN; + dev->open = fst_open; + dev->stop = fst_close; + dev->do_ioctl = fst_ioctl; + dev->watchdog_timeo = FST_TX_TIMEOUT; + dev->tx_timeout = fst_tx_timeout; + card->ports[i].hdlc.attach = fst_attach; + card->ports[i].hdlc.xmit = fst_start_xmit; + + if (( err = register_hdlc_device ( &card->ports[i].hdlc )) < 0 ) + { + printk_err ("Cannot register HDLC device for port %d" + " (errno %d)\n", i, -err ); card->nports = i; break; } @@ -1637,8 +1497,8 @@ spin_lock_init ( &card->card_lock ); printk ( KERN_INFO "%s-%s: %s IRQ%d, %d ports\n", - card->ports[0].dev->name, - card->ports[card->nports-1].dev->name, + hdlc_to_dev(&card->ports[0].hdlc)->name, + hdlc_to_dev(&card->ports[card->nports-1].hdlc)->name, type_strings[card->type], card->irq, card->nports ); } @@ -1789,8 +1649,7 @@ for ( i = 0 ; i < card->nports ; i++ ) { - unregister_netdev ( card->ports[i].dev ); - kfree ( card->ports[i].dev ); + unregister_hdlc_device ( &card->ports[i].hdlc ); } fst_disable_intr ( card ); @@ -1810,7 +1669,7 @@ name: FST_NAME, id_table: fst_pci_dev_id, probe: fst_add_one, - remove: fst_remove_one, + remove: __devexit_p(fst_remove_one), suspend: NULL, resume: NULL, }; @@ -1830,4 +1689,3 @@ module_init ( fst_init ); module_exit ( fst_cleanup_module ); -MODULE_LICENSE("GPL"); diff -Nru a/drivers/net/wan/farsync.h b/drivers/net/wan/farsync.h --- a/drivers/net/wan/farsync.h Thu Mar 7 18:17:45 2002 +++ b/drivers/net/wan/farsync.h Thu Mar 7 18:17:45 2002 @@ -32,13 +32,8 @@ * A short common prefix is useful for routines within the driver to avoid * conflict with other similar drivers and I chosen to use "fst_" for this * purpose (FarSite T-series). - * - * Finally the device driver needs a short network interface name. Since - * "hdlc" is already in use I've chosen the even less informative "sync" - * for the present. */ #define FST_NAME "fst" /* In debug/info etc */ -#define FST_NDEV_NAME "sync" /* For net interface */ #define FST_DEV_NAME "farsync" /* For misc interfaces */ diff -Nru a/drivers/net/wan/hd64570.h b/drivers/net/wan/hd64570.h --- a/drivers/net/wan/hd64570.h Thu Mar 7 18:17:41 2002 +++ b/drivers/net/wan/hd64570.h Thu Mar 7 18:17:41 2002 @@ -152,7 +152,7 @@ u32 bp; /* Buffer Pointer (24 bits) */ u16 len; /* Data Length */ u8 stat; /* Status */ - u8 unused2; + u8 unused; /* pads to 2-byte boundary */ }__attribute__ ((packed)) pkt_desc; @@ -202,7 +202,11 @@ #define MD0_CRC_ITU_0 0x06 #define MD0_CRC_ITU 0x07 -#define MD2_NRZI 0x20 /* NRZI mode */ +#define MD2_NRZ 0x00 +#define MD2_NRZI 0x20 +#define MD2_MANCHESTER 0x80 +#define MD2_FM_MARK 0xA0 +#define MD2_FM_SPACE 0xC0 #define MD2_LOOPBACK 0x03 /* Local data Loopback */ #define CTL_NORTS 0x01 diff -Nru a/drivers/net/wan/hd6457x.c b/drivers/net/wan/hd6457x.c --- a/drivers/net/wan/hd6457x.c Thu Mar 7 18:17:39 2002 +++ b/drivers/net/wan/hd6457x.c Thu Mar 7 18:17:39 2002 @@ -42,13 +42,7 @@ #error Either hd64570.h or hd64572.h must be included #endif - -static card_t *first_card; -static card_t **new_card = &first_card; - - -/* Maximum events to handle at each interrupt - should I increase it? */ -#define INTR_WORK 4 +static char sca_version[]="1.09"; #define get_msci(port) (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET) #define get_dmac_rx(port) (phy_node(port) ? DMAC1RX_OFFSET : DMAC0RX_OFFSET) @@ -63,11 +57,19 @@ #define sca_ina(reg, card) sca_inw(reg, card) #define writea(value, ptr) writew(value, ptr) +#else /* HD64572 */ +#define sca_outa(value, reg, card) sca_outl(value, reg, card) +#define sca_ina(reg, card) sca_inl(reg, card) +#define writea(value, ptr) writel(value, ptr) +#endif + static inline int sca_intr_status(card_t *card) { + u8 result = 0; + +#ifdef __HD64570_H /* HD64570 */ u8 isr0 = sca_in(ISR0, card); u8 isr1 = sca_in(ISR1, card); - u8 result = 0; if (isr1 & 0x03) result |= SCA_INTR_DMAC_RX(0); if (isr1 & 0x0C) result |= SCA_INTR_DMAC_TX(0); @@ -76,19 +78,8 @@ if (isr0 & 0x0F) result |= SCA_INTR_MSCI(0); if (isr0 & 0xF0) result |= SCA_INTR_MSCI(1); - return result; -} - #else /* HD64572 */ -#define sca_outa(value, reg, card) sca_outl(value, reg, card) -#define sca_ina(reg, card) sca_inl(reg, card) -#define writea(value, ptr) writel(value, ptr) - - -static inline int sca_intr_status(card_t *card) -{ u32 isr0 = sca_inl(ISR0, card); - u8 result = 0; if (isr0 & 0x0000000F) result |= SCA_INTR_DMAC_RX(0); if (isr0 & 0x000000F0) result |= SCA_INTR_DMAC_TX(0); @@ -97,11 +88,17 @@ if (isr0 & 0x003E0000) result |= SCA_INTR_MSCI(0); if (isr0 & 0x3E000000) result |= SCA_INTR_MSCI(1); - return result; -} - #endif /* HD64570 vs HD64572 */ + if (!(result & SCA_INTR_DMAC_TX(0))) + if (sca_in(DSR_TX(0), card) & DSR_EOM) + result |= SCA_INTR_DMAC_TX(0); + if (!(result & SCA_INTR_DMAC_TX(1))) + if (sca_in(DSR_TX(1), card) & DSR_EOM) + result |= SCA_INTR_DMAC_TX(1); + + return result; +} @@ -250,8 +247,7 @@ -static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc, - u8 rxin) +static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc, u8 rxin) { struct sk_buff *skb; u16 len; @@ -289,13 +285,16 @@ openwin(card, 0); #endif skb_put(skb, len); -#ifdef DEBUG_PKT +#ifdef CONFIG_HDLC_DEBUG_PKT printk(KERN_DEBUG "%s RX(%i):", hdlc_to_name(&port->hdlc), skb->len); debug_frame(skb); #endif port->hdlc.stats.rx_packets++; port->hdlc.stats.rx_bytes += skb->len; - hdlc_netif_rx(&port->hdlc, skb); + skb->mac.raw = skb->data; + skb->dev = hdlc_to_dev(&port->hdlc); + skb->protocol = htons(ETH_P_HDLC); + netif_rx(skb); } @@ -320,14 +319,9 @@ pkt_desc *desc; u32 cda = sca_ina(dmac + CDAL, card); - if (cda == desc_off) + if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc))) break; /* No frame received */ -#ifdef __HD64572_H - if (cda == desc_off + 8) - break; /* SCA-II updates CDA in 2 steps */ -#endif - desc = desc_address(port, port->rxin, 0); stat = readb(&desc->stat); if (!(stat & ST_RX_EOM)) @@ -371,20 +365,17 @@ DSR_TX(phy_node(port)), card); while (1) { - u32 desc_off = desc_offset(port, port->txlast, 1); pkt_desc *desc; - u16 len; - if (sca_ina(dmac + CDAL, card) == desc_off) + u32 desc_off = desc_offset(port, port->txlast, 1); + u32 cda = sca_ina(dmac + CDAL, card); + if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc))) break; /* Transmitter is/will_be sending this frame */ desc = desc_address(port, port->txlast, 1); - len = readw(&desc->len); - port->hdlc.stats.tx_packets++; - port->hdlc.stats.tx_bytes += len; + port->hdlc.stats.tx_bytes += readw(&desc->len); writeb(0, &desc->stat); /* Free descriptor */ - port->txlast = (port->txlast + 1) % port_to_card(port)->ring_buffers; } @@ -398,7 +389,8 @@ static void sca_intr(int irq, void* dev_id, struct pt_regs *regs) { card_t *card = dev_id; - int boguscnt = INTR_WORK; +/* Maximum events to handle at each interrupt - should I increase it? */ + int boguscnt = 4; int i; u8 stat; @@ -421,9 +413,11 @@ } if (--boguscnt < 0) { +#if 0 printk(KERN_ERR "%s: too much work at " "interrupt\n", hdlc_to_name(&port->hdlc)); +#endif goto exit; } } @@ -437,47 +431,22 @@ -static inline int sca_set_loopback(port_t *port, int line) +static void sca_set_port(port_t *port) { card_t* card = port_to_card(port); u8 msci = get_msci(port); u8 md2 = sca_in(msci + MD2, card); - - switch(line) { - case LINE_DEFAULT: - md2 &= ~MD2_LOOPBACK; - port->line &= ~LINE_LOOPBACK; - break; - - case LINE_LOOPBACK: - md2 |= MD2_LOOPBACK; - port->line |= LINE_LOOPBACK; - break; - - default: - return -EINVAL; - } - - sca_out(md2, msci + MD2, card); - return 0; -} - - - -static void sca_set_clock(port_t *port) -{ - card_t *card = port_to_card(port); - u8 msci = get_msci(port); unsigned int tmc, br = 10, brv = 1024; - if (port->clkrate > 0) { + + if (port->settings.clock_rate > 0) { /* Try lower br for better accuracy*/ do { br--; brv >>= 1; /* brv = 2^9 = 512 max in specs */ /* Baud Rate = CLOCK_BASE / TMC / 2^BR */ - tmc = CLOCK_BASE / (brv * port->clkrate); + tmc = CLOCK_BASE / (brv * port->settings.clock_rate); }while(br > 1 && tmc <= 128); if (tmc < 1) { @@ -487,11 +456,11 @@ } else if (tmc > 255) tmc = 256; /* tmc=0 means 256 - low baud rates */ - port->clkrate = CLOCK_BASE / (brv * tmc); + port->settings.clock_rate = CLOCK_BASE / (brv * tmc); } else { br = 9; /* Minimum clock rate */ tmc = 256; /* 8bit = 0 */ - port->clkrate = CLOCK_BASE / (256 * 512); + port->settings.clock_rate = CLOCK_BASE / (256 * 512); } port->rxs = (port->rxs & ~CLK_BRG_MASK) | br; @@ -509,27 +478,59 @@ /* Set BRG bits */ sca_out(port->rxs, msci + RXS, card); sca_out(port->txs, msci + TXS, card); + + if (port->settings.loopback) + md2 |= MD2_LOOPBACK; + else + md2 &= ~MD2_LOOPBACK; + + sca_out(md2, msci + MD2, card); + } -static void sca_set_hdlc_mode(port_t *port, u8 idle, u8 crc, u8 nrzi) +static void sca_open(hdlc_device *hdlc) { + port_t *port = hdlc_to_port(hdlc); card_t* card = port_to_card(port); u8 msci = get_msci(port); - u8 md2 = (nrzi ? MD2_NRZI : 0) | - ((port->line & LINE_LOOPBACK) ? MD2_LOOPBACK : 0); - u8 ctl = (idle ? CTL_IDLE : 0); -#ifdef __HD64572_H - ctl |= CTL_URCT | CTL_URSKP; /* Skip the rest of underrun frame */ -#endif + u8 md0, md2; + + switch(port->encoding) { + case ENCODING_NRZ: md2 = MD2_NRZ; break; + case ENCODING_NRZI: md2 = MD2_NRZI; break; + case ENCODING_FM_MARK: md2 = MD2_FM_MARK; break; + case ENCODING_FM_SPACE: md2 = MD2_FM_SPACE; break; + default: md2 = MD2_MANCHESTER; + } + + if (port->settings.loopback) + md2 |= MD2_LOOPBACK; + + switch(port->parity) { + case PARITY_CRC16_PR0: md0 = MD0_HDLC | MD0_CRC_16_0; break; + case PARITY_CRC16_PR1: md0 = MD0_HDLC | MD0_CRC_16; break; +#ifdef __HD64570_H + case PARITY_CRC16_PR0_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU_0; break; +#else + case PARITY_CRC32_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU32; break; +#endif + case PARITY_CRC16_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU; break; + default: md0 = MD0_HDLC | MD0_CRC_NONE; + } sca_out(CMD_RESET, msci + CMD, card); - sca_out(MD0_HDLC | crc, msci + MD0, card); + sca_out(md0, msci + MD0, card); sca_out(0x00, msci + MD1, card); /* no address field check */ sca_out(md2, msci + MD2, card); sca_out(0x7E, msci + IDL, card); /* flag character 0x7E */ - sca_out(ctl, msci + CTL, card); +#ifdef __HD64570_H + sca_out(CTL_IDLE, msci + CTL, card); +#else + /* Skip the rest of underrun frame */ + sca_out(CTL_IDLE | CTL_URCT | CTL_URSKP, msci + CTL, card); +#endif #ifdef __HD64570_H /* Allow at least 8 bytes before requesting RX DMA operation */ @@ -539,24 +540,28 @@ sca_out(0x14, msci + TRC1, card); /* +1=TXRDY/DMA deactiv condition */ #else sca_out(0x0F, msci + RNR, card); /* +1=RX DMA activation condition */ - /* Setting than to larger value may cause Illegal Access */ - sca_out(0x20, msci + TNR0, card); /* =TX DMA activation condition */ - sca_out(0x30, msci + TNR1, card); /* +1=TX DMA deactivation condition*/ - sca_out(0x04, msci + TCR, card); /* =Critical TX DMA activ condition */ + sca_out(0x3C, msci + TFS, card); /* +1 = TX start */ + sca_out(0x38, msci + TCR, card); /* =Critical TX DMA activ condition */ + sca_out(0x38, msci + TNR0, card); /* =TX DMA activation condition */ + sca_out(0x3F, msci + TNR1, card); /* +1=TX DMA deactivation condition*/ #endif +/* We're using the following interrupts: + - TXINT (DMAC completed all transmisions, underflow or CTS change) + - all DMA interrupts +*/ #ifdef __HD64570_H /* MSCI TX INT IRQ enable */ sca_out(IE0_TXINT, msci + IE0, card); - sca_out(IE1_UDRN, msci + IE1, card); /* TX underrun IRQ */ + sca_out(IE1_UDRN, msci + IE1, card); /* TX underrun -> TXINT */ sca_out(sca_in(IER0, card) | (phy_node(port) ? 0x80 : 0x08), IER0, card); /* DMA IRQ enable */ sca_out(sca_in(IER1, card) | (phy_node(port) ? 0xF0 : 0x0F), IER1, card); #else - /* MSCI TX INT and underrrun IRQ enable */ + /* MSCI TX INT IRQ enable */ sca_outl(IE0_TXINT | IE0_UDRN, msci + IE0, card); /* DMA & MSCI IRQ enable */ sca_outl(sca_in(IER0, card) | @@ -573,11 +578,52 @@ sca_out(port->txs, msci + TXS, card); sca_out(CMD_TX_ENABLE, msci + CMD, card); sca_out(CMD_RX_ENABLE, msci + CMD, card); + + netif_start_queue(hdlc_to_dev(hdlc)); +} + + + +static void sca_close(hdlc_device *hdlc) +{ + port_t *port = hdlc_to_port(hdlc); + + /* reset channel */ + netif_stop_queue(hdlc_to_dev(hdlc)); + sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port)); } -#ifdef DEBUG_RINGS +static int sca_attach(hdlc_device *hdlc, unsigned short encoding, + unsigned short parity) +{ + if (encoding != ENCODING_NRZ && + encoding != ENCODING_NRZI && + encoding != ENCODING_FM_MARK && + encoding != ENCODING_FM_SPACE && + encoding != ENCODING_MANCHESTER) + return -EINVAL; + + if (parity != PARITY_NONE && + parity != PARITY_CRC16_PR0 && + parity != PARITY_CRC16_PR1 && +#ifdef __HD64570_H + parity != PARITY_CRC16_PR0_CCITT && +#else + parity != PARITY_CRC32_PR1_CCITT && +#endif + parity != PARITY_CRC16_PR1_CCITT) + return -EINVAL; + + hdlc_to_port(hdlc)->encoding = encoding; + hdlc_to_port(hdlc)->parity = parity; + return 0; +} + + + +#ifdef CONFIG_HDLC_DEBUG_RINGS static void sca_dump_rings(hdlc_device *hdlc) { port_t *port = hdlc_to_port(hdlc); @@ -644,34 +690,14 @@ openwin(card, page); /* Restore original page */ #endif } -#endif /* DEBUG_RINGS */ - - - -static void sca_open(hdlc_device *hdlc) -{ - port_t *port = hdlc_to_port(hdlc); - - sca_set_hdlc_mode(port, 1, MD0_CRC_ITU, 0); - netif_start_queue(hdlc_to_dev(hdlc)); -} - - -static void sca_close(hdlc_device *hdlc) -{ - port_t *port = hdlc_to_port(hdlc); - - /* reset channel */ - netif_stop_queue(hdlc_to_dev(hdlc)); - sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port)); -} +#endif /* CONFIG_HDLC_DEBUG_RINGS */ -static int sca_xmit(hdlc_device *hdlc, struct sk_buff *skb) +static int sca_xmit(struct sk_buff *skb, struct net_device *dev) { + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); - struct net_device *dev = hdlc_to_dev(hdlc); card_t *card = port_to_card(port); pkt_desc *desc; u32 buff, len; @@ -685,7 +711,7 @@ desc = desc_address(port, port->txin + 1, 1); if (readb(&desc->stat)) { /* allow 1 packet gap */ /* should never happen - previous xmit should stop queue */ -#ifdef DEBUG_PKT +#ifdef CONFIG_HDLC_DEBUG_PKT printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name); #endif netif_stop_queue(dev); @@ -693,7 +719,7 @@ return 1; /* request packet to be queued */ } -#ifdef DEBUG_PKT +#ifdef CONFIG_HDLC_DEBUG_PKT printk(KERN_DEBUG "%s TX(%i):", hdlc_to_name(hdlc), skb->len); debug_frame(skb); #endif diff -Nru a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c --- a/drivers/net/wan/hdlc.c Thu Mar 7 18:17:42 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1453 +0,0 @@ -/* - * Generic HDLC support routines for Linux - * - * Copyright (C) 1999, 2000 Krzysztof Halasa - * - * 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. - * - * Current status: - * - this is work in progress - * - not heavily tested on SMP - * - currently supported: - * * raw IP-in-HDLC - * * Cisco HDLC - * * Frame Relay with ANSI or CCITT LMI (both user and network side) - * * PPP (using syncppp.c) - * * X.25 - * - * Use sethdlc utility to set line parameters, protocol and PVCs - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* #define DEBUG_PKT */ -/* #define DEBUG_HARD_HEADER */ -/* #define DEBUG_FECN */ -/* #define DEBUG_BECN */ - -static const char* version = "HDLC support module revision 1.02 for Linux 2.4"; - - -#define CISCO_MULTICAST 0x8F /* Cisco multicast address */ -#define CISCO_UNICAST 0x0F /* Cisco unicast address */ -#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */ -#define CISCO_SYS_INFO 0x2000 /* Cisco interface/system info */ -#define CISCO_ADDR_REQ 0 /* Cisco address request */ -#define CISCO_ADDR_REPLY 1 /* Cisco address reply */ -#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */ - -static int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); - -/******************************************************** - * - * Cisco HDLC support - * - *******************************************************/ - -static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev, - u16 type, void *daddr, void *saddr, - unsigned int len) -{ - hdlc_header *data; -#ifdef DEBUG_HARD_HEADER - printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name); -#endif - - skb_push(skb, sizeof(hdlc_header)); - data = (hdlc_header*)skb->data; - if (type == CISCO_KEEPALIVE) - data->address = CISCO_MULTICAST; - else - data->address = CISCO_UNICAST; - data->control = 0; - data->protocol = htons(type); - - return sizeof(hdlc_header); -} - - - -static void cisco_keepalive_send(hdlc_device *hdlc, u32 type, - u32 par1, u32 par2) -{ - struct sk_buff *skb; - cisco_packet *data; - - skb = dev_alloc_skb(sizeof(hdlc_header)+sizeof(cisco_packet)); - if (!skb) { - printk(KERN_WARNING "%s: Memory squeeze on cisco_keepalive_send()\n", - hdlc_to_name(hdlc)); - return; - } - skb_reserve(skb, 4); - cisco_hard_header(skb, hdlc_to_dev(hdlc), CISCO_KEEPALIVE, - NULL, NULL, 0); - data = (cisco_packet*)skb->tail; - - data->type = htonl(type); - data->par1 = htonl(par1); - data->par2 = htonl(par2); - data->rel = 0xFFFF; - data->time = htonl(jiffies * 1000 / HZ); - - skb_put(skb, sizeof(cisco_packet)); - skb->priority = TC_PRIO_CONTROL; - skb->dev = hdlc_to_dev(hdlc); - - dev_queue_xmit(skb); -} - - - -static void cisco_netif(hdlc_device *hdlc, struct sk_buff *skb) -{ - hdlc_header *data = (hdlc_header*)skb->data; - cisco_packet *cisco_data; - struct in_device *in_dev; - u32 addr, mask; - - if (skb->lenaddress != CISCO_MULTICAST && - data->address != CISCO_UNICAST) - goto rx_error; - - skb_pull(skb, sizeof(hdlc_header)); - - switch(ntohs(data->protocol)) { - case ETH_P_IP: - case ETH_P_IPX: - case ETH_P_IPV6: - skb->protocol = data->protocol; - skb->dev = hdlc_to_dev(hdlc); - netif_rx(skb); - return; - - case CISCO_SYS_INFO: - /* Packet is not needed, drop it. */ - dev_kfree_skb_any(skb); - return; - - case CISCO_KEEPALIVE: - if (skb->len != CISCO_PACKET_LEN && - skb->len != CISCO_BIG_PACKET_LEN) { - printk(KERN_INFO "%s: Invalid length of Cisco " - "control packet (%d bytes)\n", - hdlc_to_name(hdlc), skb->len); - goto rx_error; - } - - cisco_data = (cisco_packet*)skb->data; - - switch(ntohl (cisco_data->type)) { - case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */ - in_dev = hdlc_to_dev(hdlc)->ip_ptr; - addr = 0; - mask = ~0; /* is the mask correct? */ - - if (in_dev != NULL) { - struct in_ifaddr **ifap = &in_dev->ifa_list; - - while (*ifap != NULL) { - if (strcmp(hdlc_to_name(hdlc), - (*ifap)->ifa_label) == 0) { - addr = (*ifap)->ifa_local; - mask = (*ifap)->ifa_mask; - break; - } - ifap = &(*ifap)->ifa_next; - } - - cisco_keepalive_send(hdlc, CISCO_ADDR_REPLY, - addr, mask); - } - dev_kfree_skb_any(skb); - return; - - case CISCO_ADDR_REPLY: - printk(KERN_INFO "%s: Unexpected Cisco IP address " - "reply\n", hdlc_to_name(hdlc)); - goto rx_error; - - case CISCO_KEEPALIVE_REQ: - hdlc->lmi.rxseq = ntohl(cisco_data->par1); - if (ntohl(cisco_data->par2) == hdlc->lmi.txseq) { - hdlc->lmi.last_poll = jiffies; - if (!(hdlc->lmi.state & LINK_STATE_RELIABLE)) { - u32 sec, min, hrs, days; - sec = ntohl(cisco_data->time) / 1000; - min = sec / 60; sec -= min * 60; - hrs = min / 60; min -= hrs * 60; - days = hrs / 24; hrs -= days * 24; - printk(KERN_INFO "%s: Link up (peer " - "uptime %ud%uh%um%us)\n", - hdlc_to_name(hdlc), days, hrs, - min, sec); - } - hdlc->lmi.state |= LINK_STATE_RELIABLE; - } - - dev_kfree_skb_any(skb); - return; - } /* switch(keepalive type) */ - } /* switch(protocol) */ - - printk(KERN_INFO "%s: Unsupported protocol %x\n", hdlc_to_name(hdlc), - data->protocol); - dev_kfree_skb_any(skb); - return; - - rx_error: - hdlc->stats.rx_errors++; /* Mark error */ - dev_kfree_skb_any(skb); -} - - - -static void cisco_timer(unsigned long arg) -{ - hdlc_device *hdlc = (hdlc_device*)arg; - - if ((hdlc->lmi.state & LINK_STATE_RELIABLE) && - (jiffies - hdlc->lmi.last_poll >= hdlc->lmi.T392 * HZ)) { - hdlc->lmi.state &= ~LINK_STATE_RELIABLE; - printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc)); - } - - cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ, ++hdlc->lmi.txseq, - hdlc->lmi.rxseq); - hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ; - - hdlc->timer.function = cisco_timer; - hdlc->timer.data = arg; - add_timer(&hdlc->timer); -} - - - -/****************************************************************** - * - * generic Frame Relay routines - * - *****************************************************************/ - - -static int fr_hard_header(struct sk_buff *skb, struct net_device *dev, - u16 type, void *daddr, void *saddr, unsigned int len) -{ - u16 head_len; - - if (!daddr) - daddr = dev->broadcast; - -#ifdef DEBUG_HARD_HEADER - printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name); -#endif - - switch(type) { - case ETH_P_IP: - head_len = 4; - skb_push(skb, head_len); - skb->data[3] = NLPID_IP; - break; - - case ETH_P_IPV6: - head_len = 4; - skb_push(skb, head_len); - skb->data[3] = NLPID_IPV6; - break; - - case LMI_PROTO: - head_len = 4; - skb_push(skb, head_len); - skb->data[3] = LMI_PROTO; - break; - - default: - head_len = 10; - skb_push(skb, head_len); - skb->data[3] = FR_PAD; - skb->data[4] = NLPID_SNAP; - skb->data[5] = FR_PAD; - skb->data[6] = FR_PAD; - skb->data[7] = FR_PAD; - skb->data[8] = type>>8; - skb->data[9] = (u8)type; - } - - memcpy(skb->data, daddr, 2); - skb->data[2] = FR_UI; - - return head_len; -} - - - -static inline void fr_log_dlci_active(pvc_device *pvc) -{ - printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc), - pvc->state & PVC_STATE_ACTIVE ? "" : "in", - pvc->state & PVC_STATE_NEW ? " new" : ""); -} - - - -static inline u8 fr_lmi_nextseq(u8 x) -{ - x++; - return x ? x : 1; -} - - - -static void fr_lmi_send(hdlc_device *hdlc, int fullrep) -{ - struct sk_buff *skb; - pvc_device *pvc = hdlc->first_pvc; - int len = mode_is(hdlc, MODE_FR_ANSI) ? LMI_ANSI_LENGTH : LMI_LENGTH; - int stat_len = 3; - u8 *data; - int i = 0; - - if (mode_is(hdlc, MODE_DCE) && fullrep) { - len += hdlc->pvc_count * (2 + stat_len); - if (len > HDLC_MAX_MTU) { - printk(KERN_WARNING "%s: Too many PVCs while sending " - "LMI full report\n", hdlc_to_name(hdlc)); - return; - } - } - - skb = dev_alloc_skb(len); - if (!skb) { - printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n", - hdlc_to_name(hdlc)); - return; - } - memset(skb->data, 0, len); - skb_reserve(skb, 4); - fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0); - data = skb->tail; - data[i++] = LMI_CALLREF; - data[i++] = mode_is(hdlc, MODE_DCE) ? LMI_STATUS : LMI_STATUS_ENQUIRY; - if (mode_is(hdlc, MODE_FR_ANSI)) - data[i++] = LMI_ANSI_LOCKSHIFT; - data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : - LMI_REPTYPE; - data[i++] = LMI_REPT_LEN; - data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY; - - data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE; - data[i++] = LMI_INTEG_LEN; - data[i++] = hdlc->lmi.txseq = fr_lmi_nextseq(hdlc->lmi.txseq); - data[i++] = hdlc->lmi.rxseq; - - if (mode_is(hdlc, MODE_DCE) && fullrep) { - while (pvc) { - data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? - LMI_CCITT_PVCSTAT:LMI_PVCSTAT; - data[i++] = stat_len; - - if ((hdlc->lmi.state & LINK_STATE_RELIABLE) && - (pvc->netdev.flags & IFF_UP) && - !(pvc->state & (PVC_STATE_ACTIVE|PVC_STATE_NEW))) { - pvc->state |= PVC_STATE_NEW; - fr_log_dlci_active(pvc); - } - - dlci_to_status(hdlc, netdev_dlci(&pvc->netdev), - data+i, pvc->state); - i += stat_len; - pvc = pvc->next; - } - } - - skb_put(skb, i); - skb->priority = TC_PRIO_CONTROL; - skb->dev = hdlc_to_dev(hdlc); - - dev_queue_xmit(skb); -} - - - -static void fr_timer(unsigned long arg) -{ - hdlc_device *hdlc = (hdlc_device*)arg; - int i, cnt = 0, reliable; - u32 list; - - if (mode_is(hdlc, MODE_DCE)) - reliable = (jiffies - hdlc->lmi.last_poll < hdlc->lmi.T392*HZ); - else { - hdlc->lmi.last_errors <<= 1; /* Shift the list */ - if (hdlc->lmi.state & LINK_STATE_REQUEST) { - printk(KERN_INFO "%s: No LMI status reply received\n", - hdlc_to_name(hdlc)); - hdlc->lmi.last_errors |= 1; - } - - for (i = 0, list = hdlc->lmi.last_errors; i < hdlc->lmi.N393; - i++, list >>= 1) - cnt += (list & 1); /* errors count */ - - reliable = (cnt < hdlc->lmi.N392); - } - - if ((hdlc->lmi.state & LINK_STATE_RELIABLE) != - (reliable ? LINK_STATE_RELIABLE : 0)) { - pvc_device *pvc = hdlc->first_pvc; - - while (pvc) {/* Deactivate all PVCs */ - pvc->state &= ~(PVC_STATE_NEW | PVC_STATE_ACTIVE); - pvc = pvc->next; - } - - hdlc->lmi.state ^= LINK_STATE_RELIABLE; - printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc), - reliable ? "" : "un"); - - if (reliable) { - hdlc->lmi.N391cnt = 0; /* Request full status */ - hdlc->lmi.state |= LINK_STATE_CHANGED; - } - } - - if (mode_is(hdlc, MODE_DCE)) - hdlc->timer.expires = jiffies + hdlc->lmi.T392*HZ; - else { - if (hdlc->lmi.N391cnt) - hdlc->lmi.N391cnt--; - - fr_lmi_send(hdlc, hdlc->lmi.N391cnt == 0); - - hdlc->lmi.state |= LINK_STATE_REQUEST; - hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ; - } - - hdlc->timer.function = fr_timer; - hdlc->timer.data = arg; - add_timer(&hdlc->timer); -} - - - -static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb) -{ - int stat_len; - pvc_device *pvc; - int reptype = -1, error; - u8 rxseq, txseq; - int i; - - if (skb->len < (mode_is(hdlc, MODE_FR_ANSI) ? - LMI_ANSI_LENGTH : LMI_LENGTH)) { - printk(KERN_INFO "%s: Short LMI frame\n", hdlc_to_name(hdlc)); - return 1; - } - - if (skb->data[5] != (!mode_is(hdlc, MODE_DCE) ? - LMI_STATUS : LMI_STATUS_ENQUIRY)) { - printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n", - hdlc_to_name(hdlc), skb->data[2], - mode_is(hdlc, MODE_DCE) ? "enquiry" : "reply"); - return 1; - } - - i = mode_is(hdlc, MODE_FR_ANSI) ? 7 : 6; - - if (skb->data[i] != - (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) { - printk(KERN_INFO "%s: Not a report type=%x\n", - hdlc_to_name(hdlc), skb->data[i]); - return 1; - } - i++; - - i++; /* Skip length field */ - - reptype = skb->data[i++]; - - if (skb->data[i]!= - (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE)) { - printk(KERN_INFO "%s: Unsupported status element=%x\n", - hdlc_to_name(hdlc), skb->data[i]); - return 1; - } - i++; - - i++; /* Skip length field */ - - hdlc->lmi.rxseq = skb->data[i++]; /* TX sequence from peer */ - rxseq = skb->data[i++]; /* Should confirm our sequence */ - - txseq = hdlc->lmi.txseq; - - if (mode_is(hdlc, MODE_DCE)) { - if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) { - printk(KERN_INFO "%s: Unsupported report type=%x\n", - hdlc_to_name(hdlc), reptype); - return 1; - } - } - - error = 0; - if (!(hdlc->lmi.state & LINK_STATE_RELIABLE)) - error = 1; - - if (rxseq == 0 || rxseq != txseq) { - hdlc->lmi.N391cnt = 0; /* Ask for full report next time */ - error = 1; - } - - if (mode_is(hdlc, MODE_DCE)) { - if ((hdlc->lmi.state & LINK_STATE_FULLREP_SENT) && !error) { -/* Stop sending full report - the last one has been confirmed by DTE */ - hdlc->lmi.state &= ~LINK_STATE_FULLREP_SENT; - pvc = hdlc->first_pvc; - while (pvc) { - if (pvc->state & PVC_STATE_NEW) { - pvc->state &= ~PVC_STATE_NEW; - pvc->state |= PVC_STATE_ACTIVE; - fr_log_dlci_active(pvc); - -/* Tell DTE that new PVC is now active */ - hdlc->lmi.state |= LINK_STATE_CHANGED; - } - pvc = pvc->next; - } - } - - if (hdlc->lmi.state & LINK_STATE_CHANGED) { - reptype = LMI_FULLREP; - hdlc->lmi.state |= LINK_STATE_FULLREP_SENT; - hdlc->lmi.state &= ~LINK_STATE_CHANGED; - } - - fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0); - return 0; - } - - /* DTE */ - - if (reptype != LMI_FULLREP || error) - return 0; - - stat_len = 3; - pvc = hdlc->first_pvc; - - while (pvc) { - pvc->newstate = 0; - pvc = pvc->next; - } - - while (skb->len >= i + 2 + stat_len) { - u16 dlci; - u8 state = 0; - - if (skb->data[i] != (mode_is(hdlc, MODE_FR_CCITT) ? - LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) { - printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n", - hdlc_to_name(hdlc), skb->data[i]); - return 1; - } - i++; - - if (skb->data[i] != stat_len) { - printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n", - hdlc_to_name(hdlc), skb->data[i]); - return 1; - } - i++; - - dlci = status_to_dlci(hdlc, skb->data+i, &state); - pvc = find_pvc(hdlc, dlci); - - if (pvc) - pvc->newstate = state; - else if (state == PVC_STATE_NEW) - printk(KERN_INFO "%s: new PVC available, DLCI=%u\n", - hdlc_to_name(hdlc), dlci); - - i += stat_len; - } - - pvc = hdlc->first_pvc; - - while (pvc) { - if (pvc->newstate == PVC_STATE_NEW) - pvc->newstate = PVC_STATE_ACTIVE; - - pvc->newstate |= (pvc->state & - ~(PVC_STATE_NEW|PVC_STATE_ACTIVE)); - if (pvc->state != pvc->newstate) { - pvc->state = pvc->newstate; - fr_log_dlci_active(pvc); - } - pvc = pvc->next; - } - - /* Next full report after N391 polls */ - hdlc->lmi.N391cnt = hdlc->lmi.N391; - - return 0; -} - - - -static void fr_netif(hdlc_device *hdlc, struct sk_buff *skb) -{ - fr_hdr *fh = (fr_hdr*)skb->data; - u8 *data = skb->data; - u16 dlci; - pvc_device *pvc; - - if (skb->len<4 || fh->ea1 || data[2] != FR_UI) - goto rx_error; - - dlci = q922_to_dlci(skb->data); - - if (dlci == LMI_DLCI) { - if (data[3] == LMI_PROTO) { - if (fr_lmi_recv(hdlc, skb)) - goto rx_error; - else { - /* No request pending */ - hdlc->lmi.state &= ~LINK_STATE_REQUEST; - hdlc->lmi.last_poll = jiffies; - dev_kfree_skb_any(skb); - return; - } - } - - printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n", - hdlc_to_name(hdlc)); - goto rx_error; - } - - pvc = find_pvc(hdlc, dlci); - if (!pvc) { -#ifdef DEBUG_PKT - printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n", - hdlc_to_name(hdlc), dlci); -#endif - goto rx_error; - } - - if ((pvc->netdev.flags & IFF_UP) == 0) { -#ifdef DEBUG_PKT - printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n", - hdlc_to_name(hdlc), dlci); -#endif - goto rx_error; - } - - pvc->stats.rx_packets++; /* PVC traffic */ - pvc->stats.rx_bytes += skb->len; - - if ((pvc->state & PVC_STATE_FECN) != (fh->fecn ? PVC_STATE_FECN : 0)) { -#ifdef DEBUG_FECN - printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc), - fh->fecn ? "N" : "FF"); -#endif - pvc->state ^= PVC_STATE_FECN; - } - - if ((pvc->state & PVC_STATE_BECN) != (fh->becn ? PVC_STATE_BECN : 0)) { -#ifdef DEBUG_FECN - printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc), - fh->becn ? "N" : "FF"); -#endif - pvc->state ^= PVC_STATE_BECN; - } - - if (pvc->state & PVC_STATE_BECN) - pvc->stats.rx_compressed++; - - if (data[3] == NLPID_IP) { - skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ - skb->protocol = htons(ETH_P_IP); - skb->dev = &pvc->netdev; - netif_rx(skb); - return; - } - - - if (data[3] == NLPID_IPV6) { - skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ - skb->protocol = htons(ETH_P_IPV6); - skb->dev = &pvc->netdev; - netif_rx(skb); - return; - } - - if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD && - data[6] == FR_PAD && data[7] == FR_PAD && - ((data[8]<<8) | data[9]) == ETH_P_ARP) { - skb_pull(skb, 10); - skb->protocol = htons(ETH_P_ARP); - skb->dev = &pvc->netdev; - netif_rx(skb); - return; - } - - printk(KERN_INFO "%s: Unusupported protocol %x\n", - hdlc_to_name(hdlc), data[3]); - dev_kfree_skb_any(skb); - return; - - rx_error: - hdlc->stats.rx_errors++; /* Mark error */ - dev_kfree_skb_any(skb); -} - - - -static void fr_cisco_open(hdlc_device *hdlc) -{ - hdlc->lmi.state = LINK_STATE_CHANGED; - hdlc->lmi.txseq = hdlc->lmi.rxseq = 0; - hdlc->lmi.last_errors = 0xFFFFFFFF; - hdlc->lmi.N391cnt = 0; - - init_timer(&hdlc->timer); - hdlc->timer.expires = jiffies + HZ; /* First poll after 1 second */ - hdlc->timer.function = mode_is(hdlc, MODE_FR) ? fr_timer : cisco_timer; - hdlc->timer.data = (unsigned long)hdlc; - add_timer(&hdlc->timer); -} - - - -static void fr_cisco_close(hdlc_device *hdlc) -{ - pvc_device *pvc = hdlc->first_pvc; - - del_timer_sync(&hdlc->timer); - - while(pvc) { /* NULL in Cisco mode */ - dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */ - pvc = pvc->next; - } -} - - - -/****************************************************************** - * - * generic HDLC routines - * - *****************************************************************/ - - - -static int hdlc_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - - - -/******************************************************** - * - * PVC device routines - * - *******************************************************/ - -static int pvc_open(struct net_device *dev) -{ - pvc_device *pvc = dev_to_pvc(dev); - int result = 0; - - if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0) - return -EIO; /* Master must be UP in order to activate PVC */ - - memset(&(pvc->stats), 0, sizeof(struct net_device_stats)); - pvc->state = 0; - - if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->open_pvc) - result = pvc->master->open_pvc(pvc); - if (result) - return result; - - pvc->master->lmi.state |= LINK_STATE_CHANGED; - return 0; -} - - - -static int pvc_close(struct net_device *dev) -{ - pvc_device *pvc = dev_to_pvc(dev); - pvc->state = 0; - - if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->close_pvc) - pvc->master->close_pvc(pvc); - - pvc->master->lmi.state |= LINK_STATE_CHANGED; - return 0; -} - - - -static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) -{ - pvc_device *pvc = dev_to_pvc(dev); - - if (pvc->state & PVC_STATE_ACTIVE) { - skb->dev = hdlc_to_dev(pvc->master); - pvc->stats.tx_bytes += skb->len; - pvc->stats.tx_packets++; - if (pvc->state & PVC_STATE_FECN) - pvc->stats.tx_compressed++; /* TX Congestion counter */ - dev_queue_xmit(skb); - } else { - pvc->stats.tx_dropped++; - dev_kfree_skb(skb); - } - - return 0; -} - - - -static struct net_device_stats *pvc_get_stats(struct net_device *dev) -{ - pvc_device *pvc = dev_to_pvc(dev); - return &pvc->stats; -} - - - -static int pvc_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - - - -static void destroy_pvc_list(hdlc_device *hdlc) -{ - pvc_device *pvc = hdlc->first_pvc; - while(pvc) { - pvc_device *next = pvc->next; - unregister_netdevice(&pvc->netdev); - kfree(pvc); - pvc = next; - } - - hdlc->first_pvc = NULL; /* All PVCs destroyed */ - hdlc->pvc_count = 0; - hdlc->lmi.state |= LINK_STATE_CHANGED; -} - - - -/******************************************************** - * - * X.25 protocol support routines - * - *******************************************************/ - -#ifdef CONFIG_HDLC_X25 -/* These functions are callbacks called by LAPB layer */ - -void x25_connect_disconnect(void *token, int reason, int code) -{ - hdlc_device *hdlc = token; - struct sk_buff *skb; - unsigned char *ptr; - - if ((skb = dev_alloc_skb(1)) == NULL) { - printk(KERN_ERR "%s: out of memory\n", hdlc_to_name(hdlc)); - return; - } - - ptr = skb_put(skb, 1); - *ptr = code; - - skb->dev = hdlc_to_dev(hdlc); - skb->protocol = htons(ETH_P_X25); - skb->mac.raw = skb->data; - skb->pkt_type = PACKET_HOST; - - netif_rx(skb); -} - -void x25_connected(void *token, int reason) -{ - x25_connect_disconnect(token, reason, 1); -} - -void x25_disconnected(void *token, int reason) -{ - x25_connect_disconnect(token, reason, 2); -} - - -int x25_data_indication(void *token, struct sk_buff *skb) -{ - hdlc_device *hdlc = token; - unsigned char *ptr; - - ptr = skb_push(skb, 1); - *ptr = 0; - - skb->dev = hdlc_to_dev(hdlc); - skb->protocol = htons(ETH_P_X25); - skb->mac.raw = skb->data; - skb->pkt_type = PACKET_HOST; - - return netif_rx(skb); -} - - -void x25_data_transmit(void *token, struct sk_buff *skb) -{ - hdlc_device *hdlc = token; - hdlc->xmit(hdlc, skb); /* Ignore return value :-( */ -} -#endif /* CONFIG_HDLC_X25 */ - - -/******************************************************** - * - * HDLC device routines - * - *******************************************************/ - -static int hdlc_open(struct net_device *dev) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - int result; - - if (hdlc->mode == MODE_NONE) - return -ENOSYS; - - memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); - - if (mode_is(hdlc, MODE_FR | MODE_SOFT) || - mode_is(hdlc, MODE_CISCO | MODE_SOFT)) - fr_cisco_open(hdlc); -#ifdef CONFIG_HDLC_PPP - else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) { - sppp_attach(&hdlc->pppdev); - /* sppp_attach nukes them. We don't need syncppp's ioctl */ - dev->do_ioctl = hdlc_ioctl; - hdlc->pppdev.sppp.pp_flags &= ~PP_CISCO; - dev->type = ARPHRD_PPP; - result = sppp_open(dev); - if (result) { - sppp_detach(dev); - return result; - } - } -#endif -#ifdef CONFIG_HDLC_X25 - else if (mode_is(hdlc, MODE_X25)) { - struct lapb_register_struct cb; - - cb.connect_confirmation = x25_connected; - cb.connect_indication = x25_connected; - cb.disconnect_confirmation = x25_disconnected; - cb.disconnect_indication = x25_disconnected; - cb.data_indication = x25_data_indication; - cb.data_transmit = x25_data_transmit; - - result = lapb_register(hdlc, &cb); - if (result != LAPB_OK) - return result; - } -#endif - result = hdlc->open(hdlc); - if (result) { - if (mode_is(hdlc, MODE_FR | MODE_SOFT) || - mode_is(hdlc, MODE_CISCO | MODE_SOFT)) - fr_cisco_close(hdlc); -#ifdef CONFIG_HDLC_PPP - else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) { - sppp_close(dev); - sppp_detach(dev); - dev->rebuild_header = NULL; - dev->change_mtu = hdlc_change_mtu; - dev->mtu = HDLC_MAX_MTU; - dev->hard_header_len = 16; - } -#endif -#ifdef CONFIG_HDLC_X25 - else if (mode_is(hdlc, MODE_X25)) - lapb_unregister(hdlc); -#endif - } - - return result; -} - - - -static int hdlc_close(struct net_device *dev) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - - hdlc->close(hdlc); - - if (mode_is(hdlc, MODE_FR | MODE_SOFT) || - mode_is(hdlc, MODE_CISCO | MODE_SOFT)) - fr_cisco_close(hdlc); -#ifdef CONFIG_HDLC_PPP - else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) { - sppp_close(dev); - sppp_detach(dev); - dev->rebuild_header = NULL; - dev->change_mtu = hdlc_change_mtu; - dev->mtu = HDLC_MAX_MTU; - dev->hard_header_len = 16; - } -#endif -#ifdef CONFIG_HDLC_X25 - else if (mode_is(hdlc, MODE_X25)) - lapb_unregister(hdlc); -#endif - return 0; -} - - - -static int hdlc_xmit(struct sk_buff *skb, struct net_device *dev) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - -#ifdef CONFIG_HDLC_X25 - if (mode_is(hdlc, MODE_X25 | MODE_SOFT)) { - int result; - - - /* X.25 to LAPB */ - switch (skb->data[0]) { - case 0: /* Data to be transmitted */ - skb_pull(skb, 1); - if ((result = lapb_data_request(hdlc, skb)) != LAPB_OK) - dev_kfree_skb(skb); - return 0; - - case 1: - if ((result = lapb_connect_request(hdlc))!= LAPB_OK) { - if (result == LAPB_CONNECTED) { - /* Send connect confirm. msg to level 3 */ - x25_connected(hdlc, 0); - } else { - printk(KERN_ERR "%s: LAPB connect " - "request failed, error code = " - "%i\n", hdlc_to_name(hdlc), - result); - } - } - break; - - case 2: - if ((result=lapb_disconnect_request(hdlc))!=LAPB_OK) { - if (result == LAPB_NOTCONNECTED) { - /* Send disconnect confirm. msg to level 3 */ - x25_disconnected(hdlc, 0); - } else { - printk(KERN_ERR "%s: LAPB disconnect " - "request failed, error code = " - "%i\n", hdlc_to_name(hdlc), - result); - } - } - break; - - default: - /* to be defined */ - break; - } - - dev_kfree_skb(skb); - return 0; - } /* MODE_X25 */ -#endif /* CONFIG_HDLC_X25 */ - - return hdlc->xmit(hdlc, skb); -} - - - -void hdlc_netif_rx(hdlc_device *hdlc, struct sk_buff *skb) -{ -/* skb contains raw HDLC frame, in both hard- and software modes */ - skb->mac.raw = skb->data; - - switch(hdlc->mode & MODE_MASK) { - case MODE_HDLC: - skb->protocol = htons(ETH_P_IP); - skb->dev = hdlc_to_dev(hdlc); - netif_rx(skb); - return; - - case MODE_FR: - fr_netif(hdlc, skb); - return; - - case MODE_CISCO: - cisco_netif(hdlc, skb); - return; - -#ifdef CONFIG_HDLC_PPP - case MODE_PPP: -#if 0 - sppp_input(hdlc_to_dev(hdlc), skb); -#else - skb->protocol = htons(ETH_P_WAN_PPP); - skb->dev = hdlc_to_dev(hdlc); - netif_rx(skb); -#endif - return; -#endif -#ifdef CONFIG_HDLC_X25 - case MODE_X25: - skb->dev = hdlc_to_dev(hdlc); - if (lapb_data_received(hdlc, skb) == LAPB_OK) - return; - break; -#endif - } - - hdlc->stats.rx_errors++; - dev_kfree_skb_any(skb); -} - - - -static struct net_device_stats *hdlc_get_stats(struct net_device *dev) -{ - return &dev_to_hdlc(dev)->stats; -} - - - -static int hdlc_set_mode(hdlc_device *hdlc, int mode) -{ - int result = -1; /* Default to soft modes */ - struct net_device *dev = hdlc_to_dev(hdlc); - - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - - if(dev->flags & IFF_UP) - return -EBUSY; - - dev->addr_len = 0; - dev->hard_header = NULL; - hdlc->mode = MODE_NONE; - - if (!(mode & MODE_SOFT)) - switch(mode & MODE_MASK) { - case MODE_HDLC: - result = hdlc->set_mode ? - hdlc->set_mode(hdlc, MODE_HDLC) : 0; - break; - - case MODE_CISCO: /* By card */ -#ifdef CONFIG_HDLC_PPP - case MODE_PPP: -#endif -#ifdef CONFIG_HDLC_X25 - case MODE_X25: -#endif - case MODE_FR: - result = hdlc->set_mode ? - hdlc->set_mode(hdlc, mode) : -ENOSYS; - break; - - default: - return -EINVAL; - } - - if (result) { - mode |= MODE_SOFT; /* Try "host software" protocol */ - - switch(mode & MODE_MASK) { - case MODE_CISCO: - dev->hard_header = cisco_hard_header; - break; - -#ifdef CONFIG_HDLC_PPP - case MODE_PPP: - break; -#endif -#ifdef CONFIG_HDLC_X25 - case MODE_X25: - break; -#endif - - case MODE_FR: - dev->hard_header = fr_hard_header; - dev->addr_len = 2; - *(u16*)dev->dev_addr = htons(LMI_DLCI); - dlci_to_q922(dev->broadcast, LMI_DLCI); - break; - - default: - return -EINVAL; - } - - result = hdlc->set_mode ? - hdlc->set_mode(hdlc, MODE_HDLC) : 0; - } - - if (result) - return result; - - hdlc->mode = mode; - switch(mode & MODE_MASK) { -#ifdef CONFIG_HDLC_PPP - case MODE_PPP: dev->type = ARPHRD_PPP; break; -#endif -#ifdef CONFIG_HDLC_X25 - case MODE_X25: dev->type = ARPHRD_X25; break; -#endif - case MODE_FR: dev->type = ARPHRD_FRAD; break; - case MODE_CISCO: dev->type = ARPHRD_CISCO; break; - default: dev->type = ARPHRD_RAWHDLC; - } - - memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); - destroy_pvc_list(hdlc); - return 0; -} - - - -static int hdlc_fr_pvc(hdlc_device *hdlc, int dlci) -{ - pvc_device **pvc_p = &hdlc->first_pvc; - pvc_device *pvc; - int result, create = 1; /* Create or delete PVC */ - - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - - if(dlci<0) { - dlci = -dlci; - create = 0; - } - - if(dlci <= 0 || dlci >= 1024) - return -EINVAL; /* Only 10 bits for DLCI, DLCI=0 is reserved */ - - if(!mode_is(hdlc, MODE_FR)) - return -EINVAL; /* Only meaningfull on FR */ - - while(*pvc_p) { - if (netdev_dlci(&(*pvc_p)->netdev) == dlci) - break; - pvc_p = &(*pvc_p)->next; - } - - if (create) { /* Create PVC */ - if (*pvc_p != NULL) - return -EEXIST; - - pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL); - if (!pvc) { - printk(KERN_WARNING "%s: Memory squeeze on " - "hdlc_fr_pvc()\n", hdlc_to_name(hdlc)); - return -ENOBUFS; - } - memset(pvc, 0, sizeof(pvc_device)); - - pvc->netdev.hard_start_xmit = pvc_xmit; - pvc->netdev.get_stats = pvc_get_stats; - pvc->netdev.open = pvc_open; - pvc->netdev.stop = pvc_close; - pvc->netdev.change_mtu = pvc_change_mtu; - pvc->netdev.mtu = HDLC_MAX_MTU; - - pvc->netdev.type = ARPHRD_DLCI; - pvc->netdev.hard_header_len = 16; - pvc->netdev.hard_header = fr_hard_header; - pvc->netdev.tx_queue_len = 0; - pvc->netdev.flags = IFF_POINTOPOINT; - - pvc->master = hdlc; - *(u16*)pvc->netdev.dev_addr = htons(dlci); - dlci_to_q922(pvc->netdev.broadcast, dlci); - pvc->netdev.addr_len = 2; - pvc->netdev.irq = hdlc_to_dev(hdlc)->irq; - - result = dev_alloc_name(&pvc->netdev, "pvc%d"); - if (result < 0) { - kfree(pvc); - *pvc_p = NULL; - return result; - } - - if (register_netdevice(&pvc->netdev) != 0) { - kfree(pvc); - *pvc_p = NULL; - return -EIO; - } - - if (!mode_is(hdlc, MODE_SOFT) && hdlc->create_pvc) { - result = hdlc->create_pvc(pvc); - if (result) { - unregister_netdevice(&pvc->netdev); - kfree(pvc); - *pvc_p = NULL; - return result; - } - } - - hdlc->lmi.state |= LINK_STATE_CHANGED; - hdlc->pvc_count++; - return 0; - } - - if (*pvc_p == NULL) /* Delete PVC */ - return -ENOENT; - - pvc = *pvc_p; - - if (pvc->netdev.flags & IFF_UP) - return -EBUSY; /* PVC in use */ - - if (!mode_is(hdlc, MODE_SOFT) && hdlc->destroy_pvc) - hdlc->destroy_pvc(pvc); - - hdlc->lmi.state |= LINK_STATE_CHANGED; - hdlc->pvc_count--; - *pvc_p = pvc->next; - unregister_netdevice(&pvc->netdev); - kfree(pvc); - return 0; -} - - - -static int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - - switch(cmd) { - case HDLCGMODE: - ifr->ifr_ifru.ifru_ivalue = hdlc->mode; - return 0; - - case HDLCSMODE: - return hdlc_set_mode(hdlc, ifr->ifr_ifru.ifru_ivalue); - - case HDLCPVC: - return hdlc_fr_pvc(hdlc, ifr->ifr_ifru.ifru_ivalue); - - default: - if (hdlc->ioctl != NULL) - return hdlc->ioctl(hdlc, ifr, cmd); - } - - return -EINVAL; -} - - - -static int hdlc_init(struct net_device *dev) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - - memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); - - dev->get_stats = hdlc_get_stats; - dev->open = hdlc_open; - dev->stop = hdlc_close; - dev->hard_start_xmit = hdlc_xmit; - dev->do_ioctl = hdlc_ioctl; - dev->change_mtu = hdlc_change_mtu; - dev->mtu = HDLC_MAX_MTU; - - dev->type = ARPHRD_RAWHDLC; - dev->hard_header_len = 16; - - dev->flags = IFF_POINTOPOINT | IFF_NOARP; - - return 0; -} - - - -int register_hdlc_device(hdlc_device *hdlc) -{ - int result; - struct net_device *dev = hdlc_to_dev(hdlc); - - dev->init = hdlc_init; - dev->priv = &hdlc->syncppp_ptr; - hdlc->syncppp_ptr = &hdlc->pppdev; - hdlc->pppdev.dev = dev; - hdlc->mode = MODE_NONE; - hdlc->lmi.T391 = 10; /* polling verification timer */ - hdlc->lmi.T392 = 15; /* link integrity verification polling timer */ - hdlc->lmi.N391 = 6; /* full status polling counter */ - hdlc->lmi.N392 = 3; /* error threshold */ - hdlc->lmi.N393 = 4; /* monitored events count */ - - result = dev_alloc_name(dev, "hdlc%d"); - if (result<0) - return result; - - result = register_netdev(dev); - if (result != 0) - return -EIO; - - MOD_INC_USE_COUNT; - return 0; -} - - - -void unregister_hdlc_device(hdlc_device *hdlc) -{ - destroy_pvc_list(hdlc); - unregister_netdev(hdlc_to_dev(hdlc)); - MOD_DEC_USE_COUNT; -} - -MODULE_AUTHOR("Krzysztof Halasa "); -MODULE_DESCRIPTION("HDLC support module"); -MODULE_LICENSE("GPL"); - -EXPORT_SYMBOL(hdlc_netif_rx); -EXPORT_SYMBOL(register_hdlc_device); -EXPORT_SYMBOL(unregister_hdlc_device); - -static int __init hdlc_module_init(void) -{ - printk(KERN_INFO "%s\n", version); - return 0; -} - - -module_init(hdlc_module_init); diff -Nru a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/wan/hdlc_cisco.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,293 @@ +/* + * Generic HDLC support routines for Linux + * Cisco HDLC support + * + * Copyright (C) 2000 - 2001 Krzysztof Halasa + * + * 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 +#include +#include + + +#define CISCO_MULTICAST 0x8F /* Cisco multicast address */ +#define CISCO_UNICAST 0x0F /* Cisco unicast address */ +#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */ +#define CISCO_SYS_INFO 0x2000 /* Cisco interface/system info */ +#define CISCO_ADDR_REQ 0 /* Cisco address request */ +#define CISCO_ADDR_REPLY 1 /* Cisco address reply */ +#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */ + + +static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev, + u16 type, void *daddr, void *saddr, + unsigned int len) +{ + hdlc_header *data; +#ifdef CONFIG_HDLC_DEBUG_HARD_HEADER + printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name); +#endif + + skb_push(skb, sizeof(hdlc_header)); + data = (hdlc_header*)skb->data; + if (type == CISCO_KEEPALIVE) + data->address = CISCO_MULTICAST; + else + data->address = CISCO_UNICAST; + data->control = 0; + data->protocol = htons(type); + + return sizeof(hdlc_header); +} + + + +static void cisco_keepalive_send(hdlc_device *hdlc, u32 type, + u32 par1, u32 par2) +{ + struct sk_buff *skb; + cisco_packet *data; + + skb = dev_alloc_skb(sizeof(hdlc_header) + sizeof(cisco_packet)); + if (!skb) { + printk(KERN_WARNING + "%s: Memory squeeze on cisco_keepalive_send()\n", + hdlc_to_name(hdlc)); + return; + } + skb_reserve(skb, 4); + cisco_hard_header(skb, hdlc_to_dev(hdlc), CISCO_KEEPALIVE, + NULL, NULL, 0); + data = (cisco_packet*)skb->tail; + + data->type = htonl(type); + data->par1 = htonl(par1); + data->par2 = htonl(par2); + data->rel = 0xFFFF; + data->time = htonl(jiffies * 1000 / HZ); + + skb_put(skb, sizeof(cisco_packet)); + skb->priority = TC_PRIO_CONTROL; + skb->dev = hdlc_to_dev(hdlc); + + dev_queue_xmit(skb); +} + + + +static void cisco_rx(struct sk_buff *skb) +{ + hdlc_device *hdlc = dev_to_hdlc(skb->dev); + hdlc_header *data = (hdlc_header*)skb->data; + cisco_packet *cisco_data; + struct in_device *in_dev; + u32 addr, mask; + + if (skb->len < sizeof(hdlc_header)) + goto rx_error; + + if (data->address != CISCO_MULTICAST && + data->address != CISCO_UNICAST) + goto rx_error; + + skb_pull(skb, sizeof(hdlc_header)); + + switch(ntohs(data->protocol)) { + case ETH_P_IP: + case ETH_P_IPX: + case ETH_P_IPV6: + skb->protocol = data->protocol; + skb->dev = hdlc_to_dev(hdlc); + netif_rx(skb); + return; + + case CISCO_SYS_INFO: + /* Packet is not needed, drop it. */ + dev_kfree_skb_any(skb); + return; + + case CISCO_KEEPALIVE: + if (skb->len != CISCO_PACKET_LEN && + skb->len != CISCO_BIG_PACKET_LEN) { + printk(KERN_INFO "%s: Invalid length of Cisco " + "control packet (%d bytes)\n", + hdlc_to_name(hdlc), skb->len); + goto rx_error; + } + + cisco_data = (cisco_packet*)skb->data; + + switch(ntohl (cisco_data->type)) { + case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */ + in_dev = hdlc_to_dev(hdlc)->ip_ptr; + addr = 0; + mask = ~0; /* is the mask correct? */ + + if (in_dev != NULL) { + struct in_ifaddr **ifap = &in_dev->ifa_list; + + while (*ifap != NULL) { + if (strcmp(hdlc_to_name(hdlc), + (*ifap)->ifa_label) == 0) { + addr = (*ifap)->ifa_local; + mask = (*ifap)->ifa_mask; + break; + } + ifap = &(*ifap)->ifa_next; + } + + cisco_keepalive_send(hdlc, CISCO_ADDR_REPLY, + addr, mask); + } + dev_kfree_skb_any(skb); + return; + + case CISCO_ADDR_REPLY: + printk(KERN_INFO "%s: Unexpected Cisco IP address " + "reply\n", hdlc_to_name(hdlc)); + goto rx_error; + + case CISCO_KEEPALIVE_REQ: + hdlc->state.cisco.rxseq = ntohl(cisco_data->par1); + if (ntohl(cisco_data->par2) == hdlc->state.cisco.txseq) { + hdlc->state.cisco.last_poll = jiffies; + if (!hdlc->state.cisco.up) { + u32 sec, min, hrs, days; + sec = ntohl(cisco_data->time) / 1000; + min = sec / 60; sec -= min * 60; + hrs = min / 60; min -= hrs * 60; + days = hrs / 24; hrs -= days * 24; + printk(KERN_INFO "%s: Link up (peer " + "uptime %ud%uh%um%us)\n", + hdlc_to_name(hdlc), days, hrs, + min, sec); + } + hdlc->state.cisco.up = 1; + } + + dev_kfree_skb_any(skb); + return; + } /* switch(keepalive type) */ + } /* switch(protocol) */ + + printk(KERN_INFO "%s: Unsupported protocol %x\n", hdlc_to_name(hdlc), + data->protocol); + dev_kfree_skb_any(skb); + return; + + rx_error: + hdlc->stats.rx_errors++; /* Mark error */ + dev_kfree_skb_any(skb); +} + + + +static void cisco_timer(unsigned long arg) +{ + hdlc_device *hdlc = (hdlc_device*)arg; + + if (hdlc->state.cisco.up && + jiffies - hdlc->state.cisco.last_poll >= + hdlc->state.cisco.settings.timeout * HZ) { + hdlc->state.cisco.up = 0; + printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc)); + } + + cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ, + ++hdlc->state.cisco.txseq, + hdlc->state.cisco.rxseq); + hdlc->state.cisco.timer.expires = jiffies + + hdlc->state.cisco.settings.interval * HZ; + hdlc->state.cisco.timer.function = cisco_timer; + hdlc->state.cisco.timer.data = arg; + add_timer(&hdlc->state.cisco.timer); +} + + + +static int cisco_open(hdlc_device *hdlc) +{ + hdlc->state.cisco.last_poll = 0; + hdlc->state.cisco.up = 0; + hdlc->state.cisco.txseq = hdlc->state.cisco.rxseq = 0; + + init_timer(&hdlc->state.cisco.timer); + hdlc->state.cisco.timer.expires = jiffies + HZ; /*First poll after 1s*/ + hdlc->state.cisco.timer.function = cisco_timer; + hdlc->state.cisco.timer.data = (unsigned long)hdlc; + add_timer(&hdlc->state.cisco.timer); + return 0; +} + + + +static void cisco_close(hdlc_device *hdlc) +{ + del_timer_sync(&hdlc->state.cisco.timer); +} + + + +int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + cisco_proto *cisco_s = &ifr->ifr_settings->ifs_hdlc.cisco; + const size_t size = sizeof(cisco_proto); + struct net_device *dev = hdlc_to_dev(hdlc); + int result; + + switch (ifr->ifr_settings->type) { + case IF_GET_PROTO: + ifr->ifr_settings->type = IF_PROTO_CISCO; + if (copy_to_user(cisco_s, &hdlc->state.cisco.settings, size)) + return -EFAULT; + return 0; + + case IF_PROTO_CISCO: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_UP) + return -EBUSY; + + if (copy_from_user(&hdlc->state.cisco.settings, cisco_s, size)) + return -EFAULT; + + /* FIXME - put sanity checks here */ + hdlc_detach(hdlc); + + result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); + if (result) { + hdlc->proto = -1; + return result; + } + + hdlc->open = cisco_open; + hdlc->stop = cisco_close; + hdlc->netif_rx = cisco_rx; + hdlc->proto = IF_PROTO_CISCO; + dev->hard_start_xmit = hdlc->xmit; + dev->hard_header = cisco_hard_header; + dev->type = ARPHRD_CISCO; + dev->addr_len = 0; + return 0; + } + + return -EINVAL; +} diff -Nru a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/wan/hdlc_fr.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,842 @@ +/* + * Generic HDLC support routines for Linux + * Frame Relay support + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * 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 +#include +#include + + +__inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci) +{ + pvc_device *pvc=hdlc->state.fr.first_pvc; + + while (pvc) { + if (netdev_dlci(&pvc->netdev) == dlci) + return pvc; + pvc = pvc->next; + } + + return NULL; +} + + + +__inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status, + int *active, int *new) +{ + *new = (status[2] & 0x08); + *active = (!*new && (status[2] & 0x02)); + + return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3); +} + + +__inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status, + int active, int new) +{ + status[0] = (dlci>>4) & 0x3F; + status[1] = ((dlci<<3) & 0x78) | 0x80; + status[2] = 0x80; + + if (new) + status[2] |= 0x08; + else if (active) + status[2] |= 0x02; +} + + + +static int fr_hard_header(struct sk_buff *skb, struct net_device *dev, + u16 type, void *daddr, void *saddr, unsigned int len) +{ + u16 head_len; + + if (!daddr) + daddr = dev->broadcast; + +#ifdef CONFIG_HDLC_DEBUG_HARD_HEADER + printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name); +#endif + + switch(type) { + case ETH_P_IP: + head_len = 4; + skb_push(skb, head_len); + skb->data[3] = NLPID_IP; + break; + + case ETH_P_IPV6: + head_len = 4; + skb_push(skb, head_len); + skb->data[3] = NLPID_IPV6; + break; + + case LMI_PROTO: + head_len = 4; + skb_push(skb, head_len); + skb->data[3] = LMI_PROTO; + break; + + default: + head_len = 10; + skb_push(skb, head_len); + skb->data[3] = FR_PAD; + skb->data[4] = NLPID_SNAP; + skb->data[5] = FR_PAD; + skb->data[6] = FR_PAD; + skb->data[7] = FR_PAD; + skb->data[8] = type>>8; + skb->data[9] = (u8)type; + } + + memcpy(skb->data, daddr, 2); + skb->data[2] = FR_UI; + + return head_len; +} + + + +static int pvc_open(struct net_device *dev) +{ + pvc_device *pvc = dev_to_pvc(dev); + + if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0) + return -EIO; /* Master must be UP in order to activate PVC */ + + if (pvc->master->state.fr.settings.lmi != LMI_NONE) + pvc->state.active = 0; + else + pvc->state.active = 1; + + pvc->state.new = 0; + pvc->master->state.fr.changed = 1; + return 0; +} + + + +static int pvc_close(struct net_device *dev) +{ + pvc_device *pvc = dev_to_pvc(dev); + pvc->state.active = pvc->state.new = 0; + pvc->master->state.fr.changed = 1; + return 0; +} + + + +static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) +{ + pvc_device *pvc = dev_to_pvc(dev); + + if (pvc->state.active) { + skb->dev = hdlc_to_dev(pvc->master); + pvc->stats.tx_bytes += skb->len; + pvc->stats.tx_packets++; + if (pvc->state.fecn) + pvc->stats.tx_compressed++; /* TX Congestion counter */ + dev_queue_xmit(skb); + } else { + pvc->stats.tx_dropped++; + dev_kfree_skb(skb); + } + + return 0; +} + + + +static struct net_device_stats *pvc_get_stats(struct net_device *dev) +{ + pvc_device *pvc = dev_to_pvc(dev); + return &pvc->stats; +} + + + +static int pvc_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + + + +static inline void fr_log_dlci_active(pvc_device *pvc) +{ + printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc), + pvc->state.active ? "" : "in", + pvc->state.new ? " new" : ""); +} + + + +static inline u8 fr_lmi_nextseq(u8 x) +{ + x++; + return x ? x : 1; +} + + + +static void fr_lmi_send(hdlc_device *hdlc, int fullrep) +{ + struct sk_buff *skb; + pvc_device *pvc = hdlc->state.fr.first_pvc; + int len = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? LMI_ANSI_LENGTH + : LMI_LENGTH; + int stat_len = 3; + u8 *data; + int i = 0; + + if (hdlc->state.fr.settings.dce && fullrep) { + len += hdlc->state.fr.pvc_count * (2 + stat_len); + if (len > HDLC_MAX_MTU) { + printk(KERN_WARNING "%s: Too many PVCs while sending " + "LMI full report\n", hdlc_to_name(hdlc)); + return; + } + } + + skb = dev_alloc_skb(len); + if (!skb) { + printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n", + hdlc_to_name(hdlc)); + return; + } + memset(skb->data, 0, len); + skb_reserve(skb, 4); + fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0); + data = skb->tail; + data[i++] = LMI_CALLREF; + data[i++] = hdlc->state.fr.settings.dce + ? LMI_STATUS : LMI_STATUS_ENQUIRY; + if (hdlc->state.fr.settings.lmi == LMI_ANSI) + data[i++] = LMI_ANSI_LOCKSHIFT; + data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_REPTYPE : LMI_REPTYPE; + data[i++] = LMI_REPT_LEN; + data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY; + + data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_ALIVE : LMI_ALIVE; + data[i++] = LMI_INTEG_LEN; + data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq); + data[i++] = hdlc->state.fr.rxseq; + + if (hdlc->state.fr.settings.dce && fullrep) { + while (pvc) { + data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT; + data[i++] = stat_len; + + if (hdlc->state.fr.reliable && + (pvc->netdev.flags & IFF_UP) && + !pvc->state.active && + !pvc->state.new) { + pvc->state.new = 1; + fr_log_dlci_active(pvc); + } + + dlci_to_status(hdlc, netdev_dlci(&pvc->netdev), + data + i, + pvc->state.active, pvc->state.new); + i += stat_len; + pvc = pvc->next; + } + } + + skb_put(skb, i); + skb->priority = TC_PRIO_CONTROL; + skb->dev = hdlc_to_dev(hdlc); + + dev_queue_xmit(skb); +} + + + +static void fr_timer(unsigned long arg) +{ + hdlc_device *hdlc = (hdlc_device*)arg; + int i, cnt = 0, reliable; + u32 list; + + if (hdlc->state.fr.settings.dce) + reliable = (jiffies - hdlc->state.fr.last_poll < + hdlc->state.fr.settings.t392 * HZ); + else { + hdlc->state.fr.last_errors <<= 1; /* Shift the list */ + if (hdlc->state.fr.request) { + if (hdlc->state.fr.reliable) + printk(KERN_INFO "%s: No LMI status reply " + "received\n", hdlc_to_name(hdlc)); + hdlc->state.fr.last_errors |= 1; + } + + list = hdlc->state.fr.last_errors; + for (i = 0; i < hdlc->state.fr.settings.n393; i++, list >>= 1) + cnt += (list & 1); /* errors count */ + + reliable = (cnt < hdlc->state.fr.settings.n392); + } + + if (hdlc->state.fr.reliable != reliable) { + pvc_device *pvc = hdlc->state.fr.first_pvc; + + hdlc->state.fr.reliable = reliable; + printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc), + reliable ? "" : "un"); + + if (reliable) { + hdlc->state.fr.n391cnt = 0; /* Request full status */ + hdlc->state.fr.changed = 1; + } else { + while (pvc) { /* Deactivate all PVCs */ + pvc->state.new = pvc->state.active = 0; + pvc = pvc->next; + } + } + } + + if (hdlc->state.fr.settings.dce) + hdlc->state.fr.timer.expires = jiffies + + hdlc->state.fr.settings.t392 * HZ; + else { + if (hdlc->state.fr.n391cnt) + hdlc->state.fr.n391cnt--; + + fr_lmi_send(hdlc, hdlc->state.fr.n391cnt == 0); + + hdlc->state.fr.request = 1; + hdlc->state.fr.timer.expires = jiffies + + hdlc->state.fr.settings.t391 * HZ; + } + + hdlc->state.fr.timer.function = fr_timer; + hdlc->state.fr.timer.data = arg; + add_timer(&hdlc->state.fr.timer); +} + + + +static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb) +{ + int stat_len; + pvc_device *pvc; + int reptype = -1, error; + u8 rxseq, txseq; + int i; + + if (skb->len < ((hdlc->state.fr.settings.lmi == LMI_ANSI) + ? LMI_ANSI_LENGTH : LMI_LENGTH)) { + printk(KERN_INFO "%s: Short LMI frame\n", hdlc_to_name(hdlc)); + return 1; + } + + if (skb->data[5] != (!hdlc->state.fr.settings.dce ? + LMI_STATUS : LMI_STATUS_ENQUIRY)) { + printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n", + hdlc_to_name(hdlc), skb->data[2], + hdlc->state.fr.settings.dce ? "enquiry" : "reply"); + return 1; + } + + i = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? 7 : 6; + + if (skb->data[i] != + ((hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) { + printk(KERN_INFO "%s: Not a report type=%x\n", + hdlc_to_name(hdlc), skb->data[i]); + return 1; + } + i++; + + i++; /* Skip length field */ + + reptype = skb->data[i++]; + + if (skb->data[i]!= + ((hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_ALIVE : LMI_ALIVE)) { + printk(KERN_INFO "%s: Unsupported status element=%x\n", + hdlc_to_name(hdlc), skb->data[i]); + return 1; + } + i++; + + i++; /* Skip length field */ + + hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */ + rxseq = skb->data[i++]; /* Should confirm our sequence */ + + txseq = hdlc->state.fr.txseq; + + if (hdlc->state.fr.settings.dce) { + if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) { + printk(KERN_INFO "%s: Unsupported report type=%x\n", + hdlc_to_name(hdlc), reptype); + return 1; + } + } + + error = 0; + if (!hdlc->state.fr.reliable) + error = 1; + + if (rxseq == 0 || rxseq != txseq) { + hdlc->state.fr.n391cnt = 0; /* Ask for full report next time */ + error = 1; + } + + if (hdlc->state.fr.settings.dce) { + if (hdlc->state.fr.fullrep_sent && !error) { +/* Stop sending full report - the last one has been confirmed by DTE */ + hdlc->state.fr.fullrep_sent = 0; + pvc = hdlc->state.fr.first_pvc; + while (pvc) { + if (pvc->state.new) { + pvc->state.new = 0; + pvc->state.active = 1; + fr_log_dlci_active(pvc); + +/* Tell DTE that new PVC is now active */ + hdlc->state.fr.changed = 1; + } + pvc = pvc->next; + } + } + + if (hdlc->state.fr.changed) { + reptype = LMI_FULLREP; + hdlc->state.fr.fullrep_sent = 1; + hdlc->state.fr.changed = 0; + } + + fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0); + return 0; + } + + /* DTE */ + + if (reptype != LMI_FULLREP || error) + return 0; + + stat_len = 3; + pvc = hdlc->state.fr.first_pvc; + + while (pvc) { + pvc->state.deleted = pvc->state.active; /* mark active PVCs */ + pvc = pvc->next; + } + + while (skb->len >= i + 2 + stat_len) { + u16 dlci; + int active, new; + + if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) { + printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n", + hdlc_to_name(hdlc), skb->data[i]); + return 1; + } + i++; + + if (skb->data[i] != stat_len) { + printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n", + hdlc_to_name(hdlc), skb->data[i]); + return 1; + } + i++; + + dlci = status_to_dlci(hdlc, skb->data + i, &active, &new); + pvc = find_pvc(hdlc, dlci); + + active |= new; + if (pvc) { + if (active && !pvc->state.active && + (pvc->netdev.flags & IFF_UP)) { + pvc->state.active = active; + fr_log_dlci_active(pvc); + } + pvc->state.deleted = 0; + } + else if (new) + printk(KERN_INFO "%s: new PVC available, DLCI=%u\n", + hdlc_to_name(hdlc), dlci); + + i += stat_len; + } + + pvc = hdlc->state.fr.first_pvc; + + while (pvc) { + if (pvc->state.deleted) { + pvc->state.active = pvc->state.new = 0; + fr_log_dlci_active(pvc); + pvc->state.deleted = 0; + } + pvc = pvc->next; + } + + /* Next full report after N391 polls */ + hdlc->state.fr.n391cnt = hdlc->state.fr.settings.n391; + + return 0; +} + + + +static void fr_rx(struct sk_buff *skb) +{ + hdlc_device *hdlc = dev_to_hdlc(skb->dev); + fr_hdr *fh = (fr_hdr*)skb->data; + u8 *data = skb->data; + u16 dlci; + pvc_device *pvc; + + if (skb->len<4 || fh->ea1 || data[2] != FR_UI) + goto rx_error; + + dlci = q922_to_dlci(skb->data); + + if (dlci == LMI_DLCI) { + if (hdlc->state.fr.settings.lmi == LMI_NONE) + goto rx_error; /* LMI packet with no LMI? */ + + if (data[3] == LMI_PROTO) { + if (fr_lmi_recv(hdlc, skb)) + goto rx_error; + else { + /* No request pending */ + hdlc->state.fr.request = 0; + hdlc->state.fr.last_poll = jiffies; + dev_kfree_skb_any(skb); + return; + } + } + + printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n", + hdlc_to_name(hdlc)); + goto rx_error; + } + + pvc = find_pvc(hdlc, dlci); + if (!pvc) { +#ifdef CONFIG_HDLC_DEBUG_PKT + printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n", + hdlc_to_name(hdlc), dlci); +#endif + goto rx_error; + } + + if ((pvc->netdev.flags & IFF_UP) == 0) { +#ifdef CONFIG_HDLC_DEBUG_PKT + printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n", + hdlc_to_name(hdlc), dlci); +#endif + goto rx_error; + } + + pvc->stats.rx_packets++; /* PVC traffic */ + pvc->stats.rx_bytes += skb->len; + + if (pvc->state.fecn != (fh->fecn ? PVC_STATE_FECN : 0)) { +#ifdef CONFIG_HDLC_DEBUG_ECN + printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc), + fh->fecn ? "N" : "FF"); +#endif + pvc->state.fecn ^= 1; + } + + if (pvc->state.becn != (fh->becn ? PVC_STATE_BECN : 0)) { +#ifdef CONFIG_HDLC_DEBUG_ECN + printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc), + fh->becn ? "N" : "FF"); +#endif + pvc->state.becn ^= 1; + } + + if (pvc->state.becn) + pvc->stats.rx_compressed++; + + skb->dev = &pvc->netdev; + + if (data[3] == NLPID_IP) { + skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ + skb->protocol = htons(ETH_P_IP); + netif_rx(skb); + return; + } + + + if (data[3] == NLPID_IPV6) { + skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ + skb->protocol = htons(ETH_P_IPV6); + netif_rx(skb); + return; + } + + if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD) { + u16 oui = ntohl(*(u16*)(data + 6)); + u16 pid = ntohl(*(u16*)(data + 8)); + skb_pull(skb, 10); + + switch ((((u32)oui) << 16) | pid) { + case ETH_P_ARP: /* routed frame with SNAP */ + case ETH_P_IPX: + skb->protocol = htons(pid); + break; + + default: + printk(KERN_INFO "%s: Unsupported protocol, OUI=%x " + "PID=%x\n", hdlc_to_name(hdlc), oui, pid); + dev_kfree_skb_any(skb); + return; + } + + netif_rx(skb); + return; + } + + printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x\n", + hdlc_to_name(hdlc), data[3]); + dev_kfree_skb_any(skb); + return; + + rx_error: + hdlc->stats.rx_errors++; /* Mark error */ + dev_kfree_skb_any(skb); +} + + + +static int fr_open(hdlc_device *hdlc) +{ + if (hdlc->state.fr.settings.lmi != LMI_NONE) { + hdlc->state.fr.last_poll = 0; + hdlc->state.fr.reliable = 0; + hdlc->state.fr.changed = 1; + hdlc->state.fr.request = 0; + hdlc->state.fr.fullrep_sent = 0; + hdlc->state.fr.last_errors = 0xFFFFFFFF; + hdlc->state.fr.n391cnt = 0; + hdlc->state.fr.txseq = hdlc->state.fr.rxseq = 0; + + init_timer(&hdlc->state.fr.timer); + /* First poll after 1 s */ + hdlc->state.fr.timer.expires = jiffies + HZ; + hdlc->state.fr.timer.function = fr_timer; + hdlc->state.fr.timer.data = (unsigned long)hdlc; + add_timer(&hdlc->state.fr.timer); + } else + hdlc->state.fr.reliable = 1; + + return 0; +} + + + +static void fr_close(hdlc_device *hdlc) +{ + pvc_device *pvc = hdlc->state.fr.first_pvc; + + if (hdlc->state.fr.settings.lmi != LMI_NONE) + del_timer_sync(&hdlc->state.fr.timer); + + while(pvc) { + dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */ + pvc = pvc->next; + } +} + + + +static int fr_pvc(hdlc_device *hdlc, unsigned int dlci, int create) +{ + pvc_device **pvc_p = &hdlc->state.fr.first_pvc; + pvc_device *pvc; + int result; + + if(dlci <= 0 || dlci >= 1024) + return -EINVAL; /* Only 10 bits for DLCI, DLCI 0 reserved */ + + while(*pvc_p) { + if (netdev_dlci(&(*pvc_p)->netdev) == dlci) + break; + pvc_p = &(*pvc_p)->next; + } + + if (create) { /* Create PVC */ + if (*pvc_p != NULL) + return -EEXIST; + + pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL); + if (!pvc) { + printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n", + hdlc_to_name(hdlc)); + return -ENOBUFS; + } + memset(pvc, 0, sizeof(pvc_device)); + + pvc->netdev.hard_start_xmit = pvc_xmit; + pvc->netdev.get_stats = pvc_get_stats; + pvc->netdev.open = pvc_open; + pvc->netdev.stop = pvc_close; + pvc->netdev.change_mtu = pvc_change_mtu; + pvc->netdev.mtu = HDLC_MAX_MTU; + + pvc->netdev.type = ARPHRD_DLCI; + pvc->netdev.hard_header_len = 16; + pvc->netdev.hard_header = fr_hard_header; + pvc->netdev.tx_queue_len = 0; + pvc->netdev.flags = IFF_POINTOPOINT; + + pvc->master = hdlc; + *(u16*)pvc->netdev.dev_addr = htons(dlci); + dlci_to_q922(pvc->netdev.broadcast, dlci); + pvc->netdev.addr_len = 2; + + result = dev_alloc_name(&pvc->netdev, "pvc%d"); + if (result < 0) { + kfree(pvc); + *pvc_p = NULL; + return result; + } + + if (register_netdevice(&pvc->netdev) != 0) { + kfree(pvc); + *pvc_p = NULL; + return -EIO; + } + + hdlc->state.fr.changed = 1; + hdlc->state.fr.pvc_count++; + return 0; + } + + if (*pvc_p == NULL) /* Delete PVC */ + return -ENOENT; + + pvc = *pvc_p; + + if (pvc->netdev.flags & IFF_UP) + return -EBUSY; /* PVC in use */ + + hdlc->state.fr.changed = 1; + hdlc->state.fr.pvc_count--; + *pvc_p = pvc->next; + unregister_netdevice(&pvc->netdev); + kfree(pvc); + return 0; +} + + + +static void fr_destroy(hdlc_device *hdlc) +{ + pvc_device *pvc = hdlc->state.fr.first_pvc; + while(pvc) { + pvc_device *next = pvc->next; + unregister_netdevice(&pvc->netdev); + kfree(pvc); + pvc = next; + } + + hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */ + hdlc->state.fr.pvc_count = 0; + hdlc->state.fr.changed = 1; +} + + + +int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + fr_proto *fr_s = &ifr->ifr_settings->ifs_hdlc.fr; + const size_t size = sizeof(fr_proto); + struct net_device *dev = hdlc_to_dev(hdlc); + fr_proto_pvc pvc; + int result; + + switch (ifr->ifr_settings->type) { + case IF_GET_PROTO: + ifr->ifr_settings->type = IF_PROTO_FR; + if (copy_to_user(fr_s, &hdlc->state.fr.settings, size)) + return -EFAULT; + return 0; + + case IF_PROTO_FR: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_UP) + return -EBUSY; + + if (copy_from_user(&hdlc->state.fr.settings, fr_s, size)) + return -EFAULT; + + /* FIXME - put sanity checks here */ + if (hdlc->proto != IF_PROTO_FR) { + hdlc_detach(hdlc); + hdlc->state.fr.first_pvc = NULL; + hdlc->state.fr.pvc_count = 0; + } + + result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); + if (result) { + hdlc->proto = -1; + return result; + } + + hdlc->open = fr_open; + hdlc->stop = fr_close; + hdlc->netif_rx = fr_rx; + hdlc->detach = fr_destroy; + hdlc->proto = IF_PROTO_FR; + dev->hard_start_xmit = hdlc->xmit; + dev->hard_header = fr_hard_header; + dev->type = ARPHRD_FRAD; + dev->addr_len = 2; + *(u16*)dev->dev_addr = htons(LMI_DLCI); + dlci_to_q922(dev->broadcast, LMI_DLCI); + return 0; + + case IF_PROTO_FR_ADD_PVC: + case IF_PROTO_FR_DEL_PVC: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (copy_from_user(&pvc, &ifr->ifr_settings->ifs_hdlc.fr_pvc, + sizeof(fr_proto_pvc))) + return -EFAULT; + + return fr_pvc(hdlc, pvc.dlci, + ifr->ifr_settings->type == IF_PROTO_FR_ADD_PVC); + } + + return -EINVAL; +} diff -Nru a/drivers/net/wan/hdlc_generic.c b/drivers/net/wan/hdlc_generic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/wan/hdlc_generic.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,188 @@ +/* + * Generic HDLC support routines for Linux + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * 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. + * + * Current status: + * - this is work in progress + * - not heavily tested on SMP + * - currently supported: + * * raw IP-in-HDLC + * * Cisco HDLC + * * Frame Relay with ANSI or CCITT LMI (both user and network side) + * * PPP + * * X.25 + * + * Use sethdlc utility to set line parameters, protocol and PVCs + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static const char* version = "HDLC support module revision 1.08"; + + +static int hdlc_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + + + +static struct net_device_stats *hdlc_get_stats(struct net_device *dev) +{ + return &dev_to_hdlc(dev)->stats; +} + + + +static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *p) +{ + dev_to_hdlc(dev)->netif_rx(skb); + return 0; +} + + + +int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + hdlc_device *hdlc = dev_to_hdlc(dev); + unsigned int proto; + + if (cmd != SIOCWANDEV) + return -EINVAL; + + switch(ifr->ifr_settings->type) { + case IF_PROTO_HDLC: + case IF_PROTO_PPP: + case IF_PROTO_CISCO: + case IF_PROTO_FR: + case IF_PROTO_X25: + proto = ifr->ifr_settings->type; + break; + + default: + proto = hdlc->proto; + } + + switch(proto) { +#ifdef CONFIG_HDLC_RAW + case IF_PROTO_HDLC: return hdlc_raw_ioctl(hdlc, ifr); +#endif +#ifdef CONFIG_HDLC_PPP + case IF_PROTO_PPP: return hdlc_ppp_ioctl(hdlc, ifr); +#endif +#ifdef CONFIG_HDLC_CISCO + case IF_PROTO_CISCO: return hdlc_cisco_ioctl(hdlc, ifr); +#endif +#ifdef CONFIG_HDLC_FR + case IF_PROTO_FR: return hdlc_fr_ioctl(hdlc, ifr); +#endif +#ifdef CONFIG_HDLC_X25 + case IF_PROTO_X25: return hdlc_x25_ioctl(hdlc, ifr); +#endif + default: return -ENOSYS; + } +} + + + +int register_hdlc_device(hdlc_device *hdlc) +{ + int result; + struct net_device *dev = hdlc_to_dev(hdlc); + + dev->get_stats = hdlc_get_stats; + dev->change_mtu = hdlc_change_mtu; + dev->mtu = HDLC_MAX_MTU; + + dev->type = ARPHRD_RAWHDLC; + dev->hard_header_len = 16; + + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + + hdlc->proto = -1; + hdlc->detach = NULL; + + result = dev_alloc_name(dev, "hdlc%d"); + if (result<0) + return result; + + result = register_netdev(dev); + if (result != 0) + return -EIO; + + MOD_INC_USE_COUNT; + return 0; +} + + + +void unregister_hdlc_device(hdlc_device *hdlc) +{ + hdlc_detach(hdlc); + + unregister_netdev(hdlc_to_dev(hdlc)); + MOD_DEC_USE_COUNT; +} + + + +MODULE_AUTHOR("Krzysztof Halasa "); +MODULE_DESCRIPTION("HDLC support module"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(hdlc_ioctl); +EXPORT_SYMBOL(register_hdlc_device); +EXPORT_SYMBOL(unregister_hdlc_device); + +struct packet_type hdlc_packet_type= +{ + __constant_htons(ETH_P_HDLC), + NULL, + hdlc_rcv, + NULL, + NULL +}; + + +static int __init hdlc_module_init(void) +{ + printk(KERN_INFO "%s\n", version); + dev_add_pack(&hdlc_packet_type); + return 0; +} + + + +static void __exit hdlc_module_exit(void) +{ + dev_remove_pack(&hdlc_packet_type); +} + + +module_init(hdlc_module_init); +module_exit(hdlc_module_exit); diff -Nru a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/wan/hdlc_ppp.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,119 @@ +/* + * Generic HDLC support routines for Linux + * Point-to-point protocol support + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * 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 +#include +#include + + +static int ppp_open(hdlc_device *hdlc) +{ + struct net_device *dev = hdlc_to_dev(hdlc); + void *old_ioctl; + int result; + + dev->priv = &hdlc->state.ppp.syncppp_ptr; + hdlc->state.ppp.syncppp_ptr = &hdlc->state.ppp.pppdev; + hdlc->state.ppp.pppdev.dev = dev; + + old_ioctl = dev->do_ioctl; + hdlc->state.ppp.old_change_mtu = dev->change_mtu; + sppp_attach(&hdlc->state.ppp.pppdev); + /* sppp_attach nukes them. We don't need syncppp's ioctl */ + dev->do_ioctl = old_ioctl; + hdlc->state.ppp.pppdev.sppp.pp_flags &= ~PP_CISCO; + dev->type = ARPHRD_PPP; + result = sppp_open(dev); + if (result) { + sppp_detach(dev); + return result; + } + + return 0; +} + + + +static void ppp_close(hdlc_device *hdlc) +{ + struct net_device *dev = hdlc_to_dev(hdlc); + + sppp_close(dev); + sppp_detach(dev); + dev->rebuild_header = NULL; + dev->change_mtu = hdlc->state.ppp.old_change_mtu; + dev->mtu = HDLC_MAX_MTU; + dev->hard_header_len = 16; +} + + + +static void ppp_rx(struct sk_buff *skb) +{ + skb->protocol = htons(ETH_P_WAN_PPP); + netif_rx(skb); +} + + + +int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + struct net_device *dev = hdlc_to_dev(hdlc); + int result; + + switch (ifr->ifr_settings->type) { + case IF_GET_PROTO: + ifr->ifr_settings->type = IF_PROTO_PPP; + return 0; /* return protocol only, no settable parameters */ + + case IF_PROTO_PPP: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_UP) + return -EBUSY; + + /* no settable parameters */ + + hdlc_detach(hdlc); + + result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); + if (result) { + hdlc->proto = -1; + return result; + } + + hdlc->open = ppp_open; + hdlc->stop = ppp_close; + hdlc->netif_rx = ppp_rx; + hdlc->proto = IF_PROTO_PPP; + dev->hard_start_xmit = hdlc->xmit; + dev->hard_header = NULL; + dev->type = ARPHRD_PPP; + dev->addr_len = 0; + return 0; + } + + return -EINVAL; +} diff -Nru a/drivers/net/wan/hdlc_raw.c b/drivers/net/wan/hdlc_raw.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/wan/hdlc_raw.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,84 @@ +/* + * Generic HDLC support routines for Linux + * HDLC support + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * 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 +#include +#include + + +static void raw_rx(struct sk_buff *skb) +{ + skb->protocol = htons(ETH_P_IP); + netif_rx(skb); +} + + + +int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + raw_hdlc_proto *raw_s = &ifr->ifr_settings->ifs_hdlc.raw_hdlc; + const size_t size = sizeof(raw_hdlc_proto); + struct net_device *dev = hdlc_to_dev(hdlc); + int result; + + switch (ifr->ifr_settings->type) { + case IF_GET_PROTO: + if (copy_to_user(raw_s, &hdlc->state.raw_hdlc.settings, size)) + return -EFAULT; + return 0; + + case IF_PROTO_HDLC: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_UP) + return -EBUSY; + + if (copy_from_user(&hdlc->state.raw_hdlc.settings, raw_s, size)) + return -EFAULT; + + + /* FIXME - put sanity checks here */ + hdlc_detach(hdlc); + + result=hdlc->attach(hdlc, hdlc->state.raw_hdlc.settings.encoding, + hdlc->state.raw_hdlc.settings.parity); + if (result) { + hdlc->proto = -1; + return result; + } + + hdlc->open = NULL; + hdlc->stop = NULL; + hdlc->netif_rx = raw_rx; + hdlc->proto = IF_PROTO_HDLC; + dev->hard_start_xmit = hdlc->xmit; + dev->hard_header = NULL; + dev->type = ARPHRD_RAWHDLC; + dev->addr_len = 0; + return 0; + } + + return -EINVAL; +} diff -Nru a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/wan/hdlc_x25.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,219 @@ +/* + * Generic HDLC support routines for Linux + * X.25 support + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * 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 +#include +#include + +/* These functions are callbacks called by LAPB layer */ + +static void x25_connect_disconnect(void *token, int reason, int code) +{ + hdlc_device *hdlc = token; + struct sk_buff *skb; + unsigned char *ptr; + + if ((skb = dev_alloc_skb(1)) == NULL) { + printk(KERN_ERR "%s: out of memory\n", hdlc_to_name(hdlc)); + return; + } + + ptr = skb_put(skb, 1); + *ptr = code; + + skb->dev = hdlc_to_dev(hdlc); + skb->protocol = htons(ETH_P_X25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + netif_rx(skb); +} + + + +static void x25_connected(void *token, int reason) +{ + x25_connect_disconnect(token, reason, 1); +} + + + +static void x25_disconnected(void *token, int reason) +{ + x25_connect_disconnect(token, reason, 2); +} + + + +static int x25_data_indication(void *token, struct sk_buff *skb) +{ + hdlc_device *hdlc = token; + unsigned char *ptr; + + ptr = skb_push(skb, 1); + *ptr = 0; + + skb->dev = hdlc_to_dev(hdlc); + skb->protocol = htons(ETH_P_X25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + return netif_rx(skb); +} + + + +static void x25_data_transmit(void *token, struct sk_buff *skb) +{ + hdlc_device *hdlc = token; + hdlc->xmit(skb, hdlc_to_dev(hdlc)); /* Ignore return value :-( */ +} + + + +static int x25_xmit(struct sk_buff *skb, struct net_device *dev) +{ + hdlc_device *hdlc = dev_to_hdlc(dev); + int result; + + + /* X.25 to LAPB */ + switch (skb->data[0]) { + case 0: /* Data to be transmitted */ + skb_pull(skb, 1); + if ((result = lapb_data_request(hdlc, skb)) != LAPB_OK) + dev_kfree_skb(skb); + return 0; + + case 1: + if ((result = lapb_connect_request(hdlc))!= LAPB_OK) { + if (result == LAPB_CONNECTED) + /* Send connect confirm. msg to level 3 */ + x25_connected(hdlc, 0); + else + printk(KERN_ERR "%s: LAPB connect request " + "failed, error code = %i\n", + hdlc_to_name(hdlc), result); + } + break; + + case 2: + if ((result = lapb_disconnect_request(hdlc)) != LAPB_OK) { + if (result == LAPB_NOTCONNECTED) + /* Send disconnect confirm. msg to level 3 */ + x25_disconnected(hdlc, 0); + else + printk(KERN_ERR "%s: LAPB disconnect request " + "failed, error code = %i\n", + hdlc_to_name(hdlc), result); + } + break; + + default: /* to be defined */ + break; + } + + dev_kfree_skb(skb); + return 0; +} + + + +static int x25_open(hdlc_device *hdlc) +{ + struct lapb_register_struct cb; + int result; + + cb.connect_confirmation = x25_connected; + cb.connect_indication = x25_connected; + cb.disconnect_confirmation = x25_disconnected; + cb.disconnect_indication = x25_disconnected; + cb.data_indication = x25_data_indication; + cb.data_transmit = x25_data_transmit; + + result = lapb_register(hdlc, &cb); + if (result != LAPB_OK) + return result; + return 0; +} + + + +static void x25_close(hdlc_device *hdlc) +{ + lapb_unregister(hdlc); +} + + + +static void x25_rx(struct sk_buff *skb) +{ + hdlc_device *hdlc = dev_to_hdlc(skb->dev); + + if (lapb_data_received(hdlc, skb) == LAPB_OK) + return; + hdlc->stats.rx_errors++; + dev_kfree_skb_any(skb); +} + + + +int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + struct net_device *dev = hdlc_to_dev(hdlc); + int result; + + switch (ifr->ifr_settings->type) { + case IF_GET_PROTO: + ifr->ifr_settings->type = IF_PROTO_X25; + return 0; /* return protocol only, no settable parameters */ + + case IF_PROTO_X25: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_UP) + return -EBUSY; + + hdlc_detach(hdlc); + + result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); + if (result) { + hdlc->proto = -1; + return result; + } + + hdlc->open = x25_open; + hdlc->stop = x25_close; + hdlc->netif_rx = x25_rx; + hdlc->proto = IF_PROTO_X25; + dev->hard_start_xmit = x25_xmit; + dev->hard_header = NULL; + dev->type = ARPHRD_X25; + dev->addr_len = 0; + return 0; + } + + return -EINVAL; +} diff -Nru a/drivers/net/wan/n2.c b/drivers/net/wan/n2.c --- a/drivers/net/wan/n2.c Thu Mar 7 18:17:40 2002 +++ b/drivers/net/wan/n2.c Thu Mar 7 18:17:40 2002 @@ -1,7 +1,7 @@ /* * SDL Inc. RISCom/N2 synchronous serial card driver for Linux * - * Copyright (C) 1998-2000 Krzysztof Halasa + * Copyright (C) 1998-2001 Krzysztof Halasa * * 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 @@ -17,6 +17,7 @@ * SDL Inc. PPP/HDLC/CISCO driver */ +#include #include #include #include @@ -33,10 +34,8 @@ #include #include "hd64570.h" -#define DEBUG_RINGS -/* #define DEBUG_PKT */ -static const char* version = "SDL RISCom/N2 driver revision: 1.02 for Linux 2.4"; +static const char* version = "SDL RISCom/N2 driver version: 1.09"; static const char* devname = "RISCom/N2"; #define USE_WINDOWSIZE 16384 @@ -87,9 +86,9 @@ hdlc_device hdlc; /* HDLC device struct - must be first */ struct card_s *card; spinlock_t lock; /* TX lock */ - int clkmode; /* clock mode */ - int clkrate; /* clock rate */ - int line; /* loopback only */ + sync_serial_settings settings; + unsigned short encoding; + unsigned short parity; u8 rxs, txs, tmc; /* SCA registers */ u8 valid; /* port enabled */ u8 phy_node; /* physical port # - 0 or 1 */ @@ -116,6 +115,9 @@ }card_t; +static card_t *first_card; +static card_t **new_card = &first_card; + #define sca_reg(reg, card) (0x8000 | (card)->io | \ ((reg) & 0x0F) | (((reg) & 0xF0) << 6)) @@ -157,7 +159,7 @@ -static int n2_set_clock(port_t *port, int value) +static int n2_set_iface(port_t *port) { card_t *card = port->card; int io = card->io; @@ -166,7 +168,7 @@ u8 rxs = port->rxs & CLK_BRG_MASK; u8 txs = port->txs & CLK_BRG_MASK; - switch(value) { + switch(port->settings.clock_type) { case CLOCK_EXT: mcr &= port->phy_node ? ~CLOCK_OUT_PORT1 : ~CLOCK_OUT_PORT0; rxs |= CLK_LINE_RX; /* RXC input */ @@ -200,17 +202,22 @@ port->txs = txs; sca_out(rxs, msci + RXS, card); sca_out(txs, msci + TXS, card); - port->clkmode = value; + sca_set_port(port); return 0; } -static int n2_open(hdlc_device *hdlc) +static int n2_open(struct net_device *dev) { + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); int io = port->card->io; - u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1 : TX422_PORT0); + u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1:TX422_PORT0); + + int result = hdlc_open(hdlc); + if (result) + return result; MOD_INC_USE_COUNT; mcr &= port->phy_node ? ~DTR_PORT1 : ~DTR_PORT0; /* set DTR ON */ @@ -219,14 +226,14 @@ outb(inb(io + N2_PCR) | PCR_ENWIN, io + N2_PCR); /* open window */ outb(inb(io + N2_PSR) | PSR_DMAEN, io + N2_PSR); /* enable dma */ sca_open(hdlc); - n2_set_clock(port, port->clkmode); - return 0; + return n2_set_iface(port); } -static void n2_close(hdlc_device *hdlc) +static int n2_close(struct net_device *dev) { + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); int io = port->card->io; u8 mcr = inb(io+N2_MCR) | (port->phy_node ? TX422_PORT1 : TX422_PORT0); @@ -234,52 +241,48 @@ sca_close(hdlc); mcr |= port->phy_node ? DTR_PORT1 : DTR_PORT0; /* set DTR OFF */ outb(mcr, io + N2_MCR); + hdlc_close(hdlc); MOD_DEC_USE_COUNT; + return 0; } -static int n2_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd) +static int n2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - int value = ifr->ifr_ifru.ifru_ivalue; - int result = 0; + union line_settings *line = &ifr->ifr_settings->ifs_line; + const size_t size = sizeof(sync_serial_settings); + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - - switch(cmd) { - case HDLCSCLOCK: - result = n2_set_clock(port, value); - case HDLCGCLOCK: - value = port->clkmode; - break; - - case HDLCSCLOCKRATE: - port->clkrate = value; - sca_set_clock(port); - case HDLCGCLOCKRATE: - value = port->clkrate; - break; - - case HDLCSLINE: - result = sca_set_loopback(port, value); - case HDLCGLINE: - value = port->line; - break; - -#ifdef DEBUG_RINGS - case HDLCRUN: +#ifdef CONFIG_HDLC_DEBUG_RINGS + if (cmd == SIOCDEVPRIVATE) { sca_dump_rings(hdlc); return 0; -#endif /* DEBUG_RINGS */ + } +#endif + if (cmd != SIOCWANDEV) + return hdlc_ioctl(dev, ifr, cmd); + + switch(ifr->ifr_settings->type) { + case IF_GET_IFACE: + ifr->ifr_settings->type = IF_IFACE_SYNC_SERIAL; + if (copy_to_user(&line->sync, &port->settings, size)) + return -EFAULT; + return 0; + + case IF_IFACE_SYNC_SERIAL: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (copy_from_user(&port->settings, &line->sync, size)) + return -EFAULT; + /* FIXME - put sanity checks here */ + return n2_set_iface(port); default: - return -EINVAL; + return hdlc_ioctl(dev, ifr, cmd); } - - ifr->ifr_ifru.ifru_ivalue = value; - return result; } @@ -465,6 +468,7 @@ sca_init(card, 0); for (cnt = 0; cnt < 2; cnt++) { port_t *port = &card->ports[cnt]; + struct net_device *dev = hdlc_to_dev(&port->hdlc); if ((cnt == 0 && !valid0) || (cnt == 1 && !valid1)) continue; @@ -476,14 +480,16 @@ port->log_node = 1; spin_lock_init(&port->lock); - hdlc_to_dev(&port->hdlc)->irq = irq; - hdlc_to_dev(&port->hdlc)->mem_start = winbase; - hdlc_to_dev(&port->hdlc)->mem_end = winbase + USE_WINDOWSIZE-1; - hdlc_to_dev(&port->hdlc)->tx_queue_len = 50; - port->hdlc.ioctl = n2_ioctl; - port->hdlc.open = n2_open; - port->hdlc.close = n2_close; + dev->irq = irq; + dev->mem_start = winbase; + dev->mem_end = winbase + USE_WINDOWSIZE-1; + dev->tx_queue_len = 50; + dev->do_ioctl = n2_ioctl; + dev->open = n2_open; + dev->stop = n2_close; + port->hdlc.attach = sca_attach; port->hdlc.xmit = sca_xmit; + port->settings.clock_type = CLOCK_EXT; if (register_hdlc_device(&port->hdlc)) { printk(KERN_WARNING "n2: unable to register hdlc " @@ -515,7 +521,7 @@ return -ENOSYS; /* no parameters specified, abort */ } - printk(KERN_INFO "%s\n", version); + printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version); do { unsigned long io, irq, ram; diff -Nru a/drivers/net/wan/sdla_x25.c b/drivers/net/wan/sdla_x25.c --- a/drivers/net/wan/sdla_x25.c Thu Mar 7 18:17:39 2002 +++ b/drivers/net/wan/sdla_x25.c Thu Mar 7 18:17:39 2002 @@ -1272,7 +1272,7 @@ add_timer(&card->u.x.x25_timer); } } - /* Device is not up untill the we are in connected state */ + /* Device is not up until the we are in connected state */ do_gettimeofday( &tv ); chan->router_start_time = tv.tv_sec; @@ -4756,7 +4756,7 @@ * send_delayed_cmd_result * * Wait commands like PLEACE CALL or CLEAR CALL must wait - * untill the result arrivers. This function passes + * until the result arrives. This function passes * the result to a waiting sock. * *===============================================================*/ diff -Nru a/drivers/net/wd.c b/drivers/net/wd.c --- a/drivers/net/wd.c Thu Mar 7 18:17:38 2002 +++ b/drivers/net/wd.c Thu Mar 7 18:17:38 2002 @@ -450,10 +450,11 @@ MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_WD_CARDS) "i"); MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_WD_CARDS) "i"); MODULE_PARM(mem_end, "1-" __MODULE_STRING(MAX_WD_CARDS) "i"); -MODULE_PARM_DESC(io, "WD80x3 I/O base address(es)"); -MODULE_PARM_DESC(irq, "WD80x3 IRQ number(s) (ignored for PureData boards)"); -MODULE_PARM_DESC(mem, "WD80x3 memory base address(es)(ignored for PureData boards)"); -MODULE_PARM_DESC(mem_end, "WD80x3 memory end address(es)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)"); +MODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)"); +MODULE_PARM_DESC(mem_end, "memory end address(es)"); +MODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver"); MODULE_LICENSE("GPL"); /* This is set up so that only a single autoprobe takes place per call. diff -Nru a/drivers/net/winbond-840.c b/drivers/net/winbond-840.c --- a/drivers/net/winbond-840.c Thu Mar 7 18:17:39 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1757 +0,0 @@ -/* winbond-840.c: A Linux PCI network adapter device driver. */ -/* - Written 1998-2001 by Donald Becker. - - This software may be used and distributed according to the terms of - the GNU General Public License (GPL), incorporated herein by reference. - Drivers based on or derived from this code fall under the GPL and must - retain the authorship, copyright and license notice. This file is not - a complete program and may only be used when the entire operating - system is licensed under the GPL. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - Support and updates available at - http://www.scyld.com/network/drivers.html - - Do not remove the copyright infomation. - Do not change the version information unless an improvement has been made. - Merely removing my name, as Compex has done in the past, does not count - as an improvement. - - Changelog: - * ported to 2.4 - ??? - * spin lock update, memory barriers, new style dma mappings - limit each tx buffer to < 1024 bytes - remove DescIntr from Rx descriptors (that's an Tx flag) - remove next pointer from Tx descriptors - synchronize tx_q_bytes - software reset in tx_timeout - Copyright (C) 2000 Manfred Spraul - * further cleanups - power management. - support for big endian descriptors - Copyright (C) 2001 Manfred Spraul - * ethtool support (jgarzik) - * Replace some MII-related magic numbers with constants (jgarzik) - - TODO: - * enable pci_power_off - * Wake-On-LAN -*/ - -#define DRV_NAME "winbond-840" -#define DRV_VERSION "1.01-d" -#define DRV_RELDATE "Nov-17-2001" - - -/* Automatically extracted configuration info: -probe-func: winbond840_probe -config-in: tristate 'Winbond W89c840 Ethernet support' CONFIG_WINBOND_840 - -c-help-name: Winbond W89c840 PCI Ethernet support -c-help-symbol: CONFIG_WINBOND_840 -c-help: This driver is for the Winbond W89c840 chip. It also works with -c-help: the TX9882 chip on the Compex RL100-ATX board. -c-help: More specific information and updates are available from -c-help: http://www.scyld.com/network/drivers.html -*/ - -/* The user-configurable values. - These may be modified when a driver module is loaded.*/ - -static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ -static int max_interrupt_work = 20; -/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). - The '840 uses a 64 element hash table based on the Ethernet CRC. */ -static int multicast_filter_limit = 32; - -/* Set the copy breakpoint for the copy-only-tiny-frames scheme. - Setting to > 1518 effectively disables this feature. */ -static int rx_copybreak; - -/* Used to pass the media type, etc. - Both 'options[]' and 'full_duplex[]' should exist for driver - interoperability. - The media type is usually passed in 'options[]'. -*/ -#define MAX_UNITS 8 /* More are supported, limit only on options */ -static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; - -/* Operational parameters that are set at compile time. */ - -/* Keep the ring sizes a power of two for compile efficiency. - The compiler will convert '%'<2^N> into a bit mask. - Making the Tx ring too large decreases the effectiveness of channel - bonding and packet priority. - There are no ill effects from too-large receive rings. */ -#define TX_RING_SIZE 16 -#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */ -#define TX_QUEUE_LEN_RESTART 5 -#define RX_RING_SIZE 32 - -#define TX_BUFLIMIT (1024-128) - -/* The presumed FIFO size for working around the Tx-FIFO-overflow bug. - To avoid overflowing we don't queue again until we have room for a - full-size packet. - */ -#define TX_FIFO_SIZE (2048) -#define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16) - - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (2*HZ) - -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ - -#ifndef __KERNEL__ -#define __KERNEL__ -#endif -#if !defined(__OPTIMIZE__) -#warning You must compile this file with the correct options! -#warning See the last lines of the source file. -#error You must compile this driver with "-O". -#endif - -/* Include files, designed to support most kernel versions 2.0.0 and later. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* Processor type for cache alignment. */ -#include -#include -#include - -/* These identify the driver base version and may not be removed. */ -static char version[] __devinitdata = -KERN_INFO DRV_NAME ".c:v" DRV_VERSION " (2.4 port) " DRV_RELDATE " Donald Becker \n" -KERN_INFO " http://www.scyld.com/network/drivers.html\n"; - -MODULE_AUTHOR("Donald Becker "); -MODULE_DESCRIPTION("Winbond W89c840 Ethernet driver"); -MODULE_LICENSE("GPL"); - -MODULE_PARM(max_interrupt_work, "i"); -MODULE_PARM(debug, "i"); -MODULE_PARM(rx_copybreak, "i"); -MODULE_PARM(multicast_filter_limit, "i"); -MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM_DESC(max_interrupt_work, "winbond-840 maximum events handled per interrupt"); -MODULE_PARM_DESC(debug, "winbond-840 debug level (0-6)"); -MODULE_PARM_DESC(rx_copybreak, "winbond-840 copy breakpoint for copy-only-tiny-frames"); -MODULE_PARM_DESC(multicast_filter_limit, "winbond-840 maximum number of filtered multicast addresses"); -MODULE_PARM_DESC(options, "winbond-840: Bits 0-3: media type, bit 17: full duplex"); -MODULE_PARM_DESC(full_duplex, "winbond-840 full duplex setting(s) (1)"); - -/* - Theory of Operation - -I. Board Compatibility - -This driver is for the Winbond w89c840 chip. - -II. Board-specific settings - -None. - -III. Driver operation - -This chip is very similar to the Digital 21*4* "Tulip" family. The first -twelve registers and the descriptor format are nearly identical. Read a -Tulip manual for operational details. - -A significant difference is that the multicast filter and station address are -stored in registers rather than loaded through a pseudo-transmit packet. - -Unlike the Tulip, transmit buffers are limited to 1KB. To transmit a -full-sized packet we must use both data buffers in a descriptor. Thus the -driver uses ring mode where descriptors are implicitly sequential in memory, -rather than using the second descriptor address as a chain pointer to -subsequent descriptors. - -IV. Notes - -If you are going to almost clone a Tulip, why not go all the way and avoid -the need for a new driver? - -IVb. References - -http://www.scyld.com/expert/100mbps.html -http://www.scyld.com/expert/NWay.html -http://www.winbond.com.tw/ - -IVc. Errata - -A horrible bug exists in the transmit FIFO. Apparently the chip doesn't -correctly detect a full FIFO, and queuing more than 2048 bytes may result in -silent data corruption. - -Test with 'ping -s 10000' on a fast computer. - -*/ - - - -/* - PCI probe table. -*/ -enum pci_id_flags_bits { - /* Set PCI command register bits before calling probe1(). */ - PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, - /* Read and map the single following PCI BAR. */ - PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4, - PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400, -}; -enum chip_capability_flags { - CanHaveMII=1, HasBrokenTx=2, AlwaysFDX=4, FDXOnNoMII=8,}; -#ifdef USE_IO_OPS -#define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER) -#else -#define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER) -#endif - -static struct pci_device_id w840_pci_tbl[] __devinitdata = { - { 0x1050, 0x0840, PCI_ANY_ID, 0x8153, 0, 0, 0 }, - { 0x1050, 0x0840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, - { 0x11f6, 0x2011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, w840_pci_tbl); - -struct pci_id_info { - const char *name; - struct match_info { - int pci, pci_mask, subsystem, subsystem_mask; - int revision, revision_mask; /* Only 8 bits. */ - } id; - enum pci_id_flags_bits pci_flags; - int io_size; /* Needed for I/O region check or ioremap(). */ - int drv_flags; /* Driver use, intended as capability flags. */ -}; -static struct pci_id_info pci_id_tbl[] = { - {"Winbond W89c840", /* Sometime a Level-One switch card. */ - { 0x08401050, 0xffffffff, 0x81530000, 0xffff0000 }, - W840_FLAGS, 128, CanHaveMII | HasBrokenTx | FDXOnNoMII}, - {"Winbond W89c840", { 0x08401050, 0xffffffff, }, - W840_FLAGS, 128, CanHaveMII | HasBrokenTx}, - {"Compex RL100-ATX", { 0x201111F6, 0xffffffff,}, - W840_FLAGS, 128, CanHaveMII | HasBrokenTx}, - {0,}, /* 0 terminated list. */ -}; - -/* This driver was written to use PCI memory space, however some x86 systems - work only with I/O space accesses. Pass -DUSE_IO_OPS to use PCI I/O space - accesses instead of memory space. */ - -#ifdef USE_IO_OPS -#undef readb -#undef readw -#undef readl -#undef writeb -#undef writew -#undef writel -#define readb inb -#define readw inw -#define readl inl -#define writeb outb -#define writew outw -#define writel outl -#endif - -/* Offsets to the Command and Status Registers, "CSRs". - While similar to the Tulip, these registers are longword aligned. - Note: It's not useful to define symbolic names for every register bit in - the device. The name can only partially document the semantics and make - the driver longer and more difficult to read. -*/ -enum w840_offsets { - PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08, - RxRingPtr=0x0C, TxRingPtr=0x10, - IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C, - RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C, - CurRxDescAddr=0x30, CurRxBufAddr=0x34, /* Debug use */ - MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40, - CurTxDescAddr=0x4C, CurTxBufAddr=0x50, -}; - -/* Bits in the interrupt status/enable registers. */ -/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */ -enum intr_status_bits { - NormalIntr=0x10000, AbnormalIntr=0x8000, - IntrPCIErr=0x2000, TimerInt=0x800, - IntrRxDied=0x100, RxNoBuf=0x80, IntrRxDone=0x40, - TxFIFOUnderflow=0x20, RxErrIntr=0x10, - TxIdle=0x04, IntrTxStopped=0x02, IntrTxDone=0x01, -}; - -/* Bits in the NetworkConfig register. */ -enum rx_mode_bits { - AcceptErr=0x80, AcceptRunt=0x40, - AcceptBroadcast=0x20, AcceptMulticast=0x10, - AcceptAllPhys=0x08, AcceptMyPhys=0x02, -}; - -enum mii_reg_bits { - MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000, - MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000, -}; - -/* The Tulip Rx and Tx buffer descriptors. */ -struct w840_rx_desc { - s32 status; - s32 length; - u32 buffer1; - u32 buffer2; -}; - -struct w840_tx_desc { - s32 status; - s32 length; - u32 buffer1, buffer2; -}; - -/* Bits in network_desc.status */ -enum desc_status_bits { - DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000, - DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000, - DescIntr=0x80000000, -}; - -#define PRIV_ALIGN 15 /* Required alignment mask */ -#define MII_CNT 1 /* winbond only supports one MII */ -struct netdev_private { - struct w840_rx_desc *rx_ring; - dma_addr_t rx_addr[RX_RING_SIZE]; - struct w840_tx_desc *tx_ring; - dma_addr_t tx_addr[TX_RING_SIZE]; - dma_addr_t ring_dma_addr; - /* The addresses of receive-in-place skbuffs. */ - struct sk_buff* rx_skbuff[RX_RING_SIZE]; - /* The saved address of a sent-in-place packet/buffer, for later free(). */ - struct sk_buff* tx_skbuff[TX_RING_SIZE]; - struct net_device_stats stats; - struct timer_list timer; /* Media monitoring timer. */ - /* Frequently used values: keep some adjacent for cache effect. */ - spinlock_t lock; - int chip_id, drv_flags; - struct pci_dev *pci_dev; - int csr6; - struct w840_rx_desc *rx_head_desc; - unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ - unsigned int rx_buf_sz; /* Based on MTU+slack. */ - unsigned int cur_tx, dirty_tx; - unsigned int tx_q_bytes; - unsigned int tx_full; /* The Tx queue is full. */ - /* MII transceiver section. */ - int mii_cnt; /* MII device addresses. */ - unsigned char phys[MII_CNT]; /* MII device addresses, but only the first is used */ - u32 mii; - struct mii_if_info mii_if; -}; - -static int eeprom_read(long ioaddr, int location); -static int mdio_read(struct net_device *dev, int phy_id, int location); -static void mdio_write(struct net_device *dev, int phy_id, int location, int value); -static int netdev_open(struct net_device *dev); -static int update_link(struct net_device *dev); -static void netdev_timer(unsigned long data); -static void init_rxtx_rings(struct net_device *dev); -static void free_rxtx_rings(struct netdev_private *np); -static void init_registers(struct net_device *dev); -static void tx_timeout(struct net_device *dev); -static int alloc_ringdesc(struct net_device *dev); -static void free_ringdesc(struct netdev_private *np); -static int start_tx(struct sk_buff *skb, struct net_device *dev); -static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); -static void netdev_error(struct net_device *dev, int intr_status); -static int netdev_rx(struct net_device *dev); -static u32 __set_rx_mode(struct net_device *dev); -static void set_rx_mode(struct net_device *dev); -static struct net_device_stats *get_stats(struct net_device *dev); -static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static int netdev_close(struct net_device *dev); - - - -static int __devinit w840_probe1 (struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct net_device *dev; - struct netdev_private *np; - static int find_cnt; - int chip_idx = ent->driver_data; - int irq; - int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0; - long ioaddr; - - i = pci_enable_device(pdev); - if (i) return i; - - pci_set_master(pdev); - - irq = pdev->irq; - - if (pci_set_dma_mask(pdev,0xFFFFffff)) { - printk(KERN_WARNING "Winbond-840: Device %s disabled due to DMA limitations.\n", - pdev->slot_name); - return -EIO; - } - dev = alloc_etherdev(sizeof(*np)); - if (!dev) - return -ENOMEM; - SET_MODULE_OWNER(dev); - - if (pci_request_regions(pdev, DRV_NAME)) - goto err_out_netdev; - -#ifdef USE_IO_OPS - ioaddr = pci_resource_start(pdev, 0); -#else - ioaddr = pci_resource_start(pdev, 1); - ioaddr = (long) ioremap (ioaddr, pci_id_tbl[chip_idx].io_size); - if (!ioaddr) - goto err_out_free_res; -#endif - - for (i = 0; i < 3; i++) - ((u16 *)dev->dev_addr)[i] = le16_to_cpu(eeprom_read(ioaddr, i)); - - /* Reset the chip to erase previous misconfiguration. - No hold time required! */ - writel(0x00000001, ioaddr + PCIBusCfg); - - dev->base_addr = ioaddr; - dev->irq = irq; - - np = dev->priv; - np->pci_dev = pdev; - np->chip_id = chip_idx; - np->drv_flags = pci_id_tbl[chip_idx].drv_flags; - spin_lock_init(&np->lock); - np->mii_if.dev = dev; - np->mii_if.mdio_read = mdio_read; - np->mii_if.mdio_write = mdio_write; - - pci_set_drvdata(pdev, dev); - - if (dev->mem_start) - option = dev->mem_start; - - /* The lower four bits are the media type. */ - if (option > 0) { - if (option & 0x200) - np->mii_if.full_duplex = 1; - if (option & 15) - printk(KERN_INFO "%s: ignoring user supplied media type %d", - dev->name, option & 15); - } - if (find_cnt < MAX_UNITS && full_duplex[find_cnt] > 0) - np->mii_if.full_duplex = 1; - - if (np->mii_if.full_duplex) - np->mii_if.duplex_lock = 1; - - /* The chip-specific entries in the device structure. */ - dev->open = &netdev_open; - dev->hard_start_xmit = &start_tx; - dev->stop = &netdev_close; - dev->get_stats = &get_stats; - dev->set_multicast_list = &set_rx_mode; - dev->do_ioctl = &netdev_ioctl; - dev->tx_timeout = &tx_timeout; - dev->watchdog_timeo = TX_TIMEOUT; - - i = register_netdev(dev); - if (i) - goto err_out_cleardev; - - printk(KERN_INFO "%s: %s at 0x%lx, ", - dev->name, pci_id_tbl[chip_idx].name, ioaddr); - for (i = 0; i < 5; i++) - printk("%2.2x:", dev->dev_addr[i]); - printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); - - if (np->drv_flags & CanHaveMII) { - int phy, phy_idx = 0; - for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) { - int mii_status = mdio_read(dev, phy, MII_BMSR); - if (mii_status != 0xffff && mii_status != 0x0000) { - np->phys[phy_idx++] = phy; - np->mii_if.advertising = mdio_read(dev, phy, MII_ADVERTISE); - np->mii = (mdio_read(dev, phy, MII_PHYSID1) << 16)+ - mdio_read(dev, phy, MII_PHYSID2); - printk(KERN_INFO "%s: MII PHY %8.8xh found at address %d, status " - "0x%4.4x advertising %4.4x.\n", - dev->name, np->mii, phy, mii_status, np->mii_if.advertising); - } - } - np->mii_cnt = phy_idx; - np->mii_if.phy_id = np->phys[0]; - if (phy_idx == 0) { - printk(KERN_WARNING "%s: MII PHY not found -- this device may " - "not operate correctly.\n", dev->name); - } - } - - find_cnt++; - return 0; - -err_out_cleardev: - pci_set_drvdata(pdev, NULL); -#ifndef USE_IO_OPS - iounmap((void *)ioaddr); -err_out_free_res: -#endif - pci_release_regions(pdev); -err_out_netdev: - kfree (dev); - return -ENODEV; -} - - -/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. These are - often serial bit streams generated by the host processor. - The example below is for the common 93c46 EEPROM, 64 16 bit words. */ - -/* Delay between EEPROM clock transitions. - No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need - a delay. Note that pre-2.0.34 kernels had a cache-alignment bug that - made udelay() unreliable. - The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is - depricated. -*/ -#define eeprom_delay(ee_addr) readl(ee_addr) - -enum EEPROM_Ctrl_Bits { - EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805, - EE_ChipSelect=0x801, EE_DataIn=0x08, -}; - -/* The EEPROM commands include the alway-set leading bit. */ -enum EEPROM_Cmds { - EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6), -}; - -static int eeprom_read(long addr, int location) -{ - int i; - int retval = 0; - long ee_addr = addr + EECtrl; - int read_cmd = location | EE_ReadCmd; - writel(EE_ChipSelect, ee_addr); - - /* Shift the read command bits out. */ - for (i = 10; i >= 0; i--) { - short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0; - writel(dataval, ee_addr); - eeprom_delay(ee_addr); - writel(dataval | EE_ShiftClk, ee_addr); - eeprom_delay(ee_addr); - } - writel(EE_ChipSelect, ee_addr); - eeprom_delay(ee_addr); - - for (i = 16; i > 0; i--) { - writel(EE_ChipSelect | EE_ShiftClk, ee_addr); - eeprom_delay(ee_addr); - retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0); - writel(EE_ChipSelect, ee_addr); - eeprom_delay(ee_addr); - } - - /* Terminate the EEPROM access. */ - writel(0, ee_addr); - return retval; -} - -/* MII transceiver control section. - Read and write the MII registers using software-generated serial - MDIO protocol. See the MII specifications or DP83840A data sheet - for details. - - The maximum data clock rate is 2.5 Mhz. The minimum timing is usually - met by back-to-back 33Mhz PCI cycles. */ -#define mdio_delay(mdio_addr) readl(mdio_addr) - -/* Set iff a MII transceiver on any interface requires mdio preamble. - This only set with older tranceivers, so the extra - code size of a per-interface flag is not worthwhile. */ -static char mii_preamble_required = 1; - -#define MDIO_WRITE0 (MDIO_EnbOutput) -#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput) - -/* Generate the preamble required for initial synchronization and - a few older transceivers. */ -static void mdio_sync(long mdio_addr) -{ - int bits = 32; - - /* Establish sync by sending at least 32 logic ones. */ - while (--bits >= 0) { - writel(MDIO_WRITE1, mdio_addr); - mdio_delay(mdio_addr); - writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); - mdio_delay(mdio_addr); - } -} - -static int mdio_read(struct net_device *dev, int phy_id, int location) -{ - long mdio_addr = dev->base_addr + MIICtrl; - int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; - int i, retval = 0; - - if (mii_preamble_required) - mdio_sync(mdio_addr); - - /* Shift the read command bits out. */ - for (i = 15; i >= 0; i--) { - int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; - - writel(dataval, mdio_addr); - mdio_delay(mdio_addr); - writel(dataval | MDIO_ShiftClk, mdio_addr); - mdio_delay(mdio_addr); - } - /* Read the two transition, 16 data, and wire-idle bits. */ - for (i = 20; i > 0; i--) { - writel(MDIO_EnbIn, mdio_addr); - mdio_delay(mdio_addr); - retval = (retval << 1) | ((readl(mdio_addr) & MDIO_DataIn) ? 1 : 0); - writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); - mdio_delay(mdio_addr); - } - return (retval>>1) & 0xffff; -} - -static void mdio_write(struct net_device *dev, int phy_id, int location, int value) -{ - struct netdev_private *np = dev->priv; - long mdio_addr = dev->base_addr + MIICtrl; - int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; - int i; - - if (location == 4 && phy_id == np->phys[0]) - np->mii_if.advertising = value; - - if (mii_preamble_required) - mdio_sync(mdio_addr); - - /* Shift the command bits out. */ - for (i = 31; i >= 0; i--) { - int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; - - writel(dataval, mdio_addr); - mdio_delay(mdio_addr); - writel(dataval | MDIO_ShiftClk, mdio_addr); - mdio_delay(mdio_addr); - } - /* Clear out extra bits. */ - for (i = 2; i > 0; i--) { - writel(MDIO_EnbIn, mdio_addr); - mdio_delay(mdio_addr); - writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); - mdio_delay(mdio_addr); - } - return; -} - - -static int netdev_open(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; - int i; - - writel(0x00000001, ioaddr + PCIBusCfg); /* Reset */ - - netif_device_detach(dev); - i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev); - if (i) - goto out_err; - - if (debug > 1) - printk(KERN_DEBUG "%s: w89c840_open() irq %d.\n", - dev->name, dev->irq); - - if((i=alloc_ringdesc(dev))) - goto out_err; - - spin_lock_irq(&np->lock); - netif_device_attach(dev); - init_registers(dev); - spin_unlock_irq(&np->lock); - - netif_start_queue(dev); - if (debug > 2) - printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name); - - /* Set the timer to check for link beat. */ - init_timer(&np->timer); - np->timer.expires = jiffies + 1*HZ; - np->timer.data = (unsigned long)dev; - np->timer.function = &netdev_timer; /* timer handler */ - add_timer(&np->timer); - return 0; -out_err: - netif_device_attach(dev); - return i; -} - -#define MII_DAVICOM_DM9101 0x0181b800 - -static int update_link(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - int duplex, fasteth, result, mii_reg; - - /* BSMR */ - mii_reg = mdio_read(dev, np->phys[0], MII_BMSR); - - if (mii_reg == 0xffff) - return np->csr6; - /* reread: the link status bit is sticky */ - mii_reg = mdio_read(dev, np->phys[0], MII_BMSR); - if (!(mii_reg & 0x4)) { - if (netif_carrier_ok(dev)) { - if (debug) - printk(KERN_INFO "%s: MII #%d reports no link. Disabling watchdog.\n", - dev->name, np->phys[0]); - netif_carrier_off(dev); - } - return np->csr6; - } - if (!netif_carrier_ok(dev)) { - if (debug) - printk(KERN_INFO "%s: MII #%d link is back. Enabling watchdog.\n", - dev->name, np->phys[0]); - netif_carrier_on(dev); - } - - if ((np->mii & ~0xf) == MII_DAVICOM_DM9101) { - /* If the link partner doesn't support autonegotiation - * the MII detects it's abilities with the "parallel detection". - * Some MIIs update the LPA register to the result of the parallel - * detection, some don't. - * The Davicom PHY [at least 0181b800] doesn't. - * Instead bit 9 and 13 of the BMCR are updated to the result - * of the negotiation.. - */ - mii_reg = mdio_read(dev, np->phys[0], MII_BMCR); - duplex = mii_reg & BMCR_FULLDPLX; - fasteth = mii_reg & BMCR_SPEED100; - } else { - int negotiated; - mii_reg = mdio_read(dev, np->phys[0], MII_LPA); - negotiated = mii_reg & np->mii_if.advertising; - - duplex = (negotiated & LPA_100FULL) || ((negotiated & 0x02C0) == LPA_10FULL); - fasteth = negotiated & 0x380; - } - duplex |= np->mii_if.duplex_lock; - /* remove fastether and fullduplex */ - result = np->csr6 & ~0x20000200; - if (duplex) - result |= 0x200; - if (fasteth) - result |= 0x20000000; - if (result != np->csr6 && debug) - printk(KERN_INFO "%s: Setting %dMBit-%s-duplex based on MII#%d\n", - dev->name, fasteth ? 100 : 10, - duplex ? "full" : "half", np->phys[0]); - return result; -} - -#define RXTX_TIMEOUT 2000 -static inline void update_csr6(struct net_device *dev, int new) -{ - struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; - int limit = RXTX_TIMEOUT; - - if (!netif_device_present(dev)) - new = 0; - if (new==np->csr6) - return; - /* stop both Tx and Rx processes */ - writel(np->csr6 & ~0x2002, ioaddr + NetworkConfig); - /* wait until they have really stopped */ - for (;;) { - int csr5 = readl(ioaddr + IntrStatus); - int t; - - t = (csr5 >> 17) & 0x07; - if (t==0||t==1) { - /* rx stopped */ - t = (csr5 >> 20) & 0x07; - if (t==0||t==1) - break; - } - - limit--; - if(!limit) { - printk(KERN_INFO "%s: couldn't stop rxtx, IntrStatus %xh.\n", - dev->name, csr5); - break; - } - udelay(1); - } - np->csr6 = new; - /* and restart them with the new configuration */ - writel(np->csr6, ioaddr + NetworkConfig); - if (new & 0x200) - np->mii_if.full_duplex = 1; -} - -static void netdev_timer(unsigned long data) -{ - struct net_device *dev = (struct net_device *)data; - struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; - - if (debug > 2) - printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x " - "config %8.8x.\n", - dev->name, (int)readl(ioaddr + IntrStatus), - (int)readl(ioaddr + NetworkConfig)); - spin_lock_irq(&np->lock); - update_csr6(dev, update_link(dev)); - spin_unlock_irq(&np->lock); - np->timer.expires = jiffies + 10*HZ; - add_timer(&np->timer); -} - -static void init_rxtx_rings(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - int i; - - np->rx_head_desc = &np->rx_ring[0]; - np->tx_ring = (struct w840_tx_desc*)&np->rx_ring[RX_RING_SIZE]; - - /* Initial all Rx descriptors. */ - for (i = 0; i < RX_RING_SIZE; i++) { - np->rx_ring[i].length = np->rx_buf_sz; - np->rx_ring[i].status = 0; - np->rx_skbuff[i] = 0; - } - /* Mark the last entry as wrapping the ring. */ - np->rx_ring[i-1].length |= DescEndRing; - - /* Fill in the Rx buffers. Handle allocation failure gracefully. */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); - np->rx_skbuff[i] = skb; - if (skb == NULL) - break; - skb->dev = dev; /* Mark as being used by this device. */ - np->rx_addr[i] = pci_map_single(np->pci_dev,skb->tail, - skb->len,PCI_DMA_FROMDEVICE); - - np->rx_ring[i].buffer1 = np->rx_addr[i]; - np->rx_ring[i].status = DescOwn; - } - - np->cur_rx = 0; - np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); - - /* Initialize the Tx descriptors */ - for (i = 0; i < TX_RING_SIZE; i++) { - np->tx_skbuff[i] = 0; - np->tx_ring[i].status = 0; - } - np->tx_full = 0; - np->tx_q_bytes = np->dirty_tx = np->cur_tx = 0; - - writel(np->ring_dma_addr, dev->base_addr + RxRingPtr); - writel(np->ring_dma_addr+sizeof(struct w840_rx_desc)*RX_RING_SIZE, - dev->base_addr + TxRingPtr); - -} - -static void free_rxtx_rings(struct netdev_private* np) -{ - int i; - /* Free all the skbuffs in the Rx queue. */ - for (i = 0; i < RX_RING_SIZE; i++) { - np->rx_ring[i].status = 0; - if (np->rx_skbuff[i]) { - pci_unmap_single(np->pci_dev, - np->rx_addr[i], - np->rx_skbuff[i]->len, - PCI_DMA_FROMDEVICE); - dev_kfree_skb(np->rx_skbuff[i]); - } - np->rx_skbuff[i] = 0; - } - for (i = 0; i < TX_RING_SIZE; i++) { - if (np->tx_skbuff[i]) { - pci_unmap_single(np->pci_dev, - np->tx_addr[i], - np->tx_skbuff[i]->len, - PCI_DMA_TODEVICE); - dev_kfree_skb(np->tx_skbuff[i]); - } - np->tx_skbuff[i] = 0; - } -} - -static void init_registers(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; - int i; - - for (i = 0; i < 6; i++) - writeb(dev->dev_addr[i], ioaddr + StationAddr + i); - - /* Initialize other registers. */ -#ifdef __BIG_ENDIAN - i = (1<<20); /* Big-endian descriptors */ -#else - i = 0; -#endif - i |= (0x04<<2); /* skip length 4 u32 */ - i |= 0x02; /* give Rx priority */ - - /* Configure the PCI bus bursts and FIFO thresholds. - 486: Set 8 longword cache alignment, 8 longword burst. - 586: Set 16 longword cache alignment, no burst limit. - Cache alignment bits 15:14 Burst length 13:8 - 0000 0000 align to cache 0800 8 longwords - 4000 8 longwords 0100 1 longword 1000 16 longwords - 8000 16 longwords 0200 2 longwords 2000 32 longwords - C000 32 longwords 0400 4 longwords */ - -#if defined (__i386__) && !defined(MODULE) - /* When not a module we can work around broken '486 PCI boards. */ - if (boot_cpu_data.x86 <= 4) { - i |= 0x4800; - printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache " - "alignment to 8 longwords.\n", dev->name); - } else { - i |= 0xE000; - } -#elif defined(__powerpc__) || defined(__i386__) || defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) - i |= 0xE000; -#elif defined(__sparc__) - i |= 0x4800; -#else -#warning Processor architecture undefined - i |= 0x4800; -#endif - writel(i, ioaddr + PCIBusCfg); - - np->csr6 = 0; - /* 128 byte Tx threshold; - Transmit on; Receive on; */ - update_csr6(dev, 0x00022002 | update_link(dev) | __set_rx_mode(dev)); - - /* Clear and Enable interrupts by setting the interrupt mask. */ - writel(0x1A0F5, ioaddr + IntrStatus); - writel(0x1A0F5, ioaddr + IntrEnable); - - writel(0, ioaddr + RxStartDemand); -} - -static void tx_timeout(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; - - printk(KERN_WARNING "%s: Transmit timed out, status %8.8x," - " resetting...\n", dev->name, (int)readl(ioaddr + IntrStatus)); - - { - int i; - printk(KERN_DEBUG " Rx ring %p: ", np->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)np->rx_ring[i].status); - printk("\n"KERN_DEBUG" Tx ring %p: ", np->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - printk(" %8.8x", np->tx_ring[i].status); - printk("\n"); - } - printk(KERN_DEBUG "Tx cur %d Tx dirty %d Tx Full %d, q bytes %d.\n", - np->cur_tx, np->dirty_tx, np->tx_full, np->tx_q_bytes); - printk(KERN_DEBUG "Tx Descriptor addr %xh.\n",readl(ioaddr+0x4C)); - - disable_irq(dev->irq); - spin_lock_irq(&np->lock); - /* - * Under high load dirty_tx and the internal tx descriptor pointer - * come out of sync, thus perform a software reset and reinitialize - * everything. - */ - - writel(1, dev->base_addr+PCIBusCfg); - udelay(1); - - free_rxtx_rings(np); - init_rxtx_rings(dev); - init_registers(dev); - spin_unlock_irq(&np->lock); - enable_irq(dev->irq); - - netif_wake_queue(dev); - dev->trans_start = jiffies; - np->stats.tx_errors++; - return; -} - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static int alloc_ringdesc(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - - np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); - - np->rx_ring = pci_alloc_consistent(np->pci_dev, - sizeof(struct w840_rx_desc)*RX_RING_SIZE + - sizeof(struct w840_tx_desc)*TX_RING_SIZE, - &np->ring_dma_addr); - if(!np->rx_ring) - return -ENOMEM; - init_rxtx_rings(dev); - return 0; -} - -static void free_ringdesc(struct netdev_private *np) -{ - pci_free_consistent(np->pci_dev, - sizeof(struct w840_rx_desc)*RX_RING_SIZE + - sizeof(struct w840_tx_desc)*TX_RING_SIZE, - np->rx_ring, np->ring_dma_addr); - -} - -static int start_tx(struct sk_buff *skb, struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - unsigned entry; - - /* Caution: the write order is important here, set the field - with the "ownership" bits last. */ - - /* Calculate the next Tx descriptor entry. */ - entry = np->cur_tx % TX_RING_SIZE; - - np->tx_addr[entry] = pci_map_single(np->pci_dev, - skb->data,skb->len, PCI_DMA_TODEVICE); - np->tx_skbuff[entry] = skb; - - np->tx_ring[entry].buffer1 = np->tx_addr[entry]; - if (skb->len < TX_BUFLIMIT) { - np->tx_ring[entry].length = DescWholePkt | skb->len; - } else { - int len = skb->len - TX_BUFLIMIT; - - np->tx_ring[entry].buffer2 = np->tx_addr[entry]+TX_BUFLIMIT; - np->tx_ring[entry].length = DescWholePkt | (len << 11) | TX_BUFLIMIT; - } - if(entry == TX_RING_SIZE-1) - np->tx_ring[entry].length |= DescEndRing; - - /* Now acquire the irq spinlock. - * The difficult race is the the ordering between - * increasing np->cur_tx and setting DescOwn: - * - if np->cur_tx is increased first the interrupt - * handler could consider the packet as transmitted - * since DescOwn is cleared. - * - If DescOwn is set first the NIC could report the - * packet as sent, but the interrupt handler would ignore it - * since the np->cur_tx was not yet increased. - */ - spin_lock_irq(&np->lock); - np->cur_tx++; - - wmb(); /* flush length, buffer1, buffer2 */ - np->tx_ring[entry].status = DescOwn; - wmb(); /* flush status and kick the hardware */ - writel(0, dev->base_addr + TxStartDemand); - np->tx_q_bytes += skb->len; - /* Work around horrible bug in the chip by marking the queue as full - when we do not have FIFO room for a maximum sized packet. */ - if (np->cur_tx - np->dirty_tx > TX_QUEUE_LEN || - ((np->drv_flags & HasBrokenTx) && np->tx_q_bytes > TX_BUG_FIFO_LIMIT)) { - netif_stop_queue(dev); - wmb(); - np->tx_full = 1; - } - spin_unlock_irq(&np->lock); - - dev->trans_start = jiffies; - - if (debug > 4) { - printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n", - dev->name, np->cur_tx, entry); - } - return 0; -} - -static void netdev_tx_done(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { - int entry = np->dirty_tx % TX_RING_SIZE; - int tx_status = np->tx_ring[entry].status; - - if (tx_status < 0) - break; - if (tx_status & 0x8000) { /* There was an error, log it. */ -#ifndef final_version - if (debug > 1) - printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", - dev->name, tx_status); -#endif - np->stats.tx_errors++; - if (tx_status & 0x0104) np->stats.tx_aborted_errors++; - if (tx_status & 0x0C80) np->stats.tx_carrier_errors++; - if (tx_status & 0x0200) np->stats.tx_window_errors++; - if (tx_status & 0x0002) np->stats.tx_fifo_errors++; - if ((tx_status & 0x0080) && np->mii_if.full_duplex == 0) - np->stats.tx_heartbeat_errors++; -#ifdef ETHER_STATS - if (tx_status & 0x0100) np->stats.collisions16++; -#endif - } else { -#ifdef ETHER_STATS - if (tx_status & 0x0001) np->stats.tx_deferred++; -#endif -#ifndef final_version - if (debug > 3) - printk(KERN_DEBUG "%s: Transmit slot %d ok, Tx status %8.8x.\n", - dev->name, entry, tx_status); -#endif - np->stats.tx_bytes += np->tx_skbuff[entry]->len; - np->stats.collisions += (tx_status >> 3) & 15; - np->stats.tx_packets++; - } - /* Free the original skb. */ - pci_unmap_single(np->pci_dev,np->tx_addr[entry], - np->tx_skbuff[entry]->len, - PCI_DMA_TODEVICE); - np->tx_q_bytes -= np->tx_skbuff[entry]->len; - dev_kfree_skb_irq(np->tx_skbuff[entry]); - np->tx_skbuff[entry] = 0; - } - if (np->tx_full && - np->cur_tx - np->dirty_tx < TX_QUEUE_LEN_RESTART && - np->tx_q_bytes < TX_BUG_FIFO_LIMIT) { - /* The ring is no longer full, clear tbusy. */ - np->tx_full = 0; - wmb(); - netif_wake_queue(dev); - } -} - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ -static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) -{ - struct net_device *dev = (struct net_device *)dev_instance; - struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; - int work_limit = max_interrupt_work; - - if (!netif_device_present(dev)) - return; - do { - u32 intr_status = readl(ioaddr + IntrStatus); - - /* Acknowledge all of the current interrupt sources ASAP. */ - writel(intr_status & 0x001ffff, ioaddr + IntrStatus); - - if (debug > 4) - printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", - dev->name, intr_status); - - if ((intr_status & (NormalIntr|AbnormalIntr)) == 0) - break; - - if (intr_status & (IntrRxDone | RxNoBuf)) - netdev_rx(dev); - if (intr_status & RxNoBuf) - writel(0, ioaddr + RxStartDemand); - - if (intr_status & (TxIdle | IntrTxDone) && - np->cur_tx != np->dirty_tx) { - spin_lock(&np->lock); - netdev_tx_done(dev); - spin_unlock(&np->lock); - } - - /* Abnormal error summary/uncommon events handlers. */ - if (intr_status & (AbnormalIntr | TxFIFOUnderflow | IntrPCIErr | - TimerInt | IntrTxStopped)) - netdev_error(dev, intr_status); - - if (--work_limit < 0) { - printk(KERN_WARNING "%s: Too much work at interrupt, " - "status=0x%4.4x.\n", dev->name, intr_status); - /* Set the timer to re-enable the other interrupts after - 10*82usec ticks. */ - spin_lock(&np->lock); - if (netif_device_present(dev)) { - writel(AbnormalIntr | TimerInt, ioaddr + IntrEnable); - writel(10, ioaddr + GPTimer); - } - spin_unlock(&np->lock); - break; - } - } while (1); - - if (debug > 3) - printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", - dev->name, (int)readl(ioaddr + IntrStatus)); -} - -/* This routine is logically part of the interrupt handler, but separated - for clarity and better register allocation. */ -static int netdev_rx(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - int entry = np->cur_rx % RX_RING_SIZE; - int work_limit = np->dirty_rx + RX_RING_SIZE - np->cur_rx; - - if (debug > 4) { - printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n", - entry, np->rx_ring[entry].status); - } - - /* If EOP is set on the next entry, it's a new packet. Send it up. */ - while (--work_limit >= 0) { - struct w840_rx_desc *desc = np->rx_head_desc; - s32 status = desc->status; - - if (debug > 4) - printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n", - status); - if (status < 0) - break; - if ((status & 0x38008300) != 0x0300) { - if ((status & 0x38000300) != 0x0300) { - /* Ingore earlier buffers. */ - if ((status & 0xffff) != 0x7fff) { - printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " - "multiple buffers, entry %#x status %4.4x!\n", - dev->name, np->cur_rx, status); - np->stats.rx_length_errors++; - } - } else if (status & 0x8000) { - /* There was a fatal error. */ - if (debug > 2) - printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", - dev->name, status); - np->stats.rx_errors++; /* end of a packet.*/ - if (status & 0x0890) np->stats.rx_length_errors++; - if (status & 0x004C) np->stats.rx_frame_errors++; - if (status & 0x0002) np->stats.rx_crc_errors++; - } - } else { - struct sk_buff *skb; - /* Omit the four octet CRC from the length. */ - int pkt_len = ((status >> 16) & 0x7ff) - 4; - -#ifndef final_version - if (debug > 4) - printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d" - " status %x.\n", pkt_len, status); -#endif - /* Check if the packet is long enough to accept without copying - to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak - && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; - skb_reserve(skb, 2); /* 16 byte align the IP header */ - pci_dma_sync_single(np->pci_dev,np->rx_addr[entry], - np->rx_skbuff[entry]->len, - PCI_DMA_FROMDEVICE); - /* Call copy + cksum if available. */ -#if HAS_IP_COPYSUM - eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); - skb_put(skb, pkt_len); -#else - memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail, - pkt_len); -#endif - } else { - pci_unmap_single(np->pci_dev,np->rx_addr[entry], - np->rx_skbuff[entry]->len, - PCI_DMA_FROMDEVICE); - skb_put(skb = np->rx_skbuff[entry], pkt_len); - np->rx_skbuff[entry] = NULL; - } -#ifndef final_version /* Remove after testing. */ - /* You will want this info for the initial debug. */ - if (debug > 5) - printk(KERN_DEBUG " Rx data %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:" - "%2.2x %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x %2.2x%2.2x " - "%d.%d.%d.%d.\n", - skb->data[0], skb->data[1], skb->data[2], skb->data[3], - skb->data[4], skb->data[5], skb->data[6], skb->data[7], - skb->data[8], skb->data[9], skb->data[10], - skb->data[11], skb->data[12], skb->data[13], - skb->data[14], skb->data[15], skb->data[16], - skb->data[17]); -#endif - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->last_rx = jiffies; - np->stats.rx_packets++; - np->stats.rx_bytes += pkt_len; - } - entry = (++np->cur_rx) % RX_RING_SIZE; - np->rx_head_desc = &np->rx_ring[entry]; - } - - /* Refill the Rx ring buffers. */ - for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { - struct sk_buff *skb; - entry = np->dirty_rx % RX_RING_SIZE; - if (np->rx_skbuff[entry] == NULL) { - skb = dev_alloc_skb(np->rx_buf_sz); - np->rx_skbuff[entry] = skb; - if (skb == NULL) - break; /* Better luck next round. */ - skb->dev = dev; /* Mark as being used by this device. */ - np->rx_addr[entry] = pci_map_single(np->pci_dev, - skb->tail, - skb->len, PCI_DMA_FROMDEVICE); - np->rx_ring[entry].buffer1 = np->rx_addr[entry]; - } - wmb(); - np->rx_ring[entry].status = DescOwn; - } - - return 0; -} - -static void netdev_error(struct net_device *dev, int intr_status) -{ - long ioaddr = dev->base_addr; - struct netdev_private *np = dev->priv; - - if (debug > 2) - printk(KERN_DEBUG "%s: Abnormal event, %8.8x.\n", - dev->name, intr_status); - if (intr_status == 0xffffffff) - return; - spin_lock(&np->lock); - if (intr_status & TxFIFOUnderflow) { - int new; - /* Bump up the Tx threshold */ -#if 0 - /* This causes lots of dropped packets, - * and under high load even tx_timeouts - */ - new = np->csr6 + 0x4000; -#else - new = (np->csr6 >> 14)&0x7f; - if (new < 64) - new *= 2; - else - new = 127; /* load full packet before starting */ - new = (np->csr6 & ~(0x7F << 14)) | (new<<14); -#endif - printk(KERN_DEBUG "%s: Tx underflow, new csr6 %8.8x.\n", - dev->name, new); - update_csr6(dev, new); - } - if (intr_status & IntrRxDied) { /* Missed a Rx frame. */ - np->stats.rx_errors++; - } - if (intr_status & TimerInt) { - /* Re-enable other interrupts. */ - if (netif_device_present(dev)) - writel(0x1A0F5, ioaddr + IntrEnable); - } - np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; - writel(0, ioaddr + RxStartDemand); - spin_unlock(&np->lock); -} - -static struct net_device_stats *get_stats(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - struct netdev_private *np = dev->priv; - - /* The chip only need report frame silently dropped. */ - spin_lock_irq(&np->lock); - if (netif_running(dev) && netif_device_present(dev)) - np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; - spin_unlock_irq(&np->lock); - - return &np->stats; -} - - -static u32 __set_rx_mode(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - u32 mc_filter[2]; /* Multicast hash filter */ - u32 rx_mode; - - if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ - /* Unconditionally log net taps. */ - printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); - memset(mc_filter, 0xff, sizeof(mc_filter)); - rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAllPhys - | AcceptMyPhys; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { - /* Too many to match, or accept all multicasts. */ - memset(mc_filter, 0xff, sizeof(mc_filter)); - rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; - } else { - struct dev_mc_list *mclist; - int i; - memset(mc_filter, 0, sizeof(mc_filter)); - for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; - i++, mclist = mclist->next) { - set_bit((ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26) ^ 0x3F, - mc_filter); - } - rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; - } - writel(mc_filter[0], ioaddr + MulticastFilter0); - writel(mc_filter[1], ioaddr + MulticastFilter1); - return rx_mode; -} - -static void set_rx_mode(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - u32 rx_mode = __set_rx_mode(dev); - spin_lock_irq(&np->lock); - update_csr6(dev, (np->csr6 & ~0x00F8) | rx_mode); - spin_unlock_irq(&np->lock); -} - -static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) -{ - struct netdev_private *np = dev->priv; - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - strcpy(info.bus_info, np->pci_dev->slot_name); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - /* get settings */ - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - spin_lock_irq(&np->lock); - mii_ethtool_gset(&np->mii_if, &ecmd); - spin_unlock_irq(&np->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - /* set settings */ - case ETHTOOL_SSET: { - int r; - struct ethtool_cmd ecmd; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - spin_lock_irq(&np->lock); - r = mii_ethtool_sset(&np->mii_if, &ecmd); - spin_unlock_irq(&np->lock); - return r; - } - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST: { - return mii_nway_restart(&np->mii_if); - } - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; - edata.data = mii_link_ok(&np->mii_if); - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - debug = edata.data; - return 0; - } - } - - return -EOPNOTSUPP; -} - -static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data; - struct netdev_private *np = dev->priv; - - switch(cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - case SIOCGMIIPHY: /* Get address of MII PHY in use. */ - data->phy_id = ((struct netdev_private *)dev->priv)->phys[0] & 0x1f; - /* Fall Through */ - - case SIOCGMIIREG: /* Read MII PHY register. */ - spin_lock_irq(&np->lock); - data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f); - spin_unlock_irq(&np->lock); - return 0; - - case SIOCSMIIREG: /* Write MII PHY register. */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - spin_lock_irq(&np->lock); - mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in); - spin_unlock_irq(&np->lock); - return 0; - default: - return -EOPNOTSUPP; - } -} - -static int netdev_close(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - struct netdev_private *np = dev->priv; - - netif_stop_queue(dev); - - if (debug > 1) { - printk(KERN_DEBUG "%s: Shutting down ethercard, status was %8.8x " - "Config %8.8x.\n", dev->name, (int)readl(ioaddr + IntrStatus), - (int)readl(ioaddr + NetworkConfig)); - printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n", - dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx); - } - - /* Stop the chip's Tx and Rx processes. */ - spin_lock_irq(&np->lock); - netif_device_detach(dev); - update_csr6(dev, 0); - writel(0x0000, ioaddr + IntrEnable); - spin_unlock_irq(&np->lock); - - free_irq(dev->irq, dev); - wmb(); - netif_device_attach(dev); - - if (readl(ioaddr + NetworkConfig) != 0xffffffff) - np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; - -#ifdef __i386__ - if (debug > 2) { - int i; - - printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n", - (int)np->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - printk(" #%d desc. %4.4x %4.4x %8.8x.\n", - i, np->tx_ring[i].length, - np->tx_ring[i].status, np->tx_ring[i].buffer1); - printk("\n"KERN_DEBUG " Rx ring %8.8x:\n", - (int)np->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) { - printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", - i, np->rx_ring[i].length, - np->rx_ring[i].status, np->rx_ring[i].buffer1); - } - } -#endif /* __i386__ debugging only */ - - del_timer_sync(&np->timer); - - free_rxtx_rings(np); - free_ringdesc(np); - - return 0; -} - -static void __devexit w840_remove1 (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - - /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ - if (dev) { - unregister_netdev(dev); - pci_release_regions(pdev); -#ifndef USE_IO_OPS - iounmap((char *)(dev->base_addr)); -#endif - kfree(dev); - } - - pci_set_drvdata(pdev, NULL); -} - -#ifdef CONFIG_PM - -/* - * suspend/resume synchronization: - * - open, close, do_ioctl: - * rtnl_lock, & netif_device_detach after the rtnl_unlock. - * - get_stats: - * spin_lock_irq(np->lock), doesn't touch hw if not present - * - hard_start_xmit: - * netif_stop_queue + spin_unlock_wait(&dev->xmit_lock); - * - tx_timeout: - * netif_device_detach + spin_unlock_wait(&dev->xmit_lock); - * - set_multicast_list - * netif_device_detach + spin_unlock_wait(&dev->xmit_lock); - * - interrupt handler - * doesn't touch hw if not present, synchronize_irq waits for - * running instances of the interrupt handler. - * - * Disabling hw requires clearing csr6 & IntrEnable. - * update_csr6 & all function that write IntrEnable check netif_device_present - * before settings any bits. - * - * Detach must occur under spin_unlock_irq(), interrupts from a detached - * device would cause an irq storm. - */ -static int w840_suspend (struct pci_dev *pdev, u32 state) -{ - struct net_device *dev = pci_get_drvdata (pdev); - struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; - - rtnl_lock(); - if (netif_running (dev)) { - del_timer_sync(&np->timer); - - spin_lock_irq(&np->lock); - netif_device_detach(dev); - update_csr6(dev, 0); - writel(0, ioaddr + IntrEnable); - netif_stop_queue(dev); - spin_unlock_irq(&np->lock); - - spin_unlock_wait(&dev->xmit_lock); - synchronize_irq(); - - np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; - - /* no more hardware accesses behind this line. */ - - if (np->csr6) BUG(); - if (readl(ioaddr + IntrEnable)) BUG(); - - /* pci_power_off(pdev, -1); */ - - free_rxtx_rings(np); - } else { - netif_device_detach(dev); - } - rtnl_unlock(); - return 0; -} - - -static int w840_resume (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata (pdev); - struct netdev_private *np = dev->priv; - - rtnl_lock(); - if (netif_device_present(dev)) - goto out; /* device not suspended */ - if (netif_running(dev)) { - pci_enable_device(pdev); - /* pci_power_on(pdev); */ - - spin_lock_irq(&np->lock); - writel(1, dev->base_addr+PCIBusCfg); - readl(dev->base_addr+PCIBusCfg); - udelay(1); - netif_device_attach(dev); - init_rxtx_rings(dev); - init_registers(dev); - spin_unlock_irq(&np->lock); - - netif_wake_queue(dev); - - np->timer.expires = jiffies + 1*HZ; - add_timer(&np->timer); - } else { - netif_device_attach(dev); - } -out: - rtnl_unlock(); - return 0; -} -#endif - -static struct pci_driver w840_driver = { - name: DRV_NAME, - id_table: w840_pci_tbl, - probe: w840_probe1, - remove: w840_remove1, -#ifdef CONFIG_PM - suspend: w840_suspend, - resume: w840_resume, -#endif -}; - -static int __init w840_init(void) -{ -/* when a module, this is printed whether or not devices are found in probe */ -#ifdef MODULE - printk(version); -#endif - return pci_module_init(&w840_driver); -} - -static void __exit w840_exit(void) -{ - pci_unregister_driver(&w840_driver); -} - -module_init(w840_init); -module_exit(w840_exit); diff -Nru a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c --- a/drivers/net/wireless/airo.c Thu Mar 7 18:17:38 2002 +++ b/drivers/net/wireless/airo.c Thu Mar 7 18:17:38 2002 @@ -63,7 +63,7 @@ name: "airo", id_table: card_ids, probe: airo_pci_probe, - remove: airo_pci_remove, + remove: __devexit_p(airo_pci_remove), }; #endif /* CONFIG_PCI */ diff -Nru a/drivers/net/wireless/netwave_cs.c b/drivers/net/wireless/netwave_cs.c --- a/drivers/net/wireless/netwave_cs.c Thu Mar 7 18:17:43 2002 +++ b/drivers/net/wireless/netwave_cs.c Thu Mar 7 18:17:43 2002 @@ -63,6 +63,9 @@ #ifdef CONFIG_NET_RADIO #include +#if WIRELESS_EXT > 12 +#include +#endif /* WIRELESS_EXT > 12 */ #endif #include @@ -269,6 +272,16 @@ because they generally can't be allocated dynamically. */ +#if WIRELESS_EXT <= 12 +/* Wireless extensions backward compatibility */ + +/* Part of iw_handler prototype we need */ +struct iw_request_info +{ + __u16 cmd; /* Wireless Extension command */ + __u16 flags; /* More to come ;-) */ +}; + /* Wireless Extension Backward compatibility - Jean II * If the new wireless device private ioctl range is not defined, * default to standard device private ioctl range */ @@ -276,8 +289,11 @@ #define SIOCIWFIRSTPRIV SIOCDEVPRIVATE #endif /* SIOCIWFIRSTPRIV */ -#define SIOCGIPSNAP SIOCIWFIRSTPRIV /* Site Survey Snapshot */ -/*#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1*/ +#else /* WIRELESS_EXT <= 12 */ +static const struct iw_handler_def netwave_handler_def; +#endif /* WIRELESS_EXT <= 12 */ + +#define SIOCGIPSNAP SIOCIWFIRSTPRIV + 1 /* Site Survey Snapshot */ #define MAX_ESA 10 @@ -483,7 +499,10 @@ /* wireless extensions */ #ifdef WIRELESS_EXT dev->get_wireless_stats = &netwave_get_wireless_stats; -#endif +#if WIRELESS_EXT > 12 + dev->wireless_handlers = (struct iw_handler_def *)&netwave_handler_def; +#endif /* WIRELESS_EXT > 12 */ +#endif /* WIRELESS_EXT */ dev->do_ioctl = &netwave_ioctl; dev->tx_timeout = &netwave_watchdog; @@ -596,6 +615,303 @@ } /* netwave_flush_stale_links */ /* + * Wireless Handler : get protocol name + */ +static int netwave_get_name(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + strcpy(wrqu->name, "Netwave"); + return 0; +} + +/* + * Wireless Handler : set Network ID + */ +static int netwave_set_nwid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long flags; + ioaddr_t iobase = dev->base_addr; + netwave_private *priv = (netwave_private *) dev->priv; + u_char *ramBase = priv->ramBase; + + /* Disable interrupts & save flags */ + save_flags(flags); + cli(); + +#if WIRELESS_EXT > 8 + if(!wrqu->nwid.disabled) { + domain = wrqu->nwid.value; +#else /* WIRELESS_EXT > 8 */ + if(wrqu->nwid.on) { + domain = wrqu->nwid.nwid; +#endif /* WIRELESS_EXT > 8 */ + printk( KERN_DEBUG "Setting domain to 0x%x%02x\n", + (domain >> 8) & 0x01, domain & 0xff); + wait_WOC(iobase); + writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0); + writeb( domain & 0xff, ramBase + NETWAVE_EREG_CB + 1); + writeb((domain >>8 ) & 0x01,ramBase + NETWAVE_EREG_CB+2); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + } + + /* ReEnable interrupts & restore flags */ + restore_flags(flags); + + return 0; +} + +/* + * Wireless Handler : get Network ID + */ +static int netwave_get_nwid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ +#if WIRELESS_EXT > 8 + wrqu->nwid.value = domain; + wrqu->nwid.disabled = 0; + wrqu->nwid.fixed = 1; +#else /* WIRELESS_EXT > 8 */ + wrqu->nwid.nwid = domain; + wrqu->nwid.on = 1; +#endif /* WIRELESS_EXT > 8 */ + + return 0; +} + +/* + * Wireless Handler : set scramble key + */ +static int netwave_set_scramble(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *key) +{ + unsigned long flags; + ioaddr_t iobase = dev->base_addr; + netwave_private *priv = (netwave_private *) dev->priv; + u_char *ramBase = priv->ramBase; + + /* Disable interrupts & save flags */ + save_flags(flags); + cli(); + + scramble_key = (key[0] << 8) | key[1]; + wait_WOC(iobase); + writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0); + writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1); + writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + + /* ReEnable interrupts & restore flags */ + restore_flags(flags); + + return 0; +} + +/* + * Wireless Handler : get scramble key + */ +static int netwave_get_scramble(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *key) +{ + key[1] = scramble_key & 0xff; + key[0] = (scramble_key>>8) & 0xff; +#if WIRELESS_EXT > 8 + wrqu->encoding.flags = IW_ENCODE_ENABLED; + wrqu->encoding.length = 2; +#else /* WIRELESS_EXT > 8 */ + wrqu->encoding.method = 1; +#endif /* WIRELESS_EXT > 8 */ + + return 0; +} + +#if WIRELESS_EXT > 8 +/* + * Wireless Handler : get mode + */ +static int netwave_get_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + if(domain & 0x100) + wrqu->mode = IW_MODE_INFRA; + else + wrqu->mode = IW_MODE_ADHOC; + + return 0; +} +#endif /* WIRELESS_EXT > 8 */ + +/* + * Wireless Handler : get range info + */ +static int netwave_get_range(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + struct iw_range *range = (struct iw_range *) extra; + int ret = 0; + + /* Set the length (very important for backward compatibility) */ + wrqu->data.length = sizeof(struct iw_range); + + /* Set all the info we don't care or don't know about to zero */ + memset(range, 0, sizeof(struct iw_range)); + +#if WIRELESS_EXT > 10 + /* Set the Wireless Extension versions */ + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 9; /* Nothing for us in v10 and v11 */ +#endif /* WIRELESS_EXT > 10 */ + + /* Set information in the range struct */ + range->throughput = 450 * 1000; /* don't argue on this ! */ + range->min_nwid = 0x0000; + range->max_nwid = 0x01FF; + + range->num_channels = range->num_frequency = 0; + + range->sensitivity = 0x3F; + range->max_qual.qual = 255; + range->max_qual.level = 255; + range->max_qual.noise = 0; + +#if WIRELESS_EXT > 7 + range->num_bitrates = 1; + range->bitrate[0] = 1000000; /* 1 Mb/s */ +#endif /* WIRELESS_EXT > 7 */ + +#if WIRELESS_EXT > 8 + range->encoding_size[0] = 2; /* 16 bits scrambling */ + range->num_encoding_sizes = 1; + range->max_encoding_tokens = 1; /* Only one key possible */ +#endif /* WIRELESS_EXT > 8 */ + + return ret; +} + +/* + * Wireless Private Handler : get snapshot + */ +static int netwave_get_snap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long flags; + ioaddr_t iobase = dev->base_addr; + netwave_private *priv = (netwave_private *) dev->priv; + u_char *ramBase = priv->ramBase; + + /* Disable interrupts & save flags */ + save_flags(flags); + cli(); + + /* Take snapshot of environment */ + netwave_snapshot( priv, ramBase, iobase); + wrqu->data.length = priv->nss.length; + memcpy(extra, (u_char *) &priv->nss, sizeof( struct site_survey)); + + priv->lastExec = jiffies; + + /* ReEnable interrupts & restore flags */ + restore_flags(flags); + + return(0); +} + +/* + * Structures to export the Wireless Handlers + * This is the stuff that are treated the wireless extensions (iwconfig) + */ + +static const struct iw_priv_args netwave_private_args[] = { +/*{ cmd, set_args, get_args, name } */ + { SIOCGIPSNAP, 0, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof(struct site_survey), + "getsitesurvey" }, +}; + +#if WIRELESS_EXT > 12 + +static const iw_handler netwave_handler[] = +{ + NULL, /* SIOCSIWNAME */ + netwave_get_name, /* SIOCGIWNAME */ + netwave_set_nwid, /* SIOCSIWNWID */ + netwave_get_nwid, /* SIOCGIWNWID */ + NULL, /* SIOCSIWFREQ */ + NULL, /* SIOCGIWFREQ */ + NULL, /* SIOCSIWMODE */ + netwave_get_mode, /* SIOCGIWMODE */ + NULL, /* SIOCSIWSENS */ + NULL, /* SIOCGIWSENS */ + NULL, /* SIOCSIWRANGE */ + netwave_get_range, /* SIOCGIWRANGE */ + NULL, /* SIOCSIWPRIV */ + NULL, /* SIOCGIWPRIV */ + NULL, /* SIOCSIWSTATS */ + NULL, /* SIOCGIWSTATS */ + NULL, /* SIOCSIWSPY */ + NULL, /* SIOCGIWSPY */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWAP */ + NULL, /* SIOCGIWAP */ + NULL, /* -- hole -- */ + NULL, /* SIOCGIWAPLIST */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWESSID */ + NULL, /* SIOCGIWESSID */ + NULL, /* SIOCSIWNICKN */ + NULL, /* SIOCGIWNICKN */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWRATE */ + NULL, /* SIOCGIWRATE */ + NULL, /* SIOCSIWRTS */ + NULL, /* SIOCGIWRTS */ + NULL, /* SIOCSIWFRAG */ + NULL, /* SIOCGIWFRAG */ + NULL, /* SIOCSIWTXPOW */ + NULL, /* SIOCGIWTXPOW */ + NULL, /* SIOCSIWRETRY */ + NULL, /* SIOCGIWRETRY */ + netwave_set_scramble, /* SIOCSIWENCODE */ + netwave_get_scramble, /* SIOCGIWENCODE */ +}; + +static const iw_handler netwave_private_handler[] = +{ + NULL, /* SIOCIWFIRSTPRIV */ + netwave_get_snap, /* SIOCIWFIRSTPRIV + 1 */ +}; + +static const struct iw_handler_def netwave_handler_def = +{ + num_standard: sizeof(netwave_handler)/sizeof(iw_handler), + num_private: sizeof(netwave_private_handler)/sizeof(iw_handler), + num_private_args: sizeof(netwave_private_args)/sizeof(struct iw_priv_args), + standard: (iw_handler *) netwave_handler, + private: (iw_handler *) netwave_private_handler, + private_args: (struct iw_priv_args *) netwave_private_args, +}; +#endif /* WIRELESS_EXT > 12 */ + +/* * Function netwave_ioctl (dev, rq, cmd) * * Perform ioctl : config & info stuff @@ -606,56 +922,28 @@ struct ifreq *rq, /* Data passed */ int cmd) /* Ioctl number */ { - unsigned long flags; int ret = 0; #ifdef WIRELESS_EXT - ioaddr_t iobase = dev->base_addr; - netwave_private *priv = (netwave_private *) dev->priv; - u_char *ramBase = priv->ramBase; +#if WIRELESS_EXT <= 12 struct iwreq *wrq = (struct iwreq *) rq; #endif +#endif DEBUG(0, "%s: ->netwave_ioctl(cmd=0x%X)\n", dev->name, cmd); - /* Disable interrupts & save flags */ - save_flags(flags); - cli(); - /* Look what is the request */ switch(cmd) { /* --------------- WIRELESS EXTENSIONS --------------- */ #ifdef WIRELESS_EXT +#if WIRELESS_EXT <= 12 case SIOCGIWNAME: - /* Get name */ - strcpy(wrq->u.name, "Netwave"); + netwave_get_name(dev, NULL, &(wrq->u), NULL); break; case SIOCSIWNWID: - /* Set domain */ -#if WIRELESS_EXT > 8 - if(!wrq->u.nwid.disabled) { - domain = wrq->u.nwid.value; -#else /* WIRELESS_EXT > 8 */ - if(wrq->u.nwid.on) { - domain = wrq->u.nwid.nwid; -#endif /* WIRELESS_EXT > 8 */ - printk( KERN_DEBUG "Setting domain to 0x%x%02x\n", - (domain >> 8) & 0x01, domain & 0xff); - wait_WOC(iobase); - writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0); - writeb( domain & 0xff, ramBase + NETWAVE_EREG_CB + 1); - writeb((domain >>8 ) & 0x01,ramBase + NETWAVE_EREG_CB+2); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); - } break; + ret = netwave_set_nwid(dev, NULL, &(wrq->u), NULL); + break; case SIOCGIWNWID: - /* Read domain*/ -#if WIRELESS_EXT > 8 - wrq->u.nwid.value = domain; - wrq->u.nwid.disabled = 0; - wrq->u.nwid.fixed = 1; -#else /* WIRELESS_EXT > 8 */ - wrq->u.nwid.nwid = domain; - wrq->u.nwid.on = 1; -#endif /* WIRELESS_EXT > 8 */ + ret = netwave_get_nwid(dev, NULL, &(wrq->u), NULL); break; #if WIRELESS_EXT > 8 /* Note : The API did change... */ case SIOCGIWENCODE: @@ -663,10 +951,7 @@ if(wrq->u.encoding.pointer != (caddr_t) 0) { char key[2]; - key[1] = scramble_key & 0xff; - key[0] = (scramble_key>>8) & 0xff; - wrq->u.encoding.flags = IW_ENCODE_ENABLED; - wrq->u.encoding.length = 2; + ret = netwave_get_scramble(dev, NULL, &(wrq->u), key); if(copy_to_user(wrq->u.encoding.pointer, key, 2)) ret = -EFAULT; } @@ -681,127 +966,68 @@ ret = -EFAULT; break; } - scramble_key = (key[0] << 8) | key[1]; - wait_WOC(iobase); - writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0); - writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1); - writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + ret = netwave_set_scramble(dev, NULL, &(wrq->u), key); } break; case SIOCGIWMODE: - /* Mode of operation */ - if(domain & 0x100) - wrq->u.mode = IW_MODE_INFRA; - else - wrq->u.mode = IW_MODE_ADHOC; - break; + /* Mode of operation */ + ret = netwave_get_mode(dev, NULL, &(wrq->u), NULL); + break; #else /* WIRELESS_EXT > 8 */ case SIOCGIWENCODE: /* Get scramble key */ - wrq->u.encoding.code = scramble_key; - wrq->u.encoding.method = 1; + ret = netwave_get_scramble(dev, NULL, &(wrq->u), + (char *) &wrq->u.encoding.code); break; case SIOCSIWENCODE: /* Set scramble key */ - scramble_key = wrq->u.encoding.code; - wait_WOC(iobase); - writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0); - writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1); - writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + ret = netwave_set_scramble(dev, NULL, &(wrq->u), + (char *) &wrq->u.encoding.code); break; #endif /* WIRELESS_EXT > 8 */ case SIOCGIWRANGE: /* Basic checking... */ if(wrq->u.data.pointer != (caddr_t) 0) { - struct iw_range range; - - /* Set the length (very important for backward compatibility) */ - wrq->u.data.length = sizeof(struct iw_range); - - /* Set all the info we don't care or don't know about to zero */ - memset(&range, 0, sizeof(range)); - -#if WIRELESS_EXT > 10 - /* Set the Wireless Extension versions */ - range.we_version_compiled = WIRELESS_EXT; - range.we_version_source = 9; /* Nothing for us in v10 and v11 */ -#endif /* WIRELESS_EXT > 10 */ - - /* Set information in the range struct */ - range.throughput = 450 * 1000; /* don't argue on this ! */ - range.min_nwid = 0x0000; - range.max_nwid = 0x01FF; - - range.num_channels = range.num_frequency = 0; - - range.sensitivity = 0x3F; - range.max_qual.qual = 255; - range.max_qual.level = 255; - range.max_qual.noise = 0; - -#if WIRELESS_EXT > 7 - range.num_bitrates = 1; - range.bitrate[0] = 1000000; /* 1 Mb/s */ -#endif /* WIRELESS_EXT > 7 */ - -#if WIRELESS_EXT > 8 - range.encoding_size[0] = 2; /* 16 bits scrambling */ - range.num_encoding_sizes = 1; - range.max_encoding_tokens = 1; /* Only one key possible */ -#endif /* WIRELESS_EXT > 8 */ - - /* Copy structure to the user buffer */ - if(copy_to_user(wrq->u.data.pointer, &range, - sizeof(struct iw_range))) - ret = -EFAULT; + struct iw_range range; + ret = netwave_get_range(dev, NULL, &(wrq->u), (char *) &range); + if (copy_to_user(wrq->u.data.pointer, &range, + sizeof(struct iw_range))) + ret = -EFAULT; } break; case SIOCGIWPRIV: /* Basic checking... */ if(wrq->u.data.pointer != (caddr_t) 0) { - struct iw_priv_args priv[] = - { /* cmd, set_args, get_args, name */ - { SIOCGIPSNAP, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 0, - sizeof(struct site_survey), - "getsitesurvey" }, - }; - /* Set the number of ioctl available */ - wrq->u.data.length = 1; + wrq->u.data.length = sizeof(netwave_private_args) / sizeof(netwave_private_args[0]); /* Copy structure to the user buffer */ - if(copy_to_user(wrq->u.data.pointer, (u_char *) priv, - sizeof(priv))) + if(copy_to_user(wrq->u.data.pointer, + (u_char *) netwave_private_args, + sizeof(netwave_private_args))) ret = -EFAULT; } break; case SIOCGIPSNAP: if(wrq->u.data.pointer != (caddr_t) 0) { - /* Take snapshot of environment */ - netwave_snapshot( priv, ramBase, iobase); - wrq->u.data.length = priv->nss.length; + char buffer[sizeof( struct site_survey)]; + ret = netwave_get_snap(dev, NULL, &(wrq->u), buffer); /* Copy structure to the user buffer */ if(copy_to_user(wrq->u.data.pointer, - (u_char *) &priv->nss, - sizeof( struct site_survey))) + buffer, + sizeof( struct site_survey))) { printk(KERN_DEBUG "Bad buffer!\n"); break; } - - priv->lastExec = jiffies; } break; -#endif +#endif /* WIRELESS_EXT <= 12 */ +#endif /* WIRELESS_EXT */ default: ret = -EOPNOTSUPP; } - /* ReEnable interrupts & restore flags */ - restore_flags(flags); - return ret; } diff -Nru a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c --- a/drivers/net/wireless/orinoco.c Thu Mar 7 18:17:44 2002 +++ b/drivers/net/wireless/orinoco.c Thu Mar 7 18:17:44 2002 @@ -1253,6 +1253,7 @@ /* Pass the packet to the networking stack */ netif_rx(skb); + dev->last_rx = jiffies; stats->rx_packets++; stats->rx_bytes += length; diff -Nru a/drivers/net/wireless/orinoco_plx.c b/drivers/net/wireless/orinoco_plx.c --- a/drivers/net/wireless/orinoco_plx.c Thu Mar 7 18:17:46 2002 +++ b/drivers/net/wireless/orinoco_plx.c Thu Mar 7 18:17:46 2002 @@ -279,7 +279,7 @@ name:"orinoco_plx", id_table:orinoco_plx_pci_id_table, probe:orinoco_plx_init_one, - remove:orinoco_plx_remove_one, + remove:__devexit_p(orinoco_plx_remove_one), suspend:0, resume:0 }; diff -Nru a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c --- a/drivers/net/wireless/wavelan.c Thu Mar 7 18:17:42 2002 +++ b/drivers/net/wireless/wavelan.c Thu Mar 7 18:17:42 2002 @@ -24,28 +24,6 @@ /*------------------------------------------------------------------*/ /* - * Wrapper for disabling interrupts and locking the driver. - * (note : inline, so optimised away) - */ -static inline void wv_splhi(net_local * lp, - unsigned long * pflags) -{ - spin_lock_irqsave(&lp->spinlock, *pflags); - /* Note : above does the cli(); itself */ -} - -/*------------------------------------------------------------------*/ -/* - * Wrapper for re-enabling interrupts and un-locking the driver. - */ -static inline void wv_splx(net_local * lp, - unsigned long * pflags) -{ - spin_unlock_irqrestore(&lp->spinlock, *pflags); -} - -/*------------------------------------------------------------------*/ -/* * Translate irq number to PSA irq parameter */ static u8 wv_irq_to_psa(int irq) @@ -870,10 +848,10 @@ /* Check if we can do it now ! */ if((netif_running(dev)) && !(netif_queue_stopped(dev))) { - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); /* May fail */ wv_82586_config(dev); - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); } else { #ifdef DEBUG_CONFIG_INFO @@ -1786,170 +1764,287 @@ /*------------------------------------------------------------------*/ /* - * Perform ioctl for configuration and information. - * It is here that the wireless extensions are treated (iwconfig). + * Wireless Handler : get protocol name */ -static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is applied */ - struct ifreq *rq, /* data passed */ - int cmd) -{ /* ioctl number */ +static int wavelan_get_name(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + strcpy(wrqu->name, "WaveLAN"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set NWID + */ +static int wavelan_set_nwid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ unsigned long ioaddr = dev->base_addr; net_local *lp = (net_local *) dev->priv; /* lp is not unused */ - struct iwreq *wrq = (struct iwreq *) rq; psa_t psa; mm_t m; unsigned long flags; int ret = 0; - int err = 0; -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, - cmd); -#endif + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Set NWID in WaveLAN. */ + if (!wrqu->nwid.disabled) { + /* Set NWID in psa */ + psa.psa_nwid[0] = (wrqu->nwid.value & 0xFF00) >> 8; + psa.psa_nwid[1] = wrqu->nwid.value & 0xFF; + psa.psa_nwid_select = 0x01; + psa_write(ioaddr, lp->hacr, + (char *) psa.psa_nwid - (char *) &psa, + (unsigned char *) psa.psa_nwid, 3); + + /* Set NWID in mmc. */ + m.w.mmw_netw_id_l = psa.psa_nwid[1]; + m.w.mmw_netw_id_h = psa.psa_nwid[0]; + mmc_write(ioaddr, + (char *) &m.w.mmw_netw_id_l - + (char *) &m, + (unsigned char *) &m.w.mmw_netw_id_l, 2); + mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00); + } else { + /* Disable NWID in the psa. */ + psa.psa_nwid_select = 0x00; + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_nwid_select - + (char *) &psa, + (unsigned char *) &psa.psa_nwid_select, + 1); + + /* Disable NWID in the mmc (no filtering). */ + mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), + MMW_LOOPT_SEL_DIS_NWID); + } + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get NWID + */ +static int wavelan_get_nwid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; /* Disable interrupts and save flags. */ - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); - /* Look what is the request */ - switch (cmd) { - /* --------------- WIRELESS EXTENSIONS --------------- */ - - case SIOCGIWNAME: - strcpy(wrq->u.name, "WaveLAN"); - break; - - case SIOCSIWNWID: - /* Set NWID in WaveLAN. */ - if (!wrq->u.nwid.disabled) { - /* Set NWID in psa */ - psa.psa_nwid[0] = - (wrq->u.nwid.value & 0xFF00) >> 8; - psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF; - psa.psa_nwid_select = 0x01; - psa_write(ioaddr, lp->hacr, - (char *) psa.psa_nwid - (char *) &psa, - (unsigned char *) psa.psa_nwid, 3); + /* Read the NWID. */ + psa_read(ioaddr, lp->hacr, + (char *) psa.psa_nwid - (char *) &psa, + (unsigned char *) psa.psa_nwid, 3); + wrqu->nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; + wrqu->nwid.disabled = !(psa.psa_nwid_select); + wrqu->nwid.fixed = 1; /* Superfluous */ - /* Set NWID in mmc. */ - m.w.mmw_netw_id_l = psa.psa_nwid[1]; - m.w.mmw_netw_id_h = psa.psa_nwid[0]; - mmc_write(ioaddr, - (char *) &m.w.mmw_netw_id_l - - (char *) &m, - (unsigned char *) &m.w.mmw_netw_id_l, 2); - mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00); - } else { - /* Disable NWID in the psa. */ - psa.psa_nwid_select = 0x00; - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_nwid_select - - (char *) &psa, - (unsigned char *) &psa.psa_nwid_select, - 1); + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); - /* Disable NWID in the mmc (no filtering). */ - mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), - MMW_LOOPT_SEL_DIS_NWID); - } - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, lp->hacr); - break; + return ret; +} - case SIOCGIWNWID: - /* Read the NWID. */ - psa_read(ioaddr, lp->hacr, - (char *) psa.psa_nwid - (char *) &psa, - (unsigned char *) psa.psa_nwid, 3); - wrq->u.nwid.value = - (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; - wrq->u.nwid.disabled = !(psa.psa_nwid_select); - wrq->u.nwid.fixed = 1; /* Superfluous */ - break; - - case SIOCSIWFREQ: - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ - if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) - ret = wv_set_frequency(ioaddr, &(wrq->u.freq)); - else - ret = -EOPNOTSUPP; - break; +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set frequency + */ +static int wavelan_set_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + unsigned long flags; + int ret; - case SIOCGIWFREQ: - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). - * Does it work for everybody, especially old cards? */ - if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { - unsigned short freq; - - /* Ask the EEPROM to read the frequency from the first area. */ - fee_read(ioaddr, 0x00, &freq, 1); - wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000; - wrq->u.freq.e = 1; - } else { - psa_read(ioaddr, lp->hacr, - (char *) &psa.psa_subband - (char *) &psa, - (unsigned char *) &psa.psa_subband, 1); - - if (psa.psa_subband <= 4) { - wrq->u.freq.m = - fixed_bands[psa.psa_subband]; - wrq->u.freq.e = (psa.psa_subband != 0); - } else - ret = -EOPNOTSUPP; - } - break; + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ + if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) + ret = wv_set_frequency(ioaddr, &(wrqu->freq)); + else + ret = -EOPNOTSUPP; - case SIOCSIWSENS: - /* Set the level threshold. */ - /* We should complain loudly if wrq->u.sens.fixed = 0, because we - * can't set auto mode... */ - psa.psa_thr_pre_set = wrq->u.sens.value & 0x3F; - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_thr_pre_set - (char *) &psa, - (unsigned char *) &psa.psa_thr_pre_set, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, lp->hacr); - mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), - psa.psa_thr_pre_set); - break; + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} - case SIOCGIWSENS: - /* Read the level threshold. */ +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get frequency + */ +static int wavelan_get_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). + * Does it work for everybody, especially old cards? */ + if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { + unsigned short freq; + + /* Ask the EEPROM to read the frequency from the first area. */ + fee_read(ioaddr, 0x00, &freq, 1); + wrqu->freq.m = ((freq >> 5) * 5 + 24000L) * 10000; + wrqu->freq.e = 1; + } else { psa_read(ioaddr, lp->hacr, - (char *) &psa.psa_thr_pre_set - (char *) &psa, - (unsigned char *) &psa.psa_thr_pre_set, 1); - wrq->u.sens.value = psa.psa_thr_pre_set & 0x3F; - wrq->u.sens.fixed = 1; - break; - - case SIOCSIWENCODE: - /* Set encryption key */ - if (!mmc_encr(ioaddr)) { + (char *) &psa.psa_subband - (char *) &psa, + (unsigned char *) &psa.psa_subband, 1); + + if (psa.psa_subband <= 4) { + wrqu->freq.m = fixed_bands[psa.psa_subband]; + wrqu->freq.e = (psa.psa_subband != 0); + } else ret = -EOPNOTSUPP; - break; - } + } - /* Basic checking... */ - if (wrq->u.encoding.pointer != (caddr_t) 0) { - /* Check the size of the key */ - if (wrq->u.encoding.length != 8) { - ret = -EINVAL; - break; - } + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); - /* Copy the key in the driver */ - wv_splx(lp, &flags); - err = copy_from_user(psa.psa_encryption_key, - wrq->u.encoding.pointer, - wrq->u.encoding.length); - wv_splhi(lp, &flags); - if (err) { - ret = -EFAULT; - break; - } + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set level threshold + */ +static int wavelan_set_sens(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Set the level threshold. */ + /* We should complain loudly if wrqu->sens.fixed = 0, because we + * can't set auto mode... */ + psa.psa_thr_pre_set = wrqu->sens.value & 0x3F; + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_thr_pre_set - (char *) &psa, + (unsigned char *) &psa.psa_thr_pre_set, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); + mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), + psa.psa_thr_pre_set); + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get level threshold + */ +static int wavelan_get_sens(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Read the level threshold. */ + psa_read(ioaddr, lp->hacr, + (char *) &psa.psa_thr_pre_set - (char *) &psa, + (unsigned char *) &psa.psa_thr_pre_set, 1); + wrqu->sens.value = psa.psa_thr_pre_set & 0x3F; + wrqu->sens.fixed = 1; + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set encryption key + */ +static int wavelan_set_encode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + unsigned long flags; + psa_t psa; + int ret = 0; + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Check if capable of encryption */ + if (!mmc_encr(ioaddr)) { + ret = -EOPNOTSUPP; + } + + /* Check the size of the key */ + if((wrqu->encoding.length != 8) && (wrqu->encoding.length != 0)) { + ret = -EINVAL; + } + + if(!ret) { + /* Basic checking... */ + if (wrqu->encoding.length == 8) { + /* Copy the key in the driver */ + memcpy(psa.psa_encryption_key, extra, + wrqu->encoding.length); psa.psa_encryption_select = 1; + psa_write(ioaddr, lp->hacr, (char *) &psa.psa_encryption_select - (char *) &psa, @@ -1963,7 +2058,8 @@ psa_encryption_key, 8); } - if (wrq->u.encoding.flags & IW_ENCODE_DISABLED) { /* disable encryption */ + /* disable encryption */ + if (wrqu->encoding.flags & IW_ENCODE_DISABLED) { psa.psa_encryption_select = 0; psa_write(ioaddr, lp->hacr, (char *) &psa.psa_encryption_select - @@ -1975,350 +2071,430 @@ } /* update the Wavelan checksum */ update_psa_checksum(dev, ioaddr, lp->hacr); - break; + } + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} - case SIOCGIWENCODE: +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get encryption key + */ +static int wavelan_get_encode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Check if encryption is available */ + if (!mmc_encr(ioaddr)) { + ret = -EOPNOTSUPP; + } else { /* Read the encryption key */ - if (!mmc_encr(ioaddr)) { - ret = -EOPNOTSUPP; - break; - } + psa_read(ioaddr, lp->hacr, + (char *) &psa.psa_encryption_select - + (char *) &psa, + (unsigned char *) &psa. + psa_encryption_select, 1 + 8); + + /* encryption is enabled ? */ + if (psa.psa_encryption_select) + wrqu->encoding.flags = IW_ENCODE_ENABLED; + else + wrqu->encoding.flags = IW_ENCODE_DISABLED; + wrqu->encoding.flags |= mmc_encr(ioaddr); - /* only super-user can see encryption key */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } + /* Copy the key to the user buffer */ + wrqu->encoding.length = 8; + memcpy(extra, psa.psa_encryption_key, wrqu->encoding.length); + } - /* Basic checking... */ - if (wrq->u.encoding.pointer != (caddr_t) 0) { - /* Verify the user buffer */ - ret = - verify_area(VERIFY_WRITE, - wrq->u.encoding.pointer, 8); - if (ret) - break; - - psa_read(ioaddr, lp->hacr, - (char *) &psa.psa_encryption_select - - (char *) &psa, - (unsigned char *) &psa. - psa_encryption_select, 1 + 8); - - /* encryption is enabled ? */ - if (psa.psa_encryption_select) - wrq->u.encoding.flags = IW_ENCODE_ENABLED; - else - wrq->u.encoding.flags = IW_ENCODE_DISABLED; - wrq->u.encoding.flags |= mmc_encr(ioaddr); + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); - /* Copy the key to the user buffer */ - wrq->u.encoding.length = 8; - wv_splx(lp, &flags); - if (copy_to_user(wrq->u.encoding.pointer, - psa.psa_encryption_key, 8)) - ret = -EFAULT; - wv_splhi(lp, &flags); - } - break; + return ret; +} - case SIOCGIWRANGE: - /* basic checking */ - if (wrq->u.data.pointer != (caddr_t) 0) { - struct iw_range range; - - /* Set the length (very important for backward - * compatibility) */ - wrq->u.data.length = sizeof(struct iw_range); - - /* Set all the info we don't care or don't know - * about to zero */ - memset(&range, 0, sizeof(range)); - - /* Set the Wireless Extension versions */ - range.we_version_compiled = WIRELESS_EXT; - range.we_version_source = 9; - - /* Set information in the range struct. */ - range.throughput = 1.6 * 1000 * 1000; /* don't argue on this ! */ - range.min_nwid = 0x0000; - range.max_nwid = 0xFFFF; - - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ - if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { - range.num_channels = 10; - range.num_frequency = - wv_frequency_list(ioaddr, range.freq, - IW_MAX_FREQUENCIES); - } else - range.num_channels = range.num_frequency = - 0; - - range.sensitivity = 0x3F; - range.max_qual.qual = MMR_SGNL_QUAL; - range.max_qual.level = MMR_SIGNAL_LVL; - range.max_qual.noise = MMR_SILENCE_LVL; - range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */ - /* Need to get better values for those two */ - range.avg_qual.level = 30; - range.avg_qual.noise = 8; - - range.num_bitrates = 1; - range.bitrate[0] = 2000000; /* 2 Mb/s */ - - /* Encryption supported ? */ - if (mmc_encr(ioaddr)) { - range.encoding_size[0] = 8; /* DES = 64 bits key */ - range.num_encoding_sizes = 1; - range.max_encoding_tokens = 1; /* Only one key possible */ - } else { - range.num_encoding_sizes = 0; - range.max_encoding_tokens = 0; - } +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get range info + */ +static int wavelan_get_range(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + struct iw_range *range = (struct iw_range *) extra; + unsigned long flags; + int ret = 0; - /* Copy structure to the user buffer. */ - wv_splx(lp, &flags); - if (copy_to_user(wrq->u.data.pointer, - &range, - sizeof(struct iw_range))) - ret = -EFAULT; - wv_splhi(lp, &flags); - } - break; + /* Set the length (very important for backward compatibility) */ + wrqu->data.length = sizeof(struct iw_range); - case SIOCGIWPRIV: - /* Basic checking */ - if (wrq->u.data.pointer != (caddr_t) 0) { - struct iw_priv_args priv[] = { - /* { cmd, - set_args, - get_args, - name } */ - { SIOCSIPQTHR, - IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, - 0, - "setqualthr" }, - { SIOCGIPQTHR, - 0, - IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, - "getqualthr" }, - { SIOCSIPHISTO, - IW_PRIV_TYPE_BYTE | 16, - 0, - "sethisto" }, - { SIOCGIPHISTO, - 0, - IW_PRIV_TYPE_INT | 16, - "gethisto" }, - }; - - /* Set the number of available ioctls. */ - wrq->u.data.length = 4; - - /* Copy structure to the user buffer. */ - wv_splx(lp, &flags); - if (copy_to_user(wrq->u.data.pointer, - (u8 *) priv, - sizeof(priv))) - ret = -EFAULT; - wv_splhi(lp, &flags); - } - break; + /* Set all the info we don't care or don't know about to zero */ + memset(range, 0, sizeof(struct iw_range)); -#ifdef WIRELESS_SPY - case SIOCSIWSPY: - /* Set the spy list */ + /* Set the Wireless Extension versions */ + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 9; + + /* Set information in the range struct. */ + range->throughput = 1.6 * 1000 * 1000; /* don't argue on this ! */ + range->min_nwid = 0x0000; + range->max_nwid = 0xFFFF; + + range->sensitivity = 0x3F; + range->max_qual.qual = MMR_SGNL_QUAL; + range->max_qual.level = MMR_SIGNAL_LVL; + range->max_qual.noise = MMR_SILENCE_LVL; + range->avg_qual.qual = MMR_SGNL_QUAL; /* Always max */ + /* Need to get better values for those two */ + range->avg_qual.level = 30; + range->avg_qual.noise = 8; - /* Check the number of addresses. */ - if (wrq->u.data.length > IW_MAX_SPY) { - ret = -E2BIG; - break; - } - lp->spy_number = wrq->u.data.length; + range->num_bitrates = 1; + range->bitrate[0] = 2000000; /* 2 Mb/s */ - /* Are there are addresses to copy? */ - if (lp->spy_number > 0) { - struct sockaddr address[IW_MAX_SPY]; - int i; - - /* Copy addresses to the driver. */ - wv_splx(lp, &flags); - err = copy_from_user(address, - wrq->u.data.pointer, - sizeof(struct sockaddr) - * lp->spy_number); - wv_splhi(lp, &flags); - if (err) { - ret = -EFAULT; - break; - } + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ + if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { + range->num_channels = 10; + range->num_frequency = wv_frequency_list(ioaddr, range->freq, + IW_MAX_FREQUENCIES); + } else + range->num_channels = range->num_frequency = 0; - /* Copy addresses to the lp structure. */ - for (i = 0; i < lp->spy_number; i++) { - memcpy(lp->spy_address[i], - address[i].sa_data, - WAVELAN_ADDR_SIZE); - } + /* Encryption supported ? */ + if (mmc_encr(ioaddr)) { + range->encoding_size[0] = 8; /* DES = 64 bits key */ + range->num_encoding_sizes = 1; + range->max_encoding_tokens = 1; /* Only one key possible */ + } else { + range->num_encoding_sizes = 0; + range->max_encoding_tokens = 0; + } + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +#ifdef WIRELESS_SPY +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set spy list + */ +static int wavelan_set_spy(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + struct sockaddr *address = (struct sockaddr *) extra; + int i; + int ret = 0; - /* Reset structure. */ - memset(lp->spy_stat, 0x00, - sizeof(iw_qual) * IW_MAX_SPY); + /* Disable spy while we copy the addresses. + * As we don't disable interrupts, we need to do this */ + lp->spy_number = 0; + + /* Are there are addresses to copy? */ + if (wrqu->data.length > 0) { + /* Copy addresses to the lp structure. */ + for (i = 0; i < wrqu->data.length; i++) { + memcpy(lp->spy_address[i], address[i].sa_data, + WAVELAN_ADDR_SIZE); + } + + /* Reset structure. */ + memset(lp->spy_stat, 0x00, sizeof(iw_qual) * IW_MAX_SPY); #ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG + "SetSpy: set of new addresses is: \n"); + for (i = 0; i < wrqu->data.length; i++) printk(KERN_DEBUG - "SetSpy: set of new addresses is: \n"); - for (i = 0; i < wrq->u.data.length; i++) - printk(KERN_DEBUG - "%02X:%02X:%02X:%02X:%02X:%02X \n", - lp->spy_address[i][0], - lp->spy_address[i][1], - lp->spy_address[i][2], - lp->spy_address[i][3], - lp->spy_address[i][4], - lp->spy_address[i][5]); -#endif /* DEBUG_IOCTL_INFO */ - } + "%02X:%02X:%02X:%02X:%02X:%02X \n", + lp->spy_address[i][0], + lp->spy_address[i][1], + lp->spy_address[i][2], + lp->spy_address[i][3], + lp->spy_address[i][4], + lp->spy_address[i][5]); +#endif /* DEBUG_IOCTL_INFO */ + } + + /* Now we can set the number of addresses */ + lp->spy_number = wrqu->data.length; + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get spy list + */ +static int wavelan_get_spy(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + struct sockaddr *address = (struct sockaddr *) extra; + int i; - break; + /* Set the number of addresses */ + wrqu->data.length = lp->spy_number; - case SIOCGIWSPY: - /* Get the spy list and spy stats. */ + /* Copy addresses from the lp structure. */ + for (i = 0; i < lp->spy_number; i++) { + memcpy(address[i].sa_data, + lp->spy_address[i], + WAVELAN_ADDR_SIZE); + address[i].sa_family = AF_UNIX; + } + /* Copy stats to the user buffer (just after). */ + if(lp->spy_number > 0) + memcpy(extra + (sizeof(struct sockaddr) * lp->spy_number), + lp->spy_stat, sizeof(iw_qual) * lp->spy_number); - /* Set the number of addresses */ - wrq->u.data.length = lp->spy_number; + /* Reset updated flags. */ + for (i = 0; i < lp->spy_number; i++) + lp->spy_stat[i].updated = 0x0; - /* Does the user want to have the addresses back? */ - if ((lp->spy_number > 0) - && (wrq->u.data.pointer != (caddr_t) 0)) { - struct sockaddr address[IW_MAX_SPY]; - int i; - - /* Copy addresses from the lp structure. */ - for (i = 0; i < lp->spy_number; i++) { - memcpy(address[i].sa_data, - lp->spy_address[i], - WAVELAN_ADDR_SIZE); - address[i].sa_family = AF_UNIX; - } + return(0); +} +#endif /* WIRELESS_SPY */ - /* Copy addresses to the user buffer. */ - wv_splx(lp, &flags); - err = copy_to_user(wrq->u.data.pointer, - address, - sizeof(struct sockaddr) - * lp->spy_number); - - /* Copy stats to the user buffer (just after). */ - err |= copy_to_user(wrq->u.data.pointer - + (sizeof(struct sockaddr) - * lp->spy_number), - lp->spy_stat, - sizeof(iw_qual) * lp->spy_number); - wv_splhi(lp, &flags); - if (err) { - ret = -EFAULT; - break; - } +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : set quality threshold + */ +static int wavelan_set_qthr(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + unsigned long flags; - /* Reset updated flags. */ - for (i = 0; i < lp->spy_number; i++) - lp->spy_stat[i].updated = 0x0; - } - /* if(pointer != NULL) */ - break; -#endif /* WIRELESS_SPY */ + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + psa.psa_quality_thr = *(extra) & 0x0F; + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_quality_thr - (char *) &psa, + (unsigned char *) &psa.psa_quality_thr, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); + mmc_out(ioaddr, mmwoff(0, mmw_quality_thr), + psa.psa_quality_thr); - /* ------------------ PRIVATE IOCTL ------------------ */ + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); - case SIOCSIPQTHR: - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - psa.psa_quality_thr = *(wrq->u.name) & 0x0F; - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_quality_thr - (char *) &psa, - (unsigned char *) &psa.psa_quality_thr, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, lp->hacr); - mmc_out(ioaddr, mmwoff(0, mmw_quality_thr), - psa.psa_quality_thr); - break; + return 0; +} - case SIOCGIPQTHR: - psa_read(ioaddr, lp->hacr, - (char *) &psa.psa_quality_thr - (char *) &psa, - (unsigned char *) &psa.psa_quality_thr, 1); - *(wrq->u.name) = psa.psa_quality_thr & 0x0F; - break; +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : get quality threshold + */ +static int wavelan_get_qthr(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + unsigned long flags; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + psa_read(ioaddr, lp->hacr, + (char *) &psa.psa_quality_thr - (char *) &psa, + (unsigned char *) &psa.psa_quality_thr, 1); + *(extra) = psa.psa_quality_thr & 0x0F; + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return 0; +} #ifdef HISTOGRAM - case SIOCSIPHISTO: - /* Verify that the user is root. */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : set histogram + */ +static int wavelan_set_histo(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ - /* Check the number of intervals. */ - if (wrq->u.data.length > 16) { - ret = -E2BIG; - break; - } - lp->his_number = wrq->u.data.length; + /* Check the number of intervals. */ + if (wrqu->data.length > 16) { + return(-E2BIG); + } - /* Are there addresses to copy? */ - if (lp->his_number > 0) { - /* Copy interval ranges to the driver */ - wv_splx(lp, &flags); - err = copy_from_user(lp->his_range, - wrq->u.data.pointer, - sizeof(char) * lp->his_number); - wv_splhi(lp, &flags); - if (err) { - ret = -EFAULT; - break; - } + /* Disable histo while we copy the addresses. + * As we don't disable interrupts, we need to do this */ + lp->his_number = 0; + + /* Are there ranges to copy? */ + if (wrqu->data.length > 0) { + /* Copy interval ranges to the driver */ + memcpy(lp->his_range, extra, wrqu->data.length); - /* Reset structure. */ - memset(lp->his_sum, 0x00, sizeof(long) * 16); + { + int i; + printk(KERN_DEBUG "Histo :"); + for(i = 0; i < wrqu->data.length; i++) + printk(" %d", lp->his_range[i]); + printk("\n"); } - break; - case SIOCGIPHISTO: - /* Set the number of intervals. */ - wrq->u.data.length = lp->his_number; - - /* Give back the distribution statistics */ - if ((lp->his_number > 0) - && (wrq->u.data.pointer != (caddr_t) 0)) { - /* Copy data to the user buffer. */ - wv_splx(lp, &flags); - if (copy_to_user(wrq->u.data.pointer, - lp->his_sum, - sizeof(long) * lp->his_number); - ret = -EFAULT; - wv_splhi(lp, &flags); + /* Reset result structure. */ + memset(lp->his_sum, 0x00, sizeof(long) * 16); + } - } /* if(pointer != NULL) */ - break; -#endif /* HISTOGRAM */ + /* Now we can set the number of ranges */ + lp->his_number = wrqu->data.length; + + return(0); +} - /* ------------------- OTHER IOCTL ------------------- */ +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : get histogram + */ +static int wavelan_get_histo(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ - default: - ret = -EOPNOTSUPP; - } /* switch (cmd) */ + /* Set the number of intervals. */ + wrqu->data.length = lp->his_number; - /* Enable interrupts and restore flags. */ - wv_splx(lp, &flags); + /* Give back the distribution statistics */ + if(lp->his_number > 0) + memcpy(extra, lp->his_sum, sizeof(long) * lp->his_number); -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name); -#endif - return ret; + return(0); } +#endif /* HISTOGRAM */ + +/*------------------------------------------------------------------*/ +/* + * Structures to export the Wireless Handlers + */ + +static const iw_handler wavelan_handler[] = +{ + NULL, /* SIOCSIWNAME */ + wavelan_get_name, /* SIOCGIWNAME */ + wavelan_set_nwid, /* SIOCSIWNWID */ + wavelan_get_nwid, /* SIOCGIWNWID */ + wavelan_set_freq, /* SIOCSIWFREQ */ + wavelan_get_freq, /* SIOCGIWFREQ */ + NULL, /* SIOCSIWMODE */ + NULL, /* SIOCGIWMODE */ + wavelan_set_sens, /* SIOCSIWSENS */ + wavelan_get_sens, /* SIOCGIWSENS */ + NULL, /* SIOCSIWRANGE */ + wavelan_get_range, /* SIOCGIWRANGE */ + NULL, /* SIOCSIWPRIV */ + NULL, /* SIOCGIWPRIV */ + NULL, /* SIOCSIWSTATS */ + NULL, /* SIOCGIWSTATS */ +#ifdef WIRELESS_SPY + wavelan_set_spy, /* SIOCSIWSPY */ + wavelan_get_spy, /* SIOCGIWSPY */ +#else /* WIRELESS_SPY */ + NULL, /* SIOCSIWSPY */ + NULL, /* SIOCGIWSPY */ +#endif /* WIRELESS_SPY */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWAP */ + NULL, /* SIOCGIWAP */ + NULL, /* -- hole -- */ + NULL, /* SIOCGIWAPLIST */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWESSID */ + NULL, /* SIOCGIWESSID */ + NULL, /* SIOCSIWNICKN */ + NULL, /* SIOCGIWNICKN */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWRATE */ + NULL, /* SIOCGIWRATE */ + NULL, /* SIOCSIWRTS */ + NULL, /* SIOCGIWRTS */ + NULL, /* SIOCSIWFRAG */ + NULL, /* SIOCGIWFRAG */ + NULL, /* SIOCSIWTXPOW */ + NULL, /* SIOCGIWTXPOW */ + NULL, /* SIOCSIWRETRY */ + NULL, /* SIOCGIWRETRY */ + /* Bummer ! Why those are only at the end ??? */ + wavelan_set_encode, /* SIOCSIWENCODE */ + wavelan_get_encode, /* SIOCGIWENCODE */ +}; + +static const iw_handler wavelan_private_handler[] = +{ + wavelan_set_qthr, /* SIOCIWFIRSTPRIV */ + wavelan_get_qthr, /* SIOCIWFIRSTPRIV + 1 */ +#ifdef HISTOGRAM + wavelan_set_histo, /* SIOCIWFIRSTPRIV + 2 */ + wavelan_get_histo, /* SIOCIWFIRSTPRIV + 3 */ +#endif /* HISTOGRAM */ +}; + +static const struct iw_priv_args wavelan_private_args[] = { +/*{ cmd, set_args, get_args, name } */ + { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" }, + { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" }, + { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16, 0, "sethisto" }, + { SIOCGIPHISTO, 0, IW_PRIV_TYPE_INT | 16, "gethisto" }, +}; + +static const struct iw_handler_def wavelan_handler_def = +{ + num_standard: sizeof(wavelan_handler)/sizeof(iw_handler), + num_private: sizeof(wavelan_private_handler)/sizeof(iw_handler), + num_private_args: sizeof(wavelan_private_args)/sizeof(struct iw_priv_args), + standard: (iw_handler *) wavelan_handler, + private: (iw_handler *) wavelan_private_handler, + private_args: (struct iw_priv_args *) wavelan_private_args, +}; /*------------------------------------------------------------------*/ /* @@ -2343,7 +2519,7 @@ return (iw_stats *) NULL; /* Disable interrupts and save flags. */ - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); wstats = &lp->wstats; @@ -2371,7 +2547,7 @@ wstats->discard.misc = 0L; /* Enable interrupts and restore flags. */ - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); #ifdef DEBUG_IOCTL_TRACE printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", @@ -2705,7 +2881,7 @@ if (clen < ETH_ZLEN) clen = ETH_ZLEN; - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); /* Check nothing bad has happened */ if (lp->tx_n_in_use == (NTXBLOCKS - 1)) { @@ -2713,7 +2889,7 @@ printk(KERN_INFO "%s: wv_packet_write(): Tx queue full.\n", dev->name); #endif - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); return 1; } @@ -2791,7 +2967,7 @@ if (lp->tx_n_in_use < NTXBLOCKS - 1) netif_wake_queue(dev); - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); #ifdef DEBUG_TX_INFO wv_packet_info((u8 *) buf, length, dev->name, @@ -2832,9 +3008,9 @@ * we can do it now. */ if (lp->reconfig_82586) { - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); wv_82586_config(dev); - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); /* Check that we can continue */ if (lp->tx_n_in_use == (NTXBLOCKS - 1)) return 1; @@ -3694,7 +3870,7 @@ /* Prevent reentrancy. We need to do that because we may have * multiple interrupt handler running concurrently. - * It is safe because wv_splhi() disables interrupts before acquiring + * It is safe because interrupts are disabled before acquiring * the spinlock. */ spin_lock(&lp->spinlock); @@ -3822,7 +3998,7 @@ return; } - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); /* Try to see if some buffers are not free (in case we missed * an interrupt */ @@ -3862,7 +4038,7 @@ if (lp->tx_n_in_use < NTXBLOCKS - 1) netif_wake_queue(dev); - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); #ifdef DEBUG_INTERRUPT_TRACE printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name); @@ -3909,7 +4085,7 @@ return -EAGAIN; } - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); if (wv_hw_reset(dev) != -1) { netif_start_queue(dev); @@ -3920,10 +4096,10 @@ "%s: wavelan_open(): impossible to start the card\n", dev->name); #endif - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); return -EAGAIN; } - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); #ifdef DEBUG_CALLBACK_TRACE printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name); @@ -3951,9 +4127,9 @@ /* * Flush the Tx and disable Rx. */ - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); wv_82586_stop(dev); - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); free_irq(dev->irq, dev); @@ -4069,8 +4245,8 @@ #endif /* SET_MAC_ADDRESS */ #ifdef WIRELESS_EXT /* if wireless extension exists in the kernel */ - dev->do_ioctl = wavelan_ioctl; dev->get_wireless_stats = wavelan_get_wireless_stats; + dev->wireless_handlers = (struct iw_handler_def *)&wavelan_handler_def; #endif dev->mtu = WAVELAN_MTU; diff -Nru a/drivers/net/wireless/wavelan.p.h b/drivers/net/wireless/wavelan.p.h --- a/drivers/net/wireless/wavelan.p.h Thu Mar 7 18:17:45 2002 +++ b/drivers/net/wireless/wavelan.p.h Thu Mar 7 18:17:45 2002 @@ -345,6 +345,12 @@ * - Fix spinlock stupid bugs that I left in. The driver is now SMP * compliant and doesn't lockup at startup. * + * Changes made for release in 2.5.2 : + * --------------------------------- + * - Use new driver API for Wireless Extensions : + * o got rid of wavelan_ioctl() + * o use a bunch of iw_handler instead + * * Wishes & dreams: * ---------------- * - roaming (see Pcmcia driver) @@ -379,6 +385,7 @@ #include #include /* Wireless extensions */ +#include /* Wireless handlers */ /* WaveLAN declarations */ #include "i82586.h" @@ -436,7 +443,7 @@ /************************ CONSTANTS & MACROS ************************/ #ifdef DEBUG_VERSION_SHOW -static const char *version = "wavelan.c : v23 (SMP + wireless extensions) 05/10/00\n"; +static const char *version = "wavelan.c : v24 (SMP + wireless extensions) 11/12/01\n"; #endif /* Watchdog temporisation */ @@ -449,11 +456,9 @@ #define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */ #define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */ -#define SIOCSIPLTHR SIOCIWFIRSTPRIV + 2 /* Set level threshold */ -#define SIOCGIPLTHR SIOCIWFIRSTPRIV + 3 /* Get level threshold */ -#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 6 /* Set histogram ranges */ -#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 7 /* Get histogram values */ +#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 2 /* Set histogram ranges */ +#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 3 /* Get histogram values */ /****************************** TYPES ******************************/ @@ -516,12 +521,6 @@ /**************************** PROTOTYPES ****************************/ /* ----------------------- MISC. SUBROUTINES ------------------------ */ -static inline void - wv_splhi(net_local *, /* Disable interrupts, lock driver */ - unsigned long *); /* flags */ -static inline void - wv_splx(net_local *, /* Enable interrupts, unlock driver */ - unsigned long *); /* flags */ static u_char wv_irq_to_psa(int); static int diff -Nru a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c --- a/drivers/net/wireless/wavelan_cs.c Thu Mar 7 18:17:45 2002 +++ b/drivers/net/wireless/wavelan_cs.c Thu Mar 7 18:17:45 2002 @@ -66,34 +66,6 @@ /*------------------------------------------------------------------*/ /* - * Wrapper for disabling interrupts. - * (note : inline, so optimised away) - */ -static inline void -wv_splhi(net_local * lp, - unsigned long * pflags) -{ - spin_lock_irqsave(&lp->spinlock, *pflags); - /* Note : above does the cli(); itself */ -} - -/*------------------------------------------------------------------*/ -/* - * Wrapper for re-enabling interrupts. - */ -static inline void -wv_splx(net_local * lp, - unsigned long * pflags) -{ - spin_unlock_irqrestore(&lp->spinlock, *pflags); - - /* Note : enabling interrupts on the hardware is done in wv_ru_start() - * via : outb(OP1_INT_ENABLE, LCCR(base)); - */ -} - -/*------------------------------------------------------------------*/ -/* * Wrapper for reporting error to cardservices */ static void cs_error(client_handle_t handle, int func, int ret) @@ -591,7 +563,7 @@ #endif /* Disable interrupts & save flags */ - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); m.w.mmw_loopt_sel = (mode==NWID_PROMISC) ? MMW_LOOPT_SEL_DIS_NWID : 0x00; mmc_write(lp->dev->base_addr, (char *)&m.w.mmw_loopt_sel - (char *)&m, (unsigned char *)&m.w.mmw_loopt_sel, 1); @@ -602,7 +574,7 @@ lp->cell_search=0; /* ReEnable interrupts & restore flags */ - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); } /* Find a record in the WavePoint table matching a given NWID */ @@ -771,7 +743,7 @@ #endif /* Disable interrupts & save flags */ - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); m.w.mmw_netw_id_l = wavepoint->nwid & 0xFF; m.w.mmw_netw_id_h = (wavepoint->nwid & 0xFF00) >> 8; @@ -779,7 +751,7 @@ mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m, (unsigned char *)&m.w.mmw_netw_id_l, 2); /* ReEnable interrupts & restore flags */ - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); wv_nwid_filter(!NWID_PROMISC,lp); lp->curr_point=wavepoint; @@ -1049,9 +1021,9 @@ /* Check if we can do it now ! */ if((link->open) && (netif_running(dev)) && !(netif_queue_stopped(dev))) { - wv_splhi(lp, &flags); /* Disable interrupts */ + spin_lock_irqsave(&lp->spinlock, flags); /* Disable interrupts */ wv_82593_config(dev); - wv_splx(lp, &flags); /* Re-enable interrupts */ + spin_unlock_irqrestore(&lp->spinlock, flags); /* Re-enable interrupts */ } else { @@ -1179,7 +1151,7 @@ return; } - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); /* Read the mmc */ mmc_out(base, mmwoff(0, mmw_freeze), 1); @@ -1191,7 +1163,7 @@ lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l; #endif /* WIRELESS_EXT */ - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); printk(KERN_DEBUG "##### wavelan modem status registers: #####\n"); #ifdef DEBUG_SHOW_UNUSED @@ -1884,557 +1856,1234 @@ /*------------------------------------------------------------------*/ /* - * Perform ioctl : config & info stuff - * This is here that are treated the wireless extensions (iwconfig) + * Wireless Handler : get protocol name */ -static int -wavelan_ioctl(struct net_device * dev, /* Device on wich the ioctl apply */ - struct ifreq * rq, /* Data passed */ - int cmd) /* Ioctl number */ +static int wavelan_get_name(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) { - ioaddr_t base = dev->base_addr; - net_local * lp = (net_local *)dev->priv; /* lp is not unused */ - struct iwreq * wrq = (struct iwreq *) rq; - psa_t psa; - mm_t m; - unsigned long flags; - int ret = 0; - -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, cmd); -#endif - - /* Disable interrupts & save flags */ - wv_splhi(lp, &flags); - - /* Look what is the request */ - switch(cmd) - { - /* --------------- WIRELESS EXTENSIONS --------------- */ + strcpy(wrqu->name, "WaveLAN"); + return 0; +} - case SIOCGIWNAME: - strcpy(wrq->u.name, "Wavelan"); - break; +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set NWID + */ +static int wavelan_set_nwid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + ioaddr_t base = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + mm_t m; + unsigned long flags; + int ret = 0; - case SIOCSIWNWID: - /* Set NWID in wavelan */ + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Set NWID in WaveLAN. */ #if WIRELESS_EXT > 8 - if(!wrq->u.nwid.disabled) - { - /* Set NWID in psa */ - psa.psa_nwid[0] = (wrq->u.nwid.value & 0xFF00) >> 8; - psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF; + if (!wrqu->nwid.disabled) { + /* Set NWID in psa */ + psa.psa_nwid[0] = (wrqu->nwid.value & 0xFF00) >> 8; + psa.psa_nwid[1] = wrqu->nwid.value & 0xFF; #else /* WIRELESS_EXT > 8 */ - if(wrq->u.nwid.on) - { - /* Set NWID in psa */ - psa.psa_nwid[0] = (wrq->u.nwid.nwid & 0xFF00) >> 8; - psa.psa_nwid[1] = wrq->u.nwid.nwid & 0xFF; + if(wrq->u.nwid.on) { + /* Set NWID in psa */ + psa.psa_nwid[0] = (wrq->u.nwid.nwid & 0xFF00) >> 8; + psa.psa_nwid[1] = wrq->u.nwid.nwid & 0xFF; #endif /* WIRELESS_EXT > 8 */ - psa.psa_nwid_select = 0x01; - psa_write(dev, (char *)psa.psa_nwid - (char *)&psa, - (unsigned char *)psa.psa_nwid, 3); - - /* Set NWID in mmc */ - m.w.mmw_netw_id_l = psa.psa_nwid[1]; - m.w.mmw_netw_id_h = psa.psa_nwid[0]; - mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m, - (unsigned char *)&m.w.mmw_netw_id_l, 2); - mmc_out(base, mmwoff(0, mmw_loopt_sel), 0x00); - } - else - { - /* Disable nwid in the psa */ - psa.psa_nwid_select = 0x00; - psa_write(dev, (char *)&psa.psa_nwid_select - (char *)&psa, - (unsigned char *)&psa.psa_nwid_select, 1); - - /* Disable nwid in the mmc (no filtering) */ - mmc_out(base, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID); - } - /* update the Wavelan checksum */ - update_psa_checksum(dev); - break; + psa.psa_nwid_select = 0x01; + psa_write(dev, + (char *) psa.psa_nwid - (char *) &psa, + (unsigned char *) psa.psa_nwid, 3); + + /* Set NWID in mmc. */ + m.w.mmw_netw_id_l = psa.psa_nwid[1]; + m.w.mmw_netw_id_h = psa.psa_nwid[0]; + mmc_write(base, + (char *) &m.w.mmw_netw_id_l - + (char *) &m, + (unsigned char *) &m.w.mmw_netw_id_l, 2); + mmc_out(base, mmwoff(0, mmw_loopt_sel), 0x00); + } else { + /* Disable NWID in the psa. */ + psa.psa_nwid_select = 0x00; + psa_write(dev, + (char *) &psa.psa_nwid_select - + (char *) &psa, + (unsigned char *) &psa.psa_nwid_select, + 1); + + /* Disable NWID in the mmc (no filtering). */ + mmc_out(base, mmwoff(0, mmw_loopt_sel), + MMW_LOOPT_SEL_DIS_NWID); + } + /* update the Wavelan checksum */ + update_psa_checksum(dev); + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} - case SIOCGIWNWID: - /* Read the NWID */ - psa_read(dev, (char *)psa.psa_nwid - (char *)&psa, - (unsigned char *)psa.psa_nwid, 3); +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get NWID + */ +static int wavelan_get_nwid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Read the NWID. */ + psa_read(dev, + (char *) psa.psa_nwid - (char *) &psa, + (unsigned char *) psa.psa_nwid, 3); #if WIRELESS_EXT > 8 - wrq->u.nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; - wrq->u.nwid.disabled = !(psa.psa_nwid_select); - wrq->u.nwid.fixed = 1; /* Superfluous */ + wrqu->nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; + wrqu->nwid.disabled = !(psa.psa_nwid_select); + wrqu->nwid.fixed = 1; /* Superfluous */ #else /* WIRELESS_EXT > 8 */ - wrq->u.nwid.nwid = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; - wrq->u.nwid.on = psa.psa_nwid_select; + wrq->u.nwid.nwid = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; + wrq->u.nwid.on = psa.psa_nwid_select; #endif /* WIRELESS_EXT > 8 */ - break; - case SIOCSIWFREQ: - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */ - if(!(mmc_in(base, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) - ret = wv_set_frequency(base, &(wrq->u.freq)); - else - ret = -EOPNOTSUPP; - break; + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); - case SIOCGIWFREQ: - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) - * (does it work for everybody ? - especially old cards...) */ - if(!(mmc_in(base, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) - { - unsigned short freq; + return ret; +} - /* Ask the EEprom to read the frequency from the first area */ - fee_read(base, 0x00 /* 1st area - frequency... */, - &freq, 1); - wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000; - wrq->u.freq.e = 1; - } - else - { - psa_read(dev, (char *)&psa.psa_subband - (char *)&psa, - (unsigned char *)&psa.psa_subband, 1); +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set frequency + */ +static int wavelan_set_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + ioaddr_t base = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + unsigned long flags; + int ret; - if(psa.psa_subband <= 4) - { - wrq->u.freq.m = fixed_bands[psa.psa_subband]; - wrq->u.freq.e = (psa.psa_subband != 0); - } - else - ret = -EOPNOTSUPP; + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ + if (!(mmc_in(base, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) + ret = wv_set_frequency(base, &(wrqu->freq)); + else + ret = -EOPNOTSUPP; + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get frequency + */ +static int wavelan_get_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + ioaddr_t base = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). + * Does it work for everybody, especially old cards? */ + if (!(mmc_in(base, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { + unsigned short freq; + + /* Ask the EEPROM to read the frequency from the first area. */ + fee_read(base, 0x00, &freq, 1); + wrqu->freq.m = ((freq >> 5) * 5 + 24000L) * 10000; + wrqu->freq.e = 1; + } else { + psa_read(dev, + (char *) &psa.psa_subband - (char *) &psa, + (unsigned char *) &psa.psa_subband, 1); + + if (psa.psa_subband <= 4) { + wrqu->freq.m = fixed_bands[psa.psa_subband]; + wrqu->freq.e = (psa.psa_subband != 0); + } else + ret = -EOPNOTSUPP; } - break; - case SIOCSIWSENS: - /* Set the level threshold */ + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set level threshold + */ +static int wavelan_set_sens(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + ioaddr_t base = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Set the level threshold. */ #if WIRELESS_EXT > 7 - /* We should complain loudly if wrq->u.sens.fixed = 0, because we - * can't set auto mode... */ - psa.psa_thr_pre_set = wrq->u.sens.value & 0x3F; + /* We should complain loudly if wrqu->sens.fixed = 0, because we + * can't set auto mode... */ + psa.psa_thr_pre_set = wrqu->sens.value & 0x3F; #else /* WIRELESS_EXT > 7 */ - psa.psa_thr_pre_set = wrq->u.sensitivity & 0x3F; + psa.psa_thr_pre_set = wrq->u.sensitivity & 0x3F; #endif /* WIRELESS_EXT > 7 */ - psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa, - (unsigned char *)&psa.psa_thr_pre_set, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev); - mmc_out(base, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set); - break; + psa_write(dev, + (char *) &psa.psa_thr_pre_set - (char *) &psa, + (unsigned char *) &psa.psa_thr_pre_set, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev); + mmc_out(base, mmwoff(0, mmw_thr_pre_set), + psa.psa_thr_pre_set); - case SIOCGIWSENS: - /* Read the level threshold */ - psa_read(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa, - (unsigned char *)&psa.psa_thr_pre_set, 1); + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get level threshold + */ +static int wavelan_get_sens(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Read the level threshold. */ + psa_read(dev, + (char *) &psa.psa_thr_pre_set - (char *) &psa, + (unsigned char *) &psa.psa_thr_pre_set, 1); #if WIRELESS_EXT > 7 - wrq->u.sens.value = psa.psa_thr_pre_set & 0x3F; - wrq->u.sens.fixed = 1; + wrqu->sens.value = psa.psa_thr_pre_set & 0x3F; + wrqu->sens.fixed = 1; #else /* WIRELESS_EXT > 7 */ - wrq->u.sensitivity = psa.psa_thr_pre_set & 0x3F; + wrq->u.sensitivity = psa.psa_thr_pre_set & 0x3F; #endif /* WIRELESS_EXT > 7 */ - break; -#if WIRELESS_EXT > 8 - case SIOCSIWENCODE: - /* Set encryption key */ - if(!mmc_encr(base)) - { - ret = -EOPNOTSUPP; - break; - } + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); - /* Basic checking... */ - if(wrq->u.encoding.pointer != (caddr_t) 0) - { - /* Check the size of the key */ - if(wrq->u.encoding.length != 8) - { - ret = -EINVAL; - break; - } + return ret; +} - /* Copy the key in the driver */ - if(copy_from_user(psa.psa_encryption_key, wrq->u.encoding.pointer, - wrq->u.encoding.length)) - { - ret = -EFAULT; - break; - } +#if WIRELESS_EXT > 8 +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set encryption key + */ +static int wavelan_set_encode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + ioaddr_t base = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + unsigned long flags; + psa_t psa; + int ret = 0; - psa.psa_encryption_select = 1; - psa_write(dev, (char *) &psa.psa_encryption_select - (char *) &psa, - (unsigned char *) &psa.psa_encryption_select, 8+1); - - mmc_out(base, mmwoff(0, mmw_encr_enable), - MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE); - mmc_write(base, mmwoff(0, mmw_encr_key), - (unsigned char *) &psa.psa_encryption_key, 8); - } - - if(wrq->u.encoding.flags & IW_ENCODE_DISABLED) - { /* disable encryption */ - psa.psa_encryption_select = 0; - psa_write(dev, (char *) &psa.psa_encryption_select - (char *) &psa, - (unsigned char *) &psa.psa_encryption_select, 1); + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); - mmc_out(base, mmwoff(0, mmw_encr_enable), 0); + /* Check if capable of encryption */ + if (!mmc_encr(base)) { + ret = -EOPNOTSUPP; } - /* update the Wavelan checksum */ - update_psa_checksum(dev); - break; - case SIOCGIWENCODE: - /* Read the encryption key */ - if(!mmc_encr(base)) - { - ret = -EOPNOTSUPP; - break; + /* Check the size of the key */ + if((wrqu->encoding.length != 8) && (wrqu->encoding.length != 0)) { + ret = -EINVAL; } - /* only super-user can see encryption key */ - if(!capable(CAP_NET_ADMIN)) - { - ret = -EPERM; - break; + if(!ret) { + /* Basic checking... */ + if (wrqu->encoding.length == 8) { + /* Copy the key in the driver */ + memcpy(psa.psa_encryption_key, extra, + wrqu->encoding.length); + psa.psa_encryption_select = 1; + + psa_write(dev, + (char *) &psa.psa_encryption_select - + (char *) &psa, + (unsigned char *) &psa. + psa_encryption_select, 8 + 1); + + mmc_out(base, mmwoff(0, mmw_encr_enable), + MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE); + mmc_write(base, mmwoff(0, mmw_encr_key), + (unsigned char *) &psa. + psa_encryption_key, 8); + } + + /* disable encryption */ + if (wrqu->encoding.flags & IW_ENCODE_DISABLED) { + psa.psa_encryption_select = 0; + psa_write(dev, + (char *) &psa.psa_encryption_select - + (char *) &psa, + (unsigned char *) &psa. + psa_encryption_select, 1); + + mmc_out(base, mmwoff(0, mmw_encr_enable), 0); + } + /* update the Wavelan checksum */ + update_psa_checksum(dev); } - /* Basic checking... */ - if(wrq->u.encoding.pointer != (caddr_t) 0) - { - psa_read(dev, (char *) &psa.psa_encryption_select - (char *) &psa, - (unsigned char *) &psa.psa_encryption_select, 1+8); + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); - /* encryption is enabled ? */ - if(psa.psa_encryption_select) - wrq->u.encoding.flags = IW_ENCODE_ENABLED; - else - wrq->u.encoding.flags = IW_ENCODE_DISABLED; - wrq->u.encoding.flags |= mmc_encr(base); + return ret; +} - /* Copy the key to the user buffer */ - wrq->u.encoding.length = 8; - if(copy_to_user(wrq->u.encoding.pointer, psa.psa_encryption_key, 8)) - ret = -EFAULT; +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get encryption key + */ +static int wavelan_get_encode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + ioaddr_t base = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Check if encryption is available */ + if (!mmc_encr(base)) { + ret = -EOPNOTSUPP; + } else { + /* Read the encryption key */ + psa_read(dev, + (char *) &psa.psa_encryption_select - + (char *) &psa, + (unsigned char *) &psa. + psa_encryption_select, 1 + 8); + + /* encryption is enabled ? */ + if (psa.psa_encryption_select) + wrqu->encoding.flags = IW_ENCODE_ENABLED; + else + wrqu->encoding.flags = IW_ENCODE_DISABLED; + wrqu->encoding.flags |= mmc_encr(base); + + /* Copy the key to the user buffer */ + wrqu->encoding.length = 8; + memcpy(extra, psa.psa_encryption_key, wrqu->encoding.length); } - break; + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} #endif /* WIRELESS_EXT > 8 */ #ifdef WAVELAN_ROAMING_EXT #if WIRELESS_EXT > 5 - case SIOCSIWESSID: - /* Check if disable */ - if(wrq->u.data.flags == 0) - lp->filter_domains = 0; - else - /* Basic checking... */ - if(wrq->u.data.pointer != (caddr_t) 0) - { - char essid[IW_ESSID_MAX_SIZE + 1]; - char * endp; - - /* Check the size of the string */ - if(wrq->u.data.length > IW_ESSID_MAX_SIZE + 1) - { - ret = -E2BIG; - break; - } - - /* Copy the string in the driver */ - if(copy_from_user(essid, wrq->u.data.pointer, wrq->u.data.length)) - { - ret = -EFAULT; - break; - } - essid[IW_ESSID_MAX_SIZE] = '\0'; +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set ESSID (domain) + */ +static int wavelan_set_essid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Check if disable */ + if(wrqu->data.flags == 0) + lp->filter_domains = 0; + else { + char essid[IW_ESSID_MAX_SIZE + 1]; + char * endp; + + /* Terminate the string */ + memcpy(essid, extra, wrqu->data.length); + essid[IW_ESSID_MAX_SIZE] = '\0'; #ifdef DEBUG_IOCTL_INFO - printk(KERN_DEBUG "SetEssid : ``%s''\n", essid); + printk(KERN_DEBUG "SetEssid : ``%s''\n", essid); #endif /* DEBUG_IOCTL_INFO */ - /* Convert to a number (note : Wavelan specific) */ - lp->domain_id = simple_strtoul(essid, &endp, 16); - /* Has it worked ? */ - if(endp > essid) - lp->filter_domains = 1; - else - { - lp->filter_domains = 0; - ret = -EINVAL; - } - } - break; + /* Convert to a number (note : Wavelan specific) */ + lp->domain_id = simple_strtoul(essid, &endp, 16); + /* Has it worked ? */ + if(endp > essid) + lp->filter_domains = 1; + else { + lp->filter_domains = 0; + ret = -EINVAL; + } + } - case SIOCGIWESSID: - /* Basic checking... */ - if(wrq->u.data.pointer != (caddr_t) 0) - { - char essid[IW_ESSID_MAX_SIZE + 1]; + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); - /* Is the domain ID active ? */ - wrq->u.data.flags = lp->filter_domains; + return ret; +} - /* Copy Domain ID into a string (Wavelan specific) */ - /* Sound crazy, be we can't have a snprintf in the kernel !!! */ - sprintf(essid, "%lX", lp->domain_id); - essid[IW_ESSID_MAX_SIZE] = '\0'; +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get ESSID (domain) + */ +static int wavelan_get_essid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ - /* Set the length */ - wrq->u.data.length = strlen(essid) + 1; + /* Is the domain ID active ? */ + wrqu->data.flags = lp->filter_domains; - /* Copy structure to the user buffer */ - if(copy_to_user(wrq->u.data.pointer, essid, wrq->u.data.length)) - ret = -EFAULT; - } - break; + /* Copy Domain ID into a string (Wavelan specific) */ + /* Sound crazy, be we can't have a snprintf in the kernel !!! */ + sprintf(extra, "%lX", lp->domain_id); + extra[IW_ESSID_MAX_SIZE] = '\0'; - case SIOCSIWAP: + /* Set the length */ + wrqu->data.length = strlen(extra) + 1; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set AP address + */ +static int wavelan_set_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ #ifdef DEBUG_IOCTL_INFO - printk(KERN_DEBUG "Set AP to : %02X:%02X:%02X:%02X:%02X:%02X\n", - wrq->u.ap_addr.sa_data[0], - wrq->u.ap_addr.sa_data[1], - wrq->u.ap_addr.sa_data[2], - wrq->u.ap_addr.sa_data[3], - wrq->u.ap_addr.sa_data[4], - wrq->u.ap_addr.sa_data[5]); + printk(KERN_DEBUG "Set AP to : %02X:%02X:%02X:%02X:%02X:%02X\n", + wrqu->ap_addr.sa_data[0], + wrqu->ap_addr.sa_data[1], + wrqu->ap_addr.sa_data[2], + wrqu->ap_addr.sa_data[3], + wrqu->ap_addr.sa_data[4], + wrqu->ap_addr.sa_data[5]); #endif /* DEBUG_IOCTL_INFO */ - ret = -EOPNOTSUPP; /* Not supported yet */ - break; + return -EOPNOTSUPP; +} - case SIOCGIWAP: - /* Should get the real McCoy instead of own Ethernet address */ - memcpy(wrq->u.ap_addr.sa_data, dev->dev_addr, WAVELAN_ADDR_SIZE); - wrq->u.ap_addr.sa_family = ARPHRD_ETHER; +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get AP address + */ +static int wavelan_get_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + /* Should get the real McCoy instead of own Ethernet address */ + memcpy(wrqu->ap_addr.sa_data, dev->dev_addr, WAVELAN_ADDR_SIZE); + wrqu->ap_addr.sa_family = ARPHRD_ETHER; - ret = -EOPNOTSUPP; /* Not supported yet */ - break; + return -EOPNOTSUPP; +} #endif /* WIRELESS_EXT > 5 */ #endif /* WAVELAN_ROAMING_EXT */ #if WIRELESS_EXT > 8 #ifdef WAVELAN_ROAMING - case SIOCSIWMODE: - switch(wrq->u.mode) - { +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set mode + */ +static int wavelan_set_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Check mode */ + switch(wrqu->mode) { case IW_MODE_ADHOC: - if(do_roaming) - { - wv_roam_cleanup(dev); - do_roaming = 0; - } - break; + if(do_roaming) { + wv_roam_cleanup(dev); + do_roaming = 0; + } + break; case IW_MODE_INFRA: - if(!do_roaming) - { - wv_roam_init(dev); - do_roaming = 1; - } - break; + if(!do_roaming) { + wv_roam_init(dev); + do_roaming = 1; + } + break; default: - ret = -EINVAL; + ret = -EINVAL; } - break; - case SIOCGIWMODE: - if(do_roaming) - wrq->u.mode = IW_MODE_INFRA; - else - wrq->u.mode = IW_MODE_ADHOC; - break; + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get mode + */ +static int wavelan_get_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + if(do_roaming) + wrqu->mode = IW_MODE_INFRA; + else + wrqu->mode = IW_MODE_ADHOC; + + return 0; +} #endif /* WAVELAN_ROAMING */ #endif /* WIRELESS_EXT > 8 */ - case SIOCGIWRANGE: - /* Basic checking... */ - if(wrq->u.data.pointer != (caddr_t) 0) - { - struct iw_range range; +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get range info + */ +static int wavelan_get_range(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + ioaddr_t base = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + struct iw_range *range = (struct iw_range *) extra; + unsigned long flags; + int ret = 0; - /* Set the length (very important for backward compatibility) */ - wrq->u.data.length = sizeof(struct iw_range); + /* Set the length (very important for backward compatibility) */ + wrqu->data.length = sizeof(struct iw_range); - /* Set all the info we don't care or don't know about to zero */ - memset(&range, 0, sizeof(range)); + /* Set all the info we don't care or don't know about to zero */ + memset(range, 0, sizeof(struct iw_range)); #if WIRELESS_EXT > 10 - /* Set the Wireless Extension versions */ - range.we_version_compiled = WIRELESS_EXT; - range.we_version_source = 9; /* Nothing for us in v10 and v11 */ + /* Set the Wireless Extension versions */ + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 9; #endif /* WIRELESS_EXT > 10 */ - /* Set information in the range struct */ - range.throughput = 1.4 * 1000 * 1000; /* don't argue on this ! */ - range.min_nwid = 0x0000; - range.max_nwid = 0xFFFF; - - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */ - if(!(mmc_in(base, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) - { - range.num_channels = 10; - range.num_frequency = wv_frequency_list(base, range.freq, - IW_MAX_FREQUENCIES); - } - else - range.num_channels = range.num_frequency = 0; - - range.sensitivity = 0x3F; - range.max_qual.qual = MMR_SGNL_QUAL; - range.max_qual.level = MMR_SIGNAL_LVL; - range.max_qual.noise = MMR_SILENCE_LVL; + /* Set information in the range struct. */ + range->throughput = 1.4 * 1000 * 1000; /* don't argue on this ! */ + range->min_nwid = 0x0000; + range->max_nwid = 0xFFFF; + + range->sensitivity = 0x3F; + range->max_qual.qual = MMR_SGNL_QUAL; + range->max_qual.level = MMR_SIGNAL_LVL; + range->max_qual.noise = MMR_SILENCE_LVL; #if WIRELESS_EXT > 11 - range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */ - /* Need to get better values for those two */ - range.avg_qual.level = 30; - range.avg_qual.noise = 8; + range->avg_qual.qual = MMR_SGNL_QUAL; /* Always max */ + /* Need to get better values for those two */ + range->avg_qual.level = 30; + range->avg_qual.noise = 8; #endif /* WIRELESS_EXT > 11 */ #if WIRELESS_EXT > 7 - range.num_bitrates = 1; - range.bitrate[0] = 2000000; /* 2 Mb/s */ + range->num_bitrates = 1; + range->bitrate[0] = 2000000; /* 2 Mb/s */ #endif /* WIRELESS_EXT > 7 */ + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ + if (!(mmc_in(base, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { + range->num_channels = 10; + range->num_frequency = wv_frequency_list(base, range->freq, + IW_MAX_FREQUENCIES); + } else + range->num_channels = range->num_frequency = 0; + #if WIRELESS_EXT > 8 - /* Encryption supported ? */ - if(mmc_encr(base)) - { - range.encoding_size[0] = 8; /* DES = 64 bits key */ - range.num_encoding_sizes = 1; - range.max_encoding_tokens = 1; /* Only one key possible */ - } - else - { - range.num_encoding_sizes = 0; - range.max_encoding_tokens = 0; - } + /* Encryption supported ? */ + if (mmc_encr(base)) { + range->encoding_size[0] = 8; /* DES = 64 bits key */ + range->num_encoding_sizes = 1; + range->max_encoding_tokens = 1; /* Only one key possible */ + } else { + range->num_encoding_sizes = 0; + range->max_encoding_tokens = 0; + } #endif /* WIRELESS_EXT > 8 */ - /* Copy structure to the user buffer */ - if(copy_to_user(wrq->u.data.pointer, &range, - sizeof(struct iw_range))) - ret = -EFAULT; + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +#ifdef WIRELESS_SPY +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set spy list + */ +static int wavelan_set_spy(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + struct sockaddr *address = (struct sockaddr *) extra; + int i; + int ret = 0; + + /* Disable spy while we copy the addresses. + * As we don't disable interrupts, we need to do this */ + lp->spy_number = 0; + + /* Are there are addresses to copy? */ + if (wrqu->data.length > 0) { + /* Copy addresses to the lp structure. */ + for (i = 0; i < wrqu->data.length; i++) { + memcpy(lp->spy_address[i], address[i].sa_data, + WAVELAN_ADDR_SIZE); + } + + /* Reset structure. */ + memset(lp->spy_stat, 0x00, sizeof(iw_qual) * IW_MAX_SPY); + +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG + "SetSpy: set of new addresses is: \n"); + for (i = 0; i < wrqu->data.length; i++) + printk(KERN_DEBUG + "%02X:%02X:%02X:%02X:%02X:%02X \n", + lp->spy_address[i][0], + lp->spy_address[i][1], + lp->spy_address[i][2], + lp->spy_address[i][3], + lp->spy_address[i][4], + lp->spy_address[i][5]); +#endif /* DEBUG_IOCTL_INFO */ + } + + /* Now we can set the number of addresses */ + lp->spy_number = wrqu->data.length; + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get spy list + */ +static int wavelan_get_spy(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + struct sockaddr *address = (struct sockaddr *) extra; + int i; + + /* Set the number of addresses */ + wrqu->data.length = lp->spy_number; + + /* Copy addresses from the lp structure. */ + for (i = 0; i < lp->spy_number; i++) { + memcpy(address[i].sa_data, + lp->spy_address[i], + WAVELAN_ADDR_SIZE); + address[i].sa_family = AF_UNIX; + } + /* Copy stats to the user buffer (just after). */ + if(lp->spy_number > 0) + memcpy(extra + (sizeof(struct sockaddr) * lp->spy_number), + lp->spy_stat, sizeof(iw_qual) * lp->spy_number); + + /* Reset updated flags. */ + for (i = 0; i < lp->spy_number; i++) + lp->spy_stat[i].updated = 0x0; + + return(0); +} +#endif /* WIRELESS_SPY */ + +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : set quality threshold + */ +static int wavelan_set_qthr(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + ioaddr_t base = dev->base_addr; + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + unsigned long flags; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + psa.psa_quality_thr = *(extra) & 0x0F; + psa_write(dev, + (char *) &psa.psa_quality_thr - (char *) &psa, + (unsigned char *) &psa.psa_quality_thr, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev); + mmc_out(base, mmwoff(0, mmw_quality_thr), + psa.psa_quality_thr); + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : get quality threshold + */ +static int wavelan_get_qthr(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + psa_t psa; + unsigned long flags; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + psa_read(dev, + (char *) &psa.psa_quality_thr - (char *) &psa, + (unsigned char *) &psa.psa_quality_thr, 1); + *(extra) = psa.psa_quality_thr & 0x0F; + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return 0; +} + +#ifdef WAVELAN_ROAMING +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : set roaming + */ +static int wavelan_set_roam(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + unsigned long flags; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Note : should check if user == root */ + if(do_roaming && (*extra)==0) + wv_roam_cleanup(dev); + else if(do_roaming==0 && (*extra)!=0) + wv_roam_init(dev); + + do_roaming = (*extra); + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : get quality threshold + */ +static int wavelan_get_roam(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + *(extra) = do_roaming; + + return 0; +} +#endif /* WAVELAN_ROAMING */ + +#ifdef HISTOGRAM +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : set histogram + */ +static int wavelan_set_histo(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + + /* Check the number of intervals. */ + if (wrqu->data.length > 16) { + return(-E2BIG); } + + /* Disable histo while we copy the addresses. + * As we don't disable interrupts, we need to do this */ + lp->his_number = 0; + + /* Are there ranges to copy? */ + if (wrqu->data.length > 0) { + /* Copy interval ranges to the driver */ + memcpy(lp->his_range, extra, wrqu->data.length); + + { + int i; + printk(KERN_DEBUG "Histo :"); + for(i = 0; i < wrqu->data.length; i++) + printk(" %d", lp->his_range[i]); + printk("\n"); + } + + /* Reset result structure. */ + memset(lp->his_sum, 0x00, sizeof(long) * 16); + } + + /* Now we can set the number of ranges */ + lp->his_number = wrqu->data.length; + + return(0); +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : get histogram + */ +static int wavelan_get_histo(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + + /* Set the number of intervals. */ + wrqu->data.length = lp->his_number; + + /* Give back the distribution statistics */ + if(lp->his_number > 0) + memcpy(extra, lp->his_sum, sizeof(long) * lp->his_number); + + return(0); +} +#endif /* HISTOGRAM */ + +/*------------------------------------------------------------------*/ +/* + * Structures to export the Wireless Handlers + */ + +static const struct iw_priv_args wavelan_private_args[] = { +/*{ cmd, set_args, get_args, name } */ + { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" }, + { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" }, + { SIOCSIPROAM, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setroam" }, + { SIOCGIPROAM, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getroam" }, + { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16, 0, "sethisto" }, + { SIOCGIPHISTO, 0, IW_PRIV_TYPE_INT | 16, "gethisto" }, +}; + +#if WIRELESS_EXT > 12 + +static const iw_handler wavelan_handler[] = +{ + NULL, /* SIOCSIWNAME */ + wavelan_get_name, /* SIOCGIWNAME */ + wavelan_set_nwid, /* SIOCSIWNWID */ + wavelan_get_nwid, /* SIOCGIWNWID */ + wavelan_set_freq, /* SIOCSIWFREQ */ + wavelan_get_freq, /* SIOCGIWFREQ */ +#ifdef WAVELAN_ROAMING + wavelan_set_mode, /* SIOCSIWMODE */ + wavelan_get_mode, /* SIOCGIWMODE */ +#else /* WAVELAN_ROAMING */ + NULL, /* SIOCSIWMODE */ + NULL, /* SIOCGIWMODE */ +#endif /* WAVELAN_ROAMING */ + wavelan_set_sens, /* SIOCSIWSENS */ + wavelan_get_sens, /* SIOCGIWSENS */ + NULL, /* SIOCSIWRANGE */ + wavelan_get_range, /* SIOCGIWRANGE */ + NULL, /* SIOCSIWPRIV */ + NULL, /* SIOCGIWPRIV */ + NULL, /* SIOCSIWSTATS */ + NULL, /* SIOCGIWSTATS */ +#ifdef WIRELESS_SPY + wavelan_set_spy, /* SIOCSIWSPY */ + wavelan_get_spy, /* SIOCGIWSPY */ +#else /* WIRELESS_SPY */ + NULL, /* SIOCSIWSPY */ + NULL, /* SIOCGIWSPY */ +#endif /* WIRELESS_SPY */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ +#ifdef WAVELAN_ROAMING_EXT + wavelan_set_wap, /* SIOCSIWAP */ + wavelan_get_wap, /* SIOCGIWAP */ + NULL, /* -- hole -- */ + NULL, /* SIOCGIWAPLIST */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + wavelan_set_essid, /* SIOCSIWESSID */ + wavelan_get_essid, /* SIOCGIWESSID */ +#else /* WAVELAN_ROAMING_EXT */ + NULL, /* SIOCSIWAP */ + NULL, /* SIOCGIWAP */ + NULL, /* -- hole -- */ + NULL, /* SIOCGIWAPLIST */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWESSID */ + NULL, /* SIOCGIWESSID */ +#endif /* WAVELAN_ROAMING_EXT */ +#if WIRELESS_EXT > 8 + NULL, /* SIOCSIWNICKN */ + NULL, /* SIOCGIWNICKN */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWRATE */ + NULL, /* SIOCGIWRATE */ + NULL, /* SIOCSIWRTS */ + NULL, /* SIOCGIWRTS */ + NULL, /* SIOCSIWFRAG */ + NULL, /* SIOCGIWFRAG */ + NULL, /* SIOCSIWTXPOW */ + NULL, /* SIOCGIWTXPOW */ + NULL, /* SIOCSIWRETRY */ + NULL, /* SIOCGIWRETRY */ + wavelan_set_encode, /* SIOCSIWENCODE */ + wavelan_get_encode, /* SIOCGIWENCODE */ +#endif /* WIRELESS_EXT > 8 */ +}; + +static const iw_handler wavelan_private_handler[] = +{ + wavelan_set_qthr, /* SIOCIWFIRSTPRIV */ + wavelan_get_qthr, /* SIOCIWFIRSTPRIV + 1 */ +#ifdef WAVELAN_ROAMING + wavelan_set_roam, /* SIOCIWFIRSTPRIV + 2 */ + wavelan_get_roam, /* SIOCIWFIRSTPRIV + 3 */ +#else /* WAVELAN_ROAMING */ + NULL, /* SIOCIWFIRSTPRIV + 2 */ + NULL, /* SIOCIWFIRSTPRIV + 3 */ +#endif /* WAVELAN_ROAMING */ +#ifdef HISTOGRAM + wavelan_set_histo, /* SIOCIWFIRSTPRIV + 4 */ + wavelan_get_histo, /* SIOCIWFIRSTPRIV + 5 */ +#endif /* HISTOGRAM */ +}; + +static const struct iw_handler_def wavelan_handler_def = +{ + num_standard: sizeof(wavelan_handler)/sizeof(iw_handler), + num_private: sizeof(wavelan_private_handler)/sizeof(iw_handler), + num_private_args: sizeof(wavelan_private_args)/sizeof(struct iw_priv_args), + standard: (iw_handler *) wavelan_handler, + private: (iw_handler *) wavelan_private_handler, + private_args: (struct iw_priv_args *) wavelan_private_args, +}; + +#else /* WIRELESS_EXT > 12 */ +/*------------------------------------------------------------------*/ +/* + * Perform ioctl : config & info stuff + * This is here that are treated the wireless extensions (iwconfig) + */ +static int +wavelan_ioctl(struct net_device * dev, /* Device on wich the ioctl apply */ + struct ifreq * rq, /* Data passed */ + int cmd) /* Ioctl number */ +{ + struct iwreq * wrq = (struct iwreq *) rq; + int ret = 0; + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, cmd); +#endif + + /* Look what is the request */ + switch(cmd) + { + /* --------------- WIRELESS EXTENSIONS --------------- */ + + case SIOCGIWNAME: + wavelan_get_name(dev, NULL, &(wrq->u), NULL); break; - case SIOCGIWPRIV: - /* Basic checking... */ - if(wrq->u.data.pointer != (caddr_t) 0) - { - struct iw_priv_args priv[] = - { /* cmd, set_args, get_args, name */ - { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" }, - { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" }, - { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16, 0, "sethisto" }, - { SIOCGIPHISTO, 0, IW_PRIV_TYPE_INT | 16, "gethisto" }, - { SIOCSIPROAM, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1 , 0, "setroam" }, - { SIOCGIPROAM, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getroam" }, - }; + case SIOCSIWNWID: + ret = wavelan_set_nwid(dev, NULL, &(wrq->u), NULL); + break; - /* Set the number of ioctl available */ - wrq->u.data.length = 6; + case SIOCGIWNWID: + ret = wavelan_get_nwid(dev, NULL, &(wrq->u), NULL); + break; - /* Copy structure to the user buffer */ - if(copy_to_user(wrq->u.data.pointer, (u_char *) priv, - sizeof(priv))) + case SIOCSIWFREQ: + ret = wavelan_set_freq(dev, NULL, &(wrq->u), NULL); + break; + + case SIOCGIWFREQ: + ret = wavelan_get_freq(dev, NULL, &(wrq->u), NULL); + break; + + case SIOCSIWSENS: + ret = wavelan_set_sens(dev, NULL, &(wrq->u), NULL); + break; + + case SIOCGIWSENS: + ret = wavelan_get_sens(dev, NULL, &(wrq->u), NULL); + break; + +#if WIRELESS_EXT > 8 + case SIOCSIWENCODE: + { + char keybuf[8]; + if (wrq->u.encoding.pointer) { + /* We actually have a key to set */ + if (wrq->u.encoding.length != 8) { + ret = -EINVAL; + break; + } + if (copy_from_user(keybuf, + wrq->u.encoding.pointer, + wrq->u.encoding.length)) { ret = -EFAULT; + break; + } + } else if (wrq->u.encoding.length != 0) { + ret = -EINVAL; + break; } + ret = wavelan_set_encode(dev, NULL, &(wrq->u), keybuf); + } break; -#ifdef WIRELESS_SPY - case SIOCSIWSPY: - /* Set the spy list */ + case SIOCGIWENCODE: + if (! capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + { + char keybuf[8]; + ret = wavelan_get_encode(dev, NULL, + &(wrq->u), + keybuf); + if (wrq->u.encoding.pointer) { + if (copy_to_user(wrq->u.encoding.pointer, + keybuf, + wrq->u.encoding.length)) + ret = -EFAULT; + } + } + break; +#endif /* WIRELESS_EXT > 8 */ - /* Check the number of addresses */ - if(wrq->u.data.length > IW_MAX_SPY) - { +#ifdef WAVELAN_ROAMING_EXT +#if WIRELESS_EXT > 5 + case SIOCSIWESSID: + { + char essidbuf[IW_ESSID_MAX_SIZE+1]; + if (wrq->u.essid.length > IW_ESSID_MAX_SIZE) { ret = -E2BIG; break; } - lp->spy_number = wrq->u.data.length; - - /* If there is some addresses to copy */ - if(lp->spy_number > 0) - { - struct sockaddr address[IW_MAX_SPY]; - int i; - - /* Copy addresses to the driver */ - if(copy_from_user(address, wrq->u.data.pointer, - sizeof(struct sockaddr) * lp->spy_number)) - { - ret = -EFAULT; - break; - } + if (copy_from_user(essidbuf, wrq->u.essid.pointer, + wrq->u.essid.length)) { + ret = -EFAULT; + break; + } + ret = wavelan_set_essid(dev, NULL, + &(wrq->u), + essidbuf); + } + break; - /* Copy addresses to the lp structure */ - for(i = 0; i < lp->spy_number; i++) - { - memcpy(lp->spy_address[i], address[i].sa_data, - WAVELAN_ADDR_SIZE); - } + case SIOCGIWESSID: + { + char essidbuf[IW_ESSID_MAX_SIZE+1]; + ret = wavelan_get_essid(dev, NULL, + &(wrq->u), + essidbuf); + if (wrq->u.essid.pointer) + if ( copy_to_user(wrq->u.essid.pointer, + essidbuf, + wrq->u.essid.length) ) + ret = -EFAULT; + } + break; - /* Reset structure... */ - memset(lp->spy_stat, 0x00, sizeof(iw_qual) * IW_MAX_SPY); + case SIOCSIWAP: + ret = wavelan_set_wap(dev, NULL, &(wrq->u), NULL); + break; -#ifdef DEBUG_IOCTL_INFO - printk(KERN_DEBUG "SetSpy - Set of new addresses is :\n"); - for(i = 0; i < wrq->u.data.length; i++) - printk(KERN_DEBUG "%02X:%02X:%02X:%02X:%02X:%02X\n", - lp->spy_address[i][0], - lp->spy_address[i][1], - lp->spy_address[i][2], - lp->spy_address[i][3], - lp->spy_address[i][4], - lp->spy_address[i][5]); -#endif /* DEBUG_IOCTL_INFO */ - } + case SIOCGIWAP: + ret = wavelan_get_wap(dev, NULL, &(wrq->u), NULL); + break; +#endif /* WIRELESS_EXT > 5 */ +#endif /* WAVELAN_ROAMING_EXT */ +#if WIRELESS_EXT > 8 +#ifdef WAVELAN_ROAMING + case SIOCSIWMODE: + ret = wavelan_set_mode(dev, NULL, &(wrq->u), NULL); break; - case SIOCGIWSPY: - /* Get the spy list and spy stats */ + case SIOCGIWMODE: + ret = wavelan_get_mode(dev, NULL, &(wrq->u), NULL); + break; +#endif /* WAVELAN_ROAMING */ +#endif /* WIRELESS_EXT > 8 */ - /* Set the number of addresses */ - wrq->u.data.length = lp->spy_number; + case SIOCGIWRANGE: + { + struct iw_range range; + ret = wavelan_get_range(dev, NULL, + &(wrq->u), + (char *) &range); + if (copy_to_user(wrq->u.data.pointer, &range, + sizeof(struct iw_range))) + ret = -EFAULT; + } + break; - /* If the user want to have the addresses back... */ - if((lp->spy_number > 0) && (wrq->u.data.pointer != (caddr_t) 0)) + case SIOCGIWPRIV: + /* Basic checking... */ + if(wrq->u.data.pointer != (caddr_t) 0) { - struct sockaddr address[IW_MAX_SPY]; - int i; - - /* Copy addresses from the lp structure */ - for(i = 0; i < lp->spy_number; i++) - { - memcpy(address[i].sa_data, lp->spy_address[i], - WAVELAN_ADDR_SIZE); - address[i].sa_family = ARPHRD_ETHER; - } - - /* Copy addresses to the user buffer */ - if(copy_to_user(wrq->u.data.pointer, address, - sizeof(struct sockaddr) * lp->spy_number)) - { - ret = -EFAULT; - break; - } + /* Set the number of ioctl available */ + wrq->u.data.length = sizeof(wavelan_private_args) / sizeof(wavelan_private_args[0]); - /* Copy stats to the user buffer (just after) */ - if(copy_to_user(wrq->u.data.pointer + - (sizeof(struct sockaddr) * lp->spy_number), - lp->spy_stat, sizeof(iw_qual) * lp->spy_number)) - { - ret = -EFAULT; - break; - } + /* Copy structure to the user buffer */ + if(copy_to_user(wrq->u.data.pointer, (u_char *) wavelan_private_args, + sizeof(wavelan_private_args))) + ret = -EFAULT; + } + break; - /* Reset updated flags */ - for(i = 0; i < lp->spy_number; i++) - lp->spy_stat[i].updated = 0x0; - } /* if(pointer != NULL) */ +#ifdef WIRELESS_SPY + case SIOCSIWSPY: + { + struct sockaddr address[IW_MAX_SPY]; + /* Check the number of addresses */ + if (wrq->u.data.length > IW_MAX_SPY) { + ret = -E2BIG; + break; + } + /* Get the data in the driver */ + if (wrq->u.data.pointer) { + if (copy_from_user((char *) address, + wrq->u.data.pointer, + sizeof(struct sockaddr) * + wrq->u.data.length)) { + ret = -EFAULT; + break; + } + } else if (wrq->u.data.length != 0) { + ret = -EINVAL; + break; + } + ret = wavelan_set_spy(dev, NULL, &(wrq->u), + (char *) address); + } + break; + case SIOCGIWSPY: + { + char buffer[IW_MAX_SPY * (sizeof(struct sockaddr) + + sizeof(struct iw_quality))]; + ret = wavelan_get_spy(dev, NULL, &(wrq->u), + buffer); + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, + buffer, + (wrq->u.data.length * + (sizeof(struct sockaddr) + + sizeof(struct iw_quality))) + )) + ret = -EFAULT; + } + } break; #endif /* WIRELESS_SPY */ @@ -2446,34 +3095,21 @@ ret = -EPERM; break; } - psa.psa_quality_thr = *(wrq->u.name) & 0x0F; - psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa, - (unsigned char *)&psa.psa_quality_thr, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev); - mmc_out(base, mmwoff(0, mmw_quality_thr), psa.psa_quality_thr); + ret = wavelan_set_qthr(dev, NULL, &(wrq->u), NULL); break; case SIOCGIPQTHR: - psa_read(dev, (char *)&psa.psa_quality_thr - (char *)&psa, - (unsigned char *)&psa.psa_quality_thr, 1); - *(wrq->u.name) = psa.psa_quality_thr & 0x0F; + ret = wavelan_get_qthr(dev, NULL, &(wrq->u), NULL); break; #ifdef WAVELAN_ROAMING case SIOCSIPROAM: /* Note : should check if user == root */ - if(do_roaming && (*wrq->u.name)==0) - wv_roam_cleanup(dev); - else if(do_roaming==0 && (*wrq->u.name)!=0) - wv_roam_init(dev); - - do_roaming = (*wrq->u.name); - + ret = wavelan_set_roam(dev, NULL, &(wrq->u), NULL); break; case SIOCGIPROAM: - *(wrq->u.name) = do_roaming; + ret = wavelan_get_roam(dev, NULL, &(wrq->u), NULL); break; #endif /* WAVELAN_ROAMING */ @@ -2484,44 +3120,44 @@ { ret = -EPERM; } - - /* Check the number of intervals */ - if(wrq->u.data.length > 16) - { - ret = -E2BIG; - break; + { + char buffer[16]; + /* Check the number of intervals */ + if(wrq->u.data.length > 16) + { + ret = -E2BIG; + break; } - lp->his_number = wrq->u.data.length; - - /* If there is some addresses to copy */ - if(lp->his_number > 0) - { - /* Copy interval ranges to the driver */ - if(copy_from_user(lp->his_range, wrq->u.data.pointer, - sizeof(char) * lp->his_number)) - { - ret = -EFAULT; - break; - } - - /* Reset structure... */ - memset(lp->his_sum, 0x00, sizeof(long) * 16); + /* Get the data in the driver */ + if (wrq->u.data.pointer) { + if (copy_from_user(buffer, + wrq->u.data.pointer, + sizeof(struct sockaddr) * + wrq->u.data.length)) { + ret = -EFAULT; + break; + } + } else if (wrq->u.data.length != 0) { + ret = -EINVAL; + break; } + ret = wavelan_set_histo(dev, NULL, &(wrq->u), + buffer); + } break; case SIOCGIPHISTO: - /* Set the number of intervals */ - wrq->u.data.length = lp->his_number; - - /* Give back the distribution statistics */ - if((lp->his_number > 0) && (wrq->u.data.pointer != (caddr_t) 0)) - { - /* Copy data to the user buffer */ - if(copy_to_user(wrq->u.data.pointer, lp->his_sum, - sizeof(long) * lp->his_number)) + { + long buffer[16]; + ret = wavelan_get_histo(dev, NULL, &(wrq->u), + (char *) buffer); + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, + buffer, + (wrq->u.data.length * sizeof(long)))) ret = -EFAULT; - - } /* if(pointer != NULL) */ + } + } break; #endif /* HISTOGRAM */ @@ -2531,14 +3167,12 @@ ret = -EOPNOTSUPP; } - /* ReEnable interrupts & restore flags */ - wv_splx(lp, &flags); - #ifdef DEBUG_IOCTL_TRACE printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name); #endif return ret; } +#endif /* WIRELESS_EXT > 12 */ /*------------------------------------------------------------------*/ /* @@ -2559,7 +3193,7 @@ #endif /* Disable interrupts & save flags */ - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); wstats = &lp->wstats; @@ -2585,7 +3219,7 @@ wstats->discard.misc = 0L; /* ReEnable interrupts & restore flags */ - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); #ifdef DEBUG_IOCTL_TRACE printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", dev->name); @@ -2921,7 +3555,7 @@ printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, length); #endif - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); /* Check if we need some padding */ if(clen < ETH_ZLEN) @@ -2951,7 +3585,7 @@ /* Keep stats up to date */ lp->stats.tx_bytes += length; - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); #ifdef DEBUG_TX_INFO wv_packet_info((u_char *) buf, length, dev->name, "wv_packet_write"); @@ -2991,9 +3625,9 @@ * we can do it now */ if(lp->reconfig_82593) { - wv_splhi(lp, &flags); /* Disable interrupts */ + spin_lock_irqsave(&lp->spinlock, flags); /* Disable interrupts */ wv_82593_config(dev); - wv_splx(lp, &flags); /* Re-enable interrupts */ + spin_unlock_irqrestore(&lp->spinlock, flags); /* Re-enable interrupts */ /* Note : the configure procedure was totally synchronous, * so the Tx buffer is now free */ } @@ -3230,7 +3864,7 @@ printk(KERN_DEBUG "%s: ->wv_ru_stop()\n", dev->name); #endif - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); /* First, send the LAN controller a stop receive command */ wv_82593_cmd(dev, "wv_graceful_shutdown(): stop-rcv", @@ -3255,7 +3889,7 @@ } while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0)); - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); /* If there was a problem */ if(spin <= 0) @@ -3299,7 +3933,7 @@ if(!wv_ru_stop(dev)) return FALSE; - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); /* Now we know that no command is being executed. */ @@ -3354,7 +3988,7 @@ } #endif - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); #ifdef DEBUG_CONFIG_TRACE printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name); @@ -3630,7 +4264,7 @@ return FALSE; /* Disable interrupts */ - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); /* Disguised goto ;-) */ do @@ -3695,7 +4329,7 @@ while(0); /* Re-enable interrupts */ - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); #ifdef DEBUG_CONFIG_TRACE printk(KERN_DEBUG "%s: <-wv_hw_config()\n", dev->name); @@ -4021,7 +4655,7 @@ /* Prevent reentrancy. We need to do that because we may have * multiple interrupt handler running concurently. - * It is safe because wv_splhi() disable interrupts before aquiring + * It is safe because interrupts are disabled before aquiring * the spinlock. */ spin_lock(&lp->spinlock); @@ -4254,7 +4888,7 @@ dev->name); #endif - wv_splhi(lp, &flags); + spin_lock_irqsave(&lp->spinlock, flags); /* Ask to abort the current command */ outb(OP0_ABORT, LCCR(base)); @@ -4265,7 +4899,7 @@ aborted = TRUE; /* Release spinlock here so that wv_hw_reset() can grab it */ - wv_splx(lp, &flags); + spin_unlock_irqrestore(&lp->spinlock, flags); /* Check if we were successful in aborting it */ if(!aborted) @@ -4530,7 +5164,11 @@ dev->watchdog_timeo = WATCHDOG_JIFFIES; #ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */ +#if WIRELESS_EXT > 12 + dev->wireless_handlers = (struct iw_handler_def *)&wavelan_handler_def; +#else /* WIRELESS_EXT > 12 */ dev->do_ioctl = wavelan_ioctl; /* wireless extensions */ +#endif /* WIRELESS_EXT > 12 */ dev->get_wireless_stats = wavelan_get_wireless_stats; #endif diff -Nru a/drivers/net/wireless/wavelan_cs.p.h b/drivers/net/wireless/wavelan_cs.p.h --- a/drivers/net/wireless/wavelan_cs.p.h Thu Mar 7 18:17:41 2002 +++ b/drivers/net/wireless/wavelan_cs.p.h Thu Mar 7 18:17:41 2002 @@ -394,6 +394,12 @@ * o control first busy loop in wv_82593_cmd() * o Extend spinlock protection in wv_hw_config() * + * Changes made for release in 3.1.33 : + * ---------------------------------- + * - Optional use new driver API for Wireless Extensions : + * o got rid of wavelan_ioctl() + * o use a bunch of iw_handler instead + * * Wishes & dreams: * ---------------- * - Cleanup and integrate the roaming code @@ -430,6 +436,9 @@ #ifdef CONFIG_NET_RADIO #include /* Wireless extensions */ +#if WIRELESS_EXT > 12 +#include +#endif /* WIRELESS_EXT > 12 */ #endif /* Pcmcia headers that we need */ @@ -498,7 +507,7 @@ /************************ CONSTANTS & MACROS ************************/ #ifdef DEBUG_VERSION_SHOW -static const char *version = "wavelan_cs.c : v23 (SMP + wireless extensions) 20/12/00\n"; +static const char *version = "wavelan_cs.c : v24 (SMP + wireless extensions) 11/1/02\n"; #endif /* Watchdog temporisation */ @@ -523,8 +532,8 @@ #define SIOCSIPROAM SIOCIWFIRSTPRIV + 2 /* Set roaming state */ #define SIOCGIPROAM SIOCIWFIRSTPRIV + 3 /* Get roaming state */ -#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 6 /* Set histogram ranges */ -#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 7 /* Get histogram values */ +#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 4 /* Set histogram ranges */ +#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 5 /* Get histogram values */ /*************************** WaveLAN Roaming **************************/ #ifdef WAVELAN_ROAMING /* Conditional compile, see above in options */ @@ -589,6 +598,16 @@ typedef struct net_local net_local; typedef struct timer_list timer_list; +#if WIRELESS_EXT <= 12 +/* Wireless extensions backward compatibility */ +/* Part of iw_handler prototype we need */ +struct iw_request_info +{ + __u16 cmd; /* Wireless Extension command */ + __u16 flags; /* More to come ;-) */ +}; +#endif /* WIRELESS_EXT <= 12 */ + /* Basic types */ typedef u_char mac_addr[WAVELAN_ADDR_SIZE]; /* Hardware address */ @@ -661,12 +680,6 @@ #endif /* WAVELAN_ROAMING */ /* ----------------------- MISC SUBROUTINES ------------------------ */ -static inline void - wv_splhi(net_local *, /* Disable interrupts */ - unsigned long *); /* flags */ -static inline void - wv_splx(net_local *, /* ReEnable interrupts */ - unsigned long *); /* flags */ static void cs_error(client_handle_t, /* Report error to cardmgr */ int, diff -Nru a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c --- a/drivers/net/yellowfin.c Thu Mar 7 18:17:42 2002 +++ b/drivers/net/yellowfin.c Thu Mar 7 18:17:42 2002 @@ -1487,7 +1487,7 @@ name: DRV_NAME, id_table: yellowfin_pci_tbl, probe: yellowfin_init_one, - remove: yellowfin_remove_one, + remove: __devexit_p(yellowfin_remove_one), }; diff -Nru a/drivers/parport/ChangeLog b/drivers/parport/ChangeLog --- a/drivers/parport/ChangeLog Thu Mar 7 18:17:45 2002 +++ b/drivers/parport/ChangeLog Thu Mar 7 18:17:45 2002 @@ -1,3 +1,21 @@ +2001-11-14 Tim Waugh + + * parport_pc.c (parport_pc_pci_probe): Hooks for PCI cards before + and after probing for ports. + * parport_serial.c (parport_register): Likewise. + +2002-01-20 Tim Waugh + + * parport_pc.c (parport_pc_compat_write_block_pio, + parport_pc_ecp_write_block_pio, parport_pc_ecp_read_block_pio): + Use the default implementations if the caller wants to use + O_NONBLOCK. + +2002-02-25 Tim Waugh + + * parport_pc.c: Make sure that priv->ctr_writable includes IntEn + even if IRQ is given as a parameter. + 2002-01-21 Tim Waugh * daisy.c: Apply patch from Max Vorobiev to make parport_daisy_select diff -Nru a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c --- a/drivers/parport/parport_pc.c Thu Mar 7 18:17:43 2002 +++ b/drivers/parport/parport_pc.c Thu Mar 7 18:17:43 2002 @@ -818,8 +818,9 @@ long int expire; const struct parport_pc_private *priv = port->physport->private_data; - /* Special case: a timeout of zero means we cannot call schedule(). */ - if (!port->physport->cad->timeout) + /* Special case: a timeout of zero means we cannot call schedule(). + * Also if O_NONBLOCK is set then use the default implementation. */ + if (port->physport->cad->timeout <= PARPORT_INACTIVITY_O_NONBLOCK) return parport_ieee1284_write_compat (port, buf, length, flags); @@ -894,8 +895,9 @@ long int expire; const struct parport_pc_private *priv = port->physport->private_data; - /* Special case: a timeout of zero means we cannot call schedule(). */ - if (!port->physport->cad->timeout) + /* Special case: a timeout of zero means we cannot call schedule(). + * Also if O_NONBLOCK is set then use the default implementation. */ + if (port->physport->cad->timeout <= PARPORT_INACTIVITY_O_NONBLOCK) return parport_ieee1284_ecp_write_data (port, buf, length, flags); @@ -1014,8 +1016,9 @@ DPRINTK (KERN_DEBUG "parport_pc: parport_pc_ecp_read_block_pio\n"); dump_parport_state ("enter fcn", port); - /* Special case: a timeout of zero means we cannot call schedule(). */ - if (!port->cad->timeout) + /* Special case: a timeout of zero means we cannot call schedule(). + * Also if O_NONBLOCK is set then use the default implementation. */ + if (port->cad->timeout <= PARPORT_INACTIVITY_O_NONBLOCK) return parport_ieee1284_ecp_read_data (port, buf, length, flags); @@ -2136,8 +2139,6 @@ { struct parport_pc_private *priv = pb->private_data; - priv->ctr_writable |= 0x10; - if (priv->ecr) { pb->irq = programmable_irq_support(pb); @@ -2162,9 +2163,6 @@ if (pb->irq == PARPORT_IRQ_NONE) pb->irq = get_superio_irq(pb); - if (pb->irq == PARPORT_IRQ_NONE) - priv->ctr_writable &= ~0x10; - return pb->irq; } @@ -2293,6 +2291,7 @@ } if (p->irq != PARPORT_IRQ_NONE) { printk(", irq %d", p->irq); + priv->ctr_writable |= 0x10; if (p->dma == PARPORT_DMA_AUTO) { p->dma = PARPORT_DMA_NONE; @@ -2720,6 +2719,15 @@ int hi; /* -1 if not there, >6 for offset-method (max BAR is 6) */ } addr[4]; + + /* If set, this is called immediately after pci_enable_device. + * If it returns non-zero, no probing will take place and the + * ports will not be used. */ + int (*preinit_hook) (struct pci_dev *pdev, int autoirq, int autodma); + + /* If set, this is called after probing for ports. If 'failed' + * is non-zero we couldn't use any of the ports. */ + void (*postinit_hook) (struct pci_dev *pdev, int failed); } cards[] __devinitdata = { /* siig_1s1p_10x_550 */ { 1, { { 3, 4 }, } }, /* siig_1s1p_10x_650 */ { 1, { { 3, 4 }, } }, @@ -2896,6 +2904,10 @@ if ((err = pci_enable_device (dev)) != 0) return err; + if (cards[i].preinit_hook && + cards[i].preinit_hook (dev, PARPORT_IRQ_NONE, PARPORT_DMA_NONE)) + return -ENODEV; + for (n = 0; n < cards[i].numports; n++) { int lo = cards[i].addr[n].lo; int hi = cards[i].addr[n].hi; @@ -2917,6 +2929,9 @@ PARPORT_DMA_NONE, dev)) count++; } + + if (cards[i].postinit_hook) + cards[i].postinit_hook (dev, count == 0); return count == 0 ? -ENODEV : 0; } diff -Nru a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c --- a/drivers/parport/parport_serial.c Thu Mar 7 18:17:46 2002 +++ b/drivers/parport/parport_serial.c Thu Mar 7 18:17:46 2002 @@ -53,6 +53,15 @@ int hi; /* -1 if not there, >6 for offset-method (max BAR is 6) */ } addr[4]; + + /* If set, this is called immediately after pci_enable_device. + * If it returns non-zero, no probing will take place and the + * ports will not be used. */ + int (*preinit_hook) (struct pci_dev *pdev, int autoirq, int autodma); + + /* If set, this is called after probing for ports. If 'failed' + * is non-zero we couldn't use any of the ports. */ + void (*postinit_hook) (struct pci_dev *pdev, int failed); } cards[] __devinitdata = { /* titan_110l */ { 1, { { 3, -1 }, } }, /* titan_210l */ { 1, { { 3, -1 }, } }, @@ -239,6 +248,10 @@ int i = id->driver_data, n; int success = 0; + if (cards[i].preinit_hook && + cards[i].preinit_hook (dev, PARPORT_IRQ_NONE, PARPORT_DMA_NONE)) + return -ENODEV; + for (n = 0; n < cards[i].numports; n++) { struct parport *port; int lo = cards[i].addr[n].lo; @@ -265,6 +278,9 @@ } } + if (cards[i].postinit_hook) + cards[i].postinit_hook (dev, !success); + return success ? 0 : 1; } @@ -331,7 +347,7 @@ name: "parport_serial", id_table: parport_serial_pci_tbl, probe: parport_serial_pci_probe, - remove: parport_serial_pci_remove, + remove: __devexit_p(parport_serial_pci_remove), }; diff -Nru a/drivers/pci/pci.c b/drivers/pci/pci.c --- a/drivers/pci/pci.c Thu Mar 7 18:17:41 2002 +++ b/drivers/pci/pci.c Thu Mar 7 18:17:41 2002 @@ -23,6 +23,7 @@ #include /* for hotplug_path */ #include #include +#include #include #include /* isa_dma_bridge_buggy */ @@ -848,6 +849,100 @@ pcibios_set_master(dev); } +/** + * pdev_set_mwi - arch helper function for pcibios_set_mwi + * @dev: the PCI device for which MWI is enabled + * + * Helper function for implementation the arch-specific pcibios_set_mwi + * function. Originally copied from drivers/net/acenic.c. + * Copyright 1998-2001 by Jes Sorensen, . + * + * RETURNS: An appriopriate -ERRNO error value on eror, or zero for success. + */ +int +pdev_set_mwi(struct pci_dev *dev) +{ + int rc = 0; + u8 cache_size; + + /* + * Looks like this is necessary to deal with on all architectures, + * even this %$#%$# N440BX Intel based thing doesn't get it right. + * 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(dev, PCI_CACHE_LINE_SIZE, &cache_size); + cache_size <<= 2; + if (cache_size != SMP_CACHE_BYTES) { + printk(KERN_WARNING "PCI: %s PCI cache line size set incorrectly " + "(%i bytes) by BIOS/FW, ", + dev->slot_name, cache_size); + if (cache_size > SMP_CACHE_BYTES) { + printk("expecting %i\n", SMP_CACHE_BYTES); + rc = -EINVAL; + } else { + printk("correcting to %i\n", SMP_CACHE_BYTES); + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, + SMP_CACHE_BYTES >> 2); + } + } + + return rc; +} + +/** + * pci_set_mwi - enables memory-write-validate PCI transaction + * @dev: the PCI device for which MWI is enabled + * + * Enables the Memory-Write-Invalidate transaction in %PCI_COMMAND, + * and then calls @pcibios_set_mwi to do the needed arch specific + * operations or a generic mwi-prep function. + * + * RETURNS: An appriopriate -ERRNO error value on eror, or zero for success. + */ +int +pci_set_mwi(struct pci_dev *dev) +{ + int rc; + u16 cmd; + +#ifdef HAVE_ARCH_PCI_MWI + rc = pcibios_set_mwi(dev); +#else + rc = pdev_set_mwi(dev); +#endif + + if (rc) + return rc; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (! (cmd & PCI_COMMAND_INVALIDATE)) { + DBG("PCI: Enabling Mem-Wr-Inval for device %s\n", dev->slot_name); + cmd |= PCI_COMMAND_INVALIDATE; + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + + return 0; +} + +/** + * pci_clear_mwi - disables Memory-Write-Invalidate for device dev + * @dev: the PCI device to disable + * + * Disables PCI Memory-Write-Invalidate transaction on the device + */ +void +pci_clear_mwi(struct pci_dev *dev) +{ + u16 cmd; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (cmd & PCI_COMMAND_INVALIDATE) { + cmd &= ~PCI_COMMAND_INVALIDATE; + pci_write_config_word(dev, PCI_COMMAND, cmd); + } +} + int pci_set_dma_mask(struct pci_dev *dev, u64 mask) { @@ -966,7 +1061,7 @@ } } -void __devinit pci_read_bridge_bases(struct pci_bus *child) +void __devinit pci_read_bridge_bases(struct pci_bus *child) { struct pci_dev *dev = child->self; u8 io_base_lo, io_limit_lo; @@ -1057,7 +1152,7 @@ } } -static struct pci_bus * __devinit pci_alloc_bus(void) +static struct pci_bus * __devinit pci_alloc_bus(void) { struct pci_bus *b; @@ -1392,7 +1487,7 @@ return max; } -int __devinit pci_bus_exists(const struct list_head *list, int nr) +int __devinit pci_bus_exists(const struct list_head *list, int nr) { const struct list_head *l; @@ -1404,7 +1499,7 @@ return 0; } -struct pci_bus * __devinit pci_alloc_primary_bus(int bus) +struct pci_bus * __devinit pci_alloc_primary_bus(int bus) { struct pci_bus *b; @@ -1431,7 +1526,7 @@ return b; } -struct pci_bus * __devinit pci_scan_bus(int bus, struct pci_ops *ops, void *sysdata) +struct pci_bus * __devinit pci_scan_bus(int bus, struct pci_ops *ops, void *sysdata) { struct pci_bus *b = pci_alloc_primary_bus(bus); if (b) { @@ -1965,7 +2060,7 @@ return 0; } -static int __devinit pci_setup(char *str) +static int __devinit pci_setup(char *str) { while (str) { char *k = strchr(str, ','); @@ -2002,6 +2097,9 @@ EXPORT_SYMBOL(pci_find_slot); EXPORT_SYMBOL(pci_find_subsys); EXPORT_SYMBOL(pci_set_master); +EXPORT_SYMBOL(pci_set_mwi); +EXPORT_SYMBOL(pci_clear_mwi); +EXPORT_SYMBOL(pdev_set_mwi); EXPORT_SYMBOL(pci_set_dma_mask); EXPORT_SYMBOL(pci_dac_set_dma_mask); EXPORT_SYMBOL(pci_assign_resource); @@ -2019,10 +2117,12 @@ EXPORT_SYMBOL(pci_add_new_bus); EXPORT_SYMBOL(pci_do_scan_bus); EXPORT_SYMBOL(pci_scan_slot); +#ifdef CONFIG_PROC_FS EXPORT_SYMBOL(pci_proc_attach_device); EXPORT_SYMBOL(pci_proc_detach_device); EXPORT_SYMBOL(pci_proc_attach_bus); EXPORT_SYMBOL(pci_proc_detach_bus); +#endif #endif EXPORT_SYMBOL(pci_set_power_state); diff -Nru a/drivers/pci/pci.ids b/drivers/pci/pci.ids --- a/drivers/pci/pci.ids Thu Mar 7 18:17:36 2002 +++ b/drivers/pci/pci.ids Thu Mar 7 18:17:36 2002 @@ -3952,6 +3952,7 @@ 9511 16PCI954 Function 1 15ed 2000 Macrolink MCCR Serial p4-7 of 8 15ed 2001 Macrolink MCCR Serial p4-15 of 16 + 9521 Oxford Semi OX16PCI952 PCI/dual 16950 UART 1416 Multiwave Innovation pte Ltd 1417 Convergenet Technologies Inc 1418 Kyushu electronics systems Inc @@ -4182,12 +4183,18 @@ 14e3 AMTELCO 14e4 BROADCOM Corporation 1644 NetXtreme BCM5700 Gigabit Ethernet + 1014 0277 Broadcom Vigil B5700 1000Base-T + 1028 00d1 Broadcom BCM5700 + 1028 0106 Broadcom BCM5700 + 1028 0109 Broadcom BCM5700 + 1028 010a Broadcom BCM5700 10b7 1000 3C996-T 1000BaseTX 10b7 1001 3C996B-T 1000BaseTX 10b7 1002 3C996C-T 1000BaseTX 10b7 1003 3C997-T 1000BaseTX 10b7 1004 3C996-SX 1000BaseSX 10b7 1005 3C997-SX 1000BaseSX + 10b7 1008 3C942 Gigabit LOM (31X31) 14e4 0002 NetXtreme 1000BaseSX 14e4 0003 NetXtreme 1000BaseSX 14e4 0004 NetXtreme 1000BaseTX @@ -4196,6 +4203,7 @@ 0e11 007c NC7770 1000BaseTX 0e11 007d NC6770 1000BaseSX 0e11 0085 NC7780 1000BaseTX + 1028 0121 Broadcom BCM5701 10b7 1004 3C996-SX 1000BaseSX 10b7 1006 3C996B-T 1000BaseTX 10b7 1007 3C1000-T 1000BaseTX @@ -4206,7 +4214,16 @@ 14e4 0007 NetXtreme BCM5701 1000BaseSX 14e4 0008 NetXtreme BCM5701 1000BaseTX 14e4 8008 NetXtreme BCM5701 1000BaseTX + 1646 NetXtreme BCM5702 Gigabit Ethernet + 14e4 8009 Broadcom BCM5702 1647 NetXtreme BCM5703 Gigabit Ethernet + 0e11 009a NC7770 + 0e11 0099 NC7780 + 14e4 0009 Broadcom BCM5703 + 14e4 8009 Broadcom BCM5703 + 164d NetXtreme BCM5702FE Gigabit Ethernet + 16a6 NetXtreme BCM5702X Gigabit Ethernet + 16a7 NetXtreme BCM5703X Gigabit Ethernet 5820 BCM5820 Crypto Accelerator 14e5 Pixelfusion Ltd 14e6 SHINING Technology Inc @@ -4646,6 +4663,8 @@ 0400 FarSync T2P (2 port X.21/V.35/V.24) 0440 FarSync T4P (4 port X.21/V.35/V.24) 1668 Action Tec Electronics Inc +173b Altima (nee BroadCom) + 03e8 AC1000 Gigabit Ethernet 1813 Ambient Technologies Inc 1a08 Sierra semiconductor 0000 SC15064 diff -Nru a/drivers/pci/quirks.c b/drivers/pci/quirks.c --- a/drivers/pci/quirks.c Thu Mar 7 18:17:37 2002 +++ b/drivers/pci/quirks.c Thu Mar 7 18:17:37 2002 @@ -444,13 +444,15 @@ static void __init quirk_amd_ordering(struct pci_dev *dev) { u32 pcic; - - pci_read_config_dword(dev, 0x42, &pcic); - if((pcic&2)==0) + pci_read_config_dword(dev, 0x4C, &pcic); + if((pcic&6)!=6) { - pcic |= 2; - printk(KERN_WARNING "BIOS disabled PCI ordering compliance, so we enabled it again.\n"); - pci_write_config_dword(dev, 0x42, pcic); + pcic |= 6; + printk(KERN_WARNING "BIOS failed to enable PCI standards compliance, fixing this error.\n"); + pci_write_config_dword(dev, 0x4C, pcic); + pci_read_config_dword(dev, 0x84, &pcic); + pcic |= (1<<23); /* Required in this mode */ + pci_write_config_dword(dev, 0x84, pcic); } } diff -Nru a/drivers/pcmcia/Config.in b/drivers/pcmcia/Config.in --- a/drivers/pcmcia/Config.in Thu Mar 7 18:17:44 2002 +++ b/drivers/pcmcia/Config.in Thu Mar 7 18:17:44 2002 @@ -24,5 +24,6 @@ dep_tristate ' HD64465 host bridge support' CONFIG_HD64465_PCMCIA $CONFIG_PCMCIA fi fi +dep_tristate ' SA1100 support' CONFIG_PCMCIA_SA1100 $CONFIG_ARCH_SA1100 $CONFIG_PCMCIA endmenu diff -Nru a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile --- a/drivers/pcmcia/Makefile Thu Mar 7 18:17:40 2002 +++ b/drivers/pcmcia/Makefile Thu Mar 7 18:17:40 2002 @@ -62,22 +62,25 @@ obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o sa1100_cs-objs-y := sa1100_generic.o +sa1100_cs-objs-$(CONFIG_SA1100_ADSBITSY) += sa1100_adsbitsy.o sa1111_generic.o sa1100_cs-objs-$(CONFIG_SA1100_ASSABET) += sa1100_assabet.o -sa1100_cs-objs-$(CONFIG_ASSABET_NEPONSET) += sa1100_neponset.o -sa1100_cs-objs-$(CONFIG_SA1100_H3600) += sa1100_h3600.o +sa1100_cs-objs-$(CONFIG_ASSABET_NEPONSET) += sa1100_neponset.o sa1111_generic.o +sa1100_cs-objs-$(CONFIG_SA1100_BADGE4) += sa1100_badge4.o sa1111_generic.o sa1100_cs-objs-$(CONFIG_SA1100_CERF) += sa1100_cerf.o +sa1100_cs-objs-$(CONFIG_SA1100_FLEXANET) += sa1100_flexanet.o +sa1100_cs-objs-$(CONFIG_SA1100_FREEBIRD) += sa1100_freebird.o +sa1100_cs-objs-$(CONFIG_SA1100_GRAPHICSMASTER) += sa1100_graphicsmaster.o sa1111_generic.o sa1100_cs-objs-$(CONFIG_SA1100_GRAPHICSCLIENT) += sa1100_graphicsclient.o -sa1100_cs-objs-$(CONFIG_SA1100_XP860) += sa1100_xp860.o +sa1100_cs-objs-$(CONFIG_SA1100_H3600) += sa1100_h3600.o +sa1100_cs-objs-$(CONFIG_SA1100_JORNADA720) += sa1100_jornada720.o sa1111_generic.o sa1100_cs-objs-$(CONFIG_SA1100_PANGOLIN) += sa1100_pangolin.o -sa1100_cs-objs-$(CONFIG_SA1100_YOPY) += sa1100_yopy.o -sa1100_cs-objs-$(CONFIG_SA1100_FREEBIRD) += sa1100_freebird.o -sa1100_cs-objs-$(CONFIG_SA1100_PFS168) += sa1100_pfs168.o -sa1100_cs-objs-$(CONFIG_SA1100_JORNADA720) += sa1100_jornada720.o -sa1100_cs-objs-$(CONFIG_SA1100_FLEXANET) += sa1100_flexanet.o +sa1100_cs-objs-$(CONFIG_SA1100_PFS168) += sa1100_pfs168.o sa1111_generic.o +sa1100_cs-objs-$(CONFIG_SA1100_PT_SYSTEM3) += sa1100_system3.o sa1111_generic.o +sa1100_cs-objs-$(CONFIG_SA1100_SHANNON) += sa1100_shannon.o sa1100_cs-objs-$(CONFIG_SA1100_SIMPAD) += sa1100_simpad.o -sa1100_cs-objs-$(CONFIG_SA1100_GRAPHICSMASTER) += sa1100_graphicsmaster.o -sa1100_cs-objs-$(CONFIG_SA1100_ADSBITSY) += sa1100_adsbitsy.o sa1100_cs-objs-$(CONFIG_SA1100_STORK) += sa1100_stork.o +sa1100_cs-objs-$(CONFIG_SA1100_XP860) += sa1100_xp860.o sa1111_generic.o +sa1100_cs-objs-$(CONFIG_SA1100_YOPY) += sa1100_yopy.o include $(TOPDIR)/Rules.make @@ -85,7 +88,7 @@ $(LD) $(LD_RFLAG) -r -o $@ $(pcmcia_core-objs) sa1100_cs.o: $(sa1100_cs-objs-y) - $(LD) -r -o $@ $(sa1100_cs-objs-y) + $(LD) -r -o $@ $(sort $(sa1100_cs-objs-y)) yenta_socket.o: $(yenta_socket-objs) $(LD) $(LD_RFLAG) -r -o $@ $(yenta_socket-objs) diff -Nru a/drivers/pcmcia/pci_socket.c b/drivers/pcmcia/pci_socket.c --- a/drivers/pcmcia/pci_socket.c Thu Mar 7 18:17:44 2002 +++ b/drivers/pcmcia/pci_socket.c Thu Mar 7 18:17:44 2002 @@ -249,7 +249,7 @@ name: "cardbus", id_table: cardbus_table, probe: cardbus_probe, - remove: cardbus_remove, + remove: __devexit_p(cardbus_remove), suspend: cardbus_suspend, resume: cardbus_resume, }; diff -Nru a/drivers/pcmcia/sa1100.h b/drivers/pcmcia/sa1100.h --- a/drivers/pcmcia/sa1100.h Thu Mar 7 18:17:37 2002 +++ b/drivers/pcmcia/sa1100.h Thu Mar 7 18:17:37 2002 @@ -38,9 +38,7 @@ #include #include #include "cs_internal.h" - -#include - +#include "sa1100_generic.h" /* MECR: Expansion Memory Configuration Register * (SA-1100 Developers Manual, p.10-13; SA-1110 Developers Manual, p.10-24) @@ -157,15 +155,24 @@ * use when responding to a Card Services query of some kind. */ struct sa1100_pcmcia_socket { + /* + * Core PCMCIA state + */ socket_state_t cs_state; - struct pcmcia_state k_state; - unsigned int irq; - void (*handler)(void *, unsigned int); - void *handler_info; pccard_io_map io_map[MAX_IO_WIN]; pccard_mem_map mem_map[MAX_WIN]; - ioaddr_t virt_io, phys_attr, phys_mem; + void (*handler)(void *, unsigned int); + void *handler_info; + + struct pcmcia_state k_state; + ioaddr_t phys_attr, phys_mem; + void *virt_io; unsigned short speed_io, speed_attr, speed_mem; + + /* + * Info from low level handler + */ + unsigned int irq; }; @@ -180,23 +187,60 @@ /* - * Declaration for all implementation specific low_level operations. + * Declaration for all machine specific init/exit functions. */ -extern struct pcmcia_low_level assabet_pcmcia_ops; -extern struct pcmcia_low_level neponset_pcmcia_ops; -extern struct pcmcia_low_level h3600_pcmcia_ops; -extern struct pcmcia_low_level cerf_pcmcia_ops; -extern struct pcmcia_low_level gcplus_pcmcia_ops; -extern struct pcmcia_low_level xp860_pcmcia_ops; -extern struct pcmcia_low_level yopy_pcmcia_ops; -extern struct pcmcia_low_level pangolin_pcmcia_ops; -extern struct pcmcia_low_level freebird_pcmcia_ops; -extern struct pcmcia_low_level pfs168_pcmcia_ops; -extern struct pcmcia_low_level jornada720_pcmcia_ops; -extern struct pcmcia_low_level flexanet_pcmcia_ops; -extern struct pcmcia_low_level simpad_pcmcia_ops; -extern struct pcmcia_low_level graphicsmaster_pcmcia_ops; -extern struct pcmcia_low_level adsbitsy_pcmcia_ops; -extern struct pcmcia_low_level stork_pcmcia_ops; +extern int pcmcia_adsbitsy_init(void); +extern void pcmcia_adsbitsy_exit(void); + +extern int pcmcia_assabet_init(void); +extern void pcmcia_assabet_exit(void); + +extern int pcmcia_badge4_init(void); +extern void pcmcia_badge4_exit(void); + +extern int pcmcia_cerf_init(void); +extern void pcmcia_cerf_exit(void); + +extern int pcmcia_flexanet_init(void); +extern void pcmcia_flexanet_exit(void); + +extern int pcmcia_freebird_init(void); +extern void pcmcia_freebird_exit(void); + +extern int pcmcia_gcplus_init(void); +extern void pcmcia_gcplus_exit(void); + +extern int pcmcia_graphicsmaster_init(void); +extern void pcmcia_graphicsmaster_exit(void); + +extern int pcmcia_jornada720_init(void); +extern void pcmcia_jornada720_exit(void); + +extern int pcmcia_neponset_init(void); +extern void pcmcia_neponset_exit(void); + +extern int pcmcia_pangolin_init(void); +extern void pcmcia_pangolin_exit(void); + +extern int pcmcia_pfs168_init(void); +extern void pcmcia_pfs168_exit(void); + +extern int pcmcia_shannon_init(void); +extern void pcmcia_shannon_exit(void); + +extern int pcmcia_simpad_init(void); +extern void pcmcia_simpad_exit(void); + +extern int pcmcia_stork_init(void); +extern void pcmcia_stork_exit(void); + +extern int pcmcia_system3_init(void); +extern void pcmcia_system3_exit(void); + +extern int pcmcia_xp860_init(void); +extern void pcmcia_xp860_exit(void); + +extern int pcmcia_yopy_init(void); +extern void pcmcia_yopy_exit(void); #endif /* !defined(_PCMCIA_SA1100_H) */ diff -Nru a/drivers/pcmcia/sa1100_adsbitsy.c b/drivers/pcmcia/sa1100_adsbitsy.c --- a/drivers/pcmcia/sa1100_adsbitsy.c Thu Mar 7 18:17:44 2002 +++ b/drivers/pcmcia/sa1100_adsbitsy.c Thu Mar 7 18:17:44 2002 @@ -11,206 +11,97 @@ */ #include #include +#include -#include #include -#include -#include + +#include "sa1100_generic.h" +#include "sa1111_generic.h" static int adsbitsy_pcmcia_init(struct pcmcia_init *init) { - int return_val=0; - /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); /* Disable Power 3.3V/5V for PCMCIA/CF */ PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3; - INTPOL1 |= (1 << (S0_READY_NINT - SA1111_IRQ(32))) | - (1 << (S1_READY_NINT - SA1111_IRQ(32))) | - (1 << (S0_CD_VALID - SA1111_IRQ(32))) | - (1 << (S1_CD_VALID - SA1111_IRQ(32))) | - (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | - (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))); - - return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT, - "GC Master PCMCIA (0) CD", NULL); - return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT, - "GC Master CF (1) CD", NULL); - return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT, - "GC Master PCMCIA (0) BVD1", NULL); - return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT, - "GC Master CF (1) BVD1", NULL); - + /* Why? */ MECR = 0x09430943; - return (return_val<0) ? -1 : 2; -} - -static int adsbitsy_pcmcia_shutdown(void) -{ - - free_irq(S0_CD_VALID, NULL); - free_irq(S1_CD_VALID, NULL); - free_irq(S0_BVD1_STSCHG, NULL); - free_irq(S1_BVD1_STSCHG, NULL); - - INTPOL1 &= ~((1 << (S0_CD_VALID - SA1111_IRQ(32))) | - (1 << (S1_CD_VALID - SA1111_IRQ(32))) | - (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | - (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)))); - - return 0; + return sa1111_pcmcia_init(init); } -static int adsbitsy_pcmcia_socket_state(struct pcmcia_state_array *state_array) +static int +adsbitsy_pcmcia_configure_socket(const struct pcmcia_configure *conf) { - unsigned long status; - int return_val=1; - - if(state_array->size<2) return -1; - - memset(state_array->state, 0, - (state_array->size)*sizeof(struct pcmcia_state)); - - status=PCSR; - - state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0; - - state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1; - - state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1; - - state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1; - - state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1; + unsigned int pa_dwr_mask, pa_dwr_set; + int ret; - state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0; - - state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; - - state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0; - - state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1; - - state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1; - - state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1; - - state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1; - - state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0; - - state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0; - - return return_val; -} - -static int adsbitsy_pcmcia_get_irq_info(struct pcmcia_irq_info *info) -{ - - switch(info->sock){ + switch (conf->sock) { case 0: - info->irq=S0_READY_NINT; - break; - - case 1: - info->irq=S1_READY_NINT; - break; - - default: - return -1; - } - - return 0; -} - -static int adsbitsy_pcmcia_configure_socket(const struct pcmcia_configure *configure) -{ - unsigned long pccr=PCCR, gpio=PA_DWR; - - switch(configure->sock){ - case 0: - - switch(configure->vcc){ - case 0: - pccr = (pccr & ~PCCR_S0_FLT); - gpio |= GPIO_GPIO0 | GPIO_GPIO1; - break; - - case 33: - pccr = (pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN; - gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1); - gpio &= ~GPIO_GPIO0; - break; - - case 50: - pccr = (pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN); - gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1); - gpio |= GPIO_GPIO0; - break; + pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1; + switch (conf->vcc) { default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, - configure->vcc); - return -1; + case 0: pa_dwr_set = GPIO_GPIO0 | GPIO_GPIO1; break; + case 33: pa_dwr_set = GPIO_GPIO1; break; + case 50: pa_dwr_set = GPIO_GPIO0; break; } - - pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST); - break; case 1: - switch(configure->vcc){ - case 0: - pccr = (pccr & ~PCCR_S1_FLT); - gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3); - break; - - case 33: - pccr = (pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN; - gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3); - gpio |= GPIO_GPIO2; - break; - - case 50: - pccr = (pccr | PCCR_S1_PSE | PCCR_S1_FLT | PCCR_S1_PWAITEN); - gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3); - gpio |= GPIO_GPIO3; - break; + pa_dwr_mask = GPIO_GPIO2 | GPIO_GPIO3; + switch (conf->vcc) { default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, - configure->vcc); - return -1; + case 0: pa_dwr_set = 0; break; + case 33: pa_dwr_set = GPIO_GPIO2; break; + case 50: pa_dwr_set = GPIO_GPIO3; break; } - if(configure->vpp!=configure->vcc && configure->vpp!=0){ - printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, - configure->vpp); - return -1; - } - - pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST); - - break; - default: return -1; } - PCCR = pccr; - PA_DWR = gpio; + if (conf->vpp != conf->vcc && conf->vpp != 0) { + printk(KERN_ERR "%s(): CF slot cannot support VPP %u\n", + __FUNCTION__, conf->vpp); + return -1; + } - return 0; + ret = sa1111_pcmcia_configure_socket(conf); + if (ret == 0) { + unsigned long flags; + + local_irq_save(flags); + PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set; + local_irq_restore(flags); + } + + return ret; } -struct pcmcia_low_level adsbitsy_pcmcia_ops = { - adsbitsy_pcmcia_init, - adsbitsy_pcmcia_shutdown, - adsbitsy_pcmcia_socket_state, - adsbitsy_pcmcia_get_irq_info, - adsbitsy_pcmcia_configure_socket +static struct pcmcia_low_level adsbitsy_pcmcia_ops = { + init: adsbitsy_pcmcia_init, + shutdown: sa1111_pcmcia_shutdown, + socket_state: sa1111_pcmcia_socket_state, + get_irq_info: sa1111_pcmcia_get_irq_info, + configure_socket: adsbitsy_pcmcia_configure_socket, + + socket_init: sa1111_pcmcia_socket_init, + socket_suspend: sa1111_pcmcia_socket_suspend, }; +int __init pcmcia_adsbitsy_init(void) +{ + int ret = -ENODEV; + if (machine_is_adsbitsy()) + ret = sa1100_register_pcmcia(&adsbitsy_pcmcia_ops); + return ret; +} + +void __exit pcmcia_adsbitsy_exit(void) +{ + sa1100_unregister_pcmcia(&adsbitsy_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_assabet.c b/drivers/pcmcia/sa1100_assabet.c --- a/drivers/pcmcia/sa1100_assabet.c Thu Mar 7 18:17:40 2002 +++ b/drivers/pcmcia/sa1100_assabet.c Thu Mar 7 18:17:40 2002 @@ -4,146 +4,221 @@ * PCMCIA implementation routines for Assabet * */ +#include #include #include +#include #include #include -#include #include -static int assabet_pcmcia_init(struct pcmcia_init *init){ - int irq, res; +#include "sa1100_generic.h" - /* Enable CF bus: */ - ASSABET_BCR_clear(ASSABET_BCR_CF_BUS_OFF); - - /* All those are inputs */ - GPDR &= ~(ASSABET_GPIO_CF_CD | ASSABET_GPIO_CF_BVD2 | ASSABET_GPIO_CF_BVD1 | ASSABET_GPIO_CF_IRQ); - - /* Set transition detect */ - set_GPIO_IRQ_edge( ASSABET_GPIO_CF_CD|ASSABET_GPIO_CF_BVD2|ASSABET_GPIO_CF_BVD1, GPIO_BOTH_EDGES ); - set_GPIO_IRQ_edge( ASSABET_GPIO_CF_IRQ, GPIO_FALLING_EDGE ); - - /* Register interrupts */ - irq = ASSABET_IRQ_GPIO_CF_CD; - res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_CD", NULL ); - if( res < 0 ) goto irq_err; - irq = ASSABET_IRQ_GPIO_CF_BVD2; - res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD2", NULL ); - if( res < 0 ) goto irq_err; - irq = ASSABET_IRQ_GPIO_CF_BVD1; - res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD1", NULL ); - if( res < 0 ) goto irq_err; - - /* There's only one slot, but it's "Slot 1": */ - return 2; - -irq_err: - printk( KERN_ERR "%s: Request for IRQ %u failed\n", __FUNCTION__, irq ); - return -1; -} +static struct irqs { + int irq; + const char *str; +} irqs[] = { + { ASSABET_IRQ_GPIO_CF_CD, "CF_CD" }, + { ASSABET_IRQ_GPIO_CF_BVD2, "CF_BVD2" }, + { ASSABET_IRQ_GPIO_CF_BVD1, "CF_BVD1" }, +}; -static int assabet_pcmcia_shutdown(void) +static int assabet_pcmcia_init(struct pcmcia_init *init) { - /* disable IRQs */ - free_irq( ASSABET_IRQ_GPIO_CF_CD, NULL ); - free_irq( ASSABET_IRQ_GPIO_CF_BVD2, NULL ); - free_irq( ASSABET_IRQ_GPIO_CF_BVD1, NULL ); - - /* Disable CF bus: */ - ASSABET_BCR_set(ASSABET_BCR_CF_BUS_OFF); - - return 0; -} - -static int assabet_pcmcia_socket_state(struct pcmcia_state_array - *state_array){ - unsigned long levels; + int i, res; - if(state_array->size<2) return -1; + /* Set transition detect */ + set_irq_type(ASSABET_IRQ_GPIO_CF_IRQ, IRQT_FALLING); - memset(state_array->state, 0, - (state_array->size)*sizeof(struct pcmcia_state)); + /* Register interrupts */ + for (i = 0; i < ARRAY_SIZE(irqs); i++) { + set_irq_type(irqs[i].irq, IRQT_NOEDGE); + res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT, + irqs[i].str, NULL); + if (res) + goto irq_err; + } + + /* There's only one slot, but it's "Slot 1": */ + return 2; + + irq_err: + printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n", + __FUNCTION__, irqs[i].irq, res); - levels=GPLR; + while (i--) + free_irq(irqs[i].irq, NULL); - state_array->state[1].detect=((levels & ASSABET_GPIO_CF_CD)==0)?1:0; + return res; +} - state_array->state[1].ready=(levels & ASSABET_GPIO_CF_IRQ)?1:0; +/* + * Release all resources. + */ +static int assabet_pcmcia_shutdown(void) +{ + int i; - state_array->state[1].bvd1=(levels & ASSABET_GPIO_CF_BVD1)?1:0; + for (i = 0; i < ARRAY_SIZE(irqs); i++) + free_irq(irqs[i].irq, NULL); + + return 0; +} - state_array->state[1].bvd2=(levels & ASSABET_GPIO_CF_BVD2)?1:0; +static int +assabet_pcmcia_socket_state(struct pcmcia_state_array *state_array) +{ + unsigned long levels; - state_array->state[1].wrprot=0; /* Not available on Assabet. */ + if (state_array->size < 2) + return -1; - state_array->state[1].vs_3v=1; /* Can only apply 3.3V on Assabet. */ + levels = GPLR; - state_array->state[1].vs_Xv=0; + state_array->state[1].detect = (levels & ASSABET_GPIO_CF_CD) ? 0 : 1; + state_array->state[1].ready = (levels & ASSABET_GPIO_CF_IRQ) ? 1 : 0; + state_array->state[1].bvd1 = (levels & ASSABET_GPIO_CF_BVD1) ? 1 : 0; + state_array->state[1].bvd2 = (levels & ASSABET_GPIO_CF_BVD2) ? 1 : 0; + state_array->state[1].wrprot = 0; /* Not available on Assabet. */ + state_array->state[1].vs_3v = 1; /* Can only apply 3.3V on Assabet. */ + state_array->state[1].vs_Xv = 0; - return 1; + return 1; } -static int assabet_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ - - if(info->sock>1) return -1; +static int assabet_pcmcia_get_irq_info(struct pcmcia_irq_info *info) +{ + if (info->sock > 1) + return -1; - if(info->sock==1) - info->irq=ASSABET_IRQ_GPIO_CF_IRQ; + if (info->sock == 1) + info->irq = ASSABET_IRQ_GPIO_CF_IRQ; - return 0; + return 0; } -static int assabet_pcmcia_configure_socket(const struct pcmcia_configure - *configure) +static int +assabet_pcmcia_configure_socket(const struct pcmcia_configure *configure) { - unsigned long value, flags; + unsigned int mask; - if(configure->sock>1) return -1; + if (configure->sock > 1) + return -1; - if(configure->sock==0) return 0; + if (configure->sock == 0) + return 0; - save_flags_cli(flags); + switch (configure->vcc) { + case 0: + mask = 0; + break; + + case 50: + printk(KERN_WARNING "%s(): CS asked for 5V, applying 3.3V...\n", + __FUNCTION__); + + case 33: /* Can only apply 3.3V to the CF slot. */ + mask = ASSABET_BCR_CF_PWR; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + return -1; + } + + /* Silently ignore Vpp, output enable, speaker enable. */ + + if (configure->reset) + mask |= ASSABET_BCR_CF_RST; + + ASSABET_BCR_frob(ASSABET_BCR_CF_RST | ASSABET_BCR_CF_PWR, mask); + + /* + * Handle suspend mode properly. This prevents a + * flood of IRQs from the CF device. + */ + if (configure->irq) + enable_irq(ASSABET_IRQ_GPIO_CF_IRQ); + else + disable_irq(ASSABET_IRQ_GPIO_CF_IRQ); - value = BCR_value; + return 0; +} - switch(configure->vcc){ - case 0: - value &= ~ASSABET_BCR_CF_PWR; - break; +/* + * Enable card status IRQs on (re-)initialisation. This can + * be called at initialisation, power management event, or + * pcmcia event. + */ +static int assabet_pcmcia_socket_init(int sock) +{ + int i; + + if (sock == 1) { + /* + * Enable CF bus + */ + ASSABET_BCR_clear(ASSABET_BCR_CF_BUS_OFF); + + for (i = 0; i < ARRAY_SIZE(irqs); i++) + set_irq_type(irqs[i].irq, IRQT_BOTHEDGE); + } - case 50: - printk(KERN_WARNING "%s(): CS asked for 5V, applying 3.3V...\n", - __FUNCTION__); + return 0; +} - case 33: /* Can only apply 3.3V to the CF slot. */ - value |= ASSABET_BCR_CF_PWR; - break; +/* + * Disable card status IRQs on suspend. + */ +static int assabet_pcmcia_socket_suspend(int sock) +{ + int i; - default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, - configure->vcc); - restore_flags(flags); - return -1; - } + if (sock == 1) { + for (i = 0; i < ARRAY_SIZE(irqs); i++) + set_irq_type(irqs[i].irq, IRQT_NOEDGE); + + /* + * Tristate the CF bus signals. Also assert CF + * reset as per user guide page 4-11. + */ + ASSABET_BCR_set(ASSABET_BCR_CF_BUS_OFF | ASSABET_BCR_CF_RST); + } - value = (configure->reset) ? (value | ASSABET_BCR_CF_RST) : (value & ~ASSABET_BCR_CF_RST); + return 0; +} - /* Silently ignore Vpp, output enable, speaker enable. */ +static struct pcmcia_low_level assabet_pcmcia_ops = { + init: assabet_pcmcia_init, + shutdown: assabet_pcmcia_shutdown, + socket_state: assabet_pcmcia_socket_state, + get_irq_info: assabet_pcmcia_get_irq_info, + configure_socket: assabet_pcmcia_configure_socket, - ASSABET_BCR = BCR_value = value; + socket_init: assabet_pcmcia_socket_init, + socket_suspend: assabet_pcmcia_socket_suspend, +}; - restore_flags(flags); +int __init pcmcia_assabet_init(void) +{ + int ret = -ENODEV; - return 0; + if (machine_is_assabet()) { + if (!machine_has_neponset()) + ret = sa1100_register_pcmcia(&assabet_pcmcia_ops); +#ifndef CONFIG_ASSABET_NEPONSET + else + printk(KERN_ERR "Card Services disabled: missing " + "Neponset support\n"); +#endif + } + return ret; } -struct pcmcia_low_level assabet_pcmcia_ops = { - assabet_pcmcia_init, - assabet_pcmcia_shutdown, - assabet_pcmcia_socket_state, - assabet_pcmcia_get_irq_info, - assabet_pcmcia_configure_socket -}; +void __exit pcmcia_assabet_exit(void) +{ + sa1100_unregister_pcmcia(&assabet_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_badge4.c b/drivers/pcmcia/sa1100_badge4.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pcmcia/sa1100_badge4.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,186 @@ +/* + * linux/drivers/pcmcia/sa1100_badge4.c + * + * BadgePAD 4 PCMCIA specific routines + * + * Christopher Hoover + * + * Copyright (C) 2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include + +#include +#include +#include + +#include "sa1100_generic.h" +#include "sa1111_generic.h" + +/* + * BadgePAD 4 Details + * + * PCM Vcc: + * + * PCM Vcc on BadgePAD 4 can be jumpered for 3.3V (short pins 1 and 3 + * on JP6) or 5V (short pins 3 and 5 on JP6). N.B., 5V supply rail + * is enabled by the SA-1110's BADGE4_GPIO_PCMEN5V (GPIO 24). + * + * PCM Vpp: + * + * PCM Vpp on BadgePAD 4 can be jumpered for 12V (short pins 2 and 4 + * on JP6) or tied to PCM Vcc (short pins 4 and 6 on JP6). N.B., 12V + * operation requires that the power supply actually supply 12V. + * + * CF Vcc: + * + * CF Vcc on BadgePAD 4 can be jumpered either for 3.3V (short pins 1 + * and 2 on JP10) or 5V (short pins 2 and 3 on JP10). The note above + * about the 5V supply rail applies. + * + * There's no way programmatically to determine how a given board is + * jumpered. This code assumes a default jumpering: 5V PCM Vcc (pins + * 3 and 5 shorted) and PCM Vpp = PCM Vcc (pins 4 and 6 shorted) and + * no jumpering for CF Vcc. If this isn't correct, Override these + * defaults with a pcmv setup argument: pcmv=,,. E.g. pcmv=33,120,50 indicates 3.3V PCM Vcc, 12.0V PCM Vpp, + * and 5.0V CF Vcc. + * + */ + +static int badge4_pcmvcc = 50; +static int badge4_pcmvpp = 50; +static int badge4_cfvcc = 0; + +static int badge4_pcmcia_init(struct pcmcia_init *init) +{ + printk(KERN_INFO __FUNCTION__ + ": badge4_pcmvcc=%d, badge4_pcmvpp=%d, badge4_cfvcc=%d\n", + badge4_pcmvcc, badge4_pcmvpp, badge4_cfvcc); + + return sa1111_pcmcia_init(init); +} + +static int badge4_pcmcia_shutdown(void) +{ + int rc = sa1111_pcmcia_shutdown(); + + /* be sure to disable 5V use */ + badge4_set_5V(BADGE4_5V_PCMCIA_SOCK0, 0); + badge4_set_5V(BADGE4_5V_PCMCIA_SOCK1, 0); + + return rc; +} + +static void complain_about_jumpering(const char *whom, + const char *supply, + int given, int wanted) +{ + printk(KERN_ERR + "%s: %s %d.%dV wanted but board is jumpered for %s %d.%dV operation" + "; re-jumper the board and/or use pcmv=xx,xx,xx\n", + whom, supply, + wanted / 10, wanted % 10, + supply, + given / 10, given % 10); +} + +static unsigned badge4_need_5V_bitmap = 0; + +static int +badge4_pcmcia_configure_socket(const struct pcmcia_configure *conf) +{ + int ret; + + switch (conf->sock) { + case 0: + if ((conf->vcc != 0) && + (conf->vcc != badge4_pcmvcc)) { + complain_about_jumpering(__FUNCTION__, "pcmvcc", + badge4_pcmvcc, conf->vcc); + return -1; + } + if ((conf->vpp != 0) && + (conf->vpp != badge4_pcmvpp)) { + complain_about_jumpering(__FUNCTION__, "pcmvpp", + badge4_pcmvpp, conf->vpp); + return -1; + } + break; + + case 1: + if ((conf->vcc != 0) && + (conf->vcc != badge4_cfvcc)) { + complain_about_jumpering(__FUNCTION__, "cfvcc", + badge4_cfvcc, conf->vcc); + return -1; + } + break; + + default: + return -1; + } + + ret = sa1111_pcmcia_configure_socket(conf); + if (ret == 0) { + unsigned long flags; + int need5V; + + local_irq_save(flags); + + need5V = ((conf->vcc == 50) || (conf->vpp == 50)); + + badge4_set_5V(BADGE4_5V_PCMCIA_SOCK(conf->sock), need5V); + + local_irq_restore(flags); + } + + return 0; +} + +static struct pcmcia_low_level badge4_pcmcia_ops = { + init: badge4_pcmcia_init, + shutdown: badge4_pcmcia_shutdown, + socket_state: sa1111_pcmcia_socket_state, + get_irq_info: sa1111_pcmcia_get_irq_info, + configure_socket: badge4_pcmcia_configure_socket, + + socket_init: sa1111_pcmcia_socket_init, + socket_suspend: sa1111_pcmcia_socket_suspend, +}; + +int __init pcmcia_badge4_init(void) +{ + int ret = -ENODEV; + + if (machine_is_badge4()) + ret = sa1100_register_pcmcia(&badge4_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_badge4_exit(void) +{ + sa1100_unregister_pcmcia(&badge4_pcmcia_ops); +} + +static int __init pcmv_setup(char *s) +{ + int v[4]; + + s = get_options(s, ARRAY_SIZE(v), v); + + if (v[0] >= 1) badge4_pcmvcc = v[1]; + if (v[0] >= 2) badge4_pcmvpp = v[2]; + if (v[0] >= 3) badge4_cfvcc = v[3]; + + return 1; +} + +__setup("pcmv=", pcmv_setup); diff -Nru a/drivers/pcmcia/sa1100_cerf.c b/drivers/pcmcia/sa1100_cerf.c --- a/drivers/pcmcia/sa1100_cerf.c Thu Mar 7 18:17:46 2002 +++ b/drivers/pcmcia/sa1100_cerf.c Thu Mar 7 18:17:46 2002 @@ -5,45 +5,62 @@ * Based off the Assabet. * */ +#include #include #include +#include #include #include -#include +#include "sa1100_generic.h" +#ifdef CONFIG_SA1100_CERF_CPLD +#define CERF_SOCKET 0 +#else +#define CERF_SOCKET 1 +#endif -static int cerf_pcmcia_init(struct pcmcia_init *init){ - int irq, res; +static struct irqs { + int irq; + const char *str; +} irqs[] = { + { IRQ_GPIO_CF_CD, "CF_CD" }, + { IRQ_GPIO_CF_BVD2, "CF_BVD2" }, + { IRQ_GPIO_CF_BVD1, "CF_BVD1" } +}; + +static int cerf_pcmcia_init(struct pcmcia_init *init) +{ + int i, res; - GPDR &= ~(GPIO_CF_CD | GPIO_CF_BVD2 | GPIO_CF_BVD1 | GPIO_CF_IRQ); - GPDR |= (GPIO_CF_RESET); + set_irq_type(IRQ_GPIO_CF_IRQ, IRQT_FALLING); - set_GPIO_IRQ_edge( GPIO_CF_CD|GPIO_CF_BVD2|GPIO_CF_BVD1, GPIO_BOTH_EDGES ); - set_GPIO_IRQ_edge( GPIO_CF_IRQ, GPIO_FALLING_EDGE ); - - irq = IRQ_GPIO_CF_CD; - res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_CD", NULL ); - if( res < 0 ) goto irq_err; - irq = IRQ_GPIO_CF_BVD2; - res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD2", NULL ); - if( res < 0 ) goto irq_err; - irq = IRQ_GPIO_CF_BVD1; - res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD1", NULL ); - if( res < 0 ) goto irq_err; + for (i = 0; i < ARRAY_SIZE(irqs); i++) { + set_irq_type(irqs[i].irq, IRQT_NOEDGE); + res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT, + irqs[i].str, NULL); + if (res) + goto irq_err; + } return 2; -irq_err: - printk( KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq ); - return -1; + irq_err: + printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n", + __FUNCTION__, irqs[i].irq, res); + + while (i--) + free_irq(irqs[i].irq, NULL); + + return res; } static int cerf_pcmcia_shutdown(void) { - free_irq( IRQ_GPIO_CF_CD, NULL ); - free_irq( IRQ_GPIO_CF_BVD2, NULL ); - free_irq( IRQ_GPIO_CF_BVD1, NULL ); + int i; + + for (i = 0; i < ARRAY_SIZE(irqs); i++) + free_irq(irqs[i].irq, NULL); return 0; } @@ -51,31 +68,18 @@ static int cerf_pcmcia_socket_state(struct pcmcia_state_array *state_array){ unsigned long levels; -#ifdef CONFIG_SA1100_CERF_CPLD - int i = 0; -#else - int i = 1; -#endif + int i = CERF_SOCKET; if(state_array->size<2) return -1; - memset(state_array->state, 0, - (state_array->size)*sizeof(struct pcmcia_state)); - levels=GPLR; state_array->state[i].detect=((levels & GPIO_CF_CD)==0)?1:0; - state_array->state[i].ready=(levels & GPIO_CF_IRQ)?1:0; - state_array->state[i].bvd1=(levels & GPIO_CF_BVD1)?1:0; - state_array->state[i].bvd2=(levels & GPIO_CF_BVD2)?1:0; - state_array->state[i].wrprot=0; - state_array->state[i].vs_3v=1; - state_array->state[i].vs_Xv=0; return 1; @@ -85,11 +89,7 @@ if(info->sock>1) return -1; -#ifdef CONFIG_SA1100_CERF_CPLD - if(info->sock==0) -#else - if(info->sock==1) -#endif + if (info->sock == CERF_SOCKET) info->irq=IRQ_GPIO_CF_IRQ; return 0; @@ -98,20 +98,12 @@ static int cerf_pcmcia_configure_socket(const struct pcmcia_configure *configure) { - unsigned long flags; - if(configure->sock>1) return -1; -#ifdef CONFIG_SA1100_CERF_CPLD - if(configure->sock==1) -#else - if(configure->sock==0) -#endif + if (configure->sock != CERF_SOCKET) return 0; - save_flags_cli(flags); - switch(configure->vcc){ case 0: break; @@ -119,43 +111,76 @@ case 50: case 33: #ifdef CONFIG_SA1100_CERF_CPLD - GPDR |= GPIO_PWR_SHUTDOWN; - GPCR |= GPIO_PWR_SHUTDOWN; + GPCR = GPIO_PWR_SHUTDOWN; #endif break; default: printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, configure->vcc); - restore_flags(flags); return -1; } if(configure->reset) { #ifdef CONFIG_SA1100_CERF_CPLD - GPDR |= GPIO_CF_RESET; - GPSR |= GPIO_CF_RESET; + GPSR = GPIO_CF_RESET; #endif } else { #ifdef CONFIG_SA1100_CERF_CPLD - GPDR |= GPIO_CF_RESET; - GPCR |= GPIO_CF_RESET; + GPCR = GPIO_CF_RESET; #endif } - restore_flags(flags); + return 0; +} + +static int cerf_pcmcia_socket_init(int sock) +{ + int i; + + if (sock == CERF_SOCKET) + for (i = 0; i < ARRAY_SIZE(irqs); i++) + set_irq_type(irqs[i].irq, IRQT_BOTHEDGE); return 0; } -struct pcmcia_low_level cerf_pcmcia_ops = { - cerf_pcmcia_init, - cerf_pcmcia_shutdown, - cerf_pcmcia_socket_state, - cerf_pcmcia_get_irq_info, - cerf_pcmcia_configure_socket +static int cerf_pcmcia_socket_suspend(int sock) +{ + int i; + + if (sock == CERF_SOCKET) + for (i = 0; i < ARRAY_SIZE(irqs); i++) + set_irq_type(irqs[i].irq, IRQT_NOEDGE); + + return 0; +} + +static struct pcmcia_low_level cerf_pcmcia_ops = { + init: cerf_pcmcia_init, + shutdown: cerf_pcmcia_shutdown, + socket_state: cerf_pcmcia_socket_state, + get_irq_info: cerf_pcmcia_get_irq_info, + configure_socket: cerf_pcmcia_configure_socket, + + socket_init: cerf_pcmcia_socket_init, + socket_suspend: cerf_pcmcia_socket_suspend, }; +int __init pcmcia_cerf_init(void) +{ + int ret = -ENODEV; + + if (machine_is_cerf()) + ret = sa1100_register_pcmcia(&cerf_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_cerf_exit(void) +{ + sa1100_unregister_pcmcia(&cerf_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_flexanet.c b/drivers/pcmcia/sa1100_flexanet.c --- a/drivers/pcmcia/sa1100_flexanet.c Thu Mar 7 18:17:44 2002 +++ b/drivers/pcmcia/sa1100_flexanet.c Thu Mar 7 18:17:44 2002 @@ -4,16 +4,25 @@ * PCMCIA implementation routines for Flexanet. * by Jordi Colomer, 09/05/2001 * - * Yet to be defined. */ #include #include +#include #include #include -#include +#include "sa1100_generic.h" +static struct { + int irq; + const char *name; +} irqs[] = { + { IRQ_GPIO_CF1_CD, "CF1_CD" }, + { IRQ_GPIO_CF1_BVD1, "CF1_BVD1" }, + { IRQ_GPIO_CF2_CD, "CF2_CD" }, + { IRQ_GPIO_CF2_BVD1, "CF2_BVD1" } +}; /* * Socket initialization. @@ -22,9 +31,37 @@ * Must return the number of slots. * */ -static int flexanet_pcmcia_init(struct pcmcia_init *init){ +static int flexanet_pcmcia_init(struct pcmcia_init *init) +{ + int i, res; - return 0; + /* Configure the GPIOs as inputs (BVD2 is not implemented) */ + GPDR &= ~(GPIO_CF1_NCD | GPIO_CF1_BVD1 | GPIO_CF1_IRQ | + GPIO_CF2_NCD | GPIO_CF2_BVD1 | GPIO_CF2_IRQ ); + + /* Set IRQ edge */ + set_irq_type(IRQ_GPIO_CF1_IRQ, IRQT_FALLING); + set_irq_type(IRQ_GPIO_CF2_IRQ, IRQT_FALLING); + + /* Register the socket interrupts (not the card interrupts) */ + for (i = 0; i < ARRAY_SIZE(irqs); i++) { + set_irq_type(irqs[i].irq, IRQT_NOEDGE); + res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT, + irqs[i].name, NULL); + if (res < 0) + break; + } + + /* If we failed, then free all interrupts requested thus far. */ + if (res < 0) { + printk(KERN_ERR "%s: request for IRQ%d failed: %d\n", + __FUNCTION__, irqs[i].irq, res); + while (i--) + free_irq(irqs[i].irq, NULL); + return res; + } + + return 2; } @@ -34,6 +71,12 @@ */ static int flexanet_pcmcia_shutdown(void) { + int i; + + /* disable IRQs */ + for (i = 0; i < ARRAY_SIZE(irqs); i++) + free_irq(irqs[i].irq, NULL); + return 0; } @@ -46,7 +89,33 @@ */ static int flexanet_pcmcia_socket_state(struct pcmcia_state_array *state_array){ - return -1; + unsigned long levels; + + if (state_array->size < 2) + return -1; + + /* Sense the GPIOs, asynchronously */ + levels = GPLR; + + /* Socket 0 */ + state_array->state[0].detect = ((levels & GPIO_CF1_NCD)==0)?1:0; + state_array->state[0].ready = (levels & GPIO_CF1_IRQ)?1:0; + state_array->state[0].bvd1 = (levels & GPIO_CF1_BVD1)?1:0; + state_array->state[0].bvd2 = 1; + state_array->state[0].wrprot = 0; + state_array->state[0].vs_3v = 1; + state_array->state[0].vs_Xv = 0; + + /* Socket 1 */ + state_array->state[1].detect = ((levels & GPIO_CF2_NCD)==0)?1:0; + state_array->state[1].ready = (levels & GPIO_CF2_IRQ)?1:0; + state_array->state[1].bvd1 = (levels & GPIO_CF2_BVD1)?1:0; + state_array->state[1].bvd2 = 1; + state_array->state[1].wrprot = 0; + state_array->state[1].vs_3v = 1; + state_array->state[1].vs_Xv = 0; + + return 1; } @@ -56,7 +125,16 @@ */ static int flexanet_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ - return -1; + /* check the socket index */ + if (info->sock > 1) + return -1; + + if (info->sock == 0) + info->irq = IRQ_GPIO_CF1_IRQ; + else if (info->sock == 1) + info->irq = IRQ_GPIO_CF2_IRQ; + + return 0; } @@ -66,19 +144,105 @@ static int flexanet_pcmcia_configure_socket(const struct pcmcia_configure *configure) { - return -1; + unsigned long value, flags, mask; + + + if (configure->sock > 1) + return -1; + + /* Ignore the VCC level since it is 3.3V and always on */ + switch (configure->vcc) + { + case 0: + printk(KERN_WARNING "%s(): CS asked to power off.\n", __FUNCTION__); + break; + + case 50: + printk(KERN_WARNING "%s(): CS asked for 5V, applying 3.3V...\n", + __FUNCTION__); + + case 33: + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + return -1; + } + + /* Reset the slot(s) using the controls in the BCR */ + mask = 0; + + switch (configure->sock) + { + case 0 : mask = FHH_BCR_CF1_RST; break; + case 1 : mask = FHH_BCR_CF2_RST; break; + } + + local_irq_save(flags); + + value = flexanet_BCR; + value = (configure->reset) ? (value | mask) : (value & ~mask); + FHH_BCR = flexanet_BCR = value; + + local_irq_restore(flags); + + return 0; } +static int flexanet_pcmcia_socket_init(int sock) +{ + if (sock == 0) { + set_irq_type(IRQ_GPIO_CF1_CD, IRQT_BOTHEDGE); + set_irq_type(IRQ_GPIO_CF1_BVD1, IRQT_BOTHEDGE); + } else if (sock == 1) { + set_irq_type(IRQ_GPIO_CF2_CD, IRQT_BOTHEDGE); + set_irq_type(IRQ_GPIO_CF2_BVD1, IRQT_BOTHEDGE); + } + + return 0; +} + +static int flexanet_pcmcia_socket_suspend(int sock) +{ + if (sock == 0) { + set_irq_type(IRQ_GPIO_CF1_CD, IRQT_NOEDGE); + set_irq_type(IRQ_GPIO_CF1_BVD1, IRQT_NOEDGE); + } else if (sock == 1) { + set_irq_type(IRQ_GPIO_CF2_CD, IRQT_NOEDGE); + set_irq_type(IRQ_GPIO_CF2_BVD1, IRQT_NOEDGE); + } + + return 0; +} /* * The set of socket operations * */ -struct pcmcia_low_level flexanet_pcmcia_ops = { - flexanet_pcmcia_init, - flexanet_pcmcia_shutdown, - flexanet_pcmcia_socket_state, - flexanet_pcmcia_get_irq_info, - flexanet_pcmcia_configure_socket +static struct pcmcia_low_level flexanet_pcmcia_ops = { + init: flexanet_pcmcia_init, + shutdown: flexanet_pcmcia_shutdown, + socket_state: flexanet_pcmcia_socket_state, + get_irq_info: flexanet_pcmcia_get_irq_info, + configure_socket: flexanet_pcmcia_configure_socket, + + socket_init: flexanet_pcmcia_socket_init, + socket_suspend: flexanet_pcmcia_socket_suspend, }; + +int __init pcmcia_flexanet_init(void) +{ + int ret = -ENODEV; + + if (machine_is_flexanet()) + ret = sa1100_register_pcmcia(&flexanet_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_flexanet_exit(void) +{ + sa1100_unregister_pcmcia(&flexanet_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_freebird.c b/drivers/pcmcia/sa1100_freebird.c --- a/drivers/pcmcia/sa1100_freebird.c Thu Mar 7 18:17:37 2002 +++ b/drivers/pcmcia/sa1100_freebird.c Thu Mar 7 18:17:37 2002 @@ -6,14 +6,22 @@ */ #include #include +#include #include #include -#include +#include "sa1100_generic.h" +static struct irqs { + int irq; + const char *str; +} irqs[] = { + { IRQ_GPIO_FREEBIRD_CF_CD, "CF_CD" }, + { IRQ_GPIO_FREEBIRD_CF_BVD, "CF_BVD1" }, +}; static int freebird_pcmcia_init(struct pcmcia_init *init){ - int irq, res; + int i, res; /* Enable Linkup CF card */ LINKUP_PRC = 0xc0; @@ -26,37 +34,38 @@ mdelay(100); LINKUP_PRC = 0xc0; - /* All those are inputs */ - ////GPDR &= ~(GPIO_CF_CD | GPIO_CF_BVD2 | GPIO_CF_BVD1 | GPIO_CF_IRQ); - GPDR &= ~(GPIO_FREEBIRD_CF_CD | GPIO_FREEBIRD_CF_IRQ | GPIO_FREEBIRD_CF_BVD); - /* Set transition detect */ - //set_GPIO_IRQ_edge( GPIO_CF_CD|GPIO_CF_BVD2|GPIO_CF_BVD1, GPIO_BOTH_EDGES ); - //set_GPIO_IRQ_edge( GPIO_CF_IRQ, GPIO_FALLING_EDGE ); - set_GPIO_IRQ_edge(GPIO_FREEBIRD_CF_CD|GPIO_FREEBIRD_CF_BVD,GPIO_BOTH_EDGES); - set_GPIO_IRQ_edge(GPIO_FREEBIRD_CF_IRQ, GPIO_FALLING_EDGE); + set_irq_type(IRQ_GPIO_FREEBIRD_CF_IRQ, IRQT_FALLING); /* Register interrupts */ - irq = IRQ_GPIO_FREEBIRD_CF_CD; - res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_CD", NULL ); - if( res < 0 ) goto irq_err; - irq = IRQ_GPIO_FREEBIRD_CF_BVD; - res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD1", NULL ); - if( res < 0 ) goto irq_err; + for (i = 0; i < ARRAY_SIZE(irqs); i++) { + set_irq_type(irqs[i].irq, IRQT_NOEDGE); + res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT, + irqs[i].str, NULL); + if (res) + goto irq_err; + } /* There's only one slot, but it's "Slot 1": */ return 2; irq_err: - printk( KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq ); - return -1; + printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n", + __FUNCTION__, irqs[i].irq, res); + + while (i--) + free_irq(irqs[i].irq, NULL); + + return res; } static int freebird_pcmcia_shutdown(void) { + int i; + /* disable IRQs */ - free_irq( IRQ_GPIO_FREEBIRD_CF_CD, NULL ); - free_irq( IRQ_GPIO_FREEBIRD_CF_BVD, NULL ); + for (i = 0; i < ARRAY_SIZE(irqs); i++) + free_irq(irqs[i].irq, NULL); /* Disable CF card */ LINKUP_PRC = 0x40; /* SSP=1 SOE=0 */ @@ -75,7 +84,7 @@ (state_array->size)*sizeof(struct pcmcia_state)); levels = LINKUP_PRS; -//printk("LINKUP_PRS=%x \n",levels); +//printk("LINKUP_PRS=%x\n",levels); state_array->state[0].detect= ((levels & (LINKUP_CD1 | LINKUP_CD2))==0)?1:0; @@ -114,7 +123,7 @@ if(configure->sock==1) return 0; - save_flags_cli(flags); + local_irq_save(flags); value = 0xc0; /* SSP=1 SOE=1 CFE=1 */ @@ -134,7 +143,7 @@ default: printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, configure->vcc); - restore_flags(flags); + local_irq_restore(flags); return -1; } @@ -145,16 +154,51 @@ LINKUP_PRC = value; //printk("LINKUP_PRC=%x\n",value); - restore_flags(flags); + local_irq_restore(flags); + + return 0; +} + +static int freebird_pcmcia_socket_init(int sock) +{ + if (sock == 1) { + set_irq_type(IRQ_GPIO_FREEBIRD_CF_CD, IRQT_BOTHEDGE); + set_irq_type(IRQ_GPIO_FREEBIRD_CF_BVD, IRQT_BOTHEDGE); + } + return 0; +} +static int freebird_pcmcia_socket_suspend(int sock) +{ + if (sock == 1) { + set_irq_type(IRQ_GPIO_FREEBIRD_CF_CD, IRQT_NOEDGE); + set_irq_type(IRQ_GPIO_FREEBIRD_CF_BVD, IRQT_NOEDGE); + } return 0; } -struct pcmcia_low_level freebird_pcmcia_ops = { - freebird_pcmcia_init, - freebird_pcmcia_shutdown, - freebird_pcmcia_socket_state, - freebird_pcmcia_get_irq_info, - freebird_pcmcia_configure_socket +static struct pcmcia_low_level freebird_pcmcia_ops = { + init: freebird_pcmcia_init, + shutdown: freebird_pcmcia_shutdown, + socket_state: freebird_pcmcia_socket_state, + get_irq_info: freebird_pcmcia_get_irq_info, + configure_socket: freebird_pcmcia_configure_socket, + + socket_init: freebird_pcmcia_socket_init, + socket_suspend: freebird_pcmcia_socket_suspend, }; +int __init pcmcia_freebird_init(void) +{ + int ret = -ENODEV; + + if (machine_is_freebird()) + ret = sa1100_register_pcmcia(&freebird_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_freebird_exit(void) +{ + sa1100_unregister_pcmcia(&freebird_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c --- a/drivers/pcmcia/sa1100_generic.c Thu Mar 7 18:17:37 2002 +++ b/drivers/pcmcia/sa1100_generic.c Thu Mar 7 18:17:37 2002 @@ -29,6 +29,10 @@ file under either the MPL or the GPL. ======================================================================*/ +/* + * Please see linux/Documentation/arm/SA1100/PCMCIA for more information + * on the low-level kernel interface. + */ #include #include @@ -43,6 +47,7 @@ #include #include #include +#include #include #include @@ -62,338 +67,94 @@ static int pc_debug; #endif -MODULE_AUTHOR("John Dorsey "); -MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-1100 Socket Controller"); - /* This structure maintains housekeeping state for each socket, such * as the last known values of the card detect pins, or the Card Services * callback value associated with the socket: */ -static struct sa1100_pcmcia_socket -sa1100_pcmcia_socket[SA1100_PCMCIA_MAX_SOCK]; - static int sa1100_pcmcia_socket_count; +static struct sa1100_pcmcia_socket sa1100_pcmcia_socket[SA1100_PCMCIA_MAX_SOCK]; +#define PCMCIA_SOCKET(x) (sa1100_pcmcia_socket + (x)) /* Returned by the low-level PCMCIA interface: */ static struct pcmcia_low_level *pcmcia_low_level; -/* Event poll timer structure */ static struct timer_list poll_timer; - - -/* Prototypes for routines which are used internally: */ - -static int sa1100_pcmcia_driver_init(void); -static void sa1100_pcmcia_driver_shutdown(void); -static void sa1100_pcmcia_task_handler(void *data); -static void sa1100_pcmcia_poll_event(unsigned long data); -static void sa1100_pcmcia_interrupt(int irq, void *dev, - struct pt_regs *regs); static struct tq_struct sa1100_pcmcia_task; -#ifdef CONFIG_PROC_FS -static int sa1100_pcmcia_proc_status(char *buf, char **start, off_t pos, - int count, int *eof, void *data); -#endif - - -/* Prototypes for operations which are exported to the - * new-and-impr^H^H^H^H^H^H^H^H^H^H in-kernel PCMCIA core: +/* + * sa1100_pcmcia_state_to_config + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * + * Convert PCMCIA socket state to our socket configure structure. */ +static struct pcmcia_configure +sa1100_pcmcia_state_to_config(unsigned int sock, socket_state_t *state) +{ + struct pcmcia_configure conf; -static int sa1100_pcmcia_init(unsigned int sock); -static int sa1100_pcmcia_suspend(unsigned int sock); -static int sa1100_pcmcia_register_callback(unsigned int sock, - void (*handler)(void *, - unsigned int), - void *info); -static int sa1100_pcmcia_inquire_socket(unsigned int sock, - socket_cap_t *cap); -static int sa1100_pcmcia_get_status(unsigned int sock, u_int *value); -static int sa1100_pcmcia_get_socket(unsigned int sock, - socket_state_t *state); -static int sa1100_pcmcia_set_socket(unsigned int sock, - socket_state_t *state); -static int sa1100_pcmcia_get_io_map(unsigned int sock, - struct pccard_io_map *io); -static int sa1100_pcmcia_set_io_map(unsigned int sock, - struct pccard_io_map *io); -static int sa1100_pcmcia_get_mem_map(unsigned int sock, - struct pccard_mem_map *mem); -static int sa1100_pcmcia_set_mem_map(unsigned int sock, - struct pccard_mem_map *mem); -#ifdef CONFIG_PROC_FS -static void sa1100_pcmcia_proc_setup(unsigned int sock, - struct proc_dir_entry *base); -#endif - -static struct pccard_operations sa1100_pcmcia_operations = { - sa1100_pcmcia_init, - sa1100_pcmcia_suspend, - sa1100_pcmcia_register_callback, - sa1100_pcmcia_inquire_socket, - sa1100_pcmcia_get_status, - sa1100_pcmcia_get_socket, - sa1100_pcmcia_set_socket, - sa1100_pcmcia_get_io_map, - sa1100_pcmcia_set_io_map, - sa1100_pcmcia_get_mem_map, - sa1100_pcmcia_set_mem_map, -#ifdef CONFIG_PROC_FS - sa1100_pcmcia_proc_setup -#endif -}; - -#ifdef CONFIG_CPU_FREQ -/* forward declaration */ -static struct notifier_block sa1100_pcmcia_notifier_block; -#endif + conf.sock = sock; + conf.vcc = state->Vcc; + conf.vpp = state->Vpp; + conf.output = state->flags & SS_OUTPUT_ENA ? 1 : 0; + conf.speaker = state->flags & SS_SPKR_ENA ? 1 : 0; + conf.reset = state->flags & SS_RESET ? 1 : 0; + conf.irq = state->io_irq != 0; + return conf; +} -/* sa1100_pcmcia_driver_init() - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * - * This routine performs a basic sanity check to ensure that this - * kernel has been built with the appropriate board-specific low-level - * PCMCIA support, performs low-level PCMCIA initialization, registers - * this socket driver with Card Services, and then spawns the daemon - * thread which is the real workhorse of the socket driver. +/* sa1100_pcmcia_sock_init() + * ^^^^^^^^^^^^^^^^^^^^^^^^^ * - * Please see linux/Documentation/arm/SA1100/PCMCIA for more information - * on the low-level kernel interface. + * (Re-)Initialise the socket, turning on status interrupts + * and PCMCIA bus. This must wait for power to stabilise + * so that the card status signals report correctly. * - * Returns: 0 on success, -1 on error - */ -static int __init sa1100_pcmcia_driver_init(void){ - servinfo_t info; - struct pcmcia_init pcmcia_init; - struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK]; - struct pcmcia_state_array state_array; - unsigned int i, clock; - unsigned long mecr; - - printk(KERN_INFO "SA-1100 PCMCIA (CS release %s)\n", CS_RELEASE); - - CardServices(GetCardServicesInfo, &info); - - if(info.Revision!=CS_RELEASE_CODE){ - printk(KERN_ERR "Card Services release codes do not match\n"); - return -1; - } - - if(machine_is_assabet()){ -#ifdef CONFIG_SA1100_ASSABET - if(machine_has_neponset()){ -#ifdef CONFIG_ASSABET_NEPONSET - pcmcia_low_level=&neponset_pcmcia_ops; -#else - printk(KERN_ERR "Card Services disabled: missing Neponset support\n"); - return -1; -#endif - }else{ - pcmcia_low_level=&assabet_pcmcia_ops; - } -#endif - } else if (machine_is_freebird()) { -#ifdef CONFIG_SA1100_FREEBIRD - pcmcia_low_level = &freebird_pcmcia_ops; -#endif - } else if (machine_is_h3600()) { -#ifdef CONFIG_SA1100_H3600 - pcmcia_low_level = &h3600_pcmcia_ops; -#endif - } else if (machine_is_cerf()) { -#ifdef CONFIG_SA1100_CERF - pcmcia_low_level = &cerf_pcmcia_ops; -#endif - } else if (machine_is_graphicsclient()) { -#ifdef CONFIG_SA1100_GRAPHICSCLIENT - pcmcia_low_level = &gcplus_pcmcia_ops; -#endif - } else if (machine_is_xp860()) { -#ifdef CONFIG_SA1100_XP860 - pcmcia_low_level = &xp860_pcmcia_ops; -#endif - } else if (machine_is_yopy()) { -#ifdef CONFIG_SA1100_YOPY - pcmcia_low_level = &yopy_pcmcia_ops; -#endif - } else if (machine_is_pangolin()) { -#ifdef CONFIG_SA1100_PANGOLIN - pcmcia_low_level = &pangolin_pcmcia_ops; -#endif - } else if (machine_is_jornada720()) { -#ifdef CONFIG_SA1100_JORNADA720 - pcmcia_low_level = &jornada720_pcmcia_ops; -#endif - } else if(machine_is_pfs168()){ -#ifdef CONFIG_SA1100_PFS168 - pcmcia_low_level=&pfs168_pcmcia_ops; -#endif - } else if(machine_is_flexanet()){ -#ifdef CONFIG_SA1100_FLEXANET - pcmcia_low_level=&flexanet_pcmcia_ops; -#endif - } else if(machine_is_simpad()){ -#ifdef CONFIG_SA1100_SIMPAD - pcmcia_low_level=&simpad_pcmcia_ops; -#endif - } else if(machine_is_graphicsmaster()) { -#ifdef CONFIG_SA1100_GRAPHICSMASTER - pcmcia_low_level=&graphicsmaster_pcmcia_ops; -#endif - } else if(machine_is_adsbitsy()) { -#ifdef CONFIG_SA1100_ADSBITSY - pcmcia_low_level=&adsbitsy_pcmcia_ops; -#endif - } else if(machine_is_stork()) { -#ifdef CONFIG_SA1100_STORK - pcmcia_low_level=&stork_pcmcia_ops; -#endif - } - - if (!pcmcia_low_level) { - printk(KERN_ERR "This hardware is not supported by the SA1100 Card Service driver\n"); - return -ENODEV; - } - - pcmcia_init.handler=sa1100_pcmcia_interrupt; - - if((sa1100_pcmcia_socket_count=pcmcia_low_level->init(&pcmcia_init))<0){ - printk(KERN_ERR "Unable to initialize kernel PCMCIA service.\n"); - return -EIO; - } - - state_array.size=sa1100_pcmcia_socket_count; - state_array.state=state; - - if(pcmcia_low_level->socket_state(&state_array)<0){ - printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n"); - return -EIO; - } - - /* We initialize the MECR to default values here, because we are - * not guaranteed to see a SetIOMap operation at runtime. - */ - mecr=0; - - clock = get_cclk_frequency() * 100; - - for(i=0; ishutdown(); - flush_scheduled_tasks(); +static int sa1100_pcmcia_sock_init(unsigned int sock) +{ + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + struct pcmcia_configure conf; - DEBUG(1, "sa1100: shutdown complete\n"); -} + DEBUG(2, "%s(): initializing socket %u\n", __FUNCTION__, sock); -module_exit(sa1100_pcmcia_driver_shutdown); + skt->cs_state = dead_socket; + conf = sa1100_pcmcia_state_to_config(sock, &dead_socket); -/* sa1100_pcmcia_init() - * ^^^^^^^^^^^^^^^^^^^^ - * We perform all of the interesting initialization tasks in - * sa1100_pcmcia_driver_init(). - * - * Returns: 0 - */ -static int sa1100_pcmcia_init(unsigned int sock){ - - DEBUG(2, "%s(): initializing socket %u\n", __FUNCTION__, sock); + pcmcia_low_level->configure_socket(&conf); - return 0; + return pcmcia_low_level->socket_init(sock); } -/* sa1100_pcmcia_suspend() +/* + * sa1100_pcmcia_suspend() * ^^^^^^^^^^^^^^^^^^^^^^^ - * We don't currently perform any actions on a suspend. + * + * Remove power on the socket, disable IRQs from the card. + * Turn off status interrupts, and disable the PCMCIA bus. * * Returns: 0 */ static int sa1100_pcmcia_suspend(unsigned int sock) { + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); struct pcmcia_configure conf; int ret; DEBUG(2, "%s(): suspending socket %u\n", __FUNCTION__, sock); - conf.sock = sock; - conf.vcc = 0; - conf.vpp = 0; - conf.output = 0; - conf.speaker = 0; - conf.reset = 1; + conf = sa1100_pcmcia_state_to_config(sock, &dead_socket); ret = pcmcia_low_level->configure_socket(&conf); - if (ret == 0) - sa1100_pcmcia_socket[sock].cs_state = dead_socket; + if (ret == 0) { + skt->cs_state = dead_socket; + ret = pcmcia_low_level->socket_suspend(sock); + } return ret; } @@ -407,52 +168,50 @@ * * Returns: an event mask for the given socket state. */ -static inline unsigned sa1100_pcmcia_events(struct pcmcia_state *state, - struct pcmcia_state *prev_state, - unsigned int mask, - unsigned int flags){ - unsigned int events=0; - - if(state->detect!=prev_state->detect){ +static inline unsigned int +sa1100_pcmcia_events(struct pcmcia_state *state, + struct pcmcia_state *prev_state, + unsigned int mask, unsigned int flags) +{ + unsigned int events = 0; - DEBUG(2, "%s(): card detect value %u\n", __FUNCTION__, state->detect); + if (state->detect != prev_state->detect) { + DEBUG(3, "%s(): card detect value %u\n", __FUNCTION__, state->detect); - events|=mask&SS_DETECT; + events |= SS_DETECT; } - if(state->ready!=prev_state->ready){ - - DEBUG(2, "%s(): card ready value %u\n", __FUNCTION__, state->ready); + if (state->ready != prev_state->ready) { + DEBUG(3, "%s(): card ready value %u\n", __FUNCTION__, state->ready); - events|=mask&((flags&SS_IOCARD)?0:SS_READY); + events |= flags & SS_IOCARD ? 0 : SS_READY; } - if(state->bvd1!=prev_state->bvd1){ - - DEBUG(2, "%s(): card BVD1 value %u\n", __FUNCTION__, state->bvd1); + if (state->bvd1 != prev_state->bvd1) { + DEBUG(3, "%s(): card BVD1 value %u\n", __FUNCTION__, state->bvd1); - events|=mask&(flags&SS_IOCARD)?SS_STSCHG:SS_BATDEAD; + events |= flags & SS_IOCARD ? SS_STSCHG : SS_BATDEAD; } - if(state->bvd2!=prev_state->bvd2){ + if (state->bvd2 != prev_state->bvd2) { + DEBUG(3, "%s(): card BVD2 value %u\n", __FUNCTION__, state->bvd2); - DEBUG(2, "%s(): card BVD2 value %u\n", __FUNCTION__, state->bvd2); - - events|=mask&(flags&SS_IOCARD)?0:SS_BATWARN; + events |= flags & SS_IOCARD ? 0 : SS_BATWARN; } - DEBUG(2, "events: %s%s%s%s%s%s\n", - (events==0)?"":"", - (events&SS_DETECT)?"DETECT ":"", - (events&SS_READY)?"READY ":"", - (events&SS_BATDEAD)?"BATDEAD ":"", - (events&SS_BATWARN)?"BATWARN ":"", - (events&SS_STSCHG)?"STSCHG ":""); + *prev_state = *state; - *prev_state=*state; + events &= mask; - return events; + DEBUG(2, "events: %s%s%s%s%s%s\n", + events == 0 ? "" : "", + events & SS_DETECT ? "DETECT " : "", + events & SS_READY ? "READY " : "", + events & SS_BATDEAD ? "BATDEAD " : "", + events & SS_BATWARN ? "BATWARN " : "", + events & SS_STSCHG ? "STSCHG " : ""); + return events; } /* sa1100_pcmcia_events() */ @@ -464,38 +223,43 @@ * callback) occurs in this thread rather than in the actual interrupt * handler due to the use of scheduling operations in the PCMCIA core. */ -static void sa1100_pcmcia_task_handler(void *data) { +static void sa1100_pcmcia_task_handler(void *data) +{ struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK]; struct pcmcia_state_array state_array; - int i, events, all_events, irq_status; + unsigned int all_events; - DEBUG(2, "%s(): entering PCMCIA monitoring thread\n", __FUNCTION__); + DEBUG(4, "%s(): entering PCMCIA monitoring thread\n", __FUNCTION__); - state_array.size=sa1100_pcmcia_socket_count; - state_array.state=state; + state_array.size = sa1100_pcmcia_socket_count; + state_array.state = state; do { + unsigned int events; + int ret, i; - DEBUG(3, "%s(): interrogating low-level PCMCIA service\n", __FUNCTION__); + memset(state, 0, sizeof(state)); - if((irq_status=pcmcia_low_level->socket_state(&state_array))<0) - printk(KERN_ERR "Error in kernel low-level PCMCIA service.\n"); + DEBUG(4, "%s(): interrogating low-level PCMCIA service\n", __FUNCTION__); - all_events=0; + ret = pcmcia_low_level->socket_state(&state_array); + if (ret < 0) { + printk(KERN_ERR "sa1100_pcmcia: unable to read socket status\n"); + break; + } - if(irq_status>0){ + all_events = 0; - for(i=0; ik_state, + skt->cs_state.csc_mask, + skt->cs_state.flags); + if (events && sa1100_pcmcia_socket[i].handler != NULL) + skt->handler(skt->handler_info, events); + } } while(all_events); } /* sa1100_pcmcia_task_handler() */ @@ -510,7 +274,7 @@ */ static void sa1100_pcmcia_poll_event(unsigned long dummy) { - DEBUG(3, "%s(): polling for events\n", __FUNCTION__); + DEBUG(4, "%s(): polling for events\n", __FUNCTION__); poll_timer.function = sa1100_pcmcia_poll_event; poll_timer.expires = jiffies + SA1100_PCMCIA_POLL_PERIOD; add_timer(&poll_timer); @@ -527,7 +291,8 @@ * handling code performs scheduling operations which cannot be * executed from within an interrupt context. */ -static void sa1100_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs){ +static void sa1100_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs) +{ DEBUG(3, "%s(): servicing IRQ %d\n", __FUNCTION__, irq); schedule_task(&sa1100_pcmcia_task); } @@ -546,17 +311,20 @@ * * Returns: 0 */ -static int sa1100_pcmcia_register_callback(unsigned int sock, - void (*handler)(void *, - unsigned int), - void *info){ - if(handler==NULL){ - sa1100_pcmcia_socket[sock].handler=NULL; +static int +sa1100_pcmcia_register_callback(unsigned int sock, + void (*handler)(void *, unsigned int), + void *info) +{ + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + + if (handler == NULL) { + skt->handler = NULL; MOD_DEC_USE_COUNT; } else { MOD_INC_USE_COUNT; - sa1100_pcmcia_socket[sock].handler=handler; - sa1100_pcmcia_socket[sock].handler_info=info; + skt->handler_info = info; + skt->handler = handler; } return 0; @@ -580,51 +348,41 @@ * an offset which is applied to client-requested base I/O addresses * in alloc_io_space(). * - * Returns: 0 on success, -1 if no pin has been configured for `sock' + * SS_CAP_PAGE_REGS: used by setup_cis_mem() in cistpl.c to set the + * force_low argument to validate_mem() in rsrc_mgr.c -- since in + * general, the mapped * addresses of the PCMCIA memory regions + * will not be within 0xffff, setting force_low would be + * undesirable. + * + * SS_CAP_STATIC_MAP: don't bother with the (user-configured) memory + * resource database; we instead pass up physical address ranges + * and allow other parts of Card Services to deal with remapping. + * + * SS_CAP_PCCARD: we can deal with 16-bit PCMCIA & CF cards, but + * not 32-bit CardBus devices. + * + * Return value is irrelevant; the pcmcia subsystem ignores it. */ -static int sa1100_pcmcia_inquire_socket(unsigned int sock, - socket_cap_t *cap){ - struct pcmcia_irq_info irq_info; - - DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock); - - if(sock>=sa1100_pcmcia_socket_count){ - printk(KERN_ERR "sa1100: socket %u not configured\n", sock); - return -1; - } +static int +sa1100_pcmcia_inquire_socket(unsigned int sock, socket_cap_t *cap) +{ + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + int ret = -1; - /* SS_CAP_PAGE_REGS: used by setup_cis_mem() in cistpl.c to set the - * force_low argument to validate_mem() in rsrc_mgr.c -- since in - * general, the mapped * addresses of the PCMCIA memory regions - * will not be within 0xffff, setting force_low would be - * undesirable. - * - * SS_CAP_STATIC_MAP: don't bother with the (user-configured) memory - * resource database; we instead pass up physical address ranges - * and allow other parts of Card Services to deal with remapping. - * - * SS_CAP_PCCARD: we can deal with 16-bit PCMCIA & CF cards, but - * not 32-bit CardBus devices. - */ - cap->features=(SS_CAP_PAGE_REGS | SS_CAP_STATIC_MAP | SS_CAP_PCCARD); + DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock); - irq_info.sock=sock; - irq_info.irq=-1; + if (sock < sa1100_pcmcia_socket_count) { + cap->features = SS_CAP_PAGE_REGS | SS_CAP_STATIC_MAP | SS_CAP_PCCARD; + cap->irq_mask = 0; + cap->map_size = PAGE_SIZE; + cap->pci_irq = skt->irq; + cap->io_offset = (unsigned long)skt->virt_io; - if(pcmcia_low_level->get_irq_info(&irq_info)<0){ - printk(KERN_ERR "Error obtaining IRQ info from kernel for socket %u\n", - sock); - return -1; + ret = 0; } - cap->irq_mask=0; - cap->map_size=PAGE_SIZE; - cap->pci_irq=irq_info.irq; - cap->io_offset=sa1100_pcmcia_socket[sock].virt_io; - - return 0; - -} /* sa1100_pcmcia_inquire_socket() */ + return ret; +} /* sa1100_pcmcia_get_status() @@ -643,58 +401,61 @@ * * Returns: 0 */ -static int sa1100_pcmcia_get_status(unsigned int sock, - unsigned int *status){ +static int +sa1100_pcmcia_get_status(unsigned int sock, unsigned int *status) +{ + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK]; struct pcmcia_state_array state_array; + unsigned int stat; + + DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock); - DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock); + state_array.size = sa1100_pcmcia_socket_count; + state_array.state = state; - state_array.size=sa1100_pcmcia_socket_count; - state_array.state=state; + memset(state, 0, sizeof(state)); - if((pcmcia_low_level->socket_state(&state_array))<0){ - printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n"); + if ((pcmcia_low_level->socket_state(&state_array)) < 0) { + printk(KERN_ERR "sa1100_pcmcia: unable to get socket status\n"); return -1; } - sa1100_pcmcia_socket[sock].k_state=state[sock]; - - *status=state[sock].detect?SS_DETECT:0; + skt->k_state = state[sock]; - *status|=state[sock].ready?SS_READY:0; + stat = state[sock].detect ? SS_DETECT : 0; + stat |= state[sock].ready ? SS_READY : 0; + stat |= state[sock].vs_3v ? SS_3VCARD : 0; + stat |= state[sock].vs_Xv ? SS_XVCARD : 0; /* The power status of individual sockets is not available * explicitly from the hardware, so we just remember the state * and regurgitate it upon request: */ - *status|=sa1100_pcmcia_socket[sock].cs_state.Vcc?SS_POWERON:0; + stat |= skt->cs_state.Vcc ? SS_POWERON : 0; - if(sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD) - *status|=state[sock].bvd1?SS_STSCHG:0; + if (skt->cs_state.flags & SS_IOCARD) + stat |= state[sock].bvd1 ? SS_STSCHG : 0; else { - if(state[sock].bvd1==0) - *status|=SS_BATDEAD; - else if(state[sock].bvd2==0) - *status|=SS_BATWARN; + if (state[sock].bvd1 == 0) + stat |= SS_BATDEAD; + else if (state[sock].bvd2 == 0) + stat |= SS_BATWARN; } - *status|=state[sock].vs_3v?SS_3VCARD:0; - - *status|=state[sock].vs_Xv?SS_XVCARD:0; - DEBUG(3, "\tstatus: %s%s%s%s%s%s%s%s\n", - (*status&SS_DETECT)?"DETECT ":"", - (*status&SS_READY)?"READY ":"", - (*status&SS_BATDEAD)?"BATDEAD ":"", - (*status&SS_BATWARN)?"BATWARN ":"", - (*status&SS_POWERON)?"POWERON ":"", - (*status&SS_STSCHG)?"STSCHG ":"", - (*status&SS_3VCARD)?"3VCARD ":"", - (*status&SS_XVCARD)?"XVCARD ":""); + stat & SS_DETECT ? "DETECT " : "", + stat & SS_READY ? "READY " : "", + stat & SS_BATDEAD ? "BATDEAD " : "", + stat & SS_BATWARN ? "BATWARN " : "", + stat & SS_POWERON ? "POWERON " : "", + stat & SS_STSCHG ? "STSCHG " : "", + stat & SS_3VCARD ? "3VCARD " : "", + stat & SS_XVCARD ? "XVCARD " : ""); - return 0; + *status = stat; + return 0; } /* sa1100_pcmcia_get_status() */ @@ -706,20 +467,18 @@ * * Returns: 0 */ -static int sa1100_pcmcia_get_socket(unsigned int sock, - socket_state_t *state){ +static int +sa1100_pcmcia_get_socket(unsigned int sock, socket_state_t *state) +{ + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); - DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock); + DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock); - /* This information was given to us in an earlier call to set_socket(), - * so we're just regurgitating it here: - */ - *state=sa1100_pcmcia_socket[sock].cs_state; + *state = skt->cs_state; return 0; } - /* sa1100_pcmcia_set_socket() * ^^^^^^^^^^^^^^^^^^^^^^^^^^ * Implements the set_socket() operation for the in-kernel PCMCIA @@ -730,14 +489,15 @@ * * Returns: 0 */ -static int sa1100_pcmcia_set_socket(unsigned int sock, - socket_state_t *state){ - struct pcmcia_configure configure; +static int +sa1100_pcmcia_set_socket(unsigned int sock, socket_state_t *state) +{ + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + struct pcmcia_configure conf; - DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock); + DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock); - DEBUG(3, "\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n" - "\tVcc %d Vpp %d irq %d\n", + DEBUG(3, "\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n", (state->csc_mask==0)?"":"", (state->csc_mask&SS_DETECT)?"DETECT ":"", (state->csc_mask&SS_READY)?"READY ":"", @@ -749,25 +509,20 @@ (state->flags&SS_IOCARD)?"IOCARD ":"", (state->flags&SS_RESET)?"RESET ":"", (state->flags&SS_SPKR_ENA)?"SPKR_ENA ":"", - (state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":"", + (state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":""); + DEBUG(3, "\tVcc %d Vpp %d irq %d\n", state->Vcc, state->Vpp, state->io_irq); - configure.sock=sock; - configure.vcc=state->Vcc; - configure.vpp=state->Vpp; - configure.output=(state->flags&SS_OUTPUT_ENA)?1:0; - configure.speaker=(state->flags&SS_SPKR_ENA)?1:0; - configure.reset=(state->flags&SS_RESET)?1:0; + conf = sa1100_pcmcia_state_to_config(sock, state); - if(pcmcia_low_level->configure_socket(&configure)<0){ - printk(KERN_ERR "Unable to configure socket %u\n", sock); + if (pcmcia_low_level->configure_socket(&conf) < 0) { + printk(KERN_ERR "sa1100_pcmcia: unable to configure socket %d\n", sock); return -1; } - sa1100_pcmcia_socket[sock].cs_state=*state; + skt->cs_state = *state; return 0; - } /* sa1100_pcmcia_set_socket() */ @@ -779,20 +534,20 @@ * * Returns: 0 on success, -1 if the map index was out of range */ -static int sa1100_pcmcia_get_io_map(unsigned int sock, - struct pccard_io_map *map){ +static int +sa1100_pcmcia_get_io_map(unsigned int sock, struct pccard_io_map *map) +{ + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + int ret = -1; - DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); + DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock); - if(map->map>=MAX_IO_WIN){ - printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, - map->map); - return -1; + if (map->map < MAX_IO_WIN) { + *map = skt->io_map[map->map]; + ret = 0; } - *map=sa1100_pcmcia_socket[sock].io_map[map->map]; - - return 0; + return ret; } @@ -805,16 +560,16 @@ * * Returns: 0 on success, -1 on error */ -static int sa1100_pcmcia_set_io_map(unsigned int sock, - struct pccard_io_map *map){ - unsigned int clock, speed; - unsigned long mecr, start; +static int +sa1100_pcmcia_set_io_map(unsigned int sock, struct pccard_io_map *map) +{ + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); - DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); + DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock); - DEBUG(4, "\tmap %u speed %u\n\tstart 0x%08lx stop 0x%08lx\n" - "\tflags: %s%s%s%s%s%s%s%s\n", - map->map, map->speed, map->start, map->stop, + DEBUG(3, "\tmap %u speed %u\n\tstart 0x%08x stop 0x%08x\n", + map->map, map->speed, map->start, map->stop); + DEBUG(3, "\tflags: %s%s%s%s%s%s%s%s\n", (map->flags==0)?"":"", (map->flags&MAP_ACTIVE)?"ACTIVE ":"", (map->flags&MAP_16BIT)?"16BIT ":"", @@ -824,45 +579,45 @@ (map->flags&MAP_USE_WAIT)?"USE_WAIT ":"", (map->flags&MAP_PREFETCH)?"PREFETCH ":""); - if(map->map>=MAX_IO_WIN){ + if (map->map >= MAX_IO_WIN) { printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, map->map); return -1; } - if(map->flags&MAP_ACTIVE){ + if (map->flags & MAP_ACTIVE) { + unsigned int clock, speed = map->speed; + unsigned long mecr; - speed=(map->speed>0)?map->speed:SA1100_PCMCIA_IO_ACCESS; + if (speed == 0) + speed = SA1100_PCMCIA_IO_ACCESS; - clock = get_cclk_frequency() * 100; + clock = cpufreq_get(0); - mecr=MECR; + mecr = MECR; MECR_BSIO_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock)); - sa1100_pcmcia_socket[sock].speed_io=speed; + skt->speed_io = speed; DEBUG(4, "%s(): FAST%u %lx BSM%u %lx BSA%u %lx BSIO%u %lx\n", __FUNCTION__, sock, MECR_FAST_GET(mecr, sock), sock, MECR_BSM_GET(mecr, sock), sock, MECR_BSA_GET(mecr, sock), sock, MECR_BSIO_GET(mecr, sock)); - MECR=mecr; - + MECR = mecr; } - start=map->start; + if (map->stop == 1) + map->stop = PAGE_SIZE-1; - if(map->stop==1) - map->stop=PAGE_SIZE-1; + map->stop -= map->start; + map->stop += (unsigned long)skt->virt_io; + map->start = (unsigned long)skt->virt_io; - map->start=sa1100_pcmcia_socket[sock].virt_io; - map->stop=map->start+(map->stop-start); - - sa1100_pcmcia_socket[sock].io_map[map->map]=*map; + skt->io_map[map->map] = *map; return 0; - } /* sa1100_pcmcia_set_io_map() */ @@ -875,20 +630,20 @@ * * Returns: 0 on success, -1 if the map index was out of range */ -static int sa1100_pcmcia_get_mem_map(unsigned int sock, - struct pccard_mem_map *map){ +static int +sa1100_pcmcia_get_mem_map(unsigned int sock, struct pccard_mem_map *map) +{ + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + int ret = -1; - DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); + DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock); - if(map->map>=MAX_WIN){ - printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, - map->map); - return -1; + if (map->map < MAX_WIN) { + *map = skt->mem_map[map->map]; + ret = 0; } - *map=sa1100_pcmcia_socket[sock].mem_map[map->map]; - - return 0; + return ret; } @@ -901,18 +656,18 @@ * * Returns: 0 on success, -1 on error */ -static int sa1100_pcmcia_set_mem_map(unsigned int sock, - struct pccard_mem_map *map){ - unsigned int clock, speed; - unsigned long mecr, start; +static int +sa1100_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *map) +{ + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + unsigned long start; - DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); + DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock); - DEBUG(4, "\tmap %u speed %u\n\tsys_start %#lx\n" - "\tsys_stop %#lx\n\tcard_start %#x\n" - "\tflags: %s%s%s%s%s%s%s%s\n", - map->map, map->speed, map->sys_start, map->sys_stop, - map->card_start, (map->flags==0)?"":"", + DEBUG(3, "\tmap %u speed %u sys_start %08lx sys_stop %08lx card_start %08x\n", + map->map, map->speed, map->sys_start, map->sys_stop, map->card_start); + DEBUG(3, "\tflags: %s%s%s%s%s%s%s%s\n", + (map->flags==0)?"":"", (map->flags&MAP_ACTIVE)?"ACTIVE ":"", (map->flags&MAP_16BIT)?"16BIT ":"", (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"", @@ -921,177 +676,175 @@ (map->flags&MAP_ATTRIB)?"ATTRIB ":"", (map->flags&MAP_USE_WAIT)?"USE_WAIT ":""); - if(map->map>=MAX_WIN){ + if (map->map >= MAX_WIN){ printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, map->map); return -1; } - if(map->flags&MAP_ACTIVE){ - - /* When clients issue RequestMap, the access speed is not always - * properly configured: + if (map->flags & MAP_ACTIVE) { + unsigned int clock, speed = map->speed; + unsigned long mecr; + + /* + * When clients issue RequestMap, the access speed is not always + * properly configured. Choose some sensible defaults. */ - if(map->speed > 0) - speed = map->speed; - else - switch(sa1100_pcmcia_socket[sock].cs_state.Vcc){ - case 33: + if (speed == 0) { + if (skt->cs_state.Vcc == 33) speed = SA1100_PCMCIA_3V_MEM_ACCESS; - break; - default: + else speed = SA1100_PCMCIA_5V_MEM_ACCESS; - } + } - clock = get_cclk_frequency() * 100; - - mecr=MECR; - - if(map->flags&MAP_ATTRIB){ + clock = cpufreq_get(0); - MECR_BSA_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock)); - sa1100_pcmcia_socket[sock].speed_attr=speed; + /* Fixme: MECR is not pre-empt safe. */ + mecr = MECR; + if (map->flags & MAP_ATTRIB) { + MECR_BSA_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock)); + skt->speed_attr = speed; } else { - MECR_BSM_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock)); - sa1100_pcmcia_socket[sock].speed_mem=speed; - + skt->speed_mem = speed; } - + DEBUG(4, "%s(): FAST%u %lx BSM%u %lx BSA%u %lx BSIO%u %lx\n", __FUNCTION__, sock, MECR_FAST_GET(mecr, sock), sock, MECR_BSM_GET(mecr, sock), sock, MECR_BSA_GET(mecr, sock), sock, MECR_BSIO_GET(mecr, sock)); - - MECR=mecr; + MECR = mecr; } - start=map->sys_start; - - if(map->sys_stop==0) - map->sys_stop=PAGE_SIZE-1; + start = (map->flags & MAP_ATTRIB) ? skt->phys_attr : skt->phys_mem; - map->sys_start=(map->flags & MAP_ATTRIB)?\ - sa1100_pcmcia_socket[sock].phys_attr:\ - sa1100_pcmcia_socket[sock].phys_mem; + if (map->sys_stop == 0) + map->sys_stop = PAGE_SIZE-1; - map->sys_stop=map->sys_start+(map->sys_stop-start); + map->sys_stop -= map->sys_start; + map->sys_stop += start; + map->sys_start = start; - sa1100_pcmcia_socket[sock].mem_map[map->map]=*map; + skt->mem_map[map->map] = *map; return 0; - } /* sa1100_pcmcia_set_mem_map() */ #if defined(CONFIG_PROC_FS) -/* sa1100_pcmcia_proc_setup() - * ^^^^^^^^^^^^^^^^^^^^^^^^^^ - * Implements the proc_setup() operation for the in-kernel PCMCIA - * service (formerly SS_ProcSetup in Card Services). - * - * Returns: 0 on success, -1 on error - */ -static void sa1100_pcmcia_proc_setup(unsigned int sock, - struct proc_dir_entry *base){ - struct proc_dir_entry *entry; - - DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); - - if((entry=create_proc_entry("status", 0, base))==NULL){ - printk(KERN_ERR "Unable to install \"status\" procfs entry\n"); - return; - } - - entry->read_proc=sa1100_pcmcia_proc_status; - entry->data=(void *)sock; -} - - /* sa1100_pcmcia_proc_status() * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Implements the /proc/bus/pccard/??/status file. * * Returns: the number of characters added to the buffer */ -static int sa1100_pcmcia_proc_status(char *buf, char **start, off_t pos, - int count, int *eof, void *data){ - char *p=buf; - unsigned int sock=(unsigned int)data; - unsigned int clock = get_cclk_frequency() * 100; +static int +sa1100_pcmcia_proc_status(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + unsigned int sock = (unsigned int)data; + unsigned int clock = cpufreq_get(0); + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); unsigned long mecr = MECR; + char *p = buf; - p+=sprintf(p, "k_flags : %s%s%s%s%s%s%s\n", - sa1100_pcmcia_socket[sock].k_state.detect?"detect ":"", - sa1100_pcmcia_socket[sock].k_state.ready?"ready ":"", - sa1100_pcmcia_socket[sock].k_state.bvd1?"bvd1 ":"", - sa1100_pcmcia_socket[sock].k_state.bvd2?"bvd2 ":"", - sa1100_pcmcia_socket[sock].k_state.wrprot?"wrprot ":"", - sa1100_pcmcia_socket[sock].k_state.vs_3v?"vs_3v ":"", - sa1100_pcmcia_socket[sock].k_state.vs_Xv?"vs_Xv ":""); + p+=sprintf(p, "k_state : %s%s%s%s%s%s%s\n", + skt->k_state.detect ? "detect " : "", + skt->k_state.ready ? "ready " : "", + skt->k_state.bvd1 ? "bvd1 " : "", + skt->k_state.bvd2 ? "bvd2 " : "", + skt->k_state.wrprot ? "wrprot " : "", + skt->k_state.vs_3v ? "vs_3v " : "", + skt->k_state.vs_Xv ? "vs_Xv " : ""); p+=sprintf(p, "status : %s%s%s%s%s%s%s%s%s\n", - sa1100_pcmcia_socket[sock].k_state.detect?"SS_DETECT ":"", - sa1100_pcmcia_socket[sock].k_state.ready?"SS_READY ":"", - sa1100_pcmcia_socket[sock].cs_state.Vcc?"SS_POWERON ":"", - sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD?\ - "SS_IOCARD ":"", - (sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD && - sa1100_pcmcia_socket[sock].k_state.bvd1)?"SS_STSCHG ":"", - ((sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD)==0 && - (sa1100_pcmcia_socket[sock].k_state.bvd1==0))?"SS_BATDEAD ":"", - ((sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD)==0 && - (sa1100_pcmcia_socket[sock].k_state.bvd2==0))?"SS_BATWARN ":"", - sa1100_pcmcia_socket[sock].k_state.vs_3v?"SS_3VCARD ":"", - sa1100_pcmcia_socket[sock].k_state.vs_Xv?"SS_XVCARD ":""); + skt->k_state.detect ? "SS_DETECT " : "", + skt->k_state.ready ? "SS_READY " : "", + skt->cs_state.Vcc ? "SS_POWERON " : "", + skt->cs_state.flags & SS_IOCARD ? "SS_IOCARD " : "", + (skt->cs_state.flags & SS_IOCARD && + skt->k_state.bvd1) ? "SS_STSCHG " : "", + ((skt->cs_state.flags & SS_IOCARD)==0 && + (skt->k_state.bvd1==0)) ? "SS_BATDEAD " : "", + ((skt->cs_state.flags & SS_IOCARD)==0 && + (skt->k_state.bvd2==0)) ? "SS_BATWARN " : "", + skt->k_state.vs_3v ? "SS_3VCARD " : "", + skt->k_state.vs_Xv ? "SS_XVCARD " : ""); p+=sprintf(p, "mask : %s%s%s%s%s\n", - sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_DETECT?\ - "SS_DETECT ":"", - sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_READY?\ - "SS_READY ":"", - sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_BATDEAD?\ - "SS_BATDEAD ":"", - sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_BATWARN?\ - "SS_BATWARN ":"", - sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_STSCHG?\ - "SS_STSCHG ":""); + skt->cs_state.csc_mask & SS_DETECT ? "SS_DETECT " : "", + skt->cs_state.csc_mask & SS_READY ? "SS_READY " : "", + skt->cs_state.csc_mask & SS_BATDEAD ? "SS_BATDEAD " : "", + skt->cs_state.csc_mask & SS_BATWARN ? "SS_BATWARN " : "", + skt->cs_state.csc_mask & SS_STSCHG ? "SS_STSCHG " : ""); p+=sprintf(p, "cs_flags : %s%s%s%s%s\n", - sa1100_pcmcia_socket[sock].cs_state.flags&SS_PWR_AUTO?\ - "SS_PWR_AUTO ":"", - sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD?\ - "SS_IOCARD ":"", - sa1100_pcmcia_socket[sock].cs_state.flags&SS_RESET?\ - "SS_RESET ":"", - sa1100_pcmcia_socket[sock].cs_state.flags&SS_SPKR_ENA?\ - "SS_SPKR_ENA ":"", - sa1100_pcmcia_socket[sock].cs_state.flags&SS_OUTPUT_ENA?\ - "SS_OUTPUT_ENA ":""); - - p+=sprintf(p, "Vcc : %d\n", sa1100_pcmcia_socket[sock].cs_state.Vcc); - - p+=sprintf(p, "Vpp : %d\n", sa1100_pcmcia_socket[sock].cs_state.Vpp); + skt->cs_state.flags & SS_PWR_AUTO ? "SS_PWR_AUTO " : "", + skt->cs_state.flags & SS_IOCARD ? "SS_IOCARD " : "", + skt->cs_state.flags & SS_RESET ? "SS_RESET " : "", + skt->cs_state.flags & SS_SPKR_ENA ? "SS_SPKR_ENA " : "", + skt->cs_state.flags & SS_OUTPUT_ENA ? "SS_OUTPUT_ENA " : ""); + + p+=sprintf(p, "Vcc : %d\n", skt->cs_state.Vcc); + p+=sprintf(p, "Vpp : %d\n", skt->cs_state.Vpp); + p+=sprintf(p, "IRQ : %d\n", skt->cs_state.io_irq); - p+=sprintf(p, "irq : %d\n", sa1100_pcmcia_socket[sock].cs_state.io_irq); - - p+=sprintf(p, "I/O : %u (%u)\n", sa1100_pcmcia_socket[sock].speed_io, + p+=sprintf(p, "I/O : %u (%u)\n", skt->speed_io, sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, sock))); - p+=sprintf(p, "attribute: %u (%u)\n", sa1100_pcmcia_socket[sock].speed_attr, + p+=sprintf(p, "attribute: %u (%u)\n", skt->speed_attr, sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, sock))); - p+=sprintf(p, "common : %u (%u)\n", sa1100_pcmcia_socket[sock].speed_mem, + p+=sprintf(p, "common : %u (%u)\n", skt->speed_mem, sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, sock))); return p-buf; } +/* sa1100_pcmcia_proc_setup() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^ + * Implements the proc_setup() operation for the in-kernel PCMCIA + * service (formerly SS_ProcSetup in Card Services). + * + * Returns: 0 on success, -1 on error + */ +static void +sa1100_pcmcia_proc_setup(unsigned int sock, struct proc_dir_entry *base) +{ + struct proc_dir_entry *entry; + + DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); + + if ((entry = create_proc_entry("status", 0, base)) == NULL){ + printk(KERN_ERR "unable to install \"status\" procfs entry\n"); + return; + } + + entry->read_proc = sa1100_pcmcia_proc_status; + entry->data = (void *)sock; +} + #endif /* defined(CONFIG_PROC_FS) */ +static struct pccard_operations sa1100_pcmcia_operations = { + init: sa1100_pcmcia_sock_init, + suspend: sa1100_pcmcia_suspend, + register_callback: sa1100_pcmcia_register_callback, + inquire_socket: sa1100_pcmcia_inquire_socket, + get_status: sa1100_pcmcia_get_status, + get_socket: sa1100_pcmcia_get_socket, + set_socket: sa1100_pcmcia_set_socket, + get_io_map: sa1100_pcmcia_get_io_map, + set_io_map: sa1100_pcmcia_set_io_map, + get_mem_map: sa1100_pcmcia_get_mem_map, + set_mem_map: sa1100_pcmcia_set_mem_map, +#ifdef CONFIG_PROC_FS + proc_setup: sa1100_pcmcia_proc_setup +#endif +}; #ifdef CONFIG_CPU_FREQ @@ -1101,25 +854,23 @@ * to a core clock frequency change) is needed, this routine establishes * new BS_xx values consistent with the clock speed `clock'. */ -static void sa1100_pcmcia_update_mecr(unsigned int clock){ +static void sa1100_pcmcia_update_mecr(unsigned int clock) +{ unsigned int sock; unsigned long mecr = MECR; for(sock = 0; sock < SA1100_PCMCIA_MAX_SOCK; ++sock){ + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); MECR_BSIO_SET(mecr, sock, - sa1100_pcmcia_mecr_bs(sa1100_pcmcia_socket[sock].speed_io, - clock)); + sa1100_pcmcia_mecr_bs(skt->speed_io, clock)); MECR_BSA_SET(mecr, sock, - sa1100_pcmcia_mecr_bs(sa1100_pcmcia_socket[sock].speed_attr, - clock)); + sa1100_pcmcia_mecr_bs(skt->speed_attr, clock)); MECR_BSM_SET(mecr, sock, - sa1100_pcmcia_mecr_bs(sa1100_pcmcia_socket[sock].speed_mem, - clock)); + sa1100_pcmcia_mecr_bs(skt->speed_mem, clock)); } MECR = mecr; - } /* sa1100_pcmcia_notifier() @@ -1132,53 +883,371 @@ * * Returns: 0 on success, -1 on error */ -static int sa1100_pcmcia_notifier(struct notifier_block *nb, - unsigned long val, void *data){ +static int +sa1100_pcmcia_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ struct cpufreq_info *ci = data; - switch(val){ - case CPUFREQ_MINMAX: - - break; - + switch (val) { case CPUFREQ_PRECHANGE: - - if(ci->new_freq > ci->old_freq){ + if (ci->new_freq > ci->old_freq) { DEBUG(2, "%s(): new frequency %u.%uMHz > %u.%uMHz, pre-updating\n", __FUNCTION__, ci->new_freq / 1000, (ci->new_freq / 100) % 10, ci->old_freq / 1000, (ci->old_freq / 100) % 10); sa1100_pcmcia_update_mecr(ci->new_freq); } - break; case CPUFREQ_POSTCHANGE: - - if(ci->new_freq < ci->old_freq){ + if (ci->new_freq < ci->old_freq) { DEBUG(2, "%s(): new frequency %u.%uMHz < %u.%uMHz, post-updating\n", __FUNCTION__, ci->new_freq / 1000, (ci->new_freq / 100) % 10, ci->old_freq / 1000, (ci->old_freq / 100) % 10); sa1100_pcmcia_update_mecr(ci->new_freq); } - break; - - default: - printk(KERN_ERR "%s(): unknown CPU frequency event %lx\n", __FUNCTION__, - val); - return -1; - } return 0; - } static struct notifier_block sa1100_pcmcia_notifier_block = { - notifier_call: sa1100_pcmcia_notifier + notifier_call: sa1100_pcmcia_notifier }; +#endif + +/* sa1100_register_pcmcia() + * ^^^^^^^^^^^^^^^^^^^^^^^^ + * + * Register an SA1100 PCMCIA low level driver with the SA1100 core. + */ +int sa1100_register_pcmcia(struct pcmcia_low_level *ops) +{ + struct pcmcia_init pcmcia_init; + struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK]; + struct pcmcia_state_array state_array; + unsigned int i, clock; + unsigned long mecr; + int ret; + + /* + * Refuse to replace an existing driver. + */ + if (pcmcia_low_level) + return -EBUSY; + + pcmcia_low_level = ops; + + pcmcia_init.handler = sa1100_pcmcia_interrupt; + ret = ops->init(&pcmcia_init); + if (ret < 0) { + printk(KERN_ERR "Unable to initialize kernel PCMCIA service (%d).\n", ret); + if (ret == -1) + ret = -EIO; + goto out; + } + + sa1100_pcmcia_socket_count = ret; + + state_array.size = ret; + state_array.state = state; + + memset(state, 0, sizeof(state)); + + if (ops->socket_state(&state_array) < 0) { + printk(KERN_ERR "Unable to get PCMCIA status driver.\n"); + ret = -EIO; + goto shutdown; + } + + /* + * We initialize the MECR to default values here, because we are + * not guaranteed to see a SetIOMap operation at runtime. + */ + mecr = 0; + + clock = cpufreq_get(0); + + for (i = 0; i < sa1100_pcmcia_socket_count; i++) { + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i); + struct pcmcia_irq_info irq_info; + + if (!request_mem_region(_PCMCIA(i), PCMCIASp, "PCMCIA")) { + ret = -EBUSY; + goto out_err; + } + + irq_info.sock = i; + irq_info.irq = -1; + ret = ops->get_irq_info(&irq_info); + if (ret < 0) + printk(KERN_ERR "Unable to get IRQ for socket %u (%d)\n", i, ret); + + skt->irq = irq_info.irq; + skt->k_state = state[i]; + skt->speed_io = SA1100_PCMCIA_IO_ACCESS; + skt->speed_attr = SA1100_PCMCIA_5V_MEM_ACCESS; + skt->speed_mem = SA1100_PCMCIA_5V_MEM_ACCESS; + skt->phys_attr = _PCMCIAAttr(i); + skt->phys_mem = _PCMCIAMem(i); + skt->virt_io = ioremap(_PCMCIAIO(i), 0x10000); + + if (skt->virt_io == NULL) { + ret = -ENOMEM; + goto out_err; + } + + MECR_FAST_SET(mecr, i, 0); + MECR_BSIO_SET(mecr, i, sa1100_pcmcia_mecr_bs(skt->speed_io, clock)); + MECR_BSA_SET(mecr, i, sa1100_pcmcia_mecr_bs(skt->speed_attr, clock)); + MECR_BSM_SET(mecr, i, sa1100_pcmcia_mecr_bs(skt->speed_mem, clock)); + } + + MECR = mecr; + + /* Only advertise as many sockets as we can detect */ + ret = register_ss_entry(sa1100_pcmcia_socket_count, + &sa1100_pcmcia_operations); + if (ret < 0) { + printk(KERN_ERR "Unable to register sockets\n"); + goto out_err; + } + + /* + * Start the event poll timer. It will reschedule by itself afterwards. + */ + sa1100_pcmcia_poll_event(0); + return 0; + + out_err: + for (i = 0; i < sa1100_pcmcia_socket_count; i++) { + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i); + iounmap(skt->virt_io); + skt->virt_io = NULL; + release_mem_region(_PCMCIA(i), PCMCIASp); + } + + shutdown: + ops->shutdown(); + + out: + pcmcia_low_level = NULL; + return ret; +} + +/* sa1100_unregister_pcmcia() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^ + * + * Unregister a previously registered pcmcia driver + */ +void sa1100_unregister_pcmcia(struct pcmcia_low_level *ops) +{ + int i; + + if (!ops) + return; + + if (ops != pcmcia_low_level) { + printk(KERN_DEBUG "PCMCIA: Trying to unregister wrong " + "low-level driver (%p != %p)", ops, + pcmcia_low_level); + return; + } + + del_timer_sync(&poll_timer); + + unregister_ss_entry(&sa1100_pcmcia_operations); + + for (i = 0; i < sa1100_pcmcia_socket_count; i++) { + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i); + + iounmap(skt->virt_io); + skt->virt_io = NULL; + + release_mem_region(_PCMCIA(i), PCMCIASp); + } + + ops->shutdown(); + + flush_scheduled_tasks(); + + pcmcia_low_level = NULL; +} + +/* sa1100_pcmcia_init() + * ^^^^^^^^^^^^^^^^^^^^ + * + * This routine performs a basic sanity check to ensure that this + * kernel has been built with the appropriate board-specific low-level + * PCMCIA support, performs low-level PCMCIA initialization, registers + * this socket driver with Card Services, and then spawns the daemon + * thread which is the real workhorse of the socket driver. + * + * Returns: 0 on success, -1 on error + */ +static int __init sa1100_pcmcia_init(void) +{ + servinfo_t info; + int ret, i; + + printk(KERN_INFO "SA-1100 PCMCIA (CS release %s)\n", CS_RELEASE); + + CardServices(GetCardServicesInfo, &info); + if (info.Revision != CS_RELEASE_CODE) { + printk(KERN_ERR "Card Services release codes do not match\n"); + return -EINVAL; + } + + for (i = 0; i < SA1100_PCMCIA_MAX_SOCK; i++) { + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i); + + skt->speed_io = SA1100_PCMCIA_IO_ACCESS; + skt->speed_attr = SA1100_PCMCIA_5V_MEM_ACCESS; + skt->speed_mem = SA1100_PCMCIA_5V_MEM_ACCESS; + } + +#ifdef CONFIG_CPU_FREQ + ret = cpufreq_register_notifier(&sa1100_pcmcia_notifier_block); + if (ret < 0) { + printk(KERN_ERR "Unable to register CPU frequency change " + "notifier (%d)\n", ret); + return ret; + } +#endif + +#ifdef CONFIG_SA1100_ADSBITSY + pcmcia_adsbitsy_init(); +#endif +#ifdef CONFIG_SA1100_ASSABET + pcmcia_assabet_init(); +#endif +#ifdef CONFIG_SA1100_BADGE4 + pcmcia_badge4_init(); +#endif +#ifdef CONFIG_SA1100_CERF + pcmcia_cerf_init(); +#endif +#ifdef CONFIG_SA1100_FLEXANET + pcmcia_flexanet_init(); +#endif +#ifdef CONFIG_SA1100_FREEBIRD + pcmcia_freebird_init(); +#endif +#ifdef CONFIG_SA1100_GRAPHICSCLIENT + pcmcia_gcplus_init(); +#endif +#ifdef CONFIG_SA1100_GRAPHICSMASTER + pcmcia_graphicsmaster_init(); +#endif +#ifdef CONFIG_SA1100_JORNADA720 + pcmcia_jornada720_init(); +#endif +#ifdef CONFIG_ASSABET_NEPONSET + pcmcia_neponset_init(); +#endif +#ifdef CONFIG_SA1100_PANGOLIN + pcmcia_pangolin_init(); +#endif +#ifdef CONFIG_SA1100_PFS168 + pcmcia_pfs_init(); +#endif +#ifdef CONFIG_SA1100_PT_SYSTEM3 + pcmcia_system3_init(); +#endif +#ifdef CONFIG_SA1100_SHANNON + pcmcia_shannon_init(); +#endif +#ifdef CONFIG_SA1100_SIMPAD + pcmcia_simpad_init(); +#endif +#ifdef CONFIG_SA1100_STORK + pcmcia_stork_init(); +#endif +#ifdef CONFIG_SA1100_XP860 + pcmcia_xp860_init(); +#endif +#ifdef CONFIG_SA1100_YOPY + pcmcia_yopy_init(); +#endif + + return 0; +} + +/* sa1100_pcmcia_exit() + * ^^^^^^^^^^^^^^^^^^^^ + * Invokes the low-level kernel service to free IRQs associated with this + * socket controller and reset GPIO edge detection. + */ +static void __exit sa1100_pcmcia_exit(void) +{ +#ifdef CONFIG_SA1100_ADSBITSY + pcmcia_adsbitsy_exit(); +#endif +#ifdef CONFIG_SA1100_ASSABET + pcmcia_assabet_exit(); +#endif +#ifdef CONFIG_SA1100_BADGE4 + pcmcia_badge4_exit(); +#endif +#ifdef CONFIG_SA1100_CERF + pcmcia_cerf_exit(); +#endif +#ifdef CONFIG_SA1100_FLEXANET + pcmcia_flexanet_exit(); +#endif +#ifdef CONFIG_SA1100_FREEBIRD + pcmcia_freebird_exit(); +#endif +#ifdef CONFIG_SA1100_GRAPHICSCLIENT + pcmcia_gcplus_exit(); +#endif +#ifdef CONFIG_SA1100_GRAPHICSMASTER + pcmcia_graphicsmaster_exit(); +#endif +#ifdef CONFIG_SA1100_JORNADA720 + pcmcia_jornada720_exit(); +#endif +#ifdef CONFIG_ASSABET_NEPONSET + pcmcia_neponset_exit(); +#endif +#ifdef CONFIG_SA1100_PANGOLIN + pcmcia_pangolin_exit(); +#endif +#ifdef CONFIG_SA1100_PFS168 + pcmcia_pfs_exit(); +#endif +#ifdef CONFIG_SA1100_SHANNON + pcmcia_shannon_exit(); +#endif +#ifdef CONFIG_SA1100_SIMPAD + pcmcia_simpad_exit(); +#endif +#ifdef CONFIG_SA1100_STORK + pcmcia_stork_exit(); +#endif +#ifdef CONFIG_SA1100_XP860 + pcmcia_xp860_exit(); +#endif +#ifdef CONFIG_SA1100_YOPY + pcmcia_yopy_exit(); +#endif + if (pcmcia_low_level) { + printk(KERN_ERR "PCMCIA: low level driver still registered\n"); + sa1100_unregister_pcmcia(pcmcia_low_level); + } + +#ifdef CONFIG_CPU_FREQ + cpufreq_unregister_notifier(&sa1100_pcmcia_notifier_block); #endif +} + +MODULE_AUTHOR("John Dorsey "); +MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-1100 Socket Controller"); +MODULE_LICENSE("Dual MPL/GPL"); +module_init(sa1100_pcmcia_init); +module_exit(sa1100_pcmcia_exit); diff -Nru a/drivers/pcmcia/sa1100_generic.h b/drivers/pcmcia/sa1100_generic.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pcmcia/sa1100_generic.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,77 @@ +/* + * linux/include/asm/arch/pcmcia.h + * + * Copyright (C) 2000 John G Dorsey + * + * This file contains definitions for the low-level SA-1100 kernel PCMCIA + * interface. Please see linux/Documentation/arm/SA1100/PCMCIA for details. + */ +#ifndef _ASM_ARCH_PCMCIA +#define _ASM_ARCH_PCMCIA + +/* Ideally, we'd support up to MAX_SOCK sockets, but the SA-1100 only + * has support for two. This shows up in lots of hardwired ways, such + * as the fact that MECR only has enough bits to configure two sockets. + * Since it's so entrenched in the hardware, limiting the software + * in this way doesn't seem too terrible. + */ +#define SA1100_PCMCIA_MAX_SOCK (2) + +struct pcmcia_init { + void (*handler)(int irq, void *dev, struct pt_regs *regs); +}; + +struct pcmcia_state { + unsigned detect: 1, + ready: 1, + bvd1: 1, + bvd2: 1, + wrprot: 1, + vs_3v: 1, + vs_Xv: 1; +}; + +struct pcmcia_state_array { + unsigned int size; + struct pcmcia_state *state; +}; + +struct pcmcia_configure { + unsigned sock: 8, + vcc: 8, + vpp: 8, + output: 1, + speaker: 1, + reset: 1, + irq: 1; +}; + +struct pcmcia_irq_info { + unsigned int sock; + unsigned int irq; +}; + +struct pcmcia_low_level { + int (*init)(struct pcmcia_init *); + int (*shutdown)(void); + int (*socket_state)(struct pcmcia_state_array *); + int (*get_irq_info)(struct pcmcia_irq_info *); + int (*configure_socket)(const struct pcmcia_configure *); + + /* + * Enable card status IRQs on (re-)initialisation. This can + * be called at initialisation, power management event, or + * pcmcia event. + */ + int (*socket_init)(int sock); + + /* + * Disable card status IRQs and PCMCIA bus on suspend. + */ + int (*socket_suspend)(int sock); +}; + +extern int sa1100_register_pcmcia(struct pcmcia_low_level *); +extern void sa1100_unregister_pcmcia(struct pcmcia_low_level *); + +#endif diff -Nru a/drivers/pcmcia/sa1100_graphicsclient.c b/drivers/pcmcia/sa1100_graphicsclient.c --- a/drivers/pcmcia/sa1100_graphicsclient.c Thu Mar 7 18:17:37 2002 +++ b/drivers/pcmcia/sa1100_graphicsclient.c Thu Mar 7 18:17:37 2002 @@ -14,10 +14,13 @@ #include #include #include +#include #include #include -#include +#include "sa1100_generic.h" + +#error This is broken! #define S0_CD_IRQ 60 // Socket 0 Card Detect IRQ #define S0_STS_IRQ 55 // Socket 0 PCMCIA IRQ @@ -47,8 +50,9 @@ irq = S0_CD_IRQ; res = request_irq(irq, init->handler, SA_INTERRUPT, "PCMCIA 0 CD", NULL); if (res < 0) { - printk(KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq); - return -1; + printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n", + __FUNCTION__, irq, res); + return res; } return 1; // 1 PCMCIA Slot @@ -106,7 +110,7 @@ if(configure->sock>1) return -1; - save_flags_cli(flags); + local_irq_save(flags); switch (configure->vcc) { case 0: @@ -126,7 +130,7 @@ default: printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, configure->vcc); - restore_flags(flags); + local_irq_restore(flags); return -1; } @@ -139,16 +143,44 @@ *PCMCIA_Power |= ADS_CS_PR_A_RESET; mdelay(30); - restore_flags(flags); + local_irq_restore(flags); return 0; } -struct pcmcia_low_level gcplus_pcmcia_ops = { - gcplus_pcmcia_init, - gcplus_pcmcia_shutdown, - gcplus_pcmcia_socket_state, - gcplus_pcmcia_get_irq_info, - gcplus_pcmcia_configure_socket +static int gcplus_pcmcia_socket_init(int sock) +{ + return 0; +} + +static int gcplus_pcmcia_socket_suspend(int sock) +{ + return 0; +} + +static struct pcmcia_low_level gcplus_pcmcia_ops = { + init: gcplus_pcmcia_init, + shutdown: gcplus_pcmcia_shutdown, + socket_state: gcplus_pcmcia_socket_state, + get_irq_info: gcplus_pcmcia_get_irq_info, + configure_socket: gcplus_pcmcia_configure_socket, + + socket_init: gcplus_pcmcia_socket_init, + socket_suspend: gcplus_pcmcia_socket_suspend, }; + +int __init pcmcia_gcplus_init(void) +{ + int ret = -ENODEV; + + if (machine_is_gcplus()) + ret = sa1100_register_pcmcia(&gcplus_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_gcplus_exit(void) +{ + sa1100_unregister_pcmcia(&gcplus_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_graphicsmaster.c b/drivers/pcmcia/sa1100_graphicsmaster.c --- a/drivers/pcmcia/sa1100_graphicsmaster.c Thu Mar 7 18:17:39 2002 +++ b/drivers/pcmcia/sa1100_graphicsmaster.c Thu Mar 7 18:17:39 2002 @@ -10,11 +10,12 @@ */ #include #include +#include -#include #include -#include -#include + +#include "sa1100_generic.h" +#include "sa1111_generic.h" static int graphicsmaster_pcmcia_init(struct pcmcia_init *init) { @@ -26,190 +27,82 @@ /* Disable Power 3.3V/5V for PCMCIA/CF */ PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3; - INTPOL1 |= (1 << (S0_READY_NINT - SA1111_IRQ(32))) | - (1 << (S1_READY_NINT - SA1111_IRQ(32))) | - (1 << (S0_CD_VALID - SA1111_IRQ(32))) | - (1 << (S1_CD_VALID - SA1111_IRQ(32))) | - (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | - (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))); - - return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT, - "GC Master PCMCIA (0) CD", NULL); - return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT, - "GC Master CF (1) CD", NULL); - return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT, - "GC Master PCMCIA (0) BVD1", NULL); - return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT, - "GC Master CF (1) BVD1", NULL); - + /* why? */ MECR = 0x09430943; - return (return_val<0) ? -1 : 2; -} - -static int graphicsmaster_pcmcia_shutdown(void) -{ - - free_irq(S0_CD_VALID, NULL); - free_irq(S1_CD_VALID, NULL); - free_irq(S0_BVD1_STSCHG, NULL); - free_irq(S1_BVD1_STSCHG, NULL); - - INTPOL1 &= ~((1 << (S0_CD_VALID - SA1111_IRQ(32))) | - (1 << (S1_CD_VALID - SA1111_IRQ(32))) | - (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | - (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)))); - - return 0; -} - -static int graphicsmaster_pcmcia_socket_state(struct pcmcia_state_array *state_array) -{ - unsigned long status; - int return_val=1; - - if(state_array->size<2) return -1; - - memset(state_array->state, 0, - (state_array->size)*sizeof(struct pcmcia_state)); - - status=PCSR; - - state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0; - - state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1; - - state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1; - - state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1; - - state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1; - - state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0; - - state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; - - state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0; - - state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1; - - state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1; - - state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1; - - state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1; - - state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0; - - state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0; - - return return_val; -} - -static int graphicsmaster_pcmcia_get_irq_info(struct pcmcia_irq_info *info) -{ - - switch(info->sock){ - case 0: - info->irq=S0_READY_NINT; - break; - - case 1: - info->irq=S1_READY_NINT; - break; - - default: - return -1; - } - - return 0; + return sa1111_pcmcia_init(init); } -static int graphicsmaster_pcmcia_configure_socket(const struct pcmcia_configure *configure) +static int +graphicsmaster_pcmcia_configure_socket(const struct pcmcia_configure *conf) { - unsigned long pccr=PCCR, gpio=PA_DWR; + unsigned int pa_dwr_mask, pa_dwr_set; + int ret; - switch(configure->sock){ + switch (conf->sock) { case 0: + pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1; - switch(configure->vcc){ - case 0: - pccr = (pccr & ~PCCR_S0_FLT); - gpio |= GPIO_GPIO0 | GPIO_GPIO1; - break; - - case 33: - pccr = (pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN; - gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1); - gpio &= ~GPIO_GPIO0; - break; - - case 50: - pccr = (pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN); - gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1); - gpio |= GPIO_GPIO0; - break; - + switch (conf->vcc) { default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, - configure->vcc); - return -1; + case 0: pa_dwr_set = GPIO_GPIO0 | GPIO_GPIO1; break; + case 33: pa_dwr_set = GPIO_GPIO1; break; + case 50: pa_dwr_set = GPIO_GPIO0; break; } - - pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST); - break; case 1: - switch(configure->vcc){ - case 0: - pccr = (pccr & ~PCCR_S1_FLT); - gpio |= GPIO_GPIO2 | GPIO_GPIO3; - break; - - case 33: - pccr = (pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN; - gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3); - gpio &= ~GPIO_GPIO2; - break; - - case 50: - pccr = (pccr | PCCR_S1_PSE | PCCR_S1_FLT | PCCR_S1_PWAITEN); - gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3); - gpio |= GPIO_GPIO2; - break; + pa_dwr_mask = GPIO_GPIO2 | GPIO_GPIO3; + switch (conf->vcc) { default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, - configure->vcc); - return -1; - } - - if(configure->vpp!=configure->vcc && configure->vpp!=0){ - printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, - configure->vpp); - return -1; + case 0: pa_dwr_set = GPIO_GPIO2 | GPIO_GPIO3; break; + case 33: pa_dwr_set = GPIO_GPIO3; break; + case 50: pa_dwr_set = GPIO_GPIO2; break; } + } - pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST); - - break; - - default: + if (conf->vpp != conf->vcc && conf->vpp != 0) { + printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, + conf->vpp); return -1; } - PCCR = pccr; - PA_DWR = gpio; + ret = sa1111_pcmcia_configure_socket(conf); + if (ret == 0) { + unsigned long flags; + + local_irq_save(flags); + PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set; + local_irq_restore(flags); + } - return 0; + return ret; } -struct pcmcia_low_level graphicsmaster_pcmcia_ops = { - graphicsmaster_pcmcia_init, - graphicsmaster_pcmcia_shutdown, - graphicsmaster_pcmcia_socket_state, - graphicsmaster_pcmcia_get_irq_info, - graphicsmaster_pcmcia_configure_socket +static struct pcmcia_low_level graphicsmaster_pcmcia_ops = { + init: graphicsmaster_pcmcia_init, + shutdown: sa1111_pcmcia_shutdown, + socket_state: sa1111_pcmcia_socket_state, + get_irq_info: sa1111_pcmcia_get_irq_info, + configure_socket: graphicsmaster_pcmcia_configure_socket, + + socket_init: sa1111_pcmcia_socket_init, + socket_suspend: sa1111_pcmcia_socket_suspend, }; + +int __init pcmcia_graphicsmaster_init(void) +{ + int ret = -ENODEV; + + if (machine_is_graphicsmaster()) + ret = sa1100_register_pcmcia(&graphicsmaster_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_graphicsmaster_exit(void) +{ + sa1100_unregister_pcmcia(&graphicsmaster_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_h3600.c b/drivers/pcmcia/sa1100_h3600.c --- a/drivers/pcmcia/sa1100_h3600.c Thu Mar 7 18:17:45 2002 +++ b/drivers/pcmcia/sa1100_h3600.c Thu Mar 7 18:17:45 2002 @@ -6,142 +6,192 @@ */ #include #include +#include #include #include -#include +#include "sa1100_generic.h" +static struct irqs { + int irq; + const char *str; +} irqs[] = { + { IRQ_GPIO_H3600_PCMCIA_CD0, "PCMCIA CD0" }, + { IRQ_GPIO_H3600_PCMCIA_CD1, "PCMCIA CD1" } +}; -static int h3600_pcmcia_init(struct pcmcia_init *init){ - int irq, res; - - /* Enable CF bus: */ - set_h3600_egpio(EGPIO_H3600_OPT_NVRAM_ON); - clr_h3600_egpio(EGPIO_H3600_OPT_RESET); - - /* All those are inputs */ - GPDR &= ~(GPIO_H3600_PCMCIA_CD0 | GPIO_H3600_PCMCIA_CD1 | GPIO_H3600_PCMCIA_IRQ0| GPIO_H3600_PCMCIA_IRQ1); - - /* Set transition detect */ - set_GPIO_IRQ_edge( GPIO_H3600_PCMCIA_CD0 | GPIO_H3600_PCMCIA_CD1, GPIO_BOTH_EDGES ); - set_GPIO_IRQ_edge( GPIO_H3600_PCMCIA_IRQ0| GPIO_H3600_PCMCIA_IRQ1, GPIO_FALLING_EDGE ); - - /* Register interrupts */ - irq = IRQ_GPIO_H3600_PCMCIA_CD0; - res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD0", NULL ); - if( res < 0 ) goto irq_err; - irq = IRQ_GPIO_H3600_PCMCIA_CD1; - res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD1", NULL ); - if( res < 0 ) goto irq_err; +static int h3600_pcmcia_init(struct pcmcia_init *init) +{ + int i, res; - return 2; + /* + * Set transition detect + */ + set_irq_type(IRQ_GPIO_H3600_PCMCIA_IRQ0, IRQT_FALLING); + set_irq_type(IRQ_GPIO_H3600_PCMCIA_IRQ1, IRQT_FALLING); + + /* + * Register interrupts + */ + for (i = res = 0; i < ARRAY_SIZE(irqs); i++) { + res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT, + irqs[i].str, NULL); + if (res) + break; + } + + if (res) { + printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n", + __FUNCTION__, irqs[i].irq, res); + + while (i--) + free_irq(irqs[i].irq, NULL); + } -irq_err: - printk( KERN_ERR __FUNCTION__ ": Request for IRQ %u failed\n", irq ); - return -1; + return res ? res : 2; } static int h3600_pcmcia_shutdown(void) { - /* disable IRQs */ - free_irq( IRQ_GPIO_H3600_PCMCIA_CD0, NULL ); - free_irq( IRQ_GPIO_H3600_PCMCIA_CD1, NULL ); + int i; + + /* + * disable IRQs + */ + for (i = 0; i < ARRAY_SIZE(irqs); i++) + free_irq(irqs[i].irq, NULL); - /* Disable CF bus: */ - clr_h3600_egpio(EGPIO_H3600_OPT_NVRAM_ON|EGPIO_H3600_OPT_ON); - set_h3600_egpio(EGPIO_H3600_OPT_RESET); + /* Disable CF bus: */ + clr_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON); + clr_h3600_egpio(IPAQ_EGPIO_OPT_ON); + set_h3600_egpio(IPAQ_EGPIO_OPT_RESET); - return 0; + return 0; } -static int h3600_pcmcia_socket_state(struct pcmcia_state_array - *state_array){ - unsigned long levels; - - if(state_array->size<2) return -1; - - memset(state_array->state, 0, - (state_array->size)*sizeof(struct pcmcia_state)); +static int +h3600_pcmcia_socket_state(struct pcmcia_state_array *state) +{ + unsigned long levels; - levels=GPLR; + if (state->size < 2) + return -1; - state_array->state[0].detect=((levels & GPIO_H3600_PCMCIA_CD0)==0)?1:0; - state_array->state[0].ready=(levels & GPIO_H3600_PCMCIA_IRQ0)?1:0; - state_array->state[0].bvd1= 0; - state_array->state[0].bvd2= 0; - state_array->state[0].wrprot=0; /* Not available on H3600. */ - state_array->state[0].vs_3v=0; - state_array->state[0].vs_Xv=0; + levels = GPLR; - state_array->state[1].detect=((levels & GPIO_H3600_PCMCIA_CD1)==0)?1:0; - state_array->state[1].ready=(levels & GPIO_H3600_PCMCIA_IRQ1)?1:0; - state_array->state[1].bvd1=0; - state_array->state[1].bvd2=0; - state_array->state[1].wrprot=0; /* Not available on H3600. */ - state_array->state[1].vs_3v=0; - state_array->state[1].vs_Xv=0; + state->state[0].detect = levels & GPIO_H3600_PCMCIA_CD0 ? 0 : 1; + state->state[0].ready = levels & GPIO_H3600_PCMCIA_IRQ0 ? 1 : 0; + state->state[0].bvd1 = 0; + state->state[0].bvd2 = 0; + state->state[0].wrprot = 0; /* Not available on H3600. */ + state->state[0].vs_3v = 0; + state->state[0].vs_Xv = 0; + + state->state[1].detect = levels & GPIO_H3600_PCMCIA_CD1 ? 0 : 1; + state->state[1].ready = levels & GPIO_H3600_PCMCIA_IRQ1 ? 1 : 0; + state->state[1].bvd1 = 0; + state->state[1].bvd2 = 0; + state->state[1].wrprot = 0; /* Not available on H3600. */ + state->state[1].vs_3v = 0; + state->state[1].vs_Xv = 0; - return 1; + return 1; } -static int h3600_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ - - switch (info->sock) { - case 0: - info->irq=IRQ_GPIO_H3600_PCMCIA_IRQ0; - break; - case 1: - info->irq=IRQ_GPIO_H3600_PCMCIA_IRQ1; - break; - default: - return -1; - } - return 0; +static int h3600_pcmcia_get_irq_info(struct pcmcia_irq_info *info) +{ + switch (info->sock) { + case 0: + info->irq = IRQ_GPIO_H3600_PCMCIA_IRQ0; + break; + case 1: + info->irq = IRQ_GPIO_H3600_PCMCIA_IRQ1; + break; + default: + return -1; + } + return 0; } -static int h3600_pcmcia_configure_socket(const struct pcmcia_configure - *configure) +static int +h3600_pcmcia_configure_socket(const struct pcmcia_configure *conf) { - unsigned long flags; - - if(configure->sock>1) return -1; + if (conf->sock > 1) + return -1; - save_flags_cli(flags); + if (conf->vcc != 0 && conf->vcc != 33 && conf->vcc != 50) { + printk(KERN_ERR "h3600_pcmcia: unrecognized Vcc %u.%uV\n", + conf->vcc / 10, conf->vcc % 10); + return -1; + } + + if (conf->reset) + set_h3600_egpio(IPAQ_EGPIO_CARD_RESET); + else + clr_h3600_egpio(IPAQ_EGPIO_CARD_RESET); - switch (configure->vcc) { - case 0: - clr_h3600_egpio(EGPIO_H3600_OPT_ON); - break; + /* Silently ignore Vpp, output enable, speaker enable. */ - case 33: - case 50: - set_h3600_egpio(EGPIO_H3600_OPT_ON); - break; - - default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, - configure->vcc); - restore_flags(flags); - return -1; - } + return 0; +} - if (configure->reset) - set_h3600_egpio(EGPIO_H3600_CARD_RESET); - else - clr_h3600_egpio(EGPIO_H3600_CARD_RESET); +static int h3600_pcmcia_socket_init(int sock) +{ + /* Enable CF bus: */ + set_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON); + set_h3600_egpio(IPAQ_EGPIO_OPT_ON); + clr_h3600_egpio(IPAQ_EGPIO_OPT_RESET); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(10*HZ / 1000); + + switch (sock) { + case 0: + set_irq_type(IRQ_GPIO_H3600_PCMCIA_CD0, IRQT_BOTHEDGE); + break; + case 1: + set_irq_type(IRQ_GPIO_H3600_PCMCIA_CD1, IRQT_BOTHEDGE); + break; + } - /* Silently ignore Vpp, output enable, speaker enable. */ + return 0; +} - restore_flags(flags); +static int h3600_pcmcia_socket_suspend(int sock) +{ + switch (sock) { + case 0: + set_irq_type(IRQ_GPIO_H3600_PCMCIA_CD0, IRQT_NOEDGE); + break; + case 1: + set_irq_type(IRQ_GPIO_H3600_PCMCIA_CD1, IRQT_NOEDGE); + break; + } + + /* + * FIXME: This doesn't fit well. We don't have the mechanism in + * the generic PCMCIA layer to deal with the idea of two sockets + * on one bus. We rely on the cs.c behaviour shutting down + * socket 0 then socket 1. + */ + if (sock == 1) { + clr_h3600_egpio(IPAQ_EGPIO_OPT_ON); + clr_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON); + /* hmm, does this suck power? */ + set_h3600_egpio(IPAQ_EGPIO_OPT_RESET); + } - return 0; + return 0; } struct pcmcia_low_level h3600_pcmcia_ops = { - h3600_pcmcia_init, - h3600_pcmcia_shutdown, - h3600_pcmcia_socket_state, - h3600_pcmcia_get_irq_info, - h3600_pcmcia_configure_socket + init: h3600_pcmcia_init, + shutdown: h3600_pcmcia_shutdown, + socket_state: h3600_pcmcia_socket_state, + get_irq_info: h3600_pcmcia_get_irq_info, + configure_socket: h3600_pcmcia_configure_socket, + + socket_init: h3600_pcmcia_socket_init, + socket_suspend: h3600_pcmcia_socket_suspend, }; diff -Nru a/drivers/pcmcia/sa1100_jornada720.c b/drivers/pcmcia/sa1100_jornada720.c --- a/drivers/pcmcia/sa1100_jornada720.c Thu Mar 7 18:17:43 2002 +++ b/drivers/pcmcia/sa1100_jornada720.c Thu Mar 7 18:17:43 2002 @@ -6,21 +6,24 @@ */ #include #include +#include -#include #include -#include -#include + +#include "sa1100_generic.h" +#include "sa1111_generic.h" #define SOCKET0_POWER GPIO_GPIO0 #define SOCKET0_3V GPIO_GPIO2 #define SOCKET1_POWER (GPIO_GPIO1 | GPIO_GPIO3) -#define SOCKET1_3V GPIO_GPIO3 +#warning *** Does SOCKET1_3V actually do anything? +#define SOCKET1_3V GPIO_GPIO3 static int jornada720_pcmcia_init(struct pcmcia_init *init) { - int return_val=0; - + /* + * What is all this crap for? + */ GRER |= 0x00000002; /* Set GPIO_A<3:1> to be outputs for PCMCIA/CF power controller: */ PA_DDR = 0; @@ -38,178 +41,82 @@ PC_SDR = 0; PC_SSR = 0; - INTPOL1 |= - (1 << (S0_READY_NINT - SA1111_IRQ(32))) | - (1 << (S1_READY_NINT - SA1111_IRQ(32))) | - (1 << (S0_CD_VALID - SA1111_IRQ(32))) | - (1 << (S1_CD_VALID - SA1111_IRQ(32))) | - (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | - (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))); - - return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT, - "Jornada720 PCMCIA (0) CD", NULL); - return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT, - "Jornada720 CF (1) CD", NULL); - return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT, - "Jornada720 PCMCIA (0) BVD1", NULL); - return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT, - "Jornada720 CF (1) BVD1", NULL); - - return (return_val<0) ? -1 : 2; -} - -static int jornada720_pcmcia_shutdown(void) -{ - free_irq(S0_CD_VALID, NULL); - free_irq(S1_CD_VALID, NULL); - free_irq(S0_BVD1_STSCHG, NULL); - free_irq(S1_BVD1_STSCHG, NULL); - - INTPOL1 &= - ~((1 << (S0_CD_VALID - SA1111_IRQ(32))) | - (1 << (S1_CD_VALID - SA1111_IRQ(32))) | - (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | - (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)))); - - return 0; -} - -static int jornada720_pcmcia_socket_state(struct pcmcia_state_array - *state_array) -{ - unsigned long status; - int return_val=1; - - if(state_array->size<2) return -1; - - memset(state_array->state, 0, - (state_array->size)*sizeof(struct pcmcia_state)); - status=PCSR; - state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0; - state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1; - state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1; - state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1; - state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1; - state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0; - state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; - state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0; - state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1; - state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1; - state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1; - state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1; - state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0; - state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0; - return return_val; -} - -static int jornada720_pcmcia_get_irq_info(struct pcmcia_irq_info *info) -{ - switch(info->sock){ - case 0: - info->irq=S0_READY_NINT; - break; - - case 1: - info->irq=S1_READY_NINT; - break; - - default: - return -1; - } - return 0; + return sa1111_pcmcia_init(init); } -static int jornada720_pcmcia_configure_socket(const struct pcmcia_configure - *configure) +static int +jornada720_pcmcia_configure_socket(const struct pcmcia_configure *conf) { - unsigned long pccr=PCCR, gpio=PA_DWR; + unsigned int pa_dwr_mask, pa_dwr_set; + int ret; printk("%s(): config socket %d vcc %d vpp %d\n", __FUNCTION__, - configure->sock, configure->vcc, configure->vpp); - switch(configure->sock){ + conf->sock, conf->vcc, conf->vpp); + + switch (conf->sock) { case 0: - switch(configure->vcc){ - case 0: - pccr = (pccr & ~PCCR_S0_FLT); - gpio&=~(SOCKET0_POWER | SOCKET0_3V); - break; - - case 33: - pccr = (pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN; - gpio |= SOCKET0_POWER | SOCKET0_3V; - break; - - case 50: - pccr = (pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN); - gpio = (gpio & ~SOCKET0_3V) | SOCKET0_POWER; - break; + pa_dwr_mask = SOCKET0_POWER | SOCKET0_3V; + switch (conf->vcc) { default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, - configure->vcc); - return -1; - } - switch(configure->vpp){ - case 0: - break; - case 50: - printk(KERN_ERR "%s(): 5.0 Vpp %u\n", __FUNCTION__, - configure->vpp); - break; - case 120: - printk(KERN_ERR "%s(): 12 Vpp %u\n", __FUNCTION__, - configure->vpp); - break; - default: - printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__, - configure->vpp); - return -1; + case 0: pa_dwr_set = 0; break; + case 33: pa_dwr_set = SOCKET0_POWER | SOCKET0_3V; break; + case 50: pa_dwr_set = SOCKET0_POWER; break; } - pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST); break; case 1: - switch(configure->vcc){ - case 0: - pccr = (pccr & ~PCCR_S1_FLT); - gpio &= ~(SOCKET1_POWER); - break; - - case 33: - pccr = (pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN; - gpio |= SOCKET1_POWER; - break; - - case 50: - pccr = (pccr | PCCR_S1_PSE | PCCR_S1_FLT | PCCR_S1_PWAITEN); - gpio = (gpio & ~(SOCKET1_POWER)) | SOCKET1_POWER; - break; + pa_dwr_mask = SOCKET1_POWER; + switch (conf->vcc) { default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, - configure->vcc); - return -1; - } - if(configure->vpp!=configure->vcc && configure->vpp!=0){ - printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, - configure->vpp); - return -1; + case 0: pa_dwr_set = 0; break; + case 33: pa_dwr_set = SOCKET1_POWER; break; + case 50: pa_dwr_set = SOCKET1_POWER; break; } - pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST); break; - default: + } + + if (conf->vpp != conf->vcc && conf->vpp != 0) { + printk(KERN_ERR "%s(): slot cannot support VPP %u\n", + __FUNCTION__, conf->vpp); return -1; } - PCCR = pccr; - PA_DWR = gpio; - return 0; + + ret = sa1111_pcmcia_configure_socket(conf); + if (ret == 0) { + unsigned long flags; + + local_irq_save(flags); + PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set; + locla_irq_restore(flags); + } + + return ret; } -struct pcmcia_low_level jornada720_pcmcia_ops = { - jornada720_pcmcia_init, - jornada720_pcmcia_shutdown, - jornada720_pcmcia_socket_state, - jornada720_pcmcia_get_irq_info, - jornada720_pcmcia_configure_socket +static struct pcmcia_low_level jornada720_pcmcia_ops = { + init: jornada720_pcmcia_init, + shutdown: sa1111_pcmcia_shutdown, + socket_state: sa1111_pcmcia_socket_state, + get_irq_info: sa1111_pcmcia_get_irq_info, + configure_socket: jornada720_pcmcia_configure_socket, + + socket_init: sa1111_pcmcia_socket_init, + socket_suspend: sa1111_pcmcia_socket_suspend, }; +int __init pcmcia_jornada720_init(void) +{ + int ret = -ENODEV; + + if (machine_is_jornada720()) + ret = sa1100_register_pcmcia(&jornada720_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_jornada720_exit(void) +{ + sa1100_unregister_pcmcia(&jornada720_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_neponset.c b/drivers/pcmcia/sa1100_neponset.c --- a/drivers/pcmcia/sa1100_neponset.c Thu Mar 7 18:17:46 2002 +++ b/drivers/pcmcia/sa1100_neponset.c Thu Mar 7 18:17:46 2002 @@ -1,249 +1,145 @@ /* - * drivers/pcmcia/sa1100_neponset.c + * linux/drivers/pcmcia/sa1100_neponset.c * * Neponset PCMCIA specific routines - * */ #include #include +#include -#include #include -#include -#include #include +#include -static int neponset_pcmcia_init(struct pcmcia_init *init){ - int return_val=0; - - /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ - PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); - - /* MAX1600 to standby mode: */ - PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); - NCR_0 &= ~(NCR_A0VPP | NCR_A1VPP); - - INTPOL1 |= - (1 << (S0_READY_NINT - SA1111_IRQ(32))) | - (1 << (S1_READY_NINT - SA1111_IRQ(32))) | - (1 << (S0_CD_VALID - SA1111_IRQ(32))) | - (1 << (S1_CD_VALID - SA1111_IRQ(32))) | - (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | - (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))); - - return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT, - "Neponset PCMCIA (0) CD", NULL); - return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT, - "Neponset CF (1) CD", NULL); - return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT, - "Neponset PCMCIA (0) BVD1", NULL); - return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT, - "Neponset CF (1) BVD1", NULL); - - return (return_val<0) ? -1 : 2; -} +#include "sa1100_generic.h" +#include "sa1111_generic.h" -static int neponset_pcmcia_shutdown(void){ +static int neponset_pcmcia_init(struct pcmcia_init *init) +{ + /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ + PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); - free_irq(S0_CD_VALID, NULL); - free_irq(S1_CD_VALID, NULL); - free_irq(S0_BVD1_STSCHG, NULL); - free_irq(S1_BVD1_STSCHG, NULL); - - INTPOL1 &= - ~((1 << (S0_CD_VALID - SA1111_IRQ(32))) | - (1 << (S1_CD_VALID - SA1111_IRQ(32))) | - (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | - (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)))); + /* MAX1600 to standby mode: */ + PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); + NCR_0 &= ~(NCR_A0VPP | NCR_A1VPP); - return 0; + return sa1111_pcmcia_init(init); } -static int neponset_pcmcia_socket_state(struct pcmcia_state_array - *state_array){ - unsigned long status; - int return_val=1; - - if(state_array->size<2) return -1; - - memset(state_array->state, 0, - (state_array->size)*sizeof(struct pcmcia_state)); - - status=PCSR; - - state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0; - - state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1; - - state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1; - - state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1; - - state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1; - - state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0; +static int +neponset_pcmcia_configure_socket(const struct pcmcia_configure *conf) +{ + unsigned int ncr_mask, pa_dwr_mask; + unsigned int ncr_set, pa_dwr_set; + int ret; + + /* Neponset uses the Maxim MAX1600, with the following connections: + + * MAX1600 Neponset + * + * A0VCC SA-1111 GPIO A<1> + * A1VCC SA-1111 GPIO A<0> + * A0VPP CPLD NCR A0VPP + * A1VPP CPLD NCR A1VPP + * B0VCC SA-1111 GPIO A<2> + * B1VCC SA-1111 GPIO A<3> + * B0VPP ground (slot B is CF) + * B1VPP ground (slot B is CF) + * + * VX VCC (5V) + * VY VCC3_3 (3.3V) + * 12INA 12V + * 12INB ground (slot B is CF) + * + * The MAX1600 CODE pin is tied to ground, placing the device in + * "Standard Intel code" mode. Refer to the Maxim data sheet for + * the corresponding truth table. + */ + + switch (conf->sock) { + case 0: + pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1; + ncr_mask = NCR_A0VPP | NCR_A1VPP; + + switch (conf->vcc) { + default: + case 0: pa_dwr_set = 0; break; + case 33: pa_dwr_set = GPIO_GPIO1; break; + case 50: pa_dwr_set = GPIO_GPIO0; break; + } + + switch (conf->vpp) { + case 0: ncr_set = 0; break; + case 120: ncr_set = NCR_A1VPP; break; + default: + if (conf->vpp == conf->vcc) + ncr_set = NCR_A0VPP; + else { + printk(KERN_ERR "%s(): unrecognized VPP %u\n", + __FUNCTION__, conf->vpp); + return -1; + } + } + break; + + case 1: + pa_dwr_mask = GPIO_GPIO2 | GPIO_GPIO3; + ncr_mask = 0; + ncr_set = 0; + + switch (conf->vcc) { + default: + case 0: pa_dwr_set = 0; break; + case 33: pa_dwr_set = GPIO_GPIO2; break; + case 50: pa_dwr_set = GPIO_GPIO3; break; + } + + if (conf->vpp != conf->vcc && conf->vpp != 0) { + printk(KERN_ERR "%s(): CF slot cannot support VPP %u\n", + __FUNCTION__, conf->vpp); + return -1; + } + break; + + default: + return -1; + } + + ret = sa1111_pcmcia_configure_socket(conf); + if (ret == 0) { + unsigned long flags; + + local_irq_save(flags); + NCR_0 = (NCR_0 & ~ncr_mask) | ncr_set; + PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set; + local_irq_restore(flags); + } - state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; - - state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0; - - state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1; - - state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1; - - state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1; - - state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1; - - state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0; - - state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0; - - return return_val; + return 0; } -static int neponset_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ +static struct pcmcia_low_level neponset_pcmcia_ops = { + init: neponset_pcmcia_init, + shutdown: sa1111_pcmcia_shutdown, + socket_state: sa1111_pcmcia_socket_state, + get_irq_info: sa1111_pcmcia_get_irq_info, + configure_socket: neponset_pcmcia_configure_socket, - switch(info->sock){ - case 0: - info->irq=S0_READY_NINT; - break; - - case 1: - info->irq=S1_READY_NINT; - break; - - default: - return -1; - } + socket_init: sa1111_pcmcia_socket_init, + socket_suspend: sa1111_pcmcia_socket_suspend, +}; - return 0; -} +int __init pcmcia_neponset_init(void) +{ + int ret = -ENODEV; -static int neponset_pcmcia_configure_socket(const struct pcmcia_configure - *configure){ - unsigned long pccr=PCCR, ncr=NCR_0, gpio=PA_DWR; - - /* Neponset uses the Maxim MAX1600, with the following connections: - * - * MAX1600 Neponset - * - * A0VCC SA-1111 GPIO A<1> - * A1VCC SA-1111 GPIO A<0> - * A0VPP CPLD NCR A0VPP - * A1VPP CPLD NCR A1VPP - * B0VCC SA-1111 GPIO A<2> - * B1VCC SA-1111 GPIO A<3> - * B0VPP ground (slot B is CF) - * B1VPP ground (slot B is CF) - * - * VX VCC (5V) - * VY VCC3_3 (3.3V) - * 12INA 12V - * 12INB ground (slot B is CF) - * - * The MAX1600 CODE pin is tied to ground, placing the device in - * "Standard Intel code" mode. Refer to the Maxim data sheet for - * the corresponding truth table. - */ - - switch(configure->sock){ - case 0: - - switch(configure->vcc){ - case 0: - pccr=(pccr & ~PCCR_S0_FLT); - gpio&=~(GPIO_GPIO0 | GPIO_GPIO1); - break; - - case 33: - pccr=(pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN; - gpio=(gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO1; - break; - - case 50: - pccr=(pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN); - gpio=(gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO0; - break; - - default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, - configure->vcc); - return -1; - } - - switch(configure->vpp){ - case 0: - ncr&=~(NCR_A0VPP | NCR_A1VPP); - break; - - case 120: - ncr=(ncr & ~(NCR_A0VPP | NCR_A1VPP)) | NCR_A1VPP; - break; - - default: - if(configure->vpp == configure->vcc) - ncr=(ncr & ~(NCR_A0VPP | NCR_A1VPP)) | NCR_A0VPP; - else { - printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__, - configure->vpp); - return -1; - } - } - - pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST); - - break; - - case 1: - switch(configure->vcc){ - case 0: - pccr=(pccr & ~PCCR_S1_FLT); - gpio&=~(GPIO_GPIO2 | GPIO_GPIO3); - break; - - case 33: - pccr=(pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN; - gpio=(gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO2; - break; - - case 50: - pccr=(pccr | PCCR_S1_PSE | PCCR_S1_FLT | PCCR_S1_PWAITEN); - gpio=(gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO3; - break; - - default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, - configure->vcc); - return -1; - } - - if(configure->vpp!=configure->vcc && configure->vpp!=0){ - printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, - configure->vpp); - return -1; - } - - pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST); - - break; - - default: - return -1; - } - - PCCR = pccr; - NCR_0 = ncr; - PA_DWR = gpio; + if (machine_is_assabet() && machine_has_neponset()) + ret = sa1100_register_pcmcia(&neponset_pcmcia_ops); - return 0; + return ret; } -struct pcmcia_low_level neponset_pcmcia_ops = { - neponset_pcmcia_init, - neponset_pcmcia_shutdown, - neponset_pcmcia_socket_state, - neponset_pcmcia_get_irq_info, - neponset_pcmcia_configure_socket -}; - +void __exit pcmcia_neponset_exit(void) +{ + sa1100_unregister_pcmcia(&neponset_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_pangolin.c b/drivers/pcmcia/sa1100_pangolin.c --- a/drivers/pcmcia/sa1100_pangolin.c Thu Mar 7 18:17:37 2002 +++ b/drivers/pcmcia/sa1100_pangolin.c Thu Mar 7 18:17:37 2002 @@ -4,48 +4,45 @@ * PCMCIA implementation routines for Pangolin * */ +#include #include #include +#include #include #include -#include +#include "sa1100_generic.h" static int pangolin_pcmcia_init(struct pcmcia_init *init){ - int irq, res; + int res; - /* set GPIO_PCMCIA_CD & GPIO_PCMCIA_IRQ as inputs */ - GPDR &= ~(GPIO_PCMCIA_CD|GPIO_PCMCIA_IRQ); #ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE - /* set GPIO pins GPIO_PCMCIA_BUS_ON & GPIO_PCMCIA_RESET as output */ - GPDR |= (GPIO_PCMCIA_BUS_ON|GPIO_PCMCIA_RESET); /* Enable PCMCIA bus: */ GPCR = GPIO_PCMCIA_BUS_ON; -#else - /* set GPIO pin GPIO_PCMCIA_RESET as output */ - GPDR |= GPIO_PCMCIA_RESET; #endif + /* Set transition detect */ - set_GPIO_IRQ_edge( GPIO_PCMCIA_CD, GPIO_BOTH_EDGES ); - set_GPIO_IRQ_edge( GPIO_PCMCIA_IRQ, GPIO_FALLING_EDGE ); + set_irq_type(IRQ_PCMCIA_CD, IRQT_NOEDGE); + set_irq_type(IRQ_PCMCIA_IRQ, IRQT_FALLING); /* Register interrupts */ - irq = IRQ_PCMCIA_CD; - res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD", NULL ); - if( res < 0 ) goto irq_err; - - /* There's only one slot, but it's "Slot 1": */ - return 2; + res = request_irq(IRQ_PCMCIA_CD, init->handler, SA_INTERRUPT, + "PCMCIA_CD", NULL); + if (res >= 0) + /* There's only one slot, but it's "Slot 1": */ + return 2; irq_err: - printk( KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq ); - return -1; + printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n", + __FUNCTION__, IRQ_PCMCIA_CD, res); + + return res; } static int pangolin_pcmcia_shutdown(void) { /* disable IRQs */ - free_irq( IRQ_PCMCIA_CD, NULL ); + free_irq(IRQ_PCMCIA_CD, NULL); #ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE /* Disable PCMCIA bus: */ GPSR = GPIO_PCMCIA_BUS_ON; @@ -105,7 +102,7 @@ #ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE if(configure->sock==0) return 0; #endif - save_flags_cli(flags); + local_irq_save(flags); /* Murphy: BUS_ON different from POWER ? */ @@ -129,7 +126,7 @@ default: printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, configure->vcc); - restore_flags(flags); + local_irq_restore(flags); return -1; } #ifdef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE @@ -143,15 +140,47 @@ } #endif /* Silently ignore Vpp, output enable, speaker enable. */ - restore_flags(flags); + local_irq_restore(flags); return 0; } -struct pcmcia_low_level pangolin_pcmcia_ops = { - pangolin_pcmcia_init, - pangolin_pcmcia_shutdown, - pangolin_pcmcia_socket_state, - pangolin_pcmcia_get_irq_info, - pangolin_pcmcia_configure_socket +static int pangolin_pcmcia_socket_init(int sock) +{ + if (sock == 1) + set_irq_type(IRQ_PCmCIA_CD, IRQT_BOTHEDGE); + return 0; +} + +static int pangolin_pcmcia_socket_suspend(int sock) +{ + if (sock == 1) + set_irq_type(IRQ_PCmCIA_CD, IRQT_NOEDGE); + return 0; +} + +static struct pcmcia_low_level pangolin_pcmcia_ops = { + init: pangolin_pcmcia_init, + shutdown: pangolin_pcmcia_shutdown, + socket_state: pangolin_pcmcia_socket_state, + get_irq_info: pangolin_pcmcia_get_irq_info, + configure_socket: pangolin_pcmcia_configure_socket, + + socket_init: pangolin_pcmcia_socket_init, + socket_suspend, pangolin_pcmcia_socket_suspend, }; + +int __init pcmcia_pangolin_init(void) +{ + int ret = -ENODEV; + + if (machine_is_pangolin()) + ret = sa1100_register_pcmcia(&pangolin_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_pangolin_exit(void) +{ + sa1100_unregister_pcmcia(&pangolin_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_pfs168.c b/drivers/pcmcia/sa1100_pfs168.c --- a/drivers/pcmcia/sa1100_pfs168.c Thu Mar 7 18:17:42 2002 +++ b/drivers/pcmcia/sa1100_pfs168.c Thu Mar 7 18:17:42 2002 @@ -7,121 +7,31 @@ */ #include #include +#include #include #include #include -#include -static int pfs168_pcmcia_init(struct pcmcia_init *init){ - int return_val=0; +#include "sa1100_generic.h" +#include "sa1111_generic.h" +static int pfs168_pcmcia_init(struct pcmcia_init *init) +{ /* TPS2211 to standby mode: */ PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); /* Set GPIO_A<3:0> to be outputs for PCMCIA (socket 0) power controller: */ PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); - INTPOL1 |= - (1 << (S0_READY_NINT - SA1111_IRQ(32))) | - (1 << (S1_READY_NINT - SA1111_IRQ(32))) | - (1 << (S0_CD_VALID - SA1111_IRQ(32))) | - (1 << (S1_CD_VALID - SA1111_IRQ(32))) | - (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | - (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))); - - return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT, - "PFS168 PCMCIA (0) CD", NULL); - return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT, - "PFS168 CF (1) CD", NULL); - return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT, - "PFS168 PCMCIA (0) BVD1", NULL); - return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT, - "PFS168 CF (1) BVD1", NULL); - - return (return_val<0) ? -1 : 2; -} - -static int pfs168_pcmcia_shutdown(void){ - - free_irq(S0_CD_VALID, NULL); - free_irq(S1_CD_VALID, NULL); - free_irq(S0_BVD1_STSCHG, NULL); - free_irq(S1_BVD1_STSCHG, NULL); - - INTPOL1 &= - ~((1 << (S0_CD_VALID - SA1111_IRQ(32))) | - (1 << (S1_CD_VALID - SA1111_IRQ(32))) | - (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | - (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)))); - - return 0; -} - -static int pfs168_pcmcia_socket_state(struct pcmcia_state_array - *state_array){ - unsigned long status; - int return_val=1; - - if(state_array->size<2) return -1; - - memset(state_array->state, 0, - (state_array->size)*sizeof(struct pcmcia_state)); - - status=PCSR; - - state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0; - - state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1; - - state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1; - - state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1; - - state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1; - - state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0; - - state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; - - state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0; - - state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1; - - state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1; - - state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1; - - state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1; - - state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0; - - state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0; - - return return_val; -} - -static int pfs168_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ - - switch(info->sock){ - case 0: - info->irq=S0_READY_NINT; - break; - - case 1: - info->irq=S1_READY_NINT; - break; - - default: - return -1; - } - - return 0; + return sa1111_pcmcia_init(init); } -static int pfs168_pcmcia_configure_socket(const struct pcmcia_configure - *configure){ - unsigned long pccr=PCCR, gpio=PA_DWR; +static int +pfs168_pcmcia_configure_socket(const struct pcmcia_configure *conf) +{ + unsigned int pa_dwr_mask = 0, pa_dwr_set = 0; + int ret; /* PFS168 uses the Texas Instruments TPS2211 for PCMCIA (socket 0) voltage control only, * with the following connections: @@ -135,103 +45,100 @@ * */ - switch(configure->sock){ + switch (conf->sock) { case 0: + pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3; - switch(configure->vcc){ - case 0: - pccr = (pccr & ~PCCR_S0_FLT); - gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1); - break; - - case 33: - pccr = (pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN; - gpio = (gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO0; - break; - - case 50: - pccr = (pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN); - gpio = (gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO1; - break; - + switch (conf->vcc) { default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, - configure->vcc); - return -1; + case 0: pa_dwr_set = 0; break; + case 33: pa_dwr_set = GPIO_GPIO0; break; + case 50: pa_dwr_set = GPIO_GPIO1; break; } - switch(configure->vpp){ + switch (conf->vpp) { case 0: - gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3); break; case 120: - printk(KERN_ERR "%s(): PFS-168 does not support Vpp %uV\n", __FUNCTION__, - configure->vpp/10); + printk(KERN_ERR "%s(): PFS-168 does not support VPP %uV\n", + __FUNCTION__, conf->vpp / 10); return -1; break; default: - if(configure->vpp == configure->vcc) - gpio = (gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO3; + if (conf->vpp == conf->vcc) + pa_dwr_set |= GPIO_GPIO3; else { - printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__, - configure->vpp); + printk(KERN_ERR "%s(): unrecognized VPP %u\n", __FUNCTION__, + conf->vpp); return -1; } } - - pccr = (configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST); - - PA_DWR = gpio; - break; case 1: - switch(configure->vcc){ - case 0: - pccr = (pccr & ~PCCR_S1_FLT); - break; + pa_dwr_mask = 0; + pa_dwr_set = 0; + switch (conf->vcc) { + case 0: case 33: - pccr = (pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN; break; case 50: - printk(KERN_ERR "%s(): PFS-168 CompactFlash socket does not support Vcc %uV\n", __FUNCTION__, - configure->vcc/10); + printk(KERN_ERR "%s(): PFS-168 CompactFlash socket does not support VCC %uV\n", + __FUNCTION__, conf->vcc / 10); return -1; - break; default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, - configure->vcc); + printk(KERN_ERR "%s(): unrecognized VCC %u\n", __FUNCTION__, + conf->vcc); return -1; } - if(configure->vpp!=configure->vcc && configure->vpp!=0){ - printk(KERN_ERR "%s(): CompactFlash socket does not support Vpp %uV\n", __FUNCTION__, - configure->vpp/10); + if (conf->vpp != conf->vcc && conf->vpp != 0) { + printk(KERN_ERR "%s(): CompactFlash socket does not support VPP %uV\n" + __FUNCTION__, conf->vpp / 10); return -1; } - - pccr = (configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST); - break; - - default: - return -1; } - PCCR = pccr; + ret = sa1111_pcmcia_configure_socket(conf); + if (ret == 0) { + unsigned long flags; + + local_irq_save(flags); + PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set; + local_irq_restore(flags); + } return 0; } -struct pcmcia_low_level pfs168_pcmcia_ops = { - pfs168_pcmcia_init, - pfs168_pcmcia_shutdown, - pfs168_pcmcia_socket_state, - pfs168_pcmcia_get_irq_info, - pfs168_pcmcia_configure_socket +static struct pcmcia_low_level pfs168_pcmcia_ops = { + init: pfs168_pcmcia_init, + shutdown: sa1111_pcmcia_shutdown, + socket_state: sa1111_pcmcia_socket_state, + get_irq_info: sa1111_pcmcia_get_irq_info, + configure_socket: pfs168_pcmcia_configure_socket, + + socket_init: sa1111_pcmcia_socket_init, + socket_suspend: sa1111_pcmcia_socket_suspend, }; + +int __init pcmcia_pfs168_init(void) +{ + int ret = -ENODEV; + + if (machine_is_pfs168()) + ret = sa1100_register_pcmcia(&pfs168_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_pfs168_exit(void) +{ + sa1100_unregister_pcmcia(&pfs168_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_shannon.c b/drivers/pcmcia/sa1100_shannon.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pcmcia/sa1100_shannon.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,177 @@ +/* + * drivers/pcmcia/sa1100_shannon.c + * + * PCMCIA implementation routines for Shannon + * + */ +#include +#include +#include + +#include +#include +#include +#include "sa1100_generic.h" + +static struct irqs { + int irq; + const char *str; +} irqs[] = { + { SHANNON_IRQ_GPIO_EJECT_0, "PCMCIA_CD_0" }, + { SHANNON_IRQ_GPIO_EJECT_1, "PCMCIA_CD_1" }, +}; + +static int shannon_pcmcia_init(struct pcmcia_init *init) +{ + int i, res; + + /* All those are inputs */ + GPDR &= ~(SHANNON_GPIO_EJECT_0 | SHANNON_GPIO_EJECT_1 | + SHANNON_GPIO_RDY_0 | SHANNON_GPIO_RDY_1); + GAFR &= ~(SHANNON_GPIO_EJECT_0 | SHANNON_GPIO_EJECT_1 | + SHANNON_GPIO_RDY_0 | SHANNON_GPIO_RDY_1); + + /* Set transition detect */ + set_irq_type(SHANNON_IRQ_GPIO_RDY_0, IRQT_FALLING); + set_irq_type(SHANNON_IRQ_GPIO_RDY_1, IRQT_FALLING); + + /* Register interrupts */ + for (i = 0; i < ARRAY_SIZE(irqs); i++) { + set_irq_type(irqs[i].irq, IRQT_NOEDGE); + res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT, + irqs[i].str, NULL); + if (res) + goto irq_err; + } + + return 2; + + irq_err: + printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n", + __FUNCTION__, irqs[i].irq, res); + + while (i--) + free_irq(irqs[i].irq, NULL); + + return res; +} + +static int shannon_pcmcia_shutdown(void) +{ + int i; + + /* disable IRQs */ + for (i = 0; i < ARRAY_SIZE(irqs); i++) + free_irq(irqs[i].irq, NULL); + + return 0; +} + +static int shannon_pcmcia_socket_state(struct pcmcia_state_array *state_array) +{ + unsigned long levels; + + memset(state_array->state, 0, + state_array->size * sizeof(struct pcmcia_state)); + + levels = GPLR; + + state_array->state[0].detect = (levels & SHANNON_GPIO_EJECT_0) ? 0 : 1; + state_array->state[0].ready = (levels & SHANNON_GPIO_RDY_0) ? 1 : 0; + state_array->state[0].wrprot = 0; /* Not available on Shannon. */ + state_array->state[0].bvd1 = 1; + state_array->state[0].bvd2 = 1; + state_array->state[0].vs_3v = 1; /* FIXME Can only apply 3.3V on Shannon. */ + state_array->state[0].vs_Xv = 0; + + state_array->state[1].detect = (levels & SHANNON_GPIO_EJECT_1) ? 0 : 1; + state_array->state[1].ready = (levels & SHANNON_GPIO_RDY_1) ? 1 : 0; + state_array->state[1].wrprot = 0; /* Not available on Shannon. */ + state_array->state[1].bvd1 = 1; + state_array->state[1].bvd2 = 1; + state_array->state[1].vs_3v = 1; /* FIXME Can only apply 3.3V on Shannon. */ + state_array->state[1].vs_Xv = 0; + + return 1; +} + +static int shannon_pcmcia_get_irq_info(struct pcmcia_irq_info *info) +{ + if (info->sock == 0) + info->irq = SHANNON_IRQ_GPIO_RDY_0; + else if (info->sock == 1) + info->irq = SHANNON_IRQ_GPIO_RDY_1; + else return -1; + + return 0; +} + +static int shannon_pcmcia_configure_socket(const struct pcmcia_configure *configure) +{ + + switch (configure->vcc) { + case 0: /* power off */ + printk(KERN_WARNING __FUNCTION__"(): CS asked for 0V, still applying 3.3V..\n"); + break; + case 50: + printk(KERN_WARNING __FUNCTION__"(): CS asked for 5V, applying 3.3V..\n"); + case 33: + break; + default: + printk(KERN_ERR __FUNCTION__"(): unrecognized Vcc %u\n", + configure->vcc); + return -1; + } + + printk(KERN_WARNING __FUNCTION__"(): Warning, Can't perform reset\n"); + + /* Silently ignore Vpp, output enable, speaker enable. */ + + return 0; +} + +static int shannon_pcmcia_socket_init(int sock) +{ + if (sock == 0) + set_irq_type(SHANNON_IRQ_GPIO_EJECT_0, IRQT_BOTHEDGE); + else if (sock == 1) + set_irq_Type(SHANNON_IRQ_GPIO_EJECT_1, IRQT_BOTHEDGE); + + return 0; +} + +static int shannon_pcmcia_socket_suspend(int sock) +{ + if (sock == 0) + set_irq_type(SHANNON_IRQ_GPIO_EJECT_0, IRQT_NOEDGE); + else if (sock == 1) + set_irq_type(SHANNON_IRQ_GPIO_EJECT_1, IRQT_NOEDGE); + + return 0; +} + +static struct pcmcia_low_level shannon_pcmcia_ops = { + init: shannon_pcmcia_init, + shutdown: shannon_pcmcia_shutdown, + socket_state: shannon_pcmcia_socket_state, + get_irq_info: shannon_pcmcia_get_irq_info, + configure_socket: shannon_pcmcia_configure_socket, + + socket_init: shannon_pcmcia_socket_init, + socket_suspend: shannon_pcmcia_socket_suspend, +}; + +int __init pcmcia_shannon_init(void) +{ + int ret = -ENODEV; + + if (machine_is_shannon()) + ret = sa1100_register_pcmcia(&shannon_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_shannon_exit(void) +{ + sa1100_unregister_pcmcia(&shannon_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_simpad.c b/drivers/pcmcia/sa1100_simpad.c --- a/drivers/pcmcia/sa1100_simpad.c Thu Mar 7 18:17:44 2002 +++ b/drivers/pcmcia/sa1100_simpad.c Thu Mar 7 18:17:44 2002 @@ -6,10 +6,11 @@ */ #include #include +#include #include #include -#include +#include "sa1100_generic.h" extern long get_cs3_shadow(void); extern void set_cs3_bit(int value); @@ -19,9 +20,6 @@ static int simpad_pcmcia_init(struct pcmcia_init *init){ int irq, res; - /* set GPIO_CF_CD & GPIO_CF_IRQ as inputs */ - GPDR &= ~(GPIO_CF_CD|GPIO_CF_IRQ); - set_cs3_bit(PCMCIA_RESET); clear_cs3_bit(PCMCIA_BUFF_DIS); clear_cs3_bit(PCMCIA_RESET); @@ -29,8 +27,8 @@ clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); /* Set transition detect */ - set_GPIO_IRQ_edge( GPIO_CF_CD, GPIO_BOTH_EDGES ); - set_GPIO_IRQ_edge( GPIO_CF_IRQ, GPIO_FALLING_EDGE ); + set_irq_type( IRQ_GPIO_CF_CD, IRQT_NOEDGE ); + set_irq_type( IRQ_GPIO_CF_IRQ, IRQT_FALLING ); /* Register interrupts */ irq = IRQ_GPIO_CF_CD; @@ -41,8 +39,9 @@ return 2; irq_err: - printk( KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq ); - return -1; + printk( KERN_ERR "%s: request for IRQ%d failed (%d)\n", + __FUNCTION__, irq, res); + return res; } static int simpad_pcmcia_shutdown(void) @@ -112,7 +111,7 @@ if(configure->sock==0) return 0; - save_flags_cli(flags); + local_irq_save(flags); /* Murphy: see table of MIC2562a-1 */ @@ -135,22 +134,51 @@ printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, configure->vcc); clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); - restore_flags(flags); + local_irq_restore(flags); return -1; } /* Silently ignore Vpp, output enable, speaker enable. */ - restore_flags(flags); + local_irq_restore(flags); return 0; } -struct pcmcia_low_level simpad_pcmcia_ops = { - simpad_pcmcia_init, - simpad_pcmcia_shutdown, - simpad_pcmcia_socket_state, - simpad_pcmcia_get_irq_info, - simpad_pcmcia_configure_socket +static int simpad_pcmcia_socket_init(int sock) +{ + set_irq_type(IRQ_GPIO_CF_CD, IRQT_BOTHEDGE); + return 0; +} + +static int simpad_pcmcia_socket_suspend(int sock) +{ + set_irq_type(IRQ_GPIO_CF_CD, IRQT_NOEDGE); + return 0; +} + +static struct pcmcia_low_level simpad_pcmcia_ops = { + init: simpad_pcmcia_init, + shutdown: simpad_pcmcia_shutdown, + socket_state: simpad_pcmcia_socket_state, + get_irq_info: simpad_pcmcia_get_irq_info, + configure_socket: simpad_pcmcia_configure_socket, + + socket_init: simpad_pcmcia_socket_init, + socket_suspend: simpad_pcmcia_socket_suspend, }; +int __init pcmcia_simpad_init(void) +{ + int ret = -ENODEV; + + if (machine_is_simpad()) + ret = sa1100_register_pcmcia(&simpad_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_simpad_exit(void) +{ + sa1100_unregister_pcmcia(&simpad_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_stork.c b/drivers/pcmcia/sa1100_stork.c --- a/drivers/pcmcia/sa1100_stork.c Thu Mar 7 18:17:45 2002 +++ b/drivers/pcmcia/sa1100_stork.c Thu Mar 7 18:17:45 2002 @@ -18,8 +18,6 @@ * PCMCIA implementation routines for stork * */ - -#include #include #include #include @@ -28,50 +26,58 @@ #include #include -#include +#include "sa1100_generic.h" static int debug = 0; -static struct pcmcia_init sa1100_stork_pcmcia_init; +static struct irqs { + int irq; + const char *str; +} irqs[] = { + { IRQ_GPIO_STORK_PCMCIA_A_CARD_DETECT, "PCMCIA_CD0" }, + { IRQ_GPIO_STORK_PCMCIA_B_CARD_DETECT, "PCMCIA_CD1" }, +}; static int stork_pcmcia_init(struct pcmcia_init *init) { - int irq, res; - printk("in stork_pcmcia_init\n"); - - sa1100_stork_pcmcia_init = *init; - - /* Enable CF bus: */ - storkSetLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON); + int irq, res; - /* All those are inputs */ - GPDR &= ~(GPIO_STORK_PCMCIA_A_CARD_DETECT | GPIO_STORK_PCMCIA_B_CARD_DETECT | GPIO_STORK_PCMCIA_A_RDY| GPIO_STORK_PCMCIA_B_RDY); + printk("in stork_pcmcia_init\n"); /* Set transition detect */ - set_GPIO_IRQ_edge( GPIO_STORK_PCMCIA_A_CARD_DETECT | GPIO_STORK_PCMCIA_B_CARD_DETECT, GPIO_BOTH_EDGES ); - set_GPIO_IRQ_edge( GPIO_STORK_PCMCIA_A_RDY| GPIO_STORK_PCMCIA_B_RDY, GPIO_FALLING_EDGE ); + set_irq_type(IRQ_GPIO_STORK_PCMCIA_A_RDY, IRQT_FALLING); + set_irq_type(IRQ_GPIO_STORK_PCMCIA_B_RDY, IRQT_FALLING); /* Register interrupts */ - irq = IRQ_GPIO_STORK_PCMCIA_A_CARD_DETECT; - res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD0", NULL ); - if( res < 0 ) goto irq_err; - irq = IRQ_GPIO_STORK_PCMCIA_B_CARD_DETECT; - res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD1", NULL ); - if( res < 0 ) goto irq_err; + for (i = 0; i < ARRAY_SIZE(irqs); i++) { + set_irq_type(irqs[i].irq, IRQT_NOEDGE); + res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT, + irqs[i].str, NULL); + if (res) + goto irq_err; + } return 2; irq_err: - printk( KERN_ERR __FUNCTION__ ": Request for IRQ %u failed\n", irq ); - return -1; + printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n", + __FUNCTION__, irq, res); + + while (i--) + free_irq(irqs[i].irq, NULL); + + return res; } static int stork_pcmcia_shutdown(void) { + int i; + printk(__FUNCTION__ "\n"); + /* disable IRQs */ - free_irq( IRQ_GPIO_STORK_PCMCIA_A_CARD_DETECT, NULL ); - free_irq( IRQ_GPIO_STORK_PCMCIA_B_CARD_DETECT, NULL ); + for (i = 0; i < ARRAY_SIZE(irqs); i++) + free_irq(irqs[i].irq, NULL); /* Disable CF bus: */ storkClearLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON); @@ -140,7 +146,7 @@ printk(__FUNCTION__ ": socket=%d vcc=%d vpp=%d reset=%d\n", card, configure->vcc, configure->vpp, configure->reset); - save_flags_cli(flags); + local_irq_save(flags); if (card == 0) { DETECT = GPIO_STORK_PCMCIA_A_CARD_DETECT; @@ -174,7 +180,7 @@ default: printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, configure->vcc); - restore_flags(flags); + local_irq_restore(flags); return -1; } @@ -183,7 +189,7 @@ else storkClearLatchB(RESET); - restore_flags(flags); + local_irq_restore(flags); /* silently ignore vpp and speaker enables. */ @@ -192,11 +198,57 @@ return 0; } -struct pcmcia_low_level stork_pcmcia_ops = { - stork_pcmcia_init, - stork_pcmcia_shutdown, - stork_pcmcia_socket_state, - stork_pcmcia_get_irq_info, - stork_pcmcia_configure_socket +static int stork_pcmcia_socket_init(int sock) +{ + storkSetLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON); + + if (sock == 0) + set_irq_type(IRQ_GPIO_STORK_PCMCIA_A_CARD_DETECT, IRQT_BOTHEDGE); + else if (sock == 1) + set_irq_type(IRQ_GPIO_STORK_PCMCIA_B_CARD_DETECT, IRQT_BOTHEDGE); + + return 0; +} + +static int stork_pcmcia_socket_suspend(int sock) +{ + if (sock == 0) + set_irq_type(IRQ_GPIO_STORK_PCMCIA_A_CARD_DETECT, IRQT_NOEDGE); + else if (sock == 1) { + set_irq_type(IRQ_GPIO_STORK_PCMCIA_B_CARD_DETECT, IRQT_NOEDGE); + + /* + * Hack! + */ + storkClearLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON); + } + + return 0; +} + +static struct pcmcia_low_level stork_pcmcia_ops = { + init: stork_pcmcia_init, + shutdown: stork_pcmcia_shutdown, + socket_state: stork_pcmcia_socket_state, + get_irq_info: stork_pcmcia_get_irq_info, + configure_socket: stork_pcmcia_configure_socket, + + socket_init: stork_pcmcia_socket_init, + socket_suspend: stork_pcmcia_socket_suspend, }; + +int __init pcmcia_stork_init(void) +{ + int ret = -ENODEV; + + if (machine_is_stork()) + ret = sa1100_register_pcmcia(&stork_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_stork_exit(void) +{ + sa1100_unregister_pcmcia(&stork_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_system3.c b/drivers/pcmcia/sa1100_system3.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pcmcia/sa1100_system3.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,131 @@ +/* + * drivers/pcmcia/sa1100_system3.c + * + * PT Diagital Board PCMCIA specific routines + * + * Copyright (C) 2001 Stefan Eletzhofer + * + * $Id: sa1100_system3.c,v 1.1.4.2 2002/02/25 13:56:45 seletz Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * $Log: sa1100_system3.c,v $ + * Revision 1.1.4.2 2002/02/25 13:56:45 seletz + * - more cleanups + * - setup interrupts for CF card only ATM + * + * Revision 1.1.4.1 2002/02/14 02:23:27 seletz + * - 2.5.2-rmk6 PCMCIA changes + * + * Revision 1.1.2.1 2002/02/13 23:49:33 seletz + * - added from 2.4.16-rmk2 + * - cleanups + * + * + */ +#include +#include +#include +#include + +#include +#include +#include + +#include "sa1100_generic.h" +#include "sa1111_generic.h" + +#define DEBUG 0 + +#ifdef DEBUG +# define DPRINTK( x, args... ) printk( "%s: line %d: "x, __FUNCTION__, __LINE__, ## args ); +#else +# define DPRINTK( x, args... ) /* nix */ +#endif + +int system3_pcmcia_init(struct pcmcia_init *init) +{ + /* Don't need no CD and BVD* interrupts */ + return 2; +} + +int system3_pcmcia_shutdown(void) +{ + return 0; +} + +int system3_pcmcia_configure_socket(const struct pcmcia_configure *conf) +{ + if (!conf) + return -1; + + /* only CF ATM */ + if (conf->sock == 0) + return -1; + + return sa1111_pcmcia_configure_socket( conf ); +} + +static int system3_pcmcia_socket_state(struct pcmcia_state_array + *state) +{ + unsigned long status = 0; + + if(state->size<2) return -1; + + memset(state->state, 0, + (state->size)*sizeof(struct pcmcia_state)); + + status=PCSR; + +#if 0 /* PCMCIA socket not yet connected */ + state->state[0].detect = status & PCSR_S0_DETECT ? 0 : 1; + state->state[0].ready = status & PCSR_S0_READY ? 1 : 0; + state->state[0].bvd1 = status & PCSR_S0_BVD1 ? 1 : 0; + state->state[0].bvd2 = 1; + state->state[0].wrprot = status & PCSR_S0_WP ? 1 : 0; + state->state[0].vs_3v = 1; + state->state[0].vs_Xv = 0; +#endif + + state->state[1].detect = status & PCSR_S1_DETECT ? 0 : 1; + state->state[1].ready = status & PCSR_S1_READY ? 1 : 0; + state->state[1].bvd1 = status & PCSR_S1_BVD1 ? 1 : 0; + state->state[1].bvd2 = 1; + state->state[1].wrprot = status & PCSR_S1_WP ? 1 : 0; + state->state[1].vs_3v = 1; + state->state[1].vs_Xv = 0; + + DPRINTK( "PCSR=0x%08lx, S1_RDY_nIREQ=%d\n", status, + state->state[1].ready ); + + return 1; +} + +struct pcmcia_low_level system3_pcmcia_ops = { + init: system3_pcmcia_init, + shutdown: system3_pcmcia_shutdown, + socket_state: system3_pcmcia_socket_state, + get_irq_info: sa1111_pcmcia_get_irq_info, + configure_socket: system3_pcmcia_configure_socket, + + socket_init: sa1111_pcmcia_socket_init, + socket_suspend: sa1111_pcmcia_socket_suspend, +}; + +int __init pcmcia_system3_init(void) +{ + int ret = -ENODEV; + + if (machine_is_pt_system3()) + ret = sa1100_register_pcmcia(&system3_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_system3_exit(void) +{ + sa1100_unregister_pcmcia(&system3_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_xp860.c b/drivers/pcmcia/sa1100_xp860.c --- a/drivers/pcmcia/sa1100_xp860.c Thu Mar 7 18:17:42 2002 +++ b/drivers/pcmcia/sa1100_xp860.c Thu Mar 7 18:17:42 2002 @@ -7,128 +7,46 @@ #include #include #include +#include #include #include -#include +#include "sa1100_generic.h" #define NCR_A0VPP (1<<16) #define NCR_A1VPP (1<<17) -static int xp860_pcmcia_init(struct pcmcia_init *init){ - int return_val=0; - +static int xp860_pcmcia_init(struct pcmcia_init *init) +{ /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); /* MAX1600 to standby mode: */ PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); + +#error Consider the following comment + /* + * 1- Please move GPDR initialisation where it is interrupt or preemption + * safe (like from xp860_map_io). + * 2- The GPCR line is bogus i.e. it will simply have absolutely no effect. + * Please see its definition in the SA1110 manual. + * 3- Please do not use NCR_* values! + */ GPDR |= (NCR_A0VPP | NCR_A1VPP); GPCR &= ~(NCR_A0VPP | NCR_A1VPP); - INTPOL1 |= - (1 << (S0_READY_NINT - SA1111_IRQ(32))) | - (1 << (S1_READY_NINT - SA1111_IRQ(32))) | - (1 << (S0_CD_VALID - SA1111_IRQ(32))) | - (1 << (S1_CD_VALID - SA1111_IRQ(32))) | - (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | - (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))); - - return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT, - "XP860 PCMCIA (0) CD", NULL); - return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT, - "XP860 CF (1) CD", NULL); - return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT, - "XP860 PCMCIA (0) BVD1", NULL); - return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT, - "XP860 CF (1) BVD1", NULL); - - return (return_val<0) ? -1 : 2; -} - -static int xp860_pcmcia_shutdown(void){ - - free_irq(S0_CD_VALID, NULL); - free_irq(S1_CD_VALID, NULL); - free_irq(S0_BVD1_STSCHG, NULL); - free_irq(S1_BVD1_STSCHG, NULL); - - INTPOL1 &= - ~((1 << (S0_CD_VALID - SA1111_IRQ(32))) | - (1 << (S1_CD_VALID - SA1111_IRQ(32))) | - (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | - (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)))); - - return 0; -} - -static int xp860_pcmcia_socket_state(struct pcmcia_state_array - *state_array){ - unsigned long status; - int return_val=1; - - if(state_array->size<2) return -1; - - memset(state_array->state, 0, - (state_array->size)*sizeof(struct pcmcia_state)); - - status=PCSR; - - state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0; - - state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1; - - state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1; - - state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1; - - state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1; - - state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0; - - state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; - - state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0; - - state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1; - - state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1; - - state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1; - - state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1; - - state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0; - - state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0; - - return return_val; + return sa1111_pcmcia_init(init); } -static int xp860_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ - - switch(info->sock){ - case 0: - info->irq=S0_READY_NINT; - break; - - case 1: - info->irq=S1_READY_NINT; - break; - - default: - return -1; - } - - return 0; -} - -static int xp860_pcmcia_configure_socket(const struct pcmcia_configure - *configure){ - unsigned long pccr=PCCR, ncr=GPLR, gpio=PA_DWR; - +static int +xp860_pcmcia_configure_socket(const struct pcmcia_configure *conf) +{ + unsigned int gpio_mask, pa_dwr_mask; + unsigned int gpio_set, pa_dwr_set; + int ret; /* Neponset uses the Maxim MAX1600, with the following connections: +#warning ^^^ This isn't a neponset! * * MAX1600 Neponset * @@ -151,105 +69,90 @@ * the corresponding truth table. */ - switch(configure->sock){ + switch (conf->sock) { case 0: - - switch(configure->vcc){ - case 0: - gpio&=~(GPIO_GPIO0 | GPIO_GPIO1); - break; - - case 33: - pccr=(pccr & ~PCCR_S0_PSE); - gpio=(gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO1; - break; - - case 50: - pccr=(pccr | PCCR_S0_PSE); - gpio=(gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO0; - break; + pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1; + gpio_mask = NCR_A0VPP | NCR_A1VPP; + switch (conf->vcc) { default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, - configure->vcc); - return -1; + case 0: pa_dwr_set = 0; break; + case 33: pa_dwr_set = GPIO_GPIO1; break; + case 50: pa_dwr_set = GPIO_GPIO0; break; } - switch(configure->vpp){ - case 0: - ncr&=~(NCR_A0VPP | NCR_A1VPP); - break; - - case 120: - ncr=(ncr & ~(NCR_A0VPP | NCR_A1VPP)) | NCR_A1VPP; - break; + switch (conf->vpp) { + case 0: gpio_set = 0; break; + case 120: gpio_set = NCR_A1VPP; break; default: - if(configure->vpp == configure->vcc) - ncr=(ncr & ~(NCR_A0VPP | NCR_A1VPP)) | NCR_A0VPP; + if (conf->vpp == conf->vcc) + gpio_set = NCR_A0VPP; else { - printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__, - configure->vpp); + printk(KERN_ERR "%s(): unrecognized Vpp %u\n", + __FUNCTION__, conf->vpp); return -1; } } - - pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST); - pccr=(configure->output)?(pccr | PCCR_S0_FLT):(pccr & ~PCCR_S0_FLT); - break; case 1: - switch(configure->vcc){ - case 0: - gpio&=~(GPIO_GPIO2 | GPIO_GPIO3); - break; - - case 33: - pccr=(pccr & ~PCCR_S1_PSE); - gpio=(gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO2; - break; - - case 50: - pccr=(pccr | PCCR_S1_PSE); - gpio=(gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO3; - break; + pa_dwr_mask = GPIO_GPIO2 | GPIO_GPIO3; + gpio_mask = 0; + gpio_set = 0; + switch (conf->vcc) { default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, - configure->vcc); - return -1; + case 0: pa_dwr_set = 0; break; + case 33: pa_dwr_set = GPIO_GPIO2; break; + case 50: pa_dwr_set = GPIO_GPIO3; break; } - if(configure->vpp!=configure->vcc && configure->vpp!=0){ - printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, - configure->vpp); + if (conf->vpp != conf->vcc && conf->vpp != 0) { + printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", + __FUNCTION__, conf->vpp); return -1; } - - pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST); - pccr=(configure->output)?(pccr | PCCR_S1_FLT):(pccr & ~PCCR_S1_FLT); - break; - - default: - return -1; } - PCCR = pccr; - ncr &= NCR_A0VPP|NCR_A1VPP; - GPSR = ncr; - GPCR = (~ncr)&(NCR_A0VPP|NCR_A1VPP); - PA_DWR = gpio; + ret = sa1111_pcmcia_configure_socket(conf); + if (ret == 0) { + unsigned long flags; + + local_irq_save(flags); + PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set; + GPSR = gpio_set; + GPCR = gpio_set ^ gpio_mask; + local_irq_restore(flags); + } - return 0; + return ret; } -struct pcmcia_low_level xp860_pcmcia_ops = { - xp860_pcmcia_init, - xp860_pcmcia_shutdown, - xp860_pcmcia_socket_state, - xp860_pcmcia_get_irq_info, - xp860_pcmcia_configure_socket +static struct pcmcia_low_level xp860_pcmcia_ops = { + init: xp860_pcmcia_init, + shutdown: sa1111_pcmcia_shutdown, + socket_state: sa1111_pcmcia_socket_state, + get_irq_info: sa1111_pcmcia_get_irq_info, + configure_socket: xp860_pcmcia_configure_socket, + + socket_init: sa1111_pcmcia_socket_init, + socket_suspend: sa1111_pcmcia_socket_suspend, }; + +int __init pcmcia_xp860_init(void) +{ + int ret = -ENODEV; + + if (machine_is_xp860()) + ret = sa1100_register_pcmcia(&xp860_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_xp860_exit(void) +{ + sa1100_unregister_pcmcia(&xp860_pcmcia_ops); +} diff -Nru a/drivers/pcmcia/sa1100_yopy.c b/drivers/pcmcia/sa1100_yopy.c --- a/drivers/pcmcia/sa1100_yopy.c Thu Mar 7 18:17:40 2002 +++ b/drivers/pcmcia/sa1100_yopy.c Thu Mar 7 18:17:40 2002 @@ -6,10 +6,11 @@ */ #include #include +#include #include #include -#include +#include "sa1100_generic.h" static inline void pcmcia_power(int on) { @@ -23,45 +24,53 @@ yopy_gpio_set(GPIO_CF_RESET, reset); } +static struct irqs { + int irq; + const char *str; +} irqs[] = { + { IRQ_CF_CD, "CF_CD" }, + { IRQ_CF_BVD2, "CF_BVD2" }, + { IRQ_CF_BVD1, "CF_BVD1" }, +}; + static int yopy_pcmcia_init(struct pcmcia_init *init) { - int irq, res; + int i, res; pcmcia_power(0); pcmcia_reset(1); - /* All those are inputs */ - GPDR &= ~(GPIO_CF_CD | GPIO_CF_BVD2 | GPIO_CF_BVD1 | GPIO_CF_IREQ); - GAFR &= ~(GPIO_CF_CD | GPIO_CF_BVD2 | GPIO_CF_BVD1 | GPIO_CF_IREQ); - /* Set transition detect */ - set_GPIO_IRQ_edge( GPIO_CF_CD|GPIO_CF_BVD2|GPIO_CF_BVD1, - GPIO_BOTH_EDGES ); - set_GPIO_IRQ_edge( GPIO_CF_IREQ, GPIO_FALLING_EDGE ); + set_irq_type(IRQ_CF_IREQ, IRQT_FALLING); /* Register interrupts */ - irq = IRQ_CF_CD; - res = request_irq(irq, init->handler, SA_INTERRUPT, "CF_CD", NULL); - if (res < 0) goto irq_err; - irq = IRQ_CF_BVD2; - res = request_irq(irq, init->handler, SA_INTERRUPT, "CF_BVD2", NULL); - if (res < 0) goto irq_err; - irq = IRQ_CF_BVD1; - res = request_irq(irq, init->handler, SA_INTERRUPT, "CF_BVD1", NULL); - if (res < 0) goto irq_err; + for (i = 0; i < ARRAY_SIZE(irqs); i++) { + set_irq_type(irqs[i].irq, IRQT_NOEDGE); + res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT, + irqs[i].str, NULL); + if (res) + goto irq_err; + } return 1; -irq_err: - printk(KERN_ERR "%s: Request for IRQ %d failed\n", __FUNCTION__, irq); - return -1; + + irq_err: + printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n", + __FUNCTION__, irqs[i].irq, res); + + while (i--) + free_irq(irqs[i].irq, NULL); + + return res; } static int yopy_pcmcia_shutdown(void) { + int i; + /* disable IRQs */ - free_irq( IRQ_CF_CD, NULL ); - free_irq( IRQ_CF_BVD2, NULL ); - free_irq( IRQ_CF_BVD1, NULL ); + for (i = 0; i < ARRAY_SIZE(irqs); i++) + free_irq(irqs[i].irq, NULL); /* Disable CF */ pcmcia_reset(1); @@ -109,7 +118,7 @@ return -1; switch (configure->vcc) { - case 0: /* power off */; + case 0: /* power off */ pcmcia_power(0); break; case 50: @@ -130,10 +139,49 @@ return 0; } -struct pcmcia_low_level yopy_pcmcia_ops = { - yopy_pcmcia_init, - yopy_pcmcia_shutdown, - yopy_pcmcia_socket_state, - yopy_pcmcia_get_irq_info, - yopy_pcmcia_configure_socket +static int yopy_pcmcia_socket_init(int sock) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(irqs); i++) + set_irq_type(irqs[i].irq, IRQT_BOTHEDGE); + + return 0; +} + +static int yopy_pcmcia_socket_suspend(int sock) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(irqs); i++) + set_irq_type(irqs[i].irq, IRQT_NOEDGE); + + return 0; +} + +static struct pcmcia_low_level yopy_pcmcia_ops = { + init: yopy_pcmcia_init, + shutdown: yopy_pcmcia_shutdown, + socket_state: yopy_pcmcia_socket_state, + get_irq_info: yopy_pcmcia_get_irq_info, + configure_socket: yopy_pcmcia_configure_socket, + + socket_init: yopy_pcmcia_socket_init, + socket_suspend: yopy_pcmcia_socket_suspend, }; + +int __init pcmcia_yopy_init(void) +{ + int ret = -ENODEV; + + if (machine_is_yopy()) + ret = sa1100_register_pcmcia(&yopy_pcmcia_ops); + + return ret; +} + +void __exit pcmcia_yopy_exit(void) +{ + sa1100_unregister_pcmcia(&yopy_pcmcia_ops); +} + diff -Nru a/drivers/pcmcia/sa1111_generic.c b/drivers/pcmcia/sa1111_generic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pcmcia/sa1111_generic.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,180 @@ +/* + * linux/drivers/pcmcia/sa1100_sa1111.c + * + * We implement the generic parts of a SA1111 PCMCIA driver. This + * basically means we handle everything except controlling the + * power. Power is machine specific... + */ +#include +#include +#include + +#include +#include +#include + +#include "sa1100_generic.h" +#include "sa1111_generic.h" + +static struct irqs { + int irq; + const char *str; +} irqs[] = { + { S0_CD_VALID, "SA1111 PCMCIA card detect" }, + { S0_BVD1_STSCHG, "SA1111 PCMCIA BVD1" }, + { S1_CD_VALID, "SA1111 CF card detect" }, + { S1_BVD1_STSCHG, "SA1111 CF BVD1" }, +}; + +int sa1111_pcmcia_init(struct pcmcia_init *init) +{ + int i, ret; + + if (!request_mem_region(_PCCR, 512, "PCMCIA")) + return -1; + + for (i = ret = 0; i < ARRAY_SIZE(irqs); i++) { + set_irq_type(irqs[i].irq, IRQT_FALLING); + ret = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT, + irqs[i].str, NULL); + if (ret) + break; + } + + if (i < ARRAY_SIZE(irqs)) { + printk(KERN_ERR "sa1111_pcmcia: unable to grab IRQ%d (%d)\n", + irqs[i].irq, ret); + while (i--) + free_irq(irqs[i].irq, NULL); + + release_mem_region(_PCCR, 16); + } + + return ret ? -1 : 2; +} + +int sa1111_pcmcia_shutdown(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(irqs); i++) + free_irq(irqs[i].irq, NULL); + + release_mem_region(_PCCR, 512); + + return 0; +} + +int sa1111_pcmcia_socket_state(struct pcmcia_state_array *state) +{ + unsigned long status; + + if (state->size < 2) + return -1; + + status = PCSR; + + state->state[0].detect = status & PCSR_S0_DETECT ? 0 : 1; + state->state[0].ready = status & PCSR_S0_READY ? 1 : 0; + state->state[0].bvd1 = status & PCSR_S0_BVD1 ? 1 : 0; + state->state[0].bvd2 = status & PCSR_S0_BVD2 ? 1 : 0; + state->state[0].wrprot = status & PCSR_S0_WP ? 1 : 0; + state->state[0].vs_3v = status & PCSR_S0_VS1 ? 0 : 1; + state->state[0].vs_Xv = status & PCSR_S0_VS2 ? 0 : 1; + + state->state[1].detect = status & PCSR_S1_DETECT ? 0 : 1; + state->state[1].ready = status & PCSR_S1_READY ? 1 : 0; + state->state[1].bvd1 = status & PCSR_S1_BVD1 ? 1 : 0; + state->state[1].bvd2 = status & PCSR_S1_BVD2 ? 1 : 0; + state->state[1].wrprot = status & PCSR_S1_WP ? 1 : 0; + state->state[1].vs_3v = status & PCSR_S1_VS1 ? 0 : 1; + state->state[1].vs_Xv = status & PCSR_S1_VS2 ? 0 : 1; + + return 1; +} + +int sa1111_pcmcia_get_irq_info(struct pcmcia_irq_info *info) +{ + int ret = 0; + + switch (info->sock) { + case 0: info->irq = S0_READY_NINT; break; + case 1: info->irq = S1_READY_NINT; break; + default: ret = 1; + } + + return ret; +} + +int sa1111_pcmcia_configure_socket(const struct pcmcia_configure *conf) +{ + unsigned int rst, flt, wait, pse, irq, pccr_mask; + unsigned long flags; + + switch (conf->sock) { + case 0: + rst = PCCR_S0_RST; + flt = PCCR_S0_FLT; + wait = PCCR_S0_PWAITEN; + pse = PCCR_S0_PSE; + irq = S0_READY_NINT; + break; + + case 1: + rst = PCCR_S1_RST; + flt = PCCR_S1_FLT; + wait = PCCR_S1_PWAITEN; + pse = PCCR_S1_PSE; + irq = S1_READY_NINT; + break; + + default: + return -1; + } + + switch (conf->vcc) { + case 0: + pccr_mask = 0; + break; + + case 33: + pccr_mask = wait; + break; + + case 50: + pccr_mask = pse | wait; + break; + + default: + printk(KERN_ERR "sa1111_pcmcia: unrecognised VCC %u\n", + conf->vcc); + return -1; + } + + if (conf->reset) + pccr_mask |= rst; + + if (conf->output) + pccr_mask |= flt; + + local_irq_save(flags); + PCCR = (PCCR & ~(pse | flt | wait | rst)) | pccr_mask; + local_irq_restore(flags); + + if (conf->irq) + enable_irq(irq); + else + disable_irq(irq); + + return 0; +} + +int sa1111_pcmcia_socket_init(int sock) +{ + return 0; +} + +int sa1111_pcmcia_socket_suspend(int sock) +{ + return 0; +} diff -Nru a/drivers/pcmcia/sa1111_generic.h b/drivers/pcmcia/sa1111_generic.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pcmcia/sa1111_generic.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,7 @@ +extern int sa1111_pcmcia_init(struct pcmcia_init *); +extern int sa1111_pcmcia_shutdown(void); +extern int sa1111_pcmcia_socket_state(struct pcmcia_state_array *); +extern int sa1111_pcmcia_get_irq_info(struct pcmcia_irq_info *); +extern int sa1111_pcmcia_configure_socket(const struct pcmcia_configure *); +extern int sa1111_pcmcia_socket_init(int); +extern int sa1111_pcmcia_socket_suspend(int); diff -Nru a/drivers/pnp/isapnp.c b/drivers/pnp/isapnp.c --- a/drivers/pnp/isapnp.c Thu Mar 7 18:17:36 2002 +++ b/drivers/pnp/isapnp.c Thu Mar 7 18:17:36 2002 @@ -21,7 +21,7 @@ * 2000-01-01 Added quirks handling for buggy hardware * Peter Denison * 2000-06-14 Added isapnp_probe_devs() and isapnp_activate_dev() - * Christoph Hellwig + * Christoph Hellwig * 2001-06-03 Added release_region calls to correspond with * request_region calls when a failure occurs. Also * added KERN_* constants to printk() calls. diff -Nru a/drivers/pnp/pnpbios_core.c b/drivers/pnp/pnpbios_core.c --- a/drivers/pnp/pnpbios_core.c Thu Mar 7 18:17:37 2002 +++ b/drivers/pnp/pnpbios_core.c Thu Mar 7 18:17:37 2002 @@ -1189,7 +1189,7 @@ subsys_initcall(pnpbios_init); -void __init pnpbios_init(void) +int __init pnpbios_init(void) { union pnp_bios_expansion_header *check; u8 sum; @@ -1200,7 +1200,7 @@ if(pnpbios_disabled) { printk(KERN_INFO "PnPBIOS: Disabled.\n"); - return; + return 0; } if ( is_sony_vaio_laptop ) @@ -1253,6 +1253,7 @@ if(kernel_thread(pnp_dock_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL)>0) unloading = 0; #endif + return 0; } #ifdef MODULE diff -Nru a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c --- a/drivers/pnp/quirks.c Thu Mar 7 18:17:41 2002 +++ b/drivers/pnp/quirks.c Thu Mar 7 18:17:41 2002 @@ -18,6 +18,10 @@ #include #include +#if 0 +#define ISAPNP_DEBUG +#endif + static void __init quirk_awe32_resources(struct pci_dev *dev) { struct isapnp_port *port, *port2, *port3; @@ -139,8 +143,10 @@ while (isapnp_fixups[i].vendor != 0) { if ((isapnp_fixups[i].vendor == dev->vendor) && (isapnp_fixups[i].device == dev->device)) { +#ifdef ISAPNP_DEBUG printk(KERN_DEBUG "isapnp: Calling quirk for %02x:%02x\n", dev->bus->number, dev->devfn); +#endif isapnp_fixups[i].quirk_function(dev); } i++; diff -Nru a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c --- a/drivers/s390/block/dasd.c Thu Mar 7 18:17:44 2002 +++ b/drivers/s390/block/dasd.c Thu Mar 7 18:17:44 2002 @@ -2477,7 +2477,7 @@ rc = -EFAULT; } else { if ( bsz >= device->sizes.bp_block ) - rc = blk_ioctl (inp->i_rdev, no, data); + rc = blk_ioctl (inp->i_bdev, no, data); else rc = -EINVAL; } @@ -2493,7 +2493,7 @@ case BLKPG: case BLKELVGET: case BLKELVSET: - return blk_ioctl (inp->i_rdev, no, data); + return blk_ioctl (inp->i_bdev, no, data); break; default:{ diff -Nru a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c --- a/drivers/s390/block/xpram.c Thu Mar 7 18:17:45 2002 +++ b/drivers/s390/block/xpram.c Thu Mar 7 18:17:45 2002 @@ -654,7 +654,7 @@ (u64 *) arg); case BLKFLSBUF: /* flush, 0x1261 */ - fsync_dev(inode->i_rdev); + fsync_bdev(inode->i_bdev); if ( capable(CAP_SYS_ADMIN) )invalidate_buffers(inode->i_rdev); return 0; @@ -1191,10 +1191,7 @@ { int i; - /* first of all, flush it all and reset all the data structures */ - - for (i=0; isch.irq)); diff -Nru a/drivers/s390/net/ctctty.c b/drivers/s390/net/ctctty.c --- a/drivers/s390/net/ctctty.c Thu Mar 7 18:17:36 2002 +++ b/drivers/s390/net/ctctty.c Thu Mar 7 18:17:36 2002 @@ -286,7 +286,7 @@ if (!info->netdev) { if (skb) - kfree(skb); + kfree_skb(skb); return 0; } if (info->flags & CTC_ASYNC_TX_LINESTAT) { diff -Nru a/drivers/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c --- a/drivers/sbus/char/jsflash.c Thu Mar 7 18:17:44 2002 +++ b/drivers/sbus/char/jsflash.c Thu Mar 7 18:17:44 2002 @@ -468,7 +468,7 @@ case BLKROSET: case BLKROGET: case BLKSSZGET: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); #endif /* case BLKFLSBUF: */ /* Program, then read, what happens? Stale? */ diff -Nru a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c --- a/drivers/scsi/3w-xxxx.c Thu Mar 7 18:17:42 2002 +++ b/drivers/scsi/3w-xxxx.c Thu Mar 7 18:17:42 2002 @@ -6,7 +6,7 @@ Arnaldo Carvalho de Melo Brad Strand - Copyright (C) 1999-2001 3ware Inc. + Copyright (C) 1999-2002 3ware Inc. Kernel compatablity By: Andre Hedrick Non-Copyright (C) 2000 Andre Hedrick @@ -106,17 +106,41 @@ Add entire aen code string list. 1.02.00.010 - Cleanup queueing code, fix jbod thoughput. Fix get_param for specific units. + 1.02.00.011 - Fix bug in tw_aen_complete() where aen's could be lost. + Fix tw_aen_drain_queue() to display useful info at init. + Set tw_host->max_id for 12 port cards. + Add ioctl support for raw command packet post from userspace + with sglist fragments (parameter and io). + 1.02.00.012 - Fix read capacity to under report by 1 sector to fix get + last sector ioctl. + 1.02.00.013 - Fix bug where more AEN codes weren't coming out during + driver initialization. + Improved handling of PCI aborts. + 1.02.00.014 - Fix bug in tw_findcards() where AEN code could be lost. + Increase timeout in tw_aen_drain_queue() to 30 seconds. + 1.02.00.015 - Re-write raw command post with data ioctl method. + Remove raid5 bounce buffers for raid5 for 6XXX for kernel 2.5 + Add tw_map/unmap_scsi_sg/single_data() for kernel 2.5 + Replace io_request_lock with host_lock for kernel 2.5 + Set max_cmd_len to 16 for 3dm for kernel 2.5 + 1.02.00.016 - Set host->max_sectors back up to 256. + 1.02.00.017 - Modified pci parity error handling/clearing from config space + during initialization. + 1.02.00.018 - Better handling of request sense opcode and sense information + for failed commands. Add tw_decode_sense(). + Replace all mdelay()'s with scsi_sleep(). + 1.02.00.019 - Revert mdelay's and scsi_sleep's, this caused problems on + some SMP systems. + 1.02.00.020 - Add pci_set_dma_mask(), rewrite kmalloc()/virt_to_bus() to + pci_alloc/free_consistent(). */ -#error Please convert me to Documentation/DMA-mapping.txt - #include MODULE_AUTHOR ("3ware Inc."); MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver"); MODULE_LICENSE("GPL"); - #include #include #include @@ -148,14 +172,17 @@ static void tw_copy_mem_info(TW_Info *info, char *data, int len); static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs); static int tw_halt(struct notifier_block *nb, ulong event, void *buf); +static int tw_map_scsi_sg_data(struct pci_dev *pdev, Scsi_Cmnd *cmd); +static u32 tw_map_scsi_single_data(struct pci_dev *pdev, Scsi_Cmnd *cmd); +static void tw_unmap_scsi_data(struct pci_dev *pdev, Scsi_Cmnd *cmd); /* Notifier block to get a notify on system shutdown/halt/reboot */ static struct notifier_block tw_notifier = { - tw_halt, NULL, 0 + tw_halt, NULL, 0 }; /* Globals */ -char *tw_driver_version="1.02.00.010"; +char *tw_driver_version="1.02.00.020"; TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT]; int tw_device_extension_count = 0; @@ -166,6 +193,7 @@ { TW_Param *param; unsigned short aen; + int error = 0; dprintk(KERN_WARNING "3w-xxxx: tw_aen_complete()\n"); if (tw_dev->alignment_virtual_address[request_id] == NULL) { @@ -184,12 +212,15 @@ if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') { printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s%d.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff], aen >> 8); } else { - printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff]); + if (aen != 0x0) + printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff]); } - } else + } else { printk(KERN_WARNING "3w-xxxx: scsi%d: Received AEN %d.\n", tw_dev->host->host_no, aen); + } } - tw_dev->aen_count++; + if (aen != 0x0) + tw_dev->aen_count++; /* Now queue the code */ tw_dev->aen_queue[tw_dev->aen_tail] = aen; @@ -205,8 +236,18 @@ tw_dev->aen_head = tw_dev->aen_head + 1; } } - tw_dev->state[request_id] = TW_S_COMPLETED; - tw_state_request_finish(tw_dev, request_id); + + if (aen != TW_AEN_QUEUE_EMPTY) { + error = tw_aen_read_queue(tw_dev, request_id); + if (error) { + printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing AEN.\n", tw_dev->host->host_no); + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + } + } else { + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + } return 0; } /* End tw_aen_complete() */ @@ -237,10 +278,11 @@ status_reg_addr = tw_dev->registers.status_reg_addr; response_que_addr = tw_dev->registers.response_que_addr; - if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT, 15)) { + if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT, 30)) { dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): No attention interrupt for card %d.\n", tw_device_extension_count); return 1; } + tw_clear_attention_interrupt(tw_dev); /* Initialize command packet */ if (tw_dev->command_packet_virtual_address[request_id] == NULL) { @@ -288,7 +330,7 @@ do { /* Post command packet */ outl(command_que_value, command_que_addr); - + /* Now poll for completion */ for (i=0;istatus != 0) { if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) { /* Bad response */ - dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags); - tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit); + tw_decode_sense(tw_dev, request_id, 0); return 1; } else { /* We know this is a 3w-1x00, and doesn't support aen's */ @@ -326,7 +367,7 @@ queue = 0; switch (aen_code) { case TW_AEN_QUEUE_EMPTY: - dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_QUEUE_EMPTY.\n"); + dprintk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]); if (first_reset != 1) { continue; } else { @@ -334,51 +375,28 @@ } break; case TW_AEN_SOFT_RESET: - dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_SOFT_RESET.\n"); if (first_reset == 0) { first_reset = 1; } else { + printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]); + tw_dev->aen_count++; queue = 1; } break; - case TW_AEN_DEGRADED_MIRROR: - dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_DEGRADED_MIRROR.\n"); - queue = 1; - break; - case TW_AEN_CONTROLLER_ERROR: - dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_CONTROLLER_ERROR.\n"); - queue = 1; - break; - case TW_AEN_REBUILD_FAIL: - dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_REBUILD_FAIL.\n"); - queue = 1; - break; - case TW_AEN_REBUILD_DONE: - dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_REBUILD_DONE.\n"); - queue = 1; - break; - case TW_AEN_QUEUE_FULL: - dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_QUEUE_FULL.\n"); - queue = 1; - break; - case TW_AEN_APORT_TIMEOUT: - printk(KERN_WARNING "3w-xxxx: Received drive timeout AEN on port %d, check drive and drive cables.\n", aen >> 8); - queue = 1; - break; - case TW_AEN_DRIVE_ERROR: - printk(KERN_WARNING "3w-xxxx: Received drive error AEN on port %d, check/replace cabling, or possible bad drive.\n", aen >> 8); - queue = 1; - break; - case TW_AEN_SMART_FAIL: - printk(KERN_WARNING "3w-xxxx: Received S.M.A.R.T. threshold AEN on port %d, check drive/cooling, or possible bad drive.\n", aen >> 8); - queue = 1; - break; - case TW_AEN_SBUF_FAIL: - printk(KERN_WARNING "3w-xxxx: Received SBUF integrity check failure AEN, reseat card or bad card.\n"); - queue = 1; - break; default: - dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unknown AEN code 0x%x.\n", aen_code); + if (aen == 0x0ff) { + printk(KERN_WARNING "3w-xxxx: AEN: AEN queue overflow.\n"); + } else { + if ((aen & 0x0ff) < TW_AEN_STRING_MAX) { + if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') { + printk(KERN_WARNING "3w-xxxx: AEN: %s%d.\n", tw_aen_string[aen & 0xff], aen >> 8); + } else { + printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]); + } + } else + printk(KERN_WARNING "3w-xxxx: Received AEN %d.\n", aen); + } + tw_dev->aen_count++; queue = 1; } @@ -488,40 +506,44 @@ return 0; } /* End tw_aen_read_queue() */ -/* This function will allocate memory and check if it is 16 d-word aligned */ -int tw_allocate_memory(TW_Device_Extension *tw_dev, int request_id, int size, int which) +/* This function will allocate memory */ +int tw_allocate_memory(TW_Device_Extension *tw_dev, int size, int which) { - u32 *virt_addr = kmalloc(size, GFP_ATOMIC); + int i; + dma_addr_t dma_handle; + u32 *cpu_addr = NULL; dprintk(KERN_NOTICE "3w-xxxx: tw_allocate_memory()\n"); - if (!virt_addr) { - printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n"); - return 1; - } + for (i=0;itw_pci_dev, size, &dma_handle); + if (cpu_addr == NULL) { + printk(KERN_WARNING "3w-xxxx: pci_alloc_consistent() failed.\n"); + return 1; + } - if ((u32)virt_addr % TW_ALIGNMENT) { - kfree(virt_addr); - printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n"); - return 1; - } + if ((u32)cpu_addr % (tw_dev->tw_pci_dev->device == TW_DEVICE_ID ? TW_ALIGNMENT_6000 : TW_ALIGNMENT_7000)) { + printk(KERN_WARNING "3w-xxxx: Couldn't allocate correctly aligned memory.\n"); + return 1; + } - switch(which) { - case 0: - tw_dev->command_packet_virtual_address[request_id] = virt_addr; - 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; + switch(which) { + case 0: + tw_dev->command_packet_virtual_address[i] = cpu_addr; + tw_dev->command_packet_physical_address[i] = dma_handle; + memset(tw_dev->command_packet_virtual_address[i], 0, size); + break; + case 1: + tw_dev->alignment_virtual_address[i] = cpu_addr; + tw_dev->alignment_physical_address[i] = dma_handle; + memset(tw_dev->alignment_virtual_address[i], 0, size); + 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() */ @@ -548,8 +570,10 @@ status_reg_addr = tw_dev->registers.status_reg_addr; status_reg_value = inl(status_reg_addr); - if (TW_STATUS_ERRORS(status_reg_value) || tw_check_bits(status_reg_value)) + if (TW_STATUS_ERRORS(status_reg_value) || tw_check_bits(status_reg_value)) { + tw_decode_bits(tw_dev, status_reg_value); return 1; + } return 0; } /* End tw_check_errors() */ @@ -614,37 +638,62 @@ dprintk(KERN_WARNING "3w-xxxx: tw_decode_bits()\n"); switch (status_reg_value & TW_STATUS_UNEXPECTED_BITS) { case TW_STATUS_PCI_PARITY_ERROR: - printk(KERN_WARNING "3w-xxxx: PCI Parity Error: Reseat card, move card, or buggy device on the bus.\n"); + printk(KERN_WARNING "3w-xxxx: PCI Parity Error: clearing.\n"); outl(TW_CONTROL_CLEAR_PARITY_ERROR, tw_dev->registers.control_reg_addr); - pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PARITY_ERRORS); break; case TW_STATUS_MICROCONTROLLER_ERROR: printk(KERN_WARNING "3w-xxxx: Microcontroller Error.\n"); break; + case TW_STATUS_PCI_ABORT: + printk(KERN_WARNING "3w-xxxx: PCI Abort: clearing.\n"); + outl(TW_CONTROL_CLEAR_PCI_ABORT, tw_dev->registers.control_reg_addr); + pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PCI_ABORT); + break; } } /* End tw_decode_bits() */ -/* This function will print readable messages from flags and status values */ -void tw_decode_error(TW_Device_Extension *tw_dev, unsigned char status, unsigned char flags, unsigned char unit) +/* This function will return valid sense buffer information for failed cmds */ +void tw_decode_sense(TW_Device_Extension *tw_dev, int request_id, int fill_sense) { - dprintk(KERN_WARNING "3w-xxxx: tw_decode_error()\n"); - switch (status) { - case 0xc7: - switch (flags) { - case 0x1b: - printk(KERN_WARNING "3w-xxxx: scsi%d: Drive timeout on unit %d, check drive and drive cables.\n", tw_dev->host->host_no, unit); - break; - case 0x51: - printk(KERN_WARNING "3w-xxxx: scsi%d: Unrecoverable drive error on unit %d, check/replace cabling, or possible bad drive.\n", tw_dev->host->host_no, unit); - break; - default: - printk(KERN_WARNING "3w-xxxx: scsi%d: Controller error: status = 0x%x, flags = 0x%x, unit #%d.\n", tw_dev->host->host_no, status, flags, unit); + int i, found=0; + TW_Command *command; + + dprintk(KERN_WARNING "3w-xxxx: tw_decode_sense()\n"); + command = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; + + printk(KERN_WARNING "3w-xxxx: scsi%d: Command failed: status = 0x%x, flags = 0x%x, unit #%d.\n", tw_dev->host->host_no, command->status, command->flags, command->byte3.unit); + + /* Attempt to return intelligent sense information */ + if (fill_sense) { + if ((command->status == 0xc7) || (command->status == 0xcb)) { + for (i=0;i<(sizeof(tw_sense_table)/sizeof(tw_sense_table[0]));i++) { + if (command->flags == tw_sense_table[i][0]) { + found=1; + + /* Valid bit and 'current errors' */ + tw_dev->srb[request_id]->sense_buffer[0] = (0x1 << 7 | 0x70); + + /* Sense key */ + tw_dev->srb[request_id]->sense_buffer[2] = tw_sense_table[i][1]; + + /* Additional sense length */ + tw_dev->srb[request_id]->sense_buffer[7] = 0xa; /* 10 bytes */ + + /* Additional sense code */ + tw_dev->srb[request_id]->sense_buffer[12] = tw_sense_table[i][2]; + + /* Additional sense code qualifier */ + tw_dev->srb[request_id]->sense_buffer[13] = tw_sense_table[i][3]; + + tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1); + } + } } - break; - default: - printk(KERN_WARNING "3w-xxxx: scsi%d: Controller error: status = 0x%x, flags = 0x%x, unit #%d.\n", tw_dev->host->host_no, status, flags, unit); + /* If no table match, error so we get a reset */ + if (found == 0) + tw_dev->srb[request_id]->result = (DID_RESET << 16); } -} /* End tw_decode_error() */ +} /* End tw_decode_sense() */ /* This function will disable interrupts on the controller */ void tw_disable_interrupts(TW_Device_Extension *tw_dev) @@ -691,11 +740,22 @@ u32 control_reg_value, control_reg_addr; control_reg_addr = tw_dev->registers.control_reg_addr; + control_reg_value = (TW_CONTROL_ENABLE_INTERRUPTS | + TW_CONTROL_UNMASK_RESPONSE_INTERRUPT); + outl(control_reg_value, control_reg_addr); +} /* End tw_enable_interrupts() */ + +/* This function will enable interrupts on the controller */ +void tw_enable_and_clear_interrupts(TW_Device_Extension *tw_dev) +{ + u32 control_reg_value, control_reg_addr; + + control_reg_addr = tw_dev->registers.control_reg_addr; control_reg_value = (TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | TW_CONTROL_UNMASK_RESPONSE_INTERRUPT | TW_CONTROL_ENABLE_INTERRUPTS); outl(control_reg_value, control_reg_addr); -} /* End tw_enable_interrupts() */ +} /* End tw_enable_and_clear_interrupts() */ /* This function will find and initialize all cards */ int tw_findcards(Scsi_Host_Template *tw_host) @@ -716,6 +776,13 @@ while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, device[i], tw_pci_dev))) { if (pci_enable_device(tw_pci_dev)) continue; + + /* We only need 32-bit addressing for 5,6,7xxx cards */ + if (pci_set_dma_mask(tw_pci_dev, 0xffffffff)) { + printk(KERN_WARNING "3w-xxxx: No suitable DMA available.\n"); + continue; + } + /* Prepare temporary device extension */ tw_dev=(TW_Device_Extension *)kmalloc(sizeof(TW_Device_Extension), GFP_ATOMIC); if (tw_dev == NULL) { @@ -724,6 +791,9 @@ } memset(tw_dev, 0, sizeof(TW_Device_Extension)); + /* Save pci_dev struct to device extension */ + tw_dev->tw_pci_dev = tw_pci_dev; + error = tw_initialize_device_extension(tw_dev); if (error) { printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize device extension for card %d.\n", numcards); @@ -738,8 +808,6 @@ tw_dev->registers.status_reg_addr = pci_resource_start(tw_pci_dev, 0) + 0x4; tw_dev->registers.command_que_addr = pci_resource_start(tw_pci_dev, 0) + 0x8; tw_dev->registers.response_que_addr = pci_resource_start(tw_pci_dev, 0) + 0xC; - /* Save pci_dev struct to device extension */ - tw_dev->tw_pci_dev = tw_pci_dev; /* Check for errors and clear them */ status_reg_value = inl(tw_dev->registers.status_reg_addr); @@ -763,14 +831,14 @@ error = tw_aen_drain_queue(tw_dev); if (error) { - printk(KERN_WARNING "3w-xxxx: tw_findcards(): No attention interrupt for card %d.\n", numcards); + printk(KERN_WARNING "3w-xxxx: AEN drain failed for card %d.\n", numcards); tries++; continue; } /* Check for controller errors */ if (tw_check_errors(tw_dev)) { - printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller errors found, soft resetting card %d.\n", numcards); + printk(KERN_WARNING "3w-xxxx: Controller errors found, retrying for card %d.\n", numcards); tries++; continue; } @@ -778,7 +846,7 @@ /* Empty the response queue */ error = tw_empty_response_que(tw_dev); if (error) { - printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't empty response queue for card %d.\n", numcards); + printk(KERN_WARNING "3w-xxxx: Couldn't empty response queue, retrying for card %d.\n", numcards); tries++; continue; } @@ -788,7 +856,7 @@ } if (tries >= TW_MAX_RESET_TRIES) { - printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller error or no attention interrupt: giving up for card %d.\n", numcards); + printk(KERN_WARNING "3w-xxxx: Controller errors, card not responding, check all cabling for card %d.\n", numcards); tw_free_device_extension(tw_dev); kfree(tw_dev); continue; @@ -818,7 +886,7 @@ error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS); if (error) { - printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initconnection for card %d.\n", numcards); + printk(KERN_WARNING "3w-xxxx: Connection initialization failed for card %d.\n", numcards); release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE); tw_free_device_extension(tw_dev); kfree(tw_dev); @@ -827,29 +895,28 @@ /* Calculate max cmds per lun, and setup queues */ if (tw_dev->num_units > 0) { - if ((tw_dev->num_raid_five > 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) { - tw_host->cmd_per_lun = (TW_MAX_BOUNCEBUF-1)/tw_dev->num_units; - tw_dev->free_head = TW_Q_START; - tw_dev->free_tail = TW_MAX_BOUNCEBUF - 1; - tw_dev->free_wrap = TW_MAX_BOUNCEBUF - 1; - } else { - tw_host->cmd_per_lun = (TW_Q_LENGTH-1)/tw_dev->num_units; - tw_dev->free_head = TW_Q_START; - tw_dev->free_tail = TW_Q_LENGTH - 1; - tw_dev->free_wrap = TW_Q_LENGTH - 1; - } + tw_host->cmd_per_lun = (TW_Q_LENGTH-1)/tw_dev->num_units; + tw_dev->free_head = TW_Q_START; + tw_dev->free_tail = TW_Q_LENGTH - 1; + tw_dev->free_wrap = TW_Q_LENGTH - 1; } - /* Register the card with the kernel SCSI layer */ + /* Register the card with the kernel SCSI layer */ host = scsi_register(tw_host, sizeof(TW_Device_Extension)); if (host == NULL) { - printk(KERN_WARNING "3w-xxxx: tw_findcards(): scsi_register() failed for card %d.\n", numcards-1); + printk(KERN_WARNING "3w-xxxx: tw_findcards(): scsi_register() failed for card %d.\n", numcards); release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE); tw_free_device_extension(tw_dev); kfree(tw_dev); continue; } + /* Set max target id's */ + host->max_id = TW_MAX_UNITS; + + /* Set max cdb size in bytes */ + host->max_cmd_len = 16; + /* Set max sectors per io */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,7) host->max_sectors = TW_MAX_SECTORS; @@ -874,7 +941,7 @@ tw_device_extension_count = numcards; tw_dev2->host = host; } else { - printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards-1); + printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards); scsi_unregister(host); release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE); tw_free_device_extension(tw_dev); @@ -924,15 +991,10 @@ /* Free command packet and generic buffer memory */ for (i=0;icommand_packet_virtual_address[i]) - kfree(tw_dev->command_packet_virtual_address[i]); + pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector), tw_dev->command_packet_virtual_address[i], tw_dev->command_packet_physical_address[i]); if (tw_dev->alignment_virtual_address[i]) - kfree(tw_dev->alignment_virtual_address[i]); - - } - for (i=0;ibounce_buffer[i]) - kfree(tw_dev->bounce_buffer[i]); + pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector), tw_dev->alignment_virtual_address[i], tw_dev->alignment_physical_address[i]); } } /* End tw_free_device_extension() */ @@ -1015,8 +1077,7 @@ } if (command_packet->status != 0) { /* bad response */ - dprintk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags); - tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit); + tw_decode_sense(tw_dev, request_id, 0); return 1; } break; /* Response was okay, so we exit */ @@ -1028,57 +1089,33 @@ /* This function will initialize the fields of a device extension */ int tw_initialize_device_extension(TW_Device_Extension *tw_dev) { - int i, imax; + int i, error=0; dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_device_extension()\n"); - imax = TW_Q_LENGTH; - for (i=0; icommand_packet_virtual_address[i] == NULL) { - printk(KERN_WARNING "3w-xxxx: tw_initialize_device_extension(): Bad command packet virtual address.\n"); - return 1; - } - memset(tw_dev->command_packet_virtual_address[i], 0, sizeof(TW_Sector)); - - /* Initialize generic buffer */ - tw_allocate_memory(tw_dev, i, sizeof(TW_Sector), 1); - if (tw_dev->alignment_virtual_address[i] == NULL) { - printk(KERN_WARNING "3w-xxxx: tw_initialize_device_extension(): Bad alignment virtual address.\n"); - return 1; - } - memset(tw_dev->alignment_virtual_address[i], 0, sizeof(TW_Sector)); + /* Initialize command packet buffers */ + error = tw_allocate_memory(tw_dev, sizeof(TW_Command), 0); + if (error) { + printk(KERN_WARNING "3w-xxxx: Command packet memory allocation failed.\n"); + return 1; + } - tw_dev->free_queue[i] = i; - tw_dev->state[i] = TW_S_INITIAL; - tw_dev->ioctl_size[i] = 0; - tw_dev->aen_queue[i] = 0; + /* Initialize generic buffer */ + error = tw_allocate_memory(tw_dev, sizeof(TW_Sector), 1); + if (error) { + printk(KERN_WARNING "3w-xxxx: Generic memory allocation failed.\n"); + return 1; } - for (i=0;iis_unit_present[i] = 0; - tw_dev->is_raid_five[i] = 0; + for (i=0;ifree_queue[i] = i; + tw_dev->state[i] = TW_S_INITIAL; } - tw_dev->num_units = 0; - tw_dev->num_aborts = 0; - tw_dev->num_resets = 0; - tw_dev->posted_request_count = 0; - tw_dev->max_posted_request_count = 0; - tw_dev->max_sgl_entries = 0; - tw_dev->sgl_entries = 0; - tw_dev->host = NULL; tw_dev->pending_head = TW_Q_START; tw_dev->pending_tail = TW_Q_START; - tw_dev->aen_head = 0; - 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; } /* End tw_initialize_device_extension() */ @@ -1089,14 +1126,13 @@ unsigned char request_id = 0; TW_Command *command_packet; TW_Param *param; - int i, j, imax, num_units = 0, num_raid_five = 0; + int i, imax, num_units = 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"); @@ -1168,8 +1204,7 @@ } if (command_packet->status != 0) { /* bad response */ - dprintk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags); - tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit); + tw_decode_sense(tw_dev, request_id, 0); return 1; } found = 1; @@ -1205,109 +1240,6 @@ return 1; } - /* Find raid 5 arrays */ - for (j=0;jis_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 */ - dprintk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags); - tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit); - 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: Bounce buffer allocation failed.\n"); - return 1; - } - memset(tw_dev->bounce_buffer[i], 0, sizeof(TW_Sector)*TW_MAX_SECTORS); - } - } - return 0; } /* End tw_initialize_units() */ @@ -1332,13 +1264,18 @@ if (tw_dev->tw_pci_dev->irq == irq) { spin_lock(&tw_dev->tw_lock); - dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt()\n"); + dprintk(KERN_WARNING "3w-xxxx: tw_interrupt()\n"); /* Read the registers */ status_reg_addr = tw_dev->registers.status_reg_addr; response_que_addr = tw_dev->registers.response_que_addr; status_reg_value = inl(status_reg_addr); + if (tw_check_bits(status_reg_value)) { + dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n"); + tw_decode_bits(tw_dev, status_reg_value); + } + /* Check which interrupt */ if (status_reg_value & TW_STATUS_HOST_INTERRUPT) do_host_interrupt=1; @@ -1403,17 +1340,18 @@ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; error = 0; if (command_packet->status != 0) { - dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Bad response, status = 0x%x, flags = 0x%x, unit = 0x%x.\n", command_packet->status, command_packet->flags, command_packet->byte3.unit); - tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit); - error = 1; + /* Bad response */ + if (tw_dev->srb[request_id] != 0) + tw_decode_sense(tw_dev, request_id, 1); + error = 3; } if (tw_dev->state[request_id] != TW_S_POSTED) { printk(KERN_WARNING "3w-xxxx: scsi%d: Received a request id (%d) (opcode = 0x%x) that wasn't posted.\n", tw_dev->host->host_no, request_id, command_packet->byte0.opcode); error = 1; } if (TW_STATUS_ERRORS(status_reg_value)) { - tw_decode_bits(tw_dev, status_reg_value); - error = 1; + tw_decode_bits(tw_dev, status_reg_value); + error = 1; } dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Response queue request id: %d.\n", request_id); /* Check for internal command */ @@ -1428,7 +1366,7 @@ dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n"); tw_decode_bits(tw_dev, status_reg_value); } - } else { + } else { switch (tw_dev->srb[request_id]->cmnd[0]) { case READ_10: case READ_6: @@ -1455,18 +1393,23 @@ tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16); tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); } - if (error) { + if (error == 1) { /* Tell scsi layer there was an error */ dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Scsi Error.\n"); tw_dev->srb[request_id]->result = (DID_RESET << 16); - } else { + } + if (error == 0) { /* Tell scsi layer command was a success */ tw_dev->srb[request_id]->result = (DID_OK << 16); } - tw_dev->state[request_id] = TW_S_COMPLETED; - tw_state_request_finish(tw_dev, request_id); - tw_dev->posted_request_count--; - tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); + if (error != 2) { + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + tw_dev->posted_request_count--; + tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); + + tw_unmap_scsi_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]); + } status_reg_value = inl(status_reg_addr); if (tw_check_bits(status_reg_value)) { dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n"); @@ -1479,19 +1422,22 @@ } spin_unlock_irqrestore(tw_dev->host->host_lock, flags); clear_bit(TW_IN_INTR, &tw_dev->flags); -} /* End tw_interrupt() */ +} /* End tw_interrupt() */ /* This function handles ioctls from userspace to the driver */ int tw_ioctl(TW_Device_Extension *tw_dev, int request_id) { unsigned char opcode; - int bufflen; + int bufflen, error = 0; TW_Param *param; - TW_Command *command_packet; + TW_Command *command_packet, *command_save; u32 param_value; TW_Ioctl *ioctl = NULL; TW_Passthru *passthru = NULL; - int tw_aen_code; + int tw_aen_code, i, use_sg; + char *data_ptr; + int total_bytes = 0; + dma_addr_t dma_handle; ioctl = (TW_Ioctl *)tw_dev->srb[request_id]->request_buffer; if (ioctl == NULL) { @@ -1598,7 +1544,7 @@ printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Passthru size (%ld) too big.\n", passthru->sg_list[0].length); return 1; } - passthru->sg_list[0].address = virt_to_bus(tw_dev->alignment_virtual_address[request_id]); + passthru->sg_list[0].address = tw_dev->alignment_physical_address[request_id]; tw_post_command_packet(tw_dev, request_id); return 0; case TW_CMD_PACKET: @@ -1612,6 +1558,161 @@ printk(KERN_WARNING "3w-xxxx: tw_ioctl(): ioctl->data NULL.\n"); return 1; } + case TW_CMD_PACKET_WITH_DATA: + dprintk(KERN_WARNING "3w-xxxx: tw_ioctl(): caught TW_CMD_PACKET_WITH_DATA.\n"); + command_save = (TW_Command *)tw_dev->alignment_virtual_address[request_id]; + if (command_save == NULL) { + printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad alignment virtual address.\n", tw_dev->host->host_no); + return 1; + } + if (ioctl->data != NULL) { + /* Copy down the command packet */ + memcpy(command_packet, ioctl->data, sizeof(TW_Command)); + memcpy(command_save, ioctl->data, sizeof(TW_Command)); + command_packet->request_id = request_id; + + /* Now deal with the two possible sglists */ + if (command_packet->byte0.sgl_offset == 2) { + use_sg = command_packet->size - 3; + for (i=0;ibyte8.param.sgl[i].length; + tw_dev->ioctl_data[request_id] = pci_alloc_consistent(tw_dev->tw_pci_dev, total_bytes, &dma_handle); + + if (!tw_dev->ioctl_data[request_id]) { + printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): kmalloc failed for request_id %d.\n", tw_dev->host->host_no, request_id); + return 1; + } + + /* Copy param sglist into the kernel */ + data_ptr = tw_dev->ioctl_data[request_id]; + for (i=0;ibyte8.param.sgl[i].address != NULL) { + error = copy_from_user(data_ptr, (u32 *)command_packet->byte8.param.sgl[i].address, command_packet->byte8.param.sgl[i].length); + if (error) { + printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying param sglist from userspace.\n", tw_dev->host->host_no); + goto tw_ioctl_bail; + } + } else { + printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad param sgl address.\n", tw_dev->host->host_no); + tw_dev->srb[request_id]->result = (DID_RESET << 16); + goto tw_ioctl_bail; + } + data_ptr+=command_packet->byte8.param.sgl[i].length; + } + command_packet->size = 4; + command_packet->byte8.param.sgl[0].address = dma_handle; + command_packet->byte8.param.sgl[0].length = total_bytes; + } + if (command_packet->byte0.sgl_offset == 3) { + use_sg = command_packet->size - 4; + for (i=0;ibyte8.io.sgl[i].length; + tw_dev->ioctl_data[request_id] = pci_alloc_consistent(tw_dev->tw_pci_dev, total_bytes, &dma_handle); + + if (!tw_dev->ioctl_data[request_id]) { + printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): pci_alloc_consistent() failed for request_id %d.\n", tw_dev->host->host_no, request_id); + return 1; + } + if (command_packet->byte0.opcode == TW_OP_WRITE) { + /* Copy io sglist into the kernel */ + data_ptr = tw_dev->ioctl_data[request_id]; + for (i=0;ibyte8.io.sgl[i].address != NULL) { + error = copy_from_user(data_ptr, (u32 *)command_packet->byte8.io.sgl[i].address, command_packet->byte8.io.sgl[i].length); + if (error) { + printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying io sglist from userspace.\n", tw_dev->host->host_no); + goto tw_ioctl_bail; + } + } else { + printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad io sgl address.\n", tw_dev->host->host_no); + tw_dev->srb[request_id]->result = (DID_RESET << 16); + goto tw_ioctl_bail; + } + data_ptr+=command_packet->byte8.io.sgl[i].length; + } + } + command_packet->size = 5; + command_packet->byte8.io.sgl[0].address = dma_handle; + command_packet->byte8.io.sgl[0].length = total_bytes; + } + + spin_unlock_irq(tw_dev->host->host_lock); + spin_unlock_irq(&tw_dev->tw_lock); + + /* Finally post the command packet */ + tw_post_command_packet(tw_dev, request_id); + + mdelay(TW_IOCTL_WAIT_TIME); + spin_lock_irq(&tw_dev->tw_lock); + spin_lock_irq(tw_dev->host->host_lock); + + if (signal_pending(current)) { + dprintk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Signal pending, aborting ioctl().\n", tw_dev->host->host_no); + tw_dev->srb[request_id]->result = (DID_OK << 16); + goto tw_ioctl_bail; + } + + tw_dev->srb[request_id]->result = (DID_OK << 16); + /* Now copy up the param or io sglist to userspace */ + if (command_packet->byte0.sgl_offset == 2) { + use_sg = command_save->size - 3; + data_ptr = phys_to_virt(command_packet->byte8.param.sgl[0].address); + for (i=0;ibyte8.param.sgl[i].address != NULL) { + error = copy_to_user((u32 *)command_save->byte8.param.sgl[i].address, data_ptr, command_save->byte8.param.sgl[i].length); + if (error) { + printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying param sglist to userspace.\n", tw_dev->host->host_no); + goto tw_ioctl_bail; + } + dprintk(KERN_WARNING "3w-xxxx: scsi%d: Copied %ld bytes to pid %d.\n", tw_dev->host->host_no, command_save->byte8.param.sgl[i].length, current->pid); + data_ptr+=command_save->byte8.param.sgl[i].length; + } else { + printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad param sgl address.\n", tw_dev->host->host_no); + tw_dev->srb[request_id]->result = (DID_RESET << 16); + goto tw_ioctl_bail; + } + } + } + if (command_packet->byte0.sgl_offset == 3) { + use_sg = command_save->size - 4; + if (command_packet->byte0.opcode == TW_OP_READ) { + data_ptr = phys_to_virt(command_packet->byte8.io.sgl[0].address); + for(i=0;ibyte8.io.sgl[i].address != NULL) { + error = copy_to_user((u32 *)command_save->byte8.io.sgl[i].address, data_ptr, command_save->byte8.io.sgl[i].length); + if (error) { + printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying io sglist to userspace.\n", tw_dev->host->host_no); + goto tw_ioctl_bail; + } + dprintk(KERN_WARNING "3w-xxxx: scsi%d: Copied %ld bytes to pid %d.\n", tw_dev->host->host_no, command_save->byte8.io.sgl[i].length, current->pid); + data_ptr+=command_save->byte8.io.sgl[i].length; + } else { + printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad io sgl address.\n", tw_dev->host->host_no); + tw_dev->srb[request_id]->result = (DID_RESET << 16); + goto tw_ioctl_bail; + } + } + } + } + + tw_ioctl_bail: + + /* Free up sglist memory */ + if (tw_dev->ioctl_data[request_id]) + pci_free_consistent(tw_dev->tw_pci_dev, total_bytes, tw_dev->ioctl_data[request_id], dma_handle); + else + printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Error freeing ioctl data.\n", tw_dev->host->host_no); + + /* Now complete the io */ + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + tw_dev->posted_request_count--; + tw_dev->srb[request_id]->scsi_done(tw_dev->srb[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; @@ -1655,6 +1756,7 @@ TW_Param *param; TW_Ioctl *ioctl = NULL; TW_Passthru *passthru = NULL; + TW_Command *command_packet; ioctl = (TW_Ioctl *)tw_dev->srb[request_id]->request_buffer; dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl_complete()\n"); @@ -1663,6 +1765,13 @@ printk(KERN_WARNING "3w-xxxx: tw_ioctl_complete(): Request buffer NULL.\n"); return 1; } + + command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; + if (command_packet == NULL) { + printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl_complete(): Bad command packet virtual address.\n", tw_dev->host->host_no); + return 1; + } + dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl_complete(): Request_bufflen = %d\n", tw_dev->srb[request_id]->request_bufflen); ioctl = (TW_Ioctl *)buff; @@ -1671,6 +1780,9 @@ passthru = (TW_Passthru *)ioctl->data; memcpy(buff, tw_dev->alignment_virtual_address[request_id], passthru->sector_count * 512); break; + case TW_CMD_PACKET_WITH_DATA: + dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_complete(): caught TW_CMD_PACKET_WITH_DATA.\n"); + return 2; /* Special case for isr to not complete io */ default: memset(buff, 0, tw_dev->srb[request_id]->request_bufflen); param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; @@ -1684,6 +1796,40 @@ return 0; } /* End tw_ioctl_complete() */ +static int tw_map_scsi_sg_data(struct pci_dev *pdev, Scsi_Cmnd *cmd) +{ + int use_sg; + int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction); + + dprintk(KERN_WARNING "3w-xxxx: tw_map_scsi_sg_data()\n"); + + if (cmd->use_sg == 0) + return 0; + + use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir); + cmd->SCp.phase = 2; + cmd->SCp.have_data_in = use_sg; + + return use_sg; +} /* End tw_map_scsi_sg_data() */ + +static u32 tw_map_scsi_single_data(struct pci_dev *pdev, Scsi_Cmnd *cmd) +{ + dma_addr_t mapping; + int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction); + + dprintk(KERN_WARNING "3w-xxxx: tw_map_scsi_single_data()\n"); + + if (cmd->request_bufflen == 0) + return 0; + + mapping = pci_map_single(pdev, cmd->request_buffer, cmd->request_bufflen, dma_dir); + cmd->SCp.phase = 2; + cmd->SCp.have_data_in = mapping; + + return mapping; +} /* End tw_map_scsi_single_data() */ + /* This function will mask the command interrupt */ void tw_mask_command_interrupt(TW_Device_Extension *tw_dev) { @@ -1704,14 +1850,25 @@ do_gettimeofday(&before); status_reg_value = inl(status_reg_addr); + if (tw_check_bits(status_reg_value)) { + dprintk(KERN_WARNING "3w-xxxx: tw_poll_status(): Unexpected bits.\n"); + tw_decode_bits(tw_dev, status_reg_value); + } + while ((status_reg_value & flag) != flag) { status_reg_value = inl(status_reg_addr); + + if (tw_check_bits(status_reg_value)) { + dprintk(KERN_WARNING "3w-xxxx: tw_poll_status(): Unexpected bits.\n"); + tw_decode_bits(tw_dev, status_reg_value); + } + do_gettimeofday(&timeout); if (before.tv_sec + seconds < timeout.tv_sec) { dprintk(KERN_WARNING "3w-xxxx: tw_poll_status(): Flag 0x%x not found.\n", flag); return 1; } - mdelay(1); + mdelay(5); } return 0; } /* End tw_poll_status() */ @@ -1787,6 +1944,7 @@ srb = tw_dev->srb[i]; srb->result = (DID_RESET << 16); tw_dev->srb[i]->scsi_done(tw_dev->srb[i]); + tw_unmap_scsi_data(tw_dev->tw_pci_dev, tw_dev->srb[i]); } } } @@ -1821,7 +1979,7 @@ error = tw_aen_drain_queue(tw_dev); if (error) { - printk(KERN_WARNING "3w-xxxx: scsi%d: Card not responding, retrying.\n", tw_dev->host->host_no); + printk(KERN_WARNING "3w-xxxx: scsi%d: AEN drain failed, retrying.\n", tw_dev->host->host_no); tries++; continue; } @@ -1857,7 +2015,7 @@ } /* Re-enable interrupts */ - tw_enable_interrupts(tw_dev); + tw_enable_and_clear_interrupts(tw_dev); return 0; } /* End tw_reset_sequence() */ @@ -2381,6 +2539,9 @@ capacity = (param_data[3] << 24) | (param_data[2] << 16) | (param_data[1] << 8) | param_data[0]; + /* Subtract one sector to fix get last sector ioctl */ + capacity -= 1; + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete(): Capacity = 0x%x.\n", capacity); /* Number of LBA's */ @@ -2403,8 +2564,8 @@ { TW_Command *command_packet; u32 command_que_addr, command_que_value = 0; - u32 lba = 0x0, num_sectors = 0x0; - int i, count = 0; + u32 lba = 0x0, num_sectors = 0x0, buffaddr = 0x0; + int i, use_sg; Scsi_Cmnd *srb; struct scatterlist *sglist; @@ -2461,45 +2622,25 @@ command_packet->byte8.io.lba = lba; command_packet->byte6.block_count = num_sectors; - 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; + /* 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"); + buffaddr = tw_map_scsi_single_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]); + command_packet->byte8.io.sgl[0].address = buffaddr; + 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) { + use_sg = tw_map_scsi_sg_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);; + for (i=0;ibyte8.io.sgl[i].address = sg_dma_address(&sglist[i]); + command_packet->byte8.io.sgl[i].length = sg_dma_len(&sglist[i]); + 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 */ - } - } + if (tw_dev->srb[request_id]->use_sg >= 1) + command_packet->size-=2; + } /* Update SG statistics */ tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg; @@ -2521,18 +2662,18 @@ /* This function will handle the request sense scsi command */ int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id) { - dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_request_sense()\n"); - - /* For now we just zero the sense buffer */ - memset(tw_dev->srb[request_id]->request_buffer, 0, tw_dev->srb[request_id]->request_bufflen); - tw_dev->state[request_id] = TW_S_COMPLETED; - tw_state_request_finish(tw_dev, request_id); - - /* If we got a request_sense, we probably want a reset, return error */ - tw_dev->srb[request_id]->result = (DID_ERROR << 16); - tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); - - return 0; + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_request_sense()\n"); + + /* For now we just zero the request buffer */ + memset(tw_dev->srb[request_id]->request_buffer, 0, tw_dev->srb[request_id]->request_bufflen); + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + + /* If we got a request_sense, we probably want a reset, return error */ + tw_dev->srb[request_id]->result = (DID_ERROR << 16); + tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); + + return 0; } /* End tw_scsiop_request_sense() */ /* This function will handle test unit ready scsi command */ @@ -2626,8 +2767,7 @@ } if (command_packet->status != 0) { /* bad response */ - dprintk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags); - tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit); + tw_decode_sense(tw_dev, request_id, 0); return 1; } break; /* Response was okay, so we exit */ @@ -2671,7 +2811,7 @@ } /* Re-enable interrupts */ - tw_enable_interrupts(tw_dev); + tw_enable_and_clear_interrupts(tw_dev); return 0; } /* End tw_shutdown_device() */ @@ -2719,7 +2859,7 @@ int id = 0; dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start()\n"); - + /* Obtain next free request_id */ do { if (tw_dev->free_head == tw_dev->free_wrap) { @@ -2738,6 +2878,19 @@ return 0; } /* End tw_state_request_start() */ +static void tw_unmap_scsi_data(struct pci_dev *pdev, Scsi_Cmnd *cmd) +{ + int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction); + + dprintk(KERN_WARNING "3w-xxxx: tw_unmap_scsi_data()\n"); + + if (cmd->use_sg) { + pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, dma_dir); + } else { + pci_unmap_single(pdev, cmd->SCp.have_data_in, cmd->request_bufflen, dma_dir); + } +} /* End tw_unmap_scsi_data() */ + /* This function will unmask the command interrupt on the controller */ void tw_unmask_command_interrupt(TW_Device_Extension *tw_dev) { @@ -2749,7 +2902,6 @@ } /* End tw_unmask_command_interrupt() */ /* Now get things going */ - static Scsi_Host_Template driver_template = TWXXXX; #include "scsi_module.c" diff -Nru a/drivers/scsi/3w-xxxx.h b/drivers/scsi/3w-xxxx.h --- a/drivers/scsi/3w-xxxx.h Thu Mar 7 18:17:40 2002 +++ b/drivers/scsi/3w-xxxx.h Thu Mar 7 18:17:40 2002 @@ -6,7 +6,7 @@ Arnaldo Carvalho de Melo Brad Strand - Copyright (C) 1999-2001 3ware Inc. + Copyright (C) 1999-2002 3ware Inc. Kernel compatablity By: Andre Hedrick Non-Copyright (C) 2000 Andre Hedrick @@ -62,7 +62,7 @@ static char *tw_aen_string[] = { "AEN queue empty", // 0x000 "Soft reset occurred", // 0x001 - "Mirorr degraded: Unit #", // 0x002 + "Unit degraded: Unit #", // 0x002 "Controller error", // 0x003 "Rebuild failed: Unit #", // 0x004 "Rebuild complete: Unit #", // 0x005 @@ -90,10 +90,36 @@ "DCB unsupported version: Port #", // 0x028 "Verify started: Unit #", // 0x029 "Verify failed: Port #", // 0x02A - "Verify complete: Unit #" // 0x02B + "Verify complete: Unit #", // 0x02B + "Overwrote bad sector during rebuild: Port #", //0x2C + "Encountered bad sector during rebuild: Port #" //0x2D }; -#define TW_AEN_STRING_MAX 0x02C +#define TW_AEN_STRING_MAX 0x02E + +/* + Sense key lookup table + Format: ESDC/flags,SenseKey,AdditionalSenseCode,AdditionalSenseCodeQualifier +*/ +static unsigned char tw_sense_table[][4] = +{ + /* Codes for newer firmware */ + // ATA Error SCSI Error + {0x01, 0x03, 0x13, 0x00}, // Address mark not found Address mark not found for data field + {0x04, 0x0b, 0x00, 0x00}, // Aborted command Aborted command + {0x10, 0x0b, 0x14, 0x00}, // ID not found Recorded entity not found + {0x40, 0x03, 0x11, 0x00}, // Uncorrectable ECC error Unrecovered read error + {0x61, 0x04, 0x00, 0x00}, // Device fault Hardware error + {0x84, 0x0b, 0x47, 0x00}, // Data CRC error SCSI parity error + {0xd0, 0x0b, 0x00, 0x00}, // Device busy Aborted command + {0xd1, 0x0b, 0x00, 0x00}, // Device busy Aborted command + + /* Codes for older firmware */ + // 3ware Error SCSI Error + {0x09, 0x0b, 0x00, 0x00}, // Unrecovered disk error Aborted command + {0x37, 0x0b, 0x04, 0x00}, // Unit offline Logical unit not ready + {0x51, 0x0b, 0x00, 0x00} // Unspecified Aborted command +}; /* Control register bit definitions */ #define TW_CONTROL_CLEAR_HOST_INTERRUPT 0x00080000 @@ -108,6 +134,7 @@ #define TW_CONTROL_DISABLE_INTERRUPTS 0x00000040 #define TW_CONTROL_ISSUE_HOST_INTERRUPT 0x00000020 #define TW_CONTROL_CLEAR_PARITY_ERROR 0x00800000 +#define TW_CONTROL_CLEAR_PCI_ABORT 0x00100000 /* Status register bit definitions */ #define TW_STATUS_MAJOR_VERSION_MASK 0xF0000000 @@ -140,6 +167,7 @@ #define TW_DEVICE_ID2 (0x1001) /* 7000 series controller */ #define TW_NUMDEVICES 2 #define TW_PCI_CLEAR_PARITY_ERRORS 0xc100 +#define TW_PCI_CLEAR_PCI_ABORT 0x2000 /* Command packet opcodes */ #define TW_OP_NOP 0x0 @@ -153,6 +181,7 @@ #define TW_OP_AEN_LISTEN 0x1c #define TW_CMD_PACKET 0x1d #define TW_ATA_PASSTHRU 0x1e +#define TW_CMD_PACKET_WITH_DATA 0x1f /* Asynchronous Event Notification (AEN) Codes */ #define TW_AEN_QUEUE_EMPTY 0x0000 @@ -169,7 +198,8 @@ #define TW_AEN_SBUF_FAIL 0x0024 /* Misc defines */ -#define TW_ALIGNMENT 0x200 /* 16 D-WORDS */ +#define TW_ALIGNMENT_6000 64 /* 64 bytes */ +#define TW_ALIGNMENT_7000 4 /* 4 bytes */ #define TW_MAX_UNITS 16 #define TW_COMMAND_ALIGNMENT_MASK 0x1ff #define TW_INIT_MESSAGE_CREDITS 0x100 @@ -179,7 +209,6 @@ #define TW_ATA_PASS_SGL_MAX 60 #define TW_MAX_PASSTHRU_BYTES 4096 #define TW_Q_LENGTH 256 -#define TW_MAX_BOUNCEBUF 16 #define TW_Q_START 0 #define TW_MAX_SLOT 32 #define TW_MAX_PCI_BUSES 255 @@ -191,12 +220,9 @@ #define TW_MAX_AEN_TRIES 100 #define TW_UNIT_ONLINE 1 #define TW_IN_INTR 1 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,7) #define TW_MAX_SECTORS 256 -#else -#define TW_MAX_SECTORS 128 -#endif #define TW_AEN_WAIT_TIME 1000 +#define TW_IOCTL_WAIT_TIME (1 * HZ) /* 1 second */ /* Macros */ #define TW_STATUS_ERRORS(x) \ @@ -262,7 +288,6 @@ } TW_Command; typedef struct TAG_TW_Ioctl { - int buffer; unsigned char opcode; unsigned short table_id; unsigned char parameter_id; @@ -345,11 +370,8 @@ TW_Registers registers; u32 *alignment_virtual_address[TW_Q_LENGTH]; u32 alignment_physical_address[TW_Q_LENGTH]; - u32 *bounce_buffer[TW_Q_LENGTH]; int is_unit_present[TW_MAX_UNITS]; - int is_raid_five[TW_MAX_UNITS]; int num_units; - int num_raid_five; u32 *command_packet_virtual_address[TW_Q_LENGTH]; u32 command_packet_physical_address[TW_Q_LENGTH]; struct pci_dev *tw_pci_dev; @@ -381,22 +403,24 @@ unsigned char aen_head; unsigned char aen_tail; long flags; /* long req'd for set_bit --RR */ + char *ioctl_data[TW_Q_LENGTH]; } TW_Device_Extension; /* Function prototypes */ int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id); int tw_aen_drain_queue(TW_Device_Extension *tw_dev); int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id); -int tw_allocate_memory(TW_Device_Extension *tw_dev, int request_id, int size, int which); +int tw_allocate_memory(TW_Device_Extension *tw_dev, int size, int which); int tw_check_bits(u32 status_reg_value); int tw_check_errors(TW_Device_Extension *tw_dev); void tw_clear_attention_interrupt(TW_Device_Extension *tw_dev); void tw_clear_host_interrupt(TW_Device_Extension *tw_dev); void tw_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value); -void tw_decode_error(TW_Device_Extension *tw_dev, unsigned char status, unsigned char flags, unsigned char unit); +void tw_decode_sense(TW_Device_Extension *tw_dev, int request_id, int fill_sense); void tw_disable_interrupts(TW_Device_Extension *tw_dev); int tw_empty_response_que(TW_Device_Extension *tw_dev); void tw_enable_interrupts(TW_Device_Extension *tw_dev); +void tw_enable_and_clear_interrupts(TW_Device_Extension *tw_dev); int tw_findcards(Scsi_Host_Template *tw_host); void tw_free_device_extension(TW_Device_Extension *tw_dev); int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits); diff -Nru a/drivers/scsi/Makefile b/drivers/scsi/Makefile --- a/drivers/scsi/Makefile Thu Mar 7 18:17:40 2002 +++ b/drivers/scsi/Makefile Thu Mar 7 18:17:40 2002 @@ -1,7 +1,7 @@ # # Makefile for linux/drivers/scsi # -# 30 May 2000, Christoph Hellwig +# 30 May 2000, Christoph Hellwig # Rewritten to use lists instead of if-statements. # # 20 Sep 2000, Torben Mathiasen diff -Nru a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c --- a/drivers/scsi/NCR5380.c Thu Mar 7 18:17:40 2002 +++ b/drivers/scsi/NCR5380.c Thu Mar 7 18:17:40 2002 @@ -83,8 +83,6 @@ * basically, transfer size needs to be reduced by one * and the last byte read as is done with PSEUDO_DMA. * - * 3. Test USLEEP code - * * 4. Test SCSI-II tagged queueing (I have no devices which support * tagged queueing) * @@ -110,6 +108,12 @@ #define READ_OVERRUNS #endif +#ifdef BOARD_REQUIRES_NO_DELAY +#define io_recovery_delay(x) +#else +#define io_recovery_delay(x) udelay(x) +#endif + /* * Design * Issues : @@ -192,9 +196,8 @@ * phase goes through the various phases as instructed by the target. * if the target goes into MSG IN and sends a DISCONNECT message, * the command structure is placed into the per instance disconnected - * queue, and NCR5380_main tries to find more work. If USLEEP - * was defined, and the target is idle for too long, the system - * will try to sleep. + * queue, and NCR5380_main tries to find more work. If the target is + * idle for too long, the system will try to sleep. * * If a command has disconnected, eventually an interrupt will trigger, * calling NCR5380_intr() which will in turn call NCR5380_reselect @@ -244,21 +247,14 @@ * rely on phase mismatch and EOP interrupts to determine end * of phase. * - * SCSI2 - if defined, SCSI-2 tagged queuing is used where possible - * * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. You * only really want to use this if you're having a problem with * dropped characters during high speed communications, and even * then, you're going to be better off twiddling with transfersize * in the high level code. * - * USLEEP - if defined, on devices that aren't disconnecting from the - * bus, we will go to sleep so that the CPU can get real work done - * when we run a command that won't complete immediately. - * - * Defaults for these will be provided if USLEEP is defined, although - * the user may want to adjust these to allocate CPU resources to - * the SCSI driver or "real" code. + * Defaults for these will be provided although the user may want to adjust + * these to allocate CPU resources to the SCSI driver or "real" code. * * USLEEP_SLEEP - amount of time, in jiffies, to sleep * @@ -322,18 +318,13 @@ static void do_reset(struct Scsi_Host *host); static struct Scsi_Host *first_instance = NULL; static Scsi_Host_Template *the_template = NULL; - -#ifdef USLEEP -struct timer_list usleep_timer; -#endif +static struct timer_list usleep_timer; /* - * Function : void initialize_SCp(Scsi_Cmnd *cmd) + * initialize_SCp - init the scsi pointer field + * @cmd: command block to set up * - * Purpose : initialize the saved data pointers for cmd to point to the - * start of the buffer. - * - * Inputs : cmd - Scsi_Cmnd structure to have pointers reset. + * Set up the internal fields in the SCSI command. */ static __inline__ void initialize_SCp(Scsi_Cmnd * cmd) @@ -362,88 +353,49 @@ static struct { unsigned char mask; const char *name; -} signals[] = { { - - SR_DBP, "PARITY" -}, { - SR_RST, "RST" -}, { - SR_BSY, "BSY" -}, -{ - SR_REQ, "REQ" -}, { - SR_MSG, "MSG" -}, { - SR_CD, "CD" -}, { - SR_IO, "IO" -}, -{ - SR_SEL, "SEL" -}, { - 0, NULL -} -}, - -basrs[] = { { - BASR_ATN, "ATN" -}, { - BASR_ACK, "ACK" -}, { - 0, NULL -} -}, - -icrs[] = { { - ICR_ASSERT_RST, "ASSERT RST" -}, { - ICR_ASSERT_ACK, "ASSERT ACK" -}, -{ - ICR_ASSERT_BSY, "ASSERT BSY" -}, { - ICR_ASSERT_SEL, "ASSERT SEL" -}, -{ - ICR_ASSERT_ATN, "ASSERT ATN" -}, { - ICR_ASSERT_DATA, "ASSERT DATA" -}, -{ - 0, NULL -} -}, - -mrs[] = { { - MR_BLOCK_DMA_MODE, "MODE BLOCK DMA" -}, { - MR_TARGET, "MODE TARGET" -}, -{ - MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK" -}, { - MR_ENABLE_PAR_INTR, - "MODE PARITY INTR" -}, { - MR_MONITOR_BSY, "MODE MONITOR BSY" -}, -{ - MR_DMA_MODE, "MODE DMA" -}, { - MR_ARBITRATE, "MODE ARBITRATION" -}, -{ - 0, NULL -} +} signals[] = { + {SR_DBP, "PARITY"}, + {SR_RST, "RST"}, + {SR_BSY, "BSY"}, + {SR_REQ, "REQ"}, + {SR_MSG, "MSG"}, + {SR_CD, "CD"}, + {SR_IO, "IO"}, + {SR_SEL, "SEL"}, + {0, NULL} +}, +basrs[] = { + {BASR_ATN, "ATN"}, + {BASR_ACK, "ACK"}, + {0, NULL} +}, +icrs[] = { + {ICR_ASSERT_RST, "ASSERT RST"}, + {ICR_ASSERT_ACK, "ASSERT ACK"}, + {ICR_ASSERT_BSY, "ASSERT BSY"}, + {ICR_ASSERT_SEL, "ASSERT SEL"}, + {ICR_ASSERT_ATN, "ASSERT ATN"}, + {ICR_ASSERT_DATA, "ASSERT DATA"}, + {0, NULL} +}, +mrs[] = { + {MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, + {MR_TARGET, "MODE TARGET"}, + {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, + {MR_ENABLE_PAR_INTR, "MODE PARITY INTR"}, + {MR_MONITOR_BSY, "MODE MONITOR BSY"}, + {MR_DMA_MODE, "MODE DMA"}, + {MR_ARBITRATE, "MODE ARBITRATION"}, + {0, NULL} }; -/* - * Function : void NCR5380_print(struct Scsi_Host *instance) +/** + * NCR5380_print - print scsi bus signals + * @instance: adapter state to dump * - * Purpose : print the SCSI bus signals for debugging purposes + * Print the SCSI bus signals for debugging purposes * - * Input : instance - which NCR5380 + * Locks: none */ static void NCR5380_print(struct Scsi_Host *instance) @@ -452,6 +404,7 @@ unsigned long flags; unsigned char status, data, basr, mr, icr, i; NCR5380_setup(instance); + /* FIXME - this needs proper locking */ save_flags(flags); cli(); data = NCR5380_read(CURRENT_SCSI_DATA_REG); @@ -483,32 +436,22 @@ unsigned char value; const char *name; } phases[] = { - - { - PHASE_DATAOUT, "DATAOUT" - }, { - PHASE_DATAIN, "DATAIN" - }, { - PHASE_CMDOUT, "CMDOUT" - }, - { - PHASE_STATIN, "STATIN" - }, { - PHASE_MSGOUT, "MSGOUT" - }, { - PHASE_MSGIN, "MSGIN" - }, - { - PHASE_UNKNOWN, "UNKNOWN" - } + {PHASE_DATAOUT, "DATAOUT"}, + {PHASE_DATAIN, "DATAIN"}, + {PHASE_CMDOUT, "CMDOUT"}, + {PHASE_STATIN, "STATIN"}, + {PHASE_MSGOUT, "MSGOUT"}, + {PHASE_MSGIN, "MSGIN"}, + {PHASE_UNKNOWN, "UNKNOWN"} }; /* - * Function : void NCR5380_print_phase(struct Scsi_Host *instance) + * NCR5380_print_phase - show SCSI phase + * @instance: adapter to dump * - * Purpose : print the current SCSI phase for debugging purposes + * Print the current SCSI phase for debugging purposes * - * Input : instance - which NCR5380 + * Locks: none */ static void NCR5380_print_phase(struct Scsi_Host *instance) @@ -520,11 +463,9 @@ status = NCR5380_read(STATUS_REG); if (!(status & SR_REQ)) - printk("scsi%d : REQ not asserted, phase unknown.\n", - instance->host_no); + printk("scsi%d : REQ not asserted, phase unknown.\n", instance->host_no); else { - for (i = 0; (phases[i].value != PHASE_UNKNOWN) && - (phases[i].value != (status & PHASE_MASK)); ++i); + for (i = 0; (phases[i].value != PHASE_UNKNOWN) && (phases[i].value != (status & PHASE_MASK)); ++i); printk("scsi%d : phase %s\n", instance->host_no, phases[i].name); } } @@ -549,7 +490,7 @@ * conditions are possible. */ -static volatile int main_running = 0; +static unsigned long main_running = 0; /* * Function : run_main(void) @@ -563,14 +504,10 @@ static __inline__ void run_main(void) { - if (!main_running) { - main_running = 1; + if (!test_and_set_bit(0, &main_running)) NCR5380_main(); - } } -#ifdef USLEEP - /* * These need tweaking, and would probably work best as per-device * flags initialized differently for disk, tape, cd, etc devices. @@ -621,63 +558,67 @@ static int should_disconnect(unsigned char cmd) { switch (cmd) { - case READ_6: - case WRITE_6: - case SEEK_6: - case READ_10: - case WRITE_10: - case SEEK_10: - return DISCONNECT_TIME_TO_DATA; - case FORMAT_UNIT: - case SEARCH_HIGH: - case SEARCH_LOW: - case SEARCH_EQUAL: - return DISCONNECT_LONG; - default: - return DISCONNECT_NONE; + case READ_6: + case WRITE_6: + case SEEK_6: + case READ_10: + case WRITE_10: + case SEEK_10: + return DISCONNECT_TIME_TO_DATA; + case FORMAT_UNIT: + case SEARCH_HIGH: + case SEARCH_LOW: + case SEARCH_EQUAL: + return DISCONNECT_LONG; + default: + return DISCONNECT_NONE; } } /* * Assumes instance->time_expires has been set in higher level code. + * + * Locks: Caller must hold io_request_lock */ static int NCR5380_set_timer(struct Scsi_Host *instance) { - unsigned long flags; struct Scsi_Host *tmp, **prev; - save_flags(flags); - cli(); if (((struct NCR5380_hostdata *) (instance->hostdata))->next_timer) { - restore_flags(flags); return -1; } - for (prev = &expires_first, tmp = expires_first; tmp; - prev = &(((struct NCR5380_hostdata *) tmp->hostdata)->next_timer), - tmp = ((struct NCR5380_hostdata *) tmp->hostdata)->next_timer) - if (((struct NCR5380_hostdata *)instance->hostdata)->time_expires < - ((struct NCR5380_hostdata *)tmp->hostdata)->time_expires) + for (prev = &expires_first, tmp = expires_first; tmp; prev = &(((struct NCR5380_hostdata *) tmp->hostdata)->next_timer), tmp = ((struct NCR5380_hostdata *) tmp->hostdata)->next_timer) + if (((struct NCR5380_hostdata *) instance->hostdata)->time_expires < ((struct NCR5380_hostdata *) tmp->hostdata)->time_expires) break; ((struct NCR5380_hostdata *) instance->hostdata)->next_timer = tmp; *prev = instance; - + mod_timer(&usleep_timer, ((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires); - restore_flags(flags); return 0; } -/* Doing something about unwanted reentrancy here might be useful */ -void NCR5380_timer_fn(unsigned long surplus_to_requirements) +/** + * NCR5380_timer_fn - handle polled timeouts + * @unused: unused + * + * Walk the list of controllers, find which controllers have exceeded + * their expiry timeout and then schedule the processing co-routine to + * do the real work. + * + * Doing something about unwanted reentrancy here might be useful + * + * Locks: disables irqs, takes and frees io_request_lock + */ + +static void NCR5380_timer_fn(unsigned long unused) { - unsigned long flags; struct Scsi_Host *instance; - save_flags(flags); - cli(); - for (; expires_first && - time_before_eq(((struct NCR5380_hostdata *)expires_first->hostdata)->time_expires, jiffies); ) - { + + spin_lock_irq(&io_request_lock); + + for (; expires_first && time_before_eq(((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires, jiffies);) { instance = ((struct NCR5380_hostdata *) expires_first->hostdata)->next_timer; ((struct NCR5380_hostdata *) expires_first->hostdata)->next_timer = NULL; ((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires = 0; @@ -685,90 +626,91 @@ } del_timer(&usleep_timer); - if (expires_first) - { - usleep_timer.expires = ((struct NCR5380_hostdata *)expires_first->hostdata)->time_expires; + if (expires_first) { + usleep_timer.expires = ((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires; add_timer(&usleep_timer); } - restore_flags(flags); - - spin_lock_irqsave(instance->host_lock, flags); run_main(); - spin_unlock_irqrestore(instance->host_lock, flags); + spin_unlock_irq(&io_request_lock); } -#endif /* def USLEEP */ +/** + * NCR5380_all_init - global setup + * + * Set up the global values and timers needed by the NCR5380 driver + */ + static inline void NCR5380_all_init(void) { static int done = 0; if (!done) { -#if (NDEBUG & NDEBUG_INIT) - printk("scsi : NCR5380_all_init()\n"); -#endif + dprintk(NDEBUG_INIT, ("scsi : NCR5380_all_init()\n")); done = 1; -#ifdef USLEEP init_timer(&usleep_timer); usleep_timer.function = NCR5380_timer_fn; -#endif } } -#ifdef AUTOPROBE_IRQ -/* - * Function : int NCR5380_probe_irq (struct Scsi_Host *instance, int possible) - * - * Purpose : autoprobe for the IRQ line used by the NCR5380. - * - * Inputs : instance - pointer to this instance of the NCR5380 driver, - * possible - bitmask of permissible interrupts. - * - * Returns : number of the IRQ selected, IRQ_NONE if no interrupt fired. - * - * XXX no effort is made to deal with spurious interrupts. - */ - static int probe_irq __initdata = 0; +/** + * probe_intr - helper for IRQ autoprobe + * @irq: interrupt number + * @dev_id: unused + * @regs: unused + * + * Set a flag to indicate the IRQ in question was received. This is + * used by the IRQ probe code. + */ + static void __init probe_intr(int irq, void *dev_id, struct pt_regs *regs) { probe_irq = irq; } +/** + * NCR5380_probe_irq - find the IRQ of an NCR5380 + * @instance: NCR5380 controller + * @possible: bitmask of ISA IRQ lines + * + * Autoprobe for the IRQ line used by the NCR5380 by triggering an IRQ + * and then looking to see what interrupt actually turned up. + * + * Locks: none, irqs must be enabled on entry + */ + static int __init NCR5380_probe_irq(struct Scsi_Host *instance, int possible) { NCR5380_local_declare(); - struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) - instance->hostdata; + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; unsigned long timeout; int trying_irqs, i, mask; NCR5380_setup(instance); for (trying_irqs = i = 0, mask = 1; i < 16; ++i, mask <<= 1) - if ((mask & possible) && (request_irq(i, &probe_intr, SA_INTERRUPT, "NCR-probe", instance) - == 0)) + if ((mask & possible) && (request_irq(i, &probe_intr, SA_INTERRUPT, "NCR-probe", NULL) == 0)) trying_irqs |= mask; timeout = jiffies + (250 * HZ / 1000); probe_irq = IRQ_NONE; -/* - * A interrupt is triggered whenever BSY = false, SEL = true - * and a bit set in the SELECT_ENABLE_REG is asserted on the - * SCSI bus. - * - * Note that the bus is only driven when the phase control signals - * (I/O, C/D, and MSG) match those in the TCR, so we must reset that - * to zero. - */ + /* + * A interrupt is triggered whenever BSY = false, SEL = true + * and a bit set in the SELECT_ENABLE_REG is asserted on the + * SCSI bus. + * + * Note that the bus is only driven when the phase control signals + * (I/O, C/D, and MSG) match those in the TCR, so we must reset that + * to zero. + */ NCR5380_write(TARGET_COMMAND_REG, 0); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | - ICR_ASSERT_SEL); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_SEL); - while (probe_irq == IRQ_NONE && time_before(jiffies,timeout)) + while (probe_irq == IRQ_NONE && time_before(jiffies, timeout)) barrier(); NCR5380_write(SELECT_ENABLE_REG, 0); @@ -780,15 +722,16 @@ return probe_irq; } -#endif /* AUTOPROBE_IRQ */ -/* - * Function : void NCR58380_print_options (struct Scsi_Host *instance) - * - * Purpose : called by probe code indicating the NCR5380 driver - * options that were selected. +/** + * NCR58380_print_options - show options + * @instance: unused for now + * + * Called by probe code indicating the NCR5380 driver options that + * were selected. At some point this will switch to runtime options + * read from the adapter in question * - * Inputs : instance, pointer to this instance. Unused. + * Locks: none */ static void __init NCR5380_print_options(struct Scsi_Host *instance) @@ -815,29 +758,25 @@ #ifdef PSEUDO_DMA " PSEUDO DMA" #endif -#ifdef SCSI2 - " SCSI-2" -#endif #ifdef UNSAFE " UNSAFE " #endif ); -#ifdef USLEEP printk(" USLEEP, USLEEP_POLL=%d USLEEP_SLEEP=%d", USLEEP_POLL, USLEEP_SLEEP); -#endif printk(" generic release=%d", NCR5380_PUBLIC_RELEASE); if (((struct NCR5380_hostdata *) instance->hostdata)->flags & FLAG_NCR53C400) { printk(" ncr53c400 release=%d", NCR53C400_PUBLIC_RELEASE); } } -/* - * Function : void NCR5380_print_status (struct Scsi_Host *instance) +/** + * NCR5380_print_status - dump controller info + * @instance: controller to dump * - * Purpose : print commands in the various queues, called from - * NCR5380_abort and NCR5380_debug to aid debugging. + * Print commands in the various queues, called from NCR5380_abort + * and NCR5380_debug to aid debugging. * - * Inputs : instance, pointer to this instance. + * Locks: called functions disable irqs, missing queue lock in proc call */ static void NCR5380_print_status(struct Scsi_Host *instance) @@ -846,16 +785,12 @@ char *start; int len; - printk("NCR5380 : coroutine is%s running.\n", - main_running ? "" : "n't"); + printk("NCR5380 : coroutine is%s running.\n", main_running ? "" : "n't"); -#ifdef NDEBUG - NCR5380_print(instance); - NCR5380_print_phase(instance); -#endif + NCR5380_dprint(NDEBUG_ANY, instance); + NCR5380_dprint_phase(NDEBUG_ANY, instance); - len = NCR5380_proc_info(pr_bfr, &start, 0, sizeof(pr_bfr), - instance->host_no, 0); + len = NCR5380_proc_info(pr_bfr, &start, 0, sizeof(pr_bfr), instance->host_no, 0); pr_bfr[len] = 0; printk("\n%s\n", pr_bfr); } @@ -886,18 +821,14 @@ #ifndef NCR5380_proc_info static #endif -int NCR5380_proc_info( - char *buffer, char **start, off_t offset, - int length, int hostno, int inout) +int NCR5380_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout) { - unsigned long flags; char *pos = buffer; struct Scsi_Host *instance; struct NCR5380_hostdata *hostdata; Scsi_Cmnd *ptr; - for (instance = first_instance; instance && - instance->host_no != hostno; instance = instance->next); + for (instance = first_instance; instance && instance->host_no != hostno; instance = instance->next); if (!instance) return (-ESRCH); hostdata = (struct NCR5380_hostdata *) instance->hostdata; @@ -935,32 +866,27 @@ SPRINTF("IRQ: %d.\n", instance->irq); #ifdef DTC_PUBLIC_RELEASE - SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", - dtc_wmaxi, dtc_maxi); + SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", dtc_wmaxi, dtc_maxi); #endif #ifdef PAS16_PUBLIC_RELEASE - SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", - pas_wmaxi, pas_maxi); + SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", pas_wmaxi, pas_maxi); #endif - save_flags(flags); - cli(); + spin_lock_irq(&io_request_lock); SPRINTF("NCR5380 : coroutine is%s running.\n", main_running ? "" : "n't"); if (!hostdata->connected) SPRINTF("scsi%d: no currently connected command\n", instance->host_no); else - pos = lprint_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected, - pos, buffer, length); + pos = lprint_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected, pos, buffer, length); SPRINTF("scsi%d: issue_queue\n", instance->host_no); - for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; - ptr = (Scsi_Cmnd *) ptr->host_scribble) + for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble) pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length); SPRINTF("scsi%d: disconnected_queue\n", instance->host_no); - for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; - ptr = (Scsi_Cmnd *) ptr->host_scribble) + for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble) pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length); - restore_flags(flags); + spin_unlock_irq(&io_request_lock); + *start = buffer; if (pos - buffer < offset) return 0; @@ -972,16 +898,14 @@ static char *lprint_Scsi_Cmnd(Scsi_Cmnd * cmd, char *pos, char *buffer, int length) { - SPRINTF("scsi%d : destination target %d, lun %d\n", - cmd->host->host_no, cmd->target, cmd->lun); + SPRINTF("scsi%d : destination target %d, lun %d\n", cmd->host->host_no, cmd->target, cmd->lun); SPRINTF(" command = "); pos = lprint_command(cmd->cmnd, pos, buffer, length); return (pos); } static -char *lprint_command(unsigned char *command, - char *pos, char *buffer, int length) +char *lprint_command(unsigned char *command, char *pos, char *buffer, int length) { int i, s; pos = lprint_opcode(command[0], pos, buffer, length); @@ -999,17 +923,18 @@ } -/* - * Function : void NCR5380_init (struct Scsi_Host *instance, flags) +/** + * NCR5380_init - initialise an NCR5380 + * @instance: adapter to configure + * @flags: control flags * - * Purpose : initializes *instance and corresponding 5380 chip, + * Initializes *instance and corresponding 5380 chip, * with flags OR'd into the initial flags value. * - * Inputs : instance - instantiation of the 5380 driver. - * - * Notes : I assume that the host, hostno, and id bits have been + * Notes : I assume that the host, hostno, and id bits have been * set correctly. I don't care about the irq and other fields. - * + * + * Locks: interrupts must be enabled when we are called */ static void __init NCR5380_init(struct Scsi_Host *instance, int flags) @@ -1017,9 +942,10 @@ NCR5380_local_declare(); int i, pass; unsigned long timeout; - struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) - instance->hostdata; + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; + if(in_interrupt()) + printk(KERN_ERR "NCR5380_init called with interrupts off!\n"); /* * On NCR53C400 boards, NCR5380 registers are mapped 8 past * the base address. @@ -1031,7 +957,6 @@ #endif NCR5380_setup(instance); - NCR5380_all_init(); hostdata->aborted = 0; @@ -1070,17 +995,13 @@ the_template = instance->hostt; first_instance = instance; } -#ifdef USLEEP hostdata->time_expires = 0; hostdata->next_timer = NULL; -#endif #ifndef AUTOSENSE if ((instance->cmd_per_lun > 1) || instance->can_queue > 1) - ) - printk("scsi%d : WARNING : support for multiple outstanding commands enabled\n" - " without AUTOSENSE option, contingent allegiance conditions may\n" - " be incorrectly cleared.\n", instance->host_no); + printk(KERN_WARNING "scsi%d : WARNING : support for multiple outstanding commands enabled\n" " without AUTOSENSE option, contingent allegiance conditions may\n" + " be incorrectly cleared.\n", instance->host_no); #endif /* def AUTOSENSE */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); @@ -1106,50 +1027,40 @@ * failing, do a hard reset of the SCSI bus */ - for (pass = 1; (NCR5380_read(STATUS_REG) & SR_BSY) && - pass <= 6; ++pass) { + for (pass = 1; (NCR5380_read(STATUS_REG) & SR_BSY) && pass <= 6; ++pass) { switch (pass) { case 1: case 3: case 5: - printk("scsi%d: SCSI bus busy, waiting up to five seconds\n", - instance->host_no); + printk("scsi%d: SCSI bus busy, waiting up to five seconds\n", instance->host_no); timeout = jiffies + 5 * HZ; - while (time_before(jiffies,timeout) && (NCR5380_read(STATUS_REG) & SR_BSY)); + while (time_before(jiffies, timeout) && (NCR5380_read(STATUS_REG) & SR_BSY)); break; case 2: - printk("scsi%d: bus busy, attempting abort\n", - instance->host_no); + printk("scsi%d: bus busy, attempting abort\n", instance->host_no); do_abort(instance); break; case 4: - printk("scsi%d: bus busy, attempting reset\n", - instance->host_no); + printk("scsi%d: bus busy, attempting reset\n", instance->host_no); do_reset(instance); break; case 6: - printk("scsi%d: bus locked solid or invalid override\n", - instance->host_no); + printk("scsi%d: bus locked solid or invalid override\n", instance->host_no); } } } -/* - * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd, - * void (*done)(Scsi_Cmnd *)) - * - * Purpose : enqueues a SCSI command - * - * Inputs : cmd - SCSI command, done - function called on completion, with - * a pointer to the command descriptor. - * - * Returns : 0 +/** + * NCR5380_queue_command - queue a command + * @cmd: SCSI command + * @done: completion handler * - * Side effects : * cmd is added to the per instance issue_queue, with minor * twiddling done to the host specific fields of cmd. If the * main coroutine is not running, it is restarted. * + * Locks: io_request lock held by caller. Called functions drop and + * retake this lock. Called functions take dma lock. */ /* Only make static if a wrapper function is used */ @@ -1158,16 +1069,14 @@ #endif int NCR5380_queue_command(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) { struct Scsi_Host *instance = cmd->host; - struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) - instance->hostdata; + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; Scsi_Cmnd *tmp; #if (NDEBUG & NDEBUG_NO_WRITE) switch (cmd->cmnd[0]) { - case WRITE_6: - case WRITE_10: - printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n", - instance->host_no); + case WRITE_6: + case WRITE_10: + printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n", instance->host_no); cmd->result = (DID_ERROR << 16); done(cmd); return 0; @@ -1175,31 +1084,22 @@ #endif /* (NDEBUG & NDEBUG_NO_WRITE) */ #ifdef NCR5380_STATS -#if 0 - if (!hostdata->connected && !hostdata->issue_queue && - !hostdata->disconnected_queue) { - hostdata->timebase = jiffies; - } -#endif -#ifdef NCR5380_STAT_LIMIT - if (cmd->request_bufflen > NCR5380_STAT_LIMIT) -#endif - switch (cmd->cmnd[0]) { - case WRITE: - case WRITE_6: - case WRITE_10: - hostdata->time_write[cmd->target] -= (jiffies - hostdata->timebase); + switch (cmd->cmnd[0]) { + case WRITE: + case WRITE_6: + case WRITE_10: + hostdata->time_write[cmd->target] -= (jiffies - hostdata->timebase); hostdata->bytes_write[cmd->target] += cmd->request_bufflen; hostdata->pendingw++; break; - case READ: - case READ_6: - case READ_10: - hostdata->time_read[cmd->target] -= (jiffies - hostdata->timebase); + case READ: + case READ_6: + case READ_10: + hostdata->time_read[cmd->target] -= (jiffies - hostdata->timebase); hostdata->bytes_read[cmd->target] += cmd->request_bufflen; hostdata->pendingr++; break; - } + } #endif /* @@ -1209,10 +1109,8 @@ cmd->host_scribble = NULL; cmd->scsi_done = done; - cmd->result = 0; - /* * Insert the cmd into the issue queue. Note that REQUEST SENSE * commands are added to the head of the queue since any command will @@ -1225,31 +1123,28 @@ cmd->host_scribble = (unsigned char *) hostdata->issue_queue; hostdata->issue_queue = cmd; } else { - for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble; - tmp = (Scsi_Cmnd *) tmp->host_scribble); + for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble; tmp = (Scsi_Cmnd *) tmp->host_scribble); LIST(cmd, tmp); tmp->host_scribble = (unsigned char *) cmd; } -#if (NDEBUG & NDEBUG_QUEUES) - printk("scsi%d : command added to %s of queue\n", instance->host_no, - (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail"); -#endif + dprintk(NDEBUG_QUEUES, ("scsi%d : command added to %s of queue\n", instance->host_no, (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail")); -/* Run the coroutine if it isn't already running. */ + /* Run the coroutine if it isn't already running. */ run_main(); return 0; } -/* - * Function : NCR5380_main (void) +/** + * NCR5380_main - NCR state machines * - * Purpose : NCR5380_main is a coroutine that runs as long as more work can + * NCR5380_main is a coroutine that runs as long as more work can * be done on the NCR5380 host adapters in a system. Both * NCR5380_queue_command() and NCR5380_intr() will try to start it * in case it is not running. * - * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should - * reenable them. This prevents reentrancy and kernel stack overflow. + * Locks; The caller must hold the io_request_lock. The lock will still be + * held on return but may be dropped while running. Called functions take + * the DMA lock. */ static void NCR5380_main(void) { @@ -1257,7 +1152,6 @@ struct Scsi_Host *instance; struct NCR5380_hostdata *hostdata; int done; - unsigned long flags; /* * We run (with interrupts disabled) until we're sure that none of @@ -1271,43 +1165,22 @@ * this should prevent any race conditions. */ - spin_unlock_irq(instance->host_lock); - - save_flags(flags); - do { - cli(); /* Freeze request queues */ + /* Lock held here */ done = 1; - for (instance = first_instance; instance && - instance->hostt == the_template; instance = instance->next) { - hostdata = (struct NCR5380_hostdata *) instance->hostdata; - cli(); -#ifdef USLEEP + for (instance = first_instance; instance && instance->hostt == the_template; instance = instance->next) { + hostdata = (struct NCR5380_hostdata *) instance->hostdata; + /* Lock held here */ if (!hostdata->connected && !hostdata->selecting) { -#else - if (!hostdata->connected) { -#endif -#if (NDEBUG & NDEBUG_MAIN) - printk("scsi%d : not connected\n", instance->host_no); -#endif + dprintk(NDEBUG_MAIN, ("scsi%d : not connected\n", instance->host_no)); /* * Search through the issue_queue for a command destined * for a target that's not busy. */ -#if (NDEBUG & NDEBUG_LISTS) - for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; tmp && (tmp != prev); prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble); - /*printk("%p ", tmp); */ - if ((tmp == prev) && tmp) - printk(" LOOP\n"); /* else printk("\n"); */ -#endif - for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, - prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) - tmp->host_scribble) { - -#if (NDEBUG & NDEBUG_LISTS) + for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble) + { if (prev != tmp) - printk("MAIN tmp=%p target=%d busy=%d lun=%d\n", tmp, tmp->target, hostdata->busy[tmp->target], tmp->lun); -#endif + dprintk(NDEBUG_LISTS, ("MAIN tmp=%p target=%d busy=%d lun=%d\n", tmp, tmp->target, hostdata->busy[tmp->target], tmp->lun)); /* When we find one, remove it from the issue queue. */ if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) { if (prev) { @@ -1319,8 +1192,6 @@ } tmp->host_scribble = NULL; - /* reenable interrupts after finding one */ - restore_flags(flags); /* * Attempt to establish an I_T_L nexus here. @@ -1328,10 +1199,7 @@ * On failure, we must add the command back to the * issue queue so we can keep trying. */ -#if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES)) - printk("scsi%d : main() : command for target %d lun %d removed from issue_queue\n", - instance->host_no, tmp->target, tmp->lun); -#endif + dprintk(NDEBUG_MAIN|NDEBUG_QUEUES, ("scsi%d : main() : command for target %d lun %d removed from issue_queue\n", instance->host_no, tmp->target, tmp->lun)); /* * A successful selection is defined as one that @@ -1343,105 +1211,81 @@ * and see if we can do an information transfer, * with failures we will restart. */ -#ifdef USLEEP - hostdata->selecting = 0; /* RvC: have to preset this - to indicate a new command is being performed */ -#endif + hostdata->selecting = 0; + /* RvC: have to preset this to indicate a new command is being performed */ if (!NCR5380_select(instance, tmp, - /* - * REQUEST SENSE commands are issued without tagged - * queueing, even on SCSI-II devices because the - * contingent allegiance condition exists for the - * entire unit. - */ - (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : - TAG_NEXT)) { + /* + * REQUEST SENSE commands are issued without tagged + * queueing, even on SCSI-II devices because the + * contingent allegiance condition exists for the + * entire unit. + */ + (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : TAG_NEXT)) { break; } else { - cli(); LIST(tmp, hostdata->issue_queue); - tmp->host_scribble = (unsigned char *) - hostdata->issue_queue; + tmp->host_scribble = (unsigned char *) hostdata->issue_queue; hostdata->issue_queue = tmp; done = 0; - restore_flags(flags); -#if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES)) - printk("scsi%d : main(): select() failed, returned to issue_queue\n", - instance->host_no); -#endif + dprintk(NDEBUG_MAIN|NDEBUG_QUEUES, ("scsi%d : main(): select() failed, returned to issue_queue\n", instance->host_no)); } + /* lock held here still */ } /* if target/lun is not busy */ } /* for */ + /* exited locked */ } /* if (!hostdata->connected) */ -#ifdef USLEEP - if (hostdata->selecting) - { - tmp = (Scsi_Cmnd *)hostdata->selecting; - if (!NCR5380_select(instance, tmp, - (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : TAG_NEXT)) - { + if (hostdata->selecting) { + tmp = (Scsi_Cmnd *) hostdata->selecting; + /* Selection will drop and retake the lock */ + if (!NCR5380_select(instance, tmp, (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : TAG_NEXT)) { /* Ok ?? */ - } - else - { + } else { /* RvC: device failed, so we wait a long time - this is needed for Mustek scanners, that - do not respond to commands immediately - after a scan */ - printk(KERN_DEBUG "scsi%d: device %d did not respond in time\n", - instance->host_no, tmp->target); - cli(); + this is needed for Mustek scanners, that + do not respond to commands immediately + after a scan */ + printk(KERN_DEBUG "scsi%d: device %d did not respond in time\n", instance->host_no, tmp->target); + //spin_lock_irq(&io_request_lock); LIST(tmp, hostdata->issue_queue); tmp->host_scribble = (unsigned char *) hostdata->issue_queue; hostdata->issue_queue = tmp; - restore_flags(flags); + //spin_unlock_irq(&io_request_lock); hostdata->time_expires = jiffies + USLEEP_WAITLONG; - NCR5380_set_timer (instance); + NCR5380_set_timer(instance); } - } /* if hostdata->selecting */ -#endif + } /* if hostdata->selecting */ if (hostdata->connected #ifdef REAL_DMA && !hostdata->dmalen #endif -#ifdef USLEEP && (!hostdata->time_expires || time_before_eq(hostdata->time_expires, jiffies)) -#endif ) { - restore_flags(flags); -#if (NDEBUG & NDEBUG_MAIN) - printk("scsi%d : main() : performing information transfer\n", - instance->host_no); -#endif + dprintk(NDEBUG_MAIN, ("scsi%d : main() : performing information transfer\n", instance->host_no)); NCR5380_information_transfer(instance); -#if (NDEBUG & NDEBUG_MAIN) - printk("scsi%d : main() : done set false\n", instance->host_no); -#endif + dprintk(NDEBUG_MAIN, ("scsi%d : main() : done set false\n", instance->host_no)); done = 0; } else break; } /* for instance */ } while (!done); - spin_lock_irq(instance->host_lock); - /* cli();*/ - main_running = 0; + /* Exit lock held */ + clear_bit(0, &main_running); } #ifndef DONT_USE_INTR #include #include -/* - * Function : void NCR5380_intr (int irq) - * - * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses +/** + * NCR5380_intr - generic NCR5380 irq handler + * + * Handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses * from the disconnected queue, and restarting NCR5380_main() * as required. * - * Inputs : int irq, irq that caused this interrupt. - * + * Locks: caller must hold the io_request lock. */ static void NCR5380_intr(int irq, void *dev_id, struct pt_regs *regs) { @@ -1449,17 +1293,12 @@ struct Scsi_Host *instance; int done; unsigned char basr; - unsigned long flags; - save_flags(flags); - cli(); -#if (NDEBUG & NDEBUG_INTR) - printk("scsi : NCR5380 irq %d triggered\n", irq); -#endif + dprintk(NDEBUG_INTR, ("scsi : NCR5380 irq %d triggered\n", irq)); + do { done = 1; - for (instance = first_instance; instance && (instance->hostt == - the_template); instance = instance->next) + for (instance = first_instance; instance && (instance->hostt == the_template); instance = instance->next) if (instance->irq == irq) { /* Look for pending interrupts */ @@ -1467,34 +1306,19 @@ basr = NCR5380_read(BUS_AND_STATUS_REG); /* XXX dispatch to appropriate routine if found and done=0 */ if (basr & BASR_IRQ) { -#if (NDEBUG & NDEBUG_INTR) - NCR5380_print(instance); -#endif - if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == - (SR_SEL | SR_IO)) { + NCR5380_dprint(NDEBUG_INTR, instance); + if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == (SR_SEL | SR_IO)) { done = 0; - restore_flags(flags); -#if (NDEBUG & NDEBUG_INTR) - printk("scsi%d : SEL interrupt\n", instance->host_no); -#endif + dprintk(NDEBUG_INTR, ("scsi%d : SEL interrupt\n", instance->host_no)); NCR5380_reselect(instance); (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); } else if (basr & BASR_PARITY_ERROR) { -#if (NDEBUG & NDEBUG_INTR) - printk("scsi%d : PARITY interrupt\n", instance->host_no); -#endif + dprintk(NDEBUG_INTR, ("scsi%d : PARITY interrupt\n", instance->host_no)); (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); } else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) { -#if (NDEBUG & NDEBUG_INTR) - printk("scsi%d : RESET interrupt\n", instance->host_no); -#endif + dprintk(NDEBUG_INTR, ("scsi%d : RESET interrupt\n", instance->host_no)); (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); } else { -/* - * XXX the rest of the interrupt conditions should *only* occur during a - * DMA transfer, which I haven't gotten around to fixing yet. - */ - #if defined(REAL_DMA) /* * We should only get PHASE MISMATCH and EOP interrupts @@ -1502,14 +1326,11 @@ * the current setting of the MODE register. */ - if ((NCR5380_read(MODE_REG) & MR_DMA) && ((basr & - BASR_END_DMA_TRANSFER) || - !(basr & BASR_PHASE_MATCH))) { + if ((NCR5380_read(MODE_REG) & MR_DMA) && ((basr & BASR_END_DMA_TRANSFER) || !(basr & BASR_PHASE_MATCH))) { int transfered; if (!hostdata->connected) - panic("scsi%d : received end of DMA interrupt with no connected cmd\n", - instance->hostno); + panic("scsi%d : received end of DMA interrupt with no connected cmd\n", instance->hostno); transfered = (hostdata->dmalen - NCR5380_dma_residual(instance)); hostdata->connected->SCp.this_residual -= transferred; @@ -1521,16 +1342,14 @@ { unsigned long timeout = jiffies + NCR_TIMEOUT; - spin_unlock_irq(instance->host_lock); - while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK - && time_before(jiffies, timeout)); - spin_lock_irq(instance->host_lock); - - if (time_after_eq(jiffies, timeout) ) - printk("scsi%d: timeout at NCR5380.c:%d\n", - host->host_no, __LINE__); + spin_unlock_irq(&io_request_lock); + while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK && time_before(jiffies, timeout)); + spin_lock_irq(&io_request_lock); + + if (time_after_eq(jiffies, timeout)) + printk("scsi%d: timeout at NCR5380.c:%d\n", host->host_no, __LINE__); } -#else /* NCR_TIMEOUT */ +#else /* NCR_TIMEOUT */ while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK); #endif @@ -1538,9 +1357,7 @@ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); } #else -#if (NDEBUG & NDEBUG_INTR) - printk("scsi : unknown interrupt, BASR 0x%X, MR 0x%X, SR 0x%x\n", basr, NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG)); -#endif + dprintk(NDEBUG_INTR, ("scsi : unknown interrupt, BASR 0x%X, MR 0x%X, SR 0x%x\n", basr, NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG))); (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); #endif } @@ -1551,42 +1368,57 @@ } while (!done); } +/** + * do_NCR5380_intr + * @irq: interrupt number + * @dev_id: device info + * @regs: registers (unused) + * + * Takes the io_request_lock and invokes the generic NCR5380 interrupt + * handler code + * + * Locks: takes and releases the io_request lock + */ static void do_NCR5380_intr(int irq, void *dev_id, struct pt_regs *regs) { unsigned long flags; - - struct Scsi_Host *dev = dev_id; - - spin_lock_irqsave(dev->host_lock, flags); + + spin_lock_irqsave(&io_request_lock, flags); NCR5380_intr(irq, dev_id, regs); - spin_unlock_irqrestore(dev->host_lock, flags); + spin_unlock_irqrestore(&io_request_lock, flags); } - #endif + +/** + * collect_stats - collect stats on a scsi command + * @hostdata: adapter + * @cmd: command being issued + * + * Update the statistical data by parsing the command in question + */ + +static void collect_stats(struct NCR5380_hostdata *hostdata, Scsi_Cmnd * cmd) +{ #ifdef NCR5380_STATS -static void collect_stats(struct NCR5380_hostdata *hostdata, Scsi_Cmnd * cmd) { -#ifdef NCR5380_STAT_LIMIT - if (cmd->request_bufflen > NCR5380_STAT_LIMIT) -#endif - switch (cmd->cmnd[0]) { - case WRITE: - case WRITE_6: - case WRITE_10: - hostdata->time_write[cmd->target] += (jiffies - hostdata->timebase); - /*hostdata->bytes_write[cmd->target] += cmd->request_bufflen; */ - hostdata->pendingw--; - break; - case READ: - case READ_6: - case READ_10: - hostdata->time_read[cmd->target] += (jiffies - hostdata->timebase); - /*hostdata->bytes_read[cmd->target] += cmd->request_bufflen; */ - hostdata->pendingr--; - break; - } -} + switch (cmd->cmnd[0]) { + case WRITE: + case WRITE_6: + case WRITE_10: + hostdata->time_write[cmd->target] += (jiffies - hostdata->timebase); + hostdata->pendingw--; + break; + case READ: + case READ_6: + case READ_10: + hostdata->time_read[cmd->target] += (jiffies - hostdata->timebase); + hostdata->pendingr--; + break; + } #endif +} + + /* * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, * int tag); @@ -1616,69 +1448,58 @@ * * If failed (no target) : cmd->scsi_done() will be called, and the * cmd->result host byte set to DID_BAD_TARGET. + * + * Locks: caller holds io_request_lock */ -static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag) { + +static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag) +{ NCR5380_local_declare(); struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; unsigned char tmp[3], phase; unsigned char *data; int len; unsigned long timeout; - unsigned long flags; -#ifdef USLEEP unsigned char value; -#endif - NCR5380_setup(instance); -#ifdef USLEEP - - if (hostdata->selecting) - { + if (hostdata->selecting) { goto part2; /* RvC: sorry prof. Dijkstra, but it keeps the rest of the code nearly the same */ } -#endif - hostdata->restart_select = 0; -#if defined (NDEBUG) && (NDEBUG & NDEBUG_ARBITRATION) - NCR5380_print(instance); - printk("scsi%d : starting arbitration, id = %d\n", instance->host_no, - instance->this_id); -#endif - save_flags(flags); - cli(); + hostdata->restart_select = 0; + + NCR5380_dprint(NDEBUG_ARBITRATION, instance); + dprintk(NDEBUG_ARBITRATION, ("scsi%d : starting arbitration, id = %d\n", instance->host_no, instance->this_id)); /* * Set the phase bits to 0, otherwise the NCR5380 won't drive the * data bus during SELECTION. */ - NCR5380_write(TARGET_COMMAND_REG, 0); - + NCR5380_write(TARGET_COMMAND_REG, 0); /* * Start arbitration. */ - NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask); - NCR5380_write(MODE_REG, MR_ARBITRATE); - - restore_flags(flags); + NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask); + NCR5380_write(MODE_REG, MR_ARBITRATE); /* Wait for arbitration logic to complete */ #if NCR_TIMEOUT { unsigned long timeout = jiffies + 2 * NCR_TIMEOUT; - spin_unlock_irq(instance->host_lock); + spin_unlock_irq(&io_request_lock); while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS) - && time_before(jiffies,timeout)); + && time_before(jiffies, timeout)); + + spin_lock_irq(&io_request_lock); - spin_lock_irq(instance->host_lock); - - if (time_after_eq(jiffies,timeout)) { + if (time_after_eq(jiffies, timeout)) { printk("scsi: arbitration timeout at %d\n", __LINE__); NCR5380_write(MODE_REG, MR_BASE); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); @@ -1689,11 +1510,7 @@ while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)); #endif -#if (NDEBUG & NDEBUG_ARBITRATION) - printk("scsi%d : arbitration complete\n", instance->host_no); -/* Avoid GCC 2.4.5 asm needs to many reloads error */ - __asm__("nop"); -#endif + dprintk(NDEBUG_ARBITRATION, ("scsi%d : arbitration complete\n", instance->host_no)); /* * The arbitration delay is 2.2us, but this is a minimum and there is @@ -1702,34 +1519,25 @@ * */ - udelay(3); + udelay(3); /* Check for lost arbitration */ - if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) || - (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) || - (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST)) { + if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) || (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) || (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST)) { NCR5380_write(MODE_REG, MR_BASE); -#if (NDEBUG & NDEBUG_ARBITRATION) - printk("scsi%d : lost arbitration, deasserting MR_ARBITRATE\n", - instance->host_no); -#endif + dprintk(NDEBUG_ARBITRATION, ("scsi%d : lost arbitration, deasserting MR_ARBITRATE\n", instance->host_no)); return -1; } NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL); if (!(hostdata->flags & FLAG_DTC3181E) && - /* RvC: DTC3181E has some trouble with this - * so we simply removed it. Seems to work with - * only Mustek scanner attached - */ - (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST)) - { + /* RvC: DTC3181E has some trouble with this + * so we simply removed it. Seems to work with + * only Mustek scanner attached + */ + (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST)) { NCR5380_write(MODE_REG, MR_BASE); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); -#if (NDEBUG & NDEBUG_ARBITRATION) - printk("scsi%d : lost arbitration, deasserting ICR_ASSERT_SEL\n", - instance->host_no); -#endif + dprintk(NDEBUG_ARBITRATION, ("scsi%d : lost arbitration, deasserting ICR_ASSERT_SEL\n", instance->host_no)); return -1; } /* @@ -1739,10 +1547,7 @@ udelay(2); -#if (NDEBUG & NDEBUG_ARBITRATION) - printk("scsi%d : won arbitration\n", instance->host_no); -#endif - + dprintk(NDEBUG_ARBITRATION, ("scsi%d : won arbitration\n", instance->host_no)); /* * Now that we have won arbitration, start Selection process, asserting @@ -1757,8 +1562,7 @@ * phase immediately after selection. */ - NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY | - ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL)); + NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY | ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL)); NCR5380_write(MODE_REG, MR_BASE); /* @@ -1774,8 +1578,7 @@ udelay(1); /* wingel -- wait two bus deskew delay >2*45ns */ /* Reset BSY */ - NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA | - ICR_ASSERT_ATN | ICR_ASSERT_SEL)); + NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL)); /* * Something weird happens when we cease to drive BSY - looks @@ -1796,9 +1599,7 @@ udelay(1); -#if (NDEBUG & NDEBUG_SELECTION) - printk("scsi%d : selecting target %d\n", instance->host_no, cmd->target); -#endif + dprintk(NDEBUG_SELECTION, ("scsi%d : selecting target %d\n", instance->host_no, cmd->target)); /* * The SCSI specification calls for a 250 ms timeout for the actual @@ -1813,40 +1614,30 @@ * and it's detecting as true. Sigh. */ -#ifdef USLEEP - hostdata->select_time = 0; /* we count the clock ticks at which we polled */ + hostdata->select_time = 0; /* we count the clock ticks at which we polled */ hostdata->selecting = cmd; part2: - /* RvC: here we enter after a sleeping period, or immediately after - execution of part 1 - we poll only once ech clock tick */ + /* RvC: here we enter after a sleeping period, or immediately after + execution of part 1 + we poll only once ech clock tick */ value = NCR5380_read(STATUS_REG) & (SR_BSY | SR_IO); - if (!value && (hostdata->select_time < 25)) - { + if (!value && (hostdata->select_time < 25)) { /* RvC: we still must wait for a device response */ - hostdata->select_time++; /* after 25 ticks the device has failed */ + hostdata->select_time++; /* after 25 ticks the device has failed */ hostdata->time_expires = jiffies + 1; NCR5380_set_timer(instance); return 0; /* RvC: we return here with hostdata->selecting set, to go to sleep */ } - hostdata->selecting = 0; /* clear this pointer, because we passed the - waiting period */ -#else - spin_unlock_irq(instance->host_lock); - while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & - (SR_BSY | SR_IO))); - spin_lock_irq(instance->host_lock); -#endif - if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == - (SR_SEL | SR_IO)) { + hostdata->selecting = 0; /* clear this pointer, because we passed the + waiting period */ + if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == (SR_SEL | SR_IO)) { NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); NCR5380_reselect(instance); - printk("scsi%d : reselection after won arbitration?\n", - instance->host_no); + printk("scsi%d : reselection after won arbitration?\n", instance->host_no); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); return -1; } @@ -1866,22 +1657,15 @@ printk("scsi%d : weirdness\n", instance->host_no); if (hostdata->restart_select) printk("\trestart select\n"); -#if (NDEBUG & NDEBUG_SELECTION) - NCR5380_print(instance); -#endif + NCR5380_dprint(NDEBUG_SELECTION, instance); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); return -1; } cmd->result = DID_BAD_TARGET << 16; -#ifdef NCR5380_STATS collect_stats(hostdata, cmd); -#endif cmd->scsi_done(cmd); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); -#if (NDEBUG & NDEBUG_SELECTION) - printk("scsi%d : target did not respond within 250ms\n", - instance->host_no); -#endif + dprintk(NDEBUG_SELECTION, ("scsi%d : target did not respond within 250ms\n", instance->host_no)); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); return 0; } @@ -1907,10 +1691,10 @@ { unsigned long timeout = jiffies + NCR_TIMEOUT; - spin_unlock_irq(instance->host_lock); + spin_unlock_irq(&io_request_lock); while (!(NCR5380_read(STATUS_REG) & SR_REQ) && time_before(jiffies, timeout)); - spin_lock_irq(instance->host_lock); - + spin_lock_irq(&io_request_lock); + if (time_after_eq(jiffies, timeout)) { printk("scsi%d: timeout at NCR5380.c:%d\n", instance->host_no, __LINE__); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); @@ -1921,47 +1705,20 @@ while (!(NCR5380_read(STATUS_REG) & SR_REQ)); #endif /* def NCR_TIMEOUT */ -#if (NDEBUG & NDEBUG_SELECTION) - printk("scsi%d : target %d selected, going into MESSAGE OUT phase.\n", - instance->host_no, cmd->target); -#endif + dprintk(NDEBUG_SELECTION, ("scsi%d : target %d selected, going into MESSAGE OUT phase.\n", instance->host_no, cmd->target)); tmp[0] = IDENTIFY(((instance->irq == IRQ_NONE) ? 0 : 1), cmd->lun); -#ifdef SCSI2 - if (cmd->device->tagged_queue && (tag != TAG_NONE)) { - tmp[1] = SIMPLE_QUEUE_TAG; - if (tag == TAG_NEXT) { - /* 0 is TAG_NONE, used to imply no tag for this command */ - if (cmd->device->current_tag == 0) - cmd->device->current_tag = 1; - - cmd->tag = cmd->device->current_tag; - cmd->device->current_tag++; - } else - cmd->tag = (unsigned char) tag; - - tmp[2] = cmd->tag; - hostdata->last_message = SIMPLE_QUEUE_TAG; - len = 3; - } else -#endif /* def SCSI2 */ - { - len = 1; - cmd->tag = 0; - } + + len = 1; + cmd->tag = 0; /* Send message(s) */ data = tmp; phase = PHASE_MSGOUT; NCR5380_transfer_pio(instance, &phase, &len, &data); -#if (NDEBUG & NDEBUG_SELECTION) - printk("scsi%d : nexus established.\n", instance->host_no); -#endif + dprintk(NDEBUG_SELECTION, ("scsi%d : nexus established.\n", instance->host_no)); /* XXX need to handle errors here */ hostdata->connected = cmd; -#ifdef SCSI2 - if (!cmd->device->tagged_queue) -#endif - hostdata->busy[cmd->target] |= (1 << cmd->lun); + hostdata->busy[cmd->target] |= (1 << cmd->lun); initialize_SCp(cmd); @@ -1994,27 +1751,22 @@ * counts, we will always do a pseudo DMA or DMA transfer. */ -static int NCR5380_transfer_pio(struct Scsi_Host *instance, - unsigned char *phase, int *count, unsigned char **data) { +static int NCR5380_transfer_pio(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data) { NCR5380_local_declare(); - register unsigned char p = *phase, tmp; - register int c = *count; - register unsigned char *d = *data; -#ifdef USLEEP + unsigned char p = *phase, tmp; + int c = *count; + unsigned char *d = *data; /* - * RvC: some administrative data to process polling time + * RvC: some administrative data to process polling time */ int break_allowed = 0; struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; -#endif NCR5380_setup(instance); -#if (NDEBUG & NDEBUG_PIO) if (!(p & SR_IO)) - printk("scsi%d : pio write %d bytes\n", instance->host_no, c); + dprintk(NDEBUG_PIO, ("scsi%d : pio write %d bytes\n", instance->host_no, c)); else - printk("scsi%d : pio read %d bytes\n", instance->host_no, c); -#endif + dprintk(NDEBUG_PIO, ("scsi%d : pio read %d bytes\n", instance->host_no, c)); /* * The NCR5380 chip will only drive the SCSI bus when the @@ -2024,56 +1776,42 @@ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p)); -#ifdef USLEEP /* RvC: don't know if this is necessary, but other SCSI I/O is short - * so breaks are not necessary there + * so breaks are not necessary there */ - if ((p == PHASE_DATAIN) || (p == PHASE_DATAOUT)) - { + if ((p == PHASE_DATAIN) || (p == PHASE_DATAOUT)) { break_allowed = 1; } -#endif - - do { /* * Wait for assertion of REQ, after which the phase bits will be * valid */ -#ifdef USLEEP /* RvC: we simply poll once, after that we stop temporarily - * and let the device buffer fill up - * if breaking is not allowed, we keep polling as long as needed + * and let the device buffer fill up + * if breaking is not allowed, we keep polling as long as needed */ - while ( !((tmp = NCR5380_read(STATUS_REG)) & SR_REQ) && - !break_allowed ); - if (!(tmp & SR_REQ)) - { + while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ) && !break_allowed); + if (!(tmp & SR_REQ)) { /* timeout condition */ hostdata->time_expires = jiffies + USLEEP_SLEEP; - NCR5380_set_timer (instance); + NCR5380_set_timer(instance); break; } -#else - while ( !((tmp = NCR5380_read(STATUS_REG)) & SR_REQ) ); -#endif -#if (NDEBUG & NDEBUG_HANDSHAKE) - printk("scsi%d : REQ detected\n", instance->host_no); -#endif + dprintk(NDEBUG_HANDSHAKE, ("scsi%d : REQ detected\n", instance->host_no)); /* Check for phase mismatch */ if ((tmp & PHASE_MASK) != p) { -#if (NDEBUG & NDEBUG_PIO) - printk("scsi%d : phase mismatch\n", instance->host_no); - NCR5380_print_phase(instance); -#endif + dprintk(NDEBUG_HANDSHAKE, ("scsi%d : phase mismatch\n", instance->host_no)); + NCR5380_dprint_phase(NDEBUG_HANDSHAKE, instance); break; } - /* Do actual transfer from SCSI bus to / from memory */ if (!(p & SR_IO)) - NCR5380_write(OUTPUT_DATA_REG, *d); + /* Do actual transfer from SCSI bus to / from memory */ + if (!(p & SR_IO)) + NCR5380_write(OUTPUT_DATA_REG, *d); else *d = NCR5380_read(CURRENT_SCSI_DATA_REG); @@ -2088,34 +1826,22 @@ if (!(p & SR_IO)) { if (!((p & SR_MSG) && c > 1)) { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_DATA); -#if (NDEBUG & NDEBUG_PIO) - NCR5380_print(instance); -#endif - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_DATA | ICR_ASSERT_ACK); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA); + NCR5380_dprint(NDEBUG_PIO, instance); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_ACK); } else { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_DATA | ICR_ASSERT_ATN); -#if (NDEBUG & NDEBUG_PIO) - NCR5380_print(instance); -#endif - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_ATN); + NCR5380_dprint(NDEBUG_PIO, instance); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK); } } else { -#if (NDEBUG & NDEBUG_PIO) - NCR5380_print(instance); -#endif + NCR5380_dprint(NDEBUG_PIO, instance); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK); } while (NCR5380_read(STATUS_REG) & SR_REQ); -#if (NDEBUG & NDEBUG_HANDSHAKE) - printk("scsi%d : req false, handshake complete\n", instance->host_no); -#endif + dprintk(NDEBUG_HANDSHAKE, ("scsi%d : req false, handshake complete\n", instance->host_no)); /* * We have several special cases to consider during REQ/ACK handshaking : @@ -2136,9 +1862,7 @@ } } while (--c); -#if (NDEBUG & NDEBUG_PIO) - printk("scsi%d : residual %d\n", instance->host_no, c); -#endif + dprintk(NDEBUG_PIO, ("scsi%d : residual %d\n", instance->host_no, c)); *count = c; *data = d; @@ -2154,36 +1878,46 @@ return -1; } +/** + * do_reset - issue a reset command + * @host: adapter to reset + * + * Issue a reset sequence to the NCR5380 and try and get the bus + * back into sane shape. + * + * Locks: caller holds io_request lock + */ + static void do_reset(struct Scsi_Host *host) { - unsigned long flags; - NCR5380_local_declare(); - NCR5380_setup(host); + NCR5380_local_declare(); + NCR5380_setup(host); + + NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(NCR5380_read(STATUS_REG) & PHASE_MASK)); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST); + udelay(25); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); +} + +/* + * Function : do_abort (Scsi_Host *host) + * + * Purpose : abort the currently established nexus. Should only be + * called from a routine which can drop into a + * + * Returns : 0 on success, -1 on failure. + * + * Locks: io_request lock held by caller + */ - save_flags(flags); - cli(); - NCR5380_write(TARGET_COMMAND_REG, - PHASE_SR_TO_TCR(NCR5380_read(STATUS_REG) & PHASE_MASK)); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST); - udelay(25); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - restore_flags(flags); -} /* - - * Function : do_abort (Scsi_Host *host) - * - * Purpose : abort the currently established nexus. Should only be - * called from a routine which can drop into a - * - * Returns : 0 on success, -1 on failure. - */ static int do_abort(struct Scsi_Host *host) { +static int do_abort(struct Scsi_Host *host) { NCR5380_local_declare(); unsigned char tmp, *msgptr, phase; int len; - NCR5380_setup(host); + NCR5380_setup(host); /* Request message out phase */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); /* * Wait for the target to indicate a valid phase by asserting @@ -2197,11 +1931,10 @@ while (!(tmp = NCR5380_read(STATUS_REG)) & SR_REQ); - NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp)); + NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp)); if ((tmp & PHASE_MASK) != PHASE_MSGOUT) { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | - ICR_ASSERT_ACK); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | ICR_ASSERT_ACK); while (NCR5380_read(STATUS_REG) & SR_REQ); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); } @@ -2237,54 +1970,46 @@ * * Also, *phase, *count, *data are modified in place. * + * Locks: io_request lock held by caller */ -static int NCR5380_transfer_dma(struct Scsi_Host *instance, - unsigned char *phase, int *count, unsigned char **data) { +static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data) { NCR5380_local_declare(); register int c = *count; register unsigned char p = *phase; register unsigned char *d = *data; unsigned char tmp; -#if defined(PSEUDO_DMA) && !defined(UNSAFE) - unsigned long flags; -#endif int foo; #if defined(REAL_DMA_POLL) int cnt, toPIO; unsigned char saved_data = 0, overrun = 0, residue; #endif - struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) - instance->hostdata; + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; - NCR5380_setup(instance); + NCR5380_setup(instance); if ((tmp = (NCR5380_read(STATUS_REG) & PHASE_MASK)) != p) { *phase = tmp; return -1; } #if defined(REAL_DMA) || defined(REAL_DMA_POLL) -#ifdef READ_OVERRUNS - if (p & SR_IO) { c -= 2; } -#endif -#if (NDEBUG & NDEBUG_DMA) -printk("scsi%d : initializing DMA channel %d for %s, %d bytes %s %0x\n", - instance->host_no, instance->dma_channel, (p & SR_IO) ? "reading" : - "writing", c, (p & SR_IO) ? "to" : "from", (unsigned) d); +#ifdef READ_OVERRUNS + if (p & SR_IO) { + c -= 2; + } #endif -hostdata->dma_len = (p & SR_IO) ? -NCR5380_dma_read_setup(instance, d, c) : -NCR5380_dma_write_setup(instance, d, c); + dprintk(NDEBUG_DMA, ("scsi%d : initializing DMA channel %d for %s, %d bytes %s %0x\n", instance->host_no, instance->dma_channel, (p & SR_IO) ? "reading" : "writing", c, (p & SR_IO) ? "to" : "from", (unsigned) d)); + hostdata->dma_len = (p & SR_IO) ? NCR5380_dma_read_setup(instance, d, c) : NCR5380_dma_write_setup(instance, d, c); #endif -NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p)); + NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p)); #ifdef REAL_DMA -NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY); + NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY); #elif defined(REAL_DMA_POLL) -NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE); + NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE); #else /* * Note : on my sample board, watch-dog timeouts occurred when interrupts @@ -2292,58 +2017,38 @@ * before the setting of DMA mode to after transfer of the last byte. */ -#if defined(PSEUDO_DMA) && !defined(UNSAFE) -save_flags(flags); -cli(); +#if defined(PSEUDO_DMA) && defined(UNSAFE) + spin_unlock_irq(&io_request_lock); #endif /* KLL May need eop and parity in 53c400 */ -if (hostdata->flags & FLAG_NCR53C400) - NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_PAR_CHECK - | MR_ENABLE_PAR_INTR | MR_ENABLE_EOP_INTR | MR_DMA_MODE - | MR_MONITOR_BSY); -else - NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE); + if (hostdata->flags & FLAG_NCR53C400) + NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_PAR_CHECK | MR_ENABLE_PAR_INTR | MR_ENABLE_EOP_INTR | MR_DMA_MODE | MR_MONITOR_BSY); + else + NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE); #endif /* def REAL_DMA */ -#if (NDEBUG & NDEBUG_DMA) & 0 -printk("scsi%d : mode reg = 0x%X\n", instance->host_no, NCR5380_read(MODE_REG)); -#endif + dprintk(NDEBUG_DMA, ("scsi%d : mode reg = 0x%X\n", instance->host_no, NCR5380_read(MODE_REG))); -/* - * FOO stuff. For some UNAPPARENT reason, I'm getting - * watchdog timers fired on bootup for NO APPARENT REASON, meaning it's - * probably a timing problem. - * - * Since this is the only place I have back-to-back writes, perhaps this - * is the problem? - */ + /* + * On the PAS16 at least I/O recovery delays are not needed here. + * Everyone else seems to want them. + */ -if (p & SR_IO) -{ -#ifndef FOO -udelay(1); -#endif -NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0); -} else { -#ifndef FOO - udelay(1); -#endif - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA); -#ifndef FOO - udelay(1); -#endif - NCR5380_write(START_DMA_SEND_REG, 0); -#ifndef FOO - udelay(1); -#endif -} + if (p & SR_IO) { + io_recovery_delay(1); + NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0); + } else { + io_recovery_delay(1); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA); + io_recovery_delay(1); + NCR5380_write(START_DMA_SEND_REG, 0); + io_recovery_delay(1); + } #if defined(REAL_DMA_POLL) -do { - tmp = NCR5380_read(BUS_AND_STATUS_REG); -} while ((tmp & BASR_PHASE_MATCH) && !(tmp & (BASR_BUSY_ERROR | - - BASR_END_DMA_TRANSFER))); + do { + tmp = NCR5380_read(BUS_AND_STATUS_REG); + } while ((tmp & BASR_PHASE_MATCH) && !(tmp & (BASR_BUSY_ERROR | BASR_END_DMA_TRANSFER))); /* At this point, either we've completed DMA, or we have a phase mismatch, @@ -2381,172 +2086,128 @@ request. */ -if (p & SR_IO) { + if (p & SR_IO) { #ifdef READ_OVERRUNS - udelay(10); - if (((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH | BASR_ACK)) == - (BASR_PHASE_MATCH | BASR_ACK))) { - saved_data = NCR5380_read(INPUT_DATA_REGISTER); - overrun = 1; - } + udelay(10); + if (((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH | BASR_ACK)) == (BASR_PHASE_MATCH | BASR_ACK))) { + saved_data = NCR5380_read(INPUT_DATA_REGISTER); + overrun = 1; + } #endif -} else { - int limit = 100; - while (((tmp = NCR5380_read(BUS_AND_STATUS_REG)) & BASR_ACK) || - (NCR5380_read(STATUS_REG) & SR_REQ)) { - if (!(tmp & BASR_PHASE_MATCH)) - break; - if (--limit < 0) - break; + } else { + int limit = 100; + while (((tmp = NCR5380_read(BUS_AND_STATUS_REG)) & BASR_ACK) || (NCR5380_read(STATUS_REG) & SR_REQ)) { + if (!(tmp & BASR_PHASE_MATCH)) + break; + if (--limit < 0) + break; + } } -} + dprintk(NDEBUG_DMA, ("scsi%d : polled DMA transfer complete, basr 0x%X, sr 0x%X\n", instance->host_no, tmp, NCR5380_read(STATUS_REG))); -#if (NDEBUG & NDEBUG_DMA) -printk("scsi%d : polled DMA transfer complete, basr 0x%X, sr 0x%X\n", - instance->host_no, tmp, NCR5380_read(STATUS_REG)); -#endif + NCR5380_write(MODE_REG, MR_BASE); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); -NCR5380_write(MODE_REG, MR_BASE); -NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - -residue = NCR5380_dma_residual(instance); -c -= residue; -*count -= c; -*data += c; -*phase = NCR5380_read(STATUS_REG) & PHASE_MASK; + residue = NCR5380_dma_residual(instance); + c -= residue; + *count -= c; + *data += c; + *phase = NCR5380_read(STATUS_REG) & PHASE_MASK; #ifdef READ_OVERRUNS -if (*phase == p && (p & SR_IO) && residue == 0) -{ -if (overrun) { -#if (NDEBUG & NDEBUG_DMA) - printk("Got an input overrun, using saved byte\n"); -#endif - **data = saved_data; - *data += 1; - *count -= 1; - cnt = toPIO = 1; -} else { - printk("No overrun??\n"); - cnt = toPIO = 2; -} -#if (NDEBUG & NDEBUG_DMA) -printk("Doing %d-byte PIO to 0x%X\n", cnt, *data); -#endif -NCR5380_transfer_pio(instance, phase, &cnt, data); -*count -= toPIO - cnt; -} + if (*phase == p && (p & SR_IO) && residue == 0) { + if (overrun) { + dprintk(NDEBUG_DMA, ("Got an input overrun, using saved byte\n")); + **data = saved_data; + *data += 1; + *count -= 1; + cnt = toPIO = 1; + } else { + printk("No overrun??\n"); + cnt = toPIO = 2; + } + dprintk(NDEBUG_DMA, ("Doing %d-byte PIO to 0x%X\n", cnt, *data)); + NCR5380_transfer_pio(instance, phase, &cnt, data); + *count -= toPIO - cnt; + } #endif -#if (NDEBUG & NDEBUG_DMA) -printk("Return with data ptr = 0x%X, count %d, last 0x%X, next 0x%X\n", - *data, *count, *(*data + *count - 1), *(*data + *count)); -#endif -return 0; + dprintk(NDEBUG_DMA, ("Return with data ptr = 0x%X, count %d, last 0x%X, next 0x%X\n", *data, *count, *(*data + *count - 1), *(*data + *count))); + return 0; #elif defined(REAL_DMA) -return 0; + return 0; #else /* defined(REAL_DMA_POLL) */ -if (p & SR_IO) { + if (p & SR_IO) { #ifdef DMA_WORKS_RIGHT - foo = NCR5380_pread(instance, d, c); + foo = NCR5380_pread(instance, d, c); #else - int diff = 1; - if (hostdata->flags & FLAG_NCR53C400) { - diff = 0; - } - if (!(foo = NCR5380_pread(instance, d, c - diff))) { - /* - * We can't disable DMA mode after successfully transferring - * what we plan to be the last byte, since that would open up - * a race condition where if the target asserted REQ before - * we got the DMA mode reset, the NCR5380 would have latched - * an additional byte into the INPUT DATA register and we'd - * have dropped it. - * - * The workaround was to transfer one fewer bytes than we - * intended to with the pseudo-DMA read function, wait for - * the chip to latch the last byte, read it, and then disable - * pseudo-DMA mode. - * - * After REQ is asserted, the NCR5380 asserts DRQ and ACK. - * REQ is deasserted when ACK is asserted, and not reasserted - * until ACK goes false. Since the NCR5380 won't lower ACK - * until DACK is asserted, which won't happen unless we twiddle - * the DMA port or we take the NCR5380 out of DMA mode, we - * can guarantee that we won't handshake another extra - * byte. - */ + int diff = 1; + if (hostdata->flags & FLAG_NCR53C400) { + diff = 0; + } + if (!(foo = NCR5380_pread(instance, d, c - diff))) { + /* + * We can't disable DMA mode after successfully transferring + * what we plan to be the last byte, since that would open up + * a race condition where if the target asserted REQ before + * we got the DMA mode reset, the NCR5380 would have latched + * an additional byte into the INPUT DATA register and we'd + * have dropped it. + * + * The workaround was to transfer one fewer bytes than we + * intended to with the pseudo-DMA read function, wait for + * the chip to latch the last byte, read it, and then disable + * pseudo-DMA mode. + * + * After REQ is asserted, the NCR5380 asserts DRQ and ACK. + * REQ is deasserted when ACK is asserted, and not reasserted + * until ACK goes false. Since the NCR5380 won't lower ACK + * until DACK is asserted, which won't happen unless we twiddle + * the DMA port or we take the NCR5380 out of DMA mode, we + * can guarantee that we won't handshake another extra + * byte. + */ - if (!(hostdata->flags & FLAG_NCR53C400)) { - while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)); - /* Wait for clean handshake */ - while (NCR5380_read(STATUS_REG) & SR_REQ); - d[c - 1] = NCR5380_read(INPUT_DATA_REG); + if (!(hostdata->flags & FLAG_NCR53C400)) { + while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)); + /* Wait for clean handshake */ + while (NCR5380_read(STATUS_REG) & SR_REQ); + d[c - 1] = NCR5380_read(INPUT_DATA_REG); + } } - } #endif -} else { + } else { #ifdef DMA_WORKS_RIGHT - foo = NCR5380_pwrite(instance, d, c); -#else - int timeout; -#if (NDEBUG & NDEBUG_C400_PWRITE) - printk("About to pwrite %d bytes\n", c); -#endif - if (!(foo = NCR5380_pwrite(instance, d, c))) { - /* - * Wait for the last byte to be sent. If REQ is being asserted for - * the byte we're interested, we'll ACK it and it will go false. - */ - if (!(hostdata->flags & FLAG_HAS_LAST_BYTE_SENT)) { - timeout = 20000; -#if 1 -#if 1 - while (!(NCR5380_read(BUS_AND_STATUS_REG) & - BASR_DRQ) && (NCR5380_read(BUS_AND_STATUS_REG) & - BASR_PHASE_MATCH)); + foo = NCR5380_pwrite(instance, d, c); #else - if (NCR5380_read(STATUS_REG) & SR_REQ) { - for (; timeout && - !(NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK); - --timeout); - for (; timeout && (NCR5380_read(STATUS_REG) & SR_REQ); - --timeout); - } -#endif - - -#if (NDEBUG & NDEBUG_LAST_BYTE_SENT) - if (!timeout) - printk("scsi%d : timed out on last byte\n", - instance->host_no); -#endif - - - if (hostdata->flags & FLAG_CHECK_LAST_BYTE_SENT) { - hostdata->flags &= ~FLAG_CHECK_LAST_BYTE_SENT; - if (NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT) { - hostdata->flags |= FLAG_HAS_LAST_BYTE_SENT; -#if (NDEBUG & NDEBUG_LAST_BYTE_SENT) - printk("scsi%d : last bit sent works\n", - instance->host_no); -#endif + int timeout; + dprintk(NDEBUG_C400_PWRITE, ("About to pwrite %d bytes\n", c)); + if (!(foo = NCR5380_pwrite(instance, d, c))) { + /* + * Wait for the last byte to be sent. If REQ is being asserted for + * the byte we're interested, we'll ACK it and it will go false. + */ + if (!(hostdata->flags & FLAG_HAS_LAST_BYTE_SENT)) { + timeout = 20000; + while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) && (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)); + + if (!timeout) + dprintk(NDEBUG_LAST_BYTE_SENT, ("scsi%d : timed out on last byte\n", instance->host_no)); + + if (hostdata->flags & FLAG_CHECK_LAST_BYTE_SENT) { + hostdata->flags &= ~FLAG_CHECK_LAST_BYTE_SENT; + if (NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT) { + hostdata->flags |= FLAG_HAS_LAST_BYTE_SENT; + dprintk(NDEBUG_LAST_WRITE_SENT, ("scsi%d : last bit sent works\n", instance->host_no)); + } } + } else { + dprintk(NDEBUG_C400_PWRITE, ("Waiting for LASTBYTE\n")); + while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT)); + dprintk(NDEBUG_C400_PWRITE, ("Got LASTBYTE\n")); } - } else { -#if (NDEBUG & NDEBUG_C400_PWRITE) - printk("Waiting for LASTBYTE\n"); -#endif - while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT)); -#if (NDEBUG & NDEBUG_C400_PWRITE) - printk("Got LASTBYTE\n"); -#endif - } -#else - udelay(5); -#endif } #endif } @@ -2554,13 +2215,9 @@ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); if ((!(p & SR_IO)) && (hostdata->flags & FLAG_NCR53C400)) { -#if (NDEBUG & NDEBUG_C400_PWRITE) - printk("53C400w: Checking for IRQ\n"); -#endif + dprintk(NDEBUG_C400_PWRITE, ("53C400w: Checking for IRQ\n")); if (NCR5380_read(BUS_AND_STATUS_REG) & BASR_IRQ) { -#if (NDEBUG & NDEBUG_C400_PWRITE) - printk("53C400w: got it, reading reset interrupt reg\n"); -#endif + dprintk(NDEBUG_C400_PWRITE, ("53C400w: got it, reading reset interrupt reg\n")); NCR5380_read(RESET_PARITY_INTERRUPT_REG); } else { printk("53C400w: IRQ NOT THERE!\n"); @@ -2569,11 +2226,8 @@ *data = d + c; *count = 0; *phase = NCR5380_read(STATUS_REG) & PHASE_MASK; -#if 0 - NCR5380_print_phase(instance); -#endif -#if defined(PSEUDO_DMA) && !defined(UNSAFE) - restore_flags(flags); +#if defined(PSEUDO_DMA) && defined(UNSAFE) + spin_lock_irq(&io_request_lock); #endif /* defined(REAL_DMA_POLL) */ return foo; #endif /* def REAL_DMA */ @@ -2595,12 +2249,13 @@ * * XXX Note : we need to watch for bus free or a reset condition here * to recover from an unexpected bus free condition. + * + * Locks: io_request_lock held by caller */ static void NCR5380_information_transfer(struct Scsi_Host *instance) { NCR5380_local_declare(); - struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) - instance->hostdata; + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)instance->hostdata; unsigned char msgout = NOP; int sink = 0; int len; @@ -2610,10 +2265,8 @@ unsigned char *data; unsigned char phase, tmp, extended_msg[10], old_phase = 0xff; Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected; -#ifdef USLEEP /* RvC: we need to set the end of the polling time */ unsigned long poll_time = jiffies + USLEEP_POLL; -#endif NCR5380_setup(instance); @@ -2624,27 +2277,22 @@ phase = (tmp & PHASE_MASK); if (phase != old_phase) { old_phase = phase; -#if (NDEBUG & NDEBUG_INFORMATION) - NCR5380_print_phase(instance); -#endif + NCR5380_dprint_phase(NDEBUG_INFORMATION, instance); } if (sink && (phase != PHASE_MSGOUT)) { NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp)); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | - ICR_ASSERT_ACK); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | ICR_ASSERT_ACK); while (NCR5380_read(STATUS_REG) & SR_REQ); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_ATN); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); sink = 0; continue; } switch (phase) { - case PHASE_DATAIN: - case PHASE_DATAOUT: + case PHASE_DATAIN: + case PHASE_DATAOUT: #if (NDEBUG & NDEBUG_NO_DATAOUT) - printk("scsi%d : NDEBUG_NO_DATAOUT set, attempted DATAOUT aborted\n", - instance->host_no); + printk("scsi%d : NDEBUG_NO_DATAOUT set, attempted DATAOUT aborted\n", instance->host_no); sink = 1; do_abort(instance); cmd->result = DID_ERROR << 16; @@ -2661,11 +2309,7 @@ --cmd->SCp.buffers_residual; cmd->SCp.this_residual = cmd->SCp.buffer->length; cmd->SCp.ptr = cmd->SCp.buffer->address; -#if (NDEBUG & NDEBUG_INFORMATION) - printk("scsi%d : %d bytes and %d buffers left\n", - instance->host_no, cmd->SCp.this_residual, - cmd->SCp.buffers_residual); -#endif + dprintk(NDEBUG_INFORMATION, ("scsi%d : %d bytes and %d buffers left\n", instance->host_no, cmd->SCp.this_residual, cmd->SCp.buffers_residual)); } /* * The preferred transfer method is going to be @@ -2686,9 +2330,7 @@ * We supplement these 2 if's with the flag. */ #ifdef NCR5380_dma_xfer_len - if (!cmd->device->borken && - !(hostdata->flags & FLAG_NO_PSEUDO_DMA) && - (transfersize = NCR5380_dma_xfer_len(instance, cmd)) != 0) { + if (!cmd->device->borken && !(hostdata->flags & FLAG_NO_PSEUDO_DMA) && (transfersize = NCR5380_dma_xfer_len(instance, cmd)) != 0) { #else transfersize = cmd->transfersize; @@ -2697,27 +2339,21 @@ transfersize = 512; #endif /* LIMIT_TRANSFERSIZE */ - if (!cmd->device->borken && transfersize && - !(hostdata->flags & FLAG_NO_PSEUDO_DMA) && - cmd->SCp.this_residual && !(cmd->SCp.this_residual % - transfersize)) { + if (!cmd->device->borken && transfersize && !(hostdata->flags & FLAG_NO_PSEUDO_DMA) && cmd->SCp.this_residual && !(cmd->SCp.this_residual % transfersize)) { /* Limit transfers to 32K, for xx400 & xx406 * pseudoDMA that transfers in 128 bytes blocks. */ if (transfersize > 32 * 1024) transfersize = 32 * 1024; #endif len = transfersize; - if (NCR5380_transfer_dma(instance, &phase, - &len, (unsigned char **) &cmd->SCp.ptr)) { + if (NCR5380_transfer_dma(instance, &phase, &len, (unsigned char **) &cmd->SCp.ptr)) { /* * If the watchdog timer fires, all future accesses to this * device will use the polled-IO. */ - printk("scsi%d : switching target %d lun %d to slow handshake\n", - instance->host_no, cmd->target, cmd->lun); + printk("scsi%d : switching target %d lun %d to slow handshake\n", instance->host_no, cmd->target, cmd->lun); cmd->device->borken = 1; - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_ATN); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); sink = 1; do_abort(instance); cmd->result = DID_ERROR << 16; @@ -2727,12 +2363,11 @@ cmd->SCp.this_residual -= transfersize - len; } else #endif /* defined(PSEUDO_DMA) || defined(REAL_DMA_POLL) */ - NCR5380_transfer_pio(instance, &phase, - (int *) &cmd->SCp.this_residual, (unsigned char **) - &cmd->SCp.ptr); + NCR5380_transfer_pio(instance, &phase, (int *) &cmd->SCp.this_residual, (unsigned char **) + &cmd->SCp.ptr); break; - case PHASE_MSGIN: - len = 1; + case PHASE_MSGIN: + len = 1; data = &tmp; NCR5380_transfer_pio(instance, &phase, &len, &data); cmd->SCp.Message = tmp; @@ -2749,24 +2384,18 @@ * next_link, done() is called as with unlinked commands. */ #ifdef LINKED - case LINKED_CMD_COMPLETE: - case LINKED_FLG_CMD_COMPLETE: + case LINKED_CMD_COMPLETE: + case LINKED_FLG_CMD_COMPLETE: /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - -#if (NDEBUG & NDEBUG_LINKED) - printk("scsi%d : target %d lun %d linked command complete.\n", - instance->host_no, cmd->target, cmd->lun); -#endif + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + dprintk(NDEBUG_LINKED, ("scsi%d : target %d lun %d linked command complete.\n", instance->host_no, cmd->target, cmd->lun)); /* * Sanity check : A linked command should only terminate with * one of these messages if there are more linked commands * available. */ - if (!cmd->next_link) { - printk("scsi%d : target %d lun %d linked command complete, no next_link\n" - instance->host_no, cmd->target, cmd->lun); + printk("scsi%d : target %d lun %d linked command complete, no next_link\n" instance->host_no, cmd->target, cmd->lun); sink = 1; do_abort(instance); return; @@ -2775,27 +2404,19 @@ /* The next command is still part of this process */ cmd->next_link->tag = cmd->tag; cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); -#if (NDEBUG & NDEBUG_LINKED) - printk("scsi%d : target %d lun %d linked request done, calling scsi_done().\n", - instance->host_no, cmd->target, cmd->lun); -#endif -#ifdef NCR5380_STATS + dprintk(NDEBUG_LINKED, ("scsi%d : target %d lun %d linked request done, calling scsi_done().\n", instance->host_no, cmd->target, cmd->lun)); collect_stats(hostdata, cmd); -#endif cmd->scsi_done(cmd); cmd = hostdata->connected; break; #endif /* def LINKED */ - case ABORT: - case COMMAND_COMPLETE: + case ABORT: + case COMMAND_COMPLETE: /* Accept message by clearing ACK */ - sink = 1; + sink = 1; NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); hostdata->connected = NULL; -#if (NDEBUG & NDEBUG_QUEUES) - printk("scsi%d : command for target %d, lun %d completed\n", - instance->host_no, cmd->target, cmd->lun); -#endif + dprintk(NDEBUG_QUEUES, ("scsi%d : command for target %d, lun %d completed\n", instance->host_no, cmd->target, cmd->lun)); hostdata->busy[cmd->target] &= ~(1 << cmd->lun); /* @@ -2820,13 +2441,8 @@ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); #ifdef AUTOSENSE - if ((cmd->cmnd[0] != REQUEST_SENSE) && - (cmd->SCp.Status == CHECK_CONDITION)) { - unsigned long flags; -#if (NDEBUG & NDEBUG_AUTOSENSE) - printk("scsi%d : performing request sense\n", - instance->host_no); -#endif + if ((cmd->cmnd[0] != REQUEST_SENSE) && (cmd->SCp.Status == CHECK_CONDITION)) { + dprintk(NDEBUG_AUTOSENSE, ("scsi%d : performing request sense\n", instance->host_no)); cmd->cmnd[0] = REQUEST_SENSE; cmd->cmnd[1] &= 0xe0; cmd->cmnd[2] = 0; @@ -2839,21 +2455,14 @@ cmd->SCp.ptr = (char *) cmd->sense_buffer; cmd->SCp.this_residual = sizeof(cmd->sense_buffer); - save_flags(flags); - cli(); LIST(cmd, hostdata->issue_queue); cmd->host_scribble = (unsigned char *) hostdata->issue_queue; hostdata->issue_queue = (Scsi_Cmnd *) cmd; - restore_flags(flags); -#if (NDEBUG & NDEBUG_QUEUES) - printk("scsi%d : REQUEST SENSE added to head of issue queue\n", instance->host_no); -#endif + dprintk(NDEBUG_QUEUES, ("scsi%d : REQUEST SENSE added to head of issue queue\n", instance->host_no)); } else { #endif /* def AUTOSENSE */ -#ifdef NCR5380_STATS collect_stats(hostdata, cmd); -#endif cmd->scsi_done(cmd); } @@ -2867,37 +2476,29 @@ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected) barrier(); return; - case MESSAGE_REJECT: + case MESSAGE_REJECT: /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); switch (hostdata->last_message) { - case HEAD_OF_QUEUE_TAG: - case ORDERED_QUEUE_TAG: - case SIMPLE_QUEUE_TAG: - cmd->device->tagged_queue = 0; + case HEAD_OF_QUEUE_TAG: + case ORDERED_QUEUE_TAG: + case SIMPLE_QUEUE_TAG: + cmd->device->tagged_queue = 0; hostdata->busy[cmd->target] |= (1 << cmd->lun); break; - default: - break; + default: + break; } - case DISCONNECT: { - unsigned long flags; + case DISCONNECT:{ /* Accept message by clearing ACK */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); cmd->device->disconnect = 1; - save_flags(flags); - cli(); LIST(cmd, hostdata->disconnected_queue); cmd->host_scribble = (unsigned char *) hostdata->disconnected_queue; hostdata->connected = NULL; hostdata->disconnected_queue = cmd; - restore_flags(flags); -#if (NDEBUG & NDEBUG_QUEUES) - printk("scsi%d : command for target %d lun %d was moved from connected to" - " the disconnected_queue\n", instance->host_no, - cmd->target, cmd->lun); -#endif + dprintk(NDEBUG_QUEUES, ("scsi%d : command for target %d lun %d was moved from connected to" " the disconnected_queue\n", instance->host_no, cmd->target, cmd->lun)); /* * Restore phase bits to 0 so an interrupted selection, * arbitration can resume. @@ -2909,9 +2510,6 @@ /* Wait for bus free to avoid nasty timeouts */ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected) barrier(); -#if 0 - NCR5380_print_status(instance); -#endif return; } /* @@ -2924,12 +2522,12 @@ * disconnecting, and we have to break spec to remain * compatible. */ - case SAVE_POINTERS: - case RESTORE_POINTERS: + case SAVE_POINTERS: + case RESTORE_POINTERS: /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); break; - case EXTENDED_MESSAGE: + case EXTENDED_MESSAGE: /* * Extended messages are sent in the following format : * Byte @@ -2942,28 +2540,19 @@ * Start the extended message buffer with the EXTENDED_MESSAGE * byte, since print_msg() wants the whole thing. */ - extended_msg[0] = EXTENDED_MESSAGE; + extended_msg[0] = EXTENDED_MESSAGE; /* Accept first byte by clearing ACK */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - -#if (NDEBUG & NDEBUG_EXTENDED) - printk("scsi%d : receiving extended message\n", - instance->host_no); -#endif + dprintk(NDEBUG_EXTENDED, ("scsi%d : receiving extended message\n", instance->host_no)); len = 2; data = extended_msg + 1; phase = PHASE_MSGIN; NCR5380_transfer_pio(instance, &phase, &len, &data); -#if (NDEBUG & NDEBUG_EXTENDED) - printk("scsi%d : length=%d, code=0x%02x\n", - instance->host_no, (int) extended_msg[1], - (int) extended_msg[2]); -#endif + dprintk(NDEBUG_EXTENDED, ("scsi%d : length=%d, code=0x%02x\n", instance->host_no, (int) extended_msg[1], (int) extended_msg[2])); - if (!len && extended_msg[1] <= - (sizeof(extended_msg) - 1)) { + if (!len && extended_msg[1] <= (sizeof(extended_msg) - 1)) { /* Accept third byte by clearing ACK */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); len = extended_msg[1] - 1; @@ -2971,26 +2560,20 @@ phase = PHASE_MSGIN; NCR5380_transfer_pio(instance, &phase, &len, &data); - -#if (NDEBUG & NDEBUG_EXTENDED) - printk("scsi%d : message received, residual %d\n", - instance->host_no, len); -#endif + dprintk(NDEBUG_EXTENDED, ("scsi%d : message received, residual %d\n", instance->host_no, len)); switch (extended_msg[2]) { - case EXTENDED_SDTR: - case EXTENDED_WDTR: - case EXTENDED_MODIFY_DATA_POINTER: - case EXTENDED_EXTENDED_IDENTIFY: - tmp = 0; + case EXTENDED_SDTR: + case EXTENDED_WDTR: + case EXTENDED_MODIFY_DATA_POINTER: + case EXTENDED_EXTENDED_IDENTIFY: + tmp = 0; } } else if (len) { - printk("scsi%d: error receiving extended message\n", - instance->host_no); + printk("scsi%d: error receiving extended message\n", instance->host_no); tmp = 0; } else { - printk("scsi%d: extended message code %02x length %d is too long\n", - instance->host_no, extended_msg[2], extended_msg[1]); + printk("scsi%d: extended message code %02x length %d is too long\n", instance->host_no, extended_msg[2], extended_msg[1]); tmp = 0; } /* Fall through to reject message */ @@ -2999,26 +2582,23 @@ * If we get something weird that we aren't expecting, * reject it. */ - default: - if (!tmp) { + default: + if (!tmp) { printk("scsi%d: rejecting message ", instance->host_no); print_msg(extended_msg); printk("\n"); } else if (tmp != EXTENDED_MESSAGE) - printk("scsi%d: rejecting unknown message %02x from target %d, lun %d\n", - instance->host_no, tmp, cmd->target, cmd->lun); + printk("scsi%d: rejecting unknown message %02x from target %d, lun %d\n", instance->host_no, tmp, cmd->target, cmd->lun); else - printk("scsi%d: rejecting unknown extended message code %02x, length %d from target %d, lun %d\n", - instance->host_no, extended_msg[1], extended_msg[0], cmd->target, cmd->lun); + printk("scsi%d: rejecting unknown extended message code %02x, length %d from target %d, lun %d\n", instance->host_no, extended_msg[1], extended_msg[0], cmd->target, cmd->lun); msgout = MESSAGE_REJECT; - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_ATN); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); break; } /* switch (tmp) */ break; - case PHASE_MSGOUT: - len = 1; + case PHASE_MSGOUT: + len = 1; data = &msgout; hostdata->last_message = msgout; NCR5380_transfer_pio(instance, &phase, &len, &data); @@ -3026,69 +2606,50 @@ hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->connected = NULL; cmd->result = DID_ERROR << 16; -#ifdef NCR5380_STATS collect_stats(hostdata, cmd); -#endif cmd->scsi_done(cmd); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); return; } msgout = NOP; break; - case PHASE_CMDOUT: - len = cmd->cmd_len; + case PHASE_CMDOUT: + len = cmd->cmd_len; data = cmd->cmnd; /* * XXX for performance reasons, on machines with a * PSEUDO-DMA architecture we should probably * use the dma transfer function. */ - NCR5380_transfer_pio(instance, &phase, &len, - &data); -#ifdef USLEEP - if (!cmd->device->disconnect && - should_disconnect(cmd->cmnd[0])) - { + NCR5380_transfer_pio(instance, &phase, &len, &data); + if (!cmd->device->disconnect && should_disconnect(cmd->cmnd[0])) { hostdata->time_expires = jiffies + USLEEP_SLEEP; -#if (NDEBUG & NDEBUG_USLEEP) - printk("scsi%d : issued command, sleeping until %ul\n", instance->host_no, - hostdata->time_expires); -#endif + dprintk(NDEBUG_USLEEP, ("scsi%d : issued command, sleeping until %ul\n", instance->host_no, hostdata->time_expires)); NCR5380_set_timer(instance); return; } -#endif /* def USLEEP */ break; - case PHASE_STATIN: - len = 1; + case PHASE_STATIN: + len = 1; data = &tmp; NCR5380_transfer_pio(instance, &phase, &len, &data); cmd->SCp.Status = tmp; break; - default: - printk("scsi%d : unknown phase\n", instance->host_no); -#ifdef NDEBUG - NCR5380_print(instance); -#endif + default: + printk("scsi%d : unknown phase\n", instance->host_no); + NCR5380_dprint(NDEBUG_ALL, instance); } /* switch(phase) */ } /* if (tmp * SR_REQ) */ -#ifdef USLEEP - else - { + else { /* RvC: go to sleep if polling time expired */ - if (!cmd->device->disconnect && time_after_eq(jiffies, poll_time)) - { + if (!cmd->device->disconnect && time_after_eq(jiffies, poll_time)) { hostdata->time_expires = jiffies + USLEEP_SLEEP; -#if (NDEBUG & NDEBUG_USLEEP) - printk("scsi%d : poll timed out, sleeping until %ul\n", instance->host_no, - hostdata->time_expires); -#endif + dprintk(NDEBUG_USLEEP, ("scsi%d : poll timed out, sleeping until %ul\n", instance->host_no, hostdata->time_expires)); NCR5380_set_timer(instance); return; } } -#endif } /* while (1) */ } @@ -3101,9 +2662,9 @@ * * Inputs : instance - this instance of the NCR5380. * + * Locks: io_request_lock held by caller */ - static void NCR5380_reselect(struct Scsi_Host *instance) { NCR5380_local_declare(); struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) @@ -3111,28 +2672,22 @@ unsigned char target_mask; unsigned char lun, phase; int len; -#ifdef SCSI2 - unsigned char tag; -#endif unsigned char msg[3]; unsigned char *data; Scsi_Cmnd *tmp = NULL, *prev; int abort = 0; - NCR5380_setup(instance); + NCR5380_setup(instance); /* * Disable arbitration, etc. since the host adapter obviously * lost, and tell an interrupted NCR5380_select() to restart. */ - NCR5380_write(MODE_REG, MR_BASE); - hostdata->restart_select = 1; - - target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask); + NCR5380_write(MODE_REG, MR_BASE); + hostdata->restart_select = 1; -#if (NDEBUG & NDEBUG_RESELECTION) - printk("scsi%d : reselect\n", instance->host_no); -#endif + target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask); + dprintk(NDEBUG_SELECTION, ("scsi%d : reselect\n", instance->host_no)); /* * At this point, we have detected that our SCSI ID is on the bus, @@ -3143,10 +2698,10 @@ * signal. */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY); while (NCR5380_read(STATUS_REG) & SR_SEL); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); /* * Wait for target to go into MSGIN. @@ -3154,15 +2709,13 @@ while (!(NCR5380_read(STATUS_REG) & SR_REQ)); - len = 1; - data = msg; - phase = PHASE_MSGIN; - NCR5380_transfer_pio(instance, &phase, &len, &data); - + len = 1; + data = msg; + phase = PHASE_MSGIN; + NCR5380_transfer_pio(instance, &phase, &len, &data); if (!msg[0] & 0x80) { - printk("scsi%d : expecting IDENTIFY message, got ", - instance->host_no); + printk("scsi%d : expecting IDENTIFY message, got ", instance->host_no); print_msg(msg); abort = 1; } else { @@ -3176,22 +2729,14 @@ * nexuses so we can chose to do additional data transfer. */ -#ifdef SCSI2 -#error "SCSI-II tagged queueing is not supported yet" -#endif - /* * Find the command corresponding to the I_T_L or I_T_L_Q nexus we * just reestablished, and remove it from the disconnected queue. */ - for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL; - tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble) + for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble) if ((target_mask == (1 << tmp->target)) && (lun == tmp->lun) -#ifdef SCSI2 - && (tag == tmp->tag) -#endif ) { if (prev) { REMOVE(prev, prev->host_scribble, tmp, tmp->host_scribble); @@ -3204,13 +2749,7 @@ break; } if (!tmp) { -#ifdef SCSI2 - printk("scsi%d : warning : target bitmask %02x lun %d tag %d not in disconnect_queue.\n", - instance->host_no, target_mask, lun, tag); -#else - printk("scsi%d : warning : target bitmask %02x lun %d not in disconnect_queue.\n", - instance->host_no, target_mask, lun); -#endif + printk("scsi%d : warning : target bitmask %02x lun %d not in disconnect_queue.\n", instance->host_no, target_mask, lun); /* * Since we have an established nexus that we can't do anything with, * we must abort it. @@ -3223,10 +2762,7 @@ do_abort(instance); } else { hostdata->connected = tmp; -#if (NDEBUG & NDEBUG_RESELECTION) - printk("scsi%d : nexus established, target = %d, lun = %d, tag = %d\n", - instance->host_no, tmp->target, tmp->lun, tmp->tag); -#endif + dprintk(NDEBUG_RESELECTION, ("scsi%d : nexus established, target = %d, lun = %d, tag = %d\n", instance->host_no, tmp->target, tmp->lun, tmp->tag)); } } @@ -3245,8 +2781,7 @@ #ifdef REAL_DMA static void NCR5380_dma_complete(NCR5380_instance * instance) { NCR5380_local_declare(); - struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata * - instance->hostdata); + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata * instance->hostdata); int transferred; NCR5380_setup(instance); @@ -3289,10 +2824,12 @@ * * Returns : 0 - success, -1 on failure. * - * XXX - there is no way to abort the command that is currently - * connected, you have to wait for it to complete. If this is - * a problem, we could implement longjmp() / setjmp(), setjmp() - * called where the loop started in NCR5380_main(). + * XXX - there is no way to abort the command that is currently + * connected, you have to wait for it to complete. If this is + * a problem, we could implement longjmp() / setjmp(), setjmp() + * called where the loop started in NCR5380_main(). + * + * Locks: io_request_lock held by caller */ #ifndef NCR5380_abort @@ -3300,10 +2837,8 @@ #endif int NCR5380_abort(Scsi_Cmnd * cmd) { NCR5380_local_declare(); - unsigned long flags; struct Scsi_Host *instance = cmd->host; - struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) - instance->hostdata; + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; Scsi_Cmnd *tmp, **prev; printk("scsi%d : aborting command\n", instance->host_no); @@ -3316,15 +2851,10 @@ NCR5380_print_status(instance); - save_flags(flags); - cli(); NCR5380_setup(instance); -#if (NDEBUG & NDEBUG_ABORT) - printk("scsi%d : abort called\n", instance->host_no); - printk(" basr 0x%X, sr 0x%X\n", - NCR5380_read(BUS_AND_STATUS_REG), NCR5380_read(STATUS_REG)); -#endif + dprintk(NDEBUG_ABORT, ("scsi%d : abort called\n", instance->host_no)); + dprintk(NDEBUG_ABORT, (" basr 0x%X, sr 0x%X\n", NCR5380_read(BUS_AND_STATUS_REG), NCR5380_read(STATUS_REG))); #if 0 /* @@ -3334,9 +2864,7 @@ */ if (hostdata->connected == cmd) { -#if (NDEBUG & NDEBUG_ABORT) - printk("scsi%d : aborting connected command\n", instance->host_no); -#endif + dprintk(NDEBUG_ABORT, ("scsi%d : aborting connected command\n", instance->host_no)); hostdata->aborted = 1; /* * We should perform BSY checking, and make sure we haven't slipped @@ -3363,24 +2891,15 @@ * Case 2 : If the command hasn't been issued yet, we simply remove it * from the issue queue. */ -#if (NDEBUG & NDEBUG_ABORT) /* KLL */ - printk("scsi%d : abort going into loop.\n", instance->host_no); -#endif - for (prev = (Scsi_Cmnd **) & (hostdata->issue_queue), - tmp = (Scsi_Cmnd *) hostdata->issue_queue; - tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = - (Scsi_Cmnd *) tmp->host_scribble) + dprintk(NDEBUG_ABORT, ("scsi%d : abort going into loop.\n", instance->host_no)); + for (prev = (Scsi_Cmnd **) & (hostdata->issue_queue), tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = (Scsi_Cmnd *) tmp->host_scribble) if (cmd == tmp) { REMOVE(5, *prev, tmp, tmp->host_scribble); (*prev) = (Scsi_Cmnd *) tmp->host_scribble; tmp->host_scribble = NULL; tmp->result = DID_ABORT << 16; - restore_flags(flags); -#if (NDEBUG & NDEBUG_ABORT) - printk("scsi%d : abort removed command from issue queue.\n", - instance->host_no); -#endif + dprintk(NDEBUG_ABORT, ("scsi%d : abort removed command from issue queue.\n", instance->host_no)); tmp->done(tmp); return SCSI_ABORT_SUCCESS; } @@ -3402,10 +2921,7 @@ */ if (hostdata->connected) { - restore_flags(flags); -#if (NDEBUG & NDEBUG_ABORT) - printk("scsi%d : abort failed, command connected.\n", instance->host_no); -#endif + dprintk(NDEBUG_ABORT, ("scsi%d : abort failed, command connected.\n", instance->host_no)); return SCSI_ABORT_NOT_RUNNING; } /* @@ -3433,34 +2949,22 @@ * it from the disconnected queue. */ - for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; - tmp = (Scsi_Cmnd *) tmp->host_scribble) + for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; tmp = (Scsi_Cmnd *) tmp->host_scribble) if (cmd == tmp) { - restore_flags(flags); -#if (NDEBUG & NDEBUG_ABORT) - printk("scsi%d : aborting disconnected command.\n", instance->host_no); -#endif + dprintk(NDEBUG_ABORT, ("scsi%d : aborting disconnected command.\n", instance->host_no)); if (NCR5380_select(instance, cmd, (int) cmd->tag)) return SCSI_ABORT_BUSY; - -#if (NDEBUG & NDEBUG_ABORT) - printk("scsi%d : nexus reestablished.\n", instance->host_no); -#endif + dprintk(NDEBUG_ABORT, ("scsi%d : nexus reestablished.\n", instance->host_no)); do_abort(instance); - cli(); - for (prev = (Scsi_Cmnd **) & (hostdata->disconnected_queue), - tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; - tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = - (Scsi_Cmnd *) tmp->host_scribble) + for (prev = (Scsi_Cmnd **) & (hostdata->disconnected_queue), tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = (Scsi_Cmnd *) tmp->host_scribble) if (cmd == tmp) { REMOVE(5, *prev, tmp, tmp->host_scribble); *prev = (Scsi_Cmnd *) tmp->host_scribble; tmp->host_scribble = NULL; tmp->result = DID_ABORT << 16; - restore_flags(flags); tmp->done(tmp); return SCSI_ABORT_SUCCESS; } @@ -3474,10 +2978,7 @@ * so we won't panic, but we will notify the user in case something really * broke. */ - - restore_flags(flags); - printk("scsi%d : warning : SCSI command probably completed successfully\n" - " before abortion\n", instance->host_no); + printk("scsi%d : warning : SCSI command probably completed successfully\n" " before abortion\n", instance->host_no); return SCSI_ABORT_NOT_RUNNING; } @@ -3489,6 +2990,7 @@ * * Returns : SCSI_RESET_WAKEUP * + * Locks: io_request_lock held by caller */ #ifndef NCR5380_reset diff -Nru a/drivers/scsi/NCR5380.h b/drivers/scsi/NCR5380.h --- a/drivers/scsi/NCR5380.h Thu Mar 7 18:17:45 2002 +++ b/drivers/scsi/NCR5380.h Thu Mar 7 18:17:45 2002 @@ -55,6 +55,8 @@ #define NDEBUG_C400_PWRITE 0x200000 #define NDEBUG_LISTS 0x400000 +#define NDEBUG_ANY 0xFFFFFFFFUL + /* * The contents of the OUTPUT DATA register are asserted on the bus when * either arbitration is occurring or the phase-indicating signals ( @@ -62,8 +64,8 @@ * bit in the INITIATOR COMMAND register is set. */ -#define OUTPUT_DATA_REG 0 /* wo DATA lines on SCSI bus */ -#define CURRENT_SCSI_DATA_REG 0 /* ro same */ +#define OUTPUT_DATA_REG 0 /* wo DATA lines on SCSI bus */ +#define CURRENT_SCSI_DATA_REG 0 /* ro same */ #define INITIATOR_COMMAND_REG 1 /* rw */ #define ICR_ASSERT_RST 0x80 /* rw Set to assert RST */ @@ -91,10 +93,10 @@ */ #define MR_BLOCK_DMA_MODE 0x80 /* rw block mode DMA */ #define MR_TARGET 0x40 /* rw target mode */ -#define MR_ENABLE_PAR_CHECK 0x20 /* rw enable parity checking */ +#define MR_ENABLE_PAR_CHECK 0x20 /* rw enable parity checking */ #define MR_ENABLE_PAR_INTR 0x10 /* rw enable bad parity interrupt */ #define MR_ENABLE_EOP_INTR 0x08 /* rw enable eop interrupt */ -#define MR_MONITOR_BSY 0x04 /* rw enable int on unexpected bsy fail */ +#define MR_MONITOR_BSY 0x04 /* rw enable int on unexpected bsy fail */ #define MR_DMA_MODE 0x02 /* rw DMA / pseudo DMA mode */ #define MR_ARBITRATE 0x01 /* rw start arbitration */ @@ -116,7 +118,7 @@ * Note : a set bit indicates an active signal, driven by us or another * device. */ -#define SR_RST 0x80 +#define SR_RST 0x80 #define SR_BSY 0x40 #define SR_REQ 0x20 #define SR_MSG 0x10 @@ -159,17 +161,17 @@ /* Write any value to this register to start an ini mode DMA receive */ #define START_DMA_INITIATOR_RECEIVE_REG 7 /* wo */ -#define C400_CONTROL_STATUS_REG NCR53C400_register_offset-8 /* rw */ +#define C400_CONTROL_STATUS_REG NCR53C400_register_offset-8 /* rw */ -#define CSR_RESET 0x80 /* wo Resets 53c400 */ -#define CSR_53C80_REG 0x80 /* ro 5380 registers busy */ -#define CSR_TRANS_DIR 0x40 /* rw Data transfer direction */ -#define CSR_SCSI_BUFF_INTR 0x20 /* rw Enable int on transfer ready */ -#define CSR_53C80_INTR 0x10 /* rw Enable 53c80 interrupts */ -#define CSR_SHARED_INTR 0x08 /* rw Interrupt sharing */ -#define CSR_HOST_BUF_NOT_RDY 0x04 /* ro Is Host buffer ready */ -#define CSR_SCSI_BUF_RDY 0x02 /* ro SCSI buffer read */ -#define CSR_GATED_53C80_IRQ 0x01 /* ro Last block xferred */ +#define CSR_RESET 0x80 /* wo Resets 53c400 */ +#define CSR_53C80_REG 0x80 /* ro 5380 registers busy */ +#define CSR_TRANS_DIR 0x40 /* rw Data transfer direction */ +#define CSR_SCSI_BUFF_INTR 0x20 /* rw Enable int on transfer ready */ +#define CSR_53C80_INTR 0x10 /* rw Enable 53c80 interrupts */ +#define CSR_SHARED_INTR 0x08 /* rw Interrupt sharing */ +#define CSR_HOST_BUF_NOT_RDY 0x04 /* ro Is Host buffer ready */ +#define CSR_SCSI_BUF_RDY 0x02 /* ro SCSI buffer read */ +#define CSR_GATED_53C80_IRQ 0x01 /* ro Last block xferred */ #if 0 #define CSR_BASE CSR_SCSI_BUFF_INTR | CSR_53C80_INTR @@ -178,13 +180,13 @@ #endif /* Number of 128-byte blocks to be transferred */ -#define C400_BLOCK_COUNTER_REG NCR53C400_register_offset-7 /* rw */ +#define C400_BLOCK_COUNTER_REG NCR53C400_register_offset-7 /* rw */ /* Resume transfer after disconnect */ -#define C400_RESUME_TRANSFER_REG NCR53C400_register_offset-6 /* wo */ +#define C400_RESUME_TRANSFER_REG NCR53C400_register_offset-6 /* wo */ /* Access to host buffer stack */ -#define C400_HOST_BUFFER NCR53C400_register_offset-4 /* rw */ +#define C400_HOST_BUFFER NCR53C400_register_offset-4 /* rw */ /* Note : PHASE_* macros are based on the values of the STATUS register */ @@ -203,8 +205,8 @@ * the target register so we can get phase mismatch interrupts on DMA * transfers. */ - -#define PHASE_SR_TO_TCR(phase) ((phase) >> 2) + +#define PHASE_SR_TO_TCR(phase) ((phase) >> 2) /* * The internal should_disconnect() function returns these based on the @@ -220,7 +222,7 @@ * These are "special" values for the tag parameter passed to NCR5380_select. */ -#define TAG_NEXT -1 /* Use next free tag */ +#define TAG_NEXT -1 /* Use next free tag */ #define TAG_NONE -2 /* * Establish I_T_L nexus instead of I_T_L_Q * even on SCSI-II devices. @@ -235,7 +237,7 @@ #define DMA_NONE 255 #define IRQ_AUTO 254 #define DMA_AUTO 254 -#define PORT_AUTO 0xffff /* autoprobe io port for 53c400a */ +#define PORT_AUTO 0xffff /* autoprobe io port for 53c400a */ #define FLAG_HAS_LAST_BYTE_SENT 1 /* NCR53c81 or better */ #define FLAG_CHECK_LAST_BYTE_SENT 2 /* Only test once */ @@ -245,134 +247,188 @@ #ifndef ASM struct NCR5380_hostdata { - NCR5380_implementation_fields; /* implementation specific */ - unsigned char id_mask, id_higher_mask; /* 1 << id, all bits greater */ - unsigned char targets_present; /* targets we have connected + NCR5380_implementation_fields; /* implementation specific */ + unsigned char id_mask, id_higher_mask; /* 1 << id, all bits greater */ + unsigned char targets_present; /* targets we have connected to, so we can call a select failure a retryable condition */ - volatile unsigned char busy[8]; /* index = target, bit = lun */ + volatile unsigned char busy[8]; /* index = target, bit = lun */ #if defined(REAL_DMA) || defined(REAL_DMA_POLL) - volatile int dma_len; /* requested length of DMA */ + volatile int dma_len; /* requested length of DMA */ #endif - volatile unsigned char last_message; /* last message OUT */ - volatile Scsi_Cmnd *connected; /* currently connected command */ - volatile Scsi_Cmnd *issue_queue; /* waiting to be issued */ - volatile Scsi_Cmnd *disconnected_queue; /* waiting for reconnect */ - volatile int restart_select; /* we have disconnected, + volatile unsigned char last_message; /* last message OUT */ + volatile Scsi_Cmnd *connected; /* currently connected command */ + volatile Scsi_Cmnd *issue_queue; /* waiting to be issued */ + volatile Scsi_Cmnd *disconnected_queue; /* waiting for reconnect */ + volatile int restart_select; /* we have disconnected, used to restart NCR5380_select() */ - volatile unsigned aborted:1; /* flag, says aborted */ - int flags; -#ifdef USLEEP - unsigned long time_expires; /* in jiffies, set prior to sleeping */ - struct Scsi_Host *next_timer; - int select_time; /* timer in select for target response */ - volatile Scsi_Cmnd *selecting; -#endif + volatile unsigned aborted:1; /* flag, says aborted */ + int flags; + unsigned long time_expires; /* in jiffies, set prior to sleeping */ + struct Scsi_Host *next_timer; + int select_time; /* timer in select for target response */ + volatile Scsi_Cmnd *selecting; #ifdef NCR5380_STATS - unsigned timebase; /* Base for time calcs */ - long time_read[8]; /* time to do reads */ - long time_write[8]; /* time to do writes */ - unsigned long bytes_read[8]; /* bytes read */ - unsigned long bytes_write[8]; /* bytes written */ - unsigned pendingr; - unsigned pendingw; + unsigned timebase; /* Base for time calcs */ + long time_read[8]; /* time to do reads */ + long time_write[8]; /* time to do writes */ + unsigned long bytes_read[8]; /* bytes read */ + unsigned long bytes_write[8]; /* bytes written */ + unsigned pendingr; + unsigned pendingw; #endif }; #ifdef __KERNEL__ -static struct Scsi_Host *first_instance; /* linked list of 5380's */ +static struct Scsi_Host *first_instance; /* linked list of 5380's */ + +#define dprintk(a,b) do {} while(0) +#define NCR5380_dprint(a,b) do {} while(0) +#define NCR5380_dprint_phase(a,b) do {} while(0) #if defined(AUTOPROBE_IRQ) -static int NCR5380_probe_irq (struct Scsi_Host *instance, int possible); +static int NCR5380_probe_irq(struct Scsi_Host *instance, int possible); #endif -static void NCR5380_init (struct Scsi_Host *instance, int flags); -static void NCR5380_information_transfer (struct Scsi_Host *instance); +static void NCR5380_init(struct Scsi_Host *instance, int flags); +static void NCR5380_information_transfer(struct Scsi_Host *instance); #ifndef DONT_USE_INTR -static void NCR5380_intr (int irq, void *dev_id, struct pt_regs * regs); -static void do_NCR5380_intr (int irq, void *dev_id, struct pt_regs * regs); +static void NCR5380_intr(int irq, void *dev_id, struct pt_regs *regs); +static void do_NCR5380_intr(int irq, void *dev_id, struct pt_regs *regs); #endif -static void NCR5380_main (void); -static void NCR5380_print_options (struct Scsi_Host *instance); -static void NCR5380_print_phase (struct Scsi_Host *instance); -static void NCR5380_print (struct Scsi_Host *instance); +static void NCR5380_main(void); +static void NCR5380_print_options(struct Scsi_Host *instance); +static void NCR5380_print_phase(struct Scsi_Host *instance); +static void NCR5380_print(struct Scsi_Host *instance); #ifndef NCR5380_abort static #endif -int NCR5380_abort (Scsi_Cmnd *cmd); +int NCR5380_abort(Scsi_Cmnd * cmd); #ifndef NCR5380_reset static #endif -int NCR5380_reset (Scsi_Cmnd *cmd, unsigned int reset_flags); +int NCR5380_reset(Scsi_Cmnd * cmd, unsigned int reset_flags); #ifndef NCR5380_queue_command -static +static #endif -int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)); +int NCR5380_queue_command(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)); -static void NCR5380_reselect (struct Scsi_Host *instance); -static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag); +static void NCR5380_reselect(struct Scsi_Host *instance); +static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag); #if defined(PSEUDO_DMA) || defined(REAL_DMA) || defined(REAL_DMA_POLL) -static int NCR5380_transfer_dma (struct Scsi_Host *instance, - unsigned char *phase, int *count, unsigned char **data); +static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data); #endif -static int NCR5380_transfer_pio (struct Scsi_Host *instance, - unsigned char *phase, int *count, unsigned char **data); +static int NCR5380_transfer_pio(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data); #if (defined(REAL_DMA) || defined(REAL_DMA_POLL)) #if defined(i386) || defined(__alpha__) -static __inline__ int NCR5380_pc_dma_setup (struct Scsi_Host *instance, - unsigned char *ptr, unsigned int count, unsigned char mode) { - unsigned limit; - unsigned long bus_addr = virt_to_bus(ptr); - - if (instance->dma_channel <=3) { - if (count > 65536) - count = 65536; - limit = 65536 - (bus_addr & 0xFFFF); - } else { - if (count > 65536 * 2) - count = 65536 * 2; - limit = 65536* 2 - (bus_addr & 0x1FFFF); - } - - if (count > limit) count = limit; - - if ((count & 1) || (bus_addr & 1)) - panic ("scsi%d : attempted unaligned DMA transfer\n", instance->host_no); - cli(); - disable_dma(instance->dma_channel); - clear_dma_ff(instance->dma_channel); - set_dma_addr(instance->dma_channel, bus_addr); - set_dma_count(instance->dma_channel, count); - set_dma_mode(instance->dma_channel, mode); - enable_dma(instance->dma_channel); - sti(); - return count; +/** + * NCR5380_pc_dma_setup - setup ISA DMA + * @instance: adapter to set up + * @ptr: block to transfer (virtual address) + * @count: number of bytes to transfer + * @mode: DMA controller mode to use + * + * Program the DMA controller ready to perform an ISA DMA transfer + * on this chip. + * + * Locks: takes and releases the ISA DMA lock. + */ + +static __inline__ int NCR5380_pc_dma_setup(struct Scsi_Host *instance, unsigned char *ptr, unsigned int count, unsigned char mode) +{ + unsigned limit; + unsigned long bus_addr = virt_to_bus(ptr); + unsigned long flags; + + if (instance->dma_channel <= 3) { + if (count > 65536) + count = 65536; + limit = 65536 - (bus_addr & 0xFFFF); + } else { + if (count > 65536 * 2) + count = 65536 * 2; + limit = 65536 * 2 - (bus_addr & 0x1FFFF); + } + + if (count > limit) + count = limit; + + if ((count & 1) || (bus_addr & 1)) + panic("scsi%d : attempted unaligned DMA transfer\n", instance->host_no); + + flags=claim_dma_lock(); + disable_dma(instance->dma_channel); + clear_dma_ff(instance->dma_channel); + set_dma_addr(instance->dma_channel, bus_addr); + set_dma_count(instance->dma_channel, count); + set_dma_mode(instance->dma_channel, mode); + enable_dma(instance->dma_channel); + release_dma_lock(flags); + + return count; } -static __inline__ int NCR5380_pc_dma_write_setup (struct Scsi_Host *instance, - unsigned char *src, unsigned int count) { - return NCR5380_pc_dma_setup (instance, src, count, DMA_MODE_WRITE); +/** + * NCR5380_pc_dma_write_setup - setup ISA DMA write + * @instance: adapter to set up + * @ptr: block to transfer (virtual address) + * @count: number of bytes to transfer + * + * Program the DMA controller ready to perform an ISA DMA write to the + * SCSI controller. + * + * Locks: called routines take and release the ISA DMA lock. + */ + +static __inline__ int NCR5380_pc_dma_write_setup(struct Scsi_Host *instance, unsigned char *src, unsigned int count) +{ + return NCR5380_pc_dma_setup(instance, src, count, DMA_MODE_WRITE); } -static __inline__ int NCR5380_pc_dma_read_setup (struct Scsi_Host *instance, - unsigned char *src, unsigned int count) { - return NCR5380_pc_dma_setup (instance, src, count, DMA_MODE_READ); +/** + * NCR5380_pc_dma_read_setup - setup ISA DMA read + * @instance: adapter to set up + * @ptr: block to transfer (virtual address) + * @count: number of bytes to transfer + * + * Program the DMA controller ready to perform an ISA DMA read from the + * SCSI controller. + * + * Locks: called routines take and release the ISA DMA lock. + */ + +static __inline__ int NCR5380_pc_dma_read_setup(struct Scsi_Host *instance, unsigned char *src, unsigned int count) +{ + return NCR5380_pc_dma_setup(instance, src, count, DMA_MODE_READ); } -static __inline__ int NCR5380_pc_dma_residual (struct Scsi_Host *instance) { - register int tmp; - cli(); - clear_dma_ff(instance->dma_channel); - tmp = get_dma_residue(instance->dma_channel); - sti(); - return tmp; +/** + * NCR5380_pc_dma_residual - return bytes left + * @instance: adapter + * + * Reports the number of bytes left over after the DMA was terminated. + * + * Locks: takes and releases the ISA DMA lock. + */ + +static __inline__ int NCR5380_pc_dma_residual(struct Scsi_Host *instance) +{ + unsigned long flags; + int tmp; + + flags = claim_dma_lock(); + clear_dma_ff(instance->dma_channel); + tmp = get_dma_residue(instance->dma_channel); + release_dma_lock(flags); + + return tmp; } -#endif /* defined(i386) || defined(__alpha__) */ -#endif /* defined(REAL_DMA) */ -#endif /* __KERNEL__ */ -#endif /* ndef ASM */ -#endif /* NCR5380_H */ +#endif /* defined(i386) || defined(__alpha__) */ +#endif /* defined(REAL_DMA) */ +#endif /* __KERNEL__ */ +#endif /* ndef ASM */ +#endif /* NCR5380_H */ diff -Nru a/drivers/scsi/aic7xxx/aic7xxx.c b/drivers/scsi/aic7xxx/aic7xxx.c --- a/drivers/scsi/aic7xxx/aic7xxx.c Thu Mar 7 18:17:44 2002 +++ b/drivers/scsi/aic7xxx/aic7xxx.c Thu Mar 7 18:17:44 2002 @@ -2584,7 +2584,7 @@ /* * Read the latched byte, but turn off SPIOEN first - * so that we don't inadvertantly cause a REQ for the + * so that we don't inadvertently cause a REQ for the * next byte. */ ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) & ~SPIOEN); diff -Nru a/drivers/scsi/eata.c b/drivers/scsi/eata.c --- a/drivers/scsi/eata.c Thu Mar 7 18:17:41 2002 +++ b/drivers/scsi/eata.c Thu Mar 7 18:17:41 2002 @@ -1,6 +1,13 @@ /* * eata.c - Low-level driver for EATA/DMA SCSI host adapters. * + * 20 Feb 2002 Rev. 7.22 for linux 2.5.5 + * + Remove any reference to virt_to_bus(). + * + Fix pio hang while detecting multiple HBAs. + * + Fixed a board detection bug: in a system with + * multiple ISA/EISA boards, all but the first one + * were erroneously detected as PCI. + * * 01 Jan 2002 Rev. 7.20 for linux 2.5.1 * + Use the dynamic DMA mapping API. * @@ -29,7 +36,7 @@ * boot time. * + Improved boot messages: all tagged capable device are * indicated as "tagged" or "soft-tagged" : - * - "soft-tagged" means that the driver is trying to do its + * - "soft-tagged" means that the driver is trying to do its * own tagging (i.e. the tc:y option is in effect); * - "tagged" means that the device supports tagged commands, * but the driver lets the HBA be responsible for tagging @@ -40,7 +47,7 @@ * + When loaded as a module, accepts the new parameter boot_options * which value is a string with the same format of the kernel boot * command line options. A valid example is: - * modprobe eata boot_options=\"0x7410,0x230,lc:y,tc:n,mq:4\" + * modprobe eata 'boot_options="0x7410,0x230,lc:y,tc:n,mq:4"' * * 9 Sep 1999 Rev. 5.10 for linux 2.2.12 and 2.3.17 * + 64bit cleanup for Linux/Alpha platform support @@ -401,8 +408,6 @@ * the driver sets host->wish_block = TRUE for all ISA boards. */ -#error Please convert me to Documentation/DMA-mapping.txt - #include #ifndef LinuxVersionCode @@ -454,7 +459,7 @@ #define ISA 0 #define ESA 1 -#undef FORCE_CONFIG +#undef FORCE_CONFIG #undef DEBUG_LINKED_COMMANDS #undef DEBUG_DETECT @@ -645,11 +650,18 @@ u_int32_t data_address; /* If sg=0 Data Address, if sg=1 sglist address */ u_int32_t sp_dma_addr; /* Address where sp is DMA'ed when cp completes */ u_int32_t sense_addr; /* Address where Sense Data is DMA'ed on error */ + /* Additional fields begin here. */ Scsi_Cmnd *SCpnt; - struct sg_list *sglist; + + /* All the cp structure is zero filled by queuecommand except the + following CP_TAIL_SIZE bytes, initialized by detect */ + dma_addr_t cp_dma_addr; /* dma handle for this cp structure */ + struct sg_list *sglist; /* pointer to the allocated SG list */ }; +#define CP_TAIL_SIZE (sizeof(struct sglist *) + sizeof(dma_addr_t)) + struct hostdata { struct mscp cp[MAX_MAILBOXES]; /* Mailboxes for this board */ unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */ @@ -657,7 +669,6 @@ unsigned int iocount; /* Total i/o done for this board */ int board_number; /* Number of this board */ char board_name[16]; /* Name of this board */ - char board_id[256]; /* data from INQUIRY on this board */ int in_reset; /* True if board is doing a reset */ int target_to[MAX_TARGET][MAX_CHANNEL]; /* N. of timeout errors on target */ int target_redo[MAX_TARGET][MAX_CHANNEL]; /* If TRUE redo i/o on target */ @@ -675,6 +686,7 @@ static struct Scsi_Host *sh[MAX_BOARDS + 1]; static const char *driver_name = "EATA"; static char sha[MAX_BOARDS]; +static spinlock_t driver_lock = SPIN_LOCK_UNLOCKED; /* Initialize num_boards so that ihdlr can work while detect is in progress */ static unsigned int num_boards = MAX_BOARDS; @@ -710,8 +722,6 @@ #define H2DEV(x) cpu_to_be32(x) #define DEV2H(x) be32_to_cpu(x) -#define V2DEV(addr) ((addr) ? H2DEV(virt_to_bus((void *)addr)) : 0) - static void do_interrupt_handler(int, void *, struct pt_regs *); static void flush_dev(Scsi_Device *, unsigned long, unsigned int, unsigned int); static int do_trace = FALSE; @@ -813,7 +823,7 @@ if (wait_on_busy(iobase, (addr ? MAXLOOP * 100 : MAXLOOP))) return TRUE; - if ((addr = V2DEV(addr))) { + if ((addr = H2DEV(addr))) { outb((char) (addr >> 24), iobase + REG_LOW); outb((char) (addr >> 16), iobase + REG_LM); outb((char) (addr >> 8), iobase + REG_MID); @@ -919,7 +929,8 @@ else protocol_rev = 'C'; - if (!setup_done && j > 0 && j <= MAX_PCI) { + if (protocol_rev != 'A' && info.forcaddr) { + printk("%s: warning, port address has been forced.\n", name); bus_type = "PCI"; is_pci = TRUE; subversion = ESA; @@ -982,7 +993,7 @@ if (is_pci) { pdev = get_pci_dev(port_base); - if (!pdev) + if (!pdev) printk("%s: warning, failed to get pci_dev structure.\n", name); } else @@ -1012,18 +1023,29 @@ #if defined(FORCE_CONFIG) { - struct eata_config config; + struct eata_config *cf; + dma_addr_t cf_dma_addr; + + cf = pci_alloc_consistent(pdev, sizeof(struct eata_config), &cf_dma_addr); + + if (!cf) { + printk("%s: config, pci_alloc_consistent failed, detaching.\n", name); + release_region(port_base, REGION_SIZE); + return FALSE; + } /* Set board configuration */ - memset((char *)&config, 0, sizeof(struct eata_config)); - config.len = (ushort) cpu_to_be16((ushort)510); - config.ocena = TRUE; + memset((char *)cf, 0, sizeof(struct eata_config)); + cf->len = (ushort) cpu_to_be16((ushort)510); + cf->ocena = TRUE; - if (do_dma(port_base, (unsigned long)&config, SET_CONFIG_DMA)) { + if (do_dma(port_base, cf_dma_addr, SET_CONFIG_DMA)) { printk("%s: busy timeout sending configuration, detaching.\n", name); + pci_free_consistent(pdev, sizeof(struct eata_config), cf, cf_dma_addr); release_region(port_base, REGION_SIZE); return FALSE; } + } #endif @@ -1111,6 +1133,10 @@ else sprintf(dma_name, "DMA %u", dma_channel); for (i = 0; i < sh[j]->can_queue; i++) + HD(j)->cp[i].cp_dma_addr = pci_map_single(HD(j)->pdev, + &HD(j)->cp[i], sizeof(struct mscp), PCI_DMA_BIDIRECTIONAL); + + for (i = 0; i < sh[j]->can_queue; i++) if (! ((&HD(j)->cp[i])->sglist = kmalloc( sh[j]->sg_tablesize * sizeof(struct sg_list), (sh[j]->unchecked_isa_dma ? GFP_DMA : 0) | GFP_ATOMIC))) { @@ -1119,7 +1145,7 @@ return FALSE; } - if (! (HD(j)->sp_cpu_addr = pci_alloc_consistent(HD(j)->pdev, + if (! (HD(j)->sp_cpu_addr = pci_alloc_consistent(HD(j)->pdev, sizeof(struct mssp), &HD(j)->sp_dma_addr))) { printk("%s: pci_alloc_consistent failed, detaching.\n", BN(j)); eata2x_release(sh[j]); @@ -1279,6 +1305,9 @@ int eata2x_detect(Scsi_Host_Template *tpnt) { unsigned int j = 0, k; + unsigned long spin_flags; + + spin_lock_irqsave(&driver_lock, spin_flags); tpnt->proc_name = "eata2x"; @@ -1304,6 +1333,7 @@ } num_boards = j; + spin_unlock_irqrestore(&driver_lock, spin_flags); return j; } @@ -1324,10 +1354,10 @@ if (!SCpnt->use_sg) { - if (!SCpnt->request_bufflen) - cpp->data_address = V2DEV(SCpnt->request_buffer); + /* If we get here with PCI_DMA_NONE, pci_map_single triggers a BUG() */ + if (!SCpnt->request_bufflen) pci_dir = PCI_DMA_BIDIRECTIONAL; - else if (SCpnt->request_buffer) + if (SCpnt->request_buffer) cpp->data_address = H2DEV(pci_map_single(HD(j)->pdev, SCpnt->request_buffer, SCpnt->request_bufflen, pci_dir)); @@ -1344,7 +1374,8 @@ } cpp->sg = TRUE; - cpp->data_address = V2DEV(cpp->sglist); + cpp->data_address = H2DEV(pci_map_single(HD(j)->pdev, cpp->sglist, + SCpnt->use_sg * sizeof(struct sg_list), pci_dir)); cpp->data_len = H2DEV((SCpnt->use_sg * sizeof(struct sg_list))); } @@ -1360,13 +1391,14 @@ pci_unmap_single(HD(j)->pdev, DEV2H(cpp->sense_addr), DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE); - if (SCpnt->use_sg) + if (SCpnt->use_sg) pci_unmap_sg(HD(j)->pdev, SCpnt->request_buffer, SCpnt->use_sg, pci_dir); - else if (DEV2H(cpp->data_address) && DEV2H(cpp->data_len)) - pci_unmap_single(HD(j)->pdev, DEV2H(cpp->data_address), - DEV2H(cpp->data_len), pci_dir); + if (!DEV2H(cpp->data_len)) pci_dir = PCI_DMA_BIDIRECTIONAL; + if (DEV2H(cpp->data_address)) + pci_unmap_single(HD(j)->pdev, DEV2H(cpp->data_address), + DEV2H(cpp->data_len), pci_dir); } static void sync_dma(unsigned int i, unsigned int j) { @@ -1381,14 +1413,15 @@ pci_dma_sync_single(HD(j)->pdev, DEV2H(cpp->sense_addr), DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE); - if (SCpnt->use_sg) - pci_dma_sync_sg(HD(j)->pdev, SCpnt->request_buffer, + if (SCpnt->use_sg) + pci_dma_sync_sg(HD(j)->pdev, SCpnt->request_buffer, SCpnt->use_sg, pci_dir); - else if (DEV2H(cpp->data_address) && DEV2H(cpp->data_len)) - pci_dma_sync_single(HD(j)->pdev, DEV2H(cpp->data_address), - DEV2H(cpp->data_len), pci_dir); + if (!DEV2H(cpp->data_len)) pci_dir = PCI_DMA_BIDIRECTIONAL; + if (DEV2H(cpp->data_address)) + pci_dma_sync_single(HD(j)->pdev, DEV2H(cpp->data_address), + DEV2H(cpp->data_len), pci_dir); } static inline void scsi_to_dev_dir(unsigned int i, unsigned int j) { @@ -1409,8 +1442,7 @@ struct mscp *cpp; Scsi_Cmnd *SCpnt; - cpp = &HD(j)->cp[i]; - SCpnt = cpp->SCpnt; + cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; if (SCpnt->sc_data_direction == SCSI_DATA_READ) { cpp->din = TRUE; @@ -1428,7 +1460,7 @@ return; } - if (SCpnt->sc_data_direction != SCSI_DATA_UNKNOWN) + if (SCpnt->sc_data_direction != SCSI_DATA_UNKNOWN) panic("%s: qcomm, invalid SCpnt->sc_data_direction.\n", BN(j)); for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++) @@ -1479,7 +1511,7 @@ /* Set pointer to control packet structure */ cpp = &HD(j)->cp[i]; - memset(cpp, 0, sizeof(struct mscp) - sizeof(struct sg_list *)); + memset(cpp, 0, sizeof(struct mscp) - CP_TAIL_SIZE); /* Set pointer to status packet structure, Big Endian format */ cpp->sp_dma_addr = H2DEV(HD(j)->sp_dma_addr); @@ -1536,7 +1568,7 @@ } /* Send control packet to the board */ - if (do_dma(sh[j]->io_port, (unsigned long) cpp, SEND_CP_DMA)) { + if (do_dma(sh[j]->io_port, cpp->cp_dma_addr, SEND_CP_DMA)) { unmap_dma(i, j); SCpnt->host_scribble = NULL; printk("%s: qcomm, target %d.%d:%d, pid %ld, adapter busy.\n", @@ -1935,7 +1967,7 @@ for (n = 0; n < n_ready; n++) { k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt; - if (do_dma(sh[j]->io_port, (unsigned long) cpp, SEND_CP_DMA)) { + if (do_dma(sh[j]->io_port, cpp->cp_dma_addr, SEND_CP_DMA)) { printk("%s: %s, target %d.%d:%d, pid %ld, mbox %d, adapter"\ " busy, will abort.\n", BN(j), (ihdlr ? "ihdlr" : "qcomm"), SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid, k); @@ -1985,7 +2017,7 @@ reg = inb(sh[j]->io_port + REG_STATUS); /* Reject any sp with supspect data */ - if (spp->eoc == FALSE) + if (spp->eoc == FALSE && HD(j)->iocount > 1) printk("%s: ihdlr, spp->eoc == FALSE, irq %d, reg 0x%x, count %d.\n", BN(j), irq, reg, HD(j)->iocount); if (spp->cpp_index < 0 || spp->cpp_index >= sh[j]->can_queue) @@ -2191,10 +2223,14 @@ for (i = 0; i < sh[j]->can_queue; i++) if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist); - if (HD(j)->sp_cpu_addr) + for (i = 0; i < sh[j]->can_queue; i++) + pci_unmap_single(HD(j)->pdev, HD(j)->cp[i].cp_dma_addr, + sizeof(struct mscp), PCI_DMA_BIDIRECTIONAL); + + if (HD(j)->sp_cpu_addr) pci_free_consistent(HD(j)->pdev, sizeof(struct mssp), HD(j)->sp_cpu_addr, HD(j)->sp_dma_addr); - + free_irq(sh[j]->irq, &sha[j]); if (sh[j]->dma_channel != NO_DMA) free_dma(sh[j]->dma_channel); diff -Nru a/drivers/scsi/eata.h b/drivers/scsi/eata.h --- a/drivers/scsi/eata.h Thu Mar 7 18:17:39 2002 +++ b/drivers/scsi/eata.h Thu Mar 7 18:17:39 2002 @@ -13,7 +13,7 @@ int eata2x_reset(Scsi_Cmnd *); int eata2x_biosparam(Disk *, kdev_t, int *); -#define EATA_VERSION "7.20.00" +#define EATA_VERSION "7.22.00" #define EATA { \ name: "EATA/DMA 2.0x rev. " EATA_VERSION " ", \ diff -Nru a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c --- a/drivers/scsi/ide-scsi.c Thu Mar 7 18:17:37 2002 +++ b/drivers/scsi/ide-scsi.c Thu Mar 7 18:17:37 2002 @@ -138,7 +138,7 @@ idescsi_discard_data (drive, bcount); return; } - count = IDE_MIN (pc->sg->length - pc->b_count, bcount); + count = min(pc->sg->length - pc->b_count, bcount); buf = page_address(pc->sg->page) + pc->sg->offset; atapi_input_bytes (drive, buf + pc->b_count, count); bcount -= count; pc->b_count += count; @@ -160,7 +160,7 @@ idescsi_output_zeros (drive, bcount); return; } - count = IDE_MIN (pc->sg->length - pc->b_count, bcount); + count = min(pc->sg->length - pc->b_count, bcount); buf = page_address(pc->sg->page) + pc->sg->offset; atapi_output_bytes (drive, buf + pc->b_count, count); bcount -= count; pc->b_count += count; @@ -182,7 +182,7 @@ if (!test_bit(PC_TRANSFORM, &pc->flags)) return; - if (drive->media == ide_cdrom || drive->media == ide_optical) { + if (drive->type == ATA_ROM || drive->type == ATA_MOD) { if (c[0] == READ_6 || c[0] == WRITE_6) { c[8] = c[4]; c[5] = c[3]; c[4] = c[2]; c[3] = c[1] & 0x1f; c[2] = 0; c[1] &= 0xe0; @@ -221,7 +221,7 @@ if (!test_bit(PC_TRANSFORM, &pc->flags)) return; - if (drive->media == ide_cdrom || drive->media == ide_optical) { + if (drive->type == ATA_ROM || drive->type == ATA_MOD) { if (pc->c[0] == MODE_SENSE_10 && sc[0] == MODE_SENSE) { scsi_buf[0] = atapi_buf[1]; /* Mode data length */ scsi_buf[1] = atapi_buf[2]; /* Medium type */ @@ -290,7 +290,7 @@ if (!test_bit(PC_WRITING, &pc->flags) && pc->actually_transferred && pc->actually_transferred <= 1024 && pc->buffer) { printk(", rst = "); scsi_buf = pc->scsi_cmd->request_buffer; - hexdump(scsi_buf, IDE_MIN(16, pc->scsi_cmd->request_bufflen)); + hexdump(scsi_buf, min(16, pc->scsi_cmd->request_bufflen)); } else printk("\n"); } } @@ -307,7 +307,7 @@ static inline unsigned long get_timeout(idescsi_pc_t *pc) { - return IDE_MAX(WAIT_CMD, pc->timeout - jiffies); + return max(WAIT_CMD, pc->timeout - jiffies); } /* @@ -431,7 +431,7 @@ scsi->pc=pc; /* Set the current packet command */ pc->actually_transferred=0; /* We haven't transferred any data yet */ pc->current_position=pc->buffer; - bcount = IDE_MIN (pc->request_transfer, 63 * 1024); /* Request to transfer the entire buffer at once */ + bcount = min(pc->request_transfer, 63 * 1024); /* Request to transfer the entire buffer at once */ if (drive->using_dma && rq->bio) dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive); @@ -508,7 +508,8 @@ */ static void idescsi_setup (ide_drive_t *drive, idescsi_scsi_t *scsi, int id) { - DRIVER(drive)->busy++; + ata_ops(drive)->busy++; + idescsi_drives[id] = drive; drive->driver_data = scsi; drive->ready_stat = 0; @@ -535,80 +536,41 @@ return 0; } -int idescsi_init(void); -int idescsi_reinit(ide_drive_t *drive); +static void idescsi_revalidate(ide_drive_t *_dummy) +{ + /* The partition information will be handled by the SCSI layer. + */ +} /* * IDE subdriver functions, registered with ide.c */ -static ide_driver_t idescsi_driver = { - name: "ide-scsi", - media: ide_scsi, - busy: 0, - supports_dma: 1, - supports_dsc_overlap: 0, +static struct ata_operations idescsi_driver = { + owner: THIS_MODULE, cleanup: idescsi_cleanup, standby: NULL, - flushcache: NULL, do_request: idescsi_do_request, end_request: idescsi_end_request, ioctl: NULL, open: idescsi_open, release: idescsi_ide_release, - media_change: NULL, - revalidate: NULL, + check_media_change: NULL, + revalidate: idescsi_revalidate, pre_reset: NULL, capacity: NULL, special: NULL, - proc: NULL, - driver_init: idescsi_init, - driver_reinit: idescsi_reinit, + proc: NULL }; -int idescsi_reinit (ide_drive_t *drive) -{ -#if 0 - idescsi_scsi_t *scsi; - byte media[] = {TYPE_DISK, TYPE_TAPE, TYPE_PROCESSOR, TYPE_WORM, TYPE_ROM, TYPE_SCANNER, TYPE_MOD, 255}; - int i, failed, id; - - if (!idescsi_initialized) - return 0; - for (i = 0; i < MAX_HWIFS * MAX_DRIVES; i++) - idescsi_drives[i] = NULL; - - MOD_INC_USE_COUNT; - for (i = 0; media[i] != 255; i++) { - failed = 0; - while ((drive = ide_scan_devices (media[i], idescsi_driver.name, NULL, failed++)) != NULL) { - - if ((scsi = (idescsi_scsi_t *) kmalloc (sizeof (idescsi_scsi_t), GFP_KERNEL)) == NULL) { - printk (KERN_ERR "ide-scsi: %s: Can't allocate a scsi structure\n", drive->name); - continue; - } - if (ide_register_subdriver (drive, &idescsi_driver)) { - printk (KERN_ERR "ide-scsi: %s: Failed to register the driver with ide.c\n", drive->name); - kfree (scsi); - continue; - } - for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++); - idescsi_setup (drive, scsi, id); - failed--; - } - } - ide_register_module(&idescsi_module); - MOD_DEC_USE_COUNT; -#endif - return 0; -} - /* * idescsi_init will register the driver for each scsi. */ -int idescsi_init (void) +static int idescsi_init(void) { ide_drive_t *drive; idescsi_scsi_t *scsi; + /* FIXME: The following is just plain wrong, since those are definitely *not* the + * media types supported by the ATA layer */ byte media[] = {TYPE_DISK, TYPE_TAPE, TYPE_PROCESSOR, TYPE_WORM, TYPE_ROM, TYPE_SCANNER, TYPE_MOD, 255}; int i, failed, id; @@ -620,7 +582,7 @@ MOD_INC_USE_COUNT; for (i = 0; media[i] != 255; i++) { failed = 0; - while ((drive = ide_scan_devices (media[i], idescsi_driver.name, NULL, failed++)) != NULL) { + while ((drive = ide_scan_devices (media[i], "ide-scsi", NULL, failed++)) != NULL) { if ((scsi = (idescsi_scsi_t *) kmalloc (sizeof (idescsi_scsi_t), GFP_KERNEL)) == NULL) { printk (KERN_ERR "ide-scsi: %s: Can't allocate a scsi structure\n", drive->name); @@ -636,7 +598,7 @@ failed--; } } - ide_register_module(&idescsi_driver); + revalidate_drives(); MOD_DEC_USE_COUNT; return 0; } @@ -651,9 +613,9 @@ host = scsi_register(host_template, 0); if(host == NULL) return 0; - + for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++) - last_lun = IDE_MAX(last_lun, idescsi_drives[id]->last_lun); + last_lun = max(last_lun, idescsi_drives[id]->last_lun); host->max_id = id; host->max_lun = last_lun + 1; host->can_queue = host->cmd_per_lun * id; @@ -668,7 +630,7 @@ for (id = 0; id < MAX_HWIFS * MAX_DRIVES; id++) { drive = idescsi_drives[id]; if (drive) - DRIVER(drive)->busy--; + ata_ops(drive)->busy--; } return 0; } @@ -898,15 +860,22 @@ int i, failed; scsi_unregister_host(&idescsi_template); + + /* FIXME: The media types scanned here have literally nothing to do + * with the media types used by the overall ATA code! + * + * This is basically showing us, that there is something wrong with the + * ide_scan_devices function. + */ + for (i = 0; media[i] != 255; i++) { failed = 0; - while ((drive = ide_scan_devices (media[i], idescsi_driver.name, &idescsi_driver, failed)) != NULL) + while ((drive = ide_scan_devices (media[i], "ide-scsi", &idescsi_driver, failed)) != NULL) if (idescsi_cleanup (drive)) { printk ("%s: exit_idescsi_module() called while still busy\n", drive->name); failed++; } } - ide_unregister_module(&idescsi_driver); } module_init(init_idescsi_module); diff -Nru a/drivers/scsi/imm.c b/drivers/scsi/imm.c --- a/drivers/scsi/imm.c Thu Mar 7 18:17:46 2002 +++ b/drivers/scsi/imm.c Thu Mar 7 18:17:46 2002 @@ -1007,7 +1007,7 @@ cmd->SCp.this_residual = cmd->request_bufflen; cmd->SCp.ptr = cmd->request_buffer; } - cmd->SCp.buffers_residual = cmd->use_sg; + cmd->SCp.buffers_residual = cmd->use_sg - 1; cmd->SCp.phase++; if (cmd->SCp.this_residual & 0x01) cmd->SCp.this_residual++; diff -Nru a/drivers/scsi/ips.c b/drivers/scsi/ips.c --- a/drivers/scsi/ips.c Thu Mar 7 18:17:36 2002 +++ b/drivers/scsi/ips.c Thu Mar 7 18:17:36 2002 @@ -81,7 +81,7 @@ /* 2.3.18 and later */ /* - Sync with other changes from the 2.3 kernels */ /* 4.00.06 - Fix timeout with initial FFDC command */ -/* 4.00.06a - Port to 2.4 (trivial) -- Christoph Hellwig */ +/* 4.00.06a - Port to 2.4 (trivial) -- Christoph Hellwig */ /* 4.10.00 - Add support for ServeRAID 4M/4L */ /* 4.10.13 - Fix for dynamic unload and proc file system */ /* 4.20.03 - Rename version to coincide with new release schedules */ diff -Nru a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c --- a/drivers/scsi/ppa.c Thu Mar 7 18:17:37 2002 +++ b/drivers/scsi/ppa.c Thu Mar 7 18:17:37 2002 @@ -738,7 +738,7 @@ if (cmd->SCp.buffers_residual--) { cmd->SCp.buffer++; cmd->SCp.this_residual = cmd->SCp.buffer->length; - cmd->SCp.ptr = cmd->SCp.buffer->address; + cmd->SCp.ptr = page_address(cmd->SCp.buffer->page) + cmd->SCp.buffer->offset; } } /* Now check to see if the drive is ready to comunicate */ @@ -923,14 +923,14 @@ /* if many buffers are available, start filling the first */ cmd->SCp.buffer = (struct scatterlist *) cmd->request_buffer; cmd->SCp.this_residual = cmd->SCp.buffer->length; - cmd->SCp.ptr = cmd->SCp.buffer->address; + cmd->SCp.ptr = page_address(cmd->SCp.buffer->page) + cmd->SCp.buffer->offset; } else { /* else fill the only available buffer */ cmd->SCp.buffer = NULL; cmd->SCp.this_residual = cmd->request_bufflen; cmd->SCp.ptr = cmd->request_buffer; } - cmd->SCp.buffers_residual = cmd->use_sg; + cmd->SCp.buffers_residual = cmd->use_sg - 1; cmd->SCp.phase++; case 5: /* Phase 5 - Data transfer stage */ diff -Nru a/drivers/scsi/qlogicfas.c b/drivers/scsi/qlogicfas.c --- a/drivers/scsi/qlogicfas.c Thu Mar 7 18:17:44 2002 +++ b/drivers/scsi/qlogicfas.c Thu Mar 7 18:17:44 2002 @@ -344,6 +344,7 @@ unsigned int reqlen; /* total length of transfer */ struct scatterlist *sglist; /* scatter-gather list pointer */ unsigned int sgcount; /* sg counter */ +char *buf; rtrc(1) j = inb(qbase + 6); @@ -391,7 +392,8 @@ REG0; return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16); } - if (ql_pdma(phase, sglist->address, sglist->length)) + buf = page_address(sglist->page) + sglist->offset; + if (ql_pdma(phase, buf, sglist->length)) break; sglist++; } diff -Nru a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c --- a/drivers/scsi/scsi.c Thu Mar 7 18:17:45 2002 +++ b/drivers/scsi/scsi.c Thu Mar 7 18:17:45 2002 @@ -165,6 +165,11 @@ void scsi_build_commandblocks(Scsi_Device * SDpnt); /* + * Private interface into the new error handling code. + */ +extern int scsi_new_reset(Scsi_Cmnd *SCpnt, unsigned int flag); + +/* * Function: scsi_initialize_queue() * * Purpose: Selects queue handler function for a device. @@ -2660,6 +2665,94 @@ */ scsi_release_commandblocks(SDpnt); kfree(SDpnt); +} + +/* + * Function: scsi_reset_provider_done_command + * + * Purpose: Dummy done routine. + * + * Notes: Some low level drivers will call scsi_done and end up here, + * others won't bother. + * We don't want the bogus command used for the bus/device + * reset to find its way into the mid-layer so we intercept + * it here. + */ +static void +scsi_reset_provider_done_command(Scsi_Cmnd *SCpnt) +{ +} + +/* + * Function: scsi_reset_provider + * + * Purpose: Send requested reset to a bus or device at any phase. + * + * Arguments: device - device to send reset to + * flag - reset type (see scsi.h) + * + * Returns: SUCCESS/FAILURE. + * + * Notes: This is used by the SCSI Generic driver to provide + * Bus/Device reset capability. + */ +int +scsi_reset_provider(Scsi_Device *dev, int flag) +{ + Scsi_Cmnd SC, *SCpnt = &SC; + int rtn; + + memset(&SCpnt->eh_timeout, 0, sizeof(SCpnt->eh_timeout)); + SCpnt->host = dev->host; + SCpnt->device = dev; + SCpnt->target = dev->id; + SCpnt->lun = dev->lun; + SCpnt->channel = dev->channel; + SCpnt->request.rq_status = RQ_SCSI_BUSY; + SCpnt->request.waiting = NULL; + SCpnt->use_sg = 0; + SCpnt->old_use_sg = 0; + SCpnt->old_cmd_len = 0; + SCpnt->underflow = 0; + SCpnt->transfersize = 0; + SCpnt->resid = 0; + SCpnt->serial_number = 0; + SCpnt->serial_number_at_timeout = 0; + SCpnt->host_scribble = NULL; + SCpnt->next = NULL; + SCpnt->state = SCSI_STATE_INITIALIZING; + SCpnt->owner = SCSI_OWNER_MIDLEVEL; + + memset(&SCpnt->cmnd, '\0', sizeof(SCpnt->cmnd)); + + SCpnt->scsi_done = scsi_reset_provider_done_command; + SCpnt->done = NULL; + SCpnt->reset_chain = NULL; + + SCpnt->buffer = NULL; + SCpnt->bufflen = 0; + SCpnt->request_buffer = NULL; + SCpnt->request_bufflen = 0; + + SCpnt->internal_timeout = NORMAL_TIMEOUT; + SCpnt->abort_reason = DID_ABORT; + + SCpnt->cmd_len = 0; + + SCpnt->sc_data_direction = SCSI_DATA_UNKNOWN; + SCpnt->sc_request = NULL; + SCpnt->sc_magic = SCSI_CMND_MAGIC; + + /* + * Sometimes the command can get back into the timer chain, + * so use the pid as an identifier. + */ + SCpnt->pid = 0; + + rtn = scsi_new_reset(SCpnt, flag); + + scsi_delete_timer(SCpnt); + return rtn; } /* diff -Nru a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h --- a/drivers/scsi/scsi.h Thu Mar 7 18:17:40 2002 +++ b/drivers/scsi/scsi.h Thu Mar 7 18:17:40 2002 @@ -834,6 +834,16 @@ current->state = TASK_RUNNING; \ }; } +/* + * old style reset request from external source + * (private to sg.c and scsi_error.c, supplied by scsi_obsolete.c) + */ +#define SCSI_TRY_RESET_DEVICE 1 +#define SCSI_TRY_RESET_BUS 2 +#define SCSI_TRY_RESET_HOST 3 + +extern int scsi_reset_provider(Scsi_Device *, int); + #endif /* diff -Nru a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c --- a/drivers/scsi/scsi_error.c Thu Mar 7 18:17:36 2002 +++ b/drivers/scsi/scsi_error.c Thu Mar 7 18:17:36 2002 @@ -979,15 +979,24 @@ case DID_SOFT_ERROR: goto maybe_retry; + case DID_ERROR: + if (msg_byte(SCpnt->result) == COMMAND_COMPLETE && + status_byte(SCpnt->result) == RESERVATION_CONFLICT) + /* + * execute reservation conflict processing code + * lower down + */ + break; + /* FALLTHROUGH */ + case DID_BUS_BUSY: case DID_PARITY: - case DID_ERROR: goto maybe_retry; case DID_TIME_OUT: /* - * When we scan the bus, we get timeout messages for - * these commands if there is no device available. - * Other hosts report DID_NO_CONNECT for the same thing. + * When we scan the bus, we get timeout messages for + * these commands if there is no device available. + * Other hosts report DID_NO_CONNECT for the same thing. */ if ((SCpnt->cmnd[0] == TEST_UNIT_READY || SCpnt->cmnd[0] == INQUIRY)) { @@ -1048,8 +1057,13 @@ */ return SUCCESS; case BUSY: - case RESERVATION_CONFLICT: goto maybe_retry; + + case RESERVATION_CONFLICT: + printk("scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n", + SCpnt->host->host_no, SCpnt->channel, + SCpnt->device->id, SCpnt->device->lun); + return SUCCESS; /* causes immediate I/O error */ default: return FAILED; } @@ -1947,6 +1961,45 @@ */ if (host->eh_notify != NULL) up(host->eh_notify); +} + +/* + * Function: scsi_new_reset + * + * Purpose: Send requested reset to a bus or device at any phase. + * + * Arguments: SCpnt - command ptr to send reset with (usually a dummy) + * flag - reset type (see scsi.h) + * + * Returns: SUCCESS/FAILURE. + * + * Notes: This is used by the SCSI Generic driver to provide + * Bus/Device reset capability. + */ +int +scsi_new_reset(Scsi_Cmnd *SCpnt, int flag) +{ + int rtn; + + switch(flag) { + case SCSI_TRY_RESET_DEVICE: + rtn = scsi_try_bus_device_reset(SCpnt, 0); + if (rtn == SUCCESS) + break; + /* FALLTHROUGH */ + case SCSI_TRY_RESET_BUS: + rtn = scsi_try_bus_reset(SCpnt); + if (rtn == SUCCESS) + break; + /* FALLTHROUGH */ + case SCSI_TRY_RESET_HOST: + rtn = scsi_try_host_reset(SCpnt); + break; + default: + rtn = FAILED; + } + + return rtn; } /* diff -Nru a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c --- a/drivers/scsi/scsi_syms.c Thu Mar 7 18:17:44 2002 +++ b/drivers/scsi/scsi_syms.c Thu Mar 7 18:17:44 2002 @@ -81,6 +81,11 @@ EXPORT_SYMBOL(scsi_deregister_blocked_host); /* + * This symbol is for the highlevel drivers (e.g. sg) only. + */ +EXPORT_SYMBOL(scsi_reset_provider); + +/* * These are here only while I debug the rest of the scsi stuff. */ EXPORT_SYMBOL(scsi_hostlist); diff -Nru a/drivers/scsi/sd.c b/drivers/scsi/sd.c --- a/drivers/scsi/sd.c Thu Mar 7 18:17:39 2002 +++ b/drivers/scsi/sd.c Thu Mar 7 18:17:39 2002 @@ -235,7 +235,7 @@ case BLKELVSET: case BLKBSZGET: case BLKBSZSET: - return blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_bdev, cmd, arg); case BLKRRPART: /* Re-read partition tables */ if (!capable(CAP_SYS_ADMIN)) diff -Nru a/drivers/scsi/sr.c b/drivers/scsi/sr.c --- a/drivers/scsi/sr.c Thu Mar 7 18:17:40 2002 +++ b/drivers/scsi/sr.c Thu Mar 7 18:17:40 2002 @@ -99,11 +99,13 @@ static void sr_release(struct cdrom_device_info *cdi) { - if (scsi_CDs[minor(cdi->dev)].device->sector_size > 2048) + Scsi_CD *SCp = cdi->handle; + + if (SCp->device->sector_size > 2048) sr_set_blocklength(minor(cdi->dev), 2048); - scsi_CDs[minor(cdi->dev)].device->access_count--; - if (scsi_CDs[minor(cdi->dev)].device->host->hostt->module) - __MOD_DEC_USE_COUNT(scsi_CDs[minor(cdi->dev)].device->host->hostt->module); + SCp->device->access_count--; + if (SCp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(SCp->device->host->hostt->module); if (sr_template.module) __MOD_DEC_USE_COUNT(sr_template.module); } @@ -144,28 +146,27 @@ int sr_media_change(struct cdrom_device_info *cdi, int slot) { + Scsi_CD *SCp = cdi->handle; int retval; if (CDSL_CURRENT != slot) { /* no changer support */ return -EINVAL; } - retval = scsi_ioctl(scsi_CDs[minor(cdi->dev)].device, - SCSI_IOCTL_TEST_UNIT_READY, 0); + retval = scsi_ioctl(SCp->device, SCSI_IOCTL_TEST_UNIT_READY, 0); if (retval) { /* Unable to test, unit probably not ready. This usually * means there is no disc in the drive. Mark as changed, * and we will figure it out later once the drive is * available again. */ - - scsi_CDs[minor(cdi->dev)].device->changed = 1; + SCp->device->changed = 1; return 1; /* This will force a flush, if called from * check_disk_change */ }; - retval = scsi_CDs[minor(cdi->dev)].device->changed; - scsi_CDs[minor(cdi->dev)].device->changed = 0; + retval = SCp->device->changed; + SCp->device->changed = 0; /* If the disk changed, the capacity will now be different, * so we force a re-read of this information */ if (retval) { @@ -179,9 +180,8 @@ * be trying to use something that is too small if the disc * has changed. */ - scsi_CDs[minor(cdi->dev)].needs_sector_size = 1; - - scsi_CDs[minor(cdi->dev)].device->sector_size = 2048; + SCp->needs_sector_size = 1; + SCp->device->sector_size = 2048; } return retval; } @@ -198,6 +198,7 @@ int good_sectors = (result == 0 ? this_count : 0); int block_sectors = 0; int device_nr = DEVICE_NR(SCpnt->request.rq_dev); + Scsi_CD *SCp = &scsi_CDs[device_nr]; #ifdef DEBUG printk("sr.c done: %x %p\n", result, SCpnt->request.bh->b_data); @@ -222,7 +223,7 @@ block_sectors = bio_sectors(SCpnt->request.bio); if (block_sectors < 4) block_sectors = 4; - if (scsi_CDs[device_nr].device->sector_size == 2048) + if (SCp->device->sector_size == 2048) error_sector <<= 2; error_sector &= ~(block_sectors - 1); good_sectors = error_sector - SCpnt->request.sector; @@ -235,7 +236,7 @@ * 75 2K sectors, we decrease the saved size value. */ if ((error_sector >> 1) < sr_sizes[device_nr] && - scsi_CDs[device_nr].capacity - error_sector < 4 * 75) + SCp->capacity - error_sector < 4 * 75) sr_sizes[device_nr] = error_sector >> 1; } @@ -250,32 +251,34 @@ static request_queue_t *sr_find_queue(kdev_t dev) { - /* - * No such device - */ - if (minor(dev) >= sr_template.dev_max || !scsi_CDs[minor(dev)].device) - return NULL; + Scsi_CD *SCp; - return &scsi_CDs[minor(dev)].device->request_queue; + if (minor(dev) >= sr_template.dev_max) + return NULL; + SCp = &scsi_CDs[minor(dev)]; + if (!SCp->device) + return NULL; + return &SCp->device->request_queue; } static int sr_init_command(Scsi_Cmnd * SCpnt) { int dev, devm, block=0, this_count, s_size; + Scsi_CD *SCp; devm = minor(SCpnt->request.rq_dev); dev = DEVICE_NR(SCpnt->request.rq_dev); + SCp = &scsi_CDs[dev]; 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 || - !scsi_CDs[dev].device->online) { + if (dev >= sr_template.nr_dev || !SCp->device || !SCp->device->online) { SCSI_LOG_HLQUEUE(2, printk("Finishing %ld sectors\n", SCpnt->request.nr_sectors)); SCSI_LOG_HLQUEUE(2, printk("Retry with 0x%p\n", SCpnt)); return 0; } - if (scsi_CDs[dev].device->changed) { + + if (SCp->device->changed) { /* * quietly refuse to do anything to a changed disc until the * changed bit has been reset @@ -292,7 +295,7 @@ * we do lazy blocksize switching (when reading XA sectors, * see CDROMREADMODE2 ioctl) */ - s_size = scsi_CDs[dev].device->sector_size; + s_size = SCp->device->sector_size; if (s_size > 2048) { if (!in_interrupt()) sr_set_blocklength(DEVICE_NR(CURRENT->rq_dev), 2048); @@ -306,7 +309,7 @@ } if (rq_data_dir(&SCpnt->request) == WRITE) { - if (!scsi_CDs[dev].device->writeable) + if (!SCp->device->writeable) return 0; SCpnt->cmnd[0] = WRITE_10; SCpnt->sc_data_direction = SCSI_DATA_WRITE; @@ -355,7 +358,7 @@ * host adapter, it's safe to assume that we can at least transfer * this many bytes between each connect / disconnect. */ - SCpnt->transfersize = scsi_CDs[dev].device->sector_size; + SCpnt->transfersize = SCp->device->sector_size; SCpnt->underflow = this_count << 9; SCpnt->allowed = MAX_RETRIES; @@ -397,22 +400,23 @@ static int sr_open(struct cdrom_device_info *cdi, int purpose) { + Scsi_CD *SCp = cdi->handle; + check_disk_change(cdi->dev); - if (minor(cdi->dev) >= sr_template.dev_max - || !scsi_CDs[minor(cdi->dev)].device) { + if (minor(cdi->dev) >= sr_template.dev_max || !SCp->device) { return -ENXIO; /* No such device */ } /* * If the device is in error recovery, wait until it is done. * If the device is offline, then disallow any access to it. */ - if (!scsi_block_when_processing_errors(scsi_CDs[minor(cdi->dev)].device)) { + if (!scsi_block_when_processing_errors(SCp->device)) { return -ENXIO; } - scsi_CDs[minor(cdi->dev)].device->access_count++; - if (scsi_CDs[minor(cdi->dev)].device->host->hostt->module) - __MOD_INC_USE_COUNT(scsi_CDs[minor(cdi->dev)].device->host->hostt->module); + SCp->device->access_count++; + if (SCp->device->host->hostt->module) + __MOD_INC_USE_COUNT(SCp->device->host->hostt->module); if (sr_template.module) __MOD_INC_USE_COUNT(sr_template.module); @@ -421,7 +425,7 @@ * this is the case, and try again. */ - if (scsi_CDs[minor(cdi->dev)].needs_sector_size) + if (SCp->needs_sector_size) get_sectorsize(minor(cdi->dev)); return 0; @@ -472,31 +476,25 @@ { unsigned char cmd[10]; unsigned char *buffer; - int the_result, retries; + int the_result, retries = 3; int sector_size; - Scsi_Request *SRpnt; + Scsi_Request *SRpnt = NULL; + Scsi_CD *SCp; request_queue_t *queue; - buffer = (unsigned char *) kmalloc(512, GFP_DMA); - SRpnt = scsi_allocate_request(scsi_CDs[i].device); - - if(buffer == NULL || SRpnt == NULL) - { - scsi_CDs[i].capacity = 0x1fffff; - sector_size = 2048; /* A guess, just in case */ - scsi_CDs[i].needs_sector_size = 1; - if(buffer) - kfree(buffer); - if(SRpnt) - scsi_release_request(SRpnt); - return; - } + SCp = &scsi_CDs[i]; + + buffer = kmalloc(512, GFP_DMA); + if (!buffer) + goto Enomem; + SRpnt = scsi_allocate_request(SCp->device); + if (!SRpnt) + goto Enomem; - retries = 3; do { cmd[0] = READ_CAPACITY; - cmd[1] = (scsi_CDs[i].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[i].device->lun << 5) & 0xe0) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->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; @@ -519,15 +517,15 @@ SRpnt = NULL; if (the_result) { - scsi_CDs[i].capacity = 0x1fffff; + SCp->capacity = 0x1fffff; sector_size = 2048; /* A guess, just in case */ - scsi_CDs[i].needs_sector_size = 1; + SCp->needs_sector_size = 1; } else { #if 0 - if (cdrom_get_last_written(MKDEV(MAJOR_NR, i), + if (cdrom_get_last_written(mkdev(MAJOR_NR, i), &scsi_CDs[i].capacity)) #endif - scsi_CDs[i].capacity = 1 + ((buffer[0] << 24) | + SCp->capacity = 1 + ((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); @@ -546,33 +544,45 @@ sector_size = 2048; /* fall through */ case 2048: - scsi_CDs[i].capacity *= 4; + SCp->capacity *= 4; /* fall through */ case 512: break; default: printk("sr%d: unsupported sector size %d.\n", i, sector_size); - scsi_CDs[i].capacity = 0; - scsi_CDs[i].needs_sector_size = 1; + SCp->capacity = 0; + SCp->needs_sector_size = 1; } - scsi_CDs[i].device->sector_size = sector_size; + SCp->device->sector_size = sector_size; /* * Add this so that we have the ability to correctly gauge * what the device is capable of. */ - scsi_CDs[i].needs_sector_size = 0; - sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9); + SCp->needs_sector_size = 0; + sr_sizes[i] = SCp->capacity >> (BLOCK_SIZE_BITS - 9); } - queue = &scsi_CDs[i].device->request_queue; + + queue = &SCp->device->request_queue; blk_queue_hardsect_size(queue, sector_size); +out: kfree(buffer); + return; + +Enomem: + SCp->capacity = 0x1fffff; + sector_size = 2048; /* A guess, just in case */ + SCp->needs_sector_size = 1; + if (SRpnt) + scsi_release_request(SRpnt); + goto out; } void get_capabilities(int i) { + Scsi_CD *SCp; unsigned char cmd[6]; unsigned char *buffer; int rc, n; @@ -589,15 +599,16 @@ "" }; - buffer = (unsigned char *) kmalloc(512, GFP_DMA); + SCp = &scsi_CDs[i]; + buffer = kmalloc(512, GFP_DMA); if (!buffer) { printk(KERN_ERR "sr: out of memory.\n"); return; } cmd[0] = MODE_SENSE; - cmd[1] = (scsi_CDs[i].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[i].device->lun << 5) & 0xe0) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->device->lun << 5) & 0xe0) : 0; cmd[2] = 0x2a; cmd[4] = 128; cmd[3] = cmd[5] = 0; @@ -605,8 +616,8 @@ if (rc) { /* failed, drive doesn't have capabilities mode page */ - scsi_CDs[i].cdi.speed = 1; - scsi_CDs[i].cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R | + SCp->cdi.speed = 1; + SCp->cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R | CDC_DVD | CDC_DVD_RAM | CDC_SELECT_DISC | CDC_SELECT_SPEED); kfree(buffer); @@ -614,13 +625,13 @@ return; } n = buffer[3] + 4; - scsi_CDs[i].cdi.speed = ((buffer[n + 8] << 8) + buffer[n + 9]) / 176; - scsi_CDs[i].readcd_known = 1; - scsi_CDs[i].readcd_cdda = buffer[n + 5] & 0x01; + SCp->cdi.speed = ((buffer[n + 8] << 8) + buffer[n + 9]) / 176; + SCp->readcd_known = 1; + SCp->readcd_cdda = buffer[n + 5] & 0x01; /* print some capability bits */ printk("sr%i: scsi3-mmc drive: %dx/%dx %s%s%s%s%s%s\n", i, ((buffer[n + 14] << 8) + buffer[n + 15]) / 176, - scsi_CDs[i].cdi.speed, + SCp->cdi.speed, buffer[n + 3] & 0x01 ? "writer " : "", /* CD Writer */ buffer[n + 3] & 0x20 ? "dvd-ram " : "", buffer[n + 2] & 0x02 ? "cd/rw " : "", /* can read rewriteable */ @@ -629,38 +640,38 @@ loadmech[buffer[n + 6] >> 5]); if ((buffer[n + 6] >> 5) == 0) /* caddy drives can't close tray... */ - scsi_CDs[i].cdi.mask |= CDC_CLOSE_TRAY; + SCp->cdi.mask |= CDC_CLOSE_TRAY; if ((buffer[n + 2] & 0x8) == 0) /* not a DVD drive */ - scsi_CDs[i].cdi.mask |= CDC_DVD; + SCp->cdi.mask |= CDC_DVD; if ((buffer[n + 3] & 0x20) == 0) { /* can't write DVD-RAM media */ - scsi_CDs[i].cdi.mask |= CDC_DVD_RAM; + SCp->cdi.mask |= CDC_DVD_RAM; } else { - scsi_CDs[i].device->writeable = 1; + SCp->device->writeable = 1; } if ((buffer[n + 3] & 0x10) == 0) /* can't write DVD-R media */ - scsi_CDs[i].cdi.mask |= CDC_DVD_R; + SCp->cdi.mask |= CDC_DVD_R; if ((buffer[n + 3] & 0x2) == 0) /* can't write CD-RW media */ - scsi_CDs[i].cdi.mask |= CDC_CD_RW; + SCp->cdi.mask |= CDC_CD_RW; if ((buffer[n + 3] & 0x1) == 0) /* can't write CD-R media */ - scsi_CDs[i].cdi.mask |= CDC_CD_R; + SCp->cdi.mask |= CDC_CD_R; if ((buffer[n + 6] & 0x8) == 0) /* can't eject */ - scsi_CDs[i].cdi.mask |= CDC_OPEN_TRAY; + SCp->cdi.mask |= CDC_OPEN_TRAY; if ((buffer[n + 6] >> 5) == mechtype_individual_changer || (buffer[n + 6] >> 5) == mechtype_cartridge_changer) - scsi_CDs[i].cdi.capacity = - cdrom_number_of_slots(&(scsi_CDs[i].cdi)); - if (scsi_CDs[i].cdi.capacity <= 1) + SCp->cdi.capacity = + cdrom_number_of_slots(&SCp->cdi); + if (SCp->cdi.capacity <= 1) /* not a changer */ - scsi_CDs[i].cdi.mask |= CDC_SELECT_DISC; + SCp->cdi.mask |= CDC_SELECT_DISC; /*else I don't think it can close its tray - scsi_CDs[i].cdi.mask |= CDC_CLOSE_TRAY; */ + SCp->cdi.mask |= CDC_CLOSE_TRAY; */ kfree(buffer); } @@ -671,8 +682,9 @@ */ static int sr_packet(struct cdrom_device_info *cdi, struct cdrom_generic_command *cgc) { - Scsi_Device *device = scsi_CDs[minor(cdi->dev)].device; - + Scsi_CD *SCp = cdi->handle; + Scsi_Device *device = SCp->device; + /* set the LUN */ if (device->scsi_level <= SCSI_2) cgc->cmd[1] |= device->lun << 5; @@ -743,47 +755,47 @@ blk_size[MAJOR_NR] = sr_sizes; for (i = 0; i < sr_template.nr_dev; ++i) { + Scsi_CD *SCp = &scsi_CDs[i]; /* If we have already seen this, then skip it. Comes up * with loadable modules. */ - if (scsi_CDs[i].capacity) + if (SCp->capacity) continue; - scsi_CDs[i].capacity = 0x1fffff; - scsi_CDs[i].device->sector_size = 2048; /* A guess, just in case */ - scsi_CDs[i].needs_sector_size = 1; - scsi_CDs[i].device->changed = 1; /* force recheck CD type */ + SCp->capacity = 0x1fffff; + SCp->device->sector_size = 2048;/* A guess, just in case */ + SCp->needs_sector_size = 1; + SCp->device->changed = 1; /* force recheck CD type */ #if 0 /* seems better to leave this for later */ get_sectorsize(i); - printk("Scd sectorsize = %d bytes.\n", scsi_CDs[i].sector_size); + printk("Scd sectorsize = %d bytes.\n", SCp->sector_size); #endif - scsi_CDs[i].use = 1; + SCp->use = 1; - scsi_CDs[i].device->ten = 1; - scsi_CDs[i].device->remap = 1; - scsi_CDs[i].readcd_known = 0; - scsi_CDs[i].readcd_cdda = 0; - sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9); - - scsi_CDs[i].cdi.ops = &sr_dops; - scsi_CDs[i].cdi.handle = &scsi_CDs[i]; - scsi_CDs[i].cdi.dev = mk_kdev(MAJOR_NR, i); - scsi_CDs[i].cdi.mask = 0; - scsi_CDs[i].cdi.capacity = 1; + SCp->device->ten = 1; + SCp->device->remap = 1; + SCp->readcd_known = 0; + SCp->readcd_cdda = 0; + sr_sizes[i] = SCp->capacity >> (BLOCK_SIZE_BITS - 9); + + SCp->cdi.ops = &sr_dops; + SCp->cdi.handle = SCp; + SCp->cdi.dev = mk_kdev(MAJOR_NR, i); + SCp->cdi.mask = 0; + SCp->cdi.capacity = 1; /* * FIXME: someone needs to handle a get_capabilities * failure properly ?? */ get_capabilities(i); - sr_vendor_init(i); + sr_vendor_init(SCp); sprintf(name, "sr%d", i); - strcpy(scsi_CDs[i].cdi.name, name); - scsi_CDs[i].cdi.de = - devfs_register (scsi_CDs[i].device->de, "cd", + strcpy(SCp->cdi.name, name); + SCp->cdi.de = devfs_register(SCp->device->de, "cd", DEVFS_FL_DEFAULT, MAJOR_NR, i, S_IFBLK | S_IRUGO | S_IWUGO, &sr_bdops, NULL); - register_cdrom(&scsi_CDs[i].cdi); + register_cdrom(&SCp->cdi); } } diff -Nru a/drivers/scsi/sr.h b/drivers/scsi/sr.h --- a/drivers/scsi/sr.h Thu Mar 7 18:17:42 2002 +++ b/drivers/scsi/sr.h Thu Mar 7 18:17:42 2002 @@ -53,7 +53,7 @@ int sr_is_xa(int minor); /* sr_vendor.c */ -void sr_vendor_init(int minor); +void sr_vendor_init(Scsi_CD *); int sr_cd_check(struct cdrom_device_info *); int sr_set_blocklength(int minor, int blocklength); diff -Nru a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c --- a/drivers/scsi/sr_ioctl.c Thu Mar 7 18:17:38 2002 +++ b/drivers/scsi/sr_ioctl.c Thu Mar 7 18:17:38 2002 @@ -48,7 +48,7 @@ if (ti->cdti_trk1 == ntracks) ti->cdti_trk1 = CDROM_LEADOUT; - else + else if (ti->cdti_trk1 != CDROM_LEADOUT) ti->cdti_trk1 ++; trk0_te.cdte_track = ti->cdti_trk0; @@ -84,7 +84,7 @@ char *bounce_buffer; SDev = scsi_CDs[target].device; - SRpnt = scsi_allocate_request(scsi_CDs[target].device); + SRpnt = scsi_allocate_request(SDev); if (!SRpnt) { printk("Unable to allocate SCSI request in sr_do_ioctl"); return -ENOMEM; @@ -124,7 +124,7 @@ if (driver_byte(result) != 0) { switch (SRpnt->sr_sense_buffer[2] & 0xf) { case UNIT_ATTENTION: - scsi_CDs[target].device->changed = 1; + SDev->changed = 1; if (!quiet) printk(KERN_INFO "sr%d: disc change detected.\n", target); if (retries++ < 10) @@ -192,22 +192,25 @@ static int test_unit_ready(int minor) { + Scsi_CD *SCp; u_char sr_cmd[10]; + SCp = &scsi_CDs[minor]; sr_cmd[0] = GPCMD_TEST_UNIT_READY; - sr_cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[minor].device->lun) << 5) : 0; + sr_cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->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); } int sr_tray_move(struct cdrom_device_info *cdi, int pos) { + Scsi_CD *SCp = cdi->handle; u_char sr_cmd[10]; sr_cmd[0] = GPCMD_START_STOP_UNIT; - sr_cmd[1] = (scsi_CDs[minor(cdi->dev)].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[minor(cdi->dev)].device->lun) << 5) : 0; + sr_cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->device->lun) << 5) : 0; sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0; sr_cmd[4] = (pos == 0) ? 0x03 /* close */ : 0x02 /* eject */ ; @@ -216,9 +219,10 @@ int sr_lock_door(struct cdrom_device_info *cdi, int lock) { - return scsi_ioctl(scsi_CDs[minor(cdi->dev)].device, - lock ? SCSI_IOCTL_DOORLOCK : SCSI_IOCTL_DOORUNLOCK, - 0); + Scsi_CD *SCp = cdi->handle; + + return scsi_ioctl(SCp->device, lock ? SCSI_IOCTL_DOORLOCK : + SCSI_IOCTL_DOORUNLOCK, 0); } int sr_drive_status(struct cdrom_device_info *cdi, int slot) @@ -235,6 +239,7 @@ int sr_disk_status(struct cdrom_device_info *cdi) { + Scsi_CD *SCp = cdi->handle; struct cdrom_tochdr toc_h; struct cdrom_tocentry toc_e; int i, rc, have_datatracks = 0; @@ -256,7 +261,7 @@ if (!have_datatracks) return CDS_AUDIO; - if (scsi_CDs[minor(cdi->dev)].xa_flag) + if (SCp->xa_flag) return CDS_XA_2_1; else return CDS_DATA_1; @@ -265,22 +270,24 @@ int sr_get_last_session(struct cdrom_device_info *cdi, struct cdrom_multisession *ms_info) { - ms_info->addr.lba = scsi_CDs[minor(cdi->dev)].ms_offset; - ms_info->xa_flag = scsi_CDs[minor(cdi->dev)].xa_flag || - (scsi_CDs[minor(cdi->dev)].ms_offset > 0); + Scsi_CD *SCp = cdi->handle; + + ms_info->addr.lba = SCp->ms_offset; + ms_info->xa_flag = SCp->xa_flag || SCp->ms_offset > 0; return 0; } int sr_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn) { + Scsi_CD *SCp = cdi->handle; u_char sr_cmd[10]; char buffer[32]; int result; sr_cmd[0] = GPCMD_READ_SUBCHANNEL; - sr_cmd[1] = (scsi_CDs[minor(cdi->dev)].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[minor(cdi->dev)].device->lun) << 5) : 0; + sr_cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->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; @@ -305,6 +312,7 @@ int sr_select_speed(struct cdrom_device_info *cdi, int speed) { + Scsi_CD *SCp = cdi->handle; u_char sr_cmd[MAX_COMMAND_SIZE]; if (speed == 0) @@ -314,8 +322,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->scsi_level <= SCSI_2) ? - ((scsi_CDs[minor(cdi->dev)].device->lun) << 5) : 0; + sr_cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->device->lun) << 5) : 0; sr_cmd[2] = (speed >> 8) & 0xff; /* MSB for speed (in kbytes/sec) */ sr_cmd[3] = speed & 0xff; /* LSB */ @@ -332,6 +340,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg) { + Scsi_CD *SCp = cdi->handle; u_char sr_cmd[10]; int result, target = minor(cdi->dev); unsigned char buffer[32]; @@ -344,8 +353,8 @@ struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg; sr_cmd[0] = GPCMD_READ_TOC_PMA_ATIP; - sr_cmd[1] = (scsi_CDs[target].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[target].device->lun) << 5) : 0; + sr_cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->device->lun) << 5) : 0; sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0; sr_cmd[8] = 12; /* LSB of length */ @@ -362,8 +371,8 @@ struct cdrom_tocentry *tocentry = (struct cdrom_tocentry *) arg; sr_cmd[0] = GPCMD_READ_TOC_PMA_ATIP; - sr_cmd[1] = (scsi_CDs[target].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[target].device->lun) << 5) : 0; + sr_cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->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; @@ -389,8 +398,8 @@ struct cdrom_ti* ti = (struct cdrom_ti*)arg; sr_cmd[0] = GPCMD_PLAYAUDIO_TI; - sr_cmd[1] = (scsi_CDs[target].device->scsi_level <= SCSI_2) ? - (scsi_CDs[target].device->lun << 5) : 0; + sr_cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; sr_cmd[4] = ti->cdti_trk0; sr_cmd[5] = ti->cdti_ind0; sr_cmd[7] = ti->cdti_trk1; @@ -432,6 +441,7 @@ int sr_read_cd(int minor, unsigned char *dest, int lba, int format, int blksize) { unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_CD *SCp = &scsi_CDs[minor]; #ifdef DEBUG printk("sr%d: sr_read_cd lba=%d format=%d blksize=%d\n", @@ -440,8 +450,8 @@ memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = GPCMD_READ_CD; /* READ_CD */ - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[1] |= ((format & 7) << 2); cmd[2] = (unsigned char) (lba >> 24) & 0xff; cmd[3] = (unsigned char) (lba >> 16) & 0xff; @@ -472,19 +482,20 @@ int sr_read_sector(int minor, int lba, int blksize, unsigned char *dest) { unsigned char cmd[MAX_COMMAND_SIZE]; /* the scsi-command */ + Scsi_CD *SCp = &scsi_CDs[minor]; int rc; /* we try the READ CD command first... */ - if (scsi_CDs[minor].readcd_known) { + if (SCp->readcd_known) { rc = sr_read_cd(minor, dest, lba, 0, blksize); if (-EDRIVE_CANT_DO_THIS != rc) return rc; - scsi_CDs[minor].readcd_known = 0; + SCp->readcd_known = 0; printk("CDROM does'nt support READ CD (0xbe) command\n"); /* fall & retry the other way */ } /* ... if this fails, we switch the blocksize using MODE SELECT */ - if (blksize != scsi_CDs[minor].device->sector_size) { + if (blksize != SCp->device->sector_size) { if (0 != (rc = sr_set_blocklength(minor, blksize))) return rc; } @@ -494,8 +505,8 @@ memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = GPCMD_READ_10; - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->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; @@ -514,6 +525,7 @@ int sr_is_xa(int minor) { unsigned char *raw_sector; + Scsi_CD *SCp = &scsi_CDs[minor]; int is_xa; if (!xa_test) @@ -522,7 +534,7 @@ raw_sector = (unsigned char *) kmalloc(2048, GFP_DMA | GFP_KERNEL); if (!raw_sector) return -ENOMEM; - if (0 == sr_read_sector(minor, scsi_CDs[minor].ms_offset + 16, + if (0 == sr_read_sector(minor, SCp->ms_offset + 16, CD_FRAMESIZE_RAW1, raw_sector)) { is_xa = (raw_sector[3] == 0x02) ? 1 : 0; } else { @@ -539,23 +551,16 @@ int sr_dev_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, unsigned long arg) { - int target; - - target = minor(cdi->dev); + Scsi_CD *SCp = cdi->handle; switch (cmd) { case BLKGETSIZE: - return put_user(scsi_CDs[target].capacity, (unsigned long *) arg); + return put_user(SCp->capacity, (unsigned long *) arg); case BLKGETSIZE64: - return put_user((u64)scsi_CDs[target].capacity << 9, (u64 *)arg); - case BLKROSET: - case BLKROGET: - case BLKFLSBUF: - case BLKSSZGET: - return blk_ioctl(cdi->dev, cmd, arg); + return put_user((u64)SCp->capacity << 9, (u64 *)arg); default: - return scsi_ioctl(scsi_CDs[target].device, cmd, (void *) arg); + return scsi_ioctl(SCp->device, cmd, (void *)arg); } } diff -Nru a/drivers/scsi/sr_vendor.c b/drivers/scsi/sr_vendor.c --- a/drivers/scsi/sr_vendor.c Thu Mar 7 18:17:44 2002 +++ b/drivers/scsi/sr_vendor.c Thu Mar 7 18:17:44 2002 @@ -58,27 +58,25 @@ #define VENDOR_TOSHIBA 3 #define VENDOR_WRITER 4 /* pre-scsi3 writers */ -#define VENDOR_ID (scsi_CDs[minor].vendor) - -void sr_vendor_init(int minor) +void sr_vendor_init(Scsi_CD *SCp) { #ifndef CONFIG_BLK_DEV_SR_VENDOR - VENDOR_ID = VENDOR_SCSI3; + SCp->vendor = VENDOR_SCSI3; #else - char *vendor = scsi_CDs[minor].device->vendor; - char *model = scsi_CDs[minor].device->model; - + char *vendor = SCp->device->vendor; + char *model = SCp->device->model; + /* default */ - VENDOR_ID = VENDOR_SCSI3; - if (scsi_CDs[minor].readcd_known) + SCp->vendor = VENDOR_SCSI3; + if (SCp->readcd_known) /* this is true for scsi3/mmc drives - no more checks */ return; - if (scsi_CDs[minor].device->type == TYPE_WORM) { - VENDOR_ID = VENDOR_WRITER; + if (SCp->device->type == TYPE_WORM) { + SCp->vendor = VENDOR_WRITER; } else if (!strncmp(vendor, "NEC", 3)) { - VENDOR_ID = VENDOR_NEC; + SCp->vendor = VENDOR_NEC; if (!strncmp(model, "CD-ROM DRIVE:25", 15) || !strncmp(model, "CD-ROM DRIVE:36", 15) || !strncmp(model, "CD-ROM DRIVE:83", 15) || @@ -90,10 +88,10 @@ #endif ) /* these can't handle multisession, may hang */ - scsi_CDs[minor].cdi.mask |= CDC_MULTI_SESSION; + SCp->cdi.mask |= CDC_MULTI_SESSION; } else if (!strncmp(vendor, "TOSHIBA", 7)) { - VENDOR_ID = VENDOR_TOSHIBA; + SCp->vendor = VENDOR_TOSHIBA; } #endif @@ -108,10 +106,11 @@ unsigned char *buffer; /* the buffer for the ioctl */ unsigned char cmd[MAX_COMMAND_SIZE]; /* the scsi-command */ struct ccs_modesel_head *modesel; + Scsi_CD *SCp = &scsi_CDs[minor]; int rc, density = 0; #ifdef CONFIG_BLK_DEV_SR_VENDOR - if (VENDOR_ID == VENDOR_TOSHIBA) + if (SCp->vendor == VENDOR_TOSHIBA) density = (blocklength > 2048) ? 0x81 : 0x83; #endif @@ -124,8 +123,8 @@ #endif memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = MODE_SELECT; - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[1] |= (1 << 4); cmd[4] = 12; modesel = (struct ccs_modesel_head *) buffer; @@ -135,7 +134,7 @@ modesel->block_length_med = (blocklength >> 8) & 0xff; modesel->block_length_lo = blocklength & 0xff; if (0 == (rc = sr_do_ioctl(minor, cmd, buffer, sizeof(*modesel), 0, SCSI_DATA_WRITE, NULL))) { - scsi_CDs[minor].device->sector_size = blocklength; + SCp->device->sector_size = blocklength; } #ifdef DEBUG else @@ -153,13 +152,14 @@ int sr_cd_check(struct cdrom_device_info *cdi) { + Scsi_CD *SCp = cdi->handle; unsigned long sector; unsigned char *buffer; /* the buffer for the ioctl */ unsigned char cmd[MAX_COMMAND_SIZE]; /* the scsi-command */ int rc, no_multi, minor; minor = minor(cdi->dev); - if (scsi_CDs[minor].cdi.mask & CDC_MULTI_SESSION) + if (SCp->cdi.mask & CDC_MULTI_SESSION) return 0; buffer = (unsigned char *) kmalloc(512, GFP_KERNEL | GFP_DMA); @@ -170,13 +170,13 @@ no_multi = 0; /* flag: the drive can't handle multisession */ rc = 0; - switch (VENDOR_ID) { + switch (SCp->vendor) { case VENDOR_SCSI3: memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = READ_TOC; - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[8] = 12; cmd[9] = 0x40; rc = sr_do_ioctl(minor, cmd, buffer, 12, 1, SCSI_DATA_READ, NULL); @@ -201,8 +201,8 @@ unsigned long min, sec, frame; memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = 0xde; - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[1] |= 0x03; cmd[2] = 0xb0; rc = sr_do_ioctl(minor, cmd, buffer, 0x16, 1, SCSI_DATA_READ, NULL); @@ -228,8 +228,8 @@ * where starts the last session ?) */ memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = 0xc7; - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[1] |= 0x03; rc = sr_do_ioctl(minor, cmd, buffer, 4, 1, SCSI_DATA_READ, NULL); if (rc == -EINVAL) { @@ -253,8 +253,8 @@ case VENDOR_WRITER: memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = READ_TOC; - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[8] = 0x04; cmd[9] = 0x40; rc = sr_do_ioctl(minor, cmd, buffer, 0x04, 1, SCSI_DATA_READ, NULL); @@ -267,8 +267,8 @@ break; } cmd[0] = READ_TOC; /* Read TOC */ - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[6] = rc & 0x7f; /* number of last session */ cmd[8] = 0x0c; cmd[9] = 0x40; @@ -285,17 +285,17 @@ /* should not happen */ printk(KERN_WARNING "sr%d: unknown vendor code (%i), not initialized ?\n", - minor, VENDOR_ID); + minor, SCp->vendor); sector = 0; no_multi = 1; break; } - scsi_CDs[minor].ms_offset = sector; - scsi_CDs[minor].xa_flag = 0; + SCp->ms_offset = sector; + SCp->xa_flag = 0; if (CDS_AUDIO != sr_disk_status(cdi) && 1 == sr_is_xa(minor)) - scsi_CDs[minor].xa_flag = 1; + SCp->xa_flag = 1; - if (2048 != scsi_CDs[minor].device->sector_size) { + if (2048 != SCp->device->sector_size) { sr_set_blocklength(minor, 2048); } if (no_multi) diff -Nru a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c --- a/drivers/scsi/u14-34f.c Thu Mar 7 18:17:41 2002 +++ b/drivers/scsi/u14-34f.c Thu Mar 7 18:17:41 2002 @@ -1,6 +1,10 @@ /* * u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters. * + * 20 Feb 2002 Rev. 7.22 for linux 2.5.5 + * + Remove any reference to virt_to_bus(). + * + Fix pio hang while detecting multiple HBAs. + * * 01 Jan 2002 Rev. 7.20 for linux 2.5.1 * + Use the dynamic DMA mapping API. * @@ -30,7 +34,7 @@ * + When loaded as a module, accepts the new parameter boot_options * which value is a string with the same format of the kernel boot * command line options. A valid example is: - * modprobe u14-34f boot_options=\"0x230,0x340,lc:y,mq:4\" + * modprobe u14-34f 'boot_options="0x230,0x340,lc:y,mq:4"' * * 22 Jul 1999 Rev. 5.00 for linux 2.2.10 and 2.3.11 * + Removed pre-2.2 source code compatibility. @@ -496,11 +500,19 @@ unsigned char adapter_status; /* non-zero indicates HA error */ unsigned char target_status; /* non-zero indicates target error */ unsigned int sense_addr PACKED; + + /* Additional fields begin here. */ Scsi_Cmnd *SCpnt; unsigned int cpp_index; /* cp index */ - struct sg_list *sglist; + + /* All the cp structure is zero filled by queuecommand except the + following CP_TAIL_SIZE bytes, initialized by detect */ + dma_addr_t cp_dma_addr; /* dma handle for this cp structure */ + struct sg_list *sglist; /* pointer to the allocated SG list */ }; +#define CP_TAIL_SIZE (sizeof(struct sglist *) + sizeof(dma_addr_t)) + struct hostdata { struct mscp cp[MAX_MAILBOXES]; /* Mailboxes for this board */ unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */ @@ -508,7 +520,6 @@ unsigned int iocount; /* Total i/o done for this board */ int board_number; /* Number of this board */ char board_name[16]; /* Name of this board */ - char board_id[256]; /* data from INQUIRY on this board */ int in_reset; /* True if board is doing a reset */ int target_to[MAX_TARGET][MAX_CHANNEL]; /* N. of timeout errors on target */ int target_redo[MAX_TARGET][MAX_CHANNEL]; /* If TRUE redo i/o on target */ @@ -518,14 +529,13 @@ struct pci_dev *pdev; /* Always NULL */ unsigned char heads; unsigned char sectors; - - /* slot != 0 for the U24F, slot == 0 for both the U14F and U34F */ - unsigned char slot; + char board_id[256]; /* data from INQUIRY on this board */ }; static struct Scsi_Host *sh[MAX_BOARDS + 1]; static const char *driver_name = "Ux4F"; static char sha[MAX_BOARDS]; +static spinlock_t driver_lock = SPIN_LOCK_UNLOCKED; /* Initialize num_boards so that ihdlr can work while detect is in progress */ static unsigned int num_boards = MAX_BOARDS; @@ -550,8 +560,6 @@ #define H2DEV(x) cpu_to_le32(x) #define DEV2H(x) le32_to_cpu(x) -#define V2DEV(addr) ((addr) ? H2DEV(virt_to_bus((void *)addr)) : 0) - static void do_interrupt_handler(int, void *, struct pt_regs *); static void flush_dev(Scsi_Device *, unsigned long, unsigned int, unsigned int); static int do_trace = FALSE; @@ -644,13 +652,18 @@ static int board_inquiry(unsigned int j) { struct mscp *cpp; + dma_addr_t id_dma_addr; unsigned int time, limit = 0; + id_dma_addr = pci_map_single(HD(j)->pdev, HD(j)->board_id, + sizeof(HD(j)->board_id), PCI_DMA_BIDIRECTIONAL); cpp = &HD(j)->cp[0]; - memset(cpp, 0, sizeof(struct mscp)); + cpp->cp_dma_addr = pci_map_single(HD(j)->pdev, cpp, sizeof(struct mscp), + PCI_DMA_BIDIRECTIONAL); + memset(cpp, 0, sizeof(struct mscp) - CP_TAIL_SIZE); cpp->opcode = OP_HOST_ADAPTER; cpp->xdir = DTD_IN; - cpp->data_address = V2DEV(HD(j)->board_id); + cpp->data_address = H2DEV(id_dma_addr); cpp->data_len = H2DEV(sizeof(HD(j)->board_id)); cpp->cdb_len = 6; cpp->cdb[0] = HA_CMD_INQUIRY; @@ -666,13 +679,15 @@ outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR); /* Store pointer in OGM address bytes */ - outl(V2DEV(cpp), sh[j]->io_port + REG_OGM); + outl(H2DEV(cpp->cp_dma_addr), sh[j]->io_port + REG_OGM); /* Issue OGM interrupt */ outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR); + spin_unlock_irq(&driver_lock); time = jiffies; while ((jiffies - time) < HZ && limit++ < 20000) udelay(100L); + spin_lock_irq(&driver_lock); if (cpp->adapter_status || HD(j)->cp_stat[0] != FREE) { HD(j)->cp_stat[0] = FREE; @@ -680,6 +695,10 @@ return TRUE; } + pci_unmap_single(HD(j)->pdev, cpp->cp_dma_addr, sizeof(struct mscp), + PCI_DMA_BIDIRECTIONAL); + pci_unmap_single(HD(j)->pdev, id_dma_addr, sizeof(HD(j)->board_id), + PCI_DMA_BIDIRECTIONAL); return FALSE; } @@ -817,6 +836,7 @@ HD(j)->heads = mapping_table[config_2.mapping_mode].heads; HD(j)->sectors = mapping_table[config_2.mapping_mode].sectors; HD(j)->subversion = subversion; + HD(j)->pdev = NULL; HD(j)->board_number = j; if (have_old_firmware) sh[j]->sg_tablesize = MAX_SAFE_SGLIST; @@ -864,6 +884,10 @@ else sprintf(dma_name, "DMA %u", dma_channel); for (i = 0; i < sh[j]->can_queue; i++) + HD(j)->cp[i].cp_dma_addr = pci_map_single(HD(j)->pdev, + &HD(j)->cp[i], sizeof(struct mscp), PCI_DMA_BIDIRECTIONAL); + + for (i = 0; i < sh[j]->can_queue; i++) if (! ((&HD(j)->cp[i])->sglist = kmalloc( sh[j]->sg_tablesize * sizeof(struct sg_list), (sh[j]->unchecked_isa_dma ? GFP_DMA : 0) | GFP_ATOMIC))) { @@ -950,6 +974,9 @@ int u14_34f_detect(Scsi_Host_Template *tpnt) { unsigned int j = 0, k; + unsigned long spin_flags; + + spin_lock_irqsave(&driver_lock, spin_flags); tpnt->proc_name = "u14-34f"; @@ -973,6 +1000,7 @@ } num_boards = j; + spin_unlock_irqrestore(&driver_lock, spin_flags); return j; } @@ -994,10 +1022,10 @@ if (!SCpnt->use_sg) { - if (!SCpnt->request_bufflen) - cpp->data_address = V2DEV(SCpnt->request_buffer); + /* If we get here with PCI_DMA_NONE, pci_map_single triggers a BUG() */ + if (!SCpnt->request_bufflen) pci_dir = PCI_DMA_BIDIRECTIONAL; - else if (SCpnt->request_buffer) + if (SCpnt->request_buffer) cpp->data_address = H2DEV(pci_map_single(HD(j)->pdev, SCpnt->request_buffer, SCpnt->request_bufflen, pci_dir)); @@ -1016,7 +1044,8 @@ cpp->sg = TRUE; cpp->use_sg = SCpnt->use_sg; - cpp->data_address = V2DEV(cpp->sglist); + cpp->data_address = H2DEV(pci_map_single(HD(j)->pdev, cpp->sglist, + SCpnt->use_sg * sizeof(struct sg_list), pci_dir)); cpp->data_len = H2DEV(data_len); } @@ -1032,13 +1061,14 @@ pci_unmap_single(HD(j)->pdev, DEV2H(cpp->sense_addr), DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE); - if (SCpnt->use_sg) + if (SCpnt->use_sg) pci_unmap_sg(HD(j)->pdev, SCpnt->request_buffer, SCpnt->use_sg, pci_dir); - else if (DEV2H(cpp->data_address) && DEV2H(cpp->data_len)) - pci_unmap_single(HD(j)->pdev, DEV2H(cpp->data_address), - DEV2H(cpp->data_len), pci_dir); + if (!DEV2H(cpp->data_len)) pci_dir = PCI_DMA_BIDIRECTIONAL; + if (DEV2H(cpp->data_address)) + pci_unmap_single(HD(j)->pdev, DEV2H(cpp->data_address), + DEV2H(cpp->data_len), pci_dir); } static void sync_dma(unsigned int i, unsigned int j) { @@ -1053,14 +1083,15 @@ pci_dma_sync_single(HD(j)->pdev, DEV2H(cpp->sense_addr), DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE); - if (SCpnt->use_sg) - pci_dma_sync_sg(HD(j)->pdev, SCpnt->request_buffer, + if (SCpnt->use_sg) + pci_dma_sync_sg(HD(j)->pdev, SCpnt->request_buffer, SCpnt->use_sg, pci_dir); - else if (DEV2H(cpp->data_address) && DEV2H(cpp->data_len)) - pci_dma_sync_single(HD(j)->pdev, DEV2H(cpp->data_address), - DEV2H(cpp->data_len), pci_dir); + if (!DEV2H(cpp->data_len)) pci_dir = PCI_DMA_BIDIRECTIONAL; + if (DEV2H(cpp->data_address)) + pci_dma_sync_single(HD(j)->pdev, DEV2H(cpp->data_address), + DEV2H(cpp->data_len), pci_dir); } static inline void scsi_to_dev_dir(unsigned int i, unsigned int j) { @@ -1096,7 +1127,7 @@ return; } - if (SCpnt->sc_data_direction != SCSI_DATA_UNKNOWN) + if (SCpnt->sc_data_direction != SCSI_DATA_UNKNOWN) panic("%s: qcomm, invalid SCpnt->sc_data_direction.\n", BN(j)); cpp->xdir = DTD_IN; @@ -1149,7 +1180,7 @@ /* Set pointer to control packet structure */ cpp = &HD(j)->cp[i]; - memset(cpp, 0, sizeof(struct mscp) - sizeof(struct sg_list *)); + memset(cpp, 0, sizeof(struct mscp) - CP_TAIL_SIZE); SCpnt->scsi_done = done; cpp->cpp_index = i; SCpnt->host_scribble = (unsigned char *) &cpp->cpp_index; @@ -1188,7 +1219,7 @@ } /* Store pointer in OGM address bytes */ - outl(V2DEV(cpp), sh[j]->io_port + REG_OGM); + outl(H2DEV(cpp->cp_dma_addr), sh[j]->io_port + REG_OGM); /* Issue OGM interrupt */ outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR); @@ -1598,7 +1629,7 @@ continue; } - outl(V2DEV(cpp), sh[j]->io_port + REG_OGM); + outl(H2DEV(cpp->cp_dma_addr), sh[j]->io_port + REG_OGM); outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR); HD(j)->cp_stat[k] = IN_USE; } @@ -1636,11 +1667,11 @@ /* Find the mailbox to be serviced on this board */ for (i = 0; i < sh[j]->can_queue; i++) - if (V2DEV(&(HD(j)->cp[i])) == ret) break; + if (H2DEV(HD(j)->cp[i].cp_dma_addr) == ret) break; if (i >= sh[j]->can_queue) panic("%s: ihdlr, invalid mscp bus address %p, cp0 %p.\n", BN(j), - (void *)ret, (void *)V2DEV(HD(j)->cp)); + (void *)ret, (void *)H2DEV(HD(j)->cp[0].cp_dma_addr)); cpp = &(HD(j)->cp[i]); spp = cpp; @@ -1839,6 +1870,10 @@ for (i = 0; i < sh[j]->can_queue; i++) if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist); + + for (i = 0; i < sh[j]->can_queue; i++) + pci_unmap_single(HD(j)->pdev, HD(j)->cp[i].cp_dma_addr, + sizeof(struct mscp), PCI_DMA_BIDIRECTIONAL); free_irq(sh[j]->irq, &sha[j]); diff -Nru a/drivers/scsi/u14-34f.h b/drivers/scsi/u14-34f.h --- a/drivers/scsi/u14-34f.h Thu Mar 7 18:17:46 2002 +++ b/drivers/scsi/u14-34f.h Thu Mar 7 18:17:46 2002 @@ -13,7 +13,7 @@ int u14_34f_reset(Scsi_Cmnd *); int u14_34f_biosparam(Disk *, kdev_t, int *); -#define U14_34F_VERSION "7.20.00" +#define U14_34F_VERSION "7.22.00" #define ULTRASTOR_14_34F { \ name: "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \ diff -Nru a/drivers/telephony/Config.help b/drivers/telephony/Config.help --- a/drivers/telephony/Config.help Thu Mar 7 18:17:46 2002 +++ b/drivers/telephony/Config.help Thu Mar 7 18:17:46 2002 @@ -26,3 +26,8 @@ If you do not have any Quicknet telephony cards, you can safely say N here. +CONFIG_PHONE_IXJ_PCMCIA + Say Y here to configure in PCMCIA service support for the Quicknet + cards manufactured by Quicknet Technologies, Inc. This changes the + card initialization code to work with the card manager daemon. + diff -Nru a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c --- a/drivers/telephony/ixj.c Thu Mar 7 18:17:37 2002 +++ b/drivers/telephony/ixj.c Thu Mar 7 18:17:37 2002 @@ -3450,7 +3450,7 @@ j->cidcw_wait = 0; if(!j->flags.cidcw_ack) { if(ixjdebug & 0x0200) { - printk("IXJ cidcw phone%d did not recieve ACK from display %ld\n", j->board, jiffies); + printk("IXJ cidcw phone%d did not receive ACK from display %ld\n", j->board, jiffies); } ixj_post_cid(j); if(j->cid_play_flag) { diff -Nru a/drivers/usb/Config.in b/drivers/usb/Config.in --- a/drivers/usb/Config.in Thu Mar 7 18:17:42 2002 +++ b/drivers/usb/Config.in Thu Mar 7 18:17:42 2002 @@ -8,7 +8,7 @@ if [ "$CONFIG_USB" = "y" -o "$CONFIG_USB" = "m" ]; then bool ' USB verbose debug messages' CONFIG_USB_DEBUG -comment 'Miscellaneous USB options' + comment 'Miscellaneous USB options' bool ' USB device filesystem' CONFIG_USB_DEVICEFS if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool ' Enforce USB bandwidth allocation (EXPERIMENTAL)' CONFIG_USB_BANDWIDTH @@ -16,90 +16,90 @@ define_bool CONFIG_USB_BANDWIDTH n fi bool ' Long timeout for slow-responding devices (some MGE Ellipse UPSes)' CONFIG_USB_LONG_TIMEOUT -fi -comment 'USB Host Controller Drivers' -source drivers/usb/hcd/Config.in -if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then - dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB -fi -if [ "$CONFIG_USB_UHCI" != "y" ]; then - dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB -else - define_bool CONFIG_USB_UHCI_ALT n -fi -dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB + comment 'USB Host Controller Drivers' + source drivers/usb/hcd/Config.in + if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then + dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB + fi + if [ "$CONFIG_USB_UHCI" != "y" ]; then + dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB + else + define_bool CONFIG_USB_UHCI_ALT n + fi + dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB -comment 'USB Device Class drivers' -dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND -dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL -if [ "$CONFIG_SCSI" = "n" ]; then - comment ' SCSI support is needed for USB Storage' -fi -dep_tristate ' USB Mass Storage support' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI - dep_mbool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG $CONFIG_USB_STORAGE - dep_mbool ' Datafab MDCFE-B Compact Flash Reader support' CONFIG_USB_STORAGE_DATAFAB $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL - dep_mbool ' Freecom USB/ATAPI Bridge support' CONFIG_USB_STORAGE_FREECOM $CONFIG_USB_STORAGE - dep_mbool ' ISD-200 USB/ATA Bridge support' CONFIG_USB_STORAGE_ISD200 $CONFIG_USB_STORAGE - dep_mbool ' Microtech CompactFlash/SmartMedia support' CONFIG_USB_STORAGE_DPCM $CONFIG_USB_STORAGE - dep_mbool ' HP CD-Writer 82xx support' CONFIG_USB_STORAGE_HP8200e $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL - dep_mbool ' SanDisk SDDR-09 (and other SmartMedia) support' CONFIG_USB_STORAGE_SDDR09 $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL - dep_mbool ' Lexar Jumpshot Compact Flash Reader' CONFIG_USB_STORAGE_JUMPSHOT $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL -dep_tristate ' USB Modem (CDC ACM) support' CONFIG_USB_ACM $CONFIG_USB -dep_tristate ' USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB - -comment 'USB Human Interface Devices (HID)' -if [ "$CONFIG_INPUT" = "n" ]; then - 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_mbool ' /dev/hiddev raw HID device support (EXPERIMENTAL)' CONFIG_USB_HIDDEV $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 + comment 'USB Device Class drivers' + dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND + dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL + if [ "$CONFIG_SCSI" = "n" ]; then + comment ' SCSI support is needed for USB Storage' + fi + dep_tristate ' USB Mass Storage support' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI + dep_mbool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG $CONFIG_USB_STORAGE + dep_mbool ' Datafab MDCFE-B Compact Flash Reader support' CONFIG_USB_STORAGE_DATAFAB $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL + dep_mbool ' Freecom USB/ATAPI Bridge support' CONFIG_USB_STORAGE_FREECOM $CONFIG_USB_STORAGE + dep_mbool ' ISD-200 USB/ATA Bridge support' CONFIG_USB_STORAGE_ISD200 $CONFIG_USB_STORAGE + dep_mbool ' Microtech CompactFlash/SmartMedia support' CONFIG_USB_STORAGE_DPCM $CONFIG_USB_STORAGE + dep_mbool ' HP CD-Writer 82xx support' CONFIG_USB_STORAGE_HP8200e $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL + dep_mbool ' SanDisk SDDR-09 (and other SmartMedia) support' CONFIG_USB_STORAGE_SDDR09 $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL + dep_mbool ' Lexar Jumpshot Compact Flash Reader' CONFIG_USB_STORAGE_JUMPSHOT $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL + dep_tristate ' USB Modem (CDC ACM) support' CONFIG_USB_ACM $CONFIG_USB + dep_tristate ' USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB + + comment 'USB Human Interface Devices (HID)' + if [ "$CONFIG_INPUT" = "n" ]; then + 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_mbool ' /dev/hiddev raw HID device support (EXPERIMENTAL)' CONFIG_USB_HIDDEV $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 + fi + dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB $CONFIG_INPUT fi - dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB $CONFIG_INPUT -fi -comment 'USB Imaging devices' -dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB -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' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI -dep_tristate ' HP53xx USB scanner support (EXPERIMENTAL)' CONFIG_USB_HPUSBSCSI $CONFIG_USB $CONFIG_SCSI $CONFIG_EXPERIMENTAL - -comment 'USB Multimedia devices' -if [ "$CONFIG_VIDEO_DEV" = "n" ]; then - comment ' Video4Linux support is needed for USB Multimedia device support' -else - 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 ' USB STV680 (Pencam) Camera support' CONFIG_USB_STV680 $CONFIG_USB $CONFIG_VIDEO_DEV - dep_tristate ' USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)' CONFIG_USB_VICAM $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL - dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL - dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB - dep_tristate ' USB Konica Webcam support' CONFIG_USB_KONICAWC $CONFIG_USB $CONFIG_VIDEO_DEV -fi + comment 'USB Imaging devices' + dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB + 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' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI + dep_tristate ' HP53xx USB scanner support (EXPERIMENTAL)' CONFIG_USB_HPUSBSCSI $CONFIG_USB $CONFIG_SCSI $CONFIG_EXPERIMENTAL + + comment 'USB Multimedia devices' + if [ "$CONFIG_VIDEO_DEV" = "n" ]; then + comment ' Video4Linux support is needed for USB Multimedia device support' + else + 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 ' USB STV680 (Pencam) Camera support' CONFIG_USB_STV680 $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)' CONFIG_USB_VICAM $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL + dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL + dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB + dep_tristate ' USB Konica Webcam support' CONFIG_USB_KONICAWC $CONFIG_USB $CONFIG_VIDEO_DEV + fi -comment 'USB Network adaptors' -if [ "$CONFIG_NET" = "n" ]; then - comment ' Networking support is needed for USB Networking device support' -else - dep_tristate ' USB ADMtek Pegasus-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $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 CATC NetMate-based Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CATC $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL - dep_tristate ' USB Communication Class Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CDCETHER $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL - dep_tristate ' USB-to-USB Networking cable device support (EXPERIMENTAL)' CONFIG_USB_USBNET $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL -fi + comment 'USB Network adaptors' + if [ "$CONFIG_NET" = "n" ]; then + comment ' Networking support is needed for USB Networking device support' + else + dep_tristate ' USB ADMtek Pegasus-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $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 CATC NetMate-based Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CATC $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USB Communication Class Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CDCETHER $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USB-to-USB Networking cable device support (EXPERIMENTAL)' CONFIG_USB_USBNET $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + fi -comment 'USB port drivers' -dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT -source drivers/usb/serial/Config.in - -comment 'USB Miscellaneous drivers' -dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB $CONFIG_EXPERIMENTAL -dep_tristate ' USB Auerswald ISDN support (EXPERIMENTAL)' CONFIG_USB_AUERSWALD $CONFIG_USB $CONFIG_EXPERIMENTAL + comment 'USB port drivers' + dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT + source drivers/usb/serial/Config.in + + comment 'USB Miscellaneous drivers' + dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB $CONFIG_EXPERIMENTAL + dep_tristate ' USB Auerswald ISDN support (EXPERIMENTAL)' CONFIG_USB_AUERSWALD $CONFIG_USB $CONFIG_EXPERIMENTAL +fi endmenu diff -Nru a/drivers/usb/audio.c b/drivers/usb/audio.c --- a/drivers/usb/audio.c Thu Mar 7 18:17:43 2002 +++ b/drivers/usb/audio.c Thu Mar 7 18:17:43 2002 @@ -124,9 +124,7 @@ * conversions. We never do sample rate conversion; these are too * expensive to be performed in the kernel. * - * Current status: - * - Pretty stable on UHCI-Acher/Fliegl/Sailer - * - Does not work on OHCI due to lack of OHCI driver supporting URB's + * Current status: no known HCD-specific issues. * * Generally: Due to the brokenness of the Audio Class spec * it seems generally impossible to write a generic Audio Class driver, @@ -298,12 +296,10 @@ struct my_data_urb { struct urb *urb; - struct usb_iso_packet_descriptor isoframe[DESCFRAMES]; }; struct my_sync_urb { struct urb *urb; - struct usb_iso_packet_descriptor isoframe[SYNCFRAMES]; }; @@ -2829,14 +2825,14 @@ init_waitqueue_head(&as->usbin.dma.wait); init_waitqueue_head(&as->usbout.dma.wait); spin_lock_init(&as->lock); - as->usbin.durb[0].urb = usb_alloc_urb(0, GFP_KERNEL); - as->usbin.durb[1].urb = usb_alloc_urb(0, GFP_KERNEL); - as->usbin.surb[0].urb = usb_alloc_urb(0, GFP_KERNEL); - as->usbin.surb[1].urb = usb_alloc_urb(0, GFP_KERNEL); - as->usbout.durb[0].urb = usb_alloc_urb(0, GFP_KERNEL); - as->usbout.durb[1].urb = usb_alloc_urb(0, GFP_KERNEL); - as->usbout.surb[0].urb = usb_alloc_urb(0, GFP_KERNEL); - as->usbout.surb[1].urb = usb_alloc_urb(0, GFP_KERNEL); + as->usbin.durb[0].urb = usb_alloc_urb (DESCFRAMES, GFP_KERNEL); + as->usbin.durb[1].urb = usb_alloc_urb (DESCFRAMES, GFP_KERNEL); + as->usbin.surb[0].urb = usb_alloc_urb (SYNCFRAMES, GFP_KERNEL); + as->usbin.surb[1].urb = usb_alloc_urb (SYNCFRAMES, GFP_KERNEL); + as->usbout.durb[0].urb = usb_alloc_urb (DESCFRAMES, GFP_KERNEL); + as->usbout.durb[1].urb = usb_alloc_urb (DESCFRAMES, GFP_KERNEL); + as->usbout.surb[0].urb = usb_alloc_urb (SYNCFRAMES, GFP_KERNEL); + as->usbout.surb[1].urb = usb_alloc_urb (SYNCFRAMES, GFP_KERNEL); if ((!as->usbin.durb[0].urb) || (!as->usbin.durb[1].urb) || (!as->usbin.surb[0].urb) || diff -Nru a/drivers/usb/auerswald.c b/drivers/usb/auerswald.c --- a/drivers/usb/auerswald.c Thu Mar 7 18:17:42 2002 +++ b/drivers/usb/auerswald.c Thu Mar 7 18:17:42 2002 @@ -61,7 +61,7 @@ #define ID_AUERSWALD 0x09BF #ifndef AUER_MINOR_BASE /* allow external override */ -#define AUER_MINOR_BASE 80 /* auerswald driver minor number */ +#define AUER_MINOR_BASE 112 /* auerswald driver minor number */ #endif /* we can have up to this number of device plugged in at once */ diff -Nru a/drivers/usb/devices.c b/drivers/usb/devices.c --- a/drivers/usb/devices.c Thu Mar 7 18:17:43 2002 +++ b/drivers/usb/devices.c Thu Mar 7 18:17:43 2002 @@ -4,8 +4,6 @@ * (C) Copyright 1999,2000 Thomas Sailer . (proc file per device) * (C) Copyright 1999 Deti Fliegl (new USB architecture) * - * $id$ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -61,6 +59,8 @@ #include #include +#include "hcd.h" + #define MAX_TOPO_LEVEL 6 /* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */ @@ -429,6 +429,10 @@ * count = device count at this level */ /* If this is the root hub, display the bandwidth information */ + /* FIXME high speed reserves 20% frametime for non-periodic, + * while full/low speed reserves only 10% ... so this is wrong + * for high speed busses. also, change how bandwidth is recorded. + */ if (level == 0) data_end += sprintf(data_end, format_bandwidth, bus->bandwidth_allocated, FRAME_TIME_MAX_USECS_ALLOC, diff -Nru a/drivers/usb/hcd/Config.help b/drivers/usb/hcd/Config.help --- a/drivers/usb/hcd/Config.help Thu Mar 7 18:17:37 2002 +++ b/drivers/usb/hcd/Config.help Thu Mar 7 18:17:37 2002 @@ -21,7 +21,6 @@ The module will be called ehci-hcd.o. If you want to compile it as a module, say M here and read . -OHCI (most USB hosts except VIA, Intel PIIX) support CONFIG_USB_OHCI_HCD The Open Host Controller Interface (OHCI) is a standard for accessing USB 1.1 host controller hardware. It does more in hardware than Intel's diff -Nru a/drivers/usb/hcd/ehci-hcd.c b/drivers/usb/hcd/ehci-hcd.c --- a/drivers/usb/hcd/ehci-hcd.c Thu Mar 7 18:17:45 2002 +++ b/drivers/usb/hcd/ehci-hcd.c Thu Mar 7 18:17:45 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2001 by David Brownell + * Copyright (c) 2000-2002 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -31,10 +31,6 @@ #include #include -#ifndef CONFIG_USB_DEBUG - #define CONFIG_USB_DEBUG /* this is still experimental! */ -#endif - #ifdef CONFIG_USB_DEBUG #define DEBUG #else @@ -44,6 +40,7 @@ #include #include "../hcd.h" +#include #include #include #include @@ -72,19 +69,25 @@ * ... * * HISTORY: + * + * 2002-03-05 Initial high-speed ISO support; reduce ITD memory; shift + * more checking to generic hcd framework (db). Make it work with + * Philips EHCI; reduce PCI traffic; shorten IRQ path (Rory Bolt). * 2002-01-14 Minor cleanup; version synch. * 2002-01-08 Fix roothub handoff of FS/LS to companion controllers. * 2002-01-04 Control/Bulk queuing behaves. + * * 2001-12-12 Initial patch version for Linux 2.5.1 kernel. + * 2001-June Works with usb-storage and NEC EHCI on 2.4 */ -#define DRIVER_VERSION "$Revision: 0.26 $" +#define DRIVER_VERSION "$Revision: 0.27 $" #define DRIVER_AUTHOR "David Brownell" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" // #define EHCI_VERBOSE_DEBUG -// #define have_iso +// #define have_split_iso #ifdef CONFIG_DEBUG_SLAB # define EHCI_SLAB_FLAGS (SLAB_POISON) @@ -186,6 +189,9 @@ dbg_hcs_params (ehci, "ehci_start"); dbg_hcc_params (ehci, "ehci_start"); + /* cache this readonly data; minimize PCI reads */ + ehci->hcs_params = readl (&ehci->caps->hcs_params); + /* * hw default: 1K periodic list heads, one per frame. * periodic_size can shrink by USBCMD update if hcc_params allows. @@ -203,7 +209,7 @@ ehci->async = 0; ehci->reclaim = 0; - ehci->next_frame = -1; + ehci->next_uframe = -1; /* controller state: unknown --> reset */ @@ -309,7 +315,7 @@ // root hub is shut down separately (first, when possible) scan_async (ehci); - if (ehci->next_frame != -1) + if (ehci->next_uframe != -1) scan_periodic (ehci); ehci_mem_cleanup (ehci); @@ -331,14 +337,12 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 params; int ports; int i; dbg ("%s: suspend to %d", hcd->bus_name, state); - params = readl (&ehci->caps->hcs_params); - ports = HCS_N_PORTS (params); + ports = HCS_N_PORTS (ehci->hcs_params); // FIXME: This assumes what's probably a D3 level suspend... @@ -374,14 +378,12 @@ static int ehci_resume (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 params; int ports; int i; dbg ("%s: resume", hcd->bus_name); - params = readl (&ehci->caps->hcs_params); - ports = HCS_N_PORTS (params); + ports = HCS_N_PORTS (ehci->hcs_params); // FIXME: if controller didn't retain state, // return and let generic code clean it up @@ -425,7 +427,7 @@ if (ehci->reclaim_ready) end_unlink_async (ehci); scan_async (ehci); - if (ehci->next_frame != -1) + if (ehci->next_uframe != -1) scan_periodic (ehci); // FIXME: when nothing is connected to the root hub, @@ -439,20 +441,20 @@ { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 status = readl (&ehci->regs->status); - int bh = 0; + int bh; - /* clear (just) interrupts */ status &= INTR_MASK; + if (!status) /* irq sharing? */ + return; + + /* clear (just) interrupts */ writel (status, &ehci->regs->status); readl (&ehci->regs->command); /* unblock posted write */ - - if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */ - return; + bh = 0; #ifdef EHCI_VERBOSE_DEBUG /* unrequested/ignored: Port Change Detect, Frame List Rollover */ - if (status & INTR_MASK) - dbg_status (ehci, "irq", status); + dbg_status (ehci, "irq", status); #endif /* INT, ERR, and IAA interrupt rates can be throttled */ @@ -519,17 +521,15 @@ return intr_submit (ehci, urb, &qtd_list, mem_flags); case PIPE_ISOCHRONOUS: -#ifdef have_iso if (urb->dev->speed == USB_SPEED_HIGH) - return itd_submit (ehci, urb); + return itd_submit (ehci, urb, mem_flags); +#ifdef have_split_iso else - return sitd_submit (ehci, urb); + return sitd_submit (ehci, urb, mem_flags); #else - // FIXME highspeed iso stuff is written but never run/tested. - // and the split iso support isn't even written yet. - dbg ("no iso support yet"); + dbg ("no split iso support yet"); return -ENOSYS; -#endif /* have_iso */ +#endif /* have_split_iso */ } return 0; diff -Nru a/drivers/usb/hcd/ehci-hub.c b/drivers/usb/hcd/ehci-hub.c --- a/drivers/usb/hcd/ehci-hub.c Thu Mar 7 18:17:43 2002 +++ b/drivers/usb/hcd/ehci-hub.c Thu Mar 7 18:17:43 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -18,8 +18,6 @@ /* this file is part of ehci-hcd.c */ -#include - /*-------------------------------------------------------------------------*/ /* @@ -70,8 +68,7 @@ /* init status to no-changes */ buf [0] = 0; - temp = readl (&ehci->caps->hcs_params); - ports = HCS_N_PORTS (temp); + ports = HCS_N_PORTS (ehci->hcs_params); if (ports > 7) { buf [1] = 0; retval++; @@ -109,8 +106,7 @@ struct ehci_hcd *ehci, struct usb_hub_descriptor *desc ) { - u32 params = readl (&ehci->caps->hcs_params); - int ports = HCS_N_PORTS (params); + int ports = HCS_N_PORTS (ehci->hcs_params); u16 temp; desc->bDescriptorType = 0x29; @@ -126,10 +122,10 @@ memset (&desc->bitmap [temp], 0xff, temp); temp = 0x0008; /* per-port overcurrent reporting */ - if (HCS_PPC (params)) /* per-port power control */ - temp |= 0x0001; - if (HCS_INDICATOR (params)) /* per-port indicators (LEDs) */ - temp |= 0x0080; + if (HCS_PPC (ehci->hcs_params)) + temp |= 0x0001; /* per-port power control */ + if (HCS_INDICATOR (ehci->hcs_params)) + temp |= 0x0080; /* per-port indicators (LEDs) */ desc->wHubCharacteristics = cpu_to_le16 (temp); } @@ -144,8 +140,7 @@ u16 wLength ) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 params = readl (&ehci->caps->hcs_params); - int ports = HCS_N_PORTS (params); + int ports = HCS_N_PORTS (ehci->hcs_params); u32 temp; unsigned long flags; int retval = 0; @@ -191,7 +186,7 @@ /* ? */ break; case USB_PORT_FEAT_POWER: - if (HCS_PPC (params)) + if (HCS_PPC (ehci->hcs_params)) writel (temp & ~PORT_POWER, &ehci->regs->port_status [wIndex]); break; @@ -302,7 +297,7 @@ &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_POWER: - if (HCS_PPC (params)) + if (HCS_PPC (ehci->hcs_params)) writel (temp | PORT_POWER, &ehci->regs->port_status [wIndex]); break; @@ -314,6 +309,13 @@ hcd->bus_name, wIndex + 1); temp |= PORT_OWNER; } else { + /* Philips 1562 wants CMD_RUN to reset */ + if (!HCD_IS_RUNNING(ehci->hcd.state)) { + u32 cmd = readl (&ehci->regs->command); + cmd |= CMD_RUN; + writel (cmd, &ehci->regs->command); + ehci->hcd.state = USB_STATE_RUNNING; + } vdbg ("%s port %d reset", hcd->bus_name, wIndex + 1); temp |= PORT_RESET; diff -Nru a/drivers/usb/hcd/ehci-mem.c b/drivers/usb/hcd/ehci-mem.c --- a/drivers/usb/hcd/ehci-mem.c Thu Mar 7 18:17:42 2002 +++ b/drivers/usb/hcd/ehci-mem.c Thu Mar 7 18:17:42 2002 @@ -18,8 +18,6 @@ /* this file is part of ehci-hcd.c */ -#include - /*-------------------------------------------------------------------------*/ /* diff -Nru a/drivers/usb/hcd/ehci-q.c b/drivers/usb/hcd/ehci-q.c --- a/drivers/usb/hcd/ehci-q.c Thu Mar 7 18:17:40 2002 +++ b/drivers/usb/hcd/ehci-q.c Thu Mar 7 18:17:40 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -18,8 +18,6 @@ /* this file is part of ehci-hcd.c */ -#include - /*-------------------------------------------------------------------------*/ /* @@ -645,12 +643,19 @@ if (usb_pipecontrol (urb->pipe)) { info1 |= 64 << 16; /* usb2 fixed maxpacket */ info1 |= 1 << 14; /* toggle from qtd */ + info2 |= (EHCI_TUNE_MULT_HS << 30); } else if (usb_pipebulk (urb->pipe)) { info1 |= 512 << 16; /* usb2 fixed maxpacket */ info2 |= (EHCI_TUNE_MULT_HS << 30); - } else - info1 |= usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe)) << 16; + } else { + u32 temp; + temp = usb_maxpacket (urb->dev, urb->pipe, + usb_pipeout (urb->pipe)); + info1 |= (temp & 0x3ff) << 16; /* maxpacket */ + /* HS intr can be "high bandwidth" */ + temp = 1 + ((temp >> 11) & 0x03); + info2 |= temp << 30; /* mult */ + } break; default: #ifdef DEBUG diff -Nru a/drivers/usb/hcd/ehci-sched.c b/drivers/usb/hcd/ehci-sched.c --- a/drivers/usb/hcd/ehci-sched.c Thu Mar 7 18:17:41 2002 +++ b/drivers/usb/hcd/ehci-sched.c Thu Mar 7 18:17:41 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -20,8 +20,6 @@ /*-------------------------------------------------------------------------*/ -#include "ehci.h" - /* * EHCI scheduled transaction support: interrupt, iso, split iso * These are called "periodic" transactions in the EHCI spec. @@ -57,12 +55,12 @@ return &periodic->qh->qh_next; case Q_TYPE_FSTN: return &periodic->fstn->fstn_next; -#ifdef have_iso case Q_TYPE_ITD: return &periodic->itd->itd_next; +#ifdef have_split_iso case Q_TYPE_SITD: return &periodic->sitd->sitd_next; -#endif /* have_iso */ +#endif /* have_split_iso */ } dbg ("BAD shadow %p tag %d", periodic->ptr, tag); // BUG (); @@ -111,9 +109,6 @@ u32 *hw_p = &ehci->periodic [frame]; union ehci_shadow *q = &ehci->pshadow [frame]; unsigned usecs = 0; -#ifdef have_iso - u32 temp = 0; -#endif while (q->ptr) { switch (Q_NEXT_TYPE (*hw_p)) { @@ -132,15 +127,13 @@ } q = &q->fstn->fstn_next; break; -#ifdef have_iso case Q_TYPE_ITD: - temp = le32_to_cpu (q->itd->transaction [uframe]); - temp >>= 16; - temp &= 0x0fff; - if (temp) - usecs += HS_USECS_ISO (temp); + /* NOTE the "one uframe per itd" policy */ + if (q->itd->hw_transaction [uframe] != 0) + usecs += q->itd->usecs; q = &q->itd->itd_next; break; +#ifdef have_split_iso case Q_TYPE_SITD: temp = q->sitd->hw_fullspeed_ep & __constant_cpu_to_le32 (1 << 31); @@ -165,7 +158,7 @@ } q = &q->sitd->sitd_next; break; -#endif /* have_iso */ +#endif /* have_split_iso */ default: BUG (); } @@ -180,6 +173,45 @@ /*-------------------------------------------------------------------------*/ +static void enable_periodic (struct ehci_hcd *ehci) +{ + u32 cmd; + + /* did clearing PSE did take effect yet? + * takes effect only at frame boundaries... + */ + while (readl (&ehci->regs->status) & STS_PSS) + udelay (20); + + cmd = readl (&ehci->regs->command) | CMD_PSE; + writel (cmd, &ehci->regs->command); + /* posted write ... PSS happens later */ + ehci->hcd.state = USB_STATE_RUNNING; + + /* make sure tasklet scans these */ + ehci->next_uframe = readl (&ehci->regs->frame_index) + % (ehci->periodic_size << 3); +} + +static void disable_periodic (struct ehci_hcd *ehci) +{ + u32 cmd; + + /* did setting PSE not take effect yet? + * takes effect only at frame boundaries... + */ + while (!(readl (&ehci->regs->status) & STS_PSS)) + udelay (20); + + cmd = readl (&ehci->regs->command) & ~CMD_PSE; + writel (cmd, &ehci->regs->command); + /* posted write ... */ + + ehci->next_uframe = -1; +} + +/*-------------------------------------------------------------------------*/ + static void intr_deschedule ( struct ehci_hcd *ehci, unsigned frame, @@ -201,21 +233,9 @@ ehci->periodic_urbs--; /* maybe turn off periodic schedule */ - if (!ehci->periodic_urbs) { - u32 cmd = readl (&ehci->regs->command); - - /* did setting PSE not take effect yet? - * takes effect only at frame boundaries... - */ - while (!(readl (&ehci->regs->status) & STS_PSS)) - udelay (20); - - cmd &= ~CMD_PSE; - writel (cmd, &ehci->regs->command); - /* posted write ... */ - - ehci->next_frame = -1; - } else + if (!ehci->periodic_urbs) + disable_periodic (ehci); + else vdbg ("periodic schedule still enabled"); spin_unlock_irqrestore (&ehci->lock, flags); @@ -244,7 +264,7 @@ ) { unsigned epnum, period; unsigned temp; - unsigned short mult, usecs; + unsigned short usecs; unsigned long flags; struct ehci_qh *qh; struct hcd_dev *dev; @@ -257,12 +277,7 @@ epnum |= 0x10; } else temp = urb->dev->epmaxpacketout [epnum]; - mult = 1; - if (urb->dev->speed == USB_SPEED_HIGH) { - /* high speed "high bandwidth" is coded in ep maxpacket */ - mult += (temp >> 11) & 0x03; - temp &= 0x03ff; - } else { + if (urb->dev->speed != USB_SPEED_HIGH) { dbg ("no intr/tt scheduling yet"); status = -ENOSYS; goto done; @@ -281,21 +296,12 @@ usecs = HS_USECS (urb->transfer_buffer_length); - /* - * force a power-of-two (frames) sized polling interval - * - * NOTE: endpoint->bInterval for highspeed is measured in uframes, - * while for full/low speeds it's in frames. Here we "know" that - * urb->interval doesn't give acccess to high interrupt rates. - */ - period = ehci->periodic_size; - temp = period; - if (unlikely (urb->interval < 1)) - urb->interval = 1; - while (temp > urb->interval) - temp >>= 1; - period = urb->interval = temp; - + /* FIXME handle HS periods of less than 1 frame. */ + if (urb->interval < 8) + period = 1; + else + period = urb->interval >> 8; + spin_lock_irqsave (&ehci->lock, flags); /* get the qh (must be empty and idle) */ @@ -337,7 +343,6 @@ unsigned frame = urb->interval; qh->hw_next = EHCI_LIST_END; - qh->hw_info2 |= cpu_to_le32 (mult << 30); qh->usecs = usecs; urb->hcpriv = qh_put (qh); @@ -380,7 +385,7 @@ /* stuff into the periodic schedule */ qh->qh_state = QH_STATE_LINKED; - vdbg ("qh %p usecs %d period %d starting frame %d.%d", + vdbg ("qh %p usecs %d period %d starting %d.%d", qh, qh->usecs, period, frame, uframe); do { if (unlikely (ehci->pshadow [frame].ptr != 0)) { @@ -395,24 +400,12 @@ frame += period; } while (frame < ehci->periodic_size); - /* maybe enable periodic schedule processing */ - if (!ehci->periodic_urbs++) { - u32 cmd; - - /* did clearing PSE did take effect yet? - * takes effect only at frame boundaries... - */ - while (readl (&ehci->regs->status) & STS_PSS) - udelay (20); + /* update bandwidth utilization records (for usbfs) */ + usb_claim_bandwidth (urb->dev, urb, usecs, 0); - cmd = readl (&ehci->regs->command) | CMD_PSE; - writel (cmd, &ehci->regs->command); - /* posted write ... PSS happens later */ - ehci->hcd.state = USB_STATE_RUNNING; - - /* make sure tasklet scans these */ - ehci->next_frame = ehci_get_frame (&ehci->hcd); - } + /* maybe enable periodic schedule processing */ + if (!ehci->periodic_urbs++) + enable_periodic (ehci); break; } while (frame); @@ -488,53 +481,56 @@ /*-------------------------------------------------------------------------*/ -#ifdef have_iso - -static inline void itd_free (struct ehci_hcd *ehci, struct ehci_itd *itd) +static void +itd_free_list (struct ehci_hcd *ehci, struct urb *urb) { - pci_pool_free (ehci->itd_pool, itd, itd->itd_dma); + struct ehci_itd *first_itd = urb->hcpriv; + + pci_unmap_single (ehci->hcd.pdev, + first_itd->buf_dma, urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); + while (!list_empty (&first_itd->itd_list)) { + struct ehci_itd *itd; + + itd = list_entry ( + first_itd->itd_list.next, + struct ehci_itd, itd_list); + list_del (&itd->itd_list); + pci_pool_free (ehci->itd_pool, itd, itd->itd_dma); + } + pci_pool_free (ehci->itd_pool, first_itd, first_itd->itd_dma); + urb->hcpriv = 0; } -/* - * Create itd and allocate into uframes within specified frame. - * Caller must update the resulting uframe links. - */ -static struct ehci_itd * -itd_make ( +static int +itd_fill ( struct ehci_hcd *ehci, + struct ehci_itd *itd, struct urb *urb, unsigned index, // urb->iso_frame_desc [index] - unsigned frame, // scheduled start - dma_addr_t dma, // mapped transfer buffer - int mem_flags + dma_addr_t dma // mapped transfer buffer ) { - struct ehci_itd *itd; u64 temp; u32 buf1; - unsigned epnum, maxp, multi, usecs; + unsigned i, epnum, maxp, multi; unsigned length; - unsigned i, bufnum; - - /* allocate itd, start to fill it */ - itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &dma); - if (!itd) - return itd; itd->hw_next = EHCI_LIST_END; itd->urb = urb; itd->index = index; - INIT_LIST_HEAD (&itd->itd_list); - itd->uframe = (frame * 8) % ehci->periodic_size; - /* tell itd about the buffer its transfers will consume */ + /* tell itd about its transfer buffer, max 2 pages */ length = urb->iso_frame_desc [index].length; dma += urb->iso_frame_desc [index].offset; temp = dma & ~0x0fff; - for (i = 0; i < 7; i++) { + for (i = 0; i < 2; i++) { itd->hw_bufp [i] = cpu_to_le32 ((u32) temp); itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32)); - temp += 0x0fff; + temp += 0x1000; } + itd->buf_dma = dma; /* * this might be a "high bandwidth" highspeed endpoint, @@ -543,282 +539,407 @@ epnum = usb_pipeendpoint (urb->pipe); if (usb_pipein (urb->pipe)) { maxp = urb->dev->epmaxpacketin [epnum]; - buf1 = (1 << 11) | maxp; + buf1 = (1 << 11); } else { maxp = urb->dev->epmaxpacketout [epnum]; - buf1 = maxp; + buf1 = 0; } + buf1 |= (maxp & 0x03ff); multi = 1; - multi += (temp >> 11) & 0x03; + multi += (maxp >> 11) & 0x03; maxp &= 0x03ff; + maxp *= multi; + + /* transfer can't fit in any uframe? */ + if (length < 0 || maxp < length) { + dbg ("BAD iso packet: %d bytes, max %d, urb %p [%d] (of %d)", + length, maxp, urb, index, + urb->iso_frame_desc [index].length); + return -ENOSPC; + } + itd->usecs = HS_USECS_ISO (length); /* "plus" info in low order bits of buffer pointers */ itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum); itd->hw_bufp [1] |= cpu_to_le32 (buf1); itd->hw_bufp [2] |= cpu_to_le32 (multi); - /* schedule as many uframes as needed */ - maxp *= multi; - usecs = HS_USECS_ISO (maxp); - bufnum = 0; - for (i = 0; i < 8; i++) { - unsigned t, offset, scratch; + /* figure hw_transaction[] value (it's scheduled later) */ + itd->transaction = EHCI_ISOC_ACTIVE; + itd->transaction |= dma & 0x0fff; /* offset; buffer=0 */ + if ((index + 1) == urb->number_of_packets) + itd->transaction |= EHCI_ITD_IOC; /* end-of-urb irq */ + itd->transaction |= length << 16; + cpu_to_le32s (&itd->transaction); - if (length <= 0) { - itd->hw_transaction [i] = 0; - continue; - } + return 0; +} - /* don't commit more than 80% periodic == 100 usec */ - if ((periodic_usecs (ehci, itd->uframe, i) + usecs) > 100) - continue; +static int +itd_urb_transaction ( + struct ehci_hcd *ehci, + struct urb *urb, + int mem_flags +) { + int frame_index; + struct ehci_itd *first_itd, *itd; + int status; + dma_addr_t buf_dma, itd_dma; - /* we'll use this uframe; figure hw_transaction */ - t = EHCI_ISOC_ACTIVE; - t |= bufnum << 12; // which buffer? - offset = temp & 0x0fff; // offset therein - t |= offset; - if ((offset + maxp) >= 4096) // hc auto-wraps end-of-"page" - bufnum++; - if (length <= maxp) { - // interrupt only needed at end-of-urb - if ((index + 1) == urb->number_of_packets) - t |= EHCI_ITD_IOC; - scratch = length; - } else - scratch = maxp; - t |= scratch << 16; - t = cpu_to_le32 (t); - - itd->hw_transaction [i] = itd->transaction [i] = t; - length -= scratch; - } - if (length > 0) { - dbg ("iso frame too big, urb %p [%d], %d extra (of %d)", - urb, index, length, urb->iso_frame_desc [index].length); - itd_free (ehci, itd); - itd = 0; + /* set up one dma mapping for this urb */ + buf_dma = pci_map_single (ehci->hcd.pdev, + urb->transfer_buffer, urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); + if (buf_dma == 0) + return -ENOMEM; + + /* allocate/init ITDs */ + for (frame_index = 0, first_itd = 0; + frame_index < urb->number_of_packets; + frame_index++) { + itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma); + if (!itd) { + status = -ENOMEM; + goto fail; + } + memset (itd, 0, sizeof *itd); + itd->itd_dma = itd_dma; + + status = itd_fill (ehci, itd, urb, frame_index, buf_dma); + if (status != 0) + goto fail; + + if (first_itd) + list_add_tail (&itd->itd_list, + &first_itd->itd_list); + else { + INIT_LIST_HEAD (&itd->itd_list); + urb->hcpriv = first_itd = itd; + } } - return itd; + urb->error_count = 0; + return 0; + +fail: + if (urb->hcpriv) + itd_free_list (ehci, urb); + return status; } +/*-------------------------------------------------------------------------*/ + static inline void itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) { - u32 ptr; + /* always prepend ITD/SITD ... only QH tree is order-sensitive */ + itd->itd_next = ehci->pshadow [frame]; + itd->hw_next = ehci->periodic [frame]; + ehci->pshadow [frame].itd = itd; + ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD; +} - ptr = cpu_to_le32 (itd->itd_dma); // type 0 == itd - if (ehci->pshadow [frame].ptr) { - if (!itd->itd_next.ptr) { - itd->itd_next = ehci->pshadow [frame]; - itd->hw_next = ehci->periodic [frame]; - } else if (itd->itd_next.ptr != ehci->pshadow [frame].ptr) { - dbg ("frame %d itd link goof", frame); - BUG (); +/* + * return zero on success, else -errno + * - start holds first uframe to start scheduling into + * - max is the first uframe it's NOT (!) OK to start scheduling into + * math to be done modulo "mod" (ehci->periodic_size << 3) + */ +static int get_iso_range ( + struct ehci_hcd *ehci, + struct urb *urb, + unsigned *start, + unsigned *max, + unsigned mod +) { + struct list_head *lh; + struct hcd_dev *dev = urb->dev->hcpriv; + int last = -1; + unsigned now, span, end; + + span = urb->interval * urb->number_of_packets; + + /* first see if we know when the next transfer SHOULD happen */ + list_for_each (lh, &dev->urb_list) { + struct urb *u; + struct ehci_itd *itd; + unsigned s; + + u = list_entry (lh, struct urb, urb_list); + if (u == urb || u->pipe != urb->pipe) + continue; + if (u->interval != urb->interval) { /* must not change! */ + dbg ("urb %p interval %d ... != %p interval %d", + u, u->interval, urb, urb->interval); + return -EINVAL; + } + + /* URB for this endpoint... covers through when? */ + itd = urb->hcpriv; + s = itd->uframe + u->interval * u->number_of_packets; + if (last < 0) + last = s; + else { + /* + * So far we can only queue two ISO URBs... + * + * FIXME do interval math, figure out whether + * this URB is "before" or not ... also, handle + * the case where the URB might have completed, + * but hasn't yet been processed. + */ + dbg ("NYET: queue >2 URBs per ISO endpoint"); + return -EDOM; } } - ehci->pshadow [frame].itd = itd; - ehci->periodic [frame] = ptr; -} -#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) + /* calculate the legal range [start,max) */ + now = readl (&ehci->regs->frame_index) + 1; /* next uframe */ + if (!ehci->periodic_urbs) + now += 8; /* startup delay */ + now %= mod; + end = now + mod; + if (last < 0) { + *start = now + ehci->i_thresh + /* paranoia */ 1; + *max = end - span; + if (*max < *start + 1) + *max = *start + 1; + } else { + *start = last % mod; + *max = (last + 1) % mod; + } -static unsigned long -itd_complete (struct ehci_hcd *ehci, struct ehci_itd *itd, unsigned long flags) + /* explicit start frame? */ + if (!(urb->transfer_flags & USB_ISO_ASAP)) { + unsigned temp; + + /* sanity check: must be in range */ + urb->start_frame %= ehci->periodic_size; + temp = urb->start_frame << 3; + if (temp < *start) + temp += mod; + if (temp > *max) + return -EDOM; + + /* use that explicit start frame */ + *start = urb->start_frame << 3; + temp += 8; + if (temp < *max) + *max = temp; + } + + // FIXME minimize wraparound to "now" ... insist max+span + // (and start+span) remains a few frames short of "end" + + *max %= ehci->periodic_size; + if ((*start + span) < end) + return 0; + return -EFBIG; +} + +static int +itd_schedule (struct ehci_hcd *ehci, struct urb *urb) { - struct urb *urb = itd->urb; + unsigned start, max, i; + int status; + unsigned mod = ehci->periodic_size << 3; - /* if not unlinking: */ - if (!(urb->transfer_flags & EHCI_STATE_UNLINK) - && ehci->hcd.state != USB_STATE_HALT) { - int i; - struct usb_iso_packet_descriptor *desc; - struct ehci_itd *first_itd = urb->hcpriv; + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc [i].status = -EINPROGRESS; + urb->iso_frame_desc [i].actual_length = 0; + } - /* update status for this frame's transfers */ - desc = &urb->iso_frame_desc [itd->index]; - desc->status = 0; - desc->actual_length = 0; - for (i = 0; i < 8; i++) { - u32 t = itd->hw_transaction [i]; - if (t & (ISO_ERRS | EHCI_ISOC_ACTIVE)) { - if (t & EHCI_ISOC_ACTIVE) - desc->status = -EXDEV; - else if (t & EHCI_ISOC_BUF_ERR) - desc->status = usb_pipein (urb->pipe) - ? -ENOSR /* couldn't read */ - : -ECOMM; /* couldn't write */ - else if (t & EHCI_ISOC_BABBLE) - desc->status = -EOVERFLOW; - else /* (t & EHCI_ISOC_XACTERR) */ - desc->status = -EPROTO; + if ((status = get_iso_range (ehci, urb, &start, &max, mod)) != 0) + return status; + + do { + unsigned uframe; + unsigned usecs; + struct ehci_itd *itd; + + /* check schedule: enough space? */ + itd = urb->hcpriv; + uframe = start; + for (i = 0, uframe = start; + i < urb->number_of_packets; + i++, uframe += urb->interval) { + uframe %= mod; + + /* can't commit more than 80% periodic == 100 usec */ + if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7) + > (100 - itd->usecs)) { + itd = 0; break; } - desc->actual_length += EHCI_ITD_LENGTH (t); + itd = list_entry (itd->itd_list.next, + struct ehci_itd, itd_list); + } + if (!itd) + continue; + + /* that's where we'll schedule this! */ + itd = urb->hcpriv; + urb->start_frame = start >> 3; + vdbg ("ISO urb %p (%d packets period %d) starting %d.%d", + urb, urb->number_of_packets, urb->interval, + urb->start_frame, start & 0x7); + for (i = 0, uframe = start, usecs = 0; + i < urb->number_of_packets; + i++, uframe += urb->interval) { + uframe %= mod; + + itd->uframe = uframe; + itd->hw_transaction [uframe & 0x07] = itd->transaction; + itd_link (ehci, (uframe >> 3) % ehci->periodic_size, + itd); + usecs += itd->usecs; + + itd = list_entry (itd->itd_list.next, + struct ehci_itd, itd_list); } - /* handle completion now? */ - if ((itd->index + 1) != urb->number_of_packets) - return flags; + /* update bandwidth utilization records (for usbfs) */ + /* FIXME usbcore expects per-frame average, which isn't + * most accurate model... this provides the total claim, + * and expects the average to be computed only display. + */ + usb_claim_bandwidth (urb->dev, urb, usecs, 1); - i = usb_pipein (urb->pipe); - if (i) - pci_dma_sync_single (ehci->hcd.pdev, - first_itd->buf_dma, - urb->transfer_buffer_length, - PCI_DMA_FROMDEVICE); + /* maybe enable periodic schedule processing */ + if (!ehci->periodic_urbs++) + enable_periodic (ehci); - /* call completion with no locks; it can unlink ... */ - spin_unlock_irqrestore (&ehci->lock, flags); - urb->complete (urb); - spin_lock_irqsave (&ehci->lock, flags); - - /* re-activate this URB? or unlink? */ - if (!(urb->transfer_flags & EHCI_STATE_UNLINK) - && ehci->hcd.state != USB_STATE_HALT) { - if (!i) - pci_dma_sync_single (ehci->hcd.pdev, - first_itd->buf_dma, - urb->transfer_buffer_length, - PCI_DMA_TODEVICE); + return 0; - itd = urb->hcpriv; - do { - for (i = 0; i < 8; i++) - itd->hw_transaction [i] - = itd->transaction [i]; - itd = list_entry (itd->itd_list.next, - struct ehci_itd, itd_list); - } while (itd != urb->hcpriv); - return flags; - } + } while ((start = ++start % mod) != max); + + /* no room in the schedule */ + dbg ("urb %p, CAN'T SCHEDULE", urb); + return -ENOSPC; +} - /* unlink done only on the last itd */ - } else if ((itd->index + 1) != urb->number_of_packets) +/*-------------------------------------------------------------------------*/ + +#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) + +static unsigned long +itd_complete ( + struct ehci_hcd *ehci, + struct ehci_itd *itd, + unsigned uframe, + unsigned long flags +) { + struct urb *urb = itd->urb; + struct usb_iso_packet_descriptor *desc; + u32 t; + + /* update status for this uframe's transfers */ + desc = &urb->iso_frame_desc [itd->index]; + + t = itd->hw_transaction [uframe]; + itd->hw_transaction [uframe] = 0; + if (t & EHCI_ISOC_ACTIVE) + desc->status = -EXDEV; + else if (t & ISO_ERRS) { + urb->error_count++; + if (t & EHCI_ISOC_BUF_ERR) + desc->status = usb_pipein (urb->pipe) + ? -ENOSR /* couldn't read */ + : -ECOMM; /* couldn't write */ + else if (t & EHCI_ISOC_BABBLE) + desc->status = -EOVERFLOW; + else /* (t & EHCI_ISOC_XACTERR) */ + desc->status = -EPROTO; + + /* HC need not update length with this error */ + if (!(t & EHCI_ISOC_BABBLE)) + desc->actual_length += EHCI_ITD_LENGTH (t); + } else { + desc->status = 0; + desc->actual_length += EHCI_ITD_LENGTH (t); + } + + vdbg ("itd %p urb %p packet %d/%d trans %x status %d len %d", + itd, urb, itd->index + 1, urb->number_of_packets, + t, desc->status, desc->actual_length); + + /* handle completion now? */ + if ((itd->index + 1) != urb->number_of_packets) return flags; - /* we're unlinking ... */ + /* + * For now, always give the urb back to the driver ... expect it + * to submit a new urb (or resubmit this), and to have another + * already queued when un-interrupted transfers are needed. + * No, that's not what OHCI or UHCI are now doing. + * + * FIXME Revisit the ISO URB model. It's cleaner not to have all + * the special case magic, but it'd be faster to reuse existing + * ITD/DMA setup and schedule state. Easy to dma_sync/complete(), + * then either reschedule or, if unlinking, free and giveback(). + * But we can't overcommit like the full and low speed HCs do, and + * there's no clean way to report an error when rescheduling... + * + * NOTE that for now we don't accelerate ISO unlinks; they just + * happen according to the current schedule. Means a delay of + * up to about a second (max). + */ + itd_free_list (ehci, urb); + if (urb->status == -EINPROGRESS) + urb->status = 0; - /* decouple urb from the hcd */ spin_unlock_irqrestore (&ehci->lock, flags); - if (ehci->hcd.state == USB_STATE_HALT) - urb->status = -ESHUTDOWN; - itd = urb->hcpriv; - urb->hcpriv = 0; - ehci_urb_done (ehci, itd->buf_dma, urb); + usb_hcd_giveback_urb (&ehci->hcd, urb); spin_lock_irqsave (&ehci->lock, flags); - /* take itds out of the hc's periodic schedule */ - list_entry (itd->itd_list.prev, struct ehci_itd, itd_list) - ->itd_list.next = 0; - do { - struct ehci_itd *next; - - if (itd->itd_list.next) - next = list_entry (itd->itd_list.next, - struct ehci_itd, itd_list); - else - next = 0; + /* defer stopping schedule; completion can submit */ + ehci->periodic_urbs--; + if (!ehci->periodic_urbs) + disable_periodic (ehci); - // FIXME: hc WILL (!) lap us here, if we get behind - // by 128 msec (or less, with smaller periodic_size). - // Reading/caching these itds will cause trouble... - - periodic_unlink (ehci, itd->uframe, itd); - itd_free (ehci, itd); - itd = next; - } while (itd); return flags; } /*-------------------------------------------------------------------------*/ -static int itd_submit (struct ehci_hcd *ehci, struct urb *urb) +static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) { - struct ehci_itd *first_itd = 0, *itd; - unsigned frame_index; - dma_addr_t dma; - unsigned long flags; - - dbg ("itd_submit"); + int status; + unsigned long flags; - /* set up one dma mapping for this urb */ - dma = pci_map_single (ehci->hcd.pdev, - urb->transfer_buffer, urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); - if (dma == 0) - return -ENOMEM; + dbg ("itd_submit urb %p", urb); + /* NOTE DMA mapping assumes this ... */ + if (urb->iso_frame_desc [0].offset != 0) + return -EINVAL; + /* - * Schedule as needed. This is VERY optimistic about free - * bandwidth! But the API assumes drivers can pick frames - * intelligently (how?), so there's no other good option. - * - * FIXME this doesn't handle urb->next rings, or try to - * use the iso periodicity. + * NOTE doing this for now, anticipating periodic URB models + * get updated to be "explicit resubmit". */ - if (urb->transfer_flags & USB_ISO_ASAP) { - urb->start_frame = ehci_get_frame (&ehci->hcd); - urb->start_frame++; - } - urb->start_frame %= ehci->periodic_size; - - /* create and populate itds (doing uframe scheduling) */ - spin_lock_irqsave (&ehci->lock, flags); - for (frame_index = 0; - frame_index < urb->number_of_packets; - frame_index++) { - itd = itd_make (ehci, urb, frame_index, - urb->start_frame + frame_index, - dma, SLAB_ATOMIC); - if (itd) { - if (first_itd) - list_add_tail (&itd->itd_list, - &first_itd->itd_list); - else - first_itd = itd; - } else { - spin_unlock_irqrestore (&ehci->lock, flags); - if (first_itd) { - while (!list_empty (&first_itd->itd_list)) { - itd = list_entry ( - first_itd->itd_list.next, - struct ehci_itd, itd_list); - list_del (&itd->itd_list); - itd_free (ehci, itd); - } - itd_free (ehci, first_itd); - } - pci_unmap_single (ehci->hcd.pdev, - dma, urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); - return -ENOMEM; - } + if (urb->next) { + dbg ("use explicit resubmit for ISO"); + return -EINVAL; } - /* stuff into the schedule */ - itd = first_itd; - do { - unsigned i; - - for (i = 0; i < 8; i++) { - if (!itd->hw_transaction [i]) - continue; - itd_link (ehci, itd->uframe + i, itd); - } - itd = list_entry (itd->itd_list.next, - struct ehci_itd, itd_list); - } while (itd != first_itd); - urb->hcpriv = first_itd; + /* allocate ITDs w/o locking anything */ + status = itd_urb_transaction (ehci, urb, mem_flags); + if (status < 0) + return status; + /* schedule ... need to lock */ + spin_lock_irqsave (&ehci->lock, flags); + status = itd_schedule (ehci, urb); spin_unlock_irqrestore (&ehci->lock, flags); - return 0; + if (status < 0) + itd_free_list (ehci, urb); + + return status; } +#ifdef have_split_iso + /*-------------------------------------------------------------------------*/ /* @@ -826,7 +947,7 @@ * the TTs in USB 2.0 hubs. */ -static inline void +static void sitd_free (struct ehci_hcd *ehci, struct ehci_sitd *sitd) { pci_pool_free (ehci->sitd_pool, sitd, sitd->sitd_dma); @@ -860,7 +981,7 @@ } -static inline void +static void sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd) { u32 ptr; @@ -893,12 +1014,11 @@ /*-------------------------------------------------------------------------*/ -static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb) +static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) { // struct ehci_sitd *first_sitd = 0; unsigned frame_index; dma_addr_t dma; - int mem_flags; dbg ("NYI -- sitd_submit"); @@ -907,8 +1027,6 @@ // FIXME: setup one big dma mapping dma = 0; - mem_flags = SLAB_ATOMIC; - for (frame_index = 0; frame_index < urb->number_of_packets; frame_index++) { @@ -940,53 +1058,62 @@ return -ENOSYS; } - -#endif /* have_iso */ +#endif /* have_split_iso */ /*-------------------------------------------------------------------------*/ static void scan_periodic (struct ehci_hcd *ehci) { - unsigned frame; - unsigned clock; + unsigned frame, clock, now_uframe, mod; unsigned long flags; + mod = ehci->periodic_size << 3; spin_lock_irqsave (&ehci->lock, flags); /* * When running, scan from last scan point up to "now" + * else clean up by scanning everything that's left. * Touches as few pages as possible: cache-friendly. - * It's safe to scan entries more than once, though. + * Don't scan ISO entries more than once, though. */ - if (HCD_IS_RUNNING (ehci->hcd.state)) { - frame = ehci->next_frame; - clock = ehci_get_frame (&ehci->hcd); + frame = ehci->next_uframe >> 3; + if (HCD_IS_RUNNING (ehci->hcd.state)) + now_uframe = readl (&ehci->regs->frame_index); + else + now_uframe = (frame << 3) - 1; + now_uframe %= mod; + clock = now_uframe >> 3; - /* when shutting down, scan everything for thoroughness */ - } else { - frame = 0; - clock = ehci->periodic_size - 1; - } for (;;) { - union ehci_shadow q; - u32 type; + union ehci_shadow q, *q_p; + u32 type, *hw_p; + unsigned uframes; restart: - q.ptr = ehci->pshadow [frame].ptr; - type = Q_NEXT_TYPE (ehci->periodic [frame]); + /* scan schedule to _before_ current frame index */ + if (frame == clock) + uframes = now_uframe & 0x07; + else + uframes = 8; + + q_p = &ehci->pshadow [frame]; + hw_p = &ehci->periodic [frame]; + q.ptr = q_p->ptr; + type = Q_NEXT_TYPE (*hw_p); /* scan each element in frame's queue for completions */ while (q.ptr != 0) { int last; + unsigned uf; union ehci_shadow temp; switch (type) { case Q_TYPE_QH: last = (q.qh->hw_next == EHCI_LIST_END); + temp = q.qh->qh_next; + type = Q_NEXT_TYPE (q.qh->hw_next); flags = intr_complete (ehci, frame, qh_put (q.qh), flags); - type = Q_NEXT_TYPE (q.qh->hw_next); - temp = q.qh->qh_next; qh_unput (ehci, q.qh); q = temp; break; @@ -999,22 +1126,49 @@ dbg ("ignoring completions from FSTNs"); } type = Q_NEXT_TYPE (q.fstn->hw_next); - temp = q.fstn->fstn_next; + q = q.fstn->fstn_next; break; -#ifdef have_iso case Q_TYPE_ITD: last = (q.itd->hw_next == EHCI_LIST_END); - flags = itd_complete (ehci, q.itd, flags); - type = Q_NEXT_TYPE (q.itd->hw_next); - q = q.itd->itd_next; + + /* Unlink each (S)ITD we see, since the ISO + * URB model forces constant rescheduling. + * That complicates sharing uframes in ITDs, + * and means we need to skip uframes the HC + * hasn't yet processed. + */ + for (uf = 0; uf < uframes; uf++) { + if (q.itd->hw_transaction [uf] != 0) { + temp = q; + *q_p = q.itd->itd_next; + *hw_p = q.itd->hw_next; + type = Q_NEXT_TYPE (*hw_p); + + /* might free q.itd ... */ + flags = itd_complete (ehci, + temp.itd, uf, flags); + break; + } + } + /* we might skip this ITD's uframe ... */ + if (uf == uframes) { + q_p = &q.itd->itd_next; + hw_p = &q.itd->hw_next; + type = Q_NEXT_TYPE (q.itd->hw_next); + } + + q = *q_p; break; +#ifdef have_split_iso case Q_TYPE_SITD: last = (q.sitd->hw_next == EHCI_LIST_END); flags = sitd_complete (ehci, q.sitd, flags); type = Q_NEXT_TYPE (q.sitd->hw_next); + + // FIXME unlink SITD after split completes q = q.sitd->sitd_next; break; -#endif /* have_iso */ +#endif /* have_split_iso */ default: dbg ("corrupt type %d frame %d shadow %p", type, frame, q.ptr); @@ -1043,13 +1197,16 @@ if (!HCD_IS_RUNNING (ehci->hcd.state)) break; - ehci->next_frame = clock; - now = ehci_get_frame (&ehci->hcd); - if (clock == now) + ehci->next_uframe = now_uframe; + now = readl (&ehci->regs->frame_index) % mod; + if (now_uframe == now) break; - clock = now; - } else if (++frame >= ehci->periodic_size) - frame = 0; + + /* rescan the rest of this frame, then ... */ + now_uframe = now; + clock = now_uframe >> 3; + } else + frame = (frame + 1) % ehci->periodic_size; } spin_unlock_irqrestore (&ehci->lock, flags); - } +} diff -Nru a/drivers/usb/hcd/ehci.h b/drivers/usb/hcd/ehci.h --- a/drivers/usb/hcd/ehci.h Thu Mar 7 18:17:41 2002 +++ b/drivers/usb/hcd/ehci.h Thu Mar 7 18:17:41 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -49,7 +49,7 @@ unsigned i_thresh; /* uframes HC might cache */ union ehci_shadow *pshadow; /* mirror hw periodic table */ - int next_frame; /* scan periodic, start here */ + int next_uframe; /* scan periodic, start here */ unsigned periodic_urbs; /* how many urbs scheduled? */ /* deferred work from IRQ, etc */ @@ -62,6 +62,7 @@ struct usb_hcd hcd; struct ehci_caps *caps; struct ehci_regs *regs; + u32 hcs_params; /* cached register copy */ /* per-HC memory pools (could be per-PCI-bus, but ...) */ struct pci_pool *qh_pool; /* qh per active urb */ @@ -324,13 +325,14 @@ union ehci_shadow itd_next; /* ptr to periodic q entry */ struct urb *urb; - unsigned index; /* in urb->iso_frame_desc */ struct list_head itd_list; /* list of urb frames' itds */ dma_addr_t buf_dma; /* frame's buffer address */ - unsigned uframe; /* in periodic schedule */ - u32 transaction [8]; /* copy of hw_transaction */ - + /* for now, only one hw_transaction per itd */ + u32 transaction; + u16 index; /* in urb->iso_frame_desc */ + u16 uframe; /* in periodic schedule */ + u16 usecs; } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/ diff -Nru a/drivers/usb/hcd/ohci-q.c b/drivers/usb/hcd/ohci-q.c --- a/drivers/usb/hcd/ohci-q.c Thu Mar 7 18:17:42 2002 +++ b/drivers/usb/hcd/ohci-q.c Thu Mar 7 18:17:42 2002 @@ -8,8 +8,6 @@ * $Id: ohci-q.c,v 1.6 2002/01/19 00:23:15 dbrownell Exp $ */ -#include - static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv) { int last = urb_priv->length - 1; diff -Nru a/drivers/usb/hcd.c b/drivers/usb/hcd.c --- a/drivers/usb/hcd.c Thu Mar 7 18:17:45 2002 +++ b/drivers/usb/hcd.c Thu Mar 7 18:17:45 2002 @@ -1,5 +1,11 @@ /* - * Copyright (c) 2001 by David Brownell + * (C) Copyright Linus Torvalds 1999 + * (C) Copyright Johannes Erdfelt 1999-2001 + * (C) Copyright Andreas Gal 1999 + * (C) Copyright Gregory P. Smith 1999 + * (C) Copyright Deti Fliegl 1999 + * (C) Copyright Randy Dunlap 2000 + * (C) Copyright David Brownell 2000-2002 * * 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 @@ -33,9 +39,6 @@ #include #include /* for UTS_SYSNAME */ -#ifndef CONFIG_USB_DEBUG - #define CONFIG_USB_DEBUG /* this is experimental! */ -#endif #ifdef CONFIG_USB_DEBUG #define DEBUG @@ -53,6 +56,8 @@ #include +// #define USB_BANDWIDTH_MESSAGES + /*-------------------------------------------------------------------------*/ /* @@ -78,19 +83,28 @@ * usb client device drivers. * * Contributors of ideas or unattributed patches include: David Brownell, - * Roman Weissgaerber, Rory Bolt, ... + * Roman Weissgaerber, Rory Bolt, Greg Kroah-Hartman, ... * * HISTORY: + * 2002-02-21 Pull in most of the usb_bus support from usb.c; some + * associated cleanup. "usb_hcd" still != "usb_bus". * 2001-12-12 Initial patch version for Linux 2.5.1 kernel. */ /*-------------------------------------------------------------------------*/ /* host controllers we manage */ -static LIST_HEAD (hcd_list); +LIST_HEAD (usb_bus_list); + +/* used when allocating bus numbers */ +#define USB_MAXBUS 64 +struct usb_busmap { + unsigned long busmap [USB_MAXBUS / (8*sizeof (unsigned long))]; +}; +static struct usb_busmap busmap; /* used when updating list of hcds */ -static DECLARE_MUTEX (hcd_list_lock); +DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */ /* used when updating hcd data */ static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED; @@ -105,6 +119,9 @@ /*-------------------------------------------------------------------------*/ +#define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff) +#define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff) + /* usb 2.0 root hub device descriptor */ static const u8 usb2_rh_dev_descriptor [18] = { 0x12, /* __u8 bLength; */ @@ -118,7 +135,7 @@ 0x00, 0x00, /* __u16 idVendor; */ 0x00, 0x00, /* __u16 idProduct; */ - 0x40, 0x02, /* __u16 bcdDevice; (v2.4) */ + KERNEL_VER, KERNEL_REL, /* __u16 bcdDevice */ 0x03, /* __u8 iManufacturer; */ 0x02, /* __u8 iProduct; */ @@ -141,7 +158,7 @@ 0x00, 0x00, /* __u16 idVendor; */ 0x00, 0x00, /* __u16 idProduct; */ - 0x40, 0x02, /* __u16 bcdDevice; (v2.4) */ + KERNEL_VER, KERNEL_REL, /* __u16 bcdDevice */ 0x03, /* __u8 iManufacturer; */ 0x02, /* __u8 iProduct; */ @@ -207,12 +224,12 @@ * helper routine for returning string descriptors in UTF-16LE * input can actually be ISO-8859-1; ASCII is its 7-bit subset */ -static int ascii2utf (char *ascii, u8 *utf, int utfmax) +static int ascii2utf (char *s, u8 *utf, int utfmax) { int retval; - for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) { - *utf++ = *ascii++ & 0x7f; + for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) { + *utf++ = *s++; *utf++ = 0; } return retval; @@ -231,8 +248,7 @@ */ static int rh_string ( int id, - struct pci_dev *pci_desc, - char *type, + struct usb_hcd *hcd, u8 *data, int len ) { @@ -246,15 +262,16 @@ // serial number } else if (id == 1) { - strcpy (buf, pci_desc->slot_name); + strcpy (buf, hcd->bus_name); // product description } else if (id == 2) { - strcpy (buf, pci_desc->name); + strcpy (buf, hcd->product_desc); // id 3 == vendor description } else if (id == 3) { - sprintf (buf, "%s %s %s", UTS_SYSNAME, UTS_RELEASE, type); + sprintf (buf, "%s %s %s", UTS_SYSNAME, UTS_RELEASE, + hcd->description); // unsupported IDs --> "protocol stall" } else @@ -321,9 +338,7 @@ break; case USB_DT_STRING << 8: urb->actual_length = rh_string ( - wValue & 0xff, - hcd->pdev, - (char *) hcd->description, + wValue & 0xff, hcd, ubuf, wLength); break; default: @@ -506,6 +521,346 @@ /*-------------------------------------------------------------------------*/ +/* exported only within usbcore */ +void usb_bus_get (struct usb_bus *bus) +{ + atomic_inc (&bus->refcnt); +} + +/* exported only within usbcore */ +void usb_bus_put (struct usb_bus *bus) +{ + if (atomic_dec_and_test (&bus->refcnt)) + kfree (bus); +} + +/*-------------------------------------------------------------------------*/ + +/* shared initialization code */ +static void usb_init_bus (struct usb_bus *bus) +{ + memset (&bus->devmap, 0, sizeof(struct usb_devmap)); + +#ifdef DEVNUM_ROUND_ROBIN + bus->devnum_next = 1; +#endif /* DEVNUM_ROUND_ROBIN */ + + bus->root_hub = NULL; + bus->hcpriv = NULL; + bus->busnum = -1; + bus->bandwidth_allocated = 0; + bus->bandwidth_int_reqs = 0; + bus->bandwidth_isoc_reqs = 0; + + INIT_LIST_HEAD (&bus->bus_list); + + atomic_set (&bus->refcnt, 1); +} + +/** + * usb_alloc_bus - creates a new USB host controller structure + * @op: pointer to a struct usb_operations that this bus structure should use + * Context: !in_interrupt() + * + * Creates a USB host controller bus structure with the specified + * usb_operations and initializes all the necessary internal objects. + * + * If no memory is available, NULL is returned. + * + * The caller should call usb_free_bus() when it is finished with the structure. + */ +struct usb_bus *usb_alloc_bus (struct usb_operations *op) +{ + struct usb_bus *bus; + + bus = kmalloc (sizeof *bus, GFP_KERNEL); + if (!bus) + return NULL; + usb_init_bus (bus); + bus->op = op; + return bus; +} +EXPORT_SYMBOL (usb_alloc_bus); + +/** + * usb_free_bus - frees the memory used by a bus structure + * @bus: pointer to the bus to free + * + * To be invoked by a HCD, only as the last step of decoupling from + * hardware. It is an error to call this if the reference count is + * anything but one. That would indicate that some system component + * did not correctly shut down, and thought the hardware was still + * accessible. + */ +void usb_free_bus (struct usb_bus *bus) +{ + if (!bus) + return; + if (atomic_read (&bus->refcnt) != 1) + err ("usb_free_bus #%d, count != 1", bus->busnum); + usb_bus_put (bus); +} +EXPORT_SYMBOL (usb_free_bus); + +/*-------------------------------------------------------------------------*/ + +/** + * usb_register_bus - registers the USB host controller with the usb core + * @bus: pointer to the bus to register + * Context: !in_interrupt() + * + * Assigns a bus number, and links the controller into usbcore data + * structures so that it can be seen by scanning the bus list. + */ +void usb_register_bus(struct usb_bus *bus) +{ + int busnum; + + down (&usb_bus_list_lock); + busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1); + if (busnum < USB_MAXBUS) { + set_bit (busnum, busmap.busmap); + bus->busnum = busnum; + } else + warn ("too many buses"); + + usb_bus_get (bus); + + /* Add it to the list of buses */ + list_add (&bus->bus_list, &usb_bus_list); + up (&usb_bus_list_lock); + + usbfs_add_bus (bus); + + info ("new USB bus registered, assigned bus number %d", bus->busnum); +} +EXPORT_SYMBOL (usb_register_bus); + +/** + * usb_deregister_bus - deregisters the USB host controller + * @bus: pointer to the bus to deregister + * Context: !in_interrupt() + * + * Recycles the bus number, and unlinks the controller from usbcore data + * structures so that it won't be seen by scanning the bus list. + */ +void usb_deregister_bus (struct usb_bus *bus) +{ + info ("USB bus %d deregistered", bus->busnum); + + /* + * NOTE: make sure that all the devices are removed by the + * controller code, as well as having it call this when cleaning + * itself up + */ + down (&usb_bus_list_lock); + list_del (&bus->bus_list); + up (&usb_bus_list_lock); + + usbfs_remove_bus (bus); + + clear_bit (bus->busnum, busmap.busmap); + + usb_bus_put (bus); +} +EXPORT_SYMBOL (usb_deregister_bus); + +/** + * usb_register_root_hub - called by HCD to register its root hub + * @usb_dev: the usb root hub device to be registered. + * @parent_dev: the parent device of this root hub. + * + * The USB host controller calls this function to register the root hub + * properly with the USB subsystem. It sets up the device properly in + * the driverfs tree, and then calls usb_new_device() to register the + * usb device. + */ +int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev) +{ + int retval; + + usb_dev->dev.parent = parent_dev; + strcpy (&usb_dev->dev.name[0], "usb_name"); + strcpy (&usb_dev->dev.bus_id[0], "usb_bus"); + retval = usb_new_device (usb_dev); + if (retval) + put_device (&usb_dev->dev); + return retval; +} +EXPORT_SYMBOL (usb_register_root_hub); + + +/*-------------------------------------------------------------------------*/ + +/* + * usb_calc_bus_time: + * Returns approximate bus time in nanoseconds for a periodic transaction. + * See USB 2.0 spec section 5.11.3 + */ +static long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount) +{ + unsigned long tmp; + + switch (speed) { + case USB_SPEED_LOW: /* INTR only */ + if (is_input) { + tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L; + return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); + } else { + tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L; + return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); + } + case USB_SPEED_FULL: /* ISOC or INTR */ + if (isoc) { + tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; + return (((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp); + } else { + tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; + return (9107L + BW_HOST_DELAY + tmp); + } + case USB_SPEED_HIGH: /* ISOC or INTR */ + // FIXME merge from EHCI code; caller will need to handle + // each part of a split separately. + return 0; + default: + dbg ("bogus device speed!"); + return -1; + } +} + +/* + * usb_check_bandwidth(): + * + * old_alloc is from host_controller->bandwidth_allocated in microseconds; + * bustime is from calc_bus_time(), but converted to microseconds. + * + * returns if successful, + * or -ENOSPC if bandwidth request fails. + * + * FIXME: + * This initial implementation does not use Endpoint.bInterval + * in managing bandwidth allocation. + * It probably needs to be expanded to use Endpoint.bInterval. + * This can be done as a later enhancement (correction). + * + * This will also probably require some kind of + * frame allocation tracking...meaning, for example, + * that if multiple drivers request interrupts every 10 USB frames, + * they don't all have to be allocated at + * frame numbers N, N+10, N+20, etc. Some of them could be at + * N+11, N+21, N+31, etc., and others at + * N+12, N+22, N+32, etc. + * + * Similarly for isochronous transfers... + * + * Individual HCDs can schedule more directly ... this logic + * is not correct for high speed transfers. + */ +int usb_check_bandwidth (struct usb_device *dev, struct urb *urb) +{ + unsigned int pipe = urb->pipe; + long bustime; + int is_in = usb_pipein (pipe); + int is_iso = usb_pipeisoc (pipe); + int old_alloc = dev->bus->bandwidth_allocated; + int new_alloc; + + + bustime = NS_TO_US (usb_calc_bus_time (dev->speed, is_in, is_iso, + usb_maxpacket (dev, pipe, !is_in))); + if (is_iso) + bustime /= urb->number_of_packets; + + new_alloc = old_alloc + (int) bustime; + if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC) { +#ifdef DEBUG + char *mode = +#ifdef CONFIG_USB_BANDWIDTH + ""; +#else + "would have "; +#endif + dbg ("usb_check_bandwidth %sFAILED: %d + %ld = %d usec", + mode, old_alloc, bustime, new_alloc); +#endif +#ifdef CONFIG_USB_BANDWIDTH + bustime = -ENOSPC; /* report error */ +#endif + } + + return bustime; +} +EXPORT_SYMBOL (usb_check_bandwidth); + + +/** + * usb_claim_bandwidth - records bandwidth for a periodic transfer + * @dev: source/target of request + * @urb: request (urb->dev == dev) + * @bustime: bandwidth consumed, in (average) microseconds per frame + * @isoc: true iff the request is isochronous + * + * Bus bandwidth reservations are recorded purely for diagnostic purposes. + * HCDs are expected not to overcommit periodic bandwidth, and to record such + * reservations whenever endpoints are added to the periodic schedule. + * + * FIXME averaging per-frame is suboptimal. Better to sum over the HCD's + * entire periodic schedule ... 32 frames for OHCI, 1024 for UHCI, settable + * for EHCI (256/512/1024 frames, default 1024) and have the bus expose how + * large its periodic schedule is. + */ +void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc) +{ + dev->bus->bandwidth_allocated += bustime; + if (isoc) + dev->bus->bandwidth_isoc_reqs++; + else + dev->bus->bandwidth_int_reqs++; + urb->bandwidth = bustime; + +#ifdef USB_BANDWIDTH_MESSAGES + dbg ("bandwidth alloc increased by %d (%s) to %d for %d requesters", + bustime, + isoc ? "ISOC" : "INTR", + dev->bus->bandwidth_allocated, + dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs); +#endif +} +EXPORT_SYMBOL (usb_claim_bandwidth); + + +/** + * usb_release_bandwidth - reverses effect of usb_claim_bandwidth() + * @dev: source/target of request + * @urb: request (urb->dev == dev) + * @isoc: true iff the request is isochronous + * + * This records that previously allocated bandwidth has been released. + * Bandwidth is released when endpoints are removed from the host controller's + * periodic schedule. + */ +void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc) +{ + dev->bus->bandwidth_allocated -= urb->bandwidth; + if (isoc) + dev->bus->bandwidth_isoc_reqs--; + else + dev->bus->bandwidth_int_reqs--; + +#ifdef USB_BANDWIDTH_MESSAGES + dbg ("bandwidth alloc reduced by %d (%s) to %d for %d requesters", + urb->bandwidth, + isoc ? "ISOC" : "INTR", + dev->bus->bandwidth_allocated, + dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs); +#endif + urb->bandwidth = 0; +} +EXPORT_SYMBOL (usb_release_bandwidth); + + +/*-------------------------------------------------------------------------*/ + #ifdef CONFIG_PCI /* PCI-based HCs are normal, but custom bus glue should be ok */ @@ -522,6 +877,7 @@ * usb_hcd_pci_probe - initialize PCI-based HCDs * @dev: USB Host Controller being probed * @id: pci hotplug id connecting controller to HCD framework + * Context: !in_interrupt() * * Allocates basic PCI resources for this USB host controller, and * then invokes the start() method for the HCD associated with it @@ -535,7 +891,6 @@ unsigned long resource, len; void *base; u8 latency, limit; - struct usb_bus *bus; struct usb_hcd *hcd; int retval, region; char buf [8], *bufp = buf; @@ -631,7 +986,6 @@ != 0) { err ("request interrupt %s failed", bufp); retval = -EBUSY; -clean_3: driver->hcd_free (hcd); goto clean_2; } @@ -643,26 +997,16 @@ (driver->flags & HCD_MEMORY) ? "pci mem" : "io base", base); -// FIXME simpler: make "bus" be that data, not pointer to it. - bus = usb_alloc_bus (&hcd_operations); - if (bus == NULL) { - dbg ("usb_alloc_bus fail"); - retval = -ENOMEM; - free_irq (dev->irq, hcd); - goto clean_3; - } - hcd->bus = bus; + usb_init_bus (&hcd->self); + hcd->self.op = &hcd_operations; + hcd->self.hcpriv = (void *) hcd; + hcd->bus = &hcd->self; hcd->bus_name = dev->slot_name; - bus->hcpriv = (void *) hcd; + hcd->product_desc = dev->name; INIT_LIST_HEAD (&hcd->dev_list); - INIT_LIST_HEAD (&hcd->hcd_list); - - down (&hcd_list_lock); - list_add (&hcd->hcd_list, &hcd_list); - up (&hcd_list_lock); - usb_register_bus (bus); + usb_register_bus (&hcd->self); if ((retval = driver->start (hcd)) < 0) usb_hcd_pci_remove (dev); @@ -678,6 +1022,7 @@ /** * usb_hcd_pci_remove - shutdown processing for PCI-based HCDs * @dev: USB Host Controller being removed + * Context: !in_interrupt() * * Reverses the effect of usb_hcd_pci_probe(), first invoking * the HCD's stop() method. It is always called from a thread @@ -717,12 +1062,9 @@ pci_resource_len (dev, hcd->region)); } - down (&hcd_list_lock); - list_del (&hcd->hcd_list); - up (&hcd_list_lock); - usb_deregister_bus (hcd->bus); - usb_free_bus (hcd->bus); + if (atomic_read (&hcd->self.refcnt) != 1) + err ("usb_hcd_pci_remove %s, count != 1", hcd->bus_name); hcd->bus = NULL; hcd->driver->hcd_free (hcd); @@ -923,7 +1265,7 @@ struct usb_hcd *hcd; struct hcd_dev *dev; unsigned long flags; - int pipe; + int pipe, temp; if (!urb || urb->hcpriv || !urb->complete) return -EINVAL; @@ -943,6 +1285,7 @@ if (hcd->state == USB_STATE_QUIESCING || !HCD_IS_RUNNING (hcd->state)) return -ESHUTDOWN; pipe = urb->pipe; + temp = usb_pipetype (urb->pipe); if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) return -EPIPE; @@ -955,7 +1298,7 @@ /* enforce simple/standard policy */ allowed = USB_ASYNC_UNLINK; // affects later unlinks allowed |= USB_NO_FSBR; // only affects UHCI - switch (usb_pipetype (pipe)) { + switch (temp) { case PIPE_CONTROL: allowed |= USB_DISABLE_SPD; break; @@ -974,15 +1317,55 @@ /* warn if submitter gave bogus flags */ if (urb->transfer_flags != orig_flags) - warn ("BOGUS urb flags, %x --> %x", + err ("BOGUS urb flags, %x --> %x", orig_flags, urb->transfer_flags); } #endif /* - * FIXME: alloc periodic bandwidth here, for interrupt and iso? - * Need to look at the ring submit mechanism for iso tds ... they - * aren't actually "periodic" in 2.4 kernels. + * Force periodic transfer intervals to be legal values that are + * a power of two (so HCDs don't need to). * + * FIXME want bus->{intr,iso}_sched_horizon values here. Each HC + * supports different values... this uses EHCI/UHCI defaults (and + * EHCI can use smaller non-default values). + */ + switch (temp) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + /* too small? */ + if (urb->interval <= 0) + return -EINVAL; + /* too big? */ + switch (urb->dev->speed) { + case USB_SPEED_HIGH: /* units are microframes */ + // NOTE usb handles 2^15 + if (urb->interval > (1024 * 8)) + urb->interval = 1024 * 8; + temp = 1024 * 8; + break; + case USB_SPEED_FULL: /* units are frames/msec */ + case USB_SPEED_LOW: + if (temp == PIPE_INTERRUPT) { + if (urb->interval > 255) + return -EINVAL; + // NOTE ohci only handles up to 32 + temp = 128; + } else { + if (urb->interval > 1024) + urb->interval = 1024; + // NOTE usb and ohci handle up to 2^15 + temp = 1024; + } + default: + return -EINVAL; + } + /* power of two? */ + while (temp > urb->interval) + temp >>= 1; + urb->interval = temp; + } + + /* * FIXME: make urb timeouts be generic, keeping the HCD cores * as simple as possible. */ @@ -1246,6 +1629,9 @@ struct usb_hcd *hcd = __hcd; int start = hcd->state; + if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */ + return; + hcd->driver->irq (hcd); if (hcd->state != start && hcd->state == USB_STATE_HALT) hc_died (hcd); @@ -1257,6 +1643,7 @@ * usb_hcd_giveback_urb - return URB from HCD to device driver * @hcd: host controller returning the URB * @urb: urb being returned to the USB device driver. + * Context: in_interrupt() * * This hands the URB from HCD to its USB device driver, using its * completion function. The HCD has freed all per-urb resources @@ -1279,16 +1666,9 @@ struct usb_device *dev; /* Release periodic transfer bandwidth */ - if (urb->bandwidth) { - switch (usb_pipetype (urb->pipe)) { - case PIPE_INTERRUPT: - usb_release_bandwidth (urb->dev, urb, 0); - break; - case PIPE_ISOCHRONOUS: - usb_release_bandwidth (urb->dev, urb, 1); - break; - } - } + if (urb->bandwidth) + usb_release_bandwidth (urb->dev, urb, + usb_pipeisoc (urb->pipe)); /* clear all state linking urb to this dev (and hcd) */ @@ -1305,7 +1685,8 @@ // hcd_monitor_hook(MONITOR_URB_UPDATE, urb, dev) if (urb->status) - dbg ("giveback urb %p status %d", urb, urb->status); + dbg ("giveback urb %p status %d len %d", + urb, urb->status, urb->actual_length); /* if no error, make sure urb->next progresses */ else if (urb->next) { diff -Nru a/drivers/usb/hcd.h b/drivers/usb/hcd.h --- a/drivers/usb/hcd.h Thu Mar 7 18:17:41 2002 +++ b/drivers/usb/hcd.h Thu Mar 7 18:17:41 2002 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2001 by David Brownell - * + * Copyright (c) 2001-2002 by David Brownell + * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your @@ -17,6 +17,8 @@ */ +#ifdef __KERNEL__ + /*-------------------------------------------------------------------------*/ /* @@ -33,11 +35,11 @@ /* * housekeeping */ - struct usb_bus *bus; /* hcd is-a bus */ - struct list_head hcd_list; + struct usb_bus *bus; /* FIXME only use "self" */ + struct usb_bus self; /* hcd is-a bus */ const char *bus_name; - + const char *product_desc; /* product/vendor string */ const char *description; /* "ehci-hcd" etc */ struct timer_list rh_timer; /* drives root hub */ @@ -98,6 +100,19 @@ /*-------------------------------------------------------------------------*/ +/* + * FIXME usb_operations should vanish or become hc_driver, + * when usb_bus and usb_hcd become the same thing. + */ + +struct usb_operations { + int (*allocate)(struct usb_device *); + int (*deallocate)(struct usb_device *); + int (*get_frame_number) (struct usb_device *usb_dev); + int (*submit_urb) (struct urb *urb, int mem_flags); + int (*unlink_urb) (struct urb *urb); +}; + /* each driver provides one of these, and hardware init support */ struct hc_driver { @@ -126,8 +141,6 @@ /* return current frame number */ int (*get_frame_number) (struct usb_hcd *hcd); -// FIXME: rework generic-to-specific HCD linkage (specific contains generic) - /* memory lifecycle */ struct usb_hcd *(*hcd_alloc) (void); void (*hcd_free) (struct usb_hcd *hcd); @@ -152,7 +165,8 @@ extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb); #ifdef CONFIG_PCI - +struct pci_dev; +struct pci_device_id; extern int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id); extern void usb_hcd_pci_remove (struct pci_dev *dev); @@ -206,6 +220,59 @@ /*-------------------------------------------------------------------------*/ +/* + * Generic bandwidth allocation constants/support + */ +#define FRAME_TIME_USECS 1000L +#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */ + /* Trying not to use worst-case bit-stuffing + of (7/6 * 8 * bytecount) = 9.33 * bytecount */ + /* bytecount = data payload byte count */ + +#define NS_TO_US(ns) ((ns + 500L) / 1000L) + /* convert & round nanoseconds to microseconds */ + +extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, + int bustime, int isoc); +extern void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, + int isoc); + +/* + * Full/low speed bandwidth allocation constants/support. + */ +#define BW_HOST_DELAY 1000L /* nanoseconds */ +#define BW_HUB_LS_SETUP 333L /* nanoseconds */ + /* 4 full-speed bit times (est.) */ + +#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */ +#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L) +#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L) + +extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb); + +/*-------------------------------------------------------------------------*/ + +extern struct usb_bus *usb_alloc_bus (struct usb_operations *); +extern void usb_free_bus (struct usb_bus *); + +extern void usb_register_bus (struct usb_bus *); +extern void usb_deregister_bus (struct usb_bus *); + +extern int usb_register_root_hub (struct usb_device *usb_dev, + struct device *parent_dev); + +/*-------------------------------------------------------------------------*/ + +/* exported only within usbcore */ + +extern struct list_head usb_bus_list; +extern struct semaphore usb_bus_list_lock; + +extern void usb_bus_get (struct usb_bus *bus); +extern void usb_bus_put (struct usb_bus *bus); + +/*-------------------------------------------------------------------------*/ + /* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */ // bleech -- resurfaced in 2.4.11 or 2.4.12 #define bitmap DeviceRemovable @@ -217,3 +284,7 @@ #define RUN_CONTEXT (in_irq () ? "in_irq" \ : (in_interrupt () ? "in_interrupt" : "can sleep")) + + +#endif /* __KERNEL__ */ + diff -Nru a/drivers/usb/hid-core.c b/drivers/usb/hid-core.c --- a/drivers/usb/hid-core.c Thu Mar 7 18:17:45 2002 +++ b/drivers/usb/hid-core.c Thu Mar 7 18:17:45 2002 @@ -1048,6 +1048,7 @@ if (hid->outhead != hid->outtail) { hid_submit_out(hid); + spin_unlock_irqrestore(&hid->outlock, flags); return; } @@ -1079,6 +1080,7 @@ if (hid->ctrlhead != hid->ctrltail) { hid_submit_ctrl(hid); + spin_unlock_irqrestore(&hid->ctrllock, flags); return; } @@ -1452,7 +1454,8 @@ } static struct usb_device_id hid_usb_ids [] = { - { bInterfaceClass: USB_INTERFACE_CLASS_HID }, + { match_flags: USB_DEVICE_ID_MATCH_INT_CLASS, + bInterfaceClass: USB_INTERFACE_CLASS_HID }, { } /* Terminating entry */ }; diff -Nru a/drivers/usb/hub.c b/drivers/usb/hub.c --- a/drivers/usb/hub.c Thu Mar 7 18:17:44 2002 +++ b/drivers/usb/hub.c Thu Mar 7 18:17:44 2002 @@ -644,6 +644,49 @@ port + 1, hub->devpath, ret); } +/* USB 2.0 spec, 7.1.7.3 / fig 7-29: + * + * Between connect detection and reset signaling there must be a delay + * of 100ms at least for debounce and power-settling. The corresponding + * timer shall restart whenever the downstream port detects a disconnect. + * + * Apparently there are some bluetooth and irda-dongles and a number + * of low-speed devices which require longer delays of about 200-400ms. + * Not covered by the spec - but easy to deal with. + * + * This implementation uses 400ms minimum debounce timeout and checks + * every 10ms for transient disconnects to restart the delay. + */ + +#define HUB_DEBOUNCE_TIMEOUT 400 +#define HUB_DEBOUNCE_STEP 10 + +/* return: -1 on error, 0 on success, 1 on disconnect. */ +static int usb_hub_port_debounce(struct usb_device *hub, int port) +{ + int ret; + unsigned delay_time; + u16 portchange, portstatus; + + for (delay_time = 0; delay_time < HUB_DEBOUNCE_TIMEOUT; /* empty */ ) { + + /* wait debounce step increment */ + wait_ms(HUB_DEBOUNCE_STEP); + + ret = usb_hub_port_status(hub, port, &portstatus, &portchange); + if (ret < 0) + return -1; + + if ((portchange & USB_PORT_STAT_C_CONNECTION)) { + usb_clear_port_feature(hub, port+1, USB_PORT_FEAT_C_CONNECTION); + delay_time = 0; + } + else + delay_time += HUB_DEBOUNCE_STEP; + } + return ((portstatus&USB_PORT_STAT_CONNECTION)) ? 0 : 1; +} + static void usb_hub_port_connect_change(struct usb_hub *hubstate, int port, u16 portstatus, u16 portchange) { @@ -671,12 +714,16 @@ return; } + if (usb_hub_port_debounce(hub, port)) { + err("connect-debounce failed, port %d disabled", port+1); + usb_hub_port_disable(hub, port); + return; + } + /* Some low speed devices have problems with the quick delay, so */ /* be a bit pessimistic with those devices. RHbug #23670 */ - if (portstatus & USB_PORT_STAT_LOW_SPEED) { - wait_ms(400); + if (portstatus & USB_PORT_STAT_LOW_SPEED) delay = HUB_LONG_RESET_TIME; - } down(&usb_address0_sem); diff -Nru a/drivers/usb/ov511.c b/drivers/usb/ov511.c --- a/drivers/usb/ov511.c Thu Mar 7 18:17:41 2002 +++ b/drivers/usb/ov511.c Thu Mar 7 18:17:41 2002 @@ -1,7 +1,7 @@ /* * OmniVision OV511 Camera-to-USB Bridge Driver * - * Copyright (c) 1999-2001 Mark W. McClelland + * Copyright (c) 1999-2002 Mark W. McClelland * Original decompression code Copyright 1998-2000 OmniVision Technologies * Many improvements by Bret Wallach * Color fixes by by Orion Sky Lawlor (2/26/2000) @@ -10,6 +10,8 @@ * Changes by Claudio Matsuoka * Original SAA7111A code by Dave Perks * Kernel I2C interface adapted from nt1003 driver + * URB error messages from pwc driver by Nemosoft + * generic_ioctl() code from videodev.c by Gerd Knorr and Alan Cox * * Based on the Linux CPiA driver written by Peter Pregler, * Scott J. Bertin and Johannes Erdfelt. @@ -53,22 +55,35 @@ #include #endif +/* A new implementation of the V4L 1 API exists that gives drivers direct + * access to file_operations. The old API is compatible with all 2.2 and 2.4 + * kernels, and all 2.5 kernels through 2.5.5 (at least). + * + * Remove this #define to enable the new API + * + * Note: This has nothing to do with the V4L 2 API. + */ +#define OV511_OLD_V4L + #include "ov511.h" /* * Version Information */ -#define DRIVER_VERSION "v1.48a for Linux 2.4" +#define DRIVER_VERSION "v1.53 for Linux 2.5" #define EMAIL "mmcclell@bigfoot.com" #define DRIVER_AUTHOR "Mark McClelland & Bret Wallach \ & Orion Sky Lawlor & Kevin Moore & Charl P. Botha \ & Claudio Matsuoka " -#define DRIVER_DESC "OV511 USB Camera Driver" +#define DRIVER_DESC "ov511 USB Camera Driver" #define OV511_I2C_RETRIES 3 #define ENABLE_Y_QUANTABLE 1 #define ENABLE_UV_QUANTABLE 1 +/* If you change this, you must also change the MODULE_PARM definition */ +#define OV511_MAX_UNIT_VIDEO 16 + /* Pixel count * 3 bytes for RGB */ #define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3) #define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval)) @@ -78,128 +93,55 @@ #define FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM) -/* PARAMETER VARIABLES: */ -/* (See ov511.txt for detailed descriptions of these.) */ - -/* Sensor automatically changes brightness */ -static int autobright = 1; - -/* Sensor automatically changes gain */ -static int autogain = 1; - -/* Sensor automatically changes exposure */ -static int autoexp = 1; - -/* 0=no debug messages - * 1=init/detection/unload and other significant messages, - * 2=some warning messages - * 3=config/control function calls - * 4=most function calls and data parsing messages - * 5=highly repetitive mesgs - * NOTE: This should be changed to 0, 1, or 2 for production kernels - */ -static int debug; /* = 0 */ - -/* Fix vertical misalignment of red and blue at 640x480 */ -static int fix_rgb_offset; /* = 0 */ - -/* Snapshot mode enabled flag */ -static int snapshot; /* = 0 */ - -/* Force image to be read in RGB instead of BGR. This option allow - * programs that expect RGB data (e.g. gqcam) to work with this driver. */ -static int force_rgb; /* = 0 */ - -/* Number of seconds before inactive buffers are deallocated */ -static int buf_timeout = 5; - -/* Number of cameras to stream from simultaneously */ -static int cams = 1; - -/* Enable compression. Needs a fast (>300 MHz) CPU. */ -static int compress; /* = 0 */ - -/* Display test pattern - doesn't work yet either */ -static int testpat; /* = 0 */ - -/* Setting this to 1 will make the sensor output GBR422 instead of YUV420. Only - * affects RGB24 mode. */ -static int sensor_gbr; /* = 0 */ - -/* Dump raw pixel data. */ -static int dumppix; /* = 0 */ - -/* LED policy. Only works on some OV511+ cameras. 0=off, 1=on (default), 2=auto - * (on when open) */ -static int led = 1; - -/* Set this to 1 to dump the bridge register contents after initialization */ -static int dump_bridge; /* = 0 */ - -/* Set this to 1 to dump the sensor register contents after initialization */ -static int dump_sensor; /* = 0 */ - -/* Temporary option for debugging "works, but no image" problem. Prints the - * first 12 bytes of data (potentially a packet header) in each isochronous - * data frame. */ -static int printph; /* = 0 */ - -/* Compression parameters - I'm not exactly sure what these do yet */ -static int phy = 0x1f; -static int phuv = 0x05; -static int pvy = 0x06; -static int pvuv = 0x06; -static int qhy = 0x14; -static int qhuv = 0x03; -static int qvy = 0x04; -static int qvuv = 0x04; - -/* Light frequency. Set to 50 or 60 (Hz), or zero for default settings */ -static int lightfreq; /* = 0 */ +/********************************************************************** + * Module Parameters + * (See ov511.txt for detailed descriptions of these) + **********************************************************************/ -/* Set this to 1 to enable banding filter by default. Compensates for - * alternating horizontal light/dark bands caused by (usually fluorescent) - * lights */ -static int bandingfilter; /* = 0 */ +/* These variables (and all static globals) default to zero */ +static int autobright = 1; +static int autogain = 1; +static int autoexp = 1; +static int debug; +static int fix_rgb_offset; +static int snapshot; +static int force_rgb; +static int buf_timeout = 5; +static int cams = 1; +static int compress; +static int testpat; +static int sensor_gbr; +static int dumppix; +static int led = 1; +static int dump_bridge; +static int dump_sensor; +static int printph; +static int phy = 0x1f; +static int phuv = 0x05; +static int pvy = 0x06; +static int pvuv = 0x06; +static int qhy = 0x14; +static int qhuv = 0x03; +static int qvy = 0x04; +static int qvuv = 0x04; +static int lightfreq; +static int bandingfilter; /* Pixel clock divisor */ -static int clockdiv = -1; +static int clockdiv = -1; /* Isoc packet size */ -static int packetsize = -1; +static int packetsize = -1; /* Frame drop register (16h) */ -static int framedrop = -1; - -/* Allows picture settings (brightness, hue, etc...) to take effect immediately, - * even in the middle of a frame. This reduces the time to change settings, but - * can ruin frames during the change. Only affects OmniVision sensors. */ -static int fastset; /* = 0 */ - -/* Forces the palette to a specific value. If an application requests a - * different palette, it will be rejected. */ -static int force_palette; /* = 0 */ - -/* Set tuner type, if not autodetected */ -static int tuner = -1; - -/* Allows proper exposure of objects that are illuminated from behind. Only - * affects OmniVision sensors. */ -static int backlight; /* = 0 */ - -/* If you change this, you must also change the MODULE_PARM definition */ -#define OV511_MAX_UNIT_VIDEO 16 +static int framedrop = -1; -/* Allows specified minor numbers to be forced. They will be assigned in the - * order that devices are detected. Note that you cannot specify 0 as a minor - * number. If you do not specify any, the next available one will be used. This - * requires kernel 2.4.5 or later. */ +static int fastset; +static int force_palette; +static int tuner = -1; +static int backlight; static int unit_video[OV511_MAX_UNIT_VIDEO]; - -/* Remove zero-padding from uncompressed incoming data. This will compensate for - * the blocks of corruption that appear when the camera cannot keep up with the - * speed of the USB bus (eg. at low frame resolutions) */ -static int remove_zeros; /* = 0 */ +static int remove_zeros; MODULE_PARM(autobright, "i"); MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness"); @@ -289,6 +231,10 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); +/********************************************************************** + * Miscellaneous Globals + **********************************************************************/ + static struct usb_driver ov511_driver; static struct ov51x_decomp_ops *ov511_decomp_ops; @@ -303,20 +249,28 @@ /* MMX support is present in kernel and CPU. Checked upon decomp module load. */ static int ov51x_mmx_available; -/* Function prototypes */ -static void ov51x_clear_snapshot(struct usb_ov511 *); -static int ov51x_check_snapshot(struct usb_ov511 *); -static inline int sensor_get_picture(struct usb_ov511 *, - struct video_picture *); -static int sensor_get_exposure(struct usb_ov511 *, unsigned char *); -static int ov511_control_ioctl(struct inode *, struct file *, unsigned int, - unsigned long); +static __devinitdata struct usb_device_id device_table [] = { + { USB_DEVICE(VEND_OMNIVISION, PROD_OV511) }, + { USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) }, + { USB_DEVICE(VEND_OMNIVISION, PROD_OV518) }, + { USB_DEVICE(VEND_OMNIVISION, PROD_OV518PLUS) }, + { USB_DEVICE(VEND_MATTEL, PROD_ME2CAM) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, device_table); + +static unsigned char yQuanTable511[] = OV511_YQUANTABLE; +static unsigned char uvQuanTable511[] = OV511_UVQUANTABLE; +static unsigned char yQuanTable518[] = OV518_YQUANTABLE; +static unsigned char uvQuanTable518[] = OV518_UVQUANTABLE; /********************************************************************** - * List of known OV511-based cameras + * Symbolic Names **********************************************************************/ -static struct cam_list clist[] = { +/* Known OV511-based cameras */ +static struct symbolic_list camlist[] = { { 0, "Generic Camera (no ID)" }, { 1, "Mustek WCam 3X" }, { 3, "D-Link DSB-C300" }, @@ -332,58 +286,94 @@ { 100, "Lifeview RoboCam" }, { 102, "AverMedia InterCam Elite" }, { 112, "MediaForte MV300" }, /* or OV7110 evaluation kit */ + { 192, "Webeye 2000B" }, { -1, NULL } }; -static __devinitdata struct usb_device_id device_table [] = { - { USB_DEVICE(VEND_OMNIVISION, PROD_OV511) }, - { USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) }, - { USB_DEVICE(VEND_OMNIVISION, PROD_OV518) }, - { USB_DEVICE(VEND_OMNIVISION, PROD_OV518PLUS) }, - { USB_DEVICE(VEND_MATTEL, PROD_ME2CAM) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, device_table); - -#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) -static struct palette_list plist[] = { +/* Video4Linux1 Palettes */ +static struct symbolic_list v4l1_plist[] = { { VIDEO_PALETTE_GREY, "GREY" }, - { VIDEO_PALETTE_HI240, "HI240" }, - { VIDEO_PALETTE_RGB565, "RGB565" }, + { VIDEO_PALETTE_HI240, "HI240" }, + { VIDEO_PALETTE_RGB565, "RGB565" }, { VIDEO_PALETTE_RGB24, "RGB24" }, { VIDEO_PALETTE_RGB32, "RGB32" }, - { VIDEO_PALETTE_RGB555, "RGB555" }, - { VIDEO_PALETTE_YUV422, "YUV422" }, - { VIDEO_PALETTE_YUYV, "YUYV" }, - { VIDEO_PALETTE_UYVY, "UYVY" }, - { VIDEO_PALETTE_YUV420, "YUV420" }, - { VIDEO_PALETTE_YUV411, "YUV411" }, - { VIDEO_PALETTE_RAW, "RAW" }, + { VIDEO_PALETTE_RGB555, "RGB555" }, + { VIDEO_PALETTE_YUV422, "YUV422" }, + { VIDEO_PALETTE_YUYV, "YUYV" }, + { VIDEO_PALETTE_UYVY, "UYVY" }, + { VIDEO_PALETTE_YUV420, "YUV420" }, + { VIDEO_PALETTE_YUV411, "YUV411" }, + { VIDEO_PALETTE_RAW, "RAW" }, { VIDEO_PALETTE_YUV422P,"YUV422P" }, { VIDEO_PALETTE_YUV411P,"YUV411P" }, { VIDEO_PALETTE_YUV420P,"YUV420P" }, { VIDEO_PALETTE_YUV410P,"YUV410P" }, { -1, NULL } }; -#endif -static unsigned char yQuanTable511[] = OV511_YQUANTABLE; -static unsigned char uvQuanTable511[] = OV511_UVQUANTABLE; -static unsigned char yQuanTable518[] = OV518_YQUANTABLE; -static unsigned char uvQuanTable518[] = OV518_UVQUANTABLE; +static struct symbolic_list brglist[] = { + { BRG_OV511, "OV511" }, + { BRG_OV511PLUS, "OV511+" }, + { BRG_OV518, "OV518" }, + { BRG_OV518PLUS, "OV518+" }, + { -1, NULL } +}; + +static struct symbolic_list senlist[] = { + { SEN_OV76BE, "OV76BE" }, + { SEN_OV7610, "OV7610" }, + { SEN_OV7620, "OV7620" }, + { SEN_OV7620AE, "OV7620AE" }, + { SEN_OV6620, "OV6620" }, + { SEN_OV6630, "OV6630" }, + { SEN_OV6630AE, "OV6630AE" }, + { SEN_OV6630AF, "OV6630AF" }, + { SEN_OV8600, "OV8600" }, + { SEN_KS0127, "KS0127" }, + { SEN_KS0127B, "KS0127B" }, + { SEN_SAA7111A, "SAA7111A" }, + { -1, NULL } +}; + +/* URB error codes: */ +static struct symbolic_list urb_errlist[] = { + { -ENOSR, "Buffer error (overrun)" }, + { -EPIPE, "Stalled (device not responding)" }, + { -EOVERFLOW, "Babble (bad cable?)" }, + { -EPROTO, "Bit-stuff error (bad cable?)" }, + { -EILSEQ, "CRC/Timeout" }, + { -ETIMEDOUT, "NAK (device does not respond)" }, + { -1, NULL } +}; + +/********************************************************************** + * Prototypes + **********************************************************************/ + +static void ov51x_clear_snapshot(struct usb_ov511 *); +static int ov51x_check_snapshot(struct usb_ov511 *); +static inline int sensor_get_picture(struct usb_ov511 *, + struct video_picture *); +static int sensor_get_exposure(struct usb_ov511 *, unsigned char *); +static int ov51x_control_ioctl(struct inode *, struct file *, unsigned int, + unsigned long); + +/********************************************************************** + * + * Memory management + * + **********************************************************************/ /* 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. + * This is used when initializing the contents of the area. */ static inline unsigned long kvirt_to_pa(unsigned long adr) { - unsigned long va, kva, ret; + unsigned long kva, ret; - va = VMALLOC_VMADDR(adr); - kva = page_address(vmalloc_to_page(va)); + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ ret = __pa(kva); return ret; } @@ -392,12 +382,9 @@ rvmalloc(unsigned long size) { void *mem; - unsigned long adr, page; - - /* Round it off to PAGE_SIZE */ - size += (PAGE_SIZE - 1); - size &= ~(PAGE_SIZE - 1); + unsigned long adr; + size = PAGE_ALIGN(size); mem = vmalloc_32(size); if (!mem) return NULL; @@ -405,13 +392,9 @@ 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))); + mem_map_reserve(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; + size -= PAGE_SIZE; } return mem; @@ -420,23 +403,16 @@ static void rvfree(void *mem, unsigned long size) { - unsigned long adr, page; + unsigned long adr; 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 = (unsigned long) mem; + while ((long) size > 0) { + mem_map_unreserve(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; + size -= PAGE_SIZE; } vfree(mem); } @@ -452,7 +428,7 @@ extern struct proc_dir_entry *video_proc_entry; static struct file_operations ov511_control_fops = { - ioctl: ov511_control_ioctl, + ioctl: ov51x_control_ioctl, }; #define YES_NO(x) ((x) ? "yes" : "no") @@ -463,30 +439,29 @@ void *data) { char *out = page; - int i, j, len; - struct usb_ov511 *ov511 = data; + int i, len; + struct usb_ov511 *ov = data; struct video_picture p; unsigned char exp; - if (!ov511 || !ov511->dev) + if (!ov || !ov->dev) return -ENODEV; - sensor_get_picture(ov511, &p); - sensor_get_exposure(ov511, &exp); + sensor_get_picture(ov, &p); + sensor_get_exposure(ov, &exp); /* IMPORTANT: This output MUST be kept under PAGE_SIZE * or we need to get more sophisticated. */ 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"); - out += sprintf(out, "streaming : %s\n", YES_NO(ov511->streaming)); - out += sprintf(out, "grabbing : %s\n", YES_NO(ov511->grabbing)); - out += sprintf(out, "compress : %s\n", YES_NO(ov511->compress)); - out += sprintf(out, "subcapture : %s\n", YES_NO(ov511->sub_flag)); + out += sprintf(out, "custom_id : %d\n", ov->customid); + out += sprintf(out, "model : %s\n", ov->desc); + out += sprintf(out, "streaming : %s\n", YES_NO(ov->streaming)); + out += sprintf(out, "grabbing : %s\n", YES_NO(ov->grabbing)); + out += sprintf(out, "compress : %s\n", YES_NO(ov->compress)); + out += sprintf(out, "subcapture : %s\n", YES_NO(ov->sub_flag)); out += sprintf(out, "sub_size : %d %d %d %d\n", - ov511->subx, ov511->suby, ov511->subw, ov511->subh); + ov->subx, ov->suby, ov->subw, ov->subh); out += sprintf(out, "data_format : %s\n", force_rgb ? "RGB" : "BGR"); out += sprintf(out, "brightness : %d\n", p.brightness >> 8); @@ -498,42 +473,21 @@ for (i = 0; i < OV511_NUMFRAMES; i++) { out += sprintf(out, "frame : %d\n", i); out += sprintf(out, " depth : %d\n", - ov511->frame[i].depth); + ov->frame[i].depth); out += sprintf(out, " size : %d %d\n", - ov511->frame[i].width, ov511->frame[i].height); - out += sprintf(out, " format : "); - for (j = 0; plist[j].num >= 0; j++) { - if (plist[j].num == ov511->frame[i].format) { - out += sprintf(out, "%s\n", plist[j].name); - break; - } - } - if (plist[j].num < 0) - out += sprintf(out, "unknown\n"); + ov->frame[i].width, ov->frame[i].height); + out += sprintf(out, " format : %s\n", + symbolic(v4l1_plist, ov->frame[i].format)); out += sprintf(out, " data_buffer : 0x%p\n", - ov511->frame[i].data); + ov->frame[i].data); } - out += sprintf(out, "snap_enabled : %s\n", - YES_NO(ov511->snap_enabled)); + out += sprintf(out, "snap_enabled : %s\n", YES_NO(ov->snap_enabled)); out += sprintf(out, "bridge : %s\n", - ov511->bridge == BRG_OV511 ? "OV511" : - ov511->bridge == BRG_OV511PLUS ? "OV511+" : - ov511->bridge == BRG_OV518 ? "OV518" : - ov511->bridge == BRG_OV518PLUS ? "OV518+" : - "unknown"); + symbolic(brglist, ov->bridge)); out += sprintf(out, "sensor : %s\n", - ov511->sensor == SEN_OV6620 ? "OV6620" : - ov511->sensor == SEN_OV6630 ? "OV6630" : - ov511->sensor == SEN_OV7610 ? "OV7610" : - ov511->sensor == SEN_OV7620 ? "OV7620" : - ov511->sensor == SEN_OV7620AE ? "OV7620AE" : - ov511->sensor == SEN_OV8600 ? "OV8600" : - ov511->sensor == SEN_KS0127 ? "KS0127" : - ov511->sensor == SEN_KS0127B ? "KS0127B" : - ov511->sensor == SEN_SAA7111A ? "SAA7111A" : - "unknown"); - out += sprintf(out, "packet_size : %d\n", ov511->packet_size); - out += sprintf(out, "framebuffer : 0x%p\n", ov511->fbuf); + symbolic(senlist, ov->sensor)); + out += sprintf(out, "packet_size : %d\n", ov->packet_size); + out += sprintf(out, "framebuffer : 0x%p\n", ov->fbuf); len = out - page; len -= off; @@ -566,16 +520,16 @@ { char *out = page; int len, status; - struct usb_ov511 *ov511 = data; + struct usb_ov511 *ov = data; - if (!ov511 || !ov511->dev) + if (!ov || !ov->dev) return -ENODEV; - status = ov51x_check_snapshot(ov511); + status = ov51x_check_snapshot(ov); out += sprintf(out, "%d", status); if (status) - ov51x_clear_snapshot(ov511); + ov51x_clear_snapshot(ov); len = out - page; len -= off; @@ -593,89 +547,91 @@ } static void -create_proc_ov511_cam(struct usb_ov511 *ov511) +create_proc_ov511_cam(struct usb_ov511 *ov) { - char dirname[4]; + char dirname[10]; - if (!ov511_proc_entry || !ov511) + if (!ov511_proc_entry || !ov) return; /* Create per-device directory */ - sprintf(dirname, "%d", ov511->vdev.minor); + snprintf(dirname, 10, "%d", ov->vdev.minor); PDEBUG(4, "creating /proc/video/ov511/%s/", dirname); - ov511->proc_devdir = create_proc_entry(dirname, S_IFDIR, - ov511_proc_entry); - if (!ov511->proc_devdir) + ov->proc_devdir = create_proc_entry(dirname, S_IFDIR, ov511_proc_entry); + if (!ov->proc_devdir) return; + ov->proc_devdir->owner = THIS_MODULE; /* Create "info" entry (human readable device information) */ PDEBUG(4, "creating /proc/video/ov511/%s/info", dirname); - ov511->proc_info = create_proc_read_entry("info", - S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir, - ov511_read_proc_info, ov511); - if (!ov511->proc_info) + ov->proc_info = create_proc_read_entry("info", S_IFREG|S_IRUGO|S_IWUSR, + ov->proc_devdir, ov511_read_proc_info, ov); + if (!ov->proc_info) return; + ov->proc_info->owner = THIS_MODULE; /* Don't create it if old snapshot mode on (would cause race cond.) */ if (!snapshot) { /* Create "button" entry (snapshot button status) */ PDEBUG(4, "creating /proc/video/ov511/%s/button", dirname); - ov511->proc_button = create_proc_read_entry("button", - S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir, - ov511_read_proc_button, ov511); - if (!ov511->proc_button) + ov->proc_button = create_proc_read_entry("button", + S_IFREG|S_IRUGO|S_IWUSR, ov->proc_devdir, + ov511_read_proc_button, ov); + if (!ov->proc_button) return; } + ov->proc_button->owner = THIS_MODULE; /* Create "control" entry (ioctl() interface) */ PDEBUG(4, "creating /proc/video/ov511/%s/control", dirname); lock_kernel(); - ov511->proc_control = create_proc_entry("control", - S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir); - if (!ov511->proc_control) { + ov->proc_control = create_proc_entry("control", S_IFREG|S_IRUGO|S_IWUSR, + ov->proc_devdir); + if (!ov->proc_control) { unlock_kernel(); return; } - ov511->proc_control->data = ov511; - ov511->proc_control->proc_fops = &ov511_control_fops; + ov->proc_control->owner = THIS_MODULE; + ov->proc_control->data = ov; + ov->proc_control->proc_fops = &ov511_control_fops; unlock_kernel(); } static void -destroy_proc_ov511_cam(struct usb_ov511 *ov511) +destroy_proc_ov511_cam(struct usb_ov511 *ov) { - char dirname[4]; + char dirname[10]; - if (!ov511 || !ov511->proc_devdir) + if (!ov || !ov->proc_devdir) return; - sprintf(dirname, "%d", ov511->vdev.minor); + snprintf(dirname, 10, "%d", ov->vdev.minor); /* Destroy "control" entry */ - if (ov511->proc_control) { + if (ov->proc_control) { PDEBUG(4, "destroying /proc/video/ov511/%s/control", dirname); - remove_proc_entry("control", ov511->proc_devdir); - ov511->proc_control = NULL; + remove_proc_entry("control", ov->proc_devdir); + ov->proc_control = NULL; } /* Destroy "button" entry */ - if (ov511->proc_button) { + if (ov->proc_button) { PDEBUG(4, "destroying /proc/video/ov511/%s/button", dirname); - remove_proc_entry("button", ov511->proc_devdir); - ov511->proc_button = NULL; + remove_proc_entry("button", ov->proc_devdir); + ov->proc_button = NULL; } /* Destroy "info" entry */ - if (ov511->proc_info) { + if (ov->proc_info) { PDEBUG(4, "destroying /proc/video/ov511/%s/info", dirname); - remove_proc_entry("info", ov511->proc_devdir); - ov511->proc_info = NULL; + remove_proc_entry("info", ov->proc_devdir); + ov->proc_info = NULL; } /* Destroy per-device directory */ PDEBUG(4, "destroying /proc/video/ov511/%s/", dirname); remove_proc_entry(dirname, ov511_proc_entry); - ov511->proc_devdir = NULL; + ov->proc_devdir = NULL; } static void @@ -717,64 +673,71 @@ * **********************************************************************/ +/* Write an OV51x register */ static int -ov511_reg_write(struct usb_device *dev, unsigned char reg, unsigned char value) +reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) { int rc; PDEBUG(5, "0x%02X:0x%02X", reg, value); - rc = usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), + down(&ov->cbuf_lock); + ov->cbuf[0] = value; + rc = usb_control_msg(ov->dev, + usb_sndctrlpipe(ov->dev, 0), 2 /* REG_IO */, USB_TYPE_CLASS | USB_RECIP_DEVICE, - 0, (__u16)reg, &value, 1, HZ); + 0, (__u16)reg, &ov->cbuf[0], 1, HZ); + up(&ov->cbuf_lock); if (rc < 0) - err("reg write: error %d", rc); + err("reg write: error %d: %s", rc, symbolic(urb_errlist, rc)); return rc; } +/* Read from an OV51x register */ /* returns: negative is error, pos or zero is data */ static int -ov511_reg_read(struct usb_device *dev, unsigned char reg) +reg_r(struct usb_ov511 *ov, unsigned char reg) { int rc; - unsigned char buffer[1]; - rc = usb_control_msg(dev, - usb_rcvctrlpipe(dev, 0), + down(&ov->cbuf_lock); + rc = usb_control_msg(ov->dev, + usb_rcvctrlpipe(ov->dev, 0), 2 /* REG_IO */, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE, - 0, (__u16)reg, buffer, 1, HZ); + 0, (__u16)reg, &ov->cbuf[0], 1, HZ); - PDEBUG(5, "0x%02X:0x%02X", reg, buffer[0]); + PDEBUG(5, "0x%02X:0x%02X", reg, ov->cbuf[0]); - if (rc < 0) { - err("reg read: error %d", rc); - return rc; - } else { - return buffer[0]; - } + if (rc < 0) + err("reg read: error %d: %s", rc, symbolic(urb_errlist, rc)); + else + rc = ov->cbuf[0]; + + up(&ov->cbuf_lock); + + return rc; } /* - * Writes bits at positions specified by mask to a reg. Bits that are in + * Writes bits at positions specified by mask to an OV51x reg. Bits that are in * the same position as 1's in "mask" are cleared and set to "value". Bits * that are in the same position as 0's in "mask" are preserved, regardless * of their respective state in "value". */ static int -ov511_reg_write_mask(struct usb_device *dev, - unsigned char reg, - unsigned char value, - unsigned char mask) +reg_w_mask(struct usb_ov511 *ov, + unsigned char reg, + unsigned char value, + unsigned char mask) { int ret; unsigned char oldval, newval; - ret = ov511_reg_read(dev, reg); + ret = reg_r(ov, reg); if (ret < 0) return ret; @@ -783,45 +746,45 @@ value &= mask; /* Enforce mask on value */ newval = oldval | value; /* Set the desired bits */ - return (ov511_reg_write(dev, reg, newval)); + return (reg_w(ov, reg, newval)); } -/* Writes multiple (n) values to a single register. Only valid with certain - * registers (0x30 and 0xc4 - 0xce). Used for writing 16 and 24-bit values. */ +/* + * Writes multiple (n) byte value to a single register. Only valid with certain + * registers (0x30 and 0xc4 - 0xce). + */ static int -ov518_reg_write_multi(struct usb_device *dev, - unsigned char reg, - unsigned char *values, - int n) +ov518_reg_w32(struct usb_ov511 *ov, unsigned char reg, u32 val, int n) { int rc; - PDEBUG(5, "0x%02X:[multiple], n=%d", reg, n); // FIXME + PDEBUG(5, "0x%02X:%7d, n=%d", reg, val, n); - if (values == NULL) { - err("reg write multiple: NULL buffer"); - return -EINVAL; - } + down(&ov->cbuf_lock); + + *((u32 *)ov->cbuf) = __cpu_to_le32(val); - rc = usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), + rc = usb_control_msg(ov->dev, + usb_sndctrlpipe(ov->dev, 0), 2 /* REG_IO */, USB_TYPE_CLASS | USB_RECIP_DEVICE, - 0, (__u16)reg, values, n, HZ); + 0, (__u16)reg, ov->cbuf, n, HZ); + up(&ov->cbuf_lock); if (rc < 0) - err("reg write multiple: error %d", rc); + err("reg write multiple: error %d: %s", rc, + symbolic(urb_errlist, rc)); return rc; } static int -ov511_upload_quan_tables(struct usb_device *dev) +ov511_upload_quan_tables(struct usb_ov511 *ov) { unsigned char *pYTable = yQuanTable511; unsigned char *pUVTable = uvQuanTable511; unsigned char val0, val1; - int i, rc, reg = OV511_OMNICE_Y_LUT_BEGIN; + int i, rc, reg = R511_COMP_LUT_BEGIN; PDEBUG(4, "Uploading quantization tables"); @@ -834,7 +797,7 @@ val0 &= 0x0f; val1 &= 0x0f; val0 |= val1 << 4; - rc = ov511_reg_write(dev, reg, val0); + rc = reg_w(ov, reg, val0); if (rc < 0) return rc; } @@ -846,8 +809,7 @@ val0 &= 0x0f; val1 &= 0x0f; val0 |= val1 << 4; - rc = ov511_reg_write(dev, reg + OV511_QUANTABLESIZE / 2, - val0); + rc = reg_w(ov, reg + OV511_QUANTABLESIZE/2, val0); if (rc < 0) return rc; } @@ -860,12 +822,12 @@ /* OV518 quantization tables are 8x4 (instead of 8x8) */ static int -ov518_upload_quan_tables(struct usb_device *dev) +ov518_upload_quan_tables(struct usb_ov511 *ov) { unsigned char *pYTable = yQuanTable518; unsigned char *pUVTable = uvQuanTable518; unsigned char val0, val1; - int i, rc, reg = OV511_OMNICE_Y_LUT_BEGIN; + int i, rc, reg = R511_COMP_LUT_BEGIN; PDEBUG(4, "Uploading quantization tables"); @@ -878,7 +840,7 @@ val0 &= 0x0f; val1 &= 0x0f; val0 |= val1 << 4; - rc = ov511_reg_write(dev, reg, val0); + rc = reg_w(ov, reg, val0); if (rc < 0) return rc; } @@ -890,8 +852,7 @@ val0 &= 0x0f; val1 &= 0x0f; val0 |= val1 << 4; - rc = ov511_reg_write(dev, reg + OV518_QUANTABLESIZE / 2, - val0); + rc = reg_w(ov, reg + OV518_QUANTABLESIZE/2, val0); if (rc < 0) return rc; } @@ -902,13 +863,39 @@ return 0; } +static int +ov51x_reset(struct usb_ov511 *ov, unsigned char reset_type) +{ + int rc; + + /* Setting bit 0 not allowed on 518/518Plus */ + if (ov->bclass == BCL_OV518) + reset_type &= 0xfe; + + PDEBUG(4, "Reset: type=0x%X", reset_type); + + rc = reg_w(ov, R51x_SYS_RESET, reset_type); + rc = reg_w(ov, R51x_SYS_RESET, 0); + + if (rc < 0) + err("reset: command failed"); + + return rc; +} + +/********************************************************************** + * + * I2C (sensor) I/O + * + **********************************************************************/ + /* NOTE: Do not call this function directly! * The OV518 I2C I/O procedure is different, hence, this function. - * This is normally only called from ov51x_i2c_write(). Note that this function + * This is normally only called from i2c_w(). Note that this function * always succeeds regardless of whether the sensor is present and working. */ static int -ov518_i2c_write_internal(struct usb_device *dev, +ov518_i2c_write_internal(struct usb_ov511 *ov, unsigned char reg, unsigned char value) { @@ -917,27 +904,23 @@ PDEBUG(5, "0x%02X:0x%02X", reg, value); /* Select camera register */ - rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE, reg); - if (rc < 0) goto error; + rc = reg_w(ov, R51x_I2C_SADDR_3, reg); + if (rc < 0) return rc; /* Write "value" to I2C data port of OV511 */ - rc = ov511_reg_write(dev, OV511_REG_I2C_DATA_PORT, value); - if (rc < 0) goto error; + rc = reg_w(ov, R51x_I2C_DATA, value); + if (rc < 0) return rc; /* Initiate 3-byte write cycle */ - rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x01); - if (rc < 0) goto error; + rc = reg_w(ov, R518_I2C_CTL, 0x01); + if (rc < 0) return rc; return 0; - -error: - err("ov518 i2c write: error %d", rc); - return rc; } /* NOTE: Do not call this function directly! */ static int -ov511_i2c_write_internal(struct usb_device *dev, +ov511_i2c_write_internal(struct usb_ov511 *ov, unsigned char reg, unsigned char value) { @@ -948,194 +931,171 @@ /* Three byte write cycle */ for (retries = OV511_I2C_RETRIES; ; ) { /* Select camera register */ - rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE, - reg); - if (rc < 0) goto error; + rc = reg_w(ov, R51x_I2C_SADDR_3, reg); + if (rc < 0) return rc; /* Write "value" to I2C data port of OV511 */ - rc = ov511_reg_write(dev, OV511_REG_I2C_DATA_PORT, value); - if (rc < 0) goto error; + rc = reg_w(ov, R51x_I2C_DATA, value); + if (rc < 0) return rc; /* Initiate 3-byte write cycle */ - rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x01); - if (rc < 0) goto error; + rc = reg_w(ov, R511_I2C_CTL, 0x01); + if (rc < 0) return rc; - do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL); + do rc = reg_r(ov, R511_I2C_CTL); while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */ - if (rc < 0) goto error; + if (rc < 0) return rc; if ((rc&2) == 0) /* Ack? */ break; #if 0 /* I2C abort */ - ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10); + reg_w(ov, R511_I2C_CTL, 0x10); #endif if (--retries < 0) { err("i2c write retries exhausted"); - rc = -1; - goto error; + return -1; } } return 0; - -error: - err("i2c write: error %d", rc); - return rc; } /* NOTE: Do not call this function directly! * The OV518 I2C I/O procedure is different, hence, this function. - * This is normally only called from ov51x_i2c_read(). Note that this function + * This is normally only called from i2c_r(). Note that this function * always succeeds regardless of whether the sensor is present and working. */ static int -ov518_i2c_read_internal(struct usb_device *dev, unsigned char reg) +ov518_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) { int rc, value; /* Select camera register */ - rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg); - if (rc < 0) goto error; + rc = reg_w(ov, R51x_I2C_SADDR_2, reg); + if (rc < 0) return rc; /* Initiate 2-byte write cycle */ - rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x03); - if (rc < 0) goto error; + rc = reg_w(ov, R518_I2C_CTL, 0x03); + if (rc < 0) return rc; /* Initiate 2-byte read cycle */ - rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x05); - if (rc < 0) goto error; + rc = reg_w(ov, R518_I2C_CTL, 0x05); + if (rc < 0) return rc; - value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT); + value = reg_r(ov, R51x_I2C_DATA); PDEBUG(5, "0x%02X:0x%02X", reg, value); return value; - -error: - err("ov518 i2c read: error %d", rc); - return rc; } /* NOTE: Do not call this function directly! * returns: negative is error, pos or zero is data */ static int -ov511_i2c_read_internal(struct usb_device *dev, unsigned char reg) +ov511_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) { int rc, value, retries; /* Two byte write cycle */ for (retries = OV511_I2C_RETRIES; ; ) { /* Select camera register */ - rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, - reg); - if (rc < 0) goto error; + rc = reg_w(ov, R51x_I2C_SADDR_2, reg); + if (rc < 0) return rc; /* Initiate 2-byte write cycle */ - rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x03); - if (rc < 0) goto error; + rc = reg_w(ov, R511_I2C_CTL, 0x03); + if (rc < 0) return rc; - do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL); + do rc = reg_r(ov, R511_I2C_CTL); while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */ - if (rc < 0) goto error; + if (rc < 0) return rc; if ((rc&2) == 0) /* Ack? */ break; /* I2C abort */ - ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10); + reg_w(ov, R511_I2C_CTL, 0x10); if (--retries < 0) { err("i2c write retries exhausted"); - rc = -1; - goto error; + return -1; } } /* Two byte read cycle */ for (retries = OV511_I2C_RETRIES; ; ) { /* Initiate 2-byte read cycle */ - rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05); - if (rc < 0) goto error; + rc = reg_w(ov, R511_I2C_CTL, 0x05); + if (rc < 0) return rc; - do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL); + do rc = reg_r(ov, R511_I2C_CTL); while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */ - if (rc < 0) goto error; + if (rc < 0) return rc; if ((rc&2) == 0) /* Ack? */ break; /* I2C abort */ - rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10); - if (rc < 0) goto error; + rc = reg_w(ov, R511_I2C_CTL, 0x10); + if (rc < 0) return rc; if (--retries < 0) { err("i2c read retries exhausted"); - rc = -1; - goto error; + return -1; } } - value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT); + value = reg_r(ov, R51x_I2C_DATA); PDEBUG(5, "0x%02X:0x%02X", reg, value); - /* This is needed to make ov51x_i2c_write() work */ - rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05); + /* This is needed to make i2c_w() work */ + rc = reg_w(ov, R511_I2C_CTL, 0x05); if (rc < 0) - goto error; + return rc; return value; - -error: - err("i2c read: error %d", rc); - return rc; } /* returns: negative is error, pos or zero is data */ static int -ov51x_i2c_read(struct usb_ov511 *ov511, unsigned char reg) +i2c_r(struct usb_ov511 *ov, unsigned char reg) { int rc; - struct usb_device *dev = ov511->dev; - down(&ov511->i2c_lock); + down(&ov->i2c_lock); - if (dev->descriptor.idProduct == PROD_OV518 || - dev->descriptor.idProduct == PROD_OV518PLUS) - rc = ov518_i2c_read_internal(dev, reg); + if (ov->bclass == BCL_OV518) + rc = ov518_i2c_read_internal(ov, reg); else - rc = ov511_i2c_read_internal(dev, reg); + rc = ov511_i2c_read_internal(ov, reg); - up(&ov511->i2c_lock); + up(&ov->i2c_lock); return rc; } static int -ov51x_i2c_write(struct usb_ov511 *ov511, - unsigned char reg, - unsigned char value) +i2c_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) { int rc; - struct usb_device *dev = ov511->dev; - down(&ov511->i2c_lock); + down(&ov->i2c_lock); - if (dev->descriptor.idProduct == PROD_OV518 || - dev->descriptor.idProduct == PROD_OV518PLUS) - rc = ov518_i2c_write_internal(dev, reg, value); + if (ov->bclass == BCL_OV518) + rc = ov518_i2c_write_internal(ov, reg, value); else - rc = ov511_i2c_write_internal(dev, reg, value); + rc = ov511_i2c_write_internal(ov, reg, value); - up(&ov511->i2c_lock); + up(&ov->i2c_lock); return rc; } /* Do not call this function directly! */ static int -ov51x_i2c_write_mask_internal(struct usb_device *dev, +ov51x_i2c_write_mask_internal(struct usb_ov511 *ov, unsigned char reg, unsigned char value, unsigned char mask) @@ -1146,11 +1106,10 @@ if (mask == 0xff) { newval = value; } else { - if (dev->descriptor.idProduct == PROD_OV518 || - dev->descriptor.idProduct == PROD_OV518PLUS) - rc = ov518_i2c_read_internal(dev, reg); + if (ov->bclass == BCL_OV518) + rc = ov518_i2c_read_internal(ov, reg); else - rc = ov511_i2c_read_internal(dev, reg); + rc = ov511_i2c_read_internal(ov, reg); if (rc < 0) return rc; @@ -1160,11 +1119,10 @@ newval = oldval | value; /* Set the desired bits */ } - if (dev->descriptor.idProduct == PROD_OV518 || - dev->descriptor.idProduct == PROD_OV518PLUS) - return (ov518_i2c_write_internal(dev, reg, newval)); + if (ov->bclass == BCL_OV518) + return (ov518_i2c_write_internal(ov, reg, newval)); else - return (ov511_i2c_write_internal(dev, reg, newval)); + return (ov511_i2c_write_internal(ov, reg, newval)); } /* Writes bits at positions specified by mask to an I2C reg. Bits that are in @@ -1173,194 +1131,204 @@ * of their respective state in "value". */ static int -ov51x_i2c_write_mask(struct usb_ov511 *ov511, - unsigned char reg, - unsigned char value, - unsigned char mask) +i2c_w_mask(struct usb_ov511 *ov, + unsigned char reg, + unsigned char value, + unsigned char mask) { int rc; - struct usb_device *dev = ov511->dev; - down(&ov511->i2c_lock); - rc = ov51x_i2c_write_mask_internal(dev, reg, value, mask); - up(&ov511->i2c_lock); + down(&ov->i2c_lock); + rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask); + up(&ov->i2c_lock); return rc; } /* Write to a specific I2C slave ID and register, using the specified mask */ static int -ov51x_i2c_write_slave(struct usb_ov511 *ov511, - unsigned char slave, - unsigned char reg, - unsigned char value, - unsigned char mask) +i2c_w_slave(struct usb_ov511 *ov, + unsigned char slave, + unsigned char reg, + unsigned char value, + unsigned char mask) { int rc = 0; - struct usb_device *dev = ov511->dev; - down(&ov511->i2c_lock); + down(&ov->i2c_lock); /* Set new slave IDs */ - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) { + if (reg_w(ov, R51x_I2C_W_SID, slave) < 0) { rc = -EIO; goto out; } - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) { + if (reg_w(ov, R51x_I2C_R_SID, slave + 1) < 0) { rc = -EIO; goto out; } - rc = ov51x_i2c_write_mask_internal(dev, reg, value, mask); + rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask); /* Don't bail out yet if error; IDs must be restored */ /* Restore primary IDs */ - slave = ov511->primary_i2c_slave; - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) { + slave = ov->primary_i2c_slave; + if (reg_w(ov, R51x_I2C_W_SID, slave) < 0) { rc = -EIO; goto out; } - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) { + if (reg_w(ov, R51x_I2C_R_SID, slave + 1) < 0) { rc = -EIO; goto out; } out: - up(&ov511->i2c_lock); + up(&ov->i2c_lock); return rc; } /* Read from a specific I2C slave ID and register */ static int -ov51x_i2c_read_slave(struct usb_ov511 *ov511, - unsigned char slave, - unsigned char reg) +i2c_r_slave(struct usb_ov511 *ov, + unsigned char slave, + unsigned char reg) { int rc; - struct usb_device *dev = ov511->dev; - down(&ov511->i2c_lock); + down(&ov->i2c_lock); /* Set new slave IDs */ - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) { + if (reg_w(ov, R51x_I2C_W_SID, slave) < 0) { rc = -EIO; goto out; } - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) { + if (reg_w(ov, R51x_I2C_R_SID, slave + 1) < 0) { rc = -EIO; goto out; } - if (dev->descriptor.idProduct == PROD_OV518 || - dev->descriptor.idProduct == PROD_OV518PLUS) - rc = ov518_i2c_read_internal(dev, reg); + if (ov->bclass == BCL_OV518) + rc = ov518_i2c_read_internal(ov, reg); else - rc = ov511_i2c_read_internal(dev, reg); + rc = ov511_i2c_read_internal(ov, reg); /* Don't bail out yet if error; IDs must be restored */ /* Restore primary IDs */ - slave = ov511->primary_i2c_slave; - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) { + slave = ov->primary_i2c_slave; + if (reg_w(ov, R51x_I2C_W_SID, slave) < 0) { rc = -EIO; goto out; } - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) { + if (reg_w(ov, R51x_I2C_R_SID, slave + 1) < 0) { rc = -EIO; goto out; } out: - up(&ov511->i2c_lock); + up(&ov->i2c_lock); return rc; } +/* Sets I2C read and write slave IDs. Returns <0 for error */ +static int +ov51x_set_slave_ids(struct usb_ov511 *ov, unsigned char sid) +{ + down(&ov->i2c_lock); + + if (reg_w(ov, R51x_I2C_W_SID, sid) < 0) + return -EIO; + + if (reg_w(ov, R51x_I2C_R_SID, sid + 1) < 0) + return -EIO; + + if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) + return -EIO; + + up(&ov->i2c_lock); + + return 0; +} + static int -ov511_write_regvals(struct usb_ov511 *ov511, - struct ov511_regvals * pRegvals) +write_regvals(struct usb_ov511 *ov, struct ov511_regvals * pRegvals) { int rc; - struct usb_device *dev = ov511->dev; while (pRegvals->bus != OV511_DONE_BUS) { if (pRegvals->bus == OV511_REG_BUS) { - if ((rc = ov511_reg_write(dev, pRegvals->reg, - pRegvals->val)) < 0) - goto error; + if ((rc = reg_w(ov, pRegvals->reg, pRegvals->val)) < 0) + return rc; } else if (pRegvals->bus == OV511_I2C_BUS) { - if ((rc = ov51x_i2c_write(ov511, pRegvals->reg, - pRegvals->val)) < 0) - goto error; + if ((rc = i2c_w(ov, pRegvals->reg, pRegvals->val)) < 0) + return rc; } else { err("Bad regval array"); - rc = -1; - goto error; + return -1; } pRegvals++; } return 0; - -error: - err("write regvals: error %d", rc); - return rc; } #ifdef OV511_DEBUG static void -ov511_dump_i2c_range(struct usb_ov511 *ov511, int reg1, int regn) +dump_i2c_range(struct usb_ov511 *ov, int reg1, int regn) { int i; int rc; + for (i = reg1; i <= regn; i++) { - rc = ov51x_i2c_read(ov511, i); + rc = i2c_r(ov, i); info("OV7610[0x%X] = 0x%X", i, rc); } } static void -ov51x_dump_i2c_regs(struct usb_ov511 *ov511) +dump_i2c_regs(struct usb_ov511 *ov) { info("I2C REGS"); - ov511_dump_i2c_range(ov511, 0x00, 0x7C); + dump_i2c_range(ov, 0x00, 0x7C); } static void -ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn) +dump_reg_range(struct usb_ov511 *ov, int reg1, int regn) { int i; int rc; + for (i = reg1; i <= regn; i++) { - rc = ov511_reg_read(dev, i); - info("OV511[0x%X] = 0x%X", i, rc); + rc = reg_r(ov, i); + info("OV511[0x%X] = 0x%X", i, rc); } } +/* FIXME: Should there be an OV518 version of this? */ static void -ov511_dump_regs(struct usb_device *dev) +ov511_dump_regs(struct usb_ov511 *ov) { info("CAMERA INTERFACE REGS"); - ov511_dump_reg_range(dev, 0x10, 0x1f); + dump_reg_range(ov, 0x10, 0x1f); info("DRAM INTERFACE REGS"); - ov511_dump_reg_range(dev, 0x20, 0x23); + dump_reg_range(ov, 0x20, 0x23); info("ISO FIFO REGS"); - ov511_dump_reg_range(dev, 0x30, 0x31); + dump_reg_range(ov, 0x30, 0x31); info("PIO REGS"); - ov511_dump_reg_range(dev, 0x38, 0x39); - ov511_dump_reg_range(dev, 0x3e, 0x3e); + dump_reg_range(ov, 0x38, 0x39); + dump_reg_range(ov, 0x3e, 0x3e); info("I2C REGS"); - ov511_dump_reg_range(dev, 0x40, 0x49); + dump_reg_range(ov, 0x40, 0x49); info("SYSTEM CONTROL REGS"); - ov511_dump_reg_range(dev, 0x50, 0x55); - ov511_dump_reg_range(dev, 0x5e, 0x5f); + dump_reg_range(ov, 0x50, 0x55); + dump_reg_range(ov, 0x5e, 0x5f); info("OmniCE REGS"); - ov511_dump_reg_range(dev, 0x70, 0x79); + dump_reg_range(ov, 0x70, 0x79); /* NOTE: Quantization tables are not readable. You will get the value * in reg. 0x79 for every table register */ - ov511_dump_reg_range(dev, 0x80, 0x9f); - ov511_dump_reg_range(dev, 0xa0, 0xbf); + dump_reg_range(ov, 0x80, 0x9f); + dump_reg_range(ov, 0xa0, 0xbf); } #endif @@ -1373,7 +1341,7 @@ /* For as-yet unimplemented I2C interface */ static void -call_i2c_clients(struct usb_ov511 *ov511, unsigned int cmd, +call_i2c_clients(struct usb_ov511 *ov, unsigned int cmd, void *arg) { /* Do nothing */ @@ -1381,59 +1349,33 @@ /*****************************************************************************/ -static int -ov511_reset(struct usb_ov511 *ov511, unsigned char reset_type) -{ - int rc; - - /* Setting bit 0 not allowed on 518/518Plus */ - if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) - reset_type &= 0xfe; - - PDEBUG(4, "Reset: type=0x%X", reset_type); - - rc = ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, reset_type); - rc = ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0); - - if (rc < 0) - err("reset: command failed"); - - return rc; -} - /* Temporarily stops OV511 from functioning. Must do this before changing * registers while the camera is streaming */ static inline int -ov511_stop(struct usb_ov511 *ov511) +ov51x_stop(struct usb_ov511 *ov) { PDEBUG(4, "stopping"); - ov511->stopped = 1; - if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) - return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, - 0x3a)); + ov->stopped = 1; + if (ov->bclass == BCL_OV518) + return (reg_w(ov, R51x_SYS_RESET, 0x3a)); else - return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, - 0x3d)); + return (reg_w(ov, R51x_SYS_RESET, 0x3d)); } /* Restarts OV511 after ov511_stop() is called. Has no effect if it is not * actually stopped (for performance). */ static inline int -ov511_restart(struct usb_ov511 *ov511) +ov51x_restart(struct usb_ov511 *ov) { - if (ov511->stopped) { + if (ov->stopped) { PDEBUG(4, "restarting"); - ov511->stopped = 0; + ov->stopped = 0; /* Reinitialize the stream */ - if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) - ov511_reg_write(ov511->dev, 0x2f, 0x80); + if (ov->bclass == BCL_OV518) + reg_w(ov, 0x2f, 0x80); - return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, - 0x00)); + return (reg_w(ov, R51x_SYS_RESET, 0x00)); } return 0; @@ -1441,14 +1383,13 @@ /* Resets the hardware snapshot button */ static void -ov51x_clear_snapshot(struct usb_ov511 *ov511) +ov51x_clear_snapshot(struct usb_ov511 *ov) { - if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) { - ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); - ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03); - ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); - } else if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) { + if (ov->bclass == BCL_OV511) { + reg_w(ov, R51x_SYS_SNAP, 0x01); + reg_w(ov, R51x_SYS_SNAP, 0x03); + reg_w(ov, R51x_SYS_SNAP, 0x01); + } else if (ov->bclass == BCL_OV518) { warn("snapshot reset not supported yet on OV518(+)"); } else { err("clear snap: invalid bridge type"); @@ -1459,19 +1400,18 @@ /* Checks the status of the snapshot button. Returns 1 if it was pressed since * it was last cleared, and zero in all other cases (including errors) */ static int -ov51x_check_snapshot(struct usb_ov511 *ov511) +ov51x_check_snapshot(struct usb_ov511 *ov) { int ret, status = 0; - if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) { - ret = ov511_reg_read(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT); + if (ov->bclass == BCL_OV511) { + ret = reg_r(ov, R51x_SYS_SNAP); if (ret < 0) { err("Error checking snspshot status (%d)", ret); } else if (ret & 0x08) { status = 1; } - } else if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) { + } else if (ov->bclass == BCL_OV518) { warn("snapshot check not supported yet on OV518(+)"); } else { err("check snap: invalid bridge type"); @@ -1480,53 +1420,33 @@ return status; } -/* Sets I2C read and write slave IDs. Returns <0 for error */ -static int -ov51x_set_slave_ids(struct usb_ov511 *ov511, - unsigned char write_id, - unsigned char read_id) -{ - struct usb_device *dev = ov511->dev; - - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, write_id) < 0) - return -EIO; - - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, read_id) < 0) - return -EIO; - - if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0) - return -EIO; - - return 0; -} - /* This does an initial reset of an OmniVision sensor and ensures that I2C * is synchronized. Returns <0 for failure. */ static int -ov51x_init_ov_sensor(struct usb_ov511 *ov511) +init_ov_sensor(struct usb_ov511 *ov) { int i, success; /* Reset the sensor */ - if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) return -EIO; + if (i2c_w(ov, 0x12, 0x80) < 0) return -EIO; /* Wait for it to initialize */ schedule_timeout (1 + 150 * HZ / 1000); for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { - if ((ov51x_i2c_read(ov511, OV7610_REG_ID_HIGH) == 0x7F) && - (ov51x_i2c_read(ov511, OV7610_REG_ID_LOW) == 0xA2)) { + if ((i2c_r(ov, OV7610_REG_ID_HIGH) == 0x7F) && + (i2c_r(ov, OV7610_REG_ID_LOW) == 0xA2)) { success = 1; continue; } /* Reset the sensor */ - if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) return -EIO; + if (i2c_w(ov, 0x12, 0x80) < 0) return -EIO; /* Wait for it to initialize */ schedule_timeout(1 + 150 * HZ / 1000); /* Dummy read to sync I2C */ - if (ov51x_i2c_read(ov511, 0x00) < 0) return -EIO; + if (i2c_r(ov, 0x00) < 0) return -EIO; } if (!success) @@ -1538,16 +1458,16 @@ } static int -ov511_set_packet_size(struct usb_ov511 *ov511, int size) +ov51x_set_packet_size(struct usb_ov511 *ov, int size) { int alt, mult; - if (ov511_stop(ov511) < 0) + if (ov51x_stop(ov) < 0) return -EIO; mult = size >> 5; - if (ov511->bridge == BRG_OV511) { + if (ov->bridge == BRG_OV511) { if (size == 0) alt = OV511_ALT_SIZE_0; else if (size == 257) alt = OV511_ALT_SIZE_257; else if (size == 513) alt = OV511_ALT_SIZE_513; @@ -1557,7 +1477,7 @@ err("Set packet size: invalid size (%d)", size); return -EINVAL; } - } else if (ov511->bridge == BRG_OV511PLUS) { + } else if (ov->bridge == BRG_OV511PLUS) { if (size == 0) alt = OV511PLUS_ALT_SIZE_0; else if (size == 33) alt = OV511PLUS_ALT_SIZE_33; else if (size == 129) alt = OV511PLUS_ALT_SIZE_129; @@ -1570,8 +1490,7 @@ err("Set packet size: invalid size (%d)", size); return -EINVAL; } - } else if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) { + } else if (ov->bclass == BCL_OV518) { if (size == 0) alt = OV518_ALT_SIZE_0; else if (size == 128) alt = OV518_ALT_SIZE_128; else if (size == 256) alt = OV518_ALT_SIZE_256; @@ -1592,32 +1511,30 @@ PDEBUG(3, "set packet size: %d, mult=%d, alt=%d", size, mult, alt); // FIXME: Don't know how to do this on OV518 yet - if (ov511->bridge != BRG_OV518 && - ov511->bridge != BRG_OV518PLUS) { - if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE, + if (ov->bclass == BCL_OV511) { + if (reg_w(ov, R51x_FIFO_PSIZE, mult) < 0) { return -EIO; } } - if (usb_set_interface(ov511->dev, ov511->iface, alt) < 0) { + if (usb_set_interface(ov->dev, ov->iface, alt) < 0) { err("Set packet size: set interface error"); return -EBUSY; } /* Initialize the stream */ - if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) - if (ov511_reg_write(ov511->dev, 0x2f, 0x80) < 0) + if (ov->bclass == BCL_OV518) + if (reg_w(ov, 0x2f, 0x80) < 0) return -EIO; // FIXME - Should we only reset the FIFO? - if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0) + if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) return -EIO; - ov511->packet_size = size; + ov->packet_size = size; - if (ov511_restart(ov511) < 0) + if (ov51x_restart(ov) < 0) return -EIO; return 0; @@ -1625,51 +1542,49 @@ /* Upload compression params and quantization tables. Returns 0 for success. */ static int -ov511_init_compression(struct usb_ov511 *ov511) +ov511_init_compression(struct usb_ov511 *ov) { - struct usb_device *dev = ov511->dev; int rc = 0; - if (!ov511->compress_inited) { + if (!ov->compress_inited) { - ov511_reg_write(dev, 0x70, phy); - ov511_reg_write(dev, 0x71, phuv); - ov511_reg_write(dev, 0x72, pvy); - ov511_reg_write(dev, 0x73, pvuv); - ov511_reg_write(dev, 0x74, qhy); - ov511_reg_write(dev, 0x75, qhuv); - ov511_reg_write(dev, 0x76, qvy); - ov511_reg_write(dev, 0x77, qvuv); + reg_w(ov, 0x70, phy); + reg_w(ov, 0x71, phuv); + reg_w(ov, 0x72, pvy); + reg_w(ov, 0x73, pvuv); + reg_w(ov, 0x74, qhy); + reg_w(ov, 0x75, qhuv); + reg_w(ov, 0x76, qvy); + reg_w(ov, 0x77, qvuv); - if (ov511_upload_quan_tables(dev) < 0) { + if (ov511_upload_quan_tables(ov) < 0) { err("Error uploading quantization tables"); rc = -EIO; goto out; } } - ov511->compress_inited = 1; + ov->compress_inited = 1; out: return rc; } /* Upload compression params and quantization tables. Returns 0 for success. */ static int -ov518_init_compression(struct usb_ov511 *ov511) +ov518_init_compression(struct usb_ov511 *ov) { - struct usb_device *dev = ov511->dev; int rc = 0; - if (!ov511->compress_inited) { + if (!ov->compress_inited) { - if (ov518_upload_quan_tables(dev) < 0) { + if (ov518_upload_quan_tables(ov) < 0) { err("Error uploading quantization tables"); rc = -EIO; goto out; } } - ov511->compress_inited = 1; + ov->compress_inited = 1; out: return rc; } @@ -1678,22 +1593,22 @@ /* Sets sensor's contrast setting to "val" */ static int -sensor_set_contrast(struct usb_ov511 *ov511, unsigned short val) +sensor_set_contrast(struct usb_ov511 *ov, unsigned short val) { int rc; PDEBUG(3, "%d", val); - if (ov511->stop_during_set) - if (ov511_stop(ov511) < 0) + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) return -EIO; - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7610: case SEN_OV6620: case SEN_OV6630: { - rc = ov51x_i2c_write(ov511, OV7610_REG_CNT, val >> 8); + rc = i2c_w(ov, OV7610_REG_CNT, val >> 8); if (rc < 0) goto out; break; @@ -1706,14 +1621,14 @@ }; /* Use Y gamma control instead. Bit 0 enables it. */ - rc = ov51x_i2c_write(ov511, 0x64, ctab[val>>12]); + rc = i2c_w(ov, 0x64, ctab[val>>12]); if (rc < 0) goto out; break; } case SEN_SAA7111A: { - rc = ov51x_i2c_write(ov511, 0x0b, val >> 9); + rc = i2c_w(ov, 0x0b, val >> 9); if (rc < 0) goto out; break; @@ -1727,9 +1642,9 @@ } rc = 0; /* Success */ - ov511->contrast = val; + ov->contrast = val; out: - if (ov511_restart(ov511) < 0) + if (ov51x_restart(ov) < 0) return -EIO; return rc; @@ -1737,15 +1652,15 @@ /* Gets sensor's contrast setting */ static int -sensor_get_contrast(struct usb_ov511 *ov511, unsigned short *val) +sensor_get_contrast(struct usb_ov511 *ov, unsigned short *val) { int rc; - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7610: case SEN_OV6620: case SEN_OV6630: - rc = ov51x_i2c_read(ov511, OV7610_REG_CNT); + rc = i2c_r(ov, OV7610_REG_CNT); if (rc < 0) return rc; else @@ -1753,14 +1668,14 @@ break; case SEN_OV7620: /* Use Y gamma reg instead. Bit 0 is the enable bit. */ - rc = ov51x_i2c_read(ov511, 0x64); + rc = i2c_r(ov, 0x64); if (rc < 0) return rc; else *val = (rc & 0xfe) << 8; break; case SEN_SAA7111A: - *val = ov511->contrast; + *val = ov->contrast; break; default: PDEBUG(3, "Unsupported with this sensor"); @@ -1768,7 +1683,7 @@ } PDEBUG(3, "%d", *val); - ov511->contrast = *val; + ov->contrast = *val; return 0; } @@ -1777,35 +1692,35 @@ /* Sets sensor's brightness setting to "val" */ static int -sensor_set_brightness(struct usb_ov511 *ov511, unsigned short val) +sensor_set_brightness(struct usb_ov511 *ov, unsigned short val) { int rc; PDEBUG(4, "%d", val); - if (ov511->stop_during_set) - if (ov511_stop(ov511) < 0) + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) return -EIO; - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7610: case SEN_OV7620AE: case SEN_OV6620: case SEN_OV6630: - rc = ov51x_i2c_write(ov511, OV7610_REG_BRT, val >> 8); + rc = i2c_w(ov, OV7610_REG_BRT, val >> 8); if (rc < 0) goto out; break; case SEN_OV7620: /* 7620 doesn't like manual changes when in auto mode */ - if (!ov511->auto_brt) { - rc = ov51x_i2c_write(ov511, OV7610_REG_BRT, val >> 8); + if (!ov->auto_brt) { + rc = i2c_w(ov, OV7610_REG_BRT, val >> 8); if (rc < 0) goto out; } break; case SEN_SAA7111A: - rc = ov51x_i2c_write(ov511, 0x0a, val >> 8); + rc = i2c_w(ov, 0x0a, val >> 8); if (rc < 0) goto out; break; @@ -1816,9 +1731,9 @@ } rc = 0; /* Success */ - ov511->brightness = val; + ov->brightness = val; out: - if (ov511_restart(ov511) < 0) + if (ov51x_restart(ov) < 0) return -EIO; return rc; @@ -1826,24 +1741,24 @@ /* Gets sensor's brightness setting */ static int -sensor_get_brightness(struct usb_ov511 *ov511, unsigned short *val) +sensor_get_brightness(struct usb_ov511 *ov, unsigned short *val) { int rc; - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7610: case SEN_OV7620AE: case SEN_OV7620: case SEN_OV6620: case SEN_OV6630: - rc = ov51x_i2c_read(ov511, OV7610_REG_BRT); + rc = i2c_r(ov, OV7610_REG_BRT); if (rc < 0) return rc; else *val = rc << 8; break; case SEN_SAA7111A: - *val = ov511->brightness; + *val = ov->brightness; break; default: PDEBUG(3, "Unsupported with this sensor"); @@ -1851,7 +1766,7 @@ } PDEBUG(3, "%d", *val); - ov511->brightness = *val; + ov->brightness = *val; return 0; } @@ -1860,36 +1775,36 @@ /* Sets sensor's saturation (color intensity) setting to "val" */ static int -sensor_set_saturation(struct usb_ov511 *ov511, unsigned short val) +sensor_set_saturation(struct usb_ov511 *ov, unsigned short val) { int rc; PDEBUG(3, "%d", val); - if (ov511->stop_during_set) - if (ov511_stop(ov511) < 0) + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) return -EIO; - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7610: case SEN_OV7620AE: case SEN_OV6620: case SEN_OV6630: - rc = ov51x_i2c_write(ov511, OV7610_REG_SAT, val >> 8); + rc = i2c_w(ov, OV7610_REG_SAT, val >> 8); if (rc < 0) goto out; break; case SEN_OV7620: // /* Use UV gamma control instead. Bits 0 & 7 are reserved. */ -// rc = ov511_i2c_write(ov511->dev, 0x62, (val >> 9) & 0x7e); +// rc = ov_i2c_write(ov->dev, 0x62, (val >> 9) & 0x7e); // if (rc < 0) // goto out; - rc = ov51x_i2c_write(ov511, OV7610_REG_SAT, val >> 8); + rc = i2c_w(ov, OV7610_REG_SAT, val >> 8); if (rc < 0) goto out; break; case SEN_SAA7111A: - rc = ov51x_i2c_write(ov511, 0x0c, val >> 9); + rc = i2c_w(ov, 0x0c, val >> 9); if (rc < 0) goto out; break; @@ -1900,9 +1815,9 @@ } rc = 0; /* Success */ - ov511->colour = val; + ov->colour = val; out: - if (ov511_restart(ov511) < 0) + if (ov51x_restart(ov) < 0) return -EIO; return rc; @@ -1910,16 +1825,16 @@ /* Gets sensor's saturation (color intensity) setting */ static int -sensor_get_saturation(struct usb_ov511 *ov511, unsigned short *val) +sensor_get_saturation(struct usb_ov511 *ov, unsigned short *val) { int rc; - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7610: case SEN_OV7620AE: case SEN_OV6620: case SEN_OV6630: - rc = ov51x_i2c_read(ov511, OV7610_REG_SAT); + rc = i2c_r(ov, OV7610_REG_SAT); if (rc < 0) return rc; else @@ -1927,19 +1842,19 @@ break; case SEN_OV7620: // /* Use UV gamma reg instead. Bits 0 & 7 are reserved. */ -// rc = ov51x_i2c_read(ov511, 0x62); +// rc = i2c_r(ov, 0x62); // if (rc < 0) // return rc; // else // *val = (rc & 0x7e) << 9; - rc = ov51x_i2c_read(ov511, OV7610_REG_SAT); + rc = i2c_r(ov, OV7610_REG_SAT); if (rc < 0) return rc; else *val = rc << 8; break; case SEN_SAA7111A: - *val = ov511->colour; + *val = ov->colour; break; default: PDEBUG(3, "Unsupported with this sensor"); @@ -1947,7 +1862,7 @@ } PDEBUG(3, "%d", *val); - ov511->colour = *val; + ov->colour = *val; return 0; } @@ -1956,44 +1871,42 @@ /* Sets sensor's hue (red/blue balance) setting to "val" */ static int -sensor_set_hue(struct usb_ov511 *ov511, unsigned short val) +sensor_set_hue(struct usb_ov511 *ov, unsigned short val) { int rc; PDEBUG(3, "%d", val); - if (ov511->stop_during_set) - if (ov511_stop(ov511) < 0) + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) return -EIO; - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7610: case SEN_OV6620: case SEN_OV6630: - rc = ov51x_i2c_write(ov511, OV7610_REG_RED, 0xFF - (val >> 8)); + rc = i2c_w(ov, OV7610_REG_RED, 0xFF - (val >> 8)); if (rc < 0) goto out; - rc = ov51x_i2c_write(ov511, OV7610_REG_BLUE, val >> 8); + rc = i2c_w(ov, OV7610_REG_BLUE, val >> 8); if (rc < 0) goto out; break; case SEN_OV7620: // Hue control is causing problems. I will enable it once it's fixed. #if 0 - rc = ov51x_i2c_write(ov511, 0x7a, - (unsigned char)(val >> 8) + 0xb); + rc = i2c_w(ov, 0x7a, (unsigned char)(val >> 8) + 0xb); if (rc < 0) goto out; - rc = ov51x_i2c_write(ov511, 0x79, - (unsigned char)(val >> 8) + 0xb); + rc = i2c_w(ov, 0x79, (unsigned char)(val >> 8) + 0xb); if (rc < 0) goto out; #endif break; case SEN_SAA7111A: - rc = ov51x_i2c_write(ov511, 0x0d, (val + 32768) >> 8); + rc = i2c_w(ov, 0x0d, (val + 32768) >> 8); if (rc < 0) goto out; break; @@ -2004,9 +1917,9 @@ } rc = 0; /* Success */ - ov511->hue = val; + ov->hue = val; out: - if (ov511_restart(ov511) < 0) + if (ov51x_restart(ov) < 0) return -EIO; return rc; @@ -2014,29 +1927,29 @@ /* Gets sensor's hue (red/blue balance) setting */ static int -sensor_get_hue(struct usb_ov511 *ov511, unsigned short *val) +sensor_get_hue(struct usb_ov511 *ov, unsigned short *val) { int rc; - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7610: case SEN_OV6620: case SEN_OV6630: - rc = ov51x_i2c_read(ov511, OV7610_REG_BLUE); + rc = i2c_r(ov, OV7610_REG_BLUE); if (rc < 0) return rc; else *val = rc << 8; break; case SEN_OV7620: - rc = ov51x_i2c_read(ov511, 0x7a); + rc = i2c_r(ov, 0x7a); if (rc < 0) return rc; else *val = rc << 8; break; case SEN_SAA7111A: - *val = ov511->hue; + *val = ov->hue; break; default: PDEBUG(3, "Unsupported with this sensor"); @@ -2044,7 +1957,7 @@ } PDEBUG(3, "%d", *val); - ov511->hue = *val; + ov->hue = *val; return 0; } @@ -2052,30 +1965,30 @@ /* -------------------------------------------------------------------------- */ static inline int -sensor_set_picture(struct usb_ov511 *ov511, struct video_picture *p) +sensor_set_picture(struct usb_ov511 *ov, struct video_picture *p) { int rc; PDEBUG(4, "sensor_set_picture"); - ov511->whiteness = p->whiteness; + ov->whiteness = p->whiteness; /* Don't return error if a setting is unsupported, or rest of settings * will not be performed */ - rc = sensor_set_contrast(ov511, p->contrast); + rc = sensor_set_contrast(ov, p->contrast); if (FATAL_ERROR(rc)) return rc; - rc = sensor_set_brightness(ov511, p->brightness); + rc = sensor_set_brightness(ov, p->brightness); if (FATAL_ERROR(rc)) return rc; - rc = sensor_set_saturation(ov511, p->colour); + rc = sensor_set_saturation(ov, p->colour); if (FATAL_ERROR(rc)) return rc; - rc = sensor_set_hue(ov511, p->hue); + rc = sensor_set_hue(ov, p->hue); if (FATAL_ERROR(rc)) return rc; @@ -2083,7 +1996,7 @@ } static inline int -sensor_get_picture(struct usb_ov511 *ov511, struct video_picture *p) +sensor_get_picture(struct usb_ov511 *ov, struct video_picture *p) { int rc; @@ -2092,27 +2005,27 @@ /* Don't return error if a setting is unsupported, or rest of settings * will not be performed */ - rc = sensor_get_contrast(ov511, &(p->contrast)); + rc = sensor_get_contrast(ov, &(p->contrast)); if (FATAL_ERROR(rc)) return rc; - rc = sensor_get_brightness(ov511, &(p->brightness)); + rc = sensor_get_brightness(ov, &(p->brightness)); if (FATAL_ERROR(rc)) return rc; - rc = sensor_get_saturation(ov511, &(p->colour)); + rc = sensor_get_saturation(ov, &(p->colour)); if (FATAL_ERROR(rc)) return rc; - rc = sensor_get_hue(ov511, &(p->hue)); + rc = sensor_get_hue(ov, &(p->hue)); if (FATAL_ERROR(rc)) return rc; p->whiteness = 105 << 8; /* Can we get these from frame[0]? -claudio? */ - p->depth = ov511->frame[0].depth; - p->palette = ov511->frame[0].format; + p->depth = ov->frame[0].depth; + p->palette = ov->frame[0].format; return 0; } @@ -2121,24 +2034,24 @@ /* Sets current exposure for sensor. This only has an effect if auto-exposure * is off */ static inline int -sensor_set_exposure(struct usb_ov511 *ov511, unsigned char val) +sensor_set_exposure(struct usb_ov511 *ov, unsigned char val) { int rc; PDEBUG(3, "%d", val); - if (ov511->stop_during_set) - if (ov511_stop(ov511) < 0) + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) return -EIO; - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV6620: case SEN_OV6630: case SEN_OV7610: case SEN_OV7620: case SEN_OV7620AE: case SEN_OV8600: - rc = ov51x_i2c_write(ov511, 0x10, val); + rc = i2c_w(ov, 0x10, val); if (rc < 0) goto out; @@ -2154,9 +2067,9 @@ } rc = 0; /* Success */ - ov511->exposure = val; + ov->exposure = val; out: - if (ov511_restart(ov511) < 0) + if (ov51x_restart(ov) < 0) return -EIO; return rc; @@ -2165,18 +2078,18 @@ /* Gets current exposure level from sensor, regardless of whether it is under * manual control. */ static int -sensor_get_exposure(struct usb_ov511 *ov511, unsigned char *val) +sensor_get_exposure(struct usb_ov511 *ov, unsigned char *val) { int rc; - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7610: case SEN_OV6620: case SEN_OV6630: case SEN_OV7620: case SEN_OV7620AE: case SEN_OV8600: - rc = ov51x_i2c_read(ov511, 0x10); + rc = i2c_r(ov, 0x10); if (rc < 0) return rc; else @@ -2194,24 +2107,22 @@ } PDEBUG(3, "%d", *val); - ov511->exposure = *val; + ov->exposure = *val; return 0; } /* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */ static inline void -ov51x_led_control(struct usb_ov511 *ov511, int enable) +ov51x_led_control(struct usb_ov511 *ov, int enable) { PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); - if (ov511->bridge == BRG_OV511PLUS) - ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_LED_CTL, - enable ? 1 : 0); - else if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) - ov511_reg_write_mask(ov511->dev, OV518_REG_GPIO_OUT, - enable ? 0x02 : 0x00, 0x02); + if (ov->bridge == BRG_OV511PLUS) + reg_w(ov, R511_SYS_LED_CTL, enable ? 1 : 0); + else if (ov->bclass == BCL_OV518) + reg_w_mask(ov, R518_GPIO_OUT, enable ? 0x02 : 0x00, 0x02); + return; } @@ -2225,7 +2136,7 @@ * Returns: 0 for success */ static int -sensor_set_light_freq(struct usb_ov511 *ov511, int freq) +sensor_set_light_freq(struct usb_ov511 *ov, int freq) { int sixty; @@ -2240,24 +2151,24 @@ return -EINVAL; } - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7610: - ov51x_i2c_write_mask(ov511, 0x2a, sixty?0x00:0x80, 0x80); - ov51x_i2c_write(ov511, 0x2b, sixty?0x00:0xac); - ov51x_i2c_write_mask(ov511, 0x13, 0x10, 0x10); - ov51x_i2c_write_mask(ov511, 0x13, 0x00, 0x10); + i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80); + i2c_w(ov, 0x2b, sixty?0x00:0xac); + i2c_w_mask(ov, 0x13, 0x10, 0x10); + i2c_w_mask(ov, 0x13, 0x00, 0x10); break; case SEN_OV7620: case SEN_OV7620AE: case SEN_OV8600: - ov51x_i2c_write_mask(ov511, 0x2a, sixty?0x00:0x80, 0x80); - ov51x_i2c_write(ov511, 0x2b, sixty?0x00:0xac); - ov51x_i2c_write_mask(ov511, 0x76, 0x01, 0x01); + i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80); + i2c_w(ov, 0x2b, sixty?0x00:0xac); + i2c_w_mask(ov, 0x76, 0x01, 0x01); break; case SEN_OV6620: case SEN_OV6630: - ov51x_i2c_write(ov511, 0x2b, sixty?0xa8:0x28); - ov51x_i2c_write(ov511, 0x2a, sixty?0x84:0xa4); + i2c_w(ov, 0x2b, sixty?0xa8:0x28); + i2c_w(ov, 0x2a, sixty?0x84:0xa4); break; case SEN_KS0127: case SEN_KS0127B: @@ -2269,7 +2180,7 @@ return -EINVAL; } - ov511->lightfreq = freq; + ov->lightfreq = freq; return 0; } @@ -2284,23 +2195,23 @@ * Returns: 0 for success */ static inline int -sensor_set_banding_filter(struct usb_ov511 *ov511, int enable) +sensor_set_banding_filter(struct usb_ov511 *ov, int enable) { int rc; PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); - if (ov511->sensor == SEN_KS0127 || ov511->sensor == SEN_KS0127B - || ov511->sensor == SEN_SAA7111A) { + if (ov->sensor == SEN_KS0127 || ov->sensor == SEN_KS0127B + || ov->sensor == SEN_SAA7111A) { PDEBUG(5, "Unsupported with this sensor"); return -EPERM; } - rc = ov51x_i2c_write_mask(ov511, 0x2d, enable?0x04:0x00, 0x04); + rc = i2c_w_mask(ov, 0x2d, enable?0x04:0x00, 0x04); if (rc < 0) return rc; - ov511->bandfilt = enable; + ov->bandfilt = enable; return 0; } @@ -2312,23 +2223,23 @@ * Returns: 0 for success */ static inline int -sensor_set_auto_brightness(struct usb_ov511 *ov511, int enable) +sensor_set_auto_brightness(struct usb_ov511 *ov, int enable) { int rc; PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); - if (ov511->sensor == SEN_KS0127 || ov511->sensor == SEN_KS0127B - || ov511->sensor == SEN_SAA7111A) { + if (ov->sensor == SEN_KS0127 || ov->sensor == SEN_KS0127B + || ov->sensor == SEN_SAA7111A) { PDEBUG(5, "Unsupported with this sensor"); return -EPERM; } - rc = ov51x_i2c_write_mask(ov511, 0x2d, enable?0x10:0x00, 0x10); + rc = i2c_w_mask(ov, 0x2d, enable?0x10:0x00, 0x10); if (rc < 0) return rc; - ov511->auto_brt = enable; + ov->auto_brt = enable; return 0; } @@ -2340,22 +2251,22 @@ * Returns: 0 for success */ static inline int -sensor_set_auto_exposure(struct usb_ov511 *ov511, int enable) +sensor_set_auto_exposure(struct usb_ov511 *ov, int enable) { PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7610: - ov51x_i2c_write_mask(ov511, 0x29, enable?0x00:0x80, 0x80); + i2c_w_mask(ov, 0x29, enable?0x00:0x80, 0x80); break; case SEN_OV6620: case SEN_OV7620: case SEN_OV7620AE: case SEN_OV8600: - ov51x_i2c_write_mask(ov511, 0x13, enable?0x01:0x00, 0x01); + i2c_w_mask(ov, 0x13, enable?0x01:0x00, 0x01); break; case SEN_OV6630: - ov51x_i2c_write_mask(ov511, 0x28, enable?0x00:0x10, 0x10); + i2c_w_mask(ov, 0x28, enable?0x00:0x10, 0x10); break; case SEN_KS0127: case SEN_KS0127B: @@ -2367,7 +2278,7 @@ return -EINVAL; } - ov511->auto_exp = enable; + ov->auto_exp = enable; return 0; } @@ -2380,27 +2291,27 @@ * Returns: 0 for success */ static int -sensor_set_backlight(struct usb_ov511 *ov511, int enable) +sensor_set_backlight(struct usb_ov511 *ov, int enable) { PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7620: case SEN_OV8600: - ov51x_i2c_write_mask(ov511, 0x68, enable?0xe0:0xc0, 0xe0); - ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08); - ov51x_i2c_write_mask(ov511, 0x28, enable?0x02:0x00, 0x02); + i2c_w_mask(ov, 0x68, enable?0xe0:0xc0, 0xe0); + i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); + i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02); break; case SEN_OV6620: - ov51x_i2c_write_mask(ov511, 0x4e, enable?0xe0:0xc0, 0xe0); - ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08); - ov51x_i2c_write_mask(ov511, 0x0e, enable?0x80:0x00, 0x80); + i2c_w_mask(ov, 0x4e, enable?0xe0:0xc0, 0xe0); + i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); + i2c_w_mask(ov, 0x0e, enable?0x80:0x00, 0x80); break; case SEN_OV6630: - ov51x_i2c_write_mask(ov511, 0x4e, enable?0x80:0x60, 0xe0); - ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08); - ov51x_i2c_write_mask(ov511, 0x28, enable?0x02:0x00, 0x02); + i2c_w_mask(ov, 0x4e, enable?0x80:0x60, 0xe0); + i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); + i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02); break; case SEN_OV7610: case SEN_OV7620AE: @@ -2414,7 +2325,7 @@ return -EINVAL; } - ov511->backlight = enable; + ov->backlight = enable; return 0; } @@ -2423,17 +2334,17 @@ * planar or not), or zero for unsupported format. */ static inline int -ov511_get_depth(int palette) +get_depth(int palette) { switch (palette) { case VIDEO_PALETTE_GREY: return 8; + case VIDEO_PALETTE_YUV420: return 12; + case VIDEO_PALETTE_YUV420P: return 12; /* Planar */ case VIDEO_PALETTE_RGB565: return 16; case VIDEO_PALETTE_RGB24: return 24; case VIDEO_PALETTE_YUV422: return 16; case VIDEO_PALETTE_YUYV: return 16; - case VIDEO_PALETTE_YUV420: return 12; case VIDEO_PALETTE_YUV422P: return 16; /* Planar */ - case VIDEO_PALETTE_YUV420P: return 12; /* Planar */ default: return 0; /* Invalid format */ } } @@ -2446,55 +2357,55 @@ return 0; else return ((frame->width * frame->height - * ov511_get_depth(frame->format)) >> 3); + * get_depth(frame->format)) >> 3); } static int -mode_init_ov_sensor_regs(struct usb_ov511 *ov511, int width, int height, +mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height, int mode, int sub_flag, int qvga) { int clock; /******** Mode (VGA/QVGA) and sensor specific regs ********/ - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7610: - ov51x_i2c_write(ov511, 0x14, qvga?0x24:0x04); + i2c_w(ov, 0x14, qvga?0x24:0x04); // FIXME: Does this improve the image quality or frame rate? #if 0 - ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20); - ov51x_i2c_write(ov511, 0x24, 0x10); - ov51x_i2c_write(ov511, 0x25, qvga?0x40:0x8a); - ov51x_i2c_write(ov511, 0x2f, qvga?0x30:0xb0); - ov51x_i2c_write(ov511, 0x35, qvga?0x1c:0x9c); + i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); + i2c_w(ov, 0x24, 0x10); + i2c_w(ov, 0x25, qvga?0x40:0x8a); + i2c_w(ov, 0x2f, qvga?0x30:0xb0); + i2c_w(ov, 0x35, qvga?0x1c:0x9c); #endif break; case SEN_OV7620: -// ov51x_i2c_write(ov511, 0x2b, 0x00); - ov51x_i2c_write(ov511, 0x14, qvga?0xa4:0x84); - ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20); - ov51x_i2c_write(ov511, 0x24, qvga?0x20:0x3a); - ov51x_i2c_write(ov511, 0x25, qvga?0x30:0x60); - ov51x_i2c_write_mask(ov511, 0x2d, qvga?0x40:0x00, 0x40); - ov51x_i2c_write_mask(ov511, 0x67, qvga?0xf0:0x90, 0xf0); - ov51x_i2c_write_mask(ov511, 0x74, qvga?0x20:0x00, 0x20); +// i2c_w(ov, 0x2b, 0x00); + i2c_w(ov, 0x14, qvga?0xa4:0x84); + i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); + i2c_w(ov, 0x24, qvga?0x20:0x3a); + i2c_w(ov, 0x25, qvga?0x30:0x60); + i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40); + i2c_w_mask(ov, 0x67, qvga?0xf0:0x90, 0xf0); + i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20); break; case SEN_OV7620AE: -// ov51x_i2c_write(ov511, 0x2b, 0x00); - ov51x_i2c_write(ov511, 0x14, qvga?0xa4:0x84); +// i2c_w(ov, 0x2b, 0x00); + i2c_w(ov, 0x14, qvga?0xa4:0x84); // FIXME: Enable this once 7620AE uses 7620 initial settings #if 0 - ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20); - ov51x_i2c_write(ov511, 0x24, qvga?0x20:0x3a); - ov51x_i2c_write(ov511, 0x25, qvga?0x30:0x60); - ov51x_i2c_write_mask(ov511, 0x2d, qvga?0x40:0x00, 0x40); - ov51x_i2c_write_mask(ov511, 0x67, qvga?0xb0:0x90, 0xf0); - ov51x_i2c_write_mask(ov511, 0x74, qvga?0x20:0x00, 0x20); + i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); + i2c_w(ov, 0x24, qvga?0x20:0x3a); + i2c_w(ov, 0x25, qvga?0x30:0x60); + i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40); + i2c_w_mask(ov, 0x67, qvga?0xb0:0x90, 0xf0); + i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20); #endif break; case SEN_OV6620: case SEN_OV6630: - ov51x_i2c_write(ov511, 0x14, qvga?0x24:0x04); + i2c_w(ov, 0x14, qvga?0x24:0x04); /* No special settings yet */ break; default: @@ -2505,19 +2416,17 @@ /******** Palette-specific regs ********/ if (mode == VIDEO_PALETTE_GREY) { - if (ov511->sensor == SEN_OV7610 - || ov511->sensor == SEN_OV7620AE) { + if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV7620AE) { /* these aren't valid on the OV6620/OV7620/6630? */ - ov51x_i2c_write_mask(ov511, 0x0e, 0x40, 0x40); + i2c_w_mask(ov, 0x0e, 0x40, 0x40); } - ov51x_i2c_write_mask(ov511, 0x13, 0x20, 0x20); + i2c_w_mask(ov, 0x13, 0x20, 0x20); } else { - if (ov511->sensor == SEN_OV7610 - || ov511->sensor == SEN_OV7620AE) { + if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV7620AE) { /* not valid on the OV6620/OV7620/6630? */ - ov51x_i2c_write_mask(ov511, 0x0e, 0x00, 0x40); + i2c_w_mask(ov, 0x0e, 0x00, 0x40); } - ov51x_i2c_write_mask(ov511, 0x13, 0x00, 0x20); + i2c_w_mask(ov, 0x13, 0x00, 0x20); } /******** Clock programming ********/ @@ -2526,13 +2435,13 @@ /* The OV6620 needs special handling. This prevents the * severe banding that normally occurs */ - if (ov511->sensor == SEN_OV6620 || ov511->sensor == SEN_OV6630) + if (ov->sensor == SEN_OV6620 || ov->sensor == SEN_OV6630) { /* Clock down */ - ov51x_i2c_write(ov511, 0x2a, 0x04); + i2c_w(ov, 0x2a, 0x04); - if (ov511->compress) { + if (ov->compress) { // clock = 0; /* This ensures the highest frame rate */ clock = 3; } else if (clockdiv == -1) { /* If user didn't override it */ @@ -2543,21 +2452,21 @@ PDEBUG(4, "Setting clock divisor to %d", clock); - ov51x_i2c_write(ov511, 0x11, clock); + i2c_w(ov, 0x11, clock); - ov51x_i2c_write(ov511, 0x2a, 0x84); + i2c_w(ov, 0x2a, 0x84); /* This next setting is critical. It seems to improve * the gain or the contrast. The "reserved" bits seem * to have some effect in this case. */ - ov51x_i2c_write(ov511, 0x2d, 0x85); + i2c_w(ov, 0x2d, 0x85); } else { - if (ov511->compress) { + if (ov->compress) { clock = 1; /* This ensures the highest frame rate */ } else if (clockdiv == -1) { /* If user didn't override it */ /* Calculate and set the clock divisor */ - clock = ((sub_flag ? ov511->subw * ov511->subh + clock = ((sub_flag ? ov->subw * ov->subh : width * height) * (mode == VIDEO_PALETTE_GREY ? 2 : 3) / 2) / 66000; @@ -2567,45 +2476,44 @@ PDEBUG(4, "Setting clock divisor to %d", clock); - ov51x_i2c_write(ov511, 0x11, clock); + i2c_w(ov, 0x11, clock); } /******** Special Features ********/ if (framedrop >= 0) - ov51x_i2c_write(ov511, 0x16, framedrop); + i2c_w(ov, 0x16, framedrop); /* We only have code to convert GBR -> RGB24 */ if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr) - ov51x_i2c_write_mask(ov511, 0x12, 0x08, 0x08); + i2c_w_mask(ov, 0x12, 0x08, 0x08); else - ov51x_i2c_write_mask(ov511, 0x12, 0x00, 0x08); + i2c_w_mask(ov, 0x12, 0x00, 0x08); /* Test Pattern */ - ov51x_i2c_write_mask(ov511, 0x12, (testpat?0x02:0x00), 0x02); + i2c_w_mask(ov, 0x12, (testpat?0x02:0x00), 0x02); /* Auto white balance */ // if (awb) - ov51x_i2c_write_mask(ov511, 0x12, 0x04, 0x04); + i2c_w_mask(ov, 0x12, 0x04, 0x04); // else -// ov51x_i2c_write_mask(ov511, 0x12, 0x00, 0x04); +// i2c_w_mask(ov, 0x12, 0x00, 0x04); - // This will go away as soon as ov511_mode_init_sensor_regs() + // This will go away as soon as ov51x_mode_init_sensor_regs() // is fully tested. /* 7620/6620/6630? don't have register 0x35, so play it safe */ - if (ov511->sensor == SEN_OV7610 || - ov511->sensor == SEN_OV7620AE) { + if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV7620AE) { if (width == 640 && height == 480) - ov51x_i2c_write(ov511, 0x35, 0x9e); + i2c_w(ov, 0x35, 0x9e); else - ov51x_i2c_write(ov511, 0x35, 0x1e); + i2c_w(ov, 0x35, 0x1e); } return 0; } static int -set_ov_sensor_window(struct usb_ov511 *ov511, int width, int height, int mode, +set_ov_sensor_window(struct usb_ov511 *ov, int width, int height, int mode, int sub_flag) { int ret; @@ -2614,7 +2522,7 @@ /* The different sensor ICs handle setting up of window differently. * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!!! */ - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7610: case SEN_OV7620AE: hwsbase = 0x38; @@ -2638,9 +2546,9 @@ return -EINVAL; } - if (ov511->sensor == SEN_OV6620 || ov511->sensor == SEN_OV6630) { + if (ov->sensor == SEN_OV6620 || ov->sensor == SEN_OV6630) { if (width > 176 && height > 144) { /* CIF */ - ret = mode_init_ov_sensor_regs(ov511, width, height, + ret = mode_init_ov_sensor_regs(ov, width, height, mode, sub_flag, 0); if (ret < 0) return ret; @@ -2652,7 +2560,7 @@ err("Illegal dimensions"); return -EINVAL; } else { /* QCIF */ - ret = mode_init_ov_sensor_regs(ov511, width, height, + ret = mode_init_ov_sensor_regs(ov, width, height, mode, sub_flag, 1); if (ret < 0) return ret; @@ -2661,7 +2569,7 @@ } } else { if (width > 320 && height > 240) { /* VGA */ - ret = mode_init_ov_sensor_regs(ov511, width, height, + ret = mode_init_ov_sensor_regs(ov, width, height, mode, sub_flag, 0); if (ret < 0) return ret; @@ -2673,7 +2581,7 @@ err("Illegal dimensions"); return -EINVAL; } else { /* QVGA */ - ret = mode_init_ov_sensor_regs(ov511, width, height, + ret = mode_init_ov_sensor_regs(ov, width, height, mode, sub_flag, 1); if (ret < 0) return ret; @@ -2689,24 +2597,20 @@ /* FIXME! - This needs to be changed to support 160x120 and 6620!!! */ if (sub_flag) { - ov51x_i2c_write(ov511, 0x17, hwsbase+(ov511->subx>>hwscale)); - ov51x_i2c_write(ov511, 0x18, - hwebase+((ov511->subx+ov511->subw)>>hwscale)); - ov51x_i2c_write(ov511, 0x19, vwsbase+(ov511->suby>>vwscale)); - ov51x_i2c_write(ov511, 0x1a, - vwebase+((ov511->suby+ov511->subh)>>vwscale)); + i2c_w(ov, 0x17, hwsbase+(ov->subx>>hwscale)); + i2c_w(ov, 0x18, hwebase+((ov->subx+ov->subw)>>hwscale)); + i2c_w(ov, 0x19, vwsbase+(ov->suby>>vwscale)); + i2c_w(ov, 0x1a, vwebase+((ov->suby+ov->subh)>>vwscale)); } else { - ov51x_i2c_write(ov511, 0x17, hwsbase + hoffset); - ov51x_i2c_write(ov511, 0x18, - hwebase + hoffset + (hwsize>>hwscale)); - ov51x_i2c_write(ov511, 0x19, vwsbase + voffset); - ov51x_i2c_write(ov511, 0x1a, - vwebase + voffset + (vwsize>>vwscale)); + i2c_w(ov, 0x17, hwsbase + hoffset); + i2c_w(ov, 0x18, hwebase + hoffset + (hwsize>>hwscale)); + i2c_w(ov, 0x19, vwsbase + voffset); + i2c_w(ov, 0x1a, vwebase + voffset + (vwsize>>vwscale)); } #ifdef OV511_DEBUG if (dump_sensor) - ov51x_dump_i2c_regs(ov511); + dump_i2c_regs(ov); #endif return 0; @@ -2717,18 +2621,14 @@ * Do not put any sensor-specific code in here (including I2C I/O functions) */ static int -ov511_mode_init_regs(struct usb_ov511 *ov511, +ov511_mode_init_regs(struct usb_ov511 *ov, int width, int height, int mode, int sub_flag) { int lncnt, pxcnt, rc = 0; - struct usb_device *dev = ov511->dev; - - if (!ov511 || !dev) - return -EFAULT; if (sub_flag) { - width = ov511->subw; - height = ov511->subh; + width = ov->subw; + height = ov->subh; } PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", @@ -2736,7 +2636,7 @@ // FIXME: This should be moved to a 7111a-specific function once // subcapture is dealt with properly - if (ov511->sensor == SEN_SAA7111A) { + if (ov->sensor == SEN_SAA7111A) { if (width == 320 && height == 240) { /* No need to do anything special */ } else if (width == 640 && height == 480) { @@ -2755,26 +2655,22 @@ return -EINVAL; } - if (width < ov511->minwidth || height < ov511->minheight) { + if (width < ov->minwidth || height < ov->minheight) { err("Requested dimensions are too small"); return -EINVAL; } - if (ov511_stop(ov511) < 0) + if (ov51x_stop(ov) < 0) return -EIO; if (mode == VIDEO_PALETTE_GREY) { - ov511_reg_write(dev, 0x16, 0x00); - - /* For snapshot */ - ov511_reg_write(dev, 0x1e, 0x00); - ov511_reg_write(dev, 0x1f, 0x01); + reg_w(ov, R511_CAM_UV_EN, 0x00); + reg_w(ov, R511_SNAP_UV_EN, 0x00); + reg_w(ov, R511_SNAP_OPTS, 0x01); } else { - ov511_reg_write(dev, 0x16, 0x01); - - /* For snapshot */ - ov511_reg_write(dev, 0x1e, 0x01); - ov511_reg_write(dev, 0x1f, 0x03); + reg_w(ov, R511_CAM_UV_EN, 0x01); + reg_w(ov, R511_SNAP_UV_EN, 0x01); + reg_w(ov, R511_SNAP_OPTS, 0x03); } /* Here I'm assuming that snapshot size == image size. @@ -2783,25 +2679,28 @@ pxcnt = (width >> 3) - 1; lncnt = (height >> 3) - 1; - ov511_reg_write(dev, 0x12, pxcnt); - ov511_reg_write(dev, 0x13, lncnt); - ov511_reg_write(dev, 0x14, 0x00); - ov511_reg_write(dev, 0x15, 0x00); - ov511_reg_write(dev, 0x18, 0x03); /* YUV420, low pass filer on */ + reg_w(ov, R511_CAM_PXCNT, pxcnt); + reg_w(ov, R511_CAM_LNCNT, lncnt); + reg_w(ov, R511_CAM_PXDIV, 0x00); + reg_w(ov, R511_CAM_LNDIV, 0x00); + + /* YUV420, low pass filer on */ + reg_w(ov, R511_CAM_OPTS, 0x03); /* Snapshot additions */ - ov511_reg_write(dev, 0x1a, pxcnt); - ov511_reg_write(dev, 0x1b, lncnt); - ov511_reg_write(dev, 0x1c, 0x00); - ov511_reg_write(dev, 0x1d, 0x00); - - if (ov511->compress) { - ov511_reg_write(dev, 0x78, 0x07); // Turn on Y & UV compression - ov511_reg_write(dev, 0x79, 0x03); // Enable LUTs - ov511_reset(ov511, OV511_RESET_OMNICE); + reg_w(ov, R511_SNAP_PXCNT, pxcnt); + reg_w(ov, R511_SNAP_LNCNT, lncnt); + reg_w(ov, R511_SNAP_PXDIV, 0x00); + reg_w(ov, R511_SNAP_LNDIV, 0x00); + + if (ov->compress) { + /* Enable Y and UV quantization and compression */ + reg_w(ov, R511_COMP_EN, 0x07); + reg_w(ov, R511_COMP_LUT_EN, 0x03); + ov51x_reset(ov, OV511_RESET_OMNICE); } //out: - if (ov511_restart(ov511) < 0) + if (ov51x_restart(ov) < 0) return -EIO; return rc; @@ -2825,17 +2724,15 @@ * Do not put any sensor-specific code in here (including I2C I/O functions) */ static int -ov518_mode_init_regs(struct usb_ov511 *ov511, +ov518_mode_init_regs(struct usb_ov511 *ov, int width, int height, int mode, int sub_flag) { int i; - struct usb_device *dev = ov511->dev; - unsigned char b[3]; /* Multiple-value reg buffer */ PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", width, height, mode, sub_flag); - if (ov511_stop(ov511) < 0) + if (ov51x_stop(ov) < 0) return -EIO; for (i = 0; mlist518[i].width; i++) { @@ -2850,116 +2747,97 @@ // /* Here I'm assuming that snapshot size == image size. // * I hope that's always true. --claudio // */ -// pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt; -// lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt; +// pxcnt = sub_flag ? (ov->subw >> 3) - 1 : mlist[i].pxcnt; +// lncnt = sub_flag ? (ov->subh >> 3) - 1 : mlist[i].lncnt; // -// ov511_reg_write(dev, 0x12, pxcnt); -// ov511_reg_write(dev, 0x13, lncnt); +// reg_w(ov, 0x12, pxcnt); +// reg_w(ov, 0x13, lncnt); /******** Set the mode ********/ /* Mode independent regs */ - ov511_reg_write(dev, 0x2b, 0x00); - ov511_reg_write(dev, 0x2d, 0x00); - ov511_reg_write(dev, 0x3b, 0x00); - ov511_reg_write(dev, 0x3d, 0x00); + reg_w(ov, 0x2b, 0x00); + reg_w(ov, 0x2d, 0x00); + reg_w(ov, 0x3b, 0x00); + reg_w(ov, 0x3d, 0x00); /* Mode dependent regs. Regs 38 - 3e are always the same as * regs 28 - 2e */ - ov511_reg_write_mask(dev, 0x28, mlist518[i].reg28 + reg_w_mask(ov, 0x28, mlist518[i].reg28 | (mode == VIDEO_PALETTE_GREY) ? 0x80:0x00, 0x8f); - ov511_reg_write(dev, 0x29, mlist518[i].reg29); - ov511_reg_write(dev, 0x2a, mlist518[i].reg2a); - ov511_reg_write(dev, 0x2c, mlist518[i].reg2c); - ov511_reg_write(dev, 0x2e, mlist518[i].reg2e); - ov511_reg_write_mask(dev, 0x38, mlist518[i].reg28 + reg_w(ov, 0x29, mlist518[i].reg29); + reg_w(ov, 0x2a, mlist518[i].reg2a); + reg_w(ov, 0x2c, mlist518[i].reg2c); + reg_w(ov, 0x2e, mlist518[i].reg2e); + reg_w_mask(ov, 0x38, mlist518[i].reg28 | (mode == VIDEO_PALETTE_GREY) ? 0x80:0x00, 0x8f); - ov511_reg_write(dev, 0x39, mlist518[i].reg29); - ov511_reg_write(dev, 0x3a, mlist518[i].reg2a); - ov511_reg_write(dev, 0x3c, mlist518[i].reg2c); - ov511_reg_write(dev, 0x3e, mlist518[i].reg2e); - ov511_reg_write(dev, 0x24, mlist518[i].reg24); - ov511_reg_write(dev, 0x25, mlist518[i].reg25); + reg_w(ov, 0x39, mlist518[i].reg29); + reg_w(ov, 0x3a, mlist518[i].reg2a); + reg_w(ov, 0x3c, mlist518[i].reg2c); + reg_w(ov, 0x3e, mlist518[i].reg2e); + reg_w(ov, 0x24, mlist518[i].reg24); + reg_w(ov, 0x25, mlist518[i].reg25); /* Windows driver does this here; who knows why */ - ov511_reg_write(dev, 0x2f, 0x80); + reg_w(ov, 0x2f, 0x80); /******** Set the framerate (to 15 FPS) ********/ /* Mode independent, but framerate dependent, regs */ /* These are for 15 FPS only */ - ov511_reg_write(dev, 0x51, 0x08); - ov511_reg_write(dev, 0x22, 0x18); - ov511_reg_write(dev, 0x23, 0xff); - ov511_reg_write(dev, 0x71, 0x19); /* Compression-related? */ + reg_w(ov, 0x51, 0x08); + reg_w(ov, 0x22, 0x18); + reg_w(ov, 0x23, 0xff); + reg_w(ov, 0x71, 0x19); /* Compression-related? */ // FIXME: Sensor-specific /* Bit 5 is what matters here. Of course, it is "reserved" */ - ov51x_i2c_write(ov511, 0x54, 0x23); + i2c_w(ov, 0x54, 0x23); - ov511_reg_write(dev, 0x2f, 0x80); + reg_w(ov, 0x2f, 0x80); /* Mode dependent regs */ if ((width == 352 && height == 288) || (width == 320 && height == 240)) { - b[0]=0x80; b[1]=0x02; - ov518_reg_write_multi(dev, 0x30, b, 2); - b[0]=0x90; b[1]=0x01; - ov518_reg_write_multi(dev, 0xc4, b, 2); - b[0]=0xf4; b[1]=0x01; - ov518_reg_write_multi(dev, 0xc6, b, 2); - b[0]=0xf4; b[1]=0x01; - ov518_reg_write_multi(dev, 0xc7, b, 2); - b[0]=0x8e; b[1]=0x00; - ov518_reg_write_multi(dev, 0xc8, b, 2); - b[0]=0x1a; b[1]=0x00; b[2]=0x02; - ov518_reg_write_multi(dev, 0xca, b, 3); - b[0]=0x14; b[1]=0x02; - ov518_reg_write_multi(dev, 0xcb, b, 2); - b[0]=0xd0; b[1]=0x07; - ov518_reg_write_multi(dev, 0xcc, b, 2); - b[0]=0x20; b[1]=0x00; - ov518_reg_write_multi(dev, 0xcd, b, 2); - b[0]=0x60; b[1]=0x02; - ov518_reg_write_multi(dev, 0xce, b, 2); - + /* 640 (280h) byte iso packets */ + ov518_reg_w32(ov, 0x30, 640, 2); /* 280h */ + ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */ + ov518_reg_w32(ov, 0xc6, 500, 2); /* 1f4h */ + ov518_reg_w32(ov, 0xc7, 500, 2); /* 1f4h */ + ov518_reg_w32(ov, 0xc8, 142, 2); /* 8eh */ + ov518_reg_w32(ov, 0xca, 131098, 3); /* 2001ah */ + ov518_reg_w32(ov, 0xcb, 532, 2); /* 214h */ + ov518_reg_w32(ov, 0xcc, 2000, 2); /* 7d0h */ + ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */ + ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */ } else if ((width == 176 && height == 144) || (width == 160 && height == 120)) { - b[0]=0x80; b[1]=0x01; - ov518_reg_write_multi(dev, 0x30, b, 2); - b[0]=0xc8; b[1]=0x00; - ov518_reg_write_multi(dev, 0xc4, b, 2); - b[0]=0x40; b[1]=0x01; - ov518_reg_write_multi(dev, 0xc6, b, 2); - b[0]=0x40; b[1]=0x01; - ov518_reg_write_multi(dev, 0xc7, b, 2); - b[0]=0x60; b[1]=0x00; - ov518_reg_write_multi(dev, 0xc8, b, 2); - b[0]=0x0f; b[1]=0x33; b[2]=0x01; - ov518_reg_write_multi(dev, 0xca, b, 3); - b[0]=0x40; b[1]=0x01; - ov518_reg_write_multi(dev, 0xcb, b, 2); - b[0]=0xec; b[1]=0x04; - ov518_reg_write_multi(dev, 0xcc, b, 2); - b[0]=0x13; b[1]=0x00; - ov518_reg_write_multi(dev, 0xcd, b, 2); - b[0]=0x6d; b[1]=0x01; - ov518_reg_write_multi(dev, 0xce, b, 2); + /* 384 (180h) byte iso packets */ + ov518_reg_w32(ov, 0x30, 384, 2); /* 180h */ + ov518_reg_w32(ov, 0xc4, 200, 2); /* c8h */ + ov518_reg_w32(ov, 0xc6, 320, 2); /* 140h */ + ov518_reg_w32(ov, 0xc7, 320, 2); /* 140h */ + ov518_reg_w32(ov, 0xc8, 96, 2); /* 60h */ + ov518_reg_w32(ov, 0xca, 78607, 3); /* 1330fh */ + ov518_reg_w32(ov, 0xcb, 320, 2); /* 140h */ + ov518_reg_w32(ov, 0xcc, 1260, 2); /* 4ech */ + ov518_reg_w32(ov, 0xcd, 19, 2); /* 13h */ + ov518_reg_w32(ov, 0xce, 365, 2); /* 16dh */ } else { /* Can't happen, since we already handled this case */ err("ov518_mode_init_regs(): **** logic error ****"); } - ov511_reg_write(dev, 0x2f, 0x80); + reg_w(ov, 0x2f, 0x80); break; } - if (ov511_restart(ov511) < 0) + if (ov51x_restart(ov) < 0) return -EIO; /* Reset it just for good measure */ - if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0) + if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) return -EIO; if (mlist518[i].width == 0) { @@ -2972,29 +2850,31 @@ /* This is a wrapper around the OV511, OV518, and sensor specific functions */ static int -mode_init_regs(struct usb_ov511 *ov511, +mode_init_regs(struct usb_ov511 *ov, int width, int height, int mode, int sub_flag) { int rc = 0; - if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) { - rc = ov518_mode_init_regs(ov511, width, height, mode, sub_flag); + if (!ov || !ov->dev) + return -EFAULT; + + if (ov->bclass == BCL_OV518) { + rc = ov518_mode_init_regs(ov, width, height, mode, sub_flag); } else { - rc = ov511_mode_init_regs(ov511, width, height, mode, sub_flag); + rc = ov511_mode_init_regs(ov, width, height, mode, sub_flag); } if (FATAL_ERROR(rc)) return rc; - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_OV7610: case SEN_OV7620: case SEN_OV7620AE: case SEN_OV8600: case SEN_OV6620: case SEN_OV6630: - rc = set_ov_sensor_window(ov511, width, height, mode, sub_flag); + rc = set_ov_sensor_window(ov, width, height, mode, sub_flag); break; case SEN_KS0127: case SEN_KS0127B: @@ -3002,10 +2882,10 @@ rc = -EINVAL; break; case SEN_SAA7111A: -// rc = mode_init_saa_sensor_regs(ov511, width, height, mode, +// rc = mode_init_saa_sensor_regs(ov, width, height, mode, // sub_flag); - PDEBUG(1, "SAA status = 0X%x", ov51x_i2c_read(ov511, 0x1f)); + PDEBUG(1, "SAA status = 0X%x", i2c_r(ov, 0x1f)); break; default: err("Unknown sensor"); @@ -3016,25 +2896,25 @@ return rc; /* Sensor-independent settings */ - rc = sensor_set_auto_brightness(ov511, ov511->auto_brt); + rc = sensor_set_auto_brightness(ov, ov->auto_brt); if (FATAL_ERROR(rc)) return rc; - rc = sensor_set_auto_exposure(ov511, ov511->auto_exp); + rc = sensor_set_auto_exposure(ov, ov->auto_exp); if (FATAL_ERROR(rc)) return rc; - rc = sensor_set_banding_filter(ov511, bandingfilter); + rc = sensor_set_banding_filter(ov, bandingfilter); if (FATAL_ERROR(rc)) return rc; - if (ov511->lightfreq) { - rc = sensor_set_light_freq(ov511, lightfreq); + if (ov->lightfreq) { + rc = sensor_set_light_freq(ov, lightfreq); if (FATAL_ERROR(rc)) return rc; } - rc = sensor_set_backlight(ov511, ov511->backlight); + rc = sensor_set_backlight(ov, ov->backlight); if (FATAL_ERROR(rc)) return rc; @@ -3045,28 +2925,28 @@ * useful for apps that use read() and do not set these. */ static int -ov51x_set_default_params(struct usb_ov511 *ov511) +ov51x_set_default_params(struct usb_ov511 *ov) { int i; - PDEBUG(3, "%dx%d, RGB24", ov511->maxwidth, ov511->maxheight); + PDEBUG(3, "%dx%d, RGB24", ov->maxwidth, ov->maxheight); /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used * (using read() instead). */ for (i = 0; i < OV511_NUMFRAMES; i++) { - ov511->frame[i].width = ov511->maxwidth; - ov511->frame[i].height = ov511->maxheight; - ov511->frame[i].bytes_read = 0; + ov->frame[i].width = ov->maxwidth; + ov->frame[i].height = ov->maxheight; + ov->frame[i].bytes_read = 0; if (force_palette) - ov511->frame[i].format = force_palette; + ov->frame[i].format = force_palette; else - ov511->frame[i].format = VIDEO_PALETTE_RGB24; - ov511->frame[i].depth = ov511_get_depth(ov511->frame[i].format); + ov->frame[i].format = VIDEO_PALETTE_RGB24; + ov->frame[i].depth = get_depth(ov->frame[i].format); } /* Initialize to max width/height, RGB24 */ - if (mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight, - ov511->frame[0].format, 0) < 0) + if (mode_init_regs(ov, ov->maxwidth, ov->maxheight, + ov->frame[0].format, 0) < 0) return -EINVAL; return 0; @@ -3080,18 +2960,17 @@ /* Set analog input port of decoder */ static int -decoder_set_input(struct usb_ov511 *ov511, int input) +decoder_set_input(struct usb_ov511 *ov, int input) { PDEBUG(4, "port %d", input); - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_SAA7111A: { /* Select mode */ - ov51x_i2c_write_mask(ov511, 0x02, input, 0x07); + i2c_w_mask(ov, 0x02, input, 0x07); /* Bypass chrominance trap for modes 4..7 */ - ov51x_i2c_write_mask(ov511, 0x09, - (input > 3) ? 0x80:0x00, 0x80); + i2c_w_mask(ov, 0x09, (input > 3) ? 0x80:0x00, 0x80); break; } default: @@ -3103,9 +2982,9 @@ /* Get ASCII name of video input */ static int -decoder_get_input_name(struct usb_ov511 *ov511, int input, char *name) +decoder_get_input_name(struct usb_ov511 *ov, int input, char *name) { - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_SAA7111A: { if (input < 0 || input > 7) @@ -3126,11 +3005,11 @@ /* Set norm (NTSC, PAL, SECAM, AUTO) */ static int -decoder_set_norm(struct usb_ov511 *ov511, int norm) +decoder_set_norm(struct usb_ov511 *ov, int norm) { PDEBUG(4, "%d", norm); - switch (ov511->sensor) { + switch (ov->sensor) { case SEN_SAA7111A: { int reg_8, reg_e; @@ -3151,8 +3030,8 @@ return -EINVAL; } - ov51x_i2c_write_mask(ov511, 0x08, reg_8, 0xc0); - ov51x_i2c_write_mask(ov511, 0x0e, reg_e, 0x70); + i2c_w_mask(ov, 0x08, reg_8, 0xc0); + i2c_w_mask(ov, 0x0e, reg_e, 0x70); break; } default: @@ -3199,8 +3078,8 @@ #define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16))) static inline void -ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, - int rowPixels, unsigned char * rgb, int bits) +move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, + int rowPixels, unsigned char * rgb, int bits) { const int rvScale = 91881; const int guScale = -22553; @@ -3270,10 +3149,10 @@ **********************************************************************/ /* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the - * array at pOut is specified by w. + * image at pOut is specified by w. */ static inline void -ov511_make_8x8(unsigned char *pIn, unsigned char *pOut, int w) +make_8x8(unsigned char *pIn, unsigned char *pOut, int w) { unsigned char *pOut1 = pOut; int x, y; @@ -3311,7 +3190,7 @@ for (y = 0; y < frame->rawheight - 1; y += 8) { pOut = pOutLine; for (x = 0; x < frame->rawwidth - 1; x += 8) { - ov511_make_8x8(pIn, pOut, frame->rawwidth); + make_8x8(pIn, pOut, frame->rawwidth); pIn += 64; pOut += 8; } @@ -3370,8 +3249,8 @@ for (y = 0; y < frame->rawheight - 1; y += 16) { pOut = pOutLine; for (x = 0; x < frame->rawwidth - 1; x += 16) { - ov511_make_8x8(pIn, pOut, w); - ov511_make_8x8(pIn + 64, pOut + a/4, w); + make_8x8(pIn, pOut, w); + make_8x8(pIn + 64, pOut + a/4, w); pIn += 384; pOut += 8; } @@ -3385,7 +3264,7 @@ for (y = 0; y < frame->rawheight - 1; y += 8) { pOut = pOutLine; for (x = 0; x < frame->rawwidth - 1; x += 8) { - ov511_make_8x8(pIn, pOut, frame->rawwidth); + make_8x8(pIn, pOut, frame->rawwidth); pIn += 64; pOut += 8; if ((++k) > 3) { @@ -3442,17 +3321,17 @@ * **********************************************************************/ -/* Chooses a decompression module, locks it, and sets ov511->decomp_ops +/* Chooses a decompression module, locks it, and sets ov->decomp_ops * accordingly. Returns -ENXIO if decompressor is not available, otherwise * returns 0 if no other error. */ static int -ov51x_request_decompressor(struct usb_ov511 *ov511) +request_decompressor(struct usb_ov511 *ov) { - if (!ov511) + if (!ov) return -ENODEV; - if (ov511->decomp_ops) { + if (ov->decomp_ops) { err("ERROR: Decompressor already requested!"); return -EINVAL; } @@ -3460,24 +3339,23 @@ lock_kernel(); /* Try to get MMX, and fall back on no-MMX if necessary */ - if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) { + if (ov->bclass == BCL_OV511) { if (ov511_mmx_decomp_ops) { PDEBUG(3, "Using OV511 MMX decompressor"); - ov511->decomp_ops = ov511_mmx_decomp_ops; + ov->decomp_ops = ov511_mmx_decomp_ops; } else if (ov511_decomp_ops) { PDEBUG(3, "Using OV511 decompressor"); - ov511->decomp_ops = ov511_decomp_ops; + ov->decomp_ops = ov511_decomp_ops; } else { err("No decompressor available"); } - } else if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) { + } else if (ov->bclass == BCL_OV518) { if (ov518_mmx_decomp_ops) { PDEBUG(3, "Using OV518 MMX decompressor"); - ov511->decomp_ops = ov518_mmx_decomp_ops; + ov->decomp_ops = ov518_mmx_decomp_ops; } else if (ov518_decomp_ops) { PDEBUG(3, "Using OV518 decompressor"); - ov511->decomp_ops = ov518_decomp_ops; + ov->decomp_ops = ov518_decomp_ops; } else { err("No decompressor available"); } @@ -3485,13 +3363,13 @@ err("Unknown bridge"); } - if (ov511->decomp_ops) { - if (!ov511->decomp_ops->decomp_lock) { - ov511->decomp_ops = NULL; + if (ov->decomp_ops) { + if (!ov->decomp_ops->decomp_lock) { + ov->decomp_ops = NULL; unlock_kernel(); return -ENOSYS; } - ov511->decomp_ops->decomp_lock(); + ov->decomp_ops->decomp_lock(); unlock_kernel(); return 0; } else { @@ -3500,25 +3378,25 @@ } } -/* Unlocks decompression module and nulls ov511->decomp_ops. Safe to call even - * if ov511->decomp_ops is NULL. +/* Unlocks decompression module and nulls ov->decomp_ops. Safe to call even + * if ov->decomp_ops is NULL. */ static void -ov51x_release_decompressor(struct usb_ov511 *ov511) +release_decompressor(struct usb_ov511 *ov) { int released = 0; /* Did we actually do anything? */ - if (!ov511) + if (!ov) return; lock_kernel(); - if (ov511->decomp_ops && ov511->decomp_ops->decomp_unlock) { - ov511->decomp_ops->decomp_unlock(); + if (ov->decomp_ops && ov->decomp_ops->decomp_unlock) { + ov->decomp_ops->decomp_unlock(); released = 1; } - ov511->decomp_ops = NULL; + ov->decomp_ops = NULL; unlock_kernel(); @@ -3527,28 +3405,30 @@ } static void -ov51x_decompress(struct usb_ov511 *ov511, struct ov511_frame *frame, - unsigned char *pIn0, unsigned char *pOut0) +decompress(struct usb_ov511 *ov, struct ov511_frame *frame, + unsigned char *pIn0, unsigned char *pOut0) { - if (!ov511->decomp_ops) - if (ov51x_request_decompressor(ov511)) + if (!ov->decomp_ops) + if (request_decompressor(ov)) return; PDEBUG(4, "Decompressing %d bytes", frame->bytes_recvd); if (frame->format == VIDEO_PALETTE_GREY - && ov511->decomp_ops->decomp_400) { - int ret = ov511->decomp_ops->decomp_400( + && ov->decomp_ops->decomp_400) { + int ret = ov->decomp_ops->decomp_400( pIn0, pOut0, + frame->compbuf, frame->rawwidth, frame->rawheight, frame->bytes_recvd); PDEBUG(4, "DEBUG: decomp_400 returned %d", ret); - } else if (ov511->decomp_ops->decomp_420) { - int ret = ov511->decomp_ops->decomp_420( + } else if (ov->decomp_ops->decomp_420) { + int ret = ov->decomp_ops->decomp_420( pIn0, pOut0, + frame->compbuf, frame->rawwidth, frame->rawheight, frame->bytes_recvd); @@ -3586,8 +3466,8 @@ u = (*pU++) - 128; v = (*pV++) - 128; - ov511_move_420_block(y00, y01, y10, y11, u, v, - frame->width, pOut, bits); + move_420_block(y00, y01, y10, y11, u, v, + frame->width, pOut, bits); pY += 2; pOut += 2 * bytes; @@ -3742,22 +3622,22 @@ * 4. Fix the RGB offset, if necessary */ static void -ov511_postprocess(struct usb_ov511 *ov511, struct ov511_frame *frame) +ov51x_postprocess(struct usb_ov511 *ov, struct ov511_frame *frame) { if (dumppix) { memset(frame->data, 0, - MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight)); + MAX_DATA_SIZE(ov->maxwidth, ov->maxheight)); PDEBUG(4, "Dumping %d bytes", frame->bytes_recvd); - memmove(frame->data, frame->rawdata, frame->bytes_recvd); + memcpy(frame->data, frame->rawdata, frame->bytes_recvd); return; } /* YUV400 must be handled separately */ if (frame->format == VIDEO_PALETTE_GREY) { /* Deinterlace frame, if necessary */ - if (ov511->sensor == SEN_SAA7111A && frame->rawheight == 480) { + if (ov->sensor == SEN_SAA7111A && frame->rawheight == 480) { if (frame->compressed) - ov51x_decompress(ov511, frame, frame->rawdata, + decompress(ov, frame, frame->rawdata, frame->tempdata); else yuv400raw_to_yuv400p(frame, frame->rawdata, @@ -3767,7 +3647,7 @@ frame->data); } else { if (frame->compressed) - ov51x_decompress(ov511, frame, frame->rawdata, + decompress(ov, frame, frame->rawdata, frame->data); else yuv400raw_to_yuv400p(frame, frame->rawdata, @@ -3779,13 +3659,13 @@ /* Process frame->data to frame->rawdata */ if (frame->compressed) - ov51x_decompress(ov511, frame, frame->rawdata, frame->tempdata); + decompress(ov, frame, frame->rawdata, frame->tempdata); else yuv420raw_to_yuv420p(frame, frame->rawdata, frame->tempdata); /* Deinterlace frame, if necessary */ - if (ov511->sensor == SEN_SAA7111A && frame->rawheight == 480) { - memmove(frame->rawdata, frame->tempdata, + if (ov->sensor == SEN_SAA7111A && frame->rawheight == 480) { + memcpy(frame->rawdata, frame->tempdata, MAX_RAW_DATA_SIZE(frame->width, frame->height)); deinterlace(frame, RAWFMT_YUV420, frame->rawdata, frame->tempdata); @@ -3813,18 +3693,19 @@ break; case VIDEO_PALETTE_YUV420: case VIDEO_PALETTE_YUV420P: - memmove(frame->data, frame->tempdata, + memcpy(frame->data, frame->tempdata, MAX_RAW_DATA_SIZE(frame->width, frame->height)); break; case VIDEO_PALETTE_YUV422P: /* Data is converted in place, so copy it in advance */ - memmove(frame->data, frame->tempdata, + memcpy(frame->data, frame->tempdata, MAX_RAW_DATA_SIZE(frame->width, frame->height)); yuv420p_to_yuv422p(frame, frame->data); break; default: - err("Cannot convert data to this format"); + err("Cannot convert data to %s", + symbolic(v4l1_plist, frame->format)); } if (fix_rgb_offset) @@ -3837,488 +3718,405 @@ * **********************************************************************/ -static int -ov511_move_data(struct usb_ov511 *ov511, struct urb *urb) +static inline void +ov511_move_data(struct usb_ov511 *ov, unsigned char *in, int n) { - unsigned char *cdata; - int data_size, num, offset, i, totlen = 0; - int aPackNum[FRAMES_PER_DESC]; - struct ov511_frame *frame; + int num, offset; + int pnum = in[ov->packet_size - 1]; /* Get packet number */ + int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight); + struct ov511_frame *frame = &ov->frame[ov->curframe]; struct timeval *ts; - PDEBUG(5, "Moving %d packets", urb->number_of_packets); - - data_size = ov511->packet_size - 1; + /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th + * byte non-zero. The EOF packet has image width/height in the + * 10th and 11th bytes. The 9th byte is given as follows: + * + * bit 7: EOF + * 6: compression enabled + * 5: 422/420/400 modes + * 4: 422/420/400 modes + * 3: 1 + * 2: snapshot button on + * 1: snapshot frame + * 0: even/odd field + */ - 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; + if (printph) { + info("ph(%3d): %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", + pnum, in[0], in[1], in[2], in[3], in[4], in[5], in[6], + in[7], in[8], in[9], in[10], in[11]); + } - urb->iso_frame_desc[i].actual_length = 0; - urb->iso_frame_desc[i].status = 0; + /* Check for SOF/EOF packet */ + if ((in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) || + (~in[8] & 0x08)) + goto check_middle; + + /* Frame end */ + if (in[8] & 0x80) { + ts = (struct timeval *)(frame->data + + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight)); + do_gettimeofday(ts); - cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + /* Get the actual frame size from the EOF header */ + frame->rawwidth = ((int)(in[9]) + 1) * 8; + frame->rawheight = ((int)(in[10]) + 1) * 8; - aPackNum[i] = n ? cdata[ov511->packet_size - 1] : -1; + PDEBUG(4, "Frame end, frame=%d, pnum=%d, w=%d, h=%d, recvd=%d", + ov->curframe, pnum, frame->rawwidth, frame->rawheight, + frame->bytes_recvd); - if (!n || ov511->curframe == -1) - continue; + /* Validate the header data */ + RESTRICT_TO_RANGE(frame->rawwidth, ov->minwidth, ov->maxwidth); + RESTRICT_TO_RANGE(frame->rawheight, ov->minheight, + ov->maxheight); - if (st) - PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st); + /* Don't allow byte count to exceed buffer size */ + RESTRICT_TO_RANGE(frame->bytes_recvd, 8, max_raw); - frame = &ov511->frame[ov511->curframe]; + if (frame->scanstate == STATE_LINES) { + int nextf; - /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th - * byte non-zero. The EOF packet has image width/height in the - * 10th and 11th bytes. The 9th byte is given as follows: - * - * bit 7: EOF - * 6: compression enabled - * 5: 422/420/400 modes - * 4: 422/420/400 modes - * 3: 1 - * 2: snapshot button on - * 1: snapshot frame - * 0: even/odd field - */ + frame->grabstate = FRAME_DONE; // FIXME: Is this right? - if (printph) { - info("packet header (%3d): %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", - cdata[ov511->packet_size - 1], - cdata[0], cdata[1], cdata[2], cdata[3], cdata[4], cdata[5], - cdata[6], cdata[7], cdata[8], cdata[9], cdata[10], cdata[11]); - } - - /* Check for SOF/EOF packet */ - if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | - cdata[4] | cdata[5] | cdata[6] | cdata[7]) || - (~cdata[8] & 0x08)) - goto check_middle; - - /* Frame end */ - if (cdata[8] & 0x80) { - ts = (struct timeval *)(frame->data - + MAX_FRAME_SIZE(ov511->maxwidth, ov511->maxheight)); - do_gettimeofday(ts); - - /* Get the actual frame size from the EOF header */ - frame->rawwidth = ((int)(cdata[9]) + 1) * 8; - frame->rawheight = ((int)(cdata[10]) + 1) * 8; - - PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d, recvd=%d", - ov511->curframe, - (int)(cdata[ov511->packet_size - 1]), - frame->rawwidth, - frame->rawheight, - frame->bytes_recvd); - - /* Validate the header data */ - RESTRICT_TO_RANGE(frame->rawwidth, ov511->minwidth, ov511->maxwidth); - RESTRICT_TO_RANGE(frame->rawheight, ov511->minheight, ov511->maxheight); - - /* Don't allow byte count to exceed buffer size */ - RESTRICT_TO_RANGE(frame->bytes_recvd, - 8, - MAX_RAW_DATA_SIZE(ov511->maxwidth, - ov511->maxheight)); - - if (frame->scanstate == STATE_LINES) { - int iFrameNext; - - frame->grabstate = FRAME_DONE; // FIXME: Is this right? - - if (waitqueue_active(&frame->wq)) { - frame->grabstate = FRAME_DONE; - wake_up_interruptible(&frame->wq); - } + if (waitqueue_active(&frame->wq)) { + frame->grabstate = FRAME_DONE; + wake_up_interruptible(&frame->wq); + } - /* If next frame is ready or grabbing, - * point to it */ - iFrameNext = (ov511->curframe + 1) % OV511_NUMFRAMES; - if (ov511->frame[iFrameNext].grabstate == FRAME_READY - || ov511->frame[iFrameNext].grabstate == FRAME_GRABBING) { - ov511->curframe = iFrameNext; - ov511->frame[iFrameNext].scanstate = STATE_SCANNING; + /* If next frame is ready or grabbing, + * point to it */ + nextf = (ov->curframe + 1) % OV511_NUMFRAMES; + if (ov->frame[nextf].grabstate == FRAME_READY + || ov->frame[nextf].grabstate == FRAME_GRABBING) { + ov->curframe = nextf; + ov->frame[nextf].scanstate = STATE_SCANNING; + } else { + if (frame->grabstate == FRAME_DONE) { + PDEBUG(4, "** Frame done **"); } else { - if (frame->grabstate == FRAME_DONE) { - PDEBUG(4, "Frame done! congratulations"); - } else { - PDEBUG(4, "Frame not ready? state = %d", - ov511->frame[iFrameNext].grabstate); - } - - ov511->curframe = -1; + PDEBUG(4, "Frame not ready? state = %d", + ov->frame[nextf].grabstate); } - } else { - PDEBUG(5, "Frame done, but not scanning"); - } - /* Image corruption caused by misplaced frame->segment = 0 - * fixed by carlosf@conectiva.com.br - */ - } else { - /* Frame start */ - PDEBUG(4, "Frame start, framenum = %d", ov511->curframe); - /* Check to see if it's a snapshot frame */ - /* FIXME?? Should the snapshot reset go here? Performance? */ - if (cdata[8] & 0x02) { - frame->snapshot = 1; - PDEBUG(3, "snapshot detected"); + ov->curframe = -1; } - - frame->scanstate = STATE_LINES; - frame->bytes_recvd = 0; - frame->compressed = cdata[8] & 0x40; + } else { + PDEBUG(5, "Frame done, but not scanning"); } + /* Image corruption caused by misplaced frame->segment = 0 + * fixed by carlosf@conectiva.com.br + */ + } else { + /* Frame start */ + PDEBUG(4, "Frame start, framenum = %d", ov->curframe); -check_middle: - /* Are we in a frame? */ - if (frame->scanstate != STATE_LINES) { - PDEBUG(5, "Not in a frame; packet skipped"); - continue; + /* Check to see if it's a snapshot frame */ + /* FIXME?? Should the snapshot reset go here? Performance? */ + if (in[8] & 0x02) { + frame->snapshot = 1; + PDEBUG(3, "snapshot detected"); } -#if 0 - /* Skip packet if first 9 bytes are zero. These are common, so - * we use a less expensive test here instead of later */ - if (frame->compressed) { - int b, skip = 1; - - for (b = 0; b < 9; b++) { - if (cdata[b]) - skip=0; - } + frame->scanstate = STATE_LINES; + frame->bytes_recvd = 0; + frame->compressed = in[8] & 0x40; + } - if (skip) { - PDEBUG(5, "Skipping packet (all zero)"); - continue; - } - } -#endif - /* If frame start, skip header */ - if (frame->bytes_recvd == 0) - offset = 9; - else - offset = 0; +check_middle: + /* Are we in a frame? */ + if (frame->scanstate != STATE_LINES) { + PDEBUG(5, "Not in a frame; packet skipped"); + return; + } - num = n - offset - 1; + /* If frame start, skip header */ + if (frame->bytes_recvd == 0) + offset = 9; + else + offset = 0; - /* Dump all data exactly as received */ - if (dumppix == 2) { - frame->bytes_recvd += n - 1; - if (frame->bytes_recvd <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight)) - memmove(frame->rawdata + frame->bytes_recvd - (n - 1), - &cdata[0], n - 1); - else - PDEBUG(3, "Raw data buffer overrun!! (%d)", - frame->bytes_recvd - - MAX_RAW_DATA_SIZE(ov511->maxwidth, - ov511->maxheight)); - } else if (!frame->compressed && !remove_zeros) { - frame->bytes_recvd += num; - if (frame->bytes_recvd <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight)) - memmove(frame->rawdata + frame->bytes_recvd - num, - &cdata[offset], num); - else - PDEBUG(3, "Raw data buffer overrun!! (%d)", - frame->bytes_recvd - - MAX_RAW_DATA_SIZE(ov511->maxwidth, - ov511->maxheight)); - } else { /* Remove all-zero FIFO lines (aligned 32-byte blocks) */ - int b, in = 0, allzero, copied=0; - if (offset) { - frame->bytes_recvd += 32 - offset; // Bytes out - memmove(frame->rawdata, - &cdata[offset], 32 - offset); - in += 32; - } + num = n - offset - 1; - while (in < n - 1) { - allzero = 1; - for (b = 0; b < 32; b++) { - if (cdata[in + b]) { - allzero = 0; - break; - } + /* Dump all data exactly as received */ + if (dumppix == 2) { + frame->bytes_recvd += n - 1; + if (frame->bytes_recvd <= max_raw) + memcpy(frame->rawdata + frame->bytes_recvd - (n - 1), + in, n - 1); + else + PDEBUG(3, "Raw data buffer overrun!! (%d)", + frame->bytes_recvd - max_raw); + } else if (!frame->compressed && !remove_zeros) { + frame->bytes_recvd += num; + if (frame->bytes_recvd <= max_raw) + memcpy(frame->rawdata + frame->bytes_recvd - num, + in + offset, num); + else + PDEBUG(3, "Raw data buffer overrun!! (%d)", + frame->bytes_recvd - max_raw); + } else { /* Remove all-zero FIFO lines (aligned 32-byte blocks) */ + int b, read = 0, allzero, copied = 0; + if (offset) { + frame->bytes_recvd += 32 - offset; // Bytes out + memcpy(frame->rawdata, in + offset, 32 - offset); + read += 32; + } + + while (read < n - 1) { + allzero = 1; + for (b = 0; b < 32; b++) { + if (in[read + b]) { + allzero = 0; + break; } + } - if (allzero) { - /* Don't copy it */ + if (allzero) { + /* Don't copy it */ + } else { + if (frame->bytes_recvd + copied + 32 <= max_raw) + { + memcpy(frame->rawdata + + frame->bytes_recvd + copied, + in + read, 32); + copied += 32; } else { - if (frame->bytes_recvd + copied + 32 - <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight)) { - memmove(frame->rawdata + frame->bytes_recvd + copied, - &cdata[in], 32); - copied += 32; - } else { - PDEBUG(3, "Raw data buffer overrun!!"); - } + PDEBUG(3, "Raw data buffer overrun!!"); } - in += 32; } - - frame->bytes_recvd += copied; + read += 32; } + frame->bytes_recvd += copied; } - - PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d", - aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4], - aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]); - - return totlen; } -static int -ov518_move_data(struct usb_ov511 *ov511, struct urb *urb) +static inline void +ov518_move_data(struct usb_ov511 *ov, unsigned char *in, int n) { - unsigned char *cdata; - int i, data_size, totlen = 0; - struct ov511_frame *frame; + int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight); + struct ov511_frame *frame = &ov->frame[ov->curframe]; struct timeval *ts; - PDEBUG(5, "Moving %d packets", urb->number_of_packets); - - /* OV518(+) has no packet numbering */ - data_size = ov511->packet_size; - - 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; - - urb->iso_frame_desc[i].actual_length = 0; - urb->iso_frame_desc[i].status = 0; - - cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - if (!n) { - PDEBUG(4, "Zero-length packet"); - continue; - } - - if (ov511->curframe == -1) { - PDEBUG(4, "No frame currently active"); - continue; - } - - if (st) - PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st); - - frame = &ov511->frame[ov511->curframe]; - -#if 0 - { - int d; - /* Print all data */ - for (d = 0; d <= data_size - 16; d += 16) { - info("%4x: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", d, - cdata[d], cdata[d+1], cdata[d+2], cdata[d+3], - cdata[d+4], cdata[d+5], cdata[d+6], cdata[d+7], - cdata[d+8], cdata[d+9], cdata[d+10], cdata[d+11], - cdata[d+12], cdata[d+13], cdata[d+14], cdata[d+15]); - } - } -#endif + if (printph) { + info("ph: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", + in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7], + in[8], in[9], in[10], in[11]); + } - if (printph) { - info("packet header: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", - cdata[0], cdata[1], cdata[2], cdata[3], cdata[4], cdata[5], - cdata[6], cdata[7], cdata[8], cdata[9], cdata[10], cdata[11]); + /* A false positive here is likely, until OVT gives me + * the definitive SOF/EOF format */ + if ((!(in[0] | in[1] | in[2] | in[3] | in[5])) && in[6]) { + if (frame->scanstate == STATE_LINES) { + PDEBUG(4, "Detected frame end/start"); + goto eof; + } else { //scanstate == STATE_SCANNING + /* Frame start */ + PDEBUG(4, "Frame start, framenum = %d", ov->curframe); + goto sof; } + } else { + goto check_middle; + } - /* A false positive here is likely, until OVT gives me - * the definitive SOF/EOF format */ - if ((!(cdata[0] | cdata[1] | cdata[2] | cdata[3] | - cdata[5])) && cdata[6]) { - - if (frame->scanstate == STATE_LINES) { - PDEBUG(4, "Detected frame end/start"); - goto eof; - } else { //scanstate == STATE_SCANNING - /* Frame start */ - PDEBUG(4, "Frame start, framenum = %d", ov511->curframe); - goto sof; - } - } else { - goto check_middle; - } - eof: - ts = (struct timeval *)(frame->data - + MAX_FRAME_SIZE(ov511->maxwidth, ov511->maxheight)); - do_gettimeofday(ts); - - PDEBUG(4, "Frame end, curframe = %d, hw=%d, vw=%d, recvd=%d", - ov511->curframe, - (int)(cdata[9]), (int)(cdata[10]), frame->bytes_recvd); - - // FIXME: Since we don't know the header formats yet, - // there is no way to know what the actual image size is - frame->rawwidth = frame->width; - frame->rawheight = frame->height; - - /* Validate the header data */ - RESTRICT_TO_RANGE(frame->rawwidth, ov511->minwidth, ov511->maxwidth); - RESTRICT_TO_RANGE(frame->rawheight, ov511->minheight, ov511->maxheight); - - /* Don't allow byte count to exceed buffer size */ - RESTRICT_TO_RANGE(frame->bytes_recvd, - 8, - MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight)); - - if (frame->scanstate == STATE_LINES) { - int iFrameNext; - - frame->grabstate = FRAME_DONE; // FIXME: Is this right? - - if (waitqueue_active(&frame->wq)) { - frame->grabstate = FRAME_DONE; - wake_up_interruptible(&frame->wq); - } - - /* If next frame is ready or grabbing, - * point to it */ - iFrameNext = (ov511->curframe + 1) % OV511_NUMFRAMES; - if (ov511->frame[iFrameNext].grabstate == FRAME_READY - || ov511->frame[iFrameNext].grabstate == FRAME_GRABBING) { - ov511->curframe = iFrameNext; - ov511->frame[iFrameNext].scanstate = STATE_SCANNING; - frame = &ov511->frame[iFrameNext]; + ts = (struct timeval *)(frame->data + + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight)); + do_gettimeofday(ts); + + PDEBUG(4, "Frame end, curframe = %d, hw=%d, vw=%d, recvd=%d", + ov->curframe, + (int)(in[9]), (int)(in[10]), frame->bytes_recvd); + + // FIXME: Since we don't know the header formats yet, + // there is no way to know what the actual image size is + frame->rawwidth = frame->width; + frame->rawheight = frame->height; + + /* Validate the header data */ + RESTRICT_TO_RANGE(frame->rawwidth, ov->minwidth, ov->maxwidth); + RESTRICT_TO_RANGE(frame->rawheight, ov->minheight, ov->maxheight); + + /* Don't allow byte count to exceed buffer size */ + RESTRICT_TO_RANGE(frame->bytes_recvd, 8, max_raw); + + if (frame->scanstate == STATE_LINES) { + int nextf; + + frame->grabstate = FRAME_DONE; // FIXME: Is this right? + + if (waitqueue_active(&frame->wq)) { + frame->grabstate = FRAME_DONE; + wake_up_interruptible(&frame->wq); + } + + /* If next frame is ready or grabbing, + * point to it */ + nextf = (ov->curframe + 1) % OV511_NUMFRAMES; + if (ov->frame[nextf].grabstate == FRAME_READY + || ov->frame[nextf].grabstate == FRAME_GRABBING) { + ov->curframe = nextf; + ov->frame[nextf].scanstate = STATE_SCANNING; + frame = &ov->frame[nextf]; + } else { + if (frame->grabstate == FRAME_DONE) { + PDEBUG(4, "** Frame done **"); } else { - if (frame->grabstate == FRAME_DONE) { - PDEBUG(4, "Frame done! congratulations"); - } else { - PDEBUG(4, "Frame not ready? state = %d", - ov511->frame[iFrameNext].grabstate); - } - - ov511->curframe = -1; - PDEBUG(4, "SOF dropped (no active frame)"); - continue; /* Nowhere to store this frame */ + PDEBUG(4, "Frame not ready? state = %d", + ov->frame[nextf].grabstate); } + + ov->curframe = -1; + PDEBUG(4, "SOF dropped (no active frame)"); + return; /* Nowhere to store this frame */ } - /* Image corruption caused by misplaced frame->segment = 0 - * fixed by carlosf@conectiva.com.br - */ + } sof: - PDEBUG(4, "Starting capture on frame %d", frame->framenum); + PDEBUG(4, "Starting capture on frame %d", frame->framenum); + // Snapshot not reverse-engineered yet. #if 0 - /* Check to see if it's a snapshot frame */ - /* FIXME?? Should the snapshot reset go here? Performance? */ - if (cdata[8] & 0x02) { - frame->snapshot = 1; - PDEBUG(3, "snapshot detected"); - } + /* Check to see if it's a snapshot frame */ + /* FIXME?? Should the snapshot reset go here? Performance? */ + if (in[8] & 0x02) { + frame->snapshot = 1; + PDEBUG(3, "snapshot detected"); + } #endif - frame->scanstate = STATE_LINES; - frame->bytes_recvd = 0; + frame->scanstate = STATE_LINES; + frame->bytes_recvd = 0; // frame->compressed = 1; check_middle: - /* Are we in a frame? */ - if (frame->scanstate != STATE_LINES) { - PDEBUG(4, "scanstate: no SOF yet"); - continue; - } - - /* Dump all data exactly as received */ - if (dumppix == 2) { - frame->bytes_recvd += n; - if (frame->bytes_recvd <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight)) - memmove(frame->rawdata + frame->bytes_recvd - n, - &cdata[0], n); - else - PDEBUG(3, "Raw data buffer overrun!! (%d)", - frame->bytes_recvd - - MAX_RAW_DATA_SIZE(ov511->maxwidth, - ov511->maxheight)); - } else { - /* All incoming data are divided into 8-byte segments. If the - * segment contains all zero bytes, it must be skipped. These - * zero-segments allow the OV518 to mainain a constant data rate - * regardless of the effectiveness of the compression. Segments - * are aligned relative to the beginning of each isochronous - * packet. The first segment is a header. - */ + /* Are we in a frame? */ + if (frame->scanstate != STATE_LINES) { + PDEBUG(4, "scanstate: no SOF yet"); + return; + } - int b, in = 0, allzero, copied=0; + /* Dump all data exactly as received */ + if (dumppix == 2) { + frame->bytes_recvd += n; + if (frame->bytes_recvd <= max_raw) + memcpy(frame->rawdata + frame->bytes_recvd - n, in, n); + else + PDEBUG(3, "Raw data buffer overrun!! (%d)", + frame->bytes_recvd - max_raw); + } else { + /* All incoming data are divided into 8-byte segments. If the + * segment contains all zero bytes, it must be skipped. These + * zero-segments allow the OV518 to mainain a constant data rate + * regardless of the effectiveness of the compression. Segments + * are aligned relative to the beginning of each isochronous + * packet. The first segment is a header (the decompressor + * skips it later). + */ -// Decompressor expects the header -#if 0 - if (frame->bytes_recvd == 0) - in += 8; /* Skip header */ -#endif + int b, read = 0, allzero, copied = 0; - while (in < n) { - allzero = 1; - for (b = 0; b < 8; b++) { - if (cdata[in + b]) { - allzero = 0; - break; - } + while (read < n) { + allzero = 1; + for (b = 0; b < 8; b++) { + if (in[read + b]) { + allzero = 0; + break; } + } - if (allzero) { - /* Don't copy it */ + if (allzero) { + /* Don't copy it */ + } else { + if (frame->bytes_recvd + copied + 8 <= max_raw) + { + memcpy(frame->rawdata + + frame->bytes_recvd + copied, + in + read, 8); + copied += 8; } else { - if (frame->bytes_recvd + copied + 8 - <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight)) { - memmove(frame->rawdata + frame->bytes_recvd + copied, - &cdata[in], 8); - copied += 8; - } else { - PDEBUG(3, "Raw data buffer overrun!!"); - } + PDEBUG(3, "Raw data buffer overrun!!"); } - in += 8; } - frame->bytes_recvd += copied; + read += 8; } + frame->bytes_recvd += copied; } - - return totlen; } static void -ov511_isoc_irq(struct urb *urb) +ov51x_isoc_irq(struct urb *urb) { - int len; - struct usb_ov511 *ov511; + int i; + struct usb_ov511 *ov; + struct ov511_sbuf *sbuf; if (!urb->context) { PDEBUG(4, "no context"); return; } - ov511 = (struct usb_ov511 *) urb->context; + sbuf = urb->context; + ov = sbuf->ov; - if (!ov511->dev || !ov511->user) { + if (!ov || !ov->dev || !ov->user) { PDEBUG(4, "no device, or not open"); return; } - if (!ov511->streaming) { + if (!ov->streaming) { PDEBUG(4, "hmmm... not streaming, but got interrupt"); return; } + if (urb->status == -ENOENT || urb->status == -ECONNRESET) { + PDEBUG(4, "URB unlinked"); + return; + } + + if (urb->status != -EINPROGRESS && urb->status != 0) { + err("ERROR: urb->status=%d: %s", urb->status, + symbolic(urb_errlist, urb->status)); + return; + } + /* Copy the data received into our frame buffer */ - if (ov511->curframe >= 0) { - if (ov511->bridge == BRG_OV511 || - ov511->bridge == BRG_OV511PLUS) - len = ov511_move_data(ov511, urb); - else if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) - len = ov518_move_data(ov511, urb); - else - err("Unknown bridge device (%d)", ov511->bridge); - } else if (waitqueue_active(&ov511->wq)) { - wake_up_interruptible(&ov511->wq); + PDEBUG(5, "sbuf[%d]: Moving %d packets", sbuf->n, + urb->number_of_packets); + for (i = 0; i < urb->number_of_packets; i++) { + /* Warning: Don't call *_move_data() if no frame active! */ + if (ov->curframe >= 0) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + unsigned char *cdata; + + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = 0; + + cdata = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + + if (!n) { + PDEBUG(4, "Zero-length packet"); + continue; + } + + if (st) + PDEBUG(2, "data error: [%d] len=%d, status=%d", + i, n, st); + + if (ov->bclass == BCL_OV511) + ov511_move_data(ov, cdata, n); + else if (ov->bclass == BCL_OV518) + ov518_move_data(ov, cdata, n); + else + err("Unknown bridge device (%d)", ov->bridge); + + } else if (waitqueue_active(&ov->wq)) { + wake_up_interruptible(&ov->wq); + } } - urb->dev = ov511->dev; + urb->dev = ov->dev; return; } @@ -4330,16 +4128,16 @@ ***************************************************************************/ static int -ov511_init_isoc(struct usb_ov511 *ov511) +ov51x_init_isoc(struct usb_ov511 *ov) { struct urb *urb; int fx, err, n, size; PDEBUG(3, "*** Initializing capture ***"); - ov511->curframe = -1; + ov->curframe = -1; - if (ov511->bridge == BRG_OV511) { + if (ov->bridge == BRG_OV511) { if (cams == 1) size = 993; else if (cams == 2) size = 513; else if (cams == 3 || cams == 4) size = 257; @@ -4347,7 +4145,7 @@ err("\"cams\" parameter too high!"); return -1; } - } else if (ov511->bridge == BRG_OV511PLUS) { + } else if (ov->bridge == BRG_OV511PLUS) { if (cams == 1) size = 961; else if (cams == 2) size = 513; else if (cams == 3 || cams == 4) size = 257; @@ -4357,8 +4155,7 @@ err("\"cams\" parameter too high!"); return -1; } - } else if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) { + } else if (ov->bclass == BCL_OV518) { if (cams == 1) size = 896; else if (cams == 2) size = 512; else if (cams == 3 || cams == 4) size = 256; @@ -4374,49 +4171,45 @@ if (packetsize == -1) { // FIXME: OV518 is hardcoded to 15 FPS (alternate 5) for now - if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) - ov511_set_packet_size(ov511, 640); + if (ov->bclass == BCL_OV518) + ov51x_set_packet_size(ov, 640); else - ov511_set_packet_size(ov511, size); + ov51x_set_packet_size(ov, size); } else { info("Forcing packet size to %d", packetsize); - ov511_set_packet_size(ov511, packetsize); + ov51x_set_packet_size(ov, packetsize); } for (n = 0; n < OV511_NUMSBUF; n++) { urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); - if (!urb) { err("init isoc: usb_alloc_urb ret. NULL"); return -ENOMEM; } - ov511->sbuf[n].urb = urb; - urb->dev = ov511->dev; - urb->context = ov511; - urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS); + ov->sbuf[n].urb = urb; + urb->dev = ov->dev; + urb->context = &ov->sbuf[n]; + urb->pipe = usb_rcvisocpipe(ov->dev, OV511_ENDPOINT_ADDRESS); urb->transfer_flags = USB_ISO_ASAP; - urb->transfer_buffer = ov511->sbuf[n].data; - urb->complete = ov511_isoc_irq; + urb->transfer_buffer = ov->sbuf[n].data; + urb->complete = ov51x_isoc_irq; urb->number_of_packets = FRAMES_PER_DESC; - urb->transfer_buffer_length = - ov511->packet_size * FRAMES_PER_DESC; + urb->transfer_buffer_length = ov->packet_size * FRAMES_PER_DESC; for (fx = 0; fx < FRAMES_PER_DESC; fx++) { - urb->iso_frame_desc[fx].offset = - ov511->packet_size * fx; - urb->iso_frame_desc[fx].length = ov511->packet_size; + urb->iso_frame_desc[fx].offset = ov->packet_size * fx; + urb->iso_frame_desc[fx].length = ov->packet_size; } } - ov511->streaming = 1; + ov->streaming = 1; - ov511->sbuf[OV511_NUMSBUF - 1].urb->next = ov511->sbuf[0].urb; + ov->sbuf[OV511_NUMSBUF - 1].urb->next = ov->sbuf[0].urb; for (n = 0; n < OV511_NUMSBUF - 1; n++) - ov511->sbuf[n].urb->next = ov511->sbuf[n+1].urb; + ov->sbuf[n].urb->next = ov->sbuf[n+1].urb; for (n = 0; n < OV511_NUMSBUF; n++) { - ov511->sbuf[n].urb->dev = ov511->dev; - err = usb_submit_urb(ov511->sbuf[n].urb, GFP_KERNEL); + ov->sbuf[n].urb->dev = ov->dev; + err = usb_submit_urb(ov->sbuf[n].urb, GFP_KERNEL); if (err) err("init isoc: usb_submit_urb(%d) ret %d", n, err); } @@ -4425,51 +4218,56 @@ } static void -ov511_stop_isoc(struct usb_ov511 *ov511) +ov51x_unlink_isoc(struct usb_ov511 *ov) { int n; - if (!ov511->streaming || !ov511->dev) + /* Unschedule all of the iso td's */ + for (n = OV511_NUMSBUF - 1; n >= 0; n--) { + if (ov->sbuf[n].urb) { + usb_unlink_urb(ov->sbuf[n].urb); + usb_free_urb(ov->sbuf[n].urb); + ov->sbuf[n].urb = NULL; + } + } +} + +static void +ov51x_stop_isoc(struct usb_ov511 *ov) +{ + if (!ov->streaming || !ov->dev) return; PDEBUG(3, "*** Stopping capture ***"); - ov511_set_packet_size(ov511, 0); + ov51x_set_packet_size(ov, 0); - ov511->streaming = 0; + ov->streaming = 0; - /* Unschedule all of the iso td's */ - for (n = OV511_NUMSBUF - 1; n >= 0; n--) { - if (ov511->sbuf[n].urb) { - ov511->sbuf[n].urb->next = NULL; - usb_unlink_urb(ov511->sbuf[n].urb); - usb_free_urb(ov511->sbuf[n].urb); - ov511->sbuf[n].urb = NULL; - } - } + ov51x_unlink_isoc(ov); } static int -ov511_new_frame(struct usb_ov511 *ov511, int framenum) +ov51x_new_frame(struct usb_ov511 *ov, int framenum) { struct ov511_frame *frame; int newnum; - PDEBUG(4, "ov511->curframe = %d, framenum = %d", ov511->curframe, - framenum); - if (!ov511->dev) + PDEBUG(4, "ov->curframe = %d, framenum = %d", ov->curframe, framenum); + + if (!ov->dev) return -1; /* If we're not grabbing a frame right now and the other frame is */ /* ready to be grabbed into, then use it instead */ - if (ov511->curframe == -1) { + if (ov->curframe == -1) { newnum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES; - if (ov511->frame[newnum].grabstate == FRAME_READY) + if (ov->frame[newnum].grabstate == FRAME_READY) framenum = newnum; } else return 0; - frame = &ov511->frame[framenum]; + frame = &ov->frame[framenum]; PDEBUG(4, "framenum = %d, width = %d, height = %d", framenum, frame->width, frame->height); @@ -4478,16 +4276,16 @@ frame->scanstate = STATE_SCANNING; frame->snapshot = 0; - ov511->curframe = framenum; + ov->curframe = framenum; /* Make sure it's not too big */ - if (frame->width > ov511->maxwidth) - frame->width = ov511->maxwidth; + if (frame->width > ov->maxwidth) + frame->width = ov->maxwidth; frame->width &= ~7L; /* Multiple of 8 */ - if (frame->height > ov511->maxheight) - frame->height = ov511->maxheight; + if (frame->height > ov->maxheight) + frame->height = ov->maxheight; frame->height &= ~3L; /* Multiple of 4 */ @@ -4499,388 +4297,373 @@ * Buffer management * ***************************************************************************/ -static int -ov511_alloc(struct usb_ov511 *ov511) + +/* + * - You must acquire buf_lock before entering this function. + * - Because this code will free any non-null pointer, you must be sure to null + * them if you explicitly free them somewhere else! + */ +static void +ov51x_do_dealloc(struct usb_ov511 *ov) { int i; - int w = ov511->maxwidth; - int h = ov511->maxheight; - PDEBUG(4, "entered"); - down(&ov511->buf_lock); - if (ov511->buf_state == BUF_PEND_DEALLOC) { - ov511->buf_state = BUF_ALLOCATED; - del_timer(&ov511->buf_timer); + if (ov->fbuf) { + rvfree(ov->fbuf, OV511_NUMFRAMES + * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight)); + ov->fbuf = NULL; } - if (ov511->buf_state == BUF_ALLOCATED) - goto out; - - ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE(w, h)); - if (!ov511->fbuf) - goto error; - - ov511->rawfbuf = vmalloc(OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h)); - if (!ov511->rawfbuf) { - rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE(w, h)); - ov511->fbuf = NULL; - goto error; + if (ov->rawfbuf) { + vfree(ov->rawfbuf); + ov->rawfbuf = NULL; } - memset(ov511->rawfbuf, 0, OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h)); - ov511->tempfbuf = vmalloc(OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h)); - if (!ov511->tempfbuf) { - vfree(ov511->rawfbuf); - ov511->rawfbuf = NULL; - rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE(w, h)); - ov511->fbuf = NULL; - goto error; + if (ov->tempfbuf) { + vfree(ov->tempfbuf); + ov->tempfbuf = NULL; } - memset(ov511->tempfbuf, 0, OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h)); for (i = 0; i < OV511_NUMSBUF; i++) { - ov511->sbuf[i].data = kmalloc(FRAMES_PER_DESC * - MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!ov511->sbuf[i].data) { - while (--i) { - kfree(ov511->sbuf[i].data); - ov511->sbuf[i].data = NULL; - } - vfree(ov511->tempfbuf); - ov511->tempfbuf = NULL; - vfree(ov511->rawfbuf); - ov511->rawfbuf = NULL; - rvfree(ov511->fbuf, - OV511_NUMFRAMES * MAX_DATA_SIZE(w, h)); - ov511->fbuf = NULL; - - goto error; + if (ov->sbuf[i].data) { + kfree(ov->sbuf[i].data); + ov->sbuf[i].data = NULL; } - PDEBUG(4, "sbuf[%d] @ %p", i, ov511->sbuf[i].data); } for (i = 0; i < OV511_NUMFRAMES; i++) { - ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE(w, h); - ov511->frame[i].rawdata = ov511->rawfbuf - + i * MAX_RAW_DATA_SIZE(w, h); - ov511->frame[i].tempdata = ov511->tempfbuf - + i * MAX_RAW_DATA_SIZE(w, h); - PDEBUG(4, "frame[%d] @ %p", i, ov511->frame[i].data); + ov->frame[i].data = NULL; + ov->frame[i].rawdata = NULL; + ov->frame[i].tempdata = NULL; + if (ov->frame[i].compbuf) { + free_page((unsigned long) ov->frame[i].compbuf); + ov->frame[i].compbuf = NULL; + } } - ov511->buf_state = BUF_ALLOCATED; -out: - up(&ov511->buf_lock); + PDEBUG(4, "buffer memory deallocated"); + ov->buf_state = BUF_NOT_ALLOCATED; PDEBUG(4, "leaving"); - return 0; -error: - ov511->buf_state = BUF_NOT_ALLOCATED; - up(&ov511->buf_lock); - PDEBUG(4, "errored"); - return -ENOMEM; } -/* - * - You must acquire buf_lock before entering this function. - * - Because this code will free any non-null pointer, you must be sure to null - * them if you explicitly free them somewhere else! - */ -static void -ov511_do_dealloc(struct usb_ov511 *ov511) +static int +ov51x_alloc(struct usb_ov511 *ov) { int i; + const int w = ov->maxwidth; + const int h = ov->maxheight; + const int data_bufsize = OV511_NUMFRAMES * MAX_DATA_SIZE(w, h); + const int raw_bufsize = OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h); + PDEBUG(4, "entered"); + down(&ov->buf_lock); - if (ov511->fbuf) { - rvfree(ov511->fbuf, OV511_NUMFRAMES - * MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight)); - ov511->fbuf = NULL; + if (ov->buf_state == BUF_PEND_DEALLOC) { + ov->buf_state = BUF_ALLOCATED; + del_timer(&ov->buf_timer); } - if (ov511->rawfbuf) { - vfree(ov511->rawfbuf); - ov511->rawfbuf = NULL; - } + if (ov->buf_state == BUF_ALLOCATED) + goto out; - if (ov511->tempfbuf) { - vfree(ov511->tempfbuf); - ov511->tempfbuf = NULL; - } + ov->fbuf = rvmalloc(data_bufsize); + if (!ov->fbuf) + goto error; + + ov->rawfbuf = vmalloc(raw_bufsize); + if (!ov->rawfbuf) + goto error; + + memset(ov->rawfbuf, 0, raw_bufsize); + + ov->tempfbuf = vmalloc(raw_bufsize); + if (!ov->tempfbuf) + goto error; + + memset(ov->tempfbuf, 0, raw_bufsize); for (i = 0; i < OV511_NUMSBUF; i++) { - if (ov511->sbuf[i].data) { - kfree(ov511->sbuf[i].data); - ov511->sbuf[i].data = NULL; - } + ov->sbuf[i].data = kmalloc(FRAMES_PER_DESC * + MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ov->sbuf[i].data) + goto error; + + PDEBUG(4, "sbuf[%d] @ %p", i, ov->sbuf[i].data); } for (i = 0; i < OV511_NUMFRAMES; i++) { - ov511->frame[i].data = NULL; - ov511->frame[i].rawdata = NULL; - ov511->frame[i].tempdata = NULL; + ov->frame[i].data = ov->fbuf + i * MAX_DATA_SIZE(w, h); + ov->frame[i].rawdata = ov->rawfbuf + + i * MAX_RAW_DATA_SIZE(w, h); + ov->frame[i].tempdata = ov->tempfbuf + + i * MAX_RAW_DATA_SIZE(w, h); + + ov->frame[i].compbuf = + (unsigned char *) __get_free_page(GFP_KERNEL); + if (!ov->frame[i].compbuf) + goto error; + + PDEBUG(4, "frame[%d] @ %p", i, ov->frame[i].data); } - PDEBUG(4, "buffer memory deallocated"); - ov511->buf_state = BUF_NOT_ALLOCATED; + ov->buf_state = BUF_ALLOCATED; +out: + up(&ov->buf_lock); PDEBUG(4, "leaving"); + return 0; +error: + ov51x_do_dealloc(ov); + up(&ov->buf_lock); + PDEBUG(4, "errored"); + return -ENOMEM; } static void -ov511_buf_callback(unsigned long data) +ov51x_buf_callback(unsigned long data) { - struct usb_ov511 *ov511 = (struct usb_ov511 *)data; + struct usb_ov511 *ov = (struct usb_ov511 *)data; PDEBUG(4, "entered"); - down(&ov511->buf_lock); + down(&ov->buf_lock); - if (ov511->buf_state == BUF_PEND_DEALLOC) - ov511_do_dealloc(ov511); + if (ov->buf_state == BUF_PEND_DEALLOC) + ov51x_do_dealloc(ov); - up(&ov511->buf_lock); + up(&ov->buf_lock); PDEBUG(4, "leaving"); } static void -ov511_dealloc(struct usb_ov511 *ov511, int now) +ov51x_dealloc(struct usb_ov511 *ov, int now) { - struct timer_list *bt = &(ov511->buf_timer); + struct timer_list *bt = &(ov->buf_timer); PDEBUG(4, "entered"); - down(&ov511->buf_lock); + down(&ov->buf_lock); PDEBUG(4, "deallocating buffer memory %s", now ? "now" : "later"); - if (ov511->buf_state == BUF_PEND_DEALLOC) { - ov511->buf_state = BUF_ALLOCATED; + if (ov->buf_state == BUF_PEND_DEALLOC) { + ov->buf_state = BUF_ALLOCATED; del_timer(bt); } if (now) - ov511_do_dealloc(ov511); + ov51x_do_dealloc(ov); else { - ov511->buf_state = BUF_PEND_DEALLOC; + ov->buf_state = BUF_PEND_DEALLOC; init_timer(bt); - bt->function = ov511_buf_callback; - bt->data = (unsigned long)ov511; + bt->function = ov51x_buf_callback; + bt->data = (unsigned long)ov; bt->expires = jiffies + buf_timeout * HZ; add_timer(bt); } - up(&ov511->buf_lock); + up(&ov->buf_lock); PDEBUG(4, "leaving"); } /**************************************************************************** * - * V4L API + * V4L 1 API * ***************************************************************************/ +#ifdef OV511_OLD_V4L static int -ov511_open(struct video_device *vdev, int flags) +ov51x_v4l1_open(struct video_device *vdev, int flags) { - struct usb_ov511 *ov511 = vdev->priv; +#else +static int +ov51x_v4l1_open(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); +#endif + struct usb_ov511 *ov = vdev->priv; int err, i; PDEBUG(4, "opening"); - down(&ov511->lock); + down(&ov->lock); err = -EBUSY; - if (ov511->user) + if (ov->user) goto out; err = -ENOMEM; - if (ov511_alloc(ov511)) + if (ov51x_alloc(ov)) goto out; - ov511->sub_flag = 0; + ov->sub_flag = 0; /* In case app doesn't set them... */ - if (ov51x_set_default_params(ov511) < 0) + if (ov51x_set_default_params(ov) < 0) goto out; /* Make sure frames are reset */ for (i = 0; i < OV511_NUMFRAMES; i++) { - ov511->frame[i].grabstate = FRAME_UNUSED; - ov511->frame[i].bytes_read = 0; + ov->frame[i].grabstate = FRAME_UNUSED; + ov->frame[i].bytes_read = 0; } /* If compression is on, make sure now that a * decompressor can be loaded */ - if (ov511->compress && !ov511->decomp_ops) { - err = ov51x_request_decompressor(ov511); - if (err) + if (ov->compress && !ov->decomp_ops) { + err = request_decompressor(ov); + if (err && !dumppix) goto out; } - err = ov511_init_isoc(ov511); + err = ov51x_init_isoc(ov); if (err) { - ov511_dealloc(ov511, 0); + ov51x_dealloc(ov, 0); goto out; } - ov511->user++; + ov->user++; - if (ov511->led_policy == LED_AUTO) - ov51x_led_control(ov511, 1); + if (ov->led_policy == LED_AUTO) + ov51x_led_control(ov, 1); out: - up(&ov511->lock); - + up(&ov->lock); return err; } +#ifdef OV511_OLD_V4L static void -ov511_close(struct video_device *dev) +ov51x_v4l1_close(struct video_device *vdev) { - struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; +#else +static int +ov51x_v4l1_close(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); +#endif + struct usb_ov511 *ov = vdev->priv; PDEBUG(4, "ov511_close"); - down(&ov511->lock); + down(&ov->lock); - ov511->user--; - ov511_stop_isoc(ov511); + ov->user--; + ov51x_stop_isoc(ov); - ov51x_release_decompressor(ov511); + release_decompressor(ov); - if (ov511->led_policy == LED_AUTO) - ov51x_led_control(ov511, 0); + if (ov->led_policy == LED_AUTO) + ov51x_led_control(ov, 0); - if (ov511->dev) - ov511_dealloc(ov511, 0); + if (ov->dev) + ov51x_dealloc(ov, 0); - up(&ov511->lock); + up(&ov->lock); /* Device unplugged while open. Only a minimum of unregistration is done * here; the disconnect callback already did the rest. */ - if (!ov511->dev) { - ov511_dealloc(ov511, 1); - video_unregister_device(&ov511->vdev); - kfree(ov511); - ov511 = NULL; + if (!ov->dev) { + down(&ov->cbuf_lock); + kfree(ov->cbuf); + ov->cbuf = NULL; + up(&ov->cbuf_lock); + + ov51x_dealloc(ov, 1); + video_unregister_device(&ov->vdev); + kfree(ov); + ov = NULL; } -} - -static int -ov511_init_done(struct video_device *vdev) -{ -#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) - create_proc_ov511_cam((struct usb_ov511 *)vdev); -#endif +#ifdef OV511_OLD_V4L + return; +#else return 0; -} - -static long -ov511_write(struct video_device *vdev, const char *buf, - unsigned long count, int noblock) -{ - return -EINVAL; +#endif } /* Do not call this function directly! */ static int -ov511_ioctl_internal(struct video_device *vdev, unsigned int cmd, void *arg) +ov51x_v4l1_ioctl_internal(struct usb_ov511 *ov, unsigned int cmd, + void *arg) { - struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev; - PDEBUG(5, "IOCtl: 0x%X", cmd); - if (!ov511->dev) + if (!ov->dev) return -EIO; switch (cmd) { case VIDIOCGCAP: { - struct video_capability b; + struct video_capability *b = arg; PDEBUG(4, "VIDIOCGCAP"); - memset(&b, 0, sizeof(b)); - sprintf(b.name, "%s USB Camera", - ov511->bridge == BRG_OV511 ? "OV511" : - ov511->bridge == BRG_OV511PLUS ? "OV511+" : - ov511->bridge == BRG_OV518 ? "OV518" : - ov511->bridge == BRG_OV518PLUS ? "OV518+" : - "unknown"); - b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; - if (ov511->has_tuner) - b.type |= VID_TYPE_TUNER; - b.channels = ov511->num_inputs; - b.audios = ov511->has_audio_proc ? 1:0; - b.maxwidth = ov511->maxwidth; - b.maxheight = ov511->maxheight; - b.minwidth = ov511->minwidth; - b.minheight = ov511->minheight; + memset(b, 0, sizeof(struct video_capability)); + sprintf(b->name, "%s USB Camera", + symbolic(brglist, ov->bridge)); + b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; + if (ov->has_tuner) + b->type |= VID_TYPE_TUNER; + b->channels = ov->num_inputs; + b->audios = ov->has_audio_proc ? 1:0; + b->maxwidth = ov->maxwidth; + b->maxheight = ov->maxheight; + b->minwidth = ov->minwidth; + b->minheight = ov->minheight; - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - return 0; } case VIDIOCGCHAN: { - struct video_channel v; + struct video_channel *v = arg; PDEBUG(4, "VIDIOCGCHAN"); - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - - if ((unsigned)(v.channel) >= ov511->num_inputs) { - err("Invalid channel (%d)", v.channel); + if ((unsigned)(v->channel) >= ov->num_inputs) { + err("Invalid channel (%d)", v->channel); return -EINVAL; } - v.norm = ov511->norm; - v.type = (ov511->has_tuner) ? VIDEO_TYPE_TV : VIDEO_TYPE_CAMERA; - v.flags = (ov511->has_tuner) ? VIDEO_VC_TUNER : 0; - v.flags |= (ov511->has_audio_proc) ? VIDEO_VC_AUDIO : 0; -// v.flags |= (ov511->has_decoder) ? VIDEO_VC_NORM : 0; - v.tuners = (ov511->has_tuner) ? 1:0; - decoder_get_input_name(ov511, v.channel, v.name); + v->norm = ov->norm; + v->type = (ov->has_tuner) ? VIDEO_TYPE_TV : VIDEO_TYPE_CAMERA; + v->flags = (ov->has_tuner) ? VIDEO_VC_TUNER : 0; + v->flags |= (ov->has_audio_proc) ? VIDEO_VC_AUDIO : 0; +// v->flags |= (ov->has_decoder) ? VIDEO_VC_NORM : 0; + v->tuners = (ov->has_tuner) ? 1:0; + decoder_get_input_name(ov, v->channel, v->name); - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; } case VIDIOCSCHAN: { - struct video_channel v; + struct video_channel *v = arg; int err; PDEBUG(4, "VIDIOCSCHAN"); - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - /* Make sure it's not a camera */ - if (!ov511->has_decoder) { - if (v.channel == 0) + if (!ov->has_decoder) { + if (v->channel == 0) return 0; else return -EINVAL; } - if (v.norm != VIDEO_MODE_PAL && - v.norm != VIDEO_MODE_NTSC && - v.norm != VIDEO_MODE_SECAM && - v.norm != VIDEO_MODE_AUTO) { - err("Invalid norm (%d)", v.norm); + if (v->norm != VIDEO_MODE_PAL && + v->norm != VIDEO_MODE_NTSC && + v->norm != VIDEO_MODE_SECAM && + v->norm != VIDEO_MODE_AUTO) { + err("Invalid norm (%d)", v->norm); return -EINVAL; } - if ((unsigned)(v.channel) >= ov511->num_inputs) { - err("Invalid channel (%d)", v.channel); + if ((unsigned)(v->channel) >= ov->num_inputs) { + err("Invalid channel (%d)", v->channel); return -EINVAL; } - err = decoder_set_input(ov511, v.channel); + err = decoder_set_input(ov, v->channel); if (err) return err; - err = decoder_set_norm(ov511, v.norm); + err = decoder_set_norm(ov, v->norm); if (err) return err; @@ -4888,272 +4671,251 @@ } case VIDIOCGPICT: { - struct video_picture p; + struct video_picture *p = arg; PDEBUG(4, "VIDIOCGPICT"); - memset(&p, 0, sizeof(p)); - - if (sensor_get_picture(ov511, &p)) + memset(p, 0, sizeof(struct video_picture)); + if (sensor_get_picture(ov, p)) return -EIO; - if (copy_to_user(arg, &p, sizeof(p))) - return -EFAULT; - return 0; } case VIDIOCSPICT: { - struct video_picture p; + struct video_picture *p = arg; int i; PDEBUG(4, "VIDIOCSPICT"); - if (copy_from_user(&p, arg, sizeof(p))) - return -EFAULT; - - if (!ov511_get_depth(p.palette)) + if (!get_depth(p->palette)) return -EINVAL; - if (sensor_set_picture(ov511, &p)) + if (sensor_set_picture(ov, p)) return -EIO; - if (force_palette && p.palette != force_palette) { - info("Palette rejected (%d)", p.palette); + if (force_palette && p->palette != force_palette) { + info("Palette rejected (%s)", + symbolic(v4l1_plist, p->palette)); return -EINVAL; } // FIXME: Format should be independent of frames - if (p.palette != ov511->frame[0].format) { + if (p->palette != ov->frame[0].format) { PDEBUG(4, "Detected format change"); /* If we're collecting previous frame wait before changing modes */ - interruptible_sleep_on(&ov511->wq); + interruptible_sleep_on(&ov->wq); if (signal_pending(current)) return -EINTR; - mode_init_regs(ov511, ov511->frame[0].width, - ov511->frame[0].height, p.palette, - ov511->sub_flag); + mode_init_regs(ov, ov->frame[0].width, + ov->frame[0].height, p->palette, ov->sub_flag); } - PDEBUG(4, "Setting depth=%d, palette=%d", p.depth, p.palette); + PDEBUG(4, "Setting depth=%d, palette=%s", + p->depth, symbolic(v4l1_plist, p->palette)); + for (i = 0; i < OV511_NUMFRAMES; i++) { - ov511->frame[i].depth = p.depth; - ov511->frame[i].format = p.palette; + ov->frame[i].depth = p->depth; + ov->frame[i].format = p->palette; } return 0; } case VIDIOCGCAPTURE: { - int vf; + int *vf = arg; PDEBUG(4, "VIDIOCGCAPTURE"); - if (copy_from_user(&vf, arg, sizeof(vf))) - return -EFAULT; - ov511->sub_flag = vf; + ov->sub_flag = *vf; return 0; } case VIDIOCSCAPTURE: { - struct video_capture vc; + struct video_capture *vc = arg; PDEBUG(4, "VIDIOCSCAPTURE"); - if (copy_from_user(&vc, arg, sizeof(vc))) - return -EFAULT; - if (vc.flags) + if (vc->flags) return -EINVAL; - if (vc.decimation) + if (vc->decimation) return -EINVAL; - vc.x &= ~3L; - vc.y &= ~1L; - vc.y &= ~31L; - - if (vc.width == 0) - vc.width = 32; - - vc.height /= 16; - vc.height *= 16; - if (vc.height == 0) - vc.height = 16; - - ov511->subx = vc.x; - ov511->suby = vc.y; - ov511->subw = vc.width; - ov511->subh = vc.height; + vc->x &= ~3L; + vc->y &= ~1L; + vc->y &= ~31L; + + if (vc->width == 0) + vc->width = 32; + + vc->height /= 16; + vc->height *= 16; + if (vc->height == 0) + vc->height = 16; + + ov->subx = vc->x; + ov->suby = vc->y; + ov->subw = vc->width; + ov->subh = vc->height; return 0; } case VIDIOCSWIN: { - struct video_window vw; + struct video_window *vw = arg; int i, result; - if (copy_from_user(&vw, arg, sizeof(vw))) - return -EFAULT; - - PDEBUG(4, "VIDIOCSWIN: width=%d, height=%d", - vw.width, vw.height); + PDEBUG(4, "VIDIOCSWIN: %dx%d", vw->width, vw->height); #if 0 - if (vw.flags) + if (vw->flags) return -EINVAL; - if (vw.clipcount) + if (vw->clipcount) return -EINVAL; - if (vw.height != ov511->maxheight) + if (vw->height != ov->maxheight) return -EINVAL; - if (vw.width != ov511->maxwidth) + if (vw->width != ov->maxwidth) return -EINVAL; #endif /* If we're collecting previous frame wait before changing modes */ - interruptible_sleep_on(&ov511->wq); + interruptible_sleep_on(&ov->wq); if (signal_pending(current)) return -EINTR; - result = mode_init_regs(ov511, vw.width, vw.height, - ov511->frame[0].format, ov511->sub_flag); + result = mode_init_regs(ov, vw->width, vw->height, + ov->frame[0].format, ov->sub_flag); if (result < 0) return result; for (i = 0; i < OV511_NUMFRAMES; i++) { - ov511->frame[i].width = vw.width; - ov511->frame[i].height = vw.height; + ov->frame[i].width = vw->width; + ov->frame[i].height = vw->height; } return 0; } case VIDIOCGWIN: { - struct video_window vw; + struct video_window *vw = arg; - memset(&vw, 0, sizeof(vw)); - vw.x = 0; /* FIXME */ - vw.y = 0; - vw.width = ov511->frame[0].width; - vw.height = ov511->frame[0].height; - vw.flags = 30; + memset(vw, 0, sizeof(struct video_window)); + vw->x = 0; /* FIXME */ + vw->y = 0; + vw->width = ov->frame[0].width; + vw->height = ov->frame[0].height; + vw->flags = 30; - PDEBUG(4, "VIDIOCGWIN: %dx%d", vw.width, vw.height); - - if (copy_to_user(arg, &vw, sizeof(vw))) - return -EFAULT; + PDEBUG(4, "VIDIOCGWIN: %dx%d", vw->width, vw->height); return 0; } case VIDIOCGMBUF: { - struct video_mbuf vm; + struct video_mbuf *vm = arg; int i; PDEBUG(4, "VIDIOCGMBUF"); - memset(&vm, 0, sizeof(vm)); - vm.size = OV511_NUMFRAMES - * MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight); - vm.frames = OV511_NUMFRAMES; + memset(vm, 0, sizeof(struct video_mbuf)); + vm->size = OV511_NUMFRAMES + * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight); + vm->frames = OV511_NUMFRAMES; - vm.offsets[0] = 0; + vm->offsets[0] = 0; for (i = 1; i < OV511_NUMFRAMES; i++) { - vm.offsets[i] = vm.offsets[i-1] - + MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight); + vm->offsets[i] = vm->offsets[i-1] + + MAX_DATA_SIZE(ov->maxwidth, ov->maxheight); } - if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) - return -EFAULT; - return 0; } case VIDIOCMCAPTURE: { - struct video_mmap vm; + struct video_mmap *vm = arg; int ret, depth; + unsigned int f = vm->frame; - if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) - return -EFAULT; + PDEBUG(4, "VIDIOCMCAPTURE: frame: %d, %dx%d, %s", f, vm->width, + vm->height, symbolic(v4l1_plist, vm->format)); - PDEBUG(4, "CMCAPTURE"); - PDEBUG(4, "frame: %d, size: %dx%d, format: %d", - vm.frame, vm.width, vm.height, vm.format); - - depth = ov511_get_depth(vm.format); + depth = get_depth(vm->format); if (!depth) { - err("VIDIOCMCAPTURE: invalid format (%d)", vm.format); + err("VIDIOCMCAPTURE: invalid format (%s)", + symbolic(v4l1_plist, vm->format)); return -EINVAL; } - if ((unsigned)vm.frame >= OV511_NUMFRAMES) { - err("VIDIOCMCAPTURE: invalid frame (%d)", vm.frame); + if (f >= OV511_NUMFRAMES) { + err("VIDIOCMCAPTURE: invalid frame (%d)", f); return -EINVAL; } - if (vm.width > ov511->maxwidth - || vm.height > ov511->maxheight) { + if (vm->width > ov->maxwidth + || vm->height > ov->maxheight) { err("VIDIOCMCAPTURE: requested dimensions too big"); return -EINVAL; } - if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING) { + if (ov->frame[f].grabstate == FRAME_GRABBING) { PDEBUG(4, "VIDIOCMCAPTURE: already grabbing"); return -EBUSY; } - if (force_palette && vm.format != force_palette) { - info("palette rejected (%d)", vm.format); + if (force_palette && (vm->format != force_palette)) { + info("palette rejected (%s)", + symbolic(v4l1_plist, vm->format)); return -EINVAL; } - if ((ov511->frame[vm.frame].width != vm.width) || - (ov511->frame[vm.frame].height != vm.height) || - (ov511->frame[vm.frame].format != vm.format) || - (ov511->frame[vm.frame].sub_flag != ov511->sub_flag) || - (ov511->frame[vm.frame].depth != depth)) { + if ((ov->frame[f].width != vm->width) || + (ov->frame[f].height != vm->height) || + (ov->frame[f].format != vm->format) || + (ov->frame[f].sub_flag != ov->sub_flag) || + (ov->frame[f].depth != depth)) { PDEBUG(4, "VIDIOCMCAPTURE: change in image parameters"); /* If we're collecting previous frame wait before changing modes */ - interruptible_sleep_on(&ov511->wq); + interruptible_sleep_on(&ov->wq); if (signal_pending(current)) return -EINTR; - ret = mode_init_regs(ov511, vm.width, vm.height, - vm.format, ov511->sub_flag); + ret = mode_init_regs(ov, vm->width, vm->height, + vm->format, ov->sub_flag); #if 0 if (ret < 0) { PDEBUG(1, "Got error while initializing regs "); return ret; } #endif - ov511->frame[vm.frame].width = vm.width; - ov511->frame[vm.frame].height = vm.height; - ov511->frame[vm.frame].format = vm.format; - ov511->frame[vm.frame].sub_flag = ov511->sub_flag; - ov511->frame[vm.frame].depth = depth; + ov->frame[f].width = vm->width; + ov->frame[f].height = vm->height; + ov->frame[f].format = vm->format; + ov->frame[f].sub_flag = ov->sub_flag; + ov->frame[f].depth = depth; } /* Mark it as ready */ - ov511->frame[vm.frame].grabstate = FRAME_READY; + ov->frame[f].grabstate = FRAME_READY; - PDEBUG(4, "VIDIOCMCAPTURE: renewing frame %d", vm.frame); + PDEBUG(4, "VIDIOCMCAPTURE: renewing frame %d", f); - return ov511_new_frame(ov511, vm.frame); + return ov51x_new_frame(ov, f); } case VIDIOCSYNC: { - int fnum, rc; + unsigned int fnum = *((unsigned int *) arg); struct ov511_frame *frame; + int rc; - if (copy_from_user((void *)&fnum, arg, sizeof(int))) - return -EFAULT; - if ((unsigned)fnum >= OV511_NUMFRAMES) { + if (fnum >= OV511_NUMFRAMES) { err("VIDIOCSYNC: invalid frame (%d)", fnum); return -EINVAL; } - frame = &ov511->frame[fnum]; + frame = &ov->frame[fnum]; PDEBUG(4, "syncing to frame %d, grabstate = %d", fnum, frame->grabstate); @@ -5165,7 +4927,7 @@ case FRAME_GRABBING: case FRAME_ERROR: redo: - if (!ov511->dev) + if (!ov->dev) return -EIO; rc = wait_event_interruptible(frame->wq, @@ -5178,15 +4940,15 @@ if (frame->grabstate == FRAME_ERROR) { int ret; - if ((ret = ov511_new_frame(ov511, fnum)) < 0) + if ((ret = ov51x_new_frame(ov, fnum)) < 0) return ret; goto redo; } /* Fall through */ case FRAME_DONE: - if (ov511->snap_enabled && !frame->snapshot) { + if (ov->snap_enabled && !frame->snapshot) { int ret; - if ((ret = ov511_new_frame(ov511, fnum)) < 0) + if ((ret = ov51x_new_frame(ov, fnum)) < 0) return ret; goto redo; } @@ -5195,13 +4957,13 @@ /* Reset the hardware snapshot button */ /* FIXME - Is this the best place for this? */ - if ((ov511->snap_enabled) && (frame->snapshot)) { + if ((ov->snap_enabled) && (frame->snapshot)) { frame->snapshot = 0; - ov51x_clear_snapshot(ov511); + ov51x_clear_snapshot(ov); } /* Decompression, format conversion, etc... */ - ov511_postprocess(ov511, frame); + ov51x_postprocess(ov, frame); break; } /* end switch */ @@ -5210,126 +4972,107 @@ } case VIDIOCGFBUF: { - struct video_buffer vb; + struct video_buffer *vb = arg; - PDEBUG(4, "VIDIOCSCHAN"); - - memset(&vb, 0, sizeof(vb)); - vb.base = NULL; /* frame buffer not supported, not used */ + PDEBUG(4, "VIDIOCGFBUF"); - if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) - return -EFAULT; + memset(vb, 0, sizeof(struct video_buffer)); return 0; } case VIDIOCGUNIT: { - struct video_unit vu; + struct video_unit *vu = arg; PDEBUG(4, "VIDIOCGUNIT"); - memset(&vu, 0, sizeof(vu)); + memset(vu, 0, sizeof(struct video_unit)); - vu.video = ov511->vdev.minor; /* Video minor */ - vu.vbi = VIDEO_NO_UNIT; /* VBI minor */ - vu.radio = VIDEO_NO_UNIT; /* Radio minor */ - vu.audio = VIDEO_NO_UNIT; /* Audio minor */ - vu.teletext = VIDEO_NO_UNIT; /* Teletext minor */ - - if (copy_to_user((void *)arg, (void *)&vu, sizeof(vu))) - return -EFAULT; + vu->video = ov->vdev.minor; + vu->vbi = VIDEO_NO_UNIT; + vu->radio = VIDEO_NO_UNIT; + vu->audio = VIDEO_NO_UNIT; + vu->teletext = VIDEO_NO_UNIT; return 0; } case VIDIOCGTUNER: { - struct video_tuner v; + struct video_tuner *v = arg; PDEBUG(4, "VIDIOCGTUNER"); - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - - if (!ov511->has_tuner || v.tuner) // Only tuner 0 + if (!ov->has_tuner || v->tuner) // Only tuner 0 return -EINVAL; - strcpy(v.name, "Television"); + strcpy(v->name, "Television"); // FIXME: Need a way to get the real values - v.rangelow = 0; - v.rangehigh = ~0; + v->rangelow = 0; + v->rangehigh = ~0; - v.flags = VIDEO_TUNER_PAL | VIDEO_TUNER_NTSC | - VIDEO_TUNER_SECAM; - v.mode = 0; /* FIXME: Not sure what this is yet */ - v.signal = 0xFFFF; /* unknown */ + v->flags = VIDEO_TUNER_PAL | VIDEO_TUNER_NTSC + | VIDEO_TUNER_SECAM; + v->mode = 0; /* FIXME: Not sure what this is yet */ + v->signal = 0xFFFF; /* unknown */ - call_i2c_clients(ov511, cmd, &v); - - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; + call_i2c_clients(ov, cmd, v); return 0; } case VIDIOCSTUNER: { - struct video_tuner v; + struct video_tuner *v = arg; int err; PDEBUG(4, "VIDIOCSTUNER"); - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - /* Only no or one tuner for now */ - if (!ov511->has_tuner || v.tuner) + if (!ov->has_tuner || v->tuner) return -EINVAL; /* and it only has certain valid modes */ - if (v.mode != VIDEO_MODE_PAL && - v.mode != VIDEO_MODE_NTSC && - v.mode != VIDEO_MODE_SECAM) return -EOPNOTSUPP; + if (v->mode != VIDEO_MODE_PAL && + v->mode != VIDEO_MODE_NTSC && + v->mode != VIDEO_MODE_SECAM) + return -EOPNOTSUPP; /* Is this right/necessary? */ - err = decoder_set_norm(ov511, v.mode); + err = decoder_set_norm(ov, v->mode); if (err) return err; - call_i2c_clients(ov511, cmd, &v); + call_i2c_clients(ov, cmd, v); return 0; } case VIDIOCGFREQ: { - unsigned long v = ov511->freq; + unsigned long v = *((unsigned long *) arg); PDEBUG(4, "VIDIOCGFREQ"); - if (!ov511->has_tuner) + if (!ov->has_tuner) return -EINVAL; + + v = ov->freq; #if 0 /* FIXME: this is necessary for testing */ v = 46*16; #endif - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; } case VIDIOCSFREQ: { - unsigned long v; - - if (!ov511->has_tuner) - return -EINVAL; - - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; + unsigned long v = *((unsigned long *) arg); PDEBUG(4, "VIDIOCSFREQ: %lx", v); - ov511->freq = v; - call_i2c_clients(ov511, cmd, &v); + if (!ov->has_tuner) + return -EINVAL; + + ov->freq = v; + call_i2c_clients(ov, cmd, &v); return 0; } @@ -5347,30 +5090,115 @@ return 0; } +#ifdef OV511_OLD_V4L +/* This is implemented as video_generic_ioctl() in the new V4L's videodev.c */ +int +ov51x_v4l1_generic_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) +{ + char sbuf[128]; + void *mbuf = NULL; + void *parg = NULL; + int err = -EINVAL; + + /* Copy arguments into temp kernel buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: + parg = arg; + break; + case _IOC_READ: /* some v4l ioctls are marked wrong ... */ + case _IOC_WRITE: + case (_IOC_WRITE | _IOC_READ): + if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { + parg = sbuf; + } else { + /* too big to allocate from stack */ + mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); + if (NULL == mbuf) + return -ENOMEM; + parg = mbuf; + } + + err = -EFAULT; + if (copy_from_user(parg, arg, _IOC_SIZE(cmd))) + goto out; + break; + } + + err = ov51x_v4l1_ioctl_internal(vdev->priv, cmd, parg); + if (err == -ENOIOCTLCMD) + err = -EINVAL; + if (err < 0) + goto out; + + /* Copy results into user buffer */ + switch (_IOC_DIR(cmd)) + { + case _IOC_READ: + case (_IOC_WRITE | _IOC_READ): + if (copy_to_user(arg, parg, _IOC_SIZE(cmd))) + err = -EFAULT; + break; + } + +out: + if (mbuf) + kfree(mbuf); + return err; +} + +static int +ov51x_v4l1_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) +{ + struct usb_ov511 *ov = vdev->priv; + int rc; + + if (down_interruptible(&ov->lock)) + return -EINTR; + + rc = ov51x_v4l1_generic_ioctl(vdev, cmd, arg); + + up(&ov->lock); + return rc; +} + +#else /* If new V4L API */ + static int -ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) +ov51x_v4l1_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) { + struct video_device *vdev = video_devdata(file); + struct usb_ov511 *ov = vdev->priv; int rc; - struct usb_ov511 *ov511 = vdev->priv; - if (down_interruptible(&ov511->lock)) + if (down_interruptible(&ov->lock)) return -EINTR; - rc = ov511_ioctl_internal(vdev, cmd, arg); + rc = ov51x_v4l1_ioctl_internal(ov, cmd, arg); - up(&ov511->lock); + up(&ov->lock); return rc; } +#endif /* OV511_OLD_V4L */ +#ifdef OV511_OLD_V4L static inline long -ov511_read(struct video_device *vdev, char *buf, unsigned long count, - int noblock) +ov51x_v4l1_read(struct video_device *vdev, char *buf, unsigned long count, + int noblock) { - struct usb_ov511 *ov511 = vdev->priv; +#else +static inline int +ov51x_v4l1_read(struct file *file, char *buf, size_t cnt, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + int noblock = file->f_flags&O_NONBLOCK; + unsigned long count = cnt; +#endif + struct usb_ov511 *ov = vdev->priv; int i, rc = 0, frmx = -1; struct ov511_frame *frame; - if (down_interruptible(&ov511->lock)) + if (down_interruptible(&ov->lock)) return -EINTR; PDEBUG(4, "%ld bytes, noblock=%d", count, noblock); @@ -5380,16 +5208,16 @@ goto error; } - if (!ov511->dev) { + if (!ov->dev) { rc = -EIO; goto error; } // FIXME: Only supports two frames /* See if a frame is completed, then use it. */ - if (ov511->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */ + if (ov->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */ frmx = 0; - else if (ov511->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */ + else if (ov->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */ frmx = 1; /* If nonblocking we return immediately */ @@ -5401,24 +5229,24 @@ /* 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 (ov511->frame[0].grabstate == FRAME_GRABBING) + if (ov->frame[0].grabstate == FRAME_GRABBING) frmx = 0; - else if (ov511->frame[1].grabstate == FRAME_GRABBING) + else if (ov->frame[1].grabstate == FRAME_GRABBING) frmx = 1; } /* If no frame is active, start one. */ if (frmx == -1) { - if ((rc = ov511_new_frame(ov511, frmx = 0))) { - err("read: ov511_new_frame error"); + if ((rc = ov51x_new_frame(ov, frmx = 0))) { + err("read: ov51x_new_frame error"); goto error; } } - frame = &ov511->frame[frmx]; + frame = &ov->frame[frmx]; restart: - if (!ov511->dev) { + if (!ov->dev) { rc = -EIO; goto error; } @@ -5437,9 +5265,9 @@ if (frame->grabstate == FRAME_ERROR) { frame->bytes_read = 0; - err("** ick! ** Errored frame %d", ov511->curframe); - if (ov511_new_frame(ov511, frmx)) { - err("read: ov511_new_frame error"); + err("** ick! ** Errored frame %d", ov->curframe); + if (ov51x_new_frame(ov, frmx)) { + err("read: ov51x_new_frame error"); goto error; } goto restart; @@ -5447,25 +5275,25 @@ /* Repeat until we get a snapshot frame */ - if (ov511->snap_enabled) + if (ov->snap_enabled) PDEBUG(4, "Waiting snapshot frame"); - if (ov511->snap_enabled && !frame->snapshot) { + if (ov->snap_enabled && !frame->snapshot) { frame->bytes_read = 0; - if ((rc = ov511_new_frame(ov511, frmx))) { - err("read: ov511_new_frame error"); + if ((rc = ov51x_new_frame(ov, frmx))) { + err("read: ov51x_new_frame error"); goto error; } goto restart; } /* Clear the snapshot */ - if (ov511->snap_enabled && frame->snapshot) { + if (ov->snap_enabled && frame->snapshot) { frame->snapshot = 0; - ov51x_clear_snapshot(ov511); + ov51x_clear_snapshot(ov); } /* Decompression, format conversion, etc... */ - ov511_postprocess(ov511, frame); + ov51x_postprocess(ov, frame); PDEBUG(4, "frmx=%d, bytes_read=%ld, length=%ld", frmx, frame->bytes_read, @@ -5497,48 +5325,61 @@ // FIXME: Only supports two frames /* Mark it as available to be used again. */ - ov511->frame[frmx].grabstate = FRAME_UNUSED; - if ((rc = ov511_new_frame(ov511, !frmx))) { - err("ov511_new_frame returned error"); + ov->frame[frmx].grabstate = FRAME_UNUSED; + if ((rc = ov51x_new_frame(ov, !frmx))) { + err("ov51x_new_frame returned error"); goto error; } } PDEBUG(4, "read finished, returning %ld (sweet)", count); - up(&ov511->lock); + up(&ov->lock); return count; error: - up(&ov511->lock); + up(&ov->lock); return rc; } -static int -ov511_mmap(struct vm_area_struct *vma, struct video_device *vdev, const char *adr, unsigned long size) +static int +#ifdef OV511_OLD_V4L +ov51x_v4l1_mmap(struct vm_area_struct *vma, struct video_device *vdev, + const char *adr, unsigned long size) { - struct usb_ov511 *ov511 = vdev->priv; unsigned long start = (unsigned long)adr; + +#else /* New V4L API */ + +ov51x_v4l1_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = video_devdata(file); + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; +#endif /* OV511_OLD_V4L */ + + struct usb_ov511 *ov = vdev->priv; unsigned long page, pos; - if (ov511->dev == NULL) + if (ov->dev == NULL) return -EIO; PDEBUG(4, "mmap: %ld (%lX) bytes", size, size); if (size > (((OV511_NUMFRAMES - * MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight) + * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))) return -EINVAL; - if (down_interruptible(&ov511->lock)) + if (down_interruptible(&ov->lock)) return -EINTR; - pos = (unsigned long)ov511->fbuf; + pos = (unsigned long)ov->fbuf; while (size > 0) { page = kvirt_to_pa(pos); - if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - up(&ov511->lock); + if (remap_page_range(vma, start, page, PAGE_SIZE, + PAGE_SHARED)) { + up(&ov->lock); return -EAGAIN; } start += PAGE_SIZE; @@ -5549,31 +5390,52 @@ size = 0; } - up(&ov511->lock); + up(&ov->lock); return 0; } -static struct video_device ov511_template = { +#ifdef OV511_OLD_V4L +static struct video_device vdev_template = { owner: THIS_MODULE, name: "OV511 USB Camera", type: VID_TYPE_CAPTURE, hardware: VID_HARDWARE_OV511, - open: ov511_open, - close: ov511_close, - read: ov511_read, - write: ov511_write, - ioctl: ov511_ioctl, - mmap: ov511_mmap, - initialize: ov511_init_done, + open: ov51x_v4l1_open, + close: ov51x_v4l1_close, + read: ov51x_v4l1_read, + ioctl: ov51x_v4l1_ioctl, + mmap: ov51x_v4l1_mmap, +}; + +#else /* New V4L API */ + +static struct file_operations ov511_fops = { + owner: THIS_MODULE, + open: ov51x_v4l1_open, + release: ov51x_v4l1_close, + read: ov51x_v4l1_read, + mmap: ov51x_v4l1_mmap, + ioctl: video_generic_ioctl, + llseek: no_llseek, }; +static struct video_device vdev_template = { + owner: THIS_MODULE, + name: "OV511 USB Camera", + type: VID_TYPE_CAPTURE, + hardware: VID_HARDWARE_OV511, + fops: &ov511_fops, + kernel_ioctl: ov51x_v4l1_ioctl, +}; +#endif /* OV511_OLD_V4L */ + #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) static int -ov511_control_ioctl(struct inode *inode, struct file *file, unsigned int cmd, +ov51x_control_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long ularg) { struct proc_dir_entry *pde; - struct usb_ov511 *ov511; + struct usb_ov511 *ov; void *arg = (void *) ularg; int rc; @@ -5581,11 +5443,11 @@ if (!pde) return -ENOENT; - ov511 = (struct usb_ov511 *) pde->data; - if (!ov511) + ov = pde->data; + if (!ov) return -ENODEV; - if (!ov511->dev) + if (!ov->dev) return -EIO; /* Should we pass through standard V4L IOCTLs? */ @@ -5610,19 +5472,19 @@ switch (opt.optnum) { case OV511_USOPT_BRIGHT: - rc = sensor_get_brightness(ov511, &(opt.val)); + rc = sensor_get_brightness(ov, &(opt.val)); if (rc) return rc; break; case OV511_USOPT_SAT: - rc = sensor_get_saturation(ov511, &(opt.val)); + rc = sensor_get_saturation(ov, &(opt.val)); if (rc) return rc; break; case OV511_USOPT_HUE: - rc = sensor_get_hue(ov511, &(opt.val)); + rc = sensor_get_hue(ov, &(opt.val)); if (rc) return rc; break; case OV511_USOPT_CONTRAST: - rc = sensor_get_contrast(ov511, &(opt.val)); + rc = sensor_get_contrast(ov, &(opt.val)); if (rc) return rc; break; default: @@ -5644,19 +5506,19 @@ switch (opt.optnum) { case OV511_USOPT_BRIGHT: - rc = sensor_set_brightness(ov511, opt.val); + rc = sensor_set_brightness(ov, opt.val); if (rc) return rc; break; case OV511_USOPT_SAT: - rc = sensor_set_saturation(ov511, opt.val); + rc = sensor_set_saturation(ov, opt.val); if (rc) return rc; break; case OV511_USOPT_HUE: - rc = sensor_set_hue(ov511, opt.val); + rc = sensor_set_hue(ov, opt.val); if (rc) return rc; break; case OV511_USOPT_CONTRAST: - rc = sensor_set_contrast(ov511, opt.val); + rc = sensor_set_contrast(ov, opt.val); if (rc) return rc; break; default: @@ -5675,19 +5537,19 @@ switch (opt.optnum) { case OV511_UIOPT_POWER_FREQ: - opt.val = ov511->lightfreq; + opt.val = ov->lightfreq; break; case OV511_UIOPT_BFILTER: - opt.val = ov511->bandfilt; + opt.val = ov->bandfilt; break; case OV511_UIOPT_LED: - opt.val = ov511->led_policy; + opt.val = ov->led_policy; break; case OV511_UIOPT_DEBUG: opt.val = debug; break; case OV511_UIOPT_COMPRESS: - opt.val = ov511->compress; + opt.val = ov->compress; break; default: err("Invalid get int option number"); @@ -5708,20 +5570,20 @@ switch (opt.optnum) { case OV511_UIOPT_POWER_FREQ: - rc = sensor_set_light_freq(ov511, opt.val); + rc = sensor_set_light_freq(ov, opt.val); if (rc) return rc; break; case OV511_UIOPT_BFILTER: - rc = sensor_set_banding_filter(ov511, opt.val); + rc = sensor_set_banding_filter(ov, opt.val); if (rc) return rc; break; case OV511_UIOPT_LED: if (opt.val <= 2) { - ov511->led_policy = opt.val; - if (ov511->led_policy == LED_OFF) - ov51x_led_control(ov511, 0); - else if (ov511->led_policy == LED_ON) - ov51x_led_control(ov511, 1); + ov->led_policy = opt.val; + if (ov->led_policy == LED_OFF) + ov51x_led_control(ov, 0); + else if (ov->led_policy == LED_ON) + ov51x_led_control(ov, 1); } else { return -EINVAL; } @@ -5733,14 +5595,12 @@ return -EINVAL; break; case OV511_UIOPT_COMPRESS: - ov511->compress = opt.val; - if (ov511->compress) { - if (ov511->bridge == BRG_OV511 || - ov511->bridge == BRG_OV511PLUS) - ov511_init_compression(ov511); - else if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) - ov518_init_compression(ov511); + ov->compress = opt.val; + if (ov->compress) { + if (ov->bclass == BCL_OV511) + ov511_init_compression(ov); + else if (ov->bclass == BCL_OV518) + ov518_init_compression(ov); } break; default: @@ -5757,7 +5617,7 @@ if (copy_from_user(&w, arg, sizeof(w))) return -EFAULT; - return ov51x_i2c_write_slave(ov511, w.slave, w.reg, w.value, + return i2c_w_slave(ov, w.slave, w.reg, w.value, w.mask); } case OV511IOC_RI2C: @@ -5767,7 +5627,7 @@ if (copy_from_user(&r, arg, sizeof(r))) return -EFAULT; - rc = ov51x_i2c_read_slave(ov511, r.slave, r.reg); + rc = i2c_r_slave(ov, r.slave, r.reg); if (rc < 0) return rc; @@ -5796,7 +5656,7 @@ * the same register settings as the OV7610, since they are very similar. */ static int -ov7xx0_configure(struct usb_ov511 *ov511) +ov7xx0_configure(struct usb_ov511 *ov) { int i, success; int rc; @@ -5913,16 +5773,15 @@ PDEBUG(4, "starting configuration"); /* This looks redundant, but is necessary for WebCam 3 */ - ov511->primary_i2c_slave = OV7xx0_I2C_WRITE_ID; - if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID, - OV7xx0_I2C_READ_ID) < 0) + ov->primary_i2c_slave = OV7xx0_SID; + if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0) return -1; - if (ov51x_init_ov_sensor(ov511) >= 0) { + if (init_ov_sensor(ov) >= 0) { PDEBUG(1, "OV7xx0 sensor initalized (method 1)"); } else { /* Reset the 76xx */ - if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) return -1; + if (i2c_w(ov, 0x12, 0x80) < 0) return -1; /* Wait for it to initialize */ schedule_timeout(1 + 150 * HZ / 1000); @@ -5930,10 +5789,8 @@ i = 0; success = 0; while (i <= i2c_detect_tries) { - if ((ov51x_i2c_read(ov511, - OV7610_REG_ID_HIGH) == 0x7F) && - (ov51x_i2c_read(ov511, - OV7610_REG_ID_LOW) == 0xA2)) { + if ((i2c_r(ov, OV7610_REG_ID_HIGH) == 0x7F) && + (i2c_r(ov, OV7610_REG_ID_LOW) == 0xA2)) { success = 1; break; } else { @@ -5957,17 +5814,17 @@ } /* Detect sensor (sub)type */ - rc = ov51x_i2c_read(ov511, OV7610_REG_COM_I); + rc = i2c_r(ov, OV7610_REG_COM_I); if (rc < 0) { err("Error detecting sensor type"); return -1; } else if ((rc & 3) == 3) { info("Sensor is an OV7610"); - ov511->sensor = SEN_OV7610; + ov->sensor = SEN_OV7610; } else if ((rc & 3) == 1) { /* I don't know what's different about the 76BE yet */ - if (ov51x_i2c_read(ov511, 0x15) & 1) + if (i2c_r(ov, 0x15) & 1) info("Sensor is an OV7620AE"); else info("Sensor is an OV76BE"); @@ -5975,48 +5832,48 @@ /* OV511+ will return all zero isoc data unless we * configure the sensor as a 7620. Someone needs to * find the exact reg. setting that causes this. */ - if (ov511->bridge == BRG_OV511PLUS) { + if (ov->bridge == BRG_OV511PLUS) { info("Enabling 511+/7620AE workaround"); - ov511->sensor = SEN_OV7620; + ov->sensor = SEN_OV7620; } else { - ov511->sensor = SEN_OV7620AE; + ov->sensor = SEN_OV7620AE; } } else if ((rc & 3) == 0) { info("Sensor is an OV7620"); - ov511->sensor = SEN_OV7620; + ov->sensor = SEN_OV7620; } else { err("Unknown image sensor version: %d", rc & 3); return -1; } - if (ov511->sensor == SEN_OV7620) { + if (ov->sensor == SEN_OV7620) { PDEBUG(4, "Writing 7620 registers"); - if (ov511_write_regvals(ov511, aRegvalsNorm7620)) + if (write_regvals(ov, aRegvalsNorm7620)) return -1; } else { PDEBUG(4, "Writing 7610 registers"); - if (ov511_write_regvals(ov511, aRegvalsNorm7610)) + if (write_regvals(ov, aRegvalsNorm7610)) return -1; } /* Set sensor-specific vars */ - ov511->maxwidth = 640; - ov511->maxheight = 480; - ov511->minwidth = 64; - ov511->minheight = 48; + ov->maxwidth = 640; + ov->maxheight = 480; + ov->minwidth = 64; + ov->minheight = 48; // FIXME: These do not match the actual settings yet - ov511->brightness = 0x80 << 8; - ov511->contrast = 0x80 << 8; - ov511->colour = 0x80 << 8; - ov511->hue = 0x80 << 8; + ov->brightness = 0x80 << 8; + ov->contrast = 0x80 << 8; + ov->colour = 0x80 << 8; + ov->hue = 0x80 << 8; return 0; } /* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */ static int -ov6xx0_configure(struct usb_ov511 *ov511) +ov6xx0_configure(struct usb_ov511 *ov) { int rc; @@ -6142,7 +5999,7 @@ PDEBUG(4, "starting sensor configuration"); - if (ov51x_init_ov_sensor(ov511) < 0) { + if (init_ov_sensor(ov) < 0) { err("Failed to read sensor ID. You might not have an OV6xx0,"); err("or it may be not responding. Report this to " EMAIL); return -1; @@ -6151,50 +6008,43 @@ } /* Detect sensor (sub)type */ - rc = ov51x_i2c_read(ov511, OV7610_REG_COM_I); + rc = i2c_r(ov, OV7610_REG_COM_I); if (rc < 0) { err("Error detecting sensor type"); return -1; - } else if ((rc & 3) == 0) { - info("Sensor is an OV6630"); - ov511->sensor = SEN_OV6630; - } else if ((rc & 3) == 1) { - info("Sensor is an OV6620"); - ov511->sensor = SEN_OV6620; - } else if ((rc & 3) == 2) { - info("Sensor is an OV6630AE"); - ov511->sensor = SEN_OV6630; - } else if ((rc & 3) == 3) { - info("Sensor is an OV6630AF"); - ov511->sensor = SEN_OV6630; } + if ((rc & 3) == 0) + ov->sensor = SEN_OV6630; + else if ((rc & 3) == 1) + ov->sensor = SEN_OV6620; + else if ((rc & 3) == 2) + ov->sensor = SEN_OV6630; + else if ((rc & 3) == 3) + ov->sensor = SEN_OV6630; + + info("Sensor is an %s", symbolic(senlist, ov->sensor)); + /* Set sensor-specific vars */ - if (ov511->sensor == SEN_OV6620) { - ov511->maxwidth = 352; - ov511->maxheight = 288; - } else { - /* 352x288 not working with OV518 yet */ - ov511->maxwidth = 320; - ov511->maxheight = 240; - } - ov511->minwidth = 64; - ov511->minheight = 48; + ov->maxwidth = 352; + ov->maxheight = 288; + ov->minwidth = 64; + ov->minheight = 48; // FIXME: These do not match the actual settings yet - ov511->brightness = 0x80 << 8; - ov511->contrast = 0x80 << 8; - ov511->colour = 0x80 << 8; - ov511->hue = 0x80 << 8; + ov->brightness = 0x80 << 8; + ov->contrast = 0x80 << 8; + ov->colour = 0x80 << 8; + ov->hue = 0x80 << 8; - if (ov511->sensor == SEN_OV6620) { + if (ov->sensor == SEN_OV6620) { PDEBUG(4, "Writing 6x20 registers"); - if (ov511_write_regvals(ov511, aRegvalsNorm6x20)) + if (write_regvals(ov, aRegvalsNorm6x20)) return -1; } else { PDEBUG(4, "Writing 6x30 registers"); - if (ov511_write_regvals(ov511, aRegvalsNorm6x30)) + if (write_regvals(ov, aRegvalsNorm6x30)) return -1; } @@ -6203,13 +6053,13 @@ /* This initializes the KS0127 and KS0127B video decoders. */ static int -ks0127_configure(struct usb_ov511 *ov511) +ks0127_configure(struct usb_ov511 *ov) { int rc; // FIXME: I don't know how to sync or reset it yet #if 0 - if (ov51x_init_ks_sensor(ov511) < 0) { + if (ov51x_init_ks_sensor(ov) < 0) { err("Failed to initialize the KS0127"); return -1; } else { @@ -6218,21 +6068,21 @@ #endif /* Detect decoder subtype */ - rc = ov51x_i2c_read(ov511, 0x00); + rc = i2c_r(ov, 0x00); if (rc < 0) { err("Error detecting sensor type"); return -1; } else if (rc & 0x08) { - rc = ov51x_i2c_read(ov511, 0x3d); + rc = i2c_r(ov, 0x3d); if (rc < 0) { err("Error detecting sensor type"); return -1; } else if ((rc & 0x0f) == 0) { info("Sensor is a KS0127"); - ov511->sensor = SEN_KS0127; + ov->sensor = SEN_KS0127; } else if ((rc & 0x0f) == 9) { info("Sensor is a KS0127B Rev. A"); - ov511->sensor = SEN_KS0127B; + ov->sensor = SEN_KS0127B; } } else { err("Error: Sensor is an unsupported KS0122"); @@ -6240,16 +6090,16 @@ } /* Set sensor-specific vars */ - ov511->maxwidth = 640; - ov511->maxheight = 480; - ov511->minwidth = 64; - ov511->minheight = 48; + ov->maxwidth = 640; + ov->maxheight = 480; + ov->minwidth = 64; + ov->minheight = 48; // FIXME: These do not match the actual settings yet - ov511->brightness = 0x80 << 8; - ov511->contrast = 0x80 << 8; - ov511->colour = 0x80 << 8; - ov511->hue = 0x80 << 8; + ov->brightness = 0x80 << 8; + ov->contrast = 0x80 << 8; + ov->colour = 0x80 << 8; + ov->hue = 0x80 << 8; /* This device is not supported yet. Bail out now... */ err("This sensor is not supported yet."); @@ -6260,9 +6110,8 @@ /* This initializes the SAA7111A video decoder. */ static int -saa7111a_configure(struct usb_ov511 *ov511) +saa7111a_configure(struct usb_ov511 *ov) { - struct usb_device *dev = ov511->dev; int rc; /* Since there is no register reset command, all registers must be @@ -6297,7 +6146,7 @@ // FIXME: I don't know how to sync or reset it yet #if 0 - if (ov51x_init_saa_sensor(ov511) < 0) { + if (ov51x_init_saa_sensor(ov) < 0) { err("Failed to initialize the SAA7111A"); return -1; } else { @@ -6306,44 +6155,44 @@ #endif /* Set sensor-specific vars */ - ov511->maxwidth = 640; - ov511->maxheight = 480; /* Even/Odd fields */ - ov511->minwidth = 320; - ov511->minheight = 240; /* Even field only */ - - ov511->has_decoder = 1; - ov511->num_inputs = 8; - ov511->norm = VIDEO_MODE_AUTO; - ov511->stop_during_set = 0; /* Decoder guarantees stable image */ + ov->maxwidth = 640; + ov->maxheight = 480; /* Even/Odd fields */ + ov->minwidth = 320; + ov->minheight = 240; /* Even field only */ + + ov->has_decoder = 1; + ov->num_inputs = 8; + ov->norm = VIDEO_MODE_AUTO; + ov->stop_during_set = 0; /* Decoder guarantees stable image */ /* Decoder doesn't change these values, so we use these instead of * acutally reading the registers (which doesn't work) */ - ov511->brightness = 0x80 << 8; - ov511->contrast = 0x40 << 9; - ov511->colour = 0x40 << 9; - ov511->hue = 32768; + ov->brightness = 0x80 << 8; + ov->contrast = 0x40 << 9; + ov->colour = 0x40 << 9; + ov->hue = 32768; PDEBUG(4, "Writing SAA7111A registers"); - if (ov511_write_regvals(ov511, aRegvalsNormSAA7111A)) + if (write_regvals(ov, aRegvalsNormSAA7111A)) return -1; /* Detect version of decoder. This must be done after writing the * initial regs or the decoder will lock up. */ - rc = ov51x_i2c_read(ov511, 0x00); + rc = i2c_r(ov, 0x00); if (rc < 0) { err("Error detecting sensor version"); return -1; } else { info("Sensor is an SAA7111A (version 0x%x)", rc); - ov511->sensor = SEN_SAA7111A; + ov->sensor = SEN_SAA7111A; } // FIXME: Fix this for OV518(+) /* Latch to negative edge of clock. Otherwise, we get incorrect * colors and jitter in the digital signal. */ - if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) - ov511_reg_write(dev, 0x11, 0x00); + if (ov->bclass == BCL_OV511) + reg_w(ov, 0x11, 0x00); else warn("SAA7111A not yet supported with OV518/OV518+"); @@ -6352,144 +6201,130 @@ /* This initializes the OV511/OV511+ and the sensor */ static int -ov511_configure(struct usb_ov511 *ov511) +ov511_configure(struct usb_ov511 *ov) { - struct usb_device *dev = ov511->dev; - int i; - static struct ov511_regvals aRegvalsInit511[] = { - { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f }, - { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, - { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f }, - { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, - { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3f }, - { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, - { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3d }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x7f }, + { OV511_REG_BUS, R51x_SYS_INIT, 0x01 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x7f }, + { OV511_REG_BUS, R51x_SYS_INIT, 0x01 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x3f }, + { OV511_REG_BUS, R51x_SYS_INIT, 0x01 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x3d }, { OV511_DONE_BUS, 0x0, 0x00}, }; static struct ov511_regvals aRegvalsNorm511[] = { - { OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 }, - { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 }, - { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x03 }, - { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 }, - { OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f }, - { OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x00 }, - { OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 }, + { OV511_REG_BUS, R511_DRAM_FLOW_CTL, 0x01 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x03 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, + { OV511_REG_BUS, R511_FIFO_OPTS, 0x1f }, + { OV511_REG_BUS, R511_COMP_EN, 0x00 }, + { OV511_REG_BUS, R511_COMP_LUT_EN, 0x03 }, { OV511_DONE_BUS, 0x0, 0x00 }, }; static struct ov511_regvals aRegvalsNorm511Plus[] = { - { OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0xff }, - { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 }, - { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x03 }, - { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 }, - { OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0xff }, - { OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x00 }, - { OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 }, + { OV511_REG_BUS, R511_DRAM_FLOW_CTL, 0xff }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x03 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, + { OV511_REG_BUS, R511_FIFO_OPTS, 0xff }, + { OV511_REG_BUS, R511_COMP_EN, 0x00 }, + { OV511_REG_BUS, R511_COMP_LUT_EN, 0x03 }, { OV511_DONE_BUS, 0x0, 0x00 }, }; PDEBUG(4, ""); - ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID); - if (ov511->customid < 0) { + ov->customid = reg_r(ov, R511_SYS_CUST_ID); + if (ov->customid < 0) { err("Unable to read camera bridge registers"); goto error; } - ov511->desc = -1; - PDEBUG (1, "CustomID = %d", ov511->customid); - for (i = 0; clist[i].id >= 0; i++) { - if (ov511->customid == clist[i].id) { - info("model: %s", clist[i].description); - ov511->desc = i; - break; - } - } + PDEBUG (1, "CustomID = %d", ov->customid); + ov->desc = symbolic(camlist, ov->customid); + info("model: %s", ov->desc); - if (clist[i].id == -1) { - err("Camera type (%d) not recognized", ov511->customid); + if (0 == strcmp(ov->desc, NOT_DEFINED_STR)) { + err("Camera type (%d) not recognized", ov->customid); err("Please notify " EMAIL " of the name,"); err("manufacturer, model, and this number of your camera."); err("Also include the output of the detection process."); } - if (clist[i].id == 6) { /* USB Life TV (NTSC) */ - ov511->tuner_type = 8; /* Temic 4036FY5 3X 1981 */ + if (ov->customid == 6) { /* USB Life TV (NTSC) */ + ov->tuner_type = 8; /* Temic 4036FY5 3X 1981 */ } - if (ov511_write_regvals(ov511, aRegvalsInit511)) goto error; + if (write_regvals(ov, aRegvalsInit511)) goto error; - if (ov511->led_policy == LED_OFF || ov511->led_policy == LED_AUTO) - ov51x_led_control(ov511, 0); + if (ov->led_policy == LED_OFF || ov->led_policy == LED_AUTO) + ov51x_led_control(ov, 0); /* The OV511+ has undocumented bits in the flow control register. * Setting it to 0xff fixes the corruption with moving objects. */ - if (ov511->bridge == BRG_OV511) { - if (ov511_write_regvals(ov511, aRegvalsNorm511)) goto error; - } else if (ov511->bridge == BRG_OV511PLUS) { - if (ov511_write_regvals(ov511, aRegvalsNorm511Plus)) goto error; + if (ov->bridge == BRG_OV511) { + if (write_regvals(ov, aRegvalsNorm511)) goto error; + } else if (ov->bridge == BRG_OV511PLUS) { + if (write_regvals(ov, aRegvalsNorm511Plus)) goto error; } else { err("Invalid bridge"); } - if (ov511_init_compression(ov511)) goto error; + if (ov511_init_compression(ov)) goto error; - ov511_set_packet_size(ov511, 0); + ov51x_set_packet_size(ov, 0); - ov511->snap_enabled = snapshot; + ov->snap_enabled = snapshot; /* Test for 7xx0 */ PDEBUG(3, "Testing for 0V7xx0"); - ov511->primary_i2c_slave = OV7xx0_I2C_WRITE_ID; - if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID, - OV7xx0_I2C_READ_ID) < 0) + ov->primary_i2c_slave = OV7xx0_SID; + if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0) goto error; - if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) { + if (i2c_w(ov, 0x12, 0x80) < 0) { /* Test for 6xx0 */ PDEBUG(3, "Testing for 0V6xx0"); - ov511->primary_i2c_slave = OV6xx0_I2C_WRITE_ID; - if (ov51x_set_slave_ids(ov511, OV6xx0_I2C_WRITE_ID, - OV6xx0_I2C_READ_ID) < 0) + ov->primary_i2c_slave = OV6xx0_SID; + if (ov51x_set_slave_ids(ov, OV6xx0_SID) < 0) goto error; - if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) { + if (i2c_w(ov, 0x12, 0x80) < 0) { /* Test for 8xx0 */ PDEBUG(3, "Testing for 0V8xx0"); - ov511->primary_i2c_slave = OV8xx0_I2C_WRITE_ID; - if (ov51x_set_slave_ids(ov511, OV8xx0_I2C_WRITE_ID, - OV8xx0_I2C_READ_ID)) + ov->primary_i2c_slave = OV8xx0_SID; + if (ov51x_set_slave_ids(ov, OV8xx0_SID)) goto error; - if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) { + if (i2c_w(ov, 0x12, 0x80) < 0) { /* Test for SAA7111A */ PDEBUG(3, "Testing for SAA7111A"); - ov511->primary_i2c_slave = SAA7111A_I2C_WRITE_ID; - if (ov51x_set_slave_ids(ov511, SAA7111A_I2C_WRITE_ID, - SAA7111A_I2C_READ_ID)) + ov->primary_i2c_slave = SAA7111A_SID; + if (ov51x_set_slave_ids(ov, SAA7111A_SID)) goto error; - if (ov51x_i2c_write(ov511, 0x0d, 0x00) < 0) { + if (i2c_w(ov, 0x0d, 0x00) < 0) { /* Test for KS0127 */ PDEBUG(3, "Testing for KS0127"); - ov511->primary_i2c_slave = KS0127_I2C_WRITE_ID; - if (ov51x_set_slave_ids(ov511, KS0127_I2C_WRITE_ID, - KS0127_I2C_READ_ID)) + ov->primary_i2c_slave = KS0127_SID; + if (ov51x_set_slave_ids(ov, KS0127_SID)) goto error; - if (ov51x_i2c_write(ov511, 0x10, 0x00) < 0) { + if (i2c_w(ov, 0x10, 0x00) < 0) { err("Can't determine sensor slave IDs"); goto error; } else { - if(ks0127_configure(ov511) < 0) { + if (ks0127_configure(ov) < 0) { err("Failed to configure KS0127"); goto error; } } } else { - if(saa7111a_configure(ov511) < 0) { + if (saa7111a_configure(ov) < 0) { err("Failed to configure SAA7111A"); goto error; } @@ -6499,13 +6334,13 @@ goto error; } } else { - if(ov6xx0_configure(ov511) < 0) { + if (ov6xx0_configure(ov) < 0) { err("Failed to configure OV6xx0"); goto error; } } } else { - if(ov7xx0_configure(ov511) < 0) { + if (ov7xx0_configure(ov) < 0) { err("Failed to configure OV7xx0"); goto error; } @@ -6521,96 +6356,90 @@ /* This initializes the OV518/OV518+ and the sensor */ static int -ov518_configure(struct usb_ov511 *ov511) +ov518_configure(struct usb_ov511 *ov) { - struct usb_device *dev = ov511->dev; - static struct ov511_regvals aRegvalsInit518[] = { - { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x40 }, - { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 }, - { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3e }, - { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 }, - { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x00 }, - { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 }, - { OV511_REG_BUS, 0x46, 0x00 }, - { OV511_REG_BUS, 0x5d, 0x03 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x40 }, + { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x3e }, + { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x00 }, + { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 }, + { OV511_REG_BUS, 0x46, 0x00 }, + { OV511_REG_BUS, 0x5d, 0x03 }, { OV511_DONE_BUS, 0x0, 0x00}, }; /* New values, based on Windows driver. Since what they do is not * known yet, this may be incorrect. */ static struct ov511_regvals aRegvalsNorm518[] = { - { OV511_REG_BUS, 0x52, 0x02 }, /* Reset snapshot */ - { OV511_REG_BUS, 0x52, 0x01 }, /* Enable snapshot */ - { OV511_REG_BUS, 0x31, 0x0f }, - { OV511_REG_BUS, 0x5d, 0x03 }, - { OV511_REG_BUS, 0x24, 0x9f }, - { OV511_REG_BUS, 0x25, 0x90 }, - { OV511_REG_BUS, 0x20, 0x00 }, /* Was 0x08 */ - { OV511_REG_BUS, 0x51, 0x04 }, - { OV511_REG_BUS, 0x71, 0x19 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, /* Reset */ + { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, /* Enable */ + { OV511_REG_BUS, 0x31, 0x0f }, + { OV511_REG_BUS, 0x5d, 0x03 }, + { OV511_REG_BUS, 0x24, 0x9f }, + { OV511_REG_BUS, 0x25, 0x90 }, + { OV511_REG_BUS, 0x20, 0x00 }, /* Was 0x08 */ + { OV511_REG_BUS, 0x51, 0x04 }, + { OV511_REG_BUS, 0x71, 0x19 }, { OV511_DONE_BUS, 0x0, 0x00 }, }; PDEBUG(4, ""); /* First 5 bits of custom ID reg are a revision ID on OV518 */ - info("Device revision %d", - 0x1F & ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID)); + info("Device revision %d", 0x1F & reg_r(ov, R511_SYS_CUST_ID)); - if (ov511_write_regvals(ov511, aRegvalsInit518)) goto error; + if (write_regvals(ov, aRegvalsInit518)) goto error; /* Set LED GPIO pin to output mode */ - if (ov511_reg_write_mask(dev, 0x57,0x00, 0x02) < 0) goto error; + if (reg_w_mask(ov, 0x57, 0x00, 0x02) < 0) goto error; /* LED is off by default with OV518; have to explicitly turn it on */ - if (ov511->led_policy == LED_OFF || ov511->led_policy == LED_AUTO) - ov51x_led_control(ov511, 0); + if (ov->led_policy == LED_OFF || ov->led_policy == LED_AUTO) + ov51x_led_control(ov, 0); else - ov51x_led_control(ov511, 1); + ov51x_led_control(ov, 1); /* Don't require compression if dumppix is enabled; otherwise it's * required. OV518 has no uncompressed mode, to save RAM. */ - if (!dumppix && !ov511->compress) { - ov511->compress = 1; + if (!dumppix && !ov->compress) { + ov->compress = 1; warn("Compression required with OV518...enabling"); } - if (ov511_write_regvals(ov511, aRegvalsNorm518)) goto error; + if (write_regvals(ov, aRegvalsNorm518)) goto error; - if (ov511_reg_write(dev, 0x2f,0x80) < 0) goto error; + if (reg_w(ov, 0x2f, 0x80) < 0) goto error; - if (ov518_init_compression(ov511)) goto error; + if (ov518_init_compression(ov)) goto error; - ov511_set_packet_size(ov511, 0); + ov51x_set_packet_size(ov, 0); - ov511->snap_enabled = snapshot; + ov->snap_enabled = snapshot; /* Test for 76xx */ - ov511->primary_i2c_slave = OV7xx0_I2C_WRITE_ID; - if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID, - OV7xx0_I2C_READ_ID) < 0) + ov->primary_i2c_slave = OV7xx0_SID; + if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0) goto error; /* The OV518 must be more aggressive about sensor detection since * I2C write will never fail if the sensor is not present. We have * to try to initialize the sensor to detect its presence */ - if (ov51x_init_ov_sensor(ov511) < 0) { + if (init_ov_sensor(ov) < 0) { /* Test for 6xx0 */ - ov511->primary_i2c_slave = OV6xx0_I2C_WRITE_ID; - if (ov51x_set_slave_ids(ov511, OV6xx0_I2C_WRITE_ID, - OV6xx0_I2C_READ_ID) < 0) + ov->primary_i2c_slave = OV6xx0_SID; + if (ov51x_set_slave_ids(ov, OV6xx0_SID) < 0) goto error; - if (ov51x_init_ov_sensor(ov511) < 0) { + if (init_ov_sensor(ov) < 0) { /* Test for 8xx0 */ - ov511->primary_i2c_slave = OV8xx0_I2C_WRITE_ID; - if (ov51x_set_slave_ids(ov511, OV8xx0_I2C_WRITE_ID, - OV8xx0_I2C_READ_ID) < 0) + ov->primary_i2c_slave = OV8xx0_SID; + if (ov51x_set_slave_ids(ov, OV8xx0_SID) < 0) goto error; - if (ov51x_init_ov_sensor(ov511) < 0) { + if (init_ov_sensor(ov) < 0) { err("Can't determine sensor slave IDs"); goto error; } else { @@ -6618,21 +6447,25 @@ goto error; } } else { - if (ov6xx0_configure(ov511) < 0) { + if (ov6xx0_configure(ov) < 0) { err("Failed to configure OV6xx0"); goto error; } } } else { - if (ov7xx0_configure(ov511) < 0) { + if (ov7xx0_configure(ov) < 0) { err("Failed to configure OV7xx0"); goto error; } } + // FIXME: Sizes > 320x240 are not working yet + ov->maxwidth = 320; + ov->maxheight = 240; + // The OV518 cannot go as low as the sensor can - ov511->minwidth = 160; - ov511->minheight = 120; + ov->minwidth = 160; + ov->minheight = 120; return 0; @@ -6654,7 +6487,7 @@ const struct usb_device_id *id) { struct usb_interface_descriptor *interface; - struct usb_ov511 *ov511; + struct usb_ov511 *ov; int i; int registered = 0; @@ -6672,109 +6505,115 @@ if (interface->bInterfaceSubClass != 0x00) return NULL; - /* Since code below may sleep, we use this as a lock */ - MOD_INC_USE_COUNT; - - if ((ov511 = kmalloc(sizeof(*ov511), GFP_KERNEL)) == NULL) { - err("couldn't kmalloc ov511 struct"); - goto error_unlock; - } - - memset(ov511, 0, sizeof(*ov511)); - - ov511->dev = dev; - ov511->iface = interface->bInterfaceNumber; - ov511->led_policy = led; - ov511->compress = compress; - ov511->lightfreq = lightfreq; - ov511->num_inputs = 1; /* Video decoder init functs. change this */ - ov511->stop_during_set = !fastset; - ov511->tuner_type = tuner; - ov511->backlight = backlight; - - ov511->auto_brt = autobright; - ov511->auto_gain = autogain; - ov511->auto_exp = autoexp; + if ((ov = kmalloc(sizeof(*ov), GFP_KERNEL)) == NULL) { + err("couldn't kmalloc ov struct"); + goto error_out; + } + + memset(ov, 0, sizeof(*ov)); + + ov->dev = dev; + ov->iface = interface->bInterfaceNumber; + ov->led_policy = led; + ov->compress = compress; + ov->lightfreq = lightfreq; + ov->num_inputs = 1; /* Video decoder init functs. change this */ + ov->stop_during_set = !fastset; + ov->tuner_type = tuner; + ov->backlight = backlight; + + ov->auto_brt = autobright; + ov->auto_gain = autogain; + ov->auto_exp = autoexp; switch (dev->descriptor.idProduct) { case PROD_OV511: - info("USB OV511 camera found"); - ov511->bridge = BRG_OV511; - ov511->bclass = BCL_OV511; + ov->bridge = BRG_OV511; + ov->bclass = BCL_OV511; break; case PROD_OV511PLUS: - info("USB OV511+ camera found"); - ov511->bridge = BRG_OV511PLUS; - ov511->bclass = BCL_OV511; + ov->bridge = BRG_OV511PLUS; + ov->bclass = BCL_OV511; break; case PROD_OV518: - info("USB OV518 camera found"); - ov511->bridge = BRG_OV518; - ov511->bclass = BCL_OV518; + ov->bridge = BRG_OV518; + ov->bclass = BCL_OV518; break; case PROD_OV518PLUS: - info("USB OV518+ camera found"); - ov511->bridge = BRG_OV518PLUS; - ov511->bclass = BCL_OV518; + ov->bridge = BRG_OV518PLUS; + ov->bclass = BCL_OV518; break; case PROD_ME2CAM: if (dev->descriptor.idVendor != VEND_MATTEL) goto error; - info("Intel Play Me2Cam (OV511+) found"); - ov511->bridge = BRG_OV511PLUS; - ov511->bclass = BCL_OV511; + ov->bridge = BRG_OV511PLUS; + ov->bclass = BCL_OV511; break; default: err("Unknown product ID 0x%x", dev->descriptor.idProduct); goto error_dealloc; } + info("USB %s video device found", symbolic(brglist, ov->bridge)); + /* Workaround for some applications that want data in RGB * instead of BGR. */ if (force_rgb) info("data format set to RGB"); - init_waitqueue_head(&ov511->wq); + init_waitqueue_head(&ov->wq); - init_MUTEX(&ov511->lock); /* to 1 == available */ - init_MUTEX(&ov511->buf_lock); - init_MUTEX(&ov511->param_lock); - init_MUTEX(&ov511->i2c_lock); - ov511->buf_state = BUF_NOT_ALLOCATED; - - if (ov511->bridge == BRG_OV518 || - ov511->bridge == BRG_OV518PLUS) { - if (ov518_configure(ov511) < 0) + init_MUTEX(&ov->lock); /* to 1 == available */ + init_MUTEX(&ov->buf_lock); + init_MUTEX(&ov->param_lock); + init_MUTEX(&ov->i2c_lock); + init_MUTEX(&ov->cbuf_lock); + + ov->buf_state = BUF_NOT_ALLOCATED; + + /* Must be kmalloc()'ed, for DMA accessibility */ + ov->cbuf = kmalloc(OV511_CBUF_SIZE, GFP_KERNEL); + if (!ov->cbuf) + goto error; + + if (ov->bclass == BCL_OV518) { + if (ov518_configure(ov) < 0) goto error; } else { - if (ov511_configure(ov511) < 0) + if (ov511_configure(ov) < 0) goto error; } for (i = 0; i < OV511_NUMFRAMES; i++) { - ov511->frame[i].framenum = i; - init_waitqueue_head(&ov511->frame[i].wq); + ov->frame[i].framenum = i; + init_waitqueue_head(&ov->frame[i].wq); + } + + for (i = 0; i < OV511_NUMSBUF; i++) { + ov->sbuf[i].ov = ov; + spin_lock_init(&ov->sbuf[i].lock); + ov->sbuf[i].n = i; } /* Unnecessary? (This is done on open(). Need to make sure variables * are properly initialized without this before removing it, though). */ - if (ov51x_set_default_params(ov511) < 0) + if (ov51x_set_default_params(ov) < 0) goto error; #ifdef OV511_DEBUG if (dump_bridge) - ov511_dump_regs(dev); + ov511_dump_regs(ov); #endif - memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template)); - ov511->vdev.priv = ov511; + memcpy(&ov->vdev, &vdev_template, sizeof(vdev_template)); + ov->vdev.priv = ov; for (i = 0; i < OV511_MAX_UNIT_VIDEO; i++) { /* Minor 0 cannot be specified; assume user wants autodetect */ if (unit_video[i] == 0) break; - if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER, + if (video_register_device(&ov->vdev, VFL_TYPE_GRABBER, unit_video[i]) >= 0) { registered = 1; break; @@ -6783,35 +6622,40 @@ /* Use the next available one */ if (!registered && - video_register_device(&ov511->vdev, VFL_TYPE_GRABBER, -1) < 0) { + video_register_device(&ov->vdev, VFL_TYPE_GRABBER, -1) < 0) { err("video_register_device failed"); goto error; } - info("Device registered on minor %d", ov511->vdev.minor); + info("Device registered on minor %d", ov->vdev.minor); - MOD_DEC_USE_COUNT; - return ov511; +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + create_proc_ov511_cam(ov); +#endif -error: - err("Camera initialization failed"); + return ov; +error: #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) /* Safe to call even if entry doesn't exist */ - destroy_proc_ov511_cam(ov511); + destroy_proc_ov511_cam(ov); #endif - usb_driver_release_interface(&ov511_driver, - &dev->actconfig->interface[ov511->iface]); + if (ov->cbuf) { + down(&ov->cbuf_lock); + kfree(ov->cbuf); + ov->cbuf = NULL; + up(&ov->cbuf_lock); + } error_dealloc: - if (ov511) { - kfree(ov511); - ov511 = NULL; + if (ov) { + kfree(ov); + ov = NULL; } -error_unlock: - MOD_DEC_USE_COUNT; +error_out: + err("Camera initialization failed"); return NULL; } @@ -6819,62 +6663,54 @@ static void ov51x_disconnect(struct usb_device *dev, void *ptr) { - struct usb_ov511 *ov511 = (struct usb_ov511 *) ptr; + struct usb_ov511 *ov = (struct usb_ov511 *) ptr; int n; - MOD_INC_USE_COUNT; - PDEBUG(3, ""); /* We don't want people trying to open up the device */ - if (!ov511->user) - video_unregister_device(&ov511->vdev); + if (!ov->user) + video_unregister_device(&ov->vdev); else PDEBUG(3, "Device open...deferring video_unregister_device"); for (n = 0; n < OV511_NUMFRAMES; n++) - ov511->frame[n].grabstate = FRAME_ERROR; + ov->frame[n].grabstate = FRAME_ERROR; - ov511->curframe = -1; + ov->curframe = -1; /* This will cause the process to request another frame */ for (n = 0; n < OV511_NUMFRAMES; n++) - if (waitqueue_active(&ov511->frame[n].wq)) - wake_up_interruptible(&ov511->frame[n].wq); - if (waitqueue_active(&ov511->wq)) - wake_up_interruptible(&ov511->wq); + if (waitqueue_active(&ov->frame[n].wq)) + wake_up_interruptible(&ov->frame[n].wq); + if (waitqueue_active(&ov->wq)) + wake_up_interruptible(&ov->wq); - ov511->streaming = 0; + ov->streaming = 0; - /* Unschedule all of the iso td's */ - for (n = OV511_NUMSBUF - 1; n >= 0; n--) { - if (ov511->sbuf[n].urb) { - ov511->sbuf[n].urb->next = NULL; - usb_unlink_urb(ov511->sbuf[n].urb); - usb_free_urb(ov511->sbuf[n].urb); - ov511->sbuf[n].urb = NULL; - } - } + ov51x_unlink_isoc(ov); #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) - destroy_proc_ov511_cam(ov511); + destroy_proc_ov511_cam(ov); #endif - usb_driver_release_interface(&ov511_driver, - &ov511->dev->actconfig->interface[ov511->iface]); - ov511->dev = NULL; + ov->dev = NULL; /* Free the memory */ - if (ov511 && !ov511->user) { - ov511_dealloc(ov511, 1); - kfree(ov511); - ov511 = NULL; + if (ov && !ov->user) { + down(&ov->cbuf_lock); + kfree(ov->cbuf); + ov->cbuf = NULL; + up(&ov->cbuf_lock); + + ov51x_dealloc(ov, 1); + kfree(ov); + ov = NULL; } - - MOD_DEC_USE_COUNT; } static struct usb_driver ov511_driver = { + owner: THIS_MODULE, name: "ov511", id_table: device_table, probe: ov51x_probe, diff -Nru a/drivers/usb/ov511.h b/drivers/usb/ov511.h --- a/drivers/usb/ov511.h Thu Mar 7 18:17:42 2002 +++ b/drivers/usb/ov511.h Thu Mar 7 18:17:42 2002 @@ -9,11 +9,11 @@ #define OV511_DEBUG /* Turn on debug messages */ #ifdef OV511_DEBUG -# define PDEBUG(level, fmt, args...) \ -if (debug >= (level)) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , \ - ## args) + #define PDEBUG(level, fmt, args...) \ + if (debug >= (level)) info("[%s:%d] " fmt, \ + __PRETTY_FUNCTION__, __LINE__ , ## args) #else -# define PDEBUG(level, fmt, args...) do {} while(0) + #define PDEBUG(level, fmt, args...) do {} while(0) #endif /* This macro restricts an int variable to an inclusive range */ @@ -36,98 +36,105 @@ #define VEND_MATTEL 0x0813 #define PROD_ME2CAM 0x0002 +/* --------------------------------- */ +/* OV51x REGISTER MNEMONICS */ +/* --------------------------------- */ + /* Camera interface register numbers */ -#define OV511_REG_CAMERA_DELAY_MODE 0x10 -#define OV511_REG_CAMERA_EDGE_MODE 0x11 -#define OV511_REG_CAMERA_CLAMPED_PIXEL_NUM 0x12 -#define OV511_REG_CAMERA_CLAMPED_LINE_NUM 0x13 -#define OV511_REG_CAMERA_PIXEL_DIVISOR 0x14 -#define OV511_REG_CAMERA_LINE_DIVISOR 0x15 -#define OV511_REG_CAMERA_DATA_INPUT_SELECT 0x16 -#define OV511_REG_CAMERA_RESERVED_LINE_MODE 0x17 -#define OV511_REG_CAMERA_BITMASK 0x18 +#define R511_CAM_DELAY 0x10 +#define R511_CAM_EDGE 0x11 +#define R511_CAM_PXCNT 0x12 +#define R511_CAM_LNCNT 0x13 +#define R511_CAM_PXDIV 0x14 +#define R511_CAM_LNDIV 0x15 +#define R511_CAM_UV_EN 0x16 +#define R511_CAM_LINE_MODE 0x17 +#define R511_CAM_OPTS 0x18 /* Snapshot mode camera interface register numbers */ -#define OV511_REG_SNAP_CAPTURED_FRAME 0x19 -#define OV511_REG_SNAP_CLAMPED_PIXEL_NUM 0x1A -#define OV511_REG_SNAP_CLAMPED_LINE_NUM 0x1B -#define OV511_REG_SNAP_PIXEL_DIVISOR 0x1C -#define OV511_REG_SNAP_LINE_DIVISOR 0x1D -#define OV511_REG_SNAP_DATA_INPUT_SELECT 0x1E -#define OV511_REG_SNAP_BITMASK 0x1F +#define R511_SNAP_FRAME 0x19 +#define R511_SNAP_PXCNT 0x1A +#define R511_SNAP_LNCNT 0x1B +#define R511_SNAP_PXDIV 0x1C +#define R511_SNAP_LNDIV 0x1D +#define R511_SNAP_UV_EN 0x1E +#define R511_SNAP_OPTS 0x1F /* DRAM register numbers */ -#define OV511_REG_DRAM_ENABLE_FLOW_CONTROL 0x20 -#define OV511_REG_DRAM_READ_CYCLE_PREDICT 0x21 -#define OV511_REG_DRAM_MANUAL_READ_CYCLE 0x22 -#define OV511_REG_DRAM_REFRESH_COUNTER 0x23 +#define R511_DRAM_FLOW_CTL 0x20 +#define R511_DRAM_ARCP 0x21 +#define R511_DRAM_MRC 0x22 +#define R511_DRAM_RFC 0x23 /* ISO FIFO register numbers */ -#define OV511_REG_FIFO_PACKET_SIZE 0x30 -#define OV511_REG_FIFO_BITMASK 0x31 +#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */ +#define R511_FIFO_OPTS 0x31 -/* PIO register numbers */ -#define OV511_REG_PIO_BITMASK 0x38 -#define OV511_REG_PIO_DATA_PORT 0x39 -#define OV511_REG_PIO_BIST 0x3E - -/* I2C register numbers */ -#define OV511_REG_I2C_CONTROL 0x40 -#define OV518_REG_I2C_CONTROL 0x47 /* OV518(+) only */ -#define OV511_REG_I2C_SLAVE_ID_WRITE 0x41 -#define OV511_REG_I2C_SUB_ADDRESS_3_BYTE 0x42 -#define OV511_REG_I2C_SUB_ADDRESS_2_BYTE 0x43 -#define OV511_REG_I2C_SLAVE_ID_READ 0x44 -#define OV511_REG_I2C_DATA_PORT 0x45 -#define OV511_REG_I2C_CLOCK_PRESCALER 0x46 -#define OV511_REG_I2C_TIME_OUT_COUNTER 0x47 - -/* I2C snapshot register numbers */ -#define OV511_REG_I2C_SNAP_SUB_ADDRESS 0x48 -#define OV511_REG_I2C_SNAP_DATA_PORT 0x49 - -/* System control register numbers */ -#define OV511_REG_SYSTEM_RESET 0x50 -#define OV511_RESET_UDC 0x01 -#define OV511_RESET_I2C 0x02 -#define OV511_RESET_FIFO 0x04 -#define OV511_RESET_OMNICE 0x08 -#define OV511_RESET_DRAM_INTF 0x10 -#define OV511_RESET_CAMERA_INTF 0x20 -#define OV511_RESET_OV511 0x40 -#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */ -#define OV511_RESET_ALL 0x7F -#define OV511_REG_SYSTEM_CLOCK_DIVISOR 0x51 -#define OV511_REG_SYSTEM_SNAPSHOT 0x52 -#define OV511_REG_SYSTEM_INIT 0x53 -#define OV511_REG_SYSTEM_PWR_CLK 0x54 /* OV511+/OV518(+) only */ -#define OV511_REG_SYSTEM_LED_CTL 0x55 /* OV511+ only */ -#define OV518_REG_GPIO_IN 0x55 /* OV518(+) only */ -#define OV518_REG_GPIO_OUT 0x56 /* OV518(+) only */ -#define OV518_REG_GPIO_CTL 0x57 /* OV518(+) only */ -#define OV518_REG_GPIO_PULSE_IN 0x58 /* OV518(+) only */ -#define OV518_REG_GPIO_PULSE_CLEAR 0x59 /* OV518(+) only */ -#define OV518_REG_GPIO_PULSE_POLARITY 0x5a /* OV518(+) only */ -#define OV518_REG_GPIO_PULSE_EN 0x5b /* OV518(+) only */ -#define OV518_REG_GPIO_RESET 0x5c /* OV518(+) only */ -#define OV511_REG_SYSTEM_USER_DEFINED 0x5E -#define OV511_REG_SYSTEM_CUSTOM_ID 0x5F - -/* OmniCE register numbers */ -#define OV511_OMNICE_PREDICTION_HORIZ_Y 0x70 -#define OV511_OMNICE_PREDICTION_HORIZ_UV 0x71 -#define OV511_OMNICE_PREDICTION_VERT_Y 0x72 -#define OV511_OMNICE_PREDICTION_VERT_UV 0x73 -#define OV511_OMNICE_QUANTIZATION_HORIZ_Y 0x74 -#define OV511_OMNICE_QUANTIZATION_HORIZ_UV 0x75 -#define OV511_OMNICE_QUANTIZATION_VERT_Y 0x76 -#define OV511_OMNICE_QUANTIZATION_VERT_UV 0x77 -#define OV511_OMNICE_ENABLE 0x78 -#define OV511_OMNICE_LUT_ENABLE 0x79 -#define OV511_OMNICE_Y_LUT_BEGIN 0x80 -#define OV511_OMNICE_Y_LUT_END 0x9F -#define OV511_OMNICE_UV_LUT_BEGIN 0xA0 -#define OV511_OMNICE_UV_LUT_END 0xBF +/* Parallel IO register numbers */ +#define R511_PIO_OPTS 0x38 +#define R511_PIO_DATA 0x39 +#define R511_PIO_BIST 0x3E +#define R518_GPIO_IN 0x55 /* OV518(+) only */ +#define R518_GPIO_OUT 0x56 /* OV518(+) only */ +#define R518_GPIO_CTL 0x57 /* OV518(+) only */ +#define R518_GPIO_PULSE_IN 0x58 /* OV518(+) only */ +#define R518_GPIO_PULSE_CLEAR 0x59 /* OV518(+) only */ +#define R518_GPIO_PULSE_POL 0x5a /* OV518(+) only */ +#define R518_GPIO_PULSE_EN 0x5b /* OV518(+) only */ +#define R518_GPIO_RESET 0x5c /* OV518(+) only */ + +/* I2C registers */ +#define R511_I2C_CTL 0x40 +#define R518_I2C_CTL 0x47 /* OV518(+) only */ +#define R51x_I2C_W_SID 0x41 +#define R51x_I2C_SADDR_3 0x42 +#define R51x_I2C_SADDR_2 0x43 +#define R51x_I2C_R_SID 0x44 +#define R51x_I2C_DATA 0x45 +#define R51x_I2C_CLOCK 0x46 +#define R51x_I2C_TIMEOUT 0x47 + +/* I2C snapshot registers */ +#define R511_SI2C_SADDR_3 0x48 +#define R511_SI2C_DATA 0x49 + +/* System control registers */ +#define R51x_SYS_RESET 0x50 + /* Reset type definitions */ +#define OV511_RESET_UDC 0x01 +#define OV511_RESET_I2C 0x02 +#define OV511_RESET_FIFO 0x04 +#define OV511_RESET_OMNICE 0x08 +#define OV511_RESET_DRAM 0x10 +#define OV511_RESET_CAM_INT 0x20 +#define OV511_RESET_OV511 0x40 +#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */ +#define OV511_RESET_ALL 0x7F + +#define R511_SYS_CLOCK_DIV 0x51 +#define R51x_SYS_SNAP 0x52 +#define R51x_SYS_INIT 0x53 +#define R511_SYS_PWR_CLK 0x54 /* OV511+/OV518(+) only */ +#define R511_SYS_LED_CTL 0x55 /* OV511+ only */ +#define R511_SYS_USER 0x5E +#define R511_SYS_CUST_ID 0x5F + +/* OmniCE (compression) registers */ +#define R511_COMP_PHY 0x70 +#define R511_COMP_PHUV 0x71 +#define R511_COMP_PVY 0x72 +#define R511_COMP_PVUV 0x73 +#define R511_COMP_QHY 0x74 +#define R511_COMP_QHUV 0x75 +#define R511_COMP_QVY 0x76 +#define R511_COMP_QVUV 0x77 +#define R511_COMP_EN 0x78 +#define R511_COMP_LUT_EN 0x79 +#define R511_COMP_LUT_BEGIN 0x80 + +/* --------------------------------- */ +/* ALTERNATE NUMBERS */ +/* --------------------------------- */ /* Alternate numbers for various max packet sizes (OV511 only) */ #define OV511_ALT_SIZE_992 0 @@ -159,6 +166,10 @@ #define OV518_ALT_SIZE_768 6 #define OV518_ALT_SIZE_896 7 +/* --------------------------------- */ +/* OV7610 REGISTER MNEMONICS */ +/* --------------------------------- */ + /* OV7610 registers */ #define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */ #define OV7610_REG_BLUE 0x01 /* blue channel balance */ @@ -210,26 +221,37 @@ /* 36-37 reserved */ #define OV7610_REG_COM_K 0x38 /* misc registers */ +/* --------------------------------- */ +/* I2C ADDRESSES */ +/* --------------------------------- */ + +#define OV7xx0_SID 0x42 +#define OV6xx0_SID 0xC0 +#define OV8xx0_SID 0xA0 +#define KS0127_SID 0xD8 +#define SAA7111A_SID 0x48 + +/* --------------------------------- */ +/* MISCELLANEOUS DEFINES */ +/* --------------------------------- */ + +#define I2C_CLOCK_PRESCALER 0x03 + #define FRAMES_PER_DESC 10 /* FIXME - What should this be? */ -#define FRAME_SIZE_PER_DESC 993 /* FIXME - Deprecated */ #define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */ #define PIXELS_PER_SEG 256 /* Pixels per segment */ -#define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ +#define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ -/* I2C addresses */ -#define OV7xx0_I2C_WRITE_ID 0x42 -#define OV7xx0_I2C_READ_ID 0x43 -#define OV6xx0_I2C_WRITE_ID 0xC0 -#define OV6xx0_I2C_READ_ID 0xC1 -#define OV8xx0_I2C_WRITE_ID 0xA0 -#define OV8xx0_I2C_READ_ID 0xA1 -#define KS0127_I2C_WRITE_ID 0xD8 -#define KS0127_I2C_READ_ID 0xD9 -#define SAA7111A_I2C_WRITE_ID 0x48 -#define SAA7111A_I2C_READ_ID 0x49 +#define OV511_NUMFRAMES 2 +#if OV511_NUMFRAMES > VIDEO_MAX_FRAME + #error "OV511_NUMFRAMES is too high" +#endif + +#define OV511_NUMSBUF 2 -#define OV511_I2C_CLOCK_PRESCALER 0x03 +/* Control transfers use up to 4 bytes */ +#define OV511_CBUF_SIZE 4 /* Bridge types */ enum { @@ -364,9 +386,14 @@ struct ov511_i2c_struct) /* ------------- End IOCTL interface -------------- */ +struct usb_ov511; /* Forward declaration */ + struct ov511_sbuf { - char *data; + struct usb_ov511 *ov; + unsigned char *data; struct urb *urb; + spinlock_t lock; + int n; }; enum { @@ -389,9 +416,10 @@ struct ov511_frame { int framenum; /* Index of this frame */ - char *data; /* Frame buffer */ - char *tempdata; /* Temp buffer for multi-stage conversions */ - char *rawdata; /* Raw camera data buffer */ + unsigned char *data; /* Frame buffer */ + unsigned char *tempdata; /* Temp buffer for multi-stage conversions */ + unsigned char *rawdata; /* Raw camera data buffer */ + unsigned char *compbuf; /* Temp buffer for decompressor */ int depth; /* Bytes per pixel */ int width; /* Width application is expecting */ @@ -416,24 +444,20 @@ int snapshot; /* True if frame was a snapshot */ }; -#define DECOMP_INTERFACE_VER 2 +#define DECOMP_INTERFACE_VER 3 /* Compression module operations */ struct ov51x_decomp_ops { - int (*decomp_400)(unsigned char *, unsigned char *, int, int, int); - int (*decomp_420)(unsigned char *, unsigned char *, int, int, int); - int (*decomp_422)(unsigned char *, unsigned char *, int, int, int); + int (*decomp_400)(unsigned char *, unsigned char *, unsigned char *, + int, int, int); + int (*decomp_420)(unsigned char *, unsigned char *, unsigned char *, + int, int, int); + int (*decomp_422)(unsigned char *, unsigned char *, unsigned char *, + int, int, int); void (*decomp_lock)(void); void (*decomp_unlock)(void); }; -#define OV511_NUMFRAMES 2 -#if OV511_NUMFRAMES > VIDEO_MAX_FRAME -#error "OV511_NUMFRAMES is too high" -#endif - -#define OV511_NUMSBUF 2 - struct usb_ov511 { struct video_device vdev; @@ -441,7 +465,7 @@ struct usb_device *dev; int customid; - int desc; + char *desc; unsigned char iface; /* Determined by sensor type */ @@ -475,9 +499,9 @@ int lightfreq; /* Power (lighting) frequency */ int bandfilt; /* Banding filter enabled flag */ - char *fbuf; /* Videodev buffer area */ - char *tempfbuf; /* Temporary (intermediate) buffer area */ - char *rawfbuf; /* Raw camera data buffer area */ + unsigned char *fbuf; /* Videodev buffer area */ + unsigned char *tempfbuf; /* Temporary (intermediate) buffer area */ + unsigned char *rawfbuf; /* Raw camera data buffer area */ int sub_flag; /* Pix Array subcapture on flag */ int subx; /* Pix Array subcapture x offset */ @@ -535,17 +559,34 @@ /* I2C interface to kernel */ struct semaphore i2c_lock; /* Protect I2C controller regs */ unsigned char primary_i2c_slave; /* I2C write id of sensor */ -}; -struct cam_list { - int id; - char *description; + /* Control transaction stuff */ + unsigned char *cbuf; /* Buffer for payload */ + struct semaphore cbuf_lock; }; -struct palette_list { +/* Used to represent a list of values and their respective symbolic names */ +struct symbolic_list { int num; char *name; }; + +#define NOT_DEFINED_STR "Unknown" + +/* Returns the name of the matching element in the symbolic_list array. The + * end of the list must be marked with an element that has a NULL name. + */ +static inline char * +symbolic(struct symbolic_list list[], int num) +{ + int i; + + for (i = 0; list[i].name != NULL; i++) + if (list[i].num == num) + return (list[i].name); + + return (NOT_DEFINED_STR); +} struct mode_list_518 { int width; diff -Nru a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c --- a/drivers/usb/pegasus.c Thu Mar 7 18:17:44 2002 +++ b/drivers/usb/pegasus.c Thu Mar 7 18:17:44 2002 @@ -1,7 +1,7 @@ /* ** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller ** -** Copyright (c) 1999-2001 Petko Manolov (pmanolov@lnxw.com) +** Copyright (c) 1999-2002 Petko Manolov (petkan@users.sourceforge.net) ** ** ** ChangeLog: @@ -21,6 +21,8 @@ ** TODO: suppressing HCD warnings spewage on disconnect. ** v0.4.13 Ethernet address is now set at probe(), not at open() ** time as this seems to break dhcpd. +** v0.5.0 branch to 2.5.x kernels +** v0.5.1 ethtool support added */ /* @@ -46,20 +48,25 @@ #include #include #include +#include +#include #include #include #include +#include #include "pegasus.h" /* * Version Information */ -#define DRIVER_VERSION "v0.4.23 (2002/02/01)" -#define DRIVER_AUTHOR "Petko Manolov " +#define DRIVER_VERSION "v0.5.1 (2002/03/06)" +#define DRIVER_AUTHOR "Petko Manolov " #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" #define PEGASUS_USE_INTR #define PEGASUS_WRITE_EEPROM +#define BMSR_MEDIA (BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | \ + BMSR_100FULL | BMSR_ANEGCAPABLE) static int loopback = 0; static int mii_mode = 0; @@ -461,8 +468,8 @@ usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK ) { __u16 auxmode; - read_mii_word( pegasus, 0, 0x1b, &auxmode ); - write_mii_word( pegasus, 0, 0x1b, auxmode | 4 ); + read_mii_word( pegasus, 0, MII_TPISTATUS, &auxmode ); + write_mii_word( pegasus, 0, MII_TPISTATUS, auxmode | 4 ); } return 0; @@ -481,16 +488,16 @@ if ( !(bmsr & 0x20) && !loopback ) warn( "%s: link NOT established (0x%x) - check the cable.", dev->name, bmsr ); - if ( read_mii_word(pegasus, pegasus->phy, MII_ANLPA, &linkpart) ) + if ( read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart) ) return 2; if ( !(linkpart & 1) ) warn( "link partner stat %x", linkpart ); data[0] = 0xc9; data[1] = 0; - if ( linkpart & (ANLPA_100TX_FD | ANLPA_10T_FD) ) + if ( linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL) ) data[1] |= 0x20; /* set full duplex */ - if ( linkpart & (ANLPA_100TX_FD | ANLPA_100TX_HD) ) + if ( linkpart & (ADVERTISE_100FULL | ADVERTISE_100HALF) ) data[1] |= 0x10; /* set 100 Mbps */ if ( mii_mode ) data[1] = 0; @@ -710,15 +717,26 @@ } +static void set_carrier(struct net_device *net) +{ + pegasus_t *pegasus; + short tmp; + + pegasus = net->priv; + read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp); + if (tmp & BMSR_LSTATUS) + netif_carrier_on(net); + else + netif_carrier_off(net); + +} + + static int pegasus_open(struct net_device *net) { pegasus_t *pegasus = (pegasus_t *)net->priv; int res; - if ( (res = enable_net_traffic(net, pegasus->usb)) ) { - err("can't enable_net_traffic() - %d", res); - return -EIO; - } FILL_BULK_URB( pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), pegasus->rx_buff, PEGASUS_MAX_MTU, @@ -735,6 +753,11 @@ #endif netif_start_queue( net ); pegasus->flags |= PEGASUS_RUNNING; + if ( (res = enable_net_traffic(net, pegasus->usb)) ) { + err("can't enable_net_traffic() - %d", res); + return -EIO; + } + set_carrier(net); return 0; } @@ -760,24 +783,103 @@ } +static int pegasus_ethtool_ioctl(struct net_device *net, void *uaddr) +{ + pegasus_t *pegasus; + int cmd; + char tmp[128]; + + pegasus = net->priv; + if (get_user(cmd, (int *)uaddr)) + return -EFAULT; + switch (cmd) { + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; + strncpy(info.driver, DRIVER_DESC, ETHTOOL_BUSINFO_LEN); + strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); + sprintf(tmp, "usb%d:%d", pegasus->usb->bus->busnum, + pegasus->usb->devnum); + strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN); + if (copy_to_user(uaddr, &info, sizeof(info))) + return -EFAULT; + return 0; + } + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd; + short lpa, bmcr; + + if (copy_from_user(&ecmd, uaddr, sizeof(ecmd))) + return -EFAULT; + ecmd.supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP | + SUPPORTED_MII); + ecmd.port = PORT_TP; + ecmd.transceiver = XCVR_INTERNAL; + ecmd.phy_address = pegasus->phy; + read_mii_word(pegasus, pegasus->phy, MII_BMCR, &bmcr); + read_mii_word(pegasus, pegasus->phy, MII_LPA, &lpa); + if (bmcr & BMCR_ANENABLE) { + ecmd.autoneg = AUTONEG_ENABLE; + ecmd.speed = lpa & (LPA_100HALF|LPA_100FULL) ? + SPEED_100 : SPEED_10; + if (ecmd.speed == SPEED_100) + ecmd.duplex = lpa & LPA_100FULL ? + DUPLEX_FULL : DUPLEX_HALF; + else + ecmd.duplex = lpa & LPA_10FULL ? + DUPLEX_FULL : DUPLEX_HALF; + } else { + ecmd.autoneg = AUTONEG_DISABLE; + ecmd.speed = bmcr & BMCR_SPEED100 ? + SPEED_100 : SPEED_10; + ecmd.duplex = bmcr & BMCR_FULLDPLX ? + DUPLEX_FULL : DUPLEX_HALF; + } + if (copy_to_user(uaddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + + return 0; + } + case ETHTOOL_SSET: { + return -EOPNOTSUPP; + } + case ETHTOOL_GLINK: { + struct ethtool_value edata = {ETHTOOL_GLINK}; + edata.data = netif_carrier_ok(net); + if (copy_to_user(uaddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + default: + return -EOPNOTSUPP; + } +} + + static int pegasus_ioctl( struct net_device *net, struct ifreq *rq, int cmd ) { __u16 *data = (__u16 *)&rq->ifr_data; pegasus_t *pegasus = net->priv; switch(cmd) { - case SIOCDEVPRIVATE: - data[0] = pegasus->phy; - case SIOCDEVPRIVATE+1: - read_mii_word(pegasus, data[0], data[1]&0x1f, &data[3]); - return 0; - case SIOCDEVPRIVATE+2: - if ( !capable(CAP_NET_ADMIN) ) - return -EPERM; - write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]); - return 0; - default: - return -EOPNOTSUPP; + case SIOCETHTOOL: + return pegasus_ethtool_ioctl(net, rq->ifr_data); + case SIOCDEVPRIVATE: + data[0] = pegasus->phy; + case SIOCDEVPRIVATE+1: + read_mii_word(pegasus, data[0], data[1]&0x1f, &data[3]); + return 0; + case SIOCDEVPRIVATE+2: + if ( !capable(CAP_NET_ADMIN) ) + return -EPERM; + write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]); + return 0; + default: + return -EOPNOTSUPP; } } diff -Nru a/drivers/usb/pegasus.h b/drivers/usb/pegasus.h --- a/drivers/usb/pegasus.h Thu Mar 7 18:17:43 2002 +++ b/drivers/usb/pegasus.h Thu Mar 7 18:17:43 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999,2000 Petko Manolov - Petkan (pmanolov@lnxw.com) + * Copyright (c) 1999-2002 Petko Manolov - Petkan (petkan@users.sourceforge.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 @@ -31,14 +31,6 @@ #define EPROM_WR_ENABLE 0x10 #define EPROM_LOAD 0x20 -#define MII_BMCR 0x00 -#define MII_BMSR 0x01 -#define BMSR_MEDIA 0x7808 -#define MII_ANLPA 0x05 -#define ANLPA_100TX_FD 0x0100 -#define ANLPA_100TX_HD 0x0080 -#define ANLPA_10T_FD 0x0040 -#define ANLPA_10T_HD 0x0020 #define PHY_DONE 0x80 #define PHY_READ 0x40 #define PHY_WRITE 0x20 @@ -184,6 +176,8 @@ PEGASUS_DEV( "ADMtek AN986 \"Pegasus\" USB Ethernet (eval. board)", VENDOR_ADMTEK, 0x0986, DEFAULT_GPIO_RESET | HAS_HOME_PNA ) +PEGASUS_DEV( "AN986A USB MAC", VENDOR_ADMTEK, 1986, + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Allied Telesyn Int. AT-USB100", VENDOR_ALLIEDTEL, 0xb100, DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Belkin F5D5050 USB Ethernet", VENDOR_BELKIN, 0x0121, @@ -254,6 +248,8 @@ DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "SMC 202 USB Ethernet", VENDOR_SMC, 0x0200, DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "SMC 2206 USB Ethernet", VENDOR_SMC, 0x0201, + DEFAULT_GPIO_RESET | PEGASUS_II) PEGASUS_DEV( "SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100, DEFAULT_GPIO_RESET ) PEGASUS_DEV( "SpeedStream USB 10/100 Ethernet", VENDOR_SIEMENS, 0x1001, diff -Nru a/drivers/usb/printer.c b/drivers/usb/printer.c --- a/drivers/usb/printer.c Thu Mar 7 18:17:39 2002 +++ b/drivers/usb/printer.c Thu Mar 7 18:17:39 2002 @@ -91,7 +91,7 @@ struct usb_device *dev; /* USB device */ devfs_handle_t devfs; /* devfs device */ struct semaphore sem; /* locks this struct, especially "dev" */ - struct urb readurb, writeurb; /* The urbs */ + struct urb *readurb, *writeurb; /* The urbs */ wait_queue_head_t wait; /* Zzzzz ... */ int readcount; /* Counter for reads */ int ifnum; /* Interface number */ @@ -253,15 +253,15 @@ usblp->used = 1; file->private_data = usblp; - usblp->writeurb.transfer_buffer_length = 0; - usblp->writeurb.status = 0; + usblp->writeurb->transfer_buffer_length = 0; + usblp->writeurb->status = 0; usblp->wcomplete = 1; /* we begin writeable */ usblp->rcomplete = 0; if (usblp->bidir) { usblp->readcount = 0; - usblp->readurb.dev = usblp->dev; - if (usb_submit_urb(&usblp->readurb, GFP_KERNEL) < 0) { + usblp->readurb->dev = usblp->dev; + if (usb_submit_urb(usblp->readurb, GFP_KERNEL) < 0) { retval = -EIO; usblp->used = 0; file->private_data = NULL; @@ -278,8 +278,10 @@ usblp_table [usblp->minor] = NULL; info ("usblp%d: removed", usblp->minor); - kfree (usblp->writeurb.transfer_buffer); + kfree (usblp->writeurb->transfer_buffer); kfree (usblp->device_id_string); + usb_free_urb(usblp->writeurb); + usb_free_urb(usblp->readurb); kfree (usblp); } @@ -292,8 +294,8 @@ usblp->used = 0; if (usblp->dev) { if (usblp->bidir) - usb_unlink_urb(&usblp->readurb); - usb_unlink_urb(&usblp->writeurb); + usb_unlink_urb(usblp->readurb); + usb_unlink_urb(usblp->writeurb); up(&usblp->sem); } else /* finish cleanup from disconnect */ usblp_cleanup (usblp); @@ -306,8 +308,8 @@ { struct usblp *usblp = file->private_data; poll_wait(file, &usblp->wait, wait); - return ((!usblp->bidir || usblp->readurb.status == -EINPROGRESS) ? 0 : POLLIN | POLLRDNORM) - | (usblp->writeurb.status == -EINPROGRESS ? 0 : POLLOUT | POLLWRNORM); + return ((!usblp->bidir || usblp->readurb->status == -EINPROGRESS) ? 0 : POLLIN | POLLRDNORM) + | (usblp->writeurb->status == -EINPROGRESS ? 0 : POLLOUT | POLLWRNORM); } static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) @@ -423,12 +425,12 @@ return -ENODEV; } - if (usblp->writeurb.status != 0) { + if (usblp->writeurb->status != 0) { if (usblp->quirks & USBLP_QUIRK_BIDIR) { if (!usblp->wcomplete) err("usblp%d: error %d writing to printer", - usblp->minor, usblp->writeurb.status); - err = usblp->writeurb.status; + usblp->minor, usblp->writeurb->status); + err = usblp->writeurb->status; } else err = usblp_check_status(usblp, err); up (&usblp->sem); @@ -440,23 +442,23 @@ continue; } - writecount += usblp->writeurb.transfer_buffer_length; - usblp->writeurb.transfer_buffer_length = 0; + writecount += usblp->writeurb->transfer_buffer_length; + usblp->writeurb->transfer_buffer_length = 0; if (writecount == count) { up (&usblp->sem); break; } - usblp->writeurb.transfer_buffer_length = (count - writecount) < USBLP_BUF_SIZE ? - (count - writecount) : USBLP_BUF_SIZE; + usblp->writeurb->transfer_buffer_length = (count - writecount) < USBLP_BUF_SIZE ? + (count - writecount) : USBLP_BUF_SIZE; - if (copy_from_user(usblp->writeurb.transfer_buffer, buffer + writecount, - usblp->writeurb.transfer_buffer_length)) return -EFAULT; + if (copy_from_user(usblp->writeurb->transfer_buffer, buffer + writecount, + usblp->writeurb->transfer_buffer_length)) return -EFAULT; - usblp->writeurb.dev = usblp->dev; + usblp->writeurb->dev = usblp->dev; usblp->wcomplete = 0; - if (usb_submit_urb(&usblp->writeurb, GFP_KERNEL)) { + if (usb_submit_urb(usblp->writeurb, GFP_KERNEL)) { count = -EIO; up (&usblp->sem); break; @@ -516,29 +518,29 @@ goto done; } - if (usblp->readurb.status) { + if (usblp->readurb->status) { err("usblp%d: error %d reading from printer", - usblp->minor, usblp->readurb.status); - usblp->readurb.dev = usblp->dev; + usblp->minor, usblp->readurb->status); + usblp->readurb->dev = usblp->dev; usblp->readcount = 0; - usb_submit_urb(&usblp->readurb, GFP_KERNEL); + usb_submit_urb(usblp->readurb, GFP_KERNEL); count = -EIO; goto done; } - count = count < usblp->readurb.actual_length - usblp->readcount ? - count : usblp->readurb.actual_length - usblp->readcount; + count = count < usblp->readurb->actual_length - usblp->readcount ? + count : usblp->readurb->actual_length - usblp->readcount; - if (copy_to_user(buffer, usblp->readurb.transfer_buffer + usblp->readcount, count)) { + if (copy_to_user(buffer, usblp->readurb->transfer_buffer + usblp->readcount, count)) { count = -EFAULT; goto done; } - if ((usblp->readcount += count) == usblp->readurb.actual_length) { + if ((usblp->readcount += count) == usblp->readurb->actual_length) { usblp->readcount = 0; - usblp->readurb.dev = usblp->dev; + usblp->readurb->dev = usblp->dev; usblp->rcomplete = 0; - if (usb_submit_urb(&usblp->readurb, GFP_KERNEL)) { + if (usb_submit_urb(usblp->readurb, GFP_KERNEL)) { count = -EIO; goto done; } @@ -668,24 +670,42 @@ init_waitqueue_head(&usblp->wait); + usblp->writeurb = usb_alloc_urb(0, GFP_KERNEL); + if (!usblp->writeurb) { + err("out of memory"); + kfree(usblp); + return NULL; + } + usblp->readurb = usb_alloc_urb(0, GFP_KERNEL); + if (!usblp->readurb) { + err("out of memory"); + usb_free_urb(usblp->writeurb); + kfree(usblp); + return NULL; + } + if (!(buf = kmalloc(USBLP_BUF_SIZE * (bidir ? 2 : 1), GFP_KERNEL))) { err("out of memory"); + usb_free_urb(usblp->writeurb); + usb_free_urb(usblp->readurb); kfree(usblp); return NULL; } if (!(usblp->device_id_string = kmalloc(DEVICE_ID_SIZE, GFP_KERNEL))) { err("out of memory"); + usb_free_urb(usblp->writeurb); + usb_free_urb(usblp->readurb); kfree(usblp); kfree(buf); return NULL; } - FILL_BULK_URB(&usblp->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), + FILL_BULK_URB(usblp->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), buf, 0, usblp_bulk_write, usblp); if (bidir) - FILL_BULK_URB(&usblp->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), + FILL_BULK_URB(usblp->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), buf + USBLP_BUF_SIZE, USBLP_BUF_SIZE, usblp_bulk_read, usblp); /* Get the device_id string if possible. FIXME: Could make this kmalloc(length). */ @@ -737,9 +757,9 @@ lock_kernel(); usblp->dev = NULL; - usb_unlink_urb(&usblp->writeurb); + usb_unlink_urb(usblp->writeurb); if (usblp->bidir) - usb_unlink_urb(&usblp->readurb); + usb_unlink_urb(usblp->readurb); if (!usblp->used) usblp_cleanup (usblp); diff -Nru a/drivers/usb/pwc-if.c b/drivers/usb/pwc-if.c --- a/drivers/usb/pwc-if.c Thu Mar 7 18:17:45 2002 +++ b/drivers/usb/pwc-if.c Thu Mar 7 18:17:45 2002 @@ -179,28 +179,24 @@ /* Private functions */ /* 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. + * This is used when initializing the contents of the area. */ static inline unsigned long kvirt_to_pa(unsigned long adr) { - unsigned long va, kva, ret; + unsigned long kva, ret; - va = VMALLOC_VMADDR(adr); - kva = page_address(vmalloc_to_page(va)); + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ ret = __pa(kva); return ret; } -static void * rvmalloc(signed long size) +static void * rvmalloc(unsigned long size) { void * mem; - unsigned long adr, page; + unsigned long adr; - /* Round it off to PAGE_SIZE */ - size += (PAGE_SIZE - 1); - size &= ~(PAGE_SIZE - 1); - + size=PAGE_ALIGN(size); mem=vmalloc_32(size); if (mem) { @@ -208,8 +204,7 @@ adr=(unsigned long) mem; while (size > 0) { - page = kvirt_to_pa(adr); - mem_map_reserve(virt_to_page(__va(page))); + mem_map_reserve(vmalloc_to_page((void *)adr)); adr+=PAGE_SIZE; size-=PAGE_SIZE; } @@ -217,20 +212,16 @@ return mem; } -static void rvfree(void * mem, signed long size) +static void rvfree(void * mem, unsigned long size) { - unsigned long adr, page; - - /* Round it off to PAGE_SIZE */ - size += (PAGE_SIZE - 1); - size &= ~(PAGE_SIZE - 1); + unsigned long adr; + if (mem) { adr=(unsigned long) mem; - while (size > 0) + while ((long) size > 0) { - page = kvirt_to_pa(adr); - mem_map_unreserve(virt_to_page(__va(page))); + mem_map_unreserve(vmalloc_to_page((void *)adr)); adr+=PAGE_SIZE; size-=PAGE_SIZE; } diff -Nru a/drivers/usb/se401.c b/drivers/usb/se401.c --- a/drivers/usb/se401.c Thu Mar 7 18:17:41 2002 +++ b/drivers/usb/se401.c Thu Mar 7 18:17:41 2002 @@ -84,15 +84,14 @@ **********************************************************************/ /* 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. + * This is used when initializing the contents of the area. */ static inline unsigned long kvirt_to_pa(unsigned long adr) { - unsigned long va, kva, ret; + unsigned long kva, ret; - va = VMALLOC_VMADDR(adr); - kva = page_address(vmalloc_to_page(va)); + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ ret = __pa(kva); return ret; } @@ -100,12 +99,9 @@ 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); + unsigned long adr; + size = PAGE_ALIGN(size); mem = vmalloc_32(size); if (!mem) return NULL; @@ -113,13 +109,9 @@ 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))); + mem_map_reserve(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; + size -= PAGE_SIZE; } return mem; @@ -127,23 +119,16 @@ static void rvfree(void *mem, unsigned long size) { - unsigned long adr, page; + unsigned long adr; 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 = (unsigned long) mem; + while ((long) size > 0) { + mem_map_unreserve(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; + size -= PAGE_SIZE; } vfree(mem); } diff -Nru a/drivers/usb/serial/Config.help b/drivers/usb/serial/Config.help --- a/drivers/usb/serial/Config.help Thu Mar 7 18:17:42 2002 +++ b/drivers/usb/serial/Config.help Thu Mar 7 18:17:42 2002 @@ -40,9 +40,10 @@ module, say M here and read . CONFIG_USB_SERIAL_IPAQ - Say Y here if you want to connect to your Compaq iPAQ running - Windows CE 3.0 using a USB autosync cable. For information on using - the driver, read . + Say Y here if you want to connect to your Compaq iPAQ or HP Jornada + 548/568 running Windows CE 3.0 or PocketPC 2002 using a USB + cradle/cable. For information on using the driver, + read . This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). diff -Nru a/drivers/usb/serial/Config.in b/drivers/usb/serial/Config.in --- a/drivers/usb/serial/Config.in Thu Mar 7 18:17:36 2002 +++ b/drivers/usb/serial/Config.in Thu Mar 7 18:17:36 2002 @@ -15,7 +15,7 @@ dep_tristate ' USB Empeg empeg-car Mark I/II Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_EMPEG $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL dep_tristate ' USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI_SIO $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL dep_tristate ' USB Handspring Visor / Palm m50x / Sony Clie Driver' CONFIG_USB_SERIAL_VISOR $CONFIG_USB_SERIAL -dep_tristate ' USB Compaq iPAQ Driver' CONFIG_USB_SERIAL_IPAQ $CONFIG_USB_SERIAL +dep_tristate ' USB Compaq iPAQ / HP Jornada Driver' CONFIG_USB_SERIAL_IPAQ $CONFIG_USB_SERIAL dep_tristate ' USB IR Dongle Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_IR $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL dep_tristate ' USB Inside Out Edgeport Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_EDGEPORT $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL dep_tristate ' USB Keyspan PDA Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN_PDA $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL diff -Nru a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c --- a/drivers/usb/serial/belkin_sa.c Thu Mar 7 18:17:37 2002 +++ b/drivers/usb/serial/belkin_sa.c Thu Mar 7 18:17:37 2002 @@ -207,8 +207,6 @@ dbg(__FUNCTION__" port %d", port->number); - down (&port->sem); - ++port->open_count; if (port->open_count == 1) { @@ -230,8 +228,6 @@ } exit: - up (&port->sem); - return retval; } /* belkin_sa_open */ @@ -249,8 +245,6 @@ dbg(__FUNCTION__" port %d", port->number); - down (&port->sem); - --port->open_count; if (port->open_count <= 0) { @@ -262,8 +256,6 @@ } port->open_count = 0; } - - up (&port->sem); } /* belkin_sa_close */ diff -Nru a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c --- a/drivers/usb/serial/cyberjack.c Thu Mar 7 18:17:46 2002 +++ b/drivers/usb/serial/cyberjack.c Thu Mar 7 18:17:46 2002 @@ -151,8 +151,6 @@ dbg(__FUNCTION__ " - port %d", port->number); - down (&port->sem); - ++port->open_count; if (port->open_count == 1) { @@ -179,8 +177,6 @@ dbg(__FUNCTION__ " - usb_submit_urb(int urb)"); } - up (&port->sem); - return result; } @@ -188,8 +184,6 @@ { dbg(__FUNCTION__ " - port %d", port->number); - down (&port->sem); - --port->open_count; if (port->open_count <= 0) { @@ -201,8 +195,6 @@ } port->open_count = 0; } - - up (&port->sem); } static int cyberjack_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) @@ -225,8 +217,6 @@ return (0); } - down (&port->sem); - if( (count+priv->wrfilled)>sizeof(priv->wrbuf) ) { /* To much data for buffer. Reset buffer. */ priv->wrfilled=0; @@ -235,8 +225,9 @@ /* Copy data */ if (from_user) { - if (copy_from_user(priv->wrbuf+priv->wrfilled, buf, count)) + if (copy_from_user(priv->wrbuf+priv->wrfilled, buf, count)) { return -EFAULT; + } } else { memcpy (priv->wrbuf+priv->wrfilled, buf, count); } @@ -277,7 +268,6 @@ /* Throw away data. No better idea what to do with it. */ priv->wrfilled=0; priv->wrsent=0; - up (&port->sem); return 0; } @@ -292,7 +282,6 @@ } } - up (&port->sem); return (count); } @@ -432,8 +421,6 @@ return; } - down (&port->sem); - dbg(__FUNCTION__ " - transmitting data (frame n)"); length = ((priv->wrfilled - priv->wrsent) > port->bulk_out_size) ? @@ -459,7 +446,6 @@ /* Throw away data. No better idea what to do with it. */ priv->wrfilled=0; priv->wrsent=0; - up (&port->sem); queue_task(&port->tqueue, &tq_immediate); mark_bh(IMMEDIATE_BH); return; @@ -477,7 +463,6 @@ priv->wrsent=0; } - up (&port->sem); queue_task(&port->tqueue, &tq_immediate); mark_bh(IMMEDIATE_BH); return; diff -Nru a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c --- a/drivers/usb/serial/empeg.c Thu Mar 7 18:17:44 2002 +++ b/drivers/usb/serial/empeg.c Thu Mar 7 18:17:44 2002 @@ -157,8 +157,6 @@ dbg(__FUNCTION__ " - port %d", port->number); - down (&port->sem); - ++port->open_count; if (port->open_count == 1) { @@ -189,8 +187,6 @@ } - up (&port->sem); - return result; } @@ -208,8 +204,6 @@ if (!serial) return; - down (&port->sem); - --port->open_count; if (port->open_count <= 0) { @@ -220,8 +214,6 @@ port->open_count = 0; } - up (&port->sem); - /* Uncomment the following line if you want to see some statistics in your syslog */ /* info ("Bytes In = %d Bytes Out = %d", bytes_in, bytes_out); */ } @@ -462,15 +454,7 @@ static void empeg_throttle (struct usb_serial_port *port) { dbg(__FUNCTION__ " - port %d", port->number); - - down (&port->sem); - usb_unlink_urb (port->read_urb); - - up (&port->sem); - - return; - } @@ -480,8 +464,6 @@ dbg(__FUNCTION__ " - port %d", port->number); - down (&port->sem); - port->read_urb->dev = port->serial->dev; result = usb_submit_urb(port->read_urb, GFP_KERNEL); @@ -489,10 +471,7 @@ if (result) err(__FUNCTION__ " - failed submitting read urb, error %d", result); - up (&port->sem); - return; - } diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c --- a/drivers/usb/serial/ftdi_sio.c Thu Mar 7 18:17:40 2002 +++ b/drivers/usb/serial/ftdi_sio.c Thu Mar 7 18:17:40 2002 @@ -319,8 +319,6 @@ dbg(__FUNCTION__); - down (&port->sem); - ++port->open_count; if (port->open_count == 1){ @@ -361,7 +359,6 @@ err(__FUNCTION__ " - failed submitting read urb, error %d", result); } - up (&port->sem); return result; } /* ftdi_sio_open */ @@ -374,7 +371,6 @@ dbg( __FUNCTION__); - down (&port->sem); --port->open_count; if (port->open_count <= 0) { @@ -411,9 +407,6 @@ tty_hangup(port->tty); } } - - up (&port->sem); - } /* ftdi_sio_close */ @@ -447,8 +440,6 @@ return (0); } - down(&port->sem); - count += data_offset; count = (count > port->bulk_out_size) ? port->bulk_out_size : count; @@ -456,7 +447,6 @@ if (from_user) { if (copy_from_user(port->write_urb->transfer_buffer + data_offset, buf, count - data_offset )){ - up (&port->sem); return -EFAULT; } } else { @@ -482,14 +472,11 @@ result = usb_submit_urb(port->write_urb, GFP_KERNEL); if (result) { err(__FUNCTION__ " - failed submitting write urb, error %d", result); - up (&port->sem); return 0; } - up (&port->sem); dbg(__FUNCTION__ " write returning: %d", count - data_offset); return (count - data_offset); - } /* ftdi_sio_write */ static void ftdi_sio_write_bulk_callback (struct urb *urb) diff -Nru a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c --- a/drivers/usb/serial/ipaq.c Thu Mar 7 18:17:41 2002 +++ b/drivers/usb/serial/ipaq.c Thu Mar 7 18:17:41 2002 @@ -1,7 +1,7 @@ /* * USB Compaq iPAQ driver * - * Copyright (C) 2001 + * Copyright (C) 2001 - 2002 * Ganesh Varadarajan * * This program is free software; you can redistribute it and/or modify @@ -9,6 +9,9 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * + * (25/2/2002) ganesh + * Added support for the HP Jornada 548 and 568. Completely untested. + * Thanks to info from Heath Robinson and Arieh Davidoff. */ #include @@ -39,9 +42,9 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.1" +#define DRIVER_VERSION "v0.2" #define DRIVER_AUTHOR "Ganesh Varadarajan " -#define DRIVER_DESC "USB Compaq iPAQ driver" +#define DRIVER_DESC "USB Compaq iPAQ, HP Jornada driver" /* Function prototypes for an ipaq */ static int ipaq_open (struct usb_serial_port *port, struct file *filp); @@ -61,7 +64,9 @@ static __devinitdata struct usb_device_id ipaq_id_table [] = { - { USB_DEVICE(IPAQ_VENDOR_ID, IPAQ_PRODUCT_ID) }, + { USB_DEVICE(COMPAQ_VENDOR_ID, COMPAQ_IPAQ_ID) }, + { USB_DEVICE(HP_VENDOR_ID, HP_JORNADA_548_ID) }, + { USB_DEVICE(HP_VENDOR_ID, HP_JORNADA_568_ID) }, { } /* Terminating entry */ }; @@ -72,7 +77,7 @@ owner: THIS_MODULE, name: "Compaq iPAQ", id_table: ipaq_id_table, - num_interrupt_in: 0, + num_interrupt_in: NUM_DONT_CARE, num_bulk_in: 1, num_bulk_out: 1, num_ports: 1, @@ -104,8 +109,6 @@ dbg(__FUNCTION__ " - port %d", port->number); - down(&port->sem); - ++port->open_count; if (port->open_count == 1) { @@ -193,8 +196,6 @@ } } - up(&port->sem); - return result; enomem: @@ -219,8 +220,6 @@ serial = get_usb_serial(port, __FUNCTION__); if (!serial) return; - - down (&port->sem); --port->open_count; @@ -238,8 +237,6 @@ port->open_count = 0; } - up (&port->sem); - /* Uncomment the following line if you want to see some statistics in your syslog */ /* info ("Bytes In = %d Bytes Out = %d", bytes_in, bytes_out); */ } diff -Nru a/drivers/usb/serial/ipaq.h b/drivers/usb/serial/ipaq.h --- a/drivers/usb/serial/ipaq.h Thu Mar 7 18:17:37 2002 +++ b/drivers/usb/serial/ipaq.h Thu Mar 7 18:17:37 2002 @@ -1,7 +1,7 @@ /* * USB Compaq iPAQ driver * - * Copyright (C) 2001 + * Copyright (C) 2001 - 2002 * Ganesh Varadarajan * * This program is free software; you can redistribute it and/or modify @@ -16,8 +16,12 @@ #define __LINUX_USB_SERIAL_IPAQ_H -#define IPAQ_VENDOR_ID 0x049f -#define IPAQ_PRODUCT_ID 0x0003 +#define COMPAQ_VENDOR_ID 0x049f +#define COMPAQ_IPAQ_ID 0x0003 + +#define HP_VENDOR_ID 0x003f +#define HP_JORNADA_548_ID 0x1016 +#define HP_JORNADA_568_ID 0x1116 /* * Since we can't queue our bulk write urbs (don't know why - it just diff -Nru a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c --- a/drivers/usb/serial/ir-usb.c Thu Mar 7 18:17:44 2002 +++ b/drivers/usb/serial/ir-usb.c Thu Mar 7 18:17:44 2002 @@ -1,8 +1,8 @@ /* * USB IR Dongle driver * - * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (C) 2002 Gary Brubaker (xavyer@ix.netcom.com) + * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2002 Gary Brubaker (xavyer@ix.netcom.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 @@ -21,6 +21,11 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * 2002_Mar_07 greg kh + * moved some needed structures and #define values from the + * net/irda/irda-usb.h file into our file, as we don't want to depend on + * that codebase compiling correctly :) + * * 2002_Jan_14 gb * Added module parameter to force specific number of XBOFs. * Added ir_xbof_change(). @@ -56,7 +61,6 @@ #include #include #include -#include #ifdef CONFIG_USB_SERIAL_DEBUG static int debug = 1; @@ -73,6 +77,33 @@ #define DRIVER_AUTHOR "Greg Kroah-Hartman " #define DRIVER_DESC "USB IR Dongle driver" +/* USB IrDA class spec information */ +#define USB_CLASS_IRDA 0x02 +#define USB_DT_IRDA 0x21 +#define IU_REQ_GET_CLASS_DESC 0x06 +#define SPEED_2400 0x01 +#define SPEED_9600 0x02 +#define SPEED_19200 0x03 +#define SPEED_38400 0x04 +#define SPEED_57600 0x05 +#define SPEED_115200 0x06 +#define SPEED_576000 0x07 +#define SPEED_1152000 0x08 +#define SPEED_4000000 0x09 + +struct irda_class_desc { + u8 bLength; + u8 bDescriptorType; + u16 bcdSpecRevision; + u8 bmDataSize; + u8 bmWindowSize; + u8 bmMinTurnaroundTime; + u16 wBaudRate; + u8 bmAdditionalBOFs; + u8 bIrdaRateSniff; + u8 bMaxUnicastList; +} __attribute__ ((packed)); + /* if overridden by the user, then use their value for the size of the read and * write urbs */ static int buffer_size = 0; @@ -158,7 +189,7 @@ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0), IU_REQ_GET_CLASS_DESC, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, ifnum, desc, sizeof(*desc), MSECS_TO_JIFFIES(500)); + 0, ifnum, desc, sizeof(*desc), HZ); dbg("%s - ret=%d", __FUNCTION__, ret); if (ret < sizeof(*desc)) { @@ -252,8 +283,6 @@ dbg("%s - port %d", __FUNCTION__, port->number); - down (&port->sem); - ++port->open_count; if (port->open_count == 1) { @@ -293,9 +322,6 @@ if (result) err("%s - failed submitting read urb, error %d", __FUNCTION__, result); } - - up (&port->sem); - return result; } @@ -312,8 +338,6 @@ if (!serial) return; - down (&port->sem); - --port->open_count; if (port->open_count <= 0) { @@ -324,7 +348,6 @@ port->open_count = 0; } - up (&port->sem); } static int ir_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) diff -Nru a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c --- a/drivers/usb/serial/keyspan.c Thu Mar 7 18:17:38 2002 +++ b/drivers/usb/serial/keyspan.c Thu Mar 7 18:17:38 2002 @@ -861,10 +861,8 @@ dbg("keyspan_open called for port%d.\n", port->number); - down (&port->sem); already_active = port->open_count; ++port->open_count; - up (&port->sem); if (already_active) return 0; @@ -926,8 +924,6 @@ p_priv->out_flip = 0; p_priv->in_flip = 0; - down (&port->sem); - if (--port->open_count <= 0) { if (serial->dev) { /* Stop reading/writing urbs */ @@ -941,7 +937,6 @@ port->open_count = 0; port->tty = 0; } - up (&port->sem); } diff -Nru a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c --- a/drivers/usb/serial/keyspan_pda.c Thu Mar 7 18:17:45 2002 +++ b/drivers/usb/serial/keyspan_pda.c Thu Mar 7 18:17:45 2002 @@ -662,8 +662,6 @@ int rc = 0; struct keyspan_pda_private *priv; - down (&port->sem); - ++port->open_count; if (port->open_count == 1) { @@ -707,12 +705,9 @@ } - - up (&port->sem); return rc; error: --port->open_count; - up (&port->sem); return rc; } @@ -721,8 +716,6 @@ { struct usb_serial *serial = port->serial; - down (&port->sem); - --port->open_count; if (port->open_count <= 0) { @@ -737,8 +730,6 @@ } port->open_count = 0; } - - up (&port->sem); } diff -Nru a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c --- a/drivers/usb/serial/kl5kusb105.c Thu Mar 7 18:17:36 2002 +++ b/drivers/usb/serial/kl5kusb105.c Thu Mar 7 18:17:36 2002 @@ -358,8 +358,6 @@ dbg(__FUNCTION__" port %d", port->number); - down (&port->sem); - ++port->open_count; if (port->open_count == 1) { @@ -437,8 +435,6 @@ } exit: - up (&port->sem); - return retval; } /* klsi_105_open */ @@ -455,8 +451,6 @@ if(!serial) return; - down (&port->sem); - --port->open_count; if (port->open_count <= 0) { @@ -482,8 +476,6 @@ port->open_count = 0; info("kl5kusb105 port stats: %ld bytes in, %ld bytes out", priv->bytes_in, priv->bytes_out); } - - up (&port->sem); } /* klsi_105_close */ @@ -505,9 +497,6 @@ dbg(__FUNCTION__ " - port %d", port->number); - down (&port->sem); /* to lock against someone else trying to - take an URB we just selected from the pool */ - while (count > 0) { /* try to find a free urb (write 0 bytes if none) */ struct urb *urb = NULL; @@ -543,7 +532,6 @@ if (from_user) { if (copy_from_user(urb->transfer_buffer + KLSI_105_DATA_OFFSET, buf, size)) { - up (&port->sem); return -EFAULT; } } else { @@ -578,7 +566,6 @@ count -= size; } exit: - up (&port->sem); priv->bytes_out+=bytes_sent; return bytes_sent; /* that's how much we wrote */ @@ -1021,34 +1008,21 @@ static void klsi_105_throttle (struct usb_serial_port *port) { - dbg(__FUNCTION__ " - port %d", port->number); - - down (&port->sem); - usb_unlink_urb (port->read_urb); - - up (&port->sem); - - return; } + static void klsi_105_unthrottle (struct usb_serial_port *port) { int result; dbg(__FUNCTION__ " - port %d", port->number); - down (&port->sem); - port->read_urb->dev = port->serial->dev; result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) err(__FUNCTION__ " - failed submitting read urb, error %d", result); - - up (&port->sem); - - return; } diff -Nru a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c --- a/drivers/usb/serial/mct_u232.c Thu Mar 7 18:17:45 2002 +++ b/drivers/usb/serial/mct_u232.c Thu Mar 7 18:17:45 2002 @@ -341,8 +341,6 @@ dbg(__FUNCTION__" port %d", port->number); - down (&port->sem); - ++port->open_count; if (port->open_count == 1) { @@ -398,8 +396,6 @@ } exit: - up (&port->sem); - return 0; } /* mct_u232_open */ @@ -408,8 +404,6 @@ { dbg(__FUNCTION__" port %d", port->number); - down (&port->sem); - --port->open_count; if (port->open_count <= 0) { @@ -421,8 +415,6 @@ } port->open_count = 0; } - - up (&port->sem); } /* mct_u232_close */ @@ -454,16 +446,12 @@ bytes_sent = 0; while (count > 0) { - - down (&port->sem); - size = (count > port->bulk_out_size) ? port->bulk_out_size : count; usb_serial_debug_data (__FILE__, __FUNCTION__, size, buf); if (from_user) { if (copy_from_user(port->write_urb->transfer_buffer, buf, size)) { - up (&port->sem); return -EFAULT; } } @@ -486,11 +474,8 @@ if (result) { err(__FUNCTION__ " - failed submitting write urb, error %d", result); - up (&port->sem); return result; } - - up (&port->sem); bytes_sent += size; if (write_blocking) diff -Nru a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c --- a/drivers/usb/serial/omninet.c Thu Mar 7 18:17:41 2002 +++ b/drivers/usb/serial/omninet.c Thu Mar 7 18:17:41 2002 @@ -157,8 +157,6 @@ if (!serial) return -ENODEV; - down (&port->sem); - ++port->open_count; if (port->open_count == 1) { @@ -166,7 +164,6 @@ if( !od ) { err(__FUNCTION__"- kmalloc(%Zd) failed.", sizeof(struct omninet_data)); port->open_count = 0; - up (&port->sem); return -ENOMEM; } @@ -184,8 +181,6 @@ err(__FUNCTION__ " - failed submitting read urb, error %d", result); } - up (&port->sem); - return result; } @@ -204,8 +199,6 @@ if (!serial) return; - down (&port->sem); - --port->open_count; if (port->open_count <= 0) { @@ -220,8 +213,6 @@ if (od) kfree(od); } - - up (&port->sem); } diff -Nru a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c --- a/drivers/usb/serial/pl2303.c Thu Mar 7 18:17:44 2002 +++ b/drivers/usb/serial/pl2303.c Thu Mar 7 18:17:44 2002 @@ -367,8 +367,6 @@ dbg (__FUNCTION__ " - port %d", port->number); - down (&port->sem); - ++port->open_count; if (port->open_count == 1) { @@ -407,7 +405,6 @@ result = usb_submit_urb (port->read_urb, GFP_KERNEL); if (result) { err(__FUNCTION__ " - failed submitting read urb, error %d", result); - up (&port->sem); pl2303_close (port, NULL); return -EPROTO; } @@ -417,12 +414,10 @@ result = usb_submit_urb (port->interrupt_in_urb, GFP_KERNEL); if (result) { err(__FUNCTION__ " - failed submitting interrupt urb, error %d", result); - up (&port->sem); pl2303_close (port, NULL); return -EPROTO; } } - up (&port->sem); return 0; } @@ -442,8 +437,6 @@ dbg (__FUNCTION__ " - port %d", port->number); - down (&port->sem); - --port->open_count; if (port->open_count <= 0) { if (serial->dev) { @@ -478,8 +471,6 @@ } port->open_count = 0; } - - up (&port->sem); } static int set_modem_info (struct usb_serial_port *port, unsigned int cmd, unsigned int *value) diff -Nru a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c --- a/drivers/usb/serial/usbserial.c Thu Mar 7 18:17:43 2002 +++ b/drivers/usb/serial/usbserial.c Thu Mar 7 18:17:43 2002 @@ -1,7 +1,7 @@ /* * USB Serial Converter driver * - * Copyright (C) 1999 - 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) * Copyright (c) 2000 Peter Berger (pberger@brimson.com) * Copyright (c) 2000 Al Borchers (borchers@steinerpoint.com) * @@ -15,6 +15,13 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (02/26/2002) gkh + * Moved all locking into the main serial_* functions, instead of having + * the individual drivers have to grab the port semaphore. This should + * reduce races. + * Reworked the MOD_INC logic a bit to always increment and decrement, even + * if the generic driver is being used. + * * (10/10/2001) gkh * usb_serial_disconnect() now sets the serial->dev pointer is to NULL to * help prevent child drivers from accessing the device since it is now @@ -337,6 +344,7 @@ /* All of the device info needed for the Generic Serial Converter */ static struct usb_serial_device_type generic_device = { + owner: THIS_MODULE, name: "Generic", id_table: generic_device_ids, num_interrupt_in: NUM_DONT_CARE, @@ -345,13 +353,6 @@ num_ports: 1, shutdown: generic_shutdown, }; - -#define if_generic_do(x) \ - if ((serial->vendor == vendor) && \ - (serial->product == product)) \ - x -#else -#define if_generic_do(x) #endif @@ -384,7 +385,7 @@ via modprobe, and modprobe will load usbserial because the serial drivers depend on it. */ - + static int serial_refcount; static struct tty_driver serial_tty_driver; @@ -392,8 +393,6 @@ static struct termios * serial_termios[SERIAL_TTY_MINORS]; static struct termios * serial_termios_locked[SERIAL_TTY_MINORS]; static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; /* initially all NULL */ - - static LIST_HEAD(usb_serial_driver_list); @@ -402,7 +401,6 @@ return serial_table[minor]; } - static struct usb_serial *get_free_serial (int num_ports, unsigned int *minor) { struct usb_serial *serial = NULL; @@ -439,7 +437,6 @@ return NULL; } - static void return_serial (struct usb_serial *serial) { int i; @@ -456,7 +453,6 @@ return; } - #ifdef USES_EZUSB_FUNCTIONS /* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */ #define CPUCS_REG 0x7F92 @@ -483,7 +479,6 @@ return result; } - int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit) { int response; @@ -497,7 +492,6 @@ #endif /* USES_EZUSB_FUNCTIONS */ - /***************************************************************************** * Driver tty interface functions *****************************************************************************/ @@ -516,266 +510,293 @@ /* get the serial object associated with this tty pointer */ serial = get_serial_by_minor (minor(tty->device)); - if (serial_paranoia_check (serial, __FUNCTION__)) { + if (serial_paranoia_check (serial, __FUNCTION__)) return -ENODEV; - } /* set up our port structure making the tty driver remember our port object, and us it */ portNumber = minor(tty->device) - serial->minor; port = &serial->port[portNumber]; tty->driver_data = port; + + down (&port->sem); port->tty = tty; + /* lock this module before we call it */ + if (serial->type->owner) + __MOD_INC_USE_COUNT(serial->type->owner); + /* pass on to the driver specific version of this function if it is available */ - if (serial->type->open) { - if (serial->type->owner) - __MOD_INC_USE_COUNT(serial->type->owner); + if (serial->type->open) retval = serial->type->open(port, filp); - if (retval) - __MOD_DEC_USE_COUNT(serial->type->owner); - } else { + else retval = generic_open(port, filp); - } + if (retval) + __MOD_DEC_USE_COUNT(serial->type->owner); + + up (&port->sem); return retval; } - static void serial_close(struct tty_struct *tty, struct file * filp) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); - if (!serial) { + if (!serial) return; - } + + down (&port->sem); dbg(__FUNCTION__ " - port %d", port->number); - + + if (tty->driver_data == NULL) { + /* disconnect beat us to the punch here, so handle it gracefully */ + goto exit; + } if (!port->open_count) { dbg (__FUNCTION__ " - port not opened"); - return; + goto exit_no_mod_dec; } /* pass on to the driver specific version of this function if it is available */ - if (serial->type->close) { + if (serial->type->close) serial->type->close(port, filp); - if (serial->type->owner) - __MOD_DEC_USE_COUNT(serial->type->owner); - } else { + else generic_close(port, filp); - } -} +exit: + if (serial->type->owner) + __MOD_DEC_USE_COUNT(serial->type->owner); + +exit_no_mod_dec: + up (&port->sem); +} static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); - - if (!serial) { + int retval = -EINVAL; + + if (!serial) return -ENODEV; - } - + + down (&port->sem); + dbg(__FUNCTION__ " - port %d, %d byte(s)", port->number, count); if (!port->open_count) { dbg (__FUNCTION__ " - port not opened"); - return -EINVAL; + goto exit; } - + /* pass on to the driver specific version of this function if it is available */ - if (serial->type->write) { - return (serial->type->write(port, from_user, buf, count)); - } else { - return (generic_write(port, from_user, buf, count)); - } -} + if (serial->type->write) + retval = serial->type->write(port, from_user, buf, count); + else + retval = generic_write(port, from_user, buf, count); +exit: + up (&port->sem); + return retval; +} static int serial_write_room (struct tty_struct *tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); + int retval = -EINVAL; - if (!serial) { + if (!serial) return -ENODEV; - } + + down (&port->sem); dbg(__FUNCTION__ " - port %d", port->number); - + if (!port->open_count) { dbg (__FUNCTION__ " - port not open"); - return -EINVAL; + goto exit; } /* pass on to the driver specific version of this function if it is available */ - if (serial->type->write_room) { - return (serial->type->write_room(port)); - } else { - return (generic_write_room(port)); - } -} + if (serial->type->write_room) + retval = serial->type->write_room(port); + else + retval = generic_write_room(port); +exit: + up (&port->sem); + return retval; +} static int serial_chars_in_buffer (struct tty_struct *tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); + int retval = -EINVAL; - if (!serial) { + if (!serial) return -ENODEV; - } + + down (&port->sem); + + dbg(__FUNCTION__ " - port %d", port->number); if (!port->open_count) { dbg (__FUNCTION__ " - port not open"); - return -EINVAL; + goto exit; } /* pass on to the driver specific version of this function if it is available */ - if (serial->type->chars_in_buffer) { - return (serial->type->chars_in_buffer(port)); - } else { - return (generic_chars_in_buffer(port)); - } -} + if (serial->type->chars_in_buffer) + retval = serial->type->chars_in_buffer(port); + else + retval = generic_chars_in_buffer(port); +exit: + up (&port->sem); + return retval; +} static void serial_throttle (struct tty_struct * tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); - if (!serial) { + if (!serial) return; - } + + down (&port->sem); dbg(__FUNCTION__ " - port %d", port->number); if (!port->open_count) { dbg (__FUNCTION__ " - port not open"); - return; + goto exit; } /* pass on to the driver specific version of this function */ - if (serial->type->throttle) { + if (serial->type->throttle) serial->type->throttle(port); - } - return; +exit: + up (&port->sem); } - static void serial_unthrottle (struct tty_struct * tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); - if (!serial) { + if (!serial) return; - } + + down (&port->sem); dbg(__FUNCTION__ " - port %d", port->number); if (!port->open_count) { dbg (__FUNCTION__ " - port not open"); - return; + goto exit; } /* pass on to the driver specific version of this function */ - if (serial->type->unthrottle) { + if (serial->type->unthrottle) serial->type->unthrottle(port); - } - return; +exit: + up (&port->sem); } - static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); + int retval = -ENODEV; - if (!serial) { + if (!serial) return -ENODEV; - } + + down (&port->sem); dbg(__FUNCTION__ " - port %d, cmd 0x%.4x", port->number, cmd); if (!port->open_count) { dbg (__FUNCTION__ " - port not open"); - return -ENODEV; + goto exit; } /* pass on to the driver specific version of this function if it is available */ - if (serial->type->ioctl) { - return (serial->type->ioctl(port, file, cmd, arg)); - } else { - return -ENOIOCTLCMD; - } -} + if (serial->type->ioctl) + retval = serial->type->ioctl(port, file, cmd, arg); + else + retval = -ENOIOCTLCMD; +exit: + up (&port->sem); + return retval; +} static void serial_set_termios (struct tty_struct *tty, struct termios * old) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); - if (!serial) { + if (!serial) return; - } + + down (&port->sem); dbg(__FUNCTION__ " - port %d", port->number); if (!port->open_count) { dbg (__FUNCTION__ " - port not open"); - return; + goto exit; } /* pass on to the driver specific version of this function if it is available */ - if (serial->type->set_termios) { + if (serial->type->set_termios) serial->type->set_termios(port, old); - } - - return; -} +exit: + up (&port->sem); +} static void serial_break (struct tty_struct *tty, int break_state) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); - if (!serial) { + if (!serial) return; - } + + down (&port->sem); dbg(__FUNCTION__ " - port %d", port->number); if (!port->open_count) { dbg (__FUNCTION__ " - port not open"); - return; + goto exit; } - /* pass on to the driver specific version of this function if it is - available */ - if (serial->type->break_ctl) { + /* pass on to the driver specific version of this function if it is available */ + if (serial->type->break_ctl) serial->type->break_ctl(port, break_state); - } -} +exit: + up (&port->sem); +} static void serial_shutdown (struct usb_serial *serial) { - if (serial->type->shutdown) { + if (serial->type->shutdown) serial->type->shutdown(serial); - } else { + else generic_shutdown(serial); - } } - - /***************************************************************************** * generic devices specific driver functions *****************************************************************************/ @@ -787,21 +808,16 @@ if (port_paranoia_check (port, __FUNCTION__)) return -ENODEV; - /* only increment our usage count, if this device is _really_ a generic device */ - if_generic_do(MOD_INC_USE_COUNT); - dbg(__FUNCTION__ " - port %d", port->number); - down (&port->sem); - ++port->open_count; - + if (port->open_count == 1) { /* force low_latency on so that our tty_push actually forces the data through, otherwise it is scheduled, and with high data rates (like with OHCI) data can get lost. */ port->tty->low_latency = 1; - + /* if we have a bulk interrupt, start reading from it */ if (serial->num_bulk_in) { /* Start reading from the device */ @@ -818,21 +834,16 @@ err(__FUNCTION__ " - failed resubmitting read urb, error %d", result); } } - - up (&port->sem); - + return result; } - static void generic_close (struct usb_serial_port *port, struct file * filp) { struct usb_serial *serial = port->serial; dbg(__FUNCTION__ " - port %d", port->number); - down (&port->sem); - --port->open_count; if (port->open_count <= 0) { @@ -845,14 +856,8 @@ } port->open_count = 0; } - - up (&port->sem); - - /* only decrement our usage count, if this device is _really_ a generic device */ - if_generic_do(MOD_DEC_USE_COUNT); } - static int generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; @@ -880,14 +885,14 @@ } else { memcpy (port->write_urb->transfer_buffer, buf, count); - } + } usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer); /* set up our urb */ usb_fill_bulk_urb (port->write_urb, serial->dev, usb_sndbulkpipe (serial->dev, - port->bulk_out_endpointAddress), + port->bulk_out_endpointAddress), port->write_urb->transfer_buffer, count, ((serial->type->write_bulk_callback) ? serial->type->write_bulk_callback : @@ -902,11 +907,10 @@ return result; } - + /* no bulk out, so return 0 bytes written */ return (0); -} - +} static int generic_write_room (struct usb_serial_port *port) { @@ -919,19 +923,18 @@ if (port->write_urb->status != -EINPROGRESS) room = port->bulk_out_size; } - + dbg(__FUNCTION__ " - returns %d", room); return (room); } - static int generic_chars_in_buffer (struct usb_serial_port *port) { struct usb_serial *serial = port->serial; int chars = 0; dbg(__FUNCTION__ " - port %d", port->number); - + if (serial->num_bulk_out) { if (port->write_urb->status == -EINPROGRESS) chars = port->write_urb->transfer_buffer_length; @@ -941,7 +944,6 @@ return (chars); } - static void generic_read_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; @@ -952,7 +954,7 @@ int result; dbg(__FUNCTION__ " - port %d", port->number); - + if (!serial) { dbg(__FUNCTION__ " - bad serial pointer, exiting"); return; @@ -992,14 +994,13 @@ err(__FUNCTION__ " - failed resubmitting read urb, error %d", result); } - static void generic_write_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); dbg(__FUNCTION__ " - port %d", port->number); - + if (!serial) { dbg(__FUNCTION__ " - bad serial pointer, exiting"); return; @@ -1012,11 +1013,10 @@ queue_task(&port->tqueue, &tq_immediate); mark_bh(IMMEDIATE_BH); - + return; } - static void generic_shutdown (struct usb_serial *serial) { int i; @@ -1025,13 +1025,13 @@ /* stop reads and writes on all ports */ for (i=0; i < serial->num_ports; ++i) { - while (serial->port[i].open_count > 0) { + down (&serial->port[i].sem); + while (serial->port[i].open_count > 0) generic_close (&serial->port[i], NULL); - } + up (&serial->port[i].sem); } } - static void port_softint(void *private) { struct usb_serial_port *port = (struct usb_serial_port *)private; @@ -1043,7 +1043,7 @@ if (!serial) { return; } - + tty = port->tty; if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { dbg(__FUNCTION__ " - write wakeup call."); @@ -1053,8 +1053,6 @@ wake_up_interruptible(&tty->write_wait); } - - static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id) { @@ -1080,7 +1078,6 @@ int max_endpoints; const struct usb_device_id *id_pattern = NULL; - /* loop through our list of known serial converters, and see if this device matches. */ found = 0; @@ -1130,7 +1127,7 @@ ++num_interrupt_in; } } - + #if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE) /* BEGIN HORRIBLE HACK FOR PL2303 */ /* this is needed due to the looney way its endpoints are set up */ @@ -1156,7 +1153,7 @@ } /* END HORRIBLE HACK FOR PL2303 */ #endif - + /* found all that we need */ info("%s converter detected", type->name); @@ -1176,7 +1173,7 @@ err("No more free serial devices"); return NULL; } - + serial->dev = dev; serial->type = type; serial->interface = interface; @@ -1289,14 +1286,14 @@ port->tqueue.data = port; init_MUTEX (&port->sem); } - + /* initialize the devfs nodes for this device and let the user know what ports we are bound to */ for (i = 0; i < serial->num_ports; ++i) { tty_register_devfs (&serial_tty_driver, 0, serial->port[i].number); info("%s converter now attached to ttyUSB%d (or usb/tts/%d for devfs)", type->name, serial->port[i].number, serial->port[i].number); } - + return serial; /* success */ @@ -1322,7 +1319,7 @@ if (port->interrupt_in_buffer) kfree (port->interrupt_in_buffer); } - + /* return the minor range that this device had */ return_serial (serial); @@ -1331,7 +1328,6 @@ return NULL; } - static void usb_serial_disconnect(struct usb_device *dev, void *ptr) { struct usb_serial *serial = (struct usb_serial *) ptr; @@ -1341,8 +1337,10 @@ if (serial) { /* fail all future close/read/write/ioctl/etc calls */ for (i = 0; i < serial->num_ports; ++i) { + down (&serial->port[i].sem); if (serial->port[i].tty != NULL) serial->port[i].tty->driver_data = NULL; + up (&serial->port[i].sem); } serial->dev = NULL; @@ -1393,7 +1391,7 @@ } else { info("device disconnected"); } - + } @@ -1407,12 +1405,12 @@ type: TTY_DRIVER_TYPE_SERIAL, subtype: SERIAL_TYPE_NORMAL, flags: TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, - + refcount: &serial_refcount, table: serial_tty, termios: serial_termios, termios_locked: serial_termios_locked, - + open: serial_open, close: serial_close, write: serial_write, diff -Nru a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c --- a/drivers/usb/serial/visor.c Thu Mar 7 18:17:41 2002 +++ b/drivers/usb/serial/visor.c Thu Mar 7 18:17:41 2002 @@ -12,6 +12,15 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (02/27/2002) gkh + * Reworked the urb handling logic. We have no more pool, but dynamically + * allocate the urb and the transfer buffer on the fly. In testing this + * does not incure any measurable overhead. This also relies on the fact + * that we have proper reference counting logic for urbs. + * + * (02/21/2002) SilaS + * Added initial support for the Palm m515 devices. + * * (02/14/2002) gkh * Added support for the Clie S-360 device. * @@ -134,7 +143,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.9" +#define DRIVER_VERSION "v2.0" #define DRIVER_AUTHOR "Greg Kroah-Hartman " #define DRIVER_DESC "USB HandSpring Visor, Palm m50x, Sony Clié driver" @@ -158,6 +167,7 @@ static __devinitdata struct usb_device_id combined_id_table [] = { { USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) }, + { USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) }, { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) }, @@ -174,6 +184,7 @@ { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) }, + { USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) }, @@ -233,12 +244,8 @@ }; -#define NUM_URBS 24 -#define URB_TRANSFER_BUFFER_SIZE 768 -static struct urb *write_urb_pool[NUM_URBS]; -static spinlock_t write_urb_pool_lock; -static int bytes_in; -static int bytes_out; +static int bytes_in; +static int bytes_out; /****************************************************************************** @@ -259,8 +266,6 @@ return -ENODEV; } - down (&port->sem); - ++port->open_count; if (port->open_count == 1) { @@ -284,8 +289,6 @@ err(__FUNCTION__ " - failed submitting read urb, error %d", result); } - up (&port->sem); - return result; } @@ -304,8 +307,6 @@ if (!serial) return; - down (&port->sem); - --port->open_count; if (port->open_count <= 0) { @@ -329,8 +330,6 @@ } port->open_count = 0; } - up (&port->sem); - /* Uncomment the following line if you want to see some statistics in your syslog */ /* info ("Bytes In = %d Bytes Out = %d", bytes_in, bytes_out); */ } @@ -340,120 +339,84 @@ { struct usb_serial *serial = port->serial; struct urb *urb; - const unsigned char *current_position = buf; - unsigned long flags; + unsigned char *buffer; int status; - int i; - int bytes_sent = 0; - int transfer_size; dbg(__FUNCTION__ " - port %d", port->number); - while (count > 0) { - /* try to find a free urb in our list of them */ - urb = NULL; - spin_lock_irqsave (&write_urb_pool_lock, flags); - for (i = 0; i < NUM_URBS; ++i) { - if (write_urb_pool[i]->status != -EINPROGRESS) { - urb = write_urb_pool[i]; - break; - } - } - spin_unlock_irqrestore (&write_urb_pool_lock, flags); - if (urb == NULL) { - dbg (__FUNCTION__ " - no more free urbs"); - goto exit; - } - if (urb->transfer_buffer == NULL) { - urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); - if (urb->transfer_buffer == NULL) { - err(__FUNCTION__" no more kernel memory..."); - goto exit; - } - } - - transfer_size = min (count, URB_TRANSFER_BUFFER_SIZE); - if (from_user) { - if (copy_from_user (urb->transfer_buffer, current_position, transfer_size)) { - bytes_sent = -EFAULT; - break; - } - } else { - memcpy (urb->transfer_buffer, current_position, transfer_size); - } + buffer = kmalloc (count, GFP_KERNEL); + if (!buffer) { + err ("out of memory"); + return -ENOMEM; + } - usb_serial_debug_data (__FILE__, __FUNCTION__, transfer_size, urb->transfer_buffer); + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err ("no more free urbs"); + kfree (buffer); + return -ENOMEM; + } - /* build up our urb */ - usb_fill_bulk_urb (urb, serial->dev, - usb_sndbulkpipe (serial->dev, - port->bulk_out_endpointAddress), - urb->transfer_buffer, transfer_size, - visor_write_bulk_callback, port); - urb->transfer_flags |= USB_QUEUE_BULK; - - /* send it down the pipe */ - status = usb_submit_urb(urb, GFP_KERNEL); - if (status) { - err(__FUNCTION__ " - usb_submit_urb(write bulk) failed with status = %d", status); - bytes_sent = status; - break; + if (from_user) { + if (copy_from_user (buffer, buf, count)) { + kfree (buffer); + usb_free_urb (urb); + return -EFAULT; } + } else { + memcpy (buffer, buf, count); + } + + usb_serial_debug_data (__FILE__, __FUNCTION__, count, buffer); - current_position += transfer_size; - bytes_sent += transfer_size; - count -= transfer_size; - bytes_out += transfer_size; + usb_fill_bulk_urb (urb, serial->dev, + usb_sndbulkpipe (serial->dev, + port->bulk_out_endpointAddress), + buffer, count, + visor_write_bulk_callback, port); + urb->transfer_flags |= USB_QUEUE_BULK; + + /* send it down the pipe */ + status = usb_submit_urb(urb, GFP_KERNEL); + if (status) { + err(__FUNCTION__ " - usb_submit_urb(write bulk) failed with status = %d", status); + count = status; + } else { + bytes_out += count; } -exit: - return bytes_sent; + /* we are done with this urb, so let the host driver + * really free it when it is finished with it */ + usb_free_urb (urb); + + return count; } static int visor_write_room (struct usb_serial_port *port) { - unsigned long flags; - int i; - int room = 0; - dbg(__FUNCTION__ " - port %d", port->number); - - spin_lock_irqsave (&write_urb_pool_lock, flags); - for (i = 0; i < NUM_URBS; ++i) { - if (write_urb_pool[i]->status != -EINPROGRESS) { - room += URB_TRANSFER_BUFFER_SIZE; - } - } - - spin_unlock_irqrestore (&write_urb_pool_lock, flags); - - dbg(__FUNCTION__ " - returns %d", room); - return (room); + /* + * We really can take anything the user throws at us + * but let's pick a nice big number to tell the tty + * layer that we have lots of free space + */ + return 2048; } static int visor_chars_in_buffer (struct usb_serial_port *port) { - unsigned long flags; - int i; - int chars = 0; - dbg(__FUNCTION__ " - port %d", port->number); - - spin_lock_irqsave (&write_urb_pool_lock, flags); - - for (i = 0; i < NUM_URBS; ++i) { - if (write_urb_pool[i]->status == -EINPROGRESS) { - chars += URB_TRANSFER_BUFFER_SIZE; - } - } - - spin_unlock_irqrestore (&write_urb_pool_lock, flags); - dbg (__FUNCTION__ " - returns %d", chars); - return (chars); + /* + * We can't really account for how much data we + * have sent out, but hasn't made it through to the + * device, so just tell the tty layer that everything + * is flushed. + */ + return 0; } @@ -471,9 +434,12 @@ return; } + /* free up the transfer buffer, as usb_free_urb() does not do this */ + kfree (urb->transfer_buffer); + queue_task(&port->tqueue, &tq_immediate); mark_bh(IMMEDIATE_BH); - + return; } @@ -534,16 +500,8 @@ static void visor_throttle (struct usb_serial_port *port) { - dbg(__FUNCTION__ " - port %d", port->number); - - down (&port->sem); - usb_unlink_urb (port->read_urb); - - up (&port->sem); - - return; } @@ -553,16 +511,10 @@ dbg(__FUNCTION__ " - port %d", port->number); - down (&port->sem); - port->read_urb->dev = port->serial->dev; result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) err(__FUNCTION__ " - failed submitting read urb, error %d", result); - - up (&port->sem); - - return; } @@ -783,30 +735,8 @@ static int __init visor_init (void) { - struct urb *urb; - int i; - usb_serial_register (&handspring_device); usb_serial_register (&clie_3_5_device); - - /* create our write urb pool and transfer buffers */ - spin_lock_init (&write_urb_pool_lock); - for (i = 0; i < NUM_URBS; ++i) { - urb = usb_alloc_urb(0, GFP_KERNEL); - write_urb_pool[i] = urb; - if (urb == NULL) { - err("No more urbs???"); - continue; - } - - urb->transfer_buffer = NULL; - urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); - if (!urb->transfer_buffer) { - err (__FUNCTION__ " - out of memory for urb buffers."); - continue; - } - } - info(DRIVER_DESC " " DRIVER_VERSION); return 0; @@ -815,27 +745,8 @@ static void __exit visor_exit (void) { - int i; - unsigned long flags; - usb_serial_deregister (&handspring_device); usb_serial_deregister (&clie_3_5_device); - - spin_lock_irqsave (&write_urb_pool_lock, flags); - - for (i = 0; i < NUM_URBS; ++i) { - if (write_urb_pool[i]) { - /* FIXME - uncomment the following usb_unlink_urb call when - * the host controllers get fixed to set urb->dev = NULL after - * the urb is finished. Otherwise this call oopses. */ - /* usb_unlink_urb(write_urb_pool[i]); */ - if (write_urb_pool[i]->transfer_buffer) - kfree(write_urb_pool[i]->transfer_buffer); - usb_free_urb (write_urb_pool[i]); - } - } - - spin_unlock_irqrestore (&write_urb_pool_lock, flags); } diff -Nru a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h --- a/drivers/usb/serial/visor.h Thu Mar 7 18:17:41 2002 +++ b/drivers/usb/serial/visor.h Thu Mar 7 18:17:41 2002 @@ -23,6 +23,7 @@ #define PALM_VENDOR_ID 0x0830 #define PALM_M500_ID 0x0001 #define PALM_M505_ID 0x0002 +#define PALM_M515_ID 0x0003 #define PALM_M125_ID 0x0040 #define SONY_VENDOR_ID 0x054C diff -Nru a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c --- a/drivers/usb/serial/whiteheat.c Thu Mar 7 18:17:42 2002 +++ b/drivers/usb/serial/whiteheat.c Thu Mar 7 18:17:42 2002 @@ -306,8 +306,6 @@ dbg(__FUNCTION__" - port %d", port->number); - down (&port->sem); - ++port->open_count; if (port->open_count == 1) { @@ -354,16 +352,12 @@ } dbg(__FUNCTION__ " - exit"); - up (&port->sem); - return retval; error_exit: --port->open_count; dbg(__FUNCTION__ " - error_exit"); - up (&port->sem); - return retval; } @@ -374,7 +368,6 @@ dbg(__FUNCTION__ " - port %d", port->number); - down (&port->sem); --port->open_count; if (port->open_count <= 0) { @@ -391,7 +384,6 @@ usb_unlink_urb (port->read_urb); port->open_count = 0; } - up (&port->sem); } @@ -410,8 +402,6 @@ dbg(__FUNCTION__ " -port %d", port->number); - down (&port->sem); - if ((!port->tty) || (!port->tty->termios)) { dbg(__FUNCTION__" - no tty structures"); goto exit; @@ -492,7 +482,6 @@ whiteheat_send_cmd (port->serial, WHITEHEAT_SETUP_PORT, (__u8 *)&port_settings, sizeof(port_settings)); exit: - up (&port->sem); return; } diff -Nru a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile --- a/drivers/usb/storage/Makefile Thu Mar 7 18:17:45 2002 +++ b/drivers/usb/storage/Makefile Thu Mar 7 18:17:45 2002 @@ -1,7 +1,7 @@ # # Makefile for the USB Mass Storage device drivers. # -# 15 Aug 2000, Christoph Hellwig +# 15 Aug 2000, Christoph Hellwig # Rewritten to use lists instead of if-statements. # diff -Nru a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c --- a/drivers/usb/storage/isd200.c Thu Mar 7 18:17:44 2002 +++ b/drivers/usb/storage/isd200.c Thu Mar 7 18:17:44 2002 @@ -821,7 +821,7 @@ * Invoke the transport and basic error-handling/recovery methods * * This is used by the protocol layers to actually send the message to - * the device and recieve the response. + * the device and receive the response. */ void isd200_invoke_transport( struct us_data *us, Scsi_Cmnd *srb, diff -Nru a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c --- a/drivers/usb/storage/transport.c Thu Mar 7 18:17:42 2002 +++ b/drivers/usb/storage/transport.c Thu Mar 7 18:17:42 2002 @@ -612,7 +612,7 @@ /* Invoke the transport and basic error-handling/recovery methods * * This is used by the protocol layers to actually send the message to - * the device and recieve the response. + * the device and receive the response. */ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us) { @@ -798,7 +798,7 @@ { struct us_data *us = (struct us_data *)urb->context; - US_DEBUGP("USB IRQ recieved for device on host %d\n", us->host_no); + US_DEBUGP("USB IRQ received for device on host %d\n", us->host_no); US_DEBUGP("-- IRQ data length is %d\n", urb->actual_length); US_DEBUGP("-- IRQ state is %d\n", urb->status); US_DEBUGP("-- Interrupt Status (0x%x, 0x%x)\n", diff -Nru a/drivers/usb/stv680.c b/drivers/usb/stv680.c --- a/drivers/usb/stv680.c Thu Mar 7 18:17:38 2002 +++ b/drivers/usb/stv680.c Thu Mar 7 18:17:38 2002 @@ -127,54 +127,25 @@ * And the STV0680 driver - Kevin ********************************************************************/ -/* 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)) { - preempt_disable(); - ptep = pte_offset_map (pmd, adr); - pte = *ptep; - pte_unmap(pte); - preempt_enable(); - 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. +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the area. */ static inline unsigned long kvirt_to_pa (unsigned long adr) { - unsigned long va, kva, ret; + unsigned long kva, ret; - va = VMALLOC_VMADDR (adr); - kva = uvirt_to_kva (pgd_offset_k (va), va); - ret = __pa (kva); + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ + ret = __pa(kva); return ret; } static void *rvmalloc (unsigned long size) { void *mem; - unsigned long adr, page; - - /* Round it off to PAGE_SIZE */ - size += (PAGE_SIZE - 1); - size &= ~(PAGE_SIZE - 1); + unsigned long adr; + size = PAGE_ALIGN(size); mem = vmalloc_32 (size); if (!mem) return NULL; @@ -182,36 +153,25 @@ 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))); + mem_map_reserve(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; + size -= PAGE_SIZE; } return mem; } static void rvfree (void *mem, unsigned long size) { - unsigned long adr, page; + unsigned long adr; 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))); + while ((long) size > 0) { + mem_map_unreserve(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; + size -= PAGE_SIZE; } vfree (mem); } diff -Nru a/drivers/usb/uhci.c b/drivers/usb/uhci.c --- a/drivers/usb/uhci.c Thu Mar 7 18:17:40 2002 +++ b/drivers/usb/uhci.c Thu Mar 7 18:17:40 2002 @@ -4,7 +4,7 @@ * Maintainer: Johannes Erdfelt * * (C) Copyright 1999 Linus Torvalds - * (C) Copyright 1999-2001 Johannes Erdfelt, johannes@erdfelt.com + * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com * (C) Copyright 1999 Randy Dunlap * (C) Copyright 1999 Georg Acher, acher@in.tum.de * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de @@ -53,11 +53,11 @@ #include #include +#include "hcd.h" #include "uhci.h" #include - /* * Version Information */ @@ -65,7 +65,6 @@ #define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber" #define DRIVER_DESC "USB Universal Host Controller Interface driver" - /* * debug = 0, no debugging messages * debug = 1, dump failed URB's except for stalls @@ -100,6 +99,7 @@ /* If a transfer is still active after this much time, turn off FSBR */ #define IDLE_TIMEOUT (HZ / 20) /* 50 ms */ +#define FSBR_DELAY (HZ / 20) /* 50 ms */ #define MAX_URB_LOOP 2048 /* Maximum number of linked URB's */ @@ -240,10 +240,10 @@ unsigned long flags; /* If it's not inserted, don't remove it */ + spin_lock_irqsave(&uhci->frame_list_lock, flags); if (td->frame == -1 && list_empty(&td->fl_list)) - return; + goto out; - spin_lock_irqsave(&uhci->frame_list_lock, flags); if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) { if (list_empty(&td->fl_list)) { uhci->fl->frame[td->frame] = td->link; @@ -268,6 +268,7 @@ list_del_init(&td->fl_list); td->frame = -1; +out: spin_unlock_irqrestore(&uhci->frame_list_lock, flags); } @@ -358,6 +359,9 @@ pci_pool_free(uhci->qh_pool, qh, qh->dma_handle); } +/* + * MUST be called with uhci->frame_list_lock acquired + */ static void _uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct urb *urb) { struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; @@ -417,11 +421,10 @@ return; /* Only go through the hoops if it's actually linked in */ + spin_lock_irqsave(&uhci->frame_list_lock, flags); if (!list_empty(&qh->list)) { qh->urbp = NULL; - spin_lock_irqsave(&uhci->frame_list_lock, flags); - pqh = list_entry(qh->list.prev, struct uhci_qh, list); if (pqh->urbp) { @@ -444,9 +447,8 @@ qh->element = qh->link = UHCI_PTR_TERM; list_del_init(&qh->list); - - spin_unlock_irqrestore(&uhci->frame_list_lock, flags); } + spin_unlock_irqrestore(&uhci->frame_list_lock, flags); spin_lock_irqsave(&uhci->qh_remove_list_lock, flags); @@ -658,6 +660,9 @@ return urbp; } +/* + * MUST be called with urb->lock acquired + */ static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td) { struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; @@ -667,6 +672,9 @@ list_add_tail(&td->list, &urbp->td_list); } +/* + * MUST be called with urb->lock acquired + */ static void uhci_remove_td_from_urb(struct uhci_td *td) { if (list_empty(&td->list)) @@ -677,22 +685,22 @@ td->urb = NULL; } +/* + * MUST be called with urb->lock acquired + */ static void uhci_destroy_urb_priv(struct urb *urb) { struct list_head *head, *tmp; struct urb_priv *urbp; struct uhci *uhci; - unsigned long flags; - - spin_lock_irqsave(&urb->lock, flags); urbp = (struct urb_priv *)urb->hcpriv; if (!urbp) - goto out; + return; if (!urbp->dev || !urbp->dev->bus || !urbp->dev->bus->hcpriv) { warn("uhci_destroy_urb_priv: urb %p belongs to disconnected device or bus?", urb); - goto out; + return; } if (!list_empty(&urb->urb_list)) @@ -715,20 +723,21 @@ uhci_free_td(uhci, td); } - if (urbp->setup_packet_dma_handle) + if (urbp->setup_packet_dma_handle) { pci_unmap_single(uhci->dev, urbp->setup_packet_dma_handle, sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); + urbp->setup_packet_dma_handle = 0; + } - if (urbp->transfer_buffer_dma_handle) + if (urbp->transfer_buffer_dma_handle) { pci_unmap_single(uhci->dev, urbp->transfer_buffer_dma_handle, urb->transfer_buffer_length, usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + urbp->transfer_buffer_dma_handle = 0; + } urb->hcpriv = NULL; kmem_cache_free(uhci_up_cachep, urbp); - -out: - spin_unlock_irqrestore(&urb->lock, flags); } static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb) @@ -757,7 +766,7 @@ if ((!(urb->transfer_flags & USB_NO_FSBR)) && urbp->fsbr) { urbp->fsbr = 0; if (!--uhci->fsbr) - uhci->skel_term_qh->link = UHCI_PTR_TERM; + uhci->fsbrtimeout = jiffies + FSBR_DELAY; } spin_unlock_irqrestore(&uhci->frame_list_lock, flags); @@ -872,7 +881,7 @@ * It's IN if the pipe is an output pipe or we're not expecting * data back. */ - destination &= ~TD_PID; + destination &= ~TD_TOKEN_PID_MASK; if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) destination |= USB_PID_IN; else @@ -1312,9 +1321,7 @@ struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; struct list_head *tmp, *head; int ret = 0; - unsigned long flags; - spin_lock_irqsave(&uhci->urb_list_lock, flags); head = &uhci->urb_list; tmp = head->next; while (tmp != head) { @@ -1337,8 +1344,6 @@ } else ret = -1; /* no previous urb found */ - spin_unlock_irqrestore(&uhci->urb_list_lock, flags); - return ret; } @@ -1446,34 +1451,30 @@ return ret; } +/* + * MUST be called with uhci->urb_list_lock acquired + */ static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb) { struct list_head *tmp, *head; - unsigned long flags; - struct urb *u = NULL; /* We don't match Isoc transfers since they are special */ if (usb_pipeisoc(urb->pipe)) return NULL; - spin_lock_irqsave(&uhci->urb_list_lock, flags); head = &uhci->urb_list; tmp = head->next; while (tmp != head) { - u = list_entry(tmp, struct urb, urb_list); + struct urb *u = list_entry(tmp, struct urb, urb_list); tmp = tmp->next; if (u->dev == urb->dev && u->pipe == urb->pipe && u->status == -EINPROGRESS) - goto out; + return u; } - u = NULL; -out: - spin_unlock_irqrestore(&uhci->urb_list_lock, flags); - - return u; + return NULL; } static int uhci_submit_urb(struct urb *urb, int mem_flags) @@ -1492,19 +1493,25 @@ return -ENODEV; } + /* increment the reference count of the urb, as we now also control it */ + urb = usb_get_urb(urb); + uhci = (struct uhci *)urb->dev->bus->hcpriv; INIT_LIST_HEAD(&urb->urb_list); usb_inc_dev_use(urb->dev); - spin_lock_irqsave(&urb->lock, flags); + spin_lock_irqsave(&uhci->urb_list_lock, flags); + spin_lock(&urb->lock); if (urb->status == -EINPROGRESS || urb->status == -ECONNRESET || urb->status == -ECONNABORTED) { dbg("uhci_submit_urb: urb not available to submit (status = %d)", urb->status); /* Since we can have problems on the out path */ - spin_unlock_irqrestore(&urb->lock, flags); + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); usb_dec_dev_use(urb->dev); + usb_put_urb(urb); return ret; } @@ -1572,18 +1579,21 @@ out: urb->status = ret; - spin_unlock_irqrestore(&urb->lock, flags); - if (ret == -EINPROGRESS) { - spin_lock_irqsave(&uhci->urb_list_lock, flags); /* We use _tail to make find_urb_ep more efficient */ list_add_tail(&urb->urb_list, &uhci->urb_list); + + spin_unlock(&urb->lock); spin_unlock_irqrestore(&uhci->urb_list_lock, flags); return 0; } uhci_unlink_generic(uhci, urb); + + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); + uhci_call_completion(urb); return ret; @@ -1592,7 +1602,7 @@ /* * Return the result of a transfer * - * Must be called with urb_list_lock acquired + * MUST be called with urb_list_lock acquired */ static void uhci_transfer_result(struct uhci *uhci, struct urb *urb) { @@ -1631,10 +1641,10 @@ urbp->status = ret; - spin_unlock_irqrestore(&urb->lock, flags); - - if (ret == -EINPROGRESS) + if (ret == -EINPROGRESS) { + spin_unlock_irqrestore(&urb->lock, flags); return; + } switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: @@ -1664,15 +1674,22 @@ usb_pipetype(urb->pipe), urb); } + /* Remove it from uhci->urb_list */ list_del_init(&urb->urb_list); uhci_add_complete(urb); + + spin_unlock_irqrestore(&urb->lock, flags); } +/* + * MUST be called with urb->lock acquired + */ static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb) { struct list_head *head, *tmp; struct urb_priv *urbp = urb->hcpriv; + int prevactive = 1; /* We can get called when urbp allocation fails, so check */ if (!urbp) @@ -1680,6 +1697,19 @@ uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ + /* + * Now we need to find out what the last successful toggle was + * so we can update the local data toggle for the next transfer + * + * There's 3 way's the last successful completed TD is found: + * + * 1) The TD is NOT active and the actual length < expected length + * 2) The TD is NOT active and it's the last TD in the chain + * 3) The TD is active and the previous TD is NOT active + * + * Control and Isochronous ignore the toggle, so this is safe + * for all types + */ head = &urbp->td_list; tmp = head->next; while (tmp != head) { @@ -1687,15 +1717,18 @@ tmp = tmp->next; - /* Control and Isochronous ignore the toggle, so this */ - /* is safe for all types */ - if ((!(td->status & TD_CTRL_ACTIVE) && - (uhci_actual_length(td->status) < uhci_expected_length(td->info)) || - tmp == head)) { + if (!(td->status & TD_CTRL_ACTIVE) && + (uhci_actual_length(td->status) < uhci_expected_length(td->info) || + tmp == head)) usb_settoggle(urb->dev, uhci_endpoint(td->info), uhci_packetout(td->info), uhci_toggle(td->info) ^ 1); - } + else if ((td->status & TD_CTRL_ACTIVE) && !prevactive) + usb_settoggle(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info), + uhci_toggle(td->info)); + + prevactive = td->status & TD_CTRL_ACTIVE; } uhci_delete_queued_urb(uhci, urb); @@ -1718,6 +1751,9 @@ uhci = (struct uhci *)urb->dev->bus->hcpriv; + spin_lock_irqsave(&uhci->urb_list_lock, flags); + spin_lock(&urb->lock); + /* Release bandwidth for Interrupt or Isoc. transfers */ /* Spinlock needed ? */ if (urb->bandwidth) { @@ -1733,35 +1769,41 @@ } } - if (urb->status != -EINPROGRESS) + if (urb->status != -EINPROGRESS) { + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); return 0; + } - spin_lock_irqsave(&uhci->urb_list_lock, flags); list_del_init(&urb->urb_list); - spin_unlock_irqrestore(&uhci->urb_list_lock, flags); uhci_unlink_generic(uhci, urb); /* Short circuit the virtual root hub */ if (urb->dev == uhci->rh.dev) { rh_unlink_urb(urb); + + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); + uhci_call_completion(urb); } else { if (urb->transfer_flags & USB_ASYNC_UNLINK) { - /* urb_list is available now since we called */ - /* uhci_unlink_generic already */ - urbp->status = urb->status = -ECONNABORTED; - spin_lock_irqsave(&uhci->urb_remove_list_lock, flags); + spin_lock(&uhci->urb_remove_list_lock); - /* Check to see if the remove list is empty */ + /* If we're the first, set the next interrupt bit */ if (list_empty(&uhci->urb_remove_list)) uhci_set_next_interrupt(uhci); list_add(&urb->urb_list, &uhci->urb_remove_list); - spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags); + spin_unlock(&uhci->urb_remove_list_lock); + + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); + } else { urb->status = -ENOENT; @@ -1774,6 +1816,9 @@ } else schedule_timeout(1+1*HZ/1000); + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); + uhci_call_completion(urb); } } @@ -1907,12 +1952,14 @@ /* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */ static int rh_send_irq(struct urb *urb) { - int i, len = 1; struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; unsigned int io_addr = uhci->io_addr; + unsigned long flags; + int i, len = 1; __u16 data = 0; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + spin_lock_irqsave(&urb->lock, flags); for (i = 0; i < uhci->rh.numports; i++) { data |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0); len = (i + 1) / 8 + 1; @@ -1922,6 +1969,8 @@ urb->actual_length = len; urbp->status = 0; + spin_unlock_irqrestore(&urb->lock, flags); + if ((data > 0) && (uhci->rh.send != 0)) { dbg("root-hub INT complete: port1: %x port2: %x data: %x", inw(io_addr + USBPORTSC1), inw(io_addr + USBPORTSC2), data); @@ -1948,7 +1997,6 @@ spin_lock_irqsave(&uhci->urb_list_lock, flags); head = &uhci->urb_list; - tmp = head->next; while (tmp != head) { struct urb *u = list_entry(tmp, struct urb, urb_list); @@ -1956,6 +2004,8 @@ tmp = tmp->next; + spin_lock(&urb->lock); + /* Check if the FSBR timed out */ if (urbp->fsbr && !urbp->fsbr_timeout && time_after_eq(jiffies, urbp->fsbrtime + IDLE_TIMEOUT)) uhci_fsbr_timeout(uhci, u); @@ -1965,6 +2015,8 @@ list_del(&u->urb_list); list_add_tail(&u->urb_list, &list); } + + spin_unlock(&urb->lock); } spin_unlock_irqrestore(&uhci->urb_list_lock, flags); @@ -1979,6 +2031,12 @@ uhci_unlink_urb(u); } + /* Really disable FSBR */ + if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) { + uhci->fsbrtimeout = 0; + uhci->skel_term_qh->link = UHCI_PTR_TERM; + } + /* enter global suspend if nothing connected */ if (!uhci->is_suspended && !ports_active(uhci)) suspend_hc(uhci); @@ -2198,6 +2256,9 @@ return stat; } +/* + * MUST be called with urb->lock acquired + */ static int rh_unlink_urb(struct urb *urb) { struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; @@ -2233,16 +2294,25 @@ static void uhci_call_completion(struct urb *urb) { - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct urb_priv *urbp; struct usb_device *dev = urb->dev; struct uhci *uhci = (struct uhci *)dev->bus->hcpriv; int is_ring = 0, killed, resubmit_interrupt, status; struct urb *nurb; + unsigned long flags; + + spin_lock_irqsave(&urb->lock, flags); + + urbp = (struct urb_priv *)urb->hcpriv; + if (!urbp || !urb->dev) { + spin_unlock_irqrestore(&urb->lock, flags); + return; + } killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED || urb->status == -ECONNRESET); resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT && - urb->interval && !killed); + urb->interval); nurb = urb->next; if (nurb && !killed) { @@ -2267,14 +2337,6 @@ is_ring = (nurb == urb); } - status = urbp->status; - if (!resubmit_interrupt) - /* We don't need urb_priv anymore */ - uhci_destroy_urb_priv(urb); - - if (!killed) - urb->status = status; - if (urbp->transfer_buffer_dma_handle) pci_dma_sync_single(uhci->dev, urbp->transfer_buffer_dma_handle, urb->transfer_buffer_length, usb_pipein(urb->pipe) ? @@ -2284,11 +2346,28 @@ pci_dma_sync_single(uhci->dev, urbp->setup_packet_dma_handle, sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); + status = urbp->status; + if (!resubmit_interrupt || killed) + /* We don't need urb_priv anymore */ + uhci_destroy_urb_priv(urb); + + if (!killed) + urb->status = status; + urb->dev = NULL; - if (urb->complete) + spin_unlock_irqrestore(&urb->lock, flags); + + if (urb->complete) { urb->complete(urb); - if (resubmit_interrupt) { + /* Recheck the status. The completion handler may have */ + /* unlinked the resubmitting interrupt URB */ + killed = (urb->status == -ENOENT || + urb->status == -ECONNABORTED || + urb->status == -ECONNRESET); + } + + if (resubmit_interrupt && !killed) { urb->dev = dev; uhci_reset_interrupt(urb); } else { @@ -2299,6 +2378,7 @@ /* We decrement the usage count after we're done */ /* with everything */ usb_dec_dev_use(dev); + usb_put_urb(urb); } } } @@ -2315,11 +2395,14 @@ struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list); struct urb *urb = urbp->urb; - tmp = tmp->next; - list_del_init(&urbp->complete_list); + spin_unlock_irqrestore(&uhci->complete_list_lock, flags); uhci_call_completion(urb); + + spin_lock_irqsave(&uhci->complete_list_lock, flags); + head = &uhci->complete_list; + tmp = head->next; } spin_unlock_irqrestore(&uhci->complete_list_lock, flags); } @@ -2341,7 +2424,8 @@ list_del_init(&urb->urb_list); urbp->status = urb->status = -ECONNRESET; - uhci_call_completion(urb); + + uhci_add_complete(urb); } spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags); } @@ -2996,7 +3080,7 @@ id_table: uhci_pci_ids, probe: uhci_pci_probe, - remove: uhci_pci_remove, + remove: __devexit_p(uhci_pci_remove), #ifdef CONFIG_PM suspend: uhci_pci_suspend, @@ -3074,3 +3158,4 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); + diff -Nru a/drivers/usb/uhci.h b/drivers/usb/uhci.h --- a/drivers/usb/uhci.h Thu Mar 7 18:17:39 2002 +++ b/drivers/usb/uhci.h Thu Mar 7 18:17:39 2002 @@ -121,15 +121,16 @@ * for TD : (a.k.a. Token) */ #define TD_TOKEN_TOGGLE 19 -#define TD_PID 0xFF +#define TD_TOKEN_PID_MASK 0xFF +#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n - 1 */ #define uhci_maxlen(token) ((token) >> 21) -#define uhci_expected_length(info) (((info >> 21) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ +#define uhci_expected_length(info) (((info >> 21) + 1) & TD_TOKEN_EXPLEN_MASK) /* 1-based */ #define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1) #define uhci_endpoint(token) (((token) >> 15) & 0xf) #define uhci_devaddr(token) (((token) >> 8) & 0x7f) #define uhci_devep(token) (((token) >> 8) & 0x7ff) -#define uhci_packetid(token) ((token) & 0xff) +#define uhci_packetid(token) ((token) & TD_TOKEN_PID_MASK) #define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN) #define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN) @@ -163,7 +164,7 @@ struct list_head list; /* P: urb->lock */ int frame; - struct list_head fl_list; /* P: frame_list_lock */ + struct list_head fl_list; /* P: uhci->frame_list_lock */ } __attribute__((aligned(16))); /* @@ -306,21 +307,26 @@ struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */ spinlock_t frame_list_lock; - struct uhci_frame_list *fl; /* Frame list */ + struct uhci_frame_list *fl; /* P: uhci->frame_list_lock */ int fsbr; /* Full speed bandwidth reclamation */ + unsigned long fsbrtimeout; /* FSBR delay */ int is_suspended; + /* Main list of URB's currently controlled by this HC */ + spinlock_t urb_list_lock; + struct list_head urb_list; /* P: uhci->urb_list_lock */ + + /* List of QH's that are done, but waiting to be unlinked (race) */ spinlock_t qh_remove_list_lock; - struct list_head qh_remove_list; + struct list_head qh_remove_list; /* P: uhci->qh_remove_list_lock */ + /* List of asynchronously unlinked URB's */ spinlock_t urb_remove_list_lock; - struct list_head urb_remove_list; - - spinlock_t urb_list_lock; - struct list_head urb_list; + struct list_head urb_remove_list; /* P: uhci->urb_remove_list_lock */ + /* List of URB's awaiting completion callback */ spinlock_t complete_list_lock; - struct list_head complete_list; + struct list_head complete_list; /* P: uhci->complete_list_lock */ struct virt_root_hub rh; /* private data of the virtual root hub */ }; @@ -333,7 +339,7 @@ dma_addr_t transfer_buffer_dma_handle; struct uhci_qh *qh; /* QH for this URB */ - struct list_head td_list; + struct list_head td_list; /* P: urb->lock */ int fsbr : 1; /* URB turned on FSBR */ int fsbr_timeout : 1; /* URB timed out on FSBR */ @@ -345,11 +351,36 @@ int status; /* Final status */ unsigned long inserttime; /* In jiffies */ - unsigned long fsbrtime; /* In jiffies */ + unsigned long fsbrtime; /* In jiffies */ - struct list_head queue_list; - struct list_head complete_list; + struct list_head queue_list; /* P: uhci->frame_list_lock */ + struct list_head complete_list; /* P: uhci->complete_list_lock */ }; + +/* + * Locking in uhci.c + * + * spinlocks are used extensively to protect the many lists and data + * structures we have. It's not that pretty, but it's necessary. We + * need to be done with all of the locks (except complete_list_lock) when + * we call urb->complete. I've tried to make it simple enough so I don't + * have to spend hours racking my brain trying to figure out if the + * locking is safe. + * + * Here's the safe locking order to prevent deadlocks: + * + * #1 uhci->urb_list_lock + * #2 urb->lock + * #3 uhci->urb_remove_list_lock, uhci->frame_list_lock, + * uhci->qh_remove_list_lock + * #4 uhci->complete_list_lock + * + * If you're going to grab 2 or more locks at once, ALWAYS grab the lock + * at the lowest level FIRST and NEVER grab locks at the same level at the + * same time. + * + * So, if you need uhci->urb_list_lock, grab it before you grab urb->lock + */ /* ------------------------------------------------------------------------- Virtual Root HUB diff -Nru a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c --- a/drivers/usb/usb-ohci.c Thu Mar 7 18:17:40 2002 +++ b/drivers/usb/usb-ohci.c Thu Mar 7 18:17:40 2002 @@ -73,6 +73,7 @@ #define OHCI_USE_NPS // force NoPowerSwitching mode // #define OHCI_VERBOSE_DEBUG /* not always helpful */ +#include "hcd.h" #include "usb-ohci.h" @@ -205,6 +206,7 @@ urb_free_priv ((struct ohci *)urb->dev->bus->hcpriv, urb_priv); usb_dec_dev_use (urb->dev); urb->dev = NULL; + usb_put_urb (urb); } } @@ -553,6 +555,9 @@ // if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) // return -EPIPE; + /* increment the reference count of the urb, as we now also control it */ + urb = usb_get_urb (urb); + usb_inc_dev_use (urb->dev); ohci = (ohci_t *) urb->dev->bus->hcpriv; @@ -568,12 +573,14 @@ * such as powering down ports */ if (ohci->disabled) { usb_dec_dev_use (urb->dev); + usb_put_urb (urb); return -ESHUTDOWN; } /* every endpoint has a ed, locate and fill it */ if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) { usb_dec_dev_use (urb->dev); + usb_put_urb (urb); return -ENOMEM; } @@ -595,6 +602,7 @@ size = urb->number_of_packets; if (size <= 0) { usb_dec_dev_use (urb->dev); + usb_put_urb (urb); return -EINVAL; } for (i = 0; i < urb->number_of_packets; i++) { @@ -615,6 +623,7 @@ urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), mem_flags); if (!urb_priv) { usb_dec_dev_use (urb->dev); + usb_put_urb (urb); return -ENOMEM; } memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *)); @@ -632,6 +641,7 @@ urb_free_priv (ohci, urb_priv); spin_unlock_irqrestore (&usb_ed_lock, flags); usb_dec_dev_use (urb->dev); + usb_put_urb (urb); return -ENOMEM; } } @@ -640,6 +650,7 @@ urb_free_priv (ohci, urb_priv); spin_unlock_irqrestore (&usb_ed_lock, flags); usb_dec_dev_use (urb->dev); + usb_put_urb (urb); return -EINVAL; } @@ -662,6 +673,7 @@ urb_free_priv (ohci, urb_priv); spin_unlock_irqrestore (&usb_ed_lock, flags); usb_dec_dev_use (urb->dev); + usb_put_urb (urb); return bustime; } usb_claim_bandwidth (urb->dev, urb, bustime, usb_pipeisoc (urb->pipe)); @@ -2100,6 +2112,7 @@ urb->dev = NULL; if (urb->complete) urb->complete (urb); + usb_put_urb (urb); return 0; } @@ -2123,6 +2136,7 @@ urb->complete (urb); } else urb->status = -ENOENT; + usb_put_urb (urb); } return 0; } @@ -2859,7 +2873,7 @@ id_table: &ohci_pci_ids [0], probe: ohci_pci_probe, - remove: ohci_pci_remove, + remove: __devexit_p(ohci_pci_remove), #ifdef CONFIG_PM suspend: ohci_pci_suspend, diff -Nru a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c --- a/drivers/usb/usb-uhci.c Thu Mar 7 18:17:44 2002 +++ b/drivers/usb/usb-uhci.c Thu Mar 7 18:17:44 2002 @@ -57,6 +57,7 @@ #define VERSTR "$Revision: 1.275 $ time " __TIME__ " " __DATE__ #include +#include "hcd.h" #include "usb-uhci.h" #include "usb-uhci-debug.h" @@ -1217,6 +1218,7 @@ urb->complete ((struct urb *) urb); } usb_dec_dev_use (usb_dev); + usb_put_urb (urb); } else spin_unlock_irqrestore (&s->urb_list_lock, flags); @@ -1305,7 +1307,7 @@ #else kfree (urb_priv); #endif - + usb_put_urb (urb); } } } @@ -1650,6 +1652,9 @@ return -EINVAL; } + /* increment the reference count of the urb, as we now also control it */ + urb = usb_get_urb (urb); + usb_inc_dev_use (urb->dev); spin_lock_irqsave (&s->urb_list_lock, flags); @@ -1665,6 +1670,7 @@ (!(urb->transfer_flags & USB_QUEUE_BULK) || !(queued_urb->transfer_flags & USB_QUEUE_BULK)))) { spin_unlock_irqrestore (&s->urb_list_lock, flags); usb_dec_dev_use (urb->dev); + usb_put_urb (urb); err("ENXIO %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,queued_urb); return -ENXIO; // urb already queued } @@ -1678,6 +1684,7 @@ if (!urb_priv) { usb_dec_dev_use (urb->dev); spin_unlock_irqrestore (&s->urb_list_lock, flags); + usb_put_urb (urb); return -ENOMEM; } @@ -1766,6 +1773,7 @@ #else kfree (urb_priv); #endif + usb_put_urb (urb); return ret; } @@ -2730,6 +2738,7 @@ } usb_dec_dev_use (usb_dev); + usb_put_urb (urb); } } diff -Nru a/drivers/usb/usb.c b/drivers/usb/usb.c --- a/drivers/usb/usb.c Thu Mar 7 18:17:45 2002 +++ b/drivers/usb/usb.c Thu Mar 7 18:17:45 2002 @@ -40,12 +40,7 @@ #endif #include -static const int usb_bandwidth_option = -#ifdef CONFIG_USB_BANDWIDTH - 1; -#else - 0; -#endif +#include "hcd.h" extern int usb_hub_init(void); extern void usb_hub_cleanup(void); @@ -61,13 +56,9 @@ * We have a per-interface "registered driver" list. */ LIST_HEAD(usb_driver_list); -LIST_HEAD(usb_bus_list); -struct semaphore usb_bus_list_lock; devfs_handle_t usb_devfs_handle; /* /dev/usb dir. */ -static struct usb_busmap busmap; - static struct usb_driver *usb_minors[16]; /** @@ -105,6 +96,7 @@ /** * usb_scan_devices - scans all unclaimed USB interfaces + * Context: !in_interrupt () * * Goes through all unclaimed USB interfaces, and offers them to all * registered USB drivers through the 'probe' function. @@ -173,6 +165,7 @@ /** * usb_deregister - unregister a USB driver * @driver: USB operations of the driver to unregister + * Context: !in_interrupt () * * Unlinks the specified driver from the internal USB driver list. */ @@ -258,259 +251,6 @@ } /* - * usb_calc_bus_time: - * - * returns (approximate) USB bus time in nanoseconds for a USB transaction. - */ -static long usb_calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount) -{ - unsigned long tmp; - - if (low_speed) /* no isoc. here */ - { - if (input_dir) - { - tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L; - return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); - } - else - { - tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L; - return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); - } - } - - /* for full-speed: */ - - if (!isoc) /* Input or Output */ - { - tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; - return (9107L + BW_HOST_DELAY + tmp); - } /* end not Isoc */ - - /* for isoc: */ - - tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; - return (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp); -} - -/* - * usb_check_bandwidth(): - * - * old_alloc is from host_controller->bandwidth_allocated in microseconds; - * bustime is from calc_bus_time(), but converted to microseconds. - * - * returns if successful, - * or -ENOSPC if bandwidth request fails. - * - * FIXME: - * This initial implementation does not use Endpoint.bInterval - * in managing bandwidth allocation. - * It probably needs to be expanded to use Endpoint.bInterval. - * This can be done as a later enhancement (correction). - * This will also probably require some kind of - * frame allocation tracking...meaning, for example, - * that if multiple drivers request interrupts every 10 USB frames, - * they don't all have to be allocated at - * frame numbers N, N+10, N+20, etc. Some of them could be at - * N+11, N+21, N+31, etc., and others at - * N+12, N+22, N+32, etc. - * However, this first cut at USB bandwidth allocation does not - * contain any frame allocation tracking. - */ -int usb_check_bandwidth (struct usb_device *dev, struct urb *urb) -{ - int new_alloc; - int old_alloc = dev->bus->bandwidth_allocated; - unsigned int pipe = urb->pipe; - long bustime; - - bustime = usb_calc_bus_time (dev->speed == USB_SPEED_LOW, - usb_pipein(pipe), usb_pipeisoc(pipe), - usb_maxpacket(dev, pipe, usb_pipeout(pipe))); - if (usb_pipeisoc(pipe)) - bustime = NS_TO_US(bustime) / urb->number_of_packets; - else - bustime = NS_TO_US(bustime); - - new_alloc = old_alloc + (int)bustime; - /* what new total allocated bus time would be */ - - if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC) - dbg("usb-check-bandwidth %sFAILED: was %u, would be %u, bustime = %ld us", - usb_bandwidth_option ? "" : "would have ", - old_alloc, new_alloc, bustime); - - if (!usb_bandwidth_option) /* don't enforce it */ - return (bustime); - return (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? bustime : -ENOSPC; -} - -void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc) -{ - dev->bus->bandwidth_allocated += bustime; - if (isoc) - dev->bus->bandwidth_isoc_reqs++; - else - dev->bus->bandwidth_int_reqs++; - urb->bandwidth = bustime; - -#ifdef USB_BANDWIDTH_MESSAGES - dbg("bandwidth alloc increased by %d to %d for %d requesters", - bustime, - dev->bus->bandwidth_allocated, - dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs); -#endif -} - -/* - * usb_release_bandwidth(): - * - * called to release a pipe's bandwidth (in microseconds) - */ -void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc) -{ - dev->bus->bandwidth_allocated -= urb->bandwidth; - if (isoc) - dev->bus->bandwidth_isoc_reqs--; - else - dev->bus->bandwidth_int_reqs--; - -#ifdef USB_BANDWIDTH_MESSAGES - dbg("bandwidth alloc reduced by %d to %d for %d requesters", - urb->bandwidth, - dev->bus->bandwidth_allocated, - dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs); -#endif - urb->bandwidth = 0; -} - -static void usb_bus_get(struct usb_bus *bus) -{ - atomic_inc(&bus->refcnt); -} - -static void usb_bus_put(struct usb_bus *bus) -{ - if (atomic_dec_and_test(&bus->refcnt)) - kfree(bus); -} - -/** - * usb_alloc_bus - creates a new USB host controller structure (usbcore-internal) - * @op: pointer to a struct usb_operations that this bus structure should use - * - * Creates a USB host controller bus structure with the specified - * usb_operations and initializes all the necessary internal objects. - * (For use only by USB Host Controller Drivers.) - * - * If no memory is available, NULL is returned. - * - * The caller should call usb_free_bus() when it is finished with the structure. - */ -struct usb_bus *usb_alloc_bus(struct usb_operations *op) -{ - struct usb_bus *bus; - - bus = kmalloc(sizeof(*bus), GFP_KERNEL); - if (!bus) - return NULL; - - memset(&bus->devmap, 0, sizeof(struct usb_devmap)); - -#ifdef DEVNUM_ROUND_ROBIN - bus->devnum_next = 1; -#endif /* DEVNUM_ROUND_ROBIN */ - - bus->op = op; - bus->root_hub = NULL; - bus->hcpriv = NULL; - bus->busnum = -1; - bus->bandwidth_allocated = 0; - bus->bandwidth_int_reqs = 0; - bus->bandwidth_isoc_reqs = 0; - - INIT_LIST_HEAD(&bus->bus_list); - - atomic_set(&bus->refcnt, 1); - - return bus; -} - -/** - * usb_free_bus - frees the memory used by a bus structure (usbcore-internal) - * @bus: pointer to the bus to free - * - * (For use only by USB Host Controller Drivers.) - */ -void usb_free_bus(struct usb_bus *bus) -{ - if (!bus) - return; - - usb_bus_put(bus); -} - -/** - * usb_register_bus - registers the USB host controller with the usb core (usbcore-internal) - * @bus: pointer to the bus to register - * - * (For use only by USB Host Controller Drivers.) - * - * This call is synchronous, and may not be used in an interrupt context. - */ -void usb_register_bus(struct usb_bus *bus) -{ - int busnum; - - down (&usb_bus_list_lock); - busnum = find_next_zero_bit(busmap.busmap, USB_MAXBUS, 1); - if (busnum < USB_MAXBUS) { - set_bit(busnum, busmap.busmap); - bus->busnum = busnum; - } else - warn("too many buses"); - - usb_bus_get(bus); - - /* Add it to the list of buses */ - list_add(&bus->bus_list, &usb_bus_list); - up (&usb_bus_list_lock); - - usbfs_add_bus(bus); - - info("new USB bus registered, assigned bus number %d", bus->busnum); -} - -/** - * usb_deregister_bus - deregisters the USB host controller (usbcore-internal) - * @bus: pointer to the bus to deregister - * - * (For use only by USB Host Controller Drivers.) - * - * This call is synchronous, and may not be used in an interrupt context. - */ -void usb_deregister_bus(struct usb_bus *bus) -{ - info("USB bus %d deregistered", bus->busnum); - - /* - * NOTE: make sure that all the devices are removed by the - * controller code, as well as having it call this when cleaning - * itself up - */ - down (&usb_bus_list_lock); - list_del(&bus->bus_list); - up (&usb_bus_list_lock); - - usbfs_remove_bus(bus); - - clear_bit(bus->busnum, busmap.busmap); - - usb_bus_put(bus); -} - -/* * This function is for doing a depth-first search for devices which * have support, for dynamic loading of driver modules. */ @@ -1016,6 +756,7 @@ * usb_alloc_dev - allocate a usb device structure (usbcore-internal) * @parent: hub to which device is connected * @bus: bus used to access the device + * Context: !in_interrupt () * * Only hub drivers (including virtual root hub drivers for host * controllers) should ever call this. @@ -1078,14 +819,12 @@ atomic_inc(&dev->refcnt); } -/* ---------------------------------------------------------------------- - * New USB Core Functions - * ----------------------------------------------------------------------*/ /** * usb_alloc_urb - creates a new urb for a USB driver to use * @iso_packets: number of iso packets for this urb - * @mem_flags: the type of memory to allocate, see kmalloc() for a list of valid options for this. + * @mem_flags: the type of memory to allocate, see kmalloc() for a list of + * valid options for this. * * Creates an urb for the USB driver to use, initializes a few internal * structures, incrementes the usage counter, and returns a pointer to it. @@ -1158,7 +897,8 @@ /** * usb_submit_urb - asynchronously issue a transfer request for an endpoint * @urb: pointer to the urb describing the request - * @mem_flags: the type of memory to allocate, see kmalloc() for a list of valid options for this. + * @mem_flags: the type of memory to allocate, see kmalloc() for a list + * of valid options for this. * * This submits a transfer request, and transfers control of the URB * describing that request to the USB subsystem. Request completion will @@ -1393,7 +1133,9 @@ * @index: USB message index value * @data: pointer to the data to send * @size: length in bytes of the data to send - * @timeout: time to wait for the message to complete before timing out (if 0 the wait is forever) + * @timeout: time in jiffies to wait for the message to complete before + * timing out (if 0 the wait is forever) + * Context: !in_interrupt () * * This function sends a simple control message to a specified endpoint * and waits for the message to complete, or timeout. @@ -1401,7 +1143,7 @@ * If successful, it returns 0, otherwise a negative error number. * * Don't use this function from within an interrupt context, like a - * bottom half handler. If you need a asyncronous message, or need to send + * bottom half handler. If you need an asynchronous message, or need to send * a message from within interrupt context, use usb_submit_urb() */ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, @@ -1436,7 +1178,9 @@ * @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 transferred in bytes - * @timeout: time to wait for the message to complete before timing out (if 0 the wait is forever) + * @timeout: time in jiffies to wait for the message to complete before + * timing out (if 0 the wait is forever) + * Context: !in_interrupt () * * This function sends a simple bulk message to a specified endpoint * and waits for the message to complete, or timeout. @@ -1446,7 +1190,7 @@ * actual_length paramater. * * Don't use this function from within an interrupt context, like a - * bottom half handler. If you need a asyncronous message, or need to + * bottom half handler. If you need an asynchronous message, or need to * send a message from within interrupt context, use usb_submit_urb() */ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, @@ -1474,6 +1218,11 @@ * Returns the current frame number for the USB host controller * used with the given USB device. This can be used when scheduling * isochronous requests. + * + * Note that different kinds of host controller have different + * "scheduling horizons". While one type might support scheduling only + * 32 frames into the future, others could support scheduling up to + * 1024 frames into the future. */ int usb_get_current_frame_number(struct usb_device *dev) { @@ -1946,6 +1695,7 @@ /** * usb_disconnect - disconnect a device (usbcore-internal) * @pdev: pointer to device being disconnected + * Context: !in_interrupt () * * Something got disconnected. Get rid of it, and all of its children. * @@ -2069,6 +1819,7 @@ * @index: the number of the descriptor * @buf: where to put the descriptor * @size: how big is "buf"? + * Context: !in_interrupt () * * Gets a USB descriptor. Convenience functions exist to simplify * getting some types of descriptors. Use @@ -2110,6 +1861,7 @@ * @index: the number of the descriptor * @buf: where to put the string * @size: how big is "buf"? + * Context: !in_interrupt () * * Retrieves a string, encoded using UTF-16LE (Unicode, 16 bits per character, * in little-endian byte order). @@ -2135,6 +1887,7 @@ /** * usb_get_device_descriptor - (re)reads the device descriptor * @dev: the device whose device descriptor is being updated + * Context: !in_interrupt () * * Updates the copy of the device descriptor stored in the device structure, * which dedicates space for this purpose. Note that several fields are @@ -2169,6 +1922,7 @@ * @type: USB_RECIP_*; for device, interface, or endpoint * @target: zero (for device), else interface or endpoint number * @data: pointer to two bytes of bitmap data + * Context: !in_interrupt () * * Returns device, interface, or endpoint status. Normally only of * interest to see if the device is self powered, or has enabled the @@ -2227,6 +1981,7 @@ * usb_clear_halt - tells device to clear endpoint halt/stall condition * @dev: device whose endpoint is halted * @pipe: endpoint "pipe" being cleared + * Context: !in_interrupt () * * This is used to clear halt conditions for bulk and interrupt endpoints, * as reported by URB completion status. Endpoints that are halted are @@ -2298,6 +2053,7 @@ * @dev: the device whose interface is being updated * @interface: the interface being updated * @alternate: the setting being chosen. + * Context: !in_interrupt () * * This is used to enable data transfers on interfaces that may not * be enabled by default. Not all devices support such configurability. @@ -2378,6 +2134,7 @@ * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated * @configuration: the configuration being chosen. + * Context: !in_interrupt () * * This is used to enable non-default device modes. Not all devices * support this kind of configurability. By default, configuration @@ -2540,6 +2297,7 @@ * @index: the number of the descriptor * @buf: where to put the string * @size: how big is "buf"? + * Context: !in_interrupt () * * This converts the UTF-16LE encoded strings returned by devices, from * usb_get_string_descriptor(), to null-terminated ISO-8859-1 encoded ones @@ -2619,8 +2377,11 @@ * @dev: the device whose path is being constructed * @buf: where to put the string * @size: how big is "buf"? + * Context: !in_interrupt () * * Returns length of the string (>= 0) or out of memory status (< 0). + * + * NOTE: prefer to use use dev->devpath directly. */ int usb_make_path(struct usb_device *dev, char *buf, size_t size) { @@ -2768,29 +2529,6 @@ return 0; } -/** - * usb_register_root_hub - called by a usb host controller to register the root hub device in the system - * @usb_dev: the usb root hub device to be registered. - * @parent_dev: the parent device of this root hub. - * - * The USB host controller calls this function to register the root hub - * properly with the USB subsystem. It sets up the device properly in - * the driverfs tree, and then calls usb_new_device() to register the - * usb device. - */ -int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev) -{ - int retval; - - usb_dev->dev.parent = parent_dev; - strcpy (&usb_dev->dev.name[0], "usb_name"); - strcpy (&usb_dev->dev.bus_id[0], "usb_bus"); - retval = usb_new_device (usb_dev); - if (retval) - put_device (&usb_dev->dev); - return retval; -} - static int usb_open(struct inode * inode, struct file * file) { int minor = minor(inode->i_rdev); @@ -2859,7 +2597,6 @@ */ static int __init usb_init(void) { - init_MUTEX(&usb_bus_list_lock); usb_major_init(); usbfs_init(); usb_hub_init(); @@ -2892,11 +2629,7 @@ EXPORT_SYMBOL(usb_register); EXPORT_SYMBOL(usb_deregister); EXPORT_SYMBOL(usb_scan_devices); -EXPORT_SYMBOL(usb_alloc_bus); -EXPORT_SYMBOL(usb_free_bus); -EXPORT_SYMBOL(usb_register_bus); -EXPORT_SYMBOL(usb_deregister_bus); -EXPORT_SYMBOL(usb_register_root_hub); + EXPORT_SYMBOL(usb_alloc_dev); EXPORT_SYMBOL(usb_free_dev); EXPORT_SYMBOL(usb_inc_dev_use); @@ -2911,10 +2644,6 @@ EXPORT_SYMBOL(usb_reset_device); EXPORT_SYMBOL(usb_connect); EXPORT_SYMBOL(usb_disconnect); - -EXPORT_SYMBOL(usb_check_bandwidth); -EXPORT_SYMBOL(usb_claim_bandwidth); -EXPORT_SYMBOL(usb_release_bandwidth); EXPORT_SYMBOL(__usb_get_extra_descriptor); diff -Nru a/drivers/usb/usbvideo.c b/drivers/usb/usbvideo.c --- a/drivers/usb/usbvideo.c Thu Mar 7 18:17:44 2002 +++ b/drivers/usb/usbvideo.c Thu Mar 7 18:17:44 2002 @@ -59,33 +59,26 @@ /* Memory management functions */ /*******************************/ -#define MDEBUG(x) do { } while(0) /* Debug memory management */ - /* * 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. + * This is used when initializing the contents of the area. */ unsigned long usbvideo_kvirt_to_pa(unsigned long adr) { - unsigned long va, kva, ret; + unsigned long kva, ret; - va = VMALLOC_VMADDR(adr); - kva = page_address(vmalloc_to_page(va)); + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ 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); + unsigned long adr; + size = PAGE_ALIGN(size); mem = vmalloc_32(size); if (!mem) return NULL; @@ -93,13 +86,9 @@ 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))); + mem_map_reserve(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; + size -= PAGE_SIZE; } return mem; @@ -107,23 +96,16 @@ void usbvideo_rvfree(void *mem, unsigned long size) { - unsigned long adr, page; + unsigned long adr; 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 = (unsigned long) mem; + while ((long) size > 0) { + mem_map_unreserve(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; + size -= PAGE_SIZE; } vfree(mem); } diff -Nru a/drivers/usb/vicam.c b/drivers/usb/vicam.c --- a/drivers/usb/vicam.c Thu Mar 7 18:17:41 2002 +++ b/drivers/usb/vicam.c Thu Mar 7 18:17:41 2002 @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include #include #include @@ -91,83 +93,25 @@ * ******************************************************************************/ -/* [DaveM] I've recoded most of this so that: - * 1) It's easier to tell what is happening - * 2) It's more portable, especially for translating things - * out of vmalloc mapped areas in the kernel. - * 3) Less unnecessary translations happen. - * - * The code used to assume that the kernel vmalloc mappings - * existed in the page tables of every process, this is simply - * not guarenteed. We now use pgd_offset_k which is the - * defined way to get at the kernel page tables. - */ - -/* 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)) { - preempt_disable(); - ptep = pte_offset_map(pmd, adr); - pte = *ptep; - pte_unmap(pte); - preempt_enable(); - if(pte_present(pte)) { - ret = (unsigned long) page_address(pte_page(pte)); - ret |= (adr & (PAGE_SIZE - 1)); - - } - } - } - 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); - 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); - 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. + * This is used when initializing the contents of the area. */ static inline unsigned long kvirt_to_pa(unsigned long adr) { - unsigned long va, kva, ret; + unsigned long kva, ret; - va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ ret = __pa(kva); return ret; } -static void * rvmalloc(signed long size) +static void * rvmalloc(unsigned long size) { void * mem; - unsigned long adr, page; + unsigned long adr; + size=PAGE_ALIGN(size); mem=vmalloc_32(size); if (mem) { @@ -175,8 +119,7 @@ adr=(unsigned long) mem; while (size > 0) { - page = kvirt_to_pa(adr); - mem_map_reserve(virt_to_page(__va(page))); + mem_map_reserve(vmalloc_to_page((void *)adr)); adr+=PAGE_SIZE; size-=PAGE_SIZE; } @@ -184,17 +127,16 @@ return mem; } -static void rvfree(void * mem, signed long size) +static void rvfree(void * mem, unsigned long size) { - unsigned long adr, page; + unsigned long adr; if (mem) { adr=(unsigned long) mem; - while (size > 0) + while ((long) size > 0) { - page = kvirt_to_pa(adr); - mem_map_unreserve(virt_to_page(__va(page))); + mem_map_unreserve(vmalloc_to_page((void *)adr)); adr+=PAGE_SIZE; size-=PAGE_SIZE; } diff -Nru a/drivers/video/Config.help b/drivers/video/Config.help --- a/drivers/video/Config.help Thu Mar 7 18:17:40 2002 +++ b/drivers/video/Config.help Thu Mar 7 18:17:40 2002 @@ -516,6 +516,19 @@ Please read the file Documentation/fb/README-sstfb.txt for supported options and other important info support. +CONFIG_FB_TRIDENT + This driver is supposed to support graphics boards with the + Trident CyberXXXX/Image/CyberBlade chips mostly found in laptops + but also on some motherboards. For more information, read + + + Say Y if you have such a graphics board. + + The driver is also available as a module ( = code which can be + inserted and removed from the running kernel whenever you want). The + module will be called rivafb.o. If you want to compile it as a + module, say M here and read . + CONFIG_FB_SBUS Say Y if you want support for SBUS or UPA based frame buffer device. diff -Nru a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c --- a/drivers/video/cyber2000fb.c Thu Mar 7 18:17:40 2002 +++ b/drivers/video/cyber2000fb.c Thu Mar 7 18:17:40 2002 @@ -1683,7 +1683,7 @@ static struct pci_driver cyberpro_driver = { name: "CyberPro", probe: cyberpro_probe, - remove: cyberpro_remove, + remove: __devexit_p(cyberpro_remove), suspend: cyberpro_suspend, resume: cyberpro_resume, id_table: cyberpro_pci_table diff -Nru a/drivers/video/imsttfb.c b/drivers/video/imsttfb.c --- a/drivers/video/imsttfb.c Thu Mar 7 18:17:44 2002 +++ b/drivers/video/imsttfb.c Thu Mar 7 18:17:44 2002 @@ -1643,7 +1643,7 @@ name: "imsttfb", id_table: imsttfb_pci_tbl, probe: imsttfb_probe, - remove: imsttfb_remove, + remove: __devexit_p(imsttfb_remove), }; static struct fb_ops imsttfb_ops = { diff -Nru a/drivers/video/matrox/matroxfb_DAC1064.c b/drivers/video/matrox/matroxfb_DAC1064.c --- a/drivers/video/matrox/matroxfb_DAC1064.c Thu Mar 7 18:17:44 2002 +++ b/drivers/video/matrox/matroxfb_DAC1064.c Thu Mar 7 18:17:44 2002 @@ -473,9 +473,12 @@ static int m1064_compute(void* outdev, struct my_timming* m) { #define minfo ((struct matrox_fb_info*)outdev) +#ifdef CONFIG_FB_MATROX_G450 if (ACCESS_FBINFO(devflags.g450dac)) { matroxfb_g450_setclk(PMINFO m->pixclock, M_PIXEL_PLL_C); - } else { + } else +#endif + { int i; int tmout; CRITFLAGS diff -Nru a/drivers/video/neofb.c b/drivers/video/neofb.c --- a/drivers/video/neofb.c Thu Mar 7 18:17:38 2002 +++ b/drivers/video/neofb.c Thu Mar 7 18:17:38 2002 @@ -2332,7 +2332,7 @@ name: "neofb", id_table: neofb_devices, probe: neofb_probe, - remove: neofb_remove + remove: __devexit_p(neofb_remove) }; /* **************************** init-time only **************************** */ diff -Nru a/drivers/video/radeonfb.c b/drivers/video/radeonfb.c --- a/drivers/video/radeonfb.c Thu Mar 7 18:17:46 2002 +++ b/drivers/video/radeonfb.c Thu Mar 7 18:17:46 2002 @@ -619,7 +619,7 @@ name: "radeonfb", id_table: radeonfb_pci_table, probe: radeonfb_pci_register, - remove: radeonfb_pci_unregister, + remove: __devexit_p(radeonfb_pci_unregister), }; diff -Nru a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c --- a/drivers/video/riva/fbdev.c Thu Mar 7 18:17:39 2002 +++ b/drivers/video/riva/fbdev.c Thu Mar 7 18:17:39 2002 @@ -1811,7 +1811,7 @@ info = &rinfo->info; strcpy(info->modename, rinfo->drvr_name); - info->node = -1; + info->node = NODEV; info->flags = FBINFO_FLAG_DEFAULT; info->fbops = &riva_fb_ops; @@ -2082,7 +2082,7 @@ name: "rivafb", id_table: rivafb_pci_tbl, probe: rivafb_init_one, - remove: rivafb_remove_one, + remove: __devexit_p(rivafb_remove_one), }; diff -Nru a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c --- a/drivers/video/sis/sis_main.c Thu Mar 7 18:17:45 2002 +++ b/drivers/video/sis/sis_main.c Thu Mar 7 18:17:45 2002 @@ -2766,7 +2766,7 @@ sisfb_crtc_to_var (&default_var); fb_info.changevar = NULL; - fb_info.node = -1; + fb_info.node = NODEV; fb_info.fbops = &sisfb_ops; fb_info.disp = &disp; fb_info.switch_con = &sisfb_switch; diff -Nru a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c --- a/drivers/video/tdfxfb.c Thu Mar 7 18:17:40 2002 +++ b/drivers/video/tdfxfb.c Thu Mar 7 18:17:40 2002 @@ -495,7 +495,7 @@ name: "tdfxfb", id_table: tdfxfb_id_table, probe: tdfxfb_probe, - remove: tdfxfb_remove, + remove: __devexit_p(tdfxfb_remove), }; MODULE_DEVICE_TABLE(pci, tdfxfb_id_table); diff -Nru a/drivers/video/vesafb.c b/drivers/video/vesafb.c --- a/drivers/video/vesafb.c Thu Mar 7 18:17:38 2002 +++ b/drivers/video/vesafb.c Thu Mar 7 18:17:38 2002 @@ -550,7 +550,7 @@ ypan = pmi_setpal = 0; /* not available or some DOS TSR ... */ if (ypan || pmi_setpal) { - pmi_base = (unsigned short*)bus_to_virt(((unsigned long)screen_info.vesapm_seg << 4) + screen_info.vesapm_off); + pmi_base = (unsigned short*)phys_to_virt(((unsigned long)screen_info.vesapm_seg << 4) + screen_info.vesapm_off); pmi_start = (void*)((char*)pmi_base + pmi_base[1]); pmi_pal = (void*)((char*)pmi_base + pmi_base[2]); printk(KERN_INFO "vesafb: pmi: set display start = %p, set palette = %p\n",pmi_start,pmi_pal); diff -Nru a/fs/Config.help b/fs/Config.help --- a/fs/Config.help Thu Mar 7 18:17:46 2002 +++ b/fs/Config.help Thu Mar 7 18:17:46 2002 @@ -859,6 +859,25 @@ hard drives and ADFS-formatted floppy disks. This is experimental codes, so if you're unsure, say N. +JFS filesystem support +CONFIG_JFS_FS + This is a port of IBM's Journaled Filesystem . More information is + available in the file Documentation/filesystems/jfs.txt. + + If you do not intend to use the JFS filesystem, say N. + +JFS Debugging +CONFIG_JFS_DEBUG + If you are experiencing any problems with the JFS filesystem, say + Y here. This will result in additional debugging messages to be + written to the system log. Under normal circumstances, this + results in very little overhead. + +JFS Statistics +CONFIG_JFS_STATISTICS + Enabling this option will cause statistics from the JFS file system + to be made available to the user in the /proc/fs/jfs/ directory. + CONFIG_DEVPTS_FS You should say Y here if you said Y to "Unix98 PTY support" above. You'll then get a virtual file system which can be mounted on diff -Nru a/fs/Config.in b/fs/Config.in --- a/fs/Config.in Thu Mar 7 18:17:38 2002 +++ b/fs/Config.in Thu Mar 7 18:17:38 2002 @@ -54,6 +54,10 @@ dep_mbool ' Microsoft Joliet CDROM extensions' CONFIG_JOLIET $CONFIG_ISO9660_FS dep_mbool ' Transparent decompression extension' CONFIG_ZISOFS $CONFIG_ISO9660_FS +tristate 'JFS filesystem support' CONFIG_JFS_FS +dep_mbool ' JFS debugging' CONFIG_JFS_DEBUG $CONFIG_JFS_FS +dep_mbool ' JFS statistics' CONFIG_JFS_STATISTICS $CONFIG_JFS_FS + tristate 'Minix fs support' CONFIG_MINIX_FS tristate 'FreeVxFS file system support (VERITAS VxFS(TM) compatible)' CONFIG_VXFS_FS @@ -102,6 +106,7 @@ dep_tristate 'NFS server support' CONFIG_NFSD $CONFIG_INET dep_mbool ' Provide NFSv3 server support' CONFIG_NFSD_V3 $CONFIG_NFSD + dep_mbool ' Provide NFS server over TCP support (EXPERIMENTAL)' CONFIG_NFSD_TCP $CONFIG_NFSD $CONFIG_EXPERIMENTAL if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then define_tristate CONFIG_SUNRPC y diff -Nru a/fs/Makefile b/fs/Makefile --- a/fs/Makefile Thu Mar 7 18:17:37 2002 +++ b/fs/Makefile Thu Mar 7 18:17:37 2002 @@ -1,7 +1,7 @@ # # Makefile for the Linux filesystems. # -# 14 Sep 2000, Christoph Hellwig +# 14 Sep 2000, Christoph Hellwig # Rewritten to use lists instead of if-statements. # @@ -67,6 +67,7 @@ subdir-$(CONFIG_REISERFS_FS) += reiserfs subdir-$(CONFIG_DEVPTS_FS) += devpts subdir-$(CONFIG_SUN_OPENPROMFS) += openpromfs +subdir-$(CONFIG_JFS_FS) += jfs obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o diff -Nru a/fs/bad_inode.c b/fs/bad_inode.c --- a/fs/bad_inode.c Thu Mar 7 18:17:39 2002 +++ b/fs/bad_inode.c Thu Mar 7 18:17:39 2002 @@ -17,9 +17,7 @@ */ static int bad_follow_link(struct dentry *dent, struct nameidata *nd) { - dput(nd->dentry); - nd->dentry = dget(dent); - return 0; + return vfs_follow_link(nd, ERR_PTR(-EIO)); } static int return_EIO(void) diff -Nru a/fs/block_dev.c b/fs/block_dev.c --- a/fs/block_dev.c Thu Mar 7 18:17:45 2002 +++ b/fs/block_dev.c Thu Mar 7 18:17:45 2002 @@ -94,7 +94,7 @@ /* Ok, we're actually changing the blocksize.. */ bdev = bdget(kdev_t_to_nr(dev)); - sync_buffers(dev, 2); + sync_buffers(bdev, 2); blksize_size[major(dev)][minor(dev)] = size; bdev->bd_inode->i_blkbits = blksize_bits(size); kill_bdev(bdev); @@ -198,7 +198,7 @@ int ret, err; ret = filemap_fdatasync(inode->i_mapping); - err = sync_buffers(inode->i_rdev, 1); + err = sync_buffers(inode->i_bdev, 1); if (err && !ret) ret = err; err = filemap_fdatawait(inode->i_mapping); @@ -439,6 +439,27 @@ spin_lock(&bdev_lock); if (inode->i_bdev) __bd_forget(inode); + spin_unlock(&bdev_lock); +} + +int bd_claim(struct block_device *bdev, void *holder) +{ + int res = -EBUSY; + spin_lock(&bdev_lock); + if (!bdev->bd_holder || bdev->bd_holder == holder) { + bdev->bd_holder = holder; + bdev->bd_holders++; + res = 0; + } + spin_unlock(&bdev_lock); + return res; +} + +void bd_release(struct block_device *bdev) +{ + spin_lock(&bdev_lock); + if (!--bdev->bd_holders) + bdev->bd_holder = NULL; spin_unlock(&bdev_lock); } diff -Nru a/fs/buffer.c b/fs/buffer.c --- a/fs/buffer.c Thu Mar 7 18:17:37 2002 +++ b/fs/buffer.c Thu Mar 7 18:17:37 2002 @@ -189,7 +189,7 @@ * return without it! */ #define NRSYNC (32) -static int write_some_buffers(kdev_t dev) +static int write_some_buffers(struct block_device *bdev) { struct buffer_head *next; struct buffer_head *array[NRSYNC]; @@ -203,7 +203,7 @@ struct buffer_head * bh = next; next = bh->b_next_free; - if (!kdev_none(dev) && !kdev_same(bh->b_dev, dev)) + if (bdev && bh->b_bdev != bdev) continue; if (test_and_set_bit(BH_Lock, &bh->b_state)) continue; @@ -231,11 +231,11 @@ /* * Write out all buffers on the dirty list. */ -static void write_unlocked_buffers(kdev_t dev) +static void write_unlocked_buffers(struct block_device *bdev) { do { spin_lock(&lru_list_lock); - } while (write_some_buffers(dev)); + } while (write_some_buffers(bdev)); run_task_queue(&tq_disk); } @@ -245,7 +245,7 @@ * This must be called with the LRU lock held, and * will return with it released. */ -static int wait_for_buffers(kdev_t dev, int index, int refile) +static int wait_for_buffers(struct block_device *bdev, int index, int refile) { struct buffer_head * next; int nr; @@ -261,7 +261,7 @@ __refile_buffer(bh); continue; } - if (!kdev_none(dev) && !kdev_same(bh->b_dev, dev)) + if (bdev && bh->b_bdev != bdev) continue; get_bh(bh); @@ -274,17 +274,17 @@ return 0; } -static inline void wait_for_some_buffers(kdev_t dev) +static inline void wait_for_some_buffers(struct block_device *bdev) { spin_lock(&lru_list_lock); - wait_for_buffers(dev, BUF_LOCKED, 1); + wait_for_buffers(bdev, BUF_LOCKED, 1); } -static int wait_for_locked_buffers(kdev_t dev, int index, int refile) +static int wait_for_locked_buffers(struct block_device *bdev, int index, int refile) { do { spin_lock(&lru_list_lock); - } while (wait_for_buffers(dev, index, refile)); + } while (wait_for_buffers(bdev, index, refile)); return 0; } @@ -298,7 +298,29 @@ * We will ultimately want to put these in a separate list, but for * now we search all of the lists for dirty buffers. */ -int sync_buffers(kdev_t dev, int wait) +int sync_buffers(struct block_device *bdev, int wait) +{ + int err = 0; + + if (!bdev) + return 0; + + /* One pass for no-wait, three for wait: + * 0) write out all dirty, unlocked buffers; + * 1) wait for all dirty locked buffers; + * 2) write out all dirty, unlocked buffers; + * 2) wait for completion by waiting for all buffers to unlock. + */ + write_unlocked_buffers(bdev); + if (wait) { + err = wait_for_locked_buffers(bdev, BUF_DIRTY, 0); + write_unlocked_buffers(bdev); + err |= wait_for_locked_buffers(bdev, BUF_LOCKED, 1); + } + return err; +} + +int sync_all_buffers(int wait) { int err = 0; @@ -308,19 +330,18 @@ * 2) write out all dirty, unlocked buffers; * 2) wait for completion by waiting for all buffers to unlock. */ - write_unlocked_buffers(dev); + write_unlocked_buffers(NULL); if (wait) { - err = wait_for_locked_buffers(dev, BUF_DIRTY, 0); - write_unlocked_buffers(dev); - err |= wait_for_locked_buffers(dev, BUF_LOCKED, 1); + err = wait_for_locked_buffers(NULL, BUF_DIRTY, 0); + write_unlocked_buffers(NULL); + err |= wait_for_locked_buffers(NULL, BUF_LOCKED, 1); } return err; } int fsync_super(struct super_block *sb) { - kdev_t dev = sb->s_dev; - sync_buffers(dev, 0); + sync_buffers(sb->s_bdev, 0); lock_kernel(); sync_inodes_sb(sb); @@ -331,48 +352,48 @@ unlock_super(sb); unlock_kernel(); - return sync_buffers(dev, 1); + return sync_buffers(sb->s_bdev, 1); } int fsync_no_super(struct block_device *bdev) { - kdev_t dev = to_kdev_t(bdev->bd_dev); - sync_buffers(dev, 0); - return sync_buffers(dev, 1); + sync_buffers(bdev, 0); + return sync_buffers(bdev, 1); } int fsync_dev(kdev_t dev) { - sync_buffers(dev, 0); - - lock_kernel(); - sync_inodes(dev); - if (!kdev_none(dev)) { - struct super_block *sb = get_super(dev); - if (sb) { - DQUOT_SYNC(sb); - drop_super(sb); - } - } else - DQUOT_SYNC(NULL); - sync_supers(dev); - unlock_kernel(); - - return sync_buffers(dev, 1); + struct block_device *bdev = bdget(kdev_t_to_nr(dev)); + if (bdev) { + int res = fsync_bdev(bdev); + bdput(bdev); + return res; + } + return 0; } -/* - * There's no real reason to pretend we should - * ever do anything differently - */ -void sync_dev(kdev_t dev) +int fsync_bdev(struct block_device *bdev) { - fsync_dev(dev); + struct super_block *sb = get_super(to_kdev_t(bdev->bd_dev)); + if (sb) { + int res = fsync_super(sb); + drop_super(sb); + return res; + } + return fsync_no_super(bdev); } asmlinkage long sys_sync(void) { - fsync_dev(NODEV); + sync_all_buffers(0); + + lock_kernel(); + sync_inodes(); + DQUOT_SYNC(NULL); + sync_supers(); + unlock_kernel(); + + sync_all_buffers(1); return 0; } @@ -384,7 +405,6 @@ { struct inode * inode = dentry->d_inode; struct super_block * sb; - kdev_t dev; int ret; lock_kernel(); @@ -399,8 +419,7 @@ unlock_super(sb); /* .. finally sync the buffers to disk */ - dev = inode->i_dev; - ret = sync_buffers(dev, 1); + ret = sync_buffers(sb->s_bdev, 1); unlock_kernel(); return ret; } @@ -663,7 +682,6 @@ { int i, nlist, slept; struct buffer_head * bh, * bh_next; - kdev_t dev = to_kdev_t(bdev->bd_dev); /* will become bdev */ retry: slept = 0; @@ -676,7 +694,7 @@ bh_next = bh->b_next_free; /* Another device? */ - if (!kdev_same(bh->b_dev, dev)) + if (bh->b_bdev != bdev) continue; /* Not hashed? */ if (!bh->b_pprev) @@ -998,7 +1016,7 @@ /* If we're getting into imbalance, start write-out */ spin_lock(&lru_list_lock); - write_some_buffers(NODEV); + write_some_buffers(NULL); /* * And if we're _really_ out of balance, wait for @@ -1007,7 +1025,7 @@ * This will throttle heavy writers. */ if (state > 0) { - wait_for_some_buffers(NODEV); + wait_for_some_buffers(NULL); wakeup_bdflush(); } } @@ -2616,7 +2634,7 @@ { lock_kernel(); sync_unlocked_inodes(); - sync_supers(NODEV); + sync_supers(); unlock_kernel(); for (;;) { @@ -2626,7 +2644,7 @@ bh = lru_list[BUF_DIRTY]; if (!bh || time_before(jiffies, bh->b_flushtime)) break; - if (write_some_buffers(NODEV)) + if (write_some_buffers(NULL)) continue; return 0; } @@ -2725,8 +2743,8 @@ CHECK_EMERGENCY_SYNC spin_lock(&lru_list_lock); - if (!write_some_buffers(NODEV) || balance_dirty_state() < 0) { - wait_for_some_buffers(NODEV); + if (!write_some_buffers(NULL) || balance_dirty_state() < 0) { + wait_for_some_buffers(NULL); interruptible_sleep_on(&bdflush_wait); } } @@ -2757,7 +2775,7 @@ complete((struct completion *)startup); for (;;) { - wait_for_some_buffers(NODEV); + wait_for_some_buffers(NULL); /* update interval */ interval = bdf_prm.b_un.interval; diff -Nru a/fs/coda/cache.c b/fs/coda/cache.c --- a/fs/coda/cache.c Thu Mar 7 18:17:46 2002 +++ b/fs/coda/cache.c Thu Mar 7 18:17:46 2002 @@ -71,7 +71,6 @@ hit = ((mask & cii->c_cached_perm) == mask) && coda_cred_ok(&cii->c_cached_cred); - CDEBUG(D_CACHE, "%s for ino %ld\n", hit ? "HIT" : "MISS", inode->i_ino); return hit; } @@ -102,9 +101,6 @@ /* don't know what to do with negative dentries */ if ( ! de->d_inode ) continue; - CDEBUG(D_DOWNCALL, "%d for %*s/%*s\n", flag, - de->d_name.len, de->d_name.name, - de->d_parent->d_name.len, de->d_parent->d_name.name); coda_flag_inode(de->d_inode, flag); } spin_unlock(&dcache_lock); diff -Nru a/fs/coda/cnode.c b/fs/coda/cnode.c --- a/fs/coda/cnode.c Thu Mar 7 18:17:40 2002 +++ b/fs/coda/cnode.c Thu Mar 7 18:17:40 2002 @@ -11,9 +11,6 @@ #include #include -extern int coda_debug; -extern int coda_print_entry; - inline int coda_fideq(ViceFid *fid1, ViceFid *fid2) { if (fid1->Vnode != fid2->Vnode) return 0; @@ -42,11 +39,6 @@ /* cnode.c */ static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr) { - CDEBUG(D_SUPER, "ino: %ld\n", inode->i_ino); - - if (coda_debug & D_SUPER ) - print_vattr(attr); - coda_vattr_to_iattr(inode, attr); if (S_ISREG(inode->i_mode)) { @@ -72,10 +64,8 @@ inode = iget4(sb, ino, coda_inocmp, fid); - if ( !inode ) { - CDEBUG(D_CNODE, "coda_iget: no inode\n"); + if (!inode) return ERR_PTR(-ENOMEM); - } /* check if the inode is already initialized */ cii = ITOC(inode); @@ -105,9 +95,6 @@ /* We get inode numbers from Venus -- see venus source */ error = venus_getattr(sb, fid, &attr); if ( error ) { - CDEBUG(D_CNODE, - "coda_cnode_make: coda_getvattr returned %d for %s.\n", - error, coda_f2s(fid)); *inode = NULL; return error; } @@ -117,10 +104,6 @@ printk("coda_cnode_make: coda_iget failed\n"); return PTR_ERR(*inode); } - - CDEBUG(D_DOWNCALL, "Done making inode: ino %ld, count %d with %s\n", - (*inode)->i_ino, atomic_read(&(*inode)->i_count), - coda_f2s(&ITOC(*inode)->c_fid)); return 0; } @@ -155,8 +138,6 @@ return NULL; } - CDEBUG(D_INODE, "%s\n", coda_f2s(fid)); - nr = coda_f2i(fid); inode = iget4(sb, nr, coda_inocmp, fid); if ( !inode ) { @@ -177,7 +158,6 @@ /* we shouldn't see inode collisions anymore */ if ( !coda_fideq(fid, &cii->c_fid) ) BUG(); - CDEBUG(D_INODE, "found %ld\n", inode->i_ino); return inode; } diff -Nru a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c --- a/fs/coda/coda_linux.c Thu Mar 7 18:17:36 2002 +++ b/fs/coda/coda_linux.c Thu Mar 7 18:17:36 2002 @@ -24,9 +24,6 @@ #include /* initialize the debugging variables */ -int coda_debug; -int coda_print_entry; -int coda_access_cache = 1; int coda_fake_statfs; /* print a fid */ @@ -92,35 +89,23 @@ { unsigned short coda_flags = 0; - if ( (flags & O_ACCMODE) == O_RDONLY ){ - CDEBUG(D_FILE, "--> C_O_READ added\n"); + if ((flags & O_ACCMODE) == O_RDONLY) coda_flags |= C_O_READ; - } - if ( (flags & O_ACCMODE) == O_RDWR ) { - CDEBUG(D_FILE, "--> C_O_READ | C_O_WRITE added\n"); + if ((flags & O_ACCMODE) == O_RDWR) coda_flags |= C_O_READ | C_O_WRITE; - } - if ( (flags & O_ACCMODE) == O_WRONLY ){ - CDEBUG(D_FILE, "--> C_O_WRITE added\n"); + if ((flags & O_ACCMODE) == O_WRONLY) coda_flags |= C_O_WRITE; - } - if ( flags & O_TRUNC ) { - CDEBUG(D_FILE, "--> C_O_TRUNC added\n"); + if (flags & O_TRUNC) coda_flags |= C_O_TRUNC; - } - if ( flags & O_CREAT ) { - CDEBUG(D_FILE, "--> C_O_CREAT added\n"); + if (flags & O_CREAT) coda_flags |= C_O_CREAT; - } - if ( flags & O_EXCL ) { + if (flags & O_EXCL) coda_flags |= C_O_EXCL; - CDEBUG(D_FILE, "--> C_O_EXCL added\n"); - } return coda_flags; } diff -Nru a/fs/coda/dir.c b/fs/coda/dir.c --- a/fs/coda/dir.c Thu Mar 7 18:17:38 2002 +++ b/fs/coda/dir.c Thu Mar 7 18:17:38 2002 @@ -50,11 +50,7 @@ static int coda_dentry_delete(struct dentry *); /* support routines */ -static void coda_prepare_fakefile(struct file *coda_file, - struct dentry *open_dentry, - struct file *open_file); -static int coda_venus_readdir(struct file *filp, void *dirent, - filldir_t filldir); +static int coda_venus_readdir(struct file *filp, filldir_t filldir, void *dirent); int coda_fsync(struct file *, struct dentry *dentry, int datasync); int coda_hasmknod; @@ -109,16 +105,10 @@ return ERR_PTR(-ENAMETOOLONG); } - CDEBUG(D_INODE, "name %s, len %ld in ino %ld, fid %s\n", - name, (long)length, dir->i_ino, coda_i2s(dir)); - lock_kernel(); /* control object, create inode on the fly */ if (coda_isroot(dir) && coda_iscontrol(name, length)) { error = coda_cnode_makectl(&res_inode, dir->i_sb); - CDEBUG(D_SPECIAL, - "Lookup on CTL object; dir ino %ld, count %d\n", - dir->i_ino, atomic_read(&dir->i_count)); dropme = 1; goto exit; } @@ -130,8 +120,6 @@ if (!error) { if (type & CODA_NOCACHE) { type &= (~CODA_NOCACHE); - CDEBUG(D_INODE, "dropme set for %s\n", - coda_f2s(&resfid)); dropme = 1; } @@ -141,13 +129,9 @@ return ERR_PTR(error); } } else if (error != -ENOENT) { - CDEBUG(D_INODE, "error for %s(%*s)%d\n", - coda_i2s(dir), (int)length, name, error); unlock_kernel(); return ERR_PTR(error); } - CDEBUG(D_INODE, "lookup: %s is (%s), type %d result %d, dropme %d\n", - name, coda_f2s(&resfid), type, error, dropme); exit: entry->d_time = 0; @@ -164,28 +148,19 @@ int coda_permission(struct inode *inode, int mask) { + umode_t mode = inode->i_mode; int error; - coda_vfs_stat.permission++; + if (!mask) + return 0; - if ( mask == 0 ) - return 0; + coda_vfs_stat.permission++; - if ( coda_access_cache ) { - coda_permission_stat.count++; + if (coda_cache_check(inode, mask)) + return 0; - if ( coda_cache_check(inode, mask) ) { - coda_permission_stat.hit_count++; - return 0; - } - } - - CDEBUG(D_INODE, "mask is %o\n", mask); error = venus_access(inode->i_sb, coda_i2f(inode), mask); - CDEBUG(D_INODE, "fid: %s, ino: %ld (mask: %o) error: %d\n", - coda_i2s(inode), inode->i_ino, mask, error); - if (!error) coda_cache_enter(inode, mask); @@ -203,7 +178,7 @@ /* optimistically we can also act as if our nose bleeds. The * granularity of the mtime is coarse anyways so we might actually be * right most of the time. Note: we only do this for directories. */ - dir->i_mtime = CURRENT_TIME; + dir->i_mtime = dir->i_ctime = CURRENT_TIME; #endif if (link) dir->i_nlink += link; @@ -215,15 +190,13 @@ int error=0; const char *name=de->d_name.name; int length=de->d_name.len; - struct inode *result = NULL; + struct inode *inode; struct ViceFid newfid; struct coda_vattr attrs; lock_kernel(); coda_vfs_stat.create++; - CDEBUG(D_INODE, "name: %s, length %d, mode %o\n", name, length, mode); - if (coda_isroot(dir) && coda_iscontrol(name, length)) { unlock_kernel(); return -EPERM; @@ -233,25 +206,22 @@ 0, mode, 0, &newfid, &attrs); if ( error ) { - CDEBUG(D_INODE, "create: %s, result %d\n", - coda_f2s(&newfid), error); unlock_kernel(); d_drop(de); return error; } - error = coda_cnode_make(&result, &newfid, dir->i_sb); - if ( error ) { + inode = coda_iget(dir->i_sb, &newfid, &attrs); + if ( IS_ERR(inode) ) { unlock_kernel(); d_drop(de); - result = NULL; - return error; + return PTR_ERR(inode); } /* invalidate the directory cnode's attributes */ coda_dir_changed(dir, 0); unlock_kernel(); - d_instantiate(de, result); + d_instantiate(de, inode); return 0; } @@ -260,7 +230,7 @@ int error=0; const char *name=de->d_name.name; int length=de->d_name.len; - struct inode *result = NULL; + struct inode *inode; struct ViceFid newfid; struct coda_vattr attrs; @@ -270,9 +240,6 @@ lock_kernel(); coda_vfs_stat.create++; - CDEBUG(D_INODE, "name: %s, length %d, mode %o, rdev %x\n", - name, length, mode, rdev); - if (coda_isroot(dir) && coda_iscontrol(name, length)) { unlock_kernel(); return -EPERM; @@ -282,32 +249,29 @@ 0, mode, rdev, &newfid, &attrs); if ( error ) { - CDEBUG(D_INODE, "mknod: %s, result %d\n", - coda_f2s(&newfid), error); - d_drop(de); unlock_kernel(); + d_drop(de); return error; } - error = coda_cnode_make(&result, &newfid, dir->i_sb); - if ( error ) { - d_drop(de); - result = NULL; + inode = coda_iget(dir->i_sb, &newfid, &attrs); + if ( IS_ERR(inode) ) { unlock_kernel(); - return error; + d_drop(de); + return PTR_ERR(inode); } /* invalidate the directory cnode's attributes */ coda_dir_changed(dir, 0); unlock_kernel(); - d_instantiate(de, result); + d_instantiate(de, inode); return 0; } static int coda_mkdir(struct inode *dir, struct dentry *de, int mode) { struct inode *inode; - struct coda_vattr attr; + struct coda_vattr attrs; const char *name = de->d_name.name; int len = de->d_name.len; int error; @@ -321,29 +285,21 @@ return -EPERM; } - CDEBUG(D_INODE, "mkdir %s (len %d) in %s, mode %o.\n", - name, len, coda_i2s(dir), mode); - - attr.va_mode = mode; + attrs.va_mode = mode; error = venus_mkdir(dir->i_sb, coda_i2f(dir), - name, len, &newfid, &attr); + name, len, &newfid, &attrs); if ( error ) { - CDEBUG(D_INODE, "mkdir error: %s result %d\n", - coda_f2s(&newfid), error); - d_drop(de); unlock_kernel(); - return error; + d_drop(de); + return error; } - CDEBUG(D_INODE, "mkdir: new dir has fid %s.\n", - coda_f2s(&newfid)); - - error = coda_cnode_make(&inode, &newfid, dir->i_sb); - if ( error ) { - d_drop(de); + inode = coda_iget(dir->i_sb, &newfid, &attrs); + if ( IS_ERR(inode) ) { unlock_kernel(); - return error; + d_drop(de); + return PTR_ERR(inode); } /* invalidate the directory cnode's attributes */ @@ -370,9 +326,6 @@ return -EPERM; } - CDEBUG(D_INODE, "old: fid: %s\n", coda_i2s(inode)); - CDEBUG(D_INODE, "directory: %s\n", coda_i2s(dir_inode)); - error = venus_link(dir_inode->i_sb, coda_i2f(inode), coda_i2f(dir_inode), (const char *)name, len); @@ -387,7 +340,6 @@ inode->i_nlink++; out: - CDEBUG(D_INODE, "link result %d\n",error); unlock_kernel(); return(error); } @@ -415,8 +367,6 @@ return -ENAMETOOLONG; } - CDEBUG(D_INODE, "symname: %s, length: %d\n", symname, symlen); - /* * This entry is now negative. Since we do not create * an inode for the entry we have to drop it. @@ -429,7 +379,6 @@ if ( !error ) coda_dir_changed(dir_inode, 0); - CDEBUG(D_INODE, "in symlink result %d\n",error); unlock_kernel(); return error; } @@ -444,12 +393,8 @@ lock_kernel(); coda_vfs_stat.unlink++; - CDEBUG(D_INODE, " %s in %s, dirino %ld\n", name , - coda_i2s(dir), dir->i_ino); - error = venus_remove(dir->i_sb, coda_i2f(dir), name, len); if ( error ) { - CDEBUG(D_INODE, "upc returned error %d\n", error); unlock_kernel(); return error; } @@ -477,7 +422,6 @@ error = venus_rmdir(dir->i_sb, coda_i2f(dir), name, len); if ( error ) { - CDEBUG(D_INODE, "upc returned error %d\n", error); unlock_kernel(); return error; } @@ -504,11 +448,6 @@ lock_kernel(); coda_vfs_stat.rename++; - CDEBUG(D_INODE, "old: %s, (%d length), new: %s" - "(%d length). old:d_count: %d, new:d_count: %d\n", - old_name, old_length, new_name, new_length, - atomic_read(&old_dentry->d_count), atomic_read(&new_dentry->d_count)); - error = venus_rename(old_dir->i_sb, coda_i2f(old_dir), coda_i2f(new_dir), old_length, new_length, (const char *) old_name, (const char *)new_name); @@ -526,8 +465,6 @@ coda_flag_inode(new_dir, C_VATTR); } } - - CDEBUG(D_INODE, "result %d\n", error); unlock_kernel(); return error; @@ -535,155 +472,108 @@ /* file operations for directories */ -int coda_readdir(struct file *file, void *dirent, filldir_t filldir) +int coda_readdir(struct file *coda_file, void *dirent, filldir_t filldir) { int result = 0; - struct dentry *cdentry; - struct inode *cinode, *inode = file->f_dentry->d_inode; - struct file *cfile, fakefile; - struct coda_inode_info *cii = ITOC(inode); + struct dentry *coda_dentry = coda_file->f_dentry; + struct inode *coda_inode = coda_dentry->d_inode; + struct coda_inode_info *cii = ITOC(coda_inode); + struct file *host_file = cii->c_container; + + BUG_ON(!host_file); coda_vfs_stat.readdir++; - cfile = cii->c_container; - if (!cfile) BUG(); + /* Access to both host and coda f_pos fields is serialized on the + * coda_file->f_dentry->d_inode->i_sem which has already been taken by + * vfs_readdir. Userspace shouldn't 'play' with the container file as + * long as the file is held open. */ + host_file->f_pos = coda_file->f_pos; - cinode = cii->c_container->f_dentry->d_inode; - if ( S_ISREG(cinode->i_mode) ) { + if ( !host_file->f_op->readdir ) /* Venus: we must read Venus dirents from the file */ - cdentry = cii->c_container->f_dentry; - coda_prepare_fakefile(file, cdentry, &fakefile); - - result = coda_venus_readdir(&fakefile, dirent, filldir); - - file->f_pos = fakefile.f_pos; - file->f_version = fakefile.f_version; - } else { - /* potemkin case: we are handed a directory inode */ - result = vfs_readdir(file, filldir, dirent); - } + result = coda_venus_readdir(host_file, filldir, dirent); + else + /* potemkin case: we were handed a directory inode */ + result = vfs_readdir(host_file, filldir, dirent); + coda_file->f_pos = host_file->f_pos; return result; } -/* support routines */ - -/* instantiate a fake file to pass to coda_venus_readdir */ -static void coda_prepare_fakefile(struct file *coda_file, - struct dentry *cont_dentry, - struct file *fake_file) -{ - fake_file->f_dentry = cont_dentry; - fake_file->f_pos = coda_file->f_pos; - fake_file->f_version = coda_file->f_version; - fake_file->f_op = cont_dentry->d_inode->i_fop; - fake_file->f_flags = coda_file->f_flags; - return ; -} - -/* - * this structure is manipulated by filldir in vfs layer. - * the count holds the remaining amount of space in the getdents buffer, - * beyond the current_dir pointer. - * - * What structure is this comment referring to?? -JH - */ +static inline unsigned int CDT2DT(unsigned char cdt) +{ + unsigned int dt; -/* should be big enough to hold any single directory entry */ -#define DIR_BUFSIZE 2048 + switch(cdt) { + case CDT_UNKNOWN: dt = DT_UNKNOWN; break; + case CDT_FIFO: dt = DT_FIFO; break; + case CDT_CHR: dt = DT_CHR; break; + case CDT_DIR: dt = DT_DIR; break; + case CDT_BLK: dt = DT_BLK; break; + case CDT_REG: dt = DT_REG; break; + case CDT_LNK: dt = DT_LNK; break; + case CDT_SOCK: dt = DT_SOCK; break; + case CDT_WHT: dt = DT_WHT; break; + default: dt = DT_UNKNOWN; break; + } + return dt; +} -static int coda_venus_readdir(struct file *filp, void *getdent, - filldir_t filldir) +/* support routines */ +static int coda_venus_readdir(struct file *filp, filldir_t filldir, + void *getdent) { - int bufsize; - int offset = filp->f_pos; /* offset in the directory file */ - int count = 0; - int pos = 0; /* offset in the block we read */ - int result = 0; /* either an error or # of entries returned */ - int errfill; - char *buff = NULL; - struct venus_dirent *vdirent; - int string_offset = (int) (&((struct venus_dirent *)(0))->d_name); - int i; - - CODA_ALLOC(buff, char *, DIR_BUFSIZE); - if ( !buff ) { - printk("coda_venus_readdir: out of memory.\n"); - return -ENOMEM; - } + int result = 0; /* # of entries returned */ + struct venus_dirent *vdir; + unsigned long vdir_size = + (unsigned long)(&((struct venus_dirent *)0)->d_name); + int ret; + + vdir = (struct venus_dirent *)kmalloc(sizeof(*vdir), GFP_KERNEL); + if (!vdir) return -ENOMEM; + + while(1) { + /* we use this routine to read the file into our buffer */ + ret = kernel_read(filp, filp->f_pos, (char *)vdir, + sizeof(*vdir)); + if (ret < 0) { + printk("coda_venus_readdir: read dir failed %d\n", ret); + break; + } + if (ret == 0) break; /* end of directory file reached */ - /* we use this routine to read the file into our buffer */ - bufsize = kernel_read(filp, filp->f_pos, buff, DIR_BUFSIZE); - if ( bufsize < 0) { - printk("coda_venus_readdir: cannot read directory %d.\n", - bufsize); - result = bufsize; - goto exit; - } - if ( bufsize == 0) { - result = 0; - goto exit; - } - - /* Parse and write into user space. Filldir tells us when done! */ - CDEBUG(D_FILE, "buffsize: %d offset %d, count %d.\n", - bufsize, offset, count); - - i = 0; - result = 0; - while ( pos + string_offset < bufsize && i < 1024) { - vdirent = (struct venus_dirent *) (buff + pos); - - /* test if the name is fully in the buffer */ - if ( pos + string_offset + (int) vdirent->d_namlen >= bufsize ){ - if ( result == 0 ) - printk("CODA: Invalid directory cfino: %ld\n", - filp->f_dentry->d_inode->i_ino); - break; - } - /* now we are certain that we can read the entry from buff */ + /* catch truncated reads */ + if (ret < vdir_size || ret < vdir_size + vdir->d_namlen) { + printk("coda_venus_readdir: short read: %ld\n", + filp->f_dentry->d_inode->i_ino); + ret = -EBADF; + break; + } + /* validate whether the directory file actually makes sense */ + if (vdir->d_reclen < vdir_size + vdir->d_namlen || + vdir->d_namlen > CODA_MAXNAMLEN) { + printk("coda_venus_readdir: Invalid directory: %ld\n", + filp->f_dentry->d_inode->i_ino); + ret = -EBADF; + break; + } - /* if we don't have a null entry, copy it */ - if ( vdirent->d_fileno && vdirent->d_reclen ) { - int namlen = vdirent->d_namlen; - off_t offs = filp->f_pos; - ino_t ino = vdirent->d_fileno; - char *name = vdirent->d_name; - - errfill = filldir(getdent, name, namlen, - offs, ino, DT_UNKNOWN); -CDEBUG(D_FILE, "entry %d: ino %ld, namlen %d, reclen %d, type %d, pos %d, string_offs %d, name %*s, offset %d, result: %d, errfill: %d.\n", i,vdirent->d_fileno, vdirent->d_namlen, vdirent->d_reclen, vdirent->d_type, pos, string_offset, vdirent->d_namlen, vdirent->d_name, (u_int) offs, result, errfill); - /* errfill means no space for filling in this round */ - if ( errfill < 0 ) { - result = 0; - break; - } - /* adjust count */ - result++; - } - /* next one */ - filp->f_pos += vdirent->d_reclen; - if ( filp->f_pos > filp->f_dentry->d_inode->i_size ) - break; - if ( !vdirent->d_reclen ) { - printk("CODA: Invalid directory, cfino: %ld\n", - filp->f_dentry->d_inode->i_ino); - result = -EINVAL; - break; + /* skip null entries */ + if (vdir->d_fileno) { + unsigned int d_type = CDT2DT(vdir->d_type); + ret = filldir(getdent, vdir->d_name, vdir->d_namlen, + filp->f_pos, vdir->d_fileno, d_type); + /* failure means no space for filling in this round */ + if (ret < 0) break; + result++; } - pos += (unsigned int) vdirent->d_reclen; - i++; - } - - if ( i >= 1024 ) { - printk("Repeating too much in readdir %ld\n", - filp->f_dentry->d_inode->i_ino); - result = -EINVAL; + /* we'll always have progress because d_reclen is unsigned and + * we've already established it is non-zero. */ + filp->f_pos += vdir->d_reclen; } - -exit: - CODA_FREE(buff, DIR_BUFSIZE); - return result; + kfree(vdir); + return result ? result : ret; } /* called when a cache lookup succeeds */ @@ -701,7 +591,7 @@ goto bad; cii = ITOC(de->d_inode); - if (cii->c_flags & (C_PURGE | C_FLUSH)) + if (!(cii->c_flags & (C_PURGE | C_FLUSH))) goto out; shrink_dcache_parent(de); @@ -710,12 +600,9 @@ if (cii->c_flags & C_FLUSH) coda_flag_inode_children(inode, C_FLUSH); - if (atomic_read(&de->d_count) > 1) { + if (atomic_read(&de->d_count) > 1) /* pretend it's valid, but don't change the flags */ - CDEBUG(D_DOWNCALL, "BOOM for: ino %ld, %s\n", - de->d_inode->i_ino, coda_f2s(&cii->c_fid)); goto out; - } /* clear the flags. */ cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH); @@ -741,9 +628,6 @@ flags = (ITOC(dentry->d_inode)->c_flags) & C_PURGE; if (is_bad_inode(dentry->d_inode) || flags) { - CDEBUG(D_DOWNCALL, "bad inode, unhashing %s/%s, %ld\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - dentry->d_inode->i_ino); return 1; } return 0; @@ -766,10 +650,6 @@ struct inode *inode = dentry->d_inode; struct coda_inode_info *cii = ITOC(inode); - CDEBUG(D_INODE, "revalidating: %*s/%*s\n", - dentry->d_name.len, dentry->d_name.name, - dentry->d_parent->d_name.len, dentry->d_parent->d_name.name); - lock_kernel(); if ( !cii->c_flags ) goto ok; @@ -798,9 +678,7 @@ if (inode->i_ino != old_ino) goto return_bad_inode; - if ( cii->c_flags ) - coda_flag_inode_children(inode, C_FLUSH); - + coda_flag_inode_children(inode, C_FLUSH); cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH); } diff -Nru a/fs/coda/file.c b/fs/coda/file.c --- a/fs/coda/file.c Thu Mar 7 18:17:42 2002 +++ b/fs/coda/file.c Thu Mar 7 18:17:42 2002 @@ -69,6 +69,7 @@ cfile->f_flags = flags; inode->i_size = cinode->i_size; + inode->i_mtime = inode->i_ctime = CURRENT_TIME; up(&inode->i_sem); return ret; @@ -103,12 +104,8 @@ lock_kernel(); coda_vfs_stat.open++; - CDEBUG(D_SPECIAL, "OPEN inode number: %ld, count %d, flags %o.\n", - f->f_dentry->d_inode->i_ino, atomic_read(&f->f_dentry->d_count), flags); - error = venus_open(i->i_sb, coda_i2f(i), coda_flags, &fh); if (error || !fh) { - CDEBUG(D_FILE, "coda_open: venus_open result %d\n", error); unlock_kernel(); return error; } @@ -132,12 +129,6 @@ f->private_data = cred; } - CDEBUG(D_FILE, "result %d, coda i->i_count is %d, cii->contcount is %d for ino %ld\n", - error, atomic_read(&i->i_count), cii->c_contcount, i->i_ino); - CDEBUG(D_FILE, "cache ino: %ld, count %d, ops %p\n", - fh->f_dentry->d_inode->i_ino, - atomic_read(&fh->f_dentry->d_inode->i_count), - fh->f_dentry->d_inode->i_op); unlock_kernel(); return 0; } @@ -174,8 +165,6 @@ cinode = cfile->f_dentry->d_inode; - CDEBUG(D_FILE, "FLUSH coda (file %p ct %d)\n", file, fcnt); - err = venus_store(inode->i_sb, coda_i2f(inode), cflags, (struct coda_cred *)file->private_data); if (err == -EOPNOTSUPP) { @@ -183,7 +172,6 @@ err = 0; } - CDEBUG(D_FILE, "coda_flush: result: %d\n", err); return err; } diff -Nru a/fs/coda/inode.c b/fs/coda/inode.c --- a/fs/coda/inode.c Thu Mar 7 18:17:45 2002 +++ b/fs/coda/inode.c Thu Mar 7 18:17:45 2002 @@ -245,10 +245,6 @@ { struct coda_inode_info *cii = ITOC(inode); - CDEBUG(D_SUPER, " inode->ino: %ld, count: %d\n", - inode->i_ino, atomic_read(&inode->i_count)); - CDEBUG(D_DOWNCALL, "clearing inode: %ld, %x\n", inode->i_ino, cii->c_flags); - if (cii->c_container) BUG(); list_del_init(&cii->c_cilist); @@ -264,9 +260,9 @@ memset(&vattr, 0, sizeof(vattr)); + inode->i_ctime = CURRENT_TIME; coda_iattr_to_vattr(iattr, &vattr); vattr.va_type = C_VNON; /* cannot set type */ - CDEBUG(D_SUPER, "vattr.va_mode %o\n", vattr.va_mode); /* Venus is responsible for truncating the container-file!!! */ error = venus_setattr(inode->i_sb, coda_i2f(inode), &vattr); @@ -275,7 +271,6 @@ coda_vattr_to_iattr(inode, &vattr); coda_cache_clear_inode(inode); } - CDEBUG(D_SUPER, "inode.i_mode %o, error %d\n", inode->i_mode, error); return error; } diff -Nru a/fs/coda/pioctl.c b/fs/coda/pioctl.c --- a/fs/coda/pioctl.c Thu Mar 7 18:17:45 2002 +++ b/fs/coda/pioctl.c Thu Mar 7 18:17:45 2002 @@ -65,8 +65,6 @@ * Look up the pathname. Note that the pathname is in * user memory, and namei takes care of this */ - CDEBUG(D_PIOCTL, "namei, data.follow = %d\n", - data.follow); if ( data.follow ) { error = user_path_walk(data.path, &nd); } else { @@ -74,15 +72,11 @@ } if ( error ) { - CDEBUG(D_PIOCTL, "error: lookup fails.\n"); return error; } else { target_inode = nd.dentry->d_inode; } - CDEBUG(D_PIOCTL, "target ino: 0x%ld, dev: 0x%x\n", - target_inode->i_ino, kdev_val(target_inode->i_dev)); - /* return if it is not a Coda inode */ if ( target_inode->i_sb != inode->i_sb ) { path_release(&nd); @@ -94,9 +88,6 @@ error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data); - CDEBUG(D_PIOCTL, "ioctl on inode %ld\n", target_inode->i_ino); - CDEBUG(D_DOWNCALL, "dput on ino: %ld, icount %d, dcount %d\n", target_inode->i_ino, - atomic_read(&target_inode->i_count), atomic_read(&nd.dentry->d_count)); path_release(&nd); return error; } diff -Nru a/fs/coda/psdev.c b/fs/coda/psdev.c --- a/fs/coda/psdev.c Thu Mar 7 18:17:45 2002 +++ b/fs/coda/psdev.c Thu Mar 7 18:17:45 2002 @@ -114,9 +114,6 @@ if (copy_from_user(&hdr, buf, 2 * sizeof(u_long))) return -EFAULT; - CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%ld,%ld), nbytes %ld\n", - current->pid, hdr.opcode, hdr.unique, (long)nbytes); - if (DOWNCALL(hdr.opcode)) { struct super_block *sb = NULL; union outputArgs *dcbuf; @@ -124,11 +121,9 @@ sb = vcp->vc_sb; if ( !sb ) { - CDEBUG(D_PSDEV, "coda_psdev_write: downcall, no SB!\n"); count = nbytes; goto out; } - CDEBUG(D_PSDEV, "handling downcall\n"); if ( nbytes < sizeof(struct coda_out_hdr) ) { printk("coda_downcall opc %ld uniq %ld, not enough!\n", @@ -182,8 +177,6 @@ goto out; } - CDEBUG(D_PSDEV,"Eureka: uniq %ld on queue!\n", hdr.unique); - /* move data into response buffer. */ if (req->uc_outSize < nbytes) { printk("psdev_write: too much cnt: %d, cnt: %ld, opc: %ld, uniq: %ld.\n", @@ -209,10 +202,6 @@ outp->fh = fget(outp->fd); } - CDEBUG(D_PSDEV, - "Found! Count %ld for (opc,uniq)=(%ld,%ld), upc_req at %p\n", - (long)count, hdr.opcode, hdr.unique, &req); - wake_up(&req->uc_sleep); out: return(count ? count : retval); @@ -277,9 +266,6 @@ goto out; } - CDEBUG(D_PSDEV, "vcread: signal msg (%d, %d)\n", - req->uc_opcode, req->uc_unique); - CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr)); upc_free(req); out: @@ -315,8 +301,6 @@ file->private_data = vcp; - CDEBUG(D_PSDEV, "device %i - inuse: %d\n", idx, vcp->vc_inuse); - unlock_kernel(); return 0; } @@ -335,14 +319,12 @@ return -1; } - CDEBUG(D_PSDEV, "psdev_release: inuse %d\n", vcp->vc_inuse); if (--vcp->vc_inuse) { unlock_kernel(); return 0; } /* Wakeup clients so they can return. */ - CDEBUG(D_PSDEV, "wake up pending clients\n"); lh = vcp->vc_pending.next; next = lh; while ( (lh = next) != &vcp->vc_pending) { @@ -359,13 +341,11 @@ } lh = &vcp->vc_processing; - CDEBUG(D_PSDEV, "wake up processing clients\n"); while ( (lh = lh->next) != &vcp->vc_processing) { req = list_entry(lh, struct upc_req, uc_chain); req->uc_flags |= REQ_ABORT; wake_up(&req->uc_sleep); } - CDEBUG(D_PSDEV, "Done.\n"); unlock_kernel(); return 0; diff -Nru a/fs/coda/sysctl.c b/fs/coda/sysctl.c --- a/fs/coda/sysctl.c Thu Mar 7 18:17:42 2002 +++ b/fs/coda/sysctl.c Thu Mar 7 18:17:42 2002 @@ -37,26 +37,18 @@ #define FS_CODA 1 /* Coda file system */ -#define CODA_DEBUG 1 /* control debugging */ -#define CODA_ENTRY 2 /* control enter/leave pattern */ #define CODA_TIMEOUT 3 /* timeout on upcalls to become intrble */ -#define CODA_MC 4 /* use/do not use the access cache */ #define CODA_HARD 5 /* mount type "hard" or "soft" */ #define CODA_VFS 6 /* vfs statistics */ #define CODA_UPCALL 7 /* upcall statistics */ -#define CODA_PERMISSION 8 /* permission statistics */ #define CODA_CACHE_INV 9 /* cache invalidation statistics */ #define CODA_FAKE_STATFS 10 /* don't query venus for actual cache usage */ static ctl_table coda_table[] = { - {CODA_DEBUG, "debug", &coda_debug, sizeof(int), 0644, NULL, &proc_dointvec}, - {CODA_ENTRY, "printentry", &coda_print_entry, sizeof(int), 0644, NULL, &proc_dointvec}, - {CODA_MC, "accesscache", &coda_access_cache, sizeof(int), 0644, NULL, &proc_dointvec}, {CODA_TIMEOUT, "timeout", &coda_timeout, sizeof(int), 0644, NULL, &proc_dointvec}, {CODA_HARD, "hard", &coda_hard, sizeof(int), 0644, NULL, &proc_dointvec}, {CODA_VFS, "vfs_stats", NULL, 0, 0644, NULL, &do_reset_coda_vfs_stats}, {CODA_UPCALL, "upcall_stats", NULL, 0, 0644, NULL, &do_reset_coda_upcall_stats}, - {CODA_PERMISSION, "permission_stats", NULL, 0, 0644, NULL, &do_reset_coda_permission_stats}, {CODA_CACHE_INV, "cache_inv_stats", NULL, 0, 0644, NULL, &do_reset_coda_cache_inv_stats}, {CODA_FAKE_STATFS, "fake_statfs", &coda_fake_statfs, sizeof(int), 0600, NULL, &proc_dointvec}, { 0 } @@ -68,7 +60,6 @@ }; struct coda_vfs_stats coda_vfs_stat; -struct coda_permission_stats coda_permission_stat; struct coda_cache_inv_stats coda_cache_inv_stat; struct coda_upcall_stats_entry coda_upcall_stat[CODA_NCALLS]; struct coda_upcallstats coda_callstats; @@ -126,11 +117,6 @@ memset( &coda_upcall_stat, 0, sizeof( coda_upcall_stat ) ); } -void reset_coda_permission_stats( void ) -{ - memset( &coda_permission_stat, 0, sizeof( coda_permission_stat ) ); -} - void reset_coda_cache_inv_stats( void ) { memset( &coda_cache_inv_stat, 0, sizeof( coda_cache_inv_stat ) ); @@ -141,7 +127,6 @@ unsigned long runtime ) { unsigned long time = runtime; /* time in us */ - CDEBUG(D_SPECIAL, "time: %ld\n", time); if ( pentry->count == 0 ) { pentry->time_sum = pentry->time_squared_sum = 0; @@ -257,21 +242,6 @@ return 0; } -int do_reset_coda_permission_stats( ctl_table * table, int write, - struct file * filp, void * buffer, - size_t * lenp ) -{ - if ( write ) { - reset_coda_permission_stats(); - - filp->f_pos += *lenp; - } else { - *lenp = 0; - } - - return 0; -} - int do_reset_coda_cache_inv_stats( ctl_table * table, int write, struct file * filp, void * buffer, size_t * lenp ) @@ -394,35 +364,6 @@ return len; } -int coda_permission_stats_get_info( char * buffer, char ** start, off_t offset, - int length) -{ - int len=0; - off_t begin; - struct coda_permission_stats * ps = & coda_permission_stat; - - /* this works as long as we are below 1024 characters! */ - len += sprintf( buffer, - "Coda permission statistics\n" - "==========================\n\n" - "count\t\t%9d\n" - "hit count\t%9d\n", - - ps->count, - ps->hit_count ); - - begin = offset; - *start = buffer + begin; - len -= begin; - - if ( len > length ) - len = length; - if ( len < 0 ) - len = 0; - - return len; -} - int coda_cache_inv_stats_get_info( char * buffer, char ** start, off_t offset, int length) { @@ -484,7 +425,6 @@ memset(&coda_callstats, 0, sizeof(coda_callstats)); reset_coda_vfs_stats(); reset_coda_upcall_stats(); - reset_coda_permission_stats(); reset_coda_cache_inv_stats(); #ifdef CONFIG_PROC_FS @@ -493,7 +433,6 @@ proc_fs_coda->owner = THIS_MODULE; coda_proc_create("vfs_stats", coda_vfs_stats_get_info); coda_proc_create("upcall_stats", coda_upcall_stats_get_info); - coda_proc_create("permission_stats", coda_permission_stats_get_info); coda_proc_create("cache_inv_stats", coda_cache_inv_stats_get_info); } #endif @@ -516,7 +455,6 @@ #if CONFIG_PROC_FS remove_proc_entry("cache_inv_stats", proc_fs_coda); - remove_proc_entry("permission_stats", proc_fs_coda); remove_proc_entry("upcall_stats", proc_fs_coda); remove_proc_entry("vfs_stats", proc_fs_coda); remove_proc_entry("coda", proc_root_fs); diff -Nru a/fs/coda/upcall.c b/fs/coda/upcall.c --- a/fs/coda/upcall.c Thu Mar 7 18:17:46 2002 +++ b/fs/coda/upcall.c Thu Mar 7 18:17:46 2002 @@ -89,8 +89,6 @@ printk("coda_get_rootfid: error %d\n", error); } else { *fidp = (ViceFid) outp->coda_root.VFid; - CDEBUG(D_SUPER, "VolumeId: %lx, VnodeId: %lx.\n", - fidp->Volume, fidp->Vnode); } CODA_FREE(inp, insize); @@ -131,7 +129,6 @@ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - CDEBUG(D_SUPER, " result %d\n", error); CODA_FREE(inp, insize); return error; } @@ -313,8 +310,6 @@ memcpy((char *)(inp) + offset, new_name, new_length); *((char *)inp + offset + new_length) = '\0'; - CDEBUG(D_INODE, "destname in packet: %s\n", - (char *)inp + (int) inp->coda_rename.destname); error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); @@ -426,7 +421,6 @@ *(buffer + retlen) = '\0'; } - CDEBUG(D_INODE, " result %d\n",error); CODA_FREE(inp, insize); return error; } @@ -455,7 +449,6 @@ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - CDEBUG(D_INODE, " result %d\n",error); CODA_FREE(inp, insize); return error; } @@ -491,7 +484,6 @@ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - CDEBUG(D_INODE, " result %d\n",error); CODA_FREE(inp, insize); return error; } @@ -580,9 +572,6 @@ /* Copy out the OUT buffer. */ if (outp->coda_ioctl.len > data->vi.out_size) { - CDEBUG(D_FILE, "return len %d <= request len %d\n", - outp->coda_ioctl.len, - data->vi.out_size); error = -EINVAL; } else { error = verify_area(VERIFY_WRITE, data->vi.out, @@ -623,7 +612,6 @@ printk("coda_statfs: Venus returns: %d\n", error); } - CDEBUG(D_INODE, " result %d\n",error); CODA_FREE(inp, insize); return error; } @@ -684,10 +672,6 @@ end.tv_usec -= begin.tv_usec; } - CDEBUG(D_SPECIAL, "begin: %ld.%06ld, elapsed: %ld.%06ld\n", - begin.tv_sec, (unsigned long)begin.tv_usec, - end.tv_sec, (unsigned long)end.tv_usec); - return ((end.tv_sec * 1000000) + end.tv_usec); } @@ -738,10 +722,6 @@ /* Append msg to pending queue and poke Venus. */ list_add(&(req->uc_chain), vcommp->vc_pending.prev); - CDEBUG(D_UPCALL, - "Proc %d wake Venus for(opc,uniq) =(%d,%d) msg at %p.zzz.\n", - current->pid, req->uc_opcode, req->uc_unique, req); - wake_up_interruptible(&vcommp->vc_waitq); /* We can be interrupted while we wait for Venus to process * our request. If the interrupt occurs before Venus has read @@ -756,29 +736,17 @@ runtime = coda_waitfor_upcall(req, vcommp); coda_upcall_stats(((union inputArgs *)buffer)->ih.opcode, runtime); - CDEBUG(D_TIMING, "opc: %d time: %ld uniq: %d size: %d\n", - req->uc_opcode, jiffies - req->uc_posttime, - req->uc_unique, req->uc_outSize); - CDEBUG(D_UPCALL, - "..process %d woken up by Venus for req at %p, data at %p\n", - current->pid, req, req->uc_data); if (vcommp->vc_inuse) { /* i.e. Venus is still alive */ /* Op went through, interrupt or not... */ if (req->uc_flags & REQ_WRITE) { out = (union outputArgs *)req->uc_data; /* here we map positive Venus errors to kernel errors */ error = -out->oh.result; - CDEBUG(D_UPCALL, - "upcall: (u,o,r) (%ld, %ld, %ld) out at %p\n", - out->oh.unique, out->oh.opcode, out->oh.result, out); *outSize = req->uc_outSize; goto exit; } if ( !(req->uc_flags & REQ_READ) && signal_pending(current)) { /* Interrupted before venus read it. */ - CDEBUG(D_UPCALL, - "Interrupted before read:(op,un) (%d.%d), flags = %x\n", - req->uc_opcode, req->uc_unique, req->uc_flags); list_del(&(req->uc_chain)); /* perhaps the best way to convince the app to give up? */ @@ -790,10 +758,6 @@ union inputArgs *sig_inputArgs; struct upc_req *sig_req; - CDEBUG(D_UPCALL, - "Sending Venus a signal: op = %d.%d, flags = %x\n", - req->uc_opcode, req->uc_unique, req->uc_flags); - list_del(&(req->uc_chain)); error = -ENOMEM; sig_req = upc_alloc(); @@ -815,9 +779,6 @@ sig_req->uc_unique = sig_inputArgs->ih.unique; sig_req->uc_inSize = sizeof(struct coda_in_hdr); sig_req->uc_outSize = sizeof(struct coda_in_hdr); - CDEBUG(D_UPCALL, - "coda_upcall: enqueing signal msg (%d, %d)\n", - sig_req->uc_opcode, sig_req->uc_unique); /* insert at head of queue! */ list_add(&(sig_req->uc_chain), &vcommp->vc_pending); @@ -876,16 +837,13 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) { /* Handle invalidation requests. */ - if ( !sb || !sb->s_root || !sb->s_root->d_inode) { - CDEBUG(D_DOWNCALL, "coda_downcall: opcode %d, no sb!\n", opcode); + if ( !sb || !sb->s_root || !sb->s_root->d_inode) return 0; - } switch (opcode) { case CODA_FLUSH : { clstats(CODA_FLUSH); - CDEBUG(D_DOWNCALL, "CODA_FLUSH\n"); coda_cache_clear_all(sb, NULL); shrink_dcache_sb(sb); coda_flag_inode(sb->s_root->d_inode, C_FLUSH); @@ -894,7 +852,6 @@ case CODA_PURGEUSER : { struct coda_cred *cred = &out->coda_purgeuser.cred; - CDEBUG(D_DOWNCALL, "CODA_PURGEUSER\n"); if ( !cred ) { printk("PURGEUSER: null cred!\n"); return 0; @@ -907,19 +864,14 @@ case CODA_ZAPDIR : { struct inode *inode; ViceFid *fid = &out->coda_zapdir.CodaFid; - CDEBUG(D_DOWNCALL, "zapdir: fid = %s...\n", coda_f2s(fid)); clstats(CODA_ZAPDIR); inode = coda_fid_to_inode(fid, sb); if (inode) { - CDEBUG(D_DOWNCALL, "zapdir: inode = %ld children flagged\n", - inode->i_ino); coda_flag_inode_children(inode, C_PURGE); - CDEBUG(D_DOWNCALL, "zapdir: inode = %ld cache cleared\n", inode->i_ino); coda_flag_inode(inode, C_VATTR); iput(inode); - } else - CDEBUG(D_DOWNCALL, "zapdir: no inode\n"); + } return(0); } @@ -928,27 +880,20 @@ struct inode *inode; struct ViceFid *fid = &out->coda_zapfile.CodaFid; clstats(CODA_ZAPFILE); - CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", coda_f2s(fid)); inode = coda_fid_to_inode(fid, sb); if ( inode ) { - CDEBUG(D_DOWNCALL, "zapfile: inode = %ld\n", - inode->i_ino); coda_flag_inode(inode, C_VATTR); iput(inode); - } else - CDEBUG(D_DOWNCALL, "zapfile: no inode\n"); + } return 0; } case CODA_PURGEFID : { struct inode *inode; ViceFid *fid = &out->coda_purgefid.CodaFid; - CDEBUG(D_DOWNCALL, "purgefid: fid = %s\n", coda_f2s(fid)); clstats(CODA_PURGEFID); inode = coda_fid_to_inode(fid, sb); if ( inode ) { - CDEBUG(D_DOWNCALL, "purgefid: inode = %ld\n", - inode->i_ino); coda_flag_inode_children(inode, C_PURGE); /* catch the dentries later if some are still busy */ @@ -956,8 +901,7 @@ d_prune_aliases(inode); iput(inode); - } else - CDEBUG(D_DOWNCALL, "purgefid: no inode\n"); + } return 0; } @@ -966,16 +910,11 @@ ViceFid *oldfid = &out->coda_replace.OldFid; ViceFid *newfid = &out->coda_replace.NewFid; clstats(CODA_REPLACE); - CDEBUG(D_DOWNCALL, "CODA_REPLACE\n"); inode = coda_fid_to_inode(oldfid, sb); if ( inode ) { - CDEBUG(D_DOWNCALL, "replacefid: inode = %ld\n", - inode->i_ino); coda_replace_fid(inode, oldfid, newfid); iput(inode); - }else - CDEBUG(D_DOWNCALL, "purgefid: no inode\n"); - + } return 0; } } diff -Nru a/fs/cramfs/README b/fs/cramfs/README --- a/fs/cramfs/README Thu Mar 7 18:17:40 2002 +++ b/fs/cramfs/README Thu Mar 7 18:17:40 2002 @@ -6,8 +6,8 @@ swapped around (though it does care that directory entries (inodes) in a given directory are contiguous, as this is used by readdir). -All data is in host-endian format; neither mkcramfs nor the kernel -ever do swabbing. (See section `Block Size' below.) +All data is currently in host-endian format; neither mkcramfs nor the +kernel ever do swabbing. (See section `Block Size' below.) : @@ -29,6 +29,10 @@ lines); put another way, the same order as `find -type d -exec ls -AU1 {} \;'. +Beginning in 2.4.7, directory entries are sorted. This optimization +allows cramfs_lookup to return more quickly when a filename does not +exist, speeds up user-space directory sorts, etc. + : One for each file that's either a symlink or a regular file of non-zero st_size. @@ -63,17 +67,15 @@ This kernel supports cramfs holes (i.e. [efficient representation of] blocks in uncompressed data consisting entirely of NUL bytes), but by default mkcramfs doesn't test for & create holes, since cramfs in -kernels up to at least 2.3.39 didn't support holes. Compile mkcramfs -with -DDO_HOLES if you want it to create files that can have holes in -them. +kernels up to at least 2.3.39 didn't support holes. Run mkcramfs +with -z if you want it to create files that can have holes in them. Tools ----- -If you're hacking on cramfs, you might find useful some tools for -testing cramfs at , including a -rudimentary fsck for cramfs. +The cramfs user-space tools, including mkcramfs and cramfsck, are +located at . Future Development @@ -103,8 +105,8 @@ PAGE_CACHE_SIZE (4096)' to `#include '. The disadvantage is that the generated cramfs cannot always be shared between different kernels, not even necessarily kernels of the same architecture if -PAGE_CACHE_SIZE is subject to change between kernel versions. - +PAGE_CACHE_SIZE is subject to change between kernel versions +(currently possible with arm and ia64). The remaining options try to make cramfs more sharable. diff -Nru a/fs/cramfs/inode.c b/fs/cramfs/inode.c --- a/fs/cramfs/inode.c Thu Mar 7 18:17:37 2002 +++ b/fs/cramfs/inode.c Thu Mar 7 18:17:37 2002 @@ -117,7 +117,7 @@ unsigned i, blocknr, buffer, unread; unsigned long devsize; unsigned int major, minor; - + char *data; if (!len) @@ -187,7 +187,7 @@ } return read_buffers[buffer] + offset; } - + static int cramfs_fill_super(struct super_block *sb, void *data, int silent) { @@ -266,7 +266,7 @@ buf->f_bavail = 0; buf->f_files = sb->CRAMFS_SB_FILES; buf->f_ffree = 0; - buf->f_namelen = 255; + buf->f_namelen = CRAMFS_MAXPATHLEN; return 0; } @@ -476,4 +476,3 @@ module_init(init_cramfs_fs) module_exit(exit_cramfs_fs) MODULE_LICENSE("GPL"); - diff -Nru a/fs/dcache.c b/fs/dcache.c --- a/fs/dcache.c Thu Mar 7 18:17:38 2002 +++ b/fs/dcache.c Thu Mar 7 18:17:38 2002 @@ -30,6 +30,7 @@ /* #define DCACHE_DEBUG 1 */ spinlock_t dcache_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; +rwlock_t dparent_lock __cacheline_aligned_in_smp = RW_LOCK_UNLOCKED; /* Right now the dcache depends on the kernel lock */ #define check_lock() if (!kernel_locked()) BUG() @@ -916,7 +917,9 @@ /* Switch the parents and the names.. */ switch_names(dentry, target); + write_lock(&dparent_lock); do_switch(dentry->d_parent, target->d_parent); + write_unlock(&dparent_lock); do_switch(dentry->d_name.len, target->d_name.len); do_switch(dentry->d_name.hash, target->d_name.hash); diff -Nru a/fs/exec.c b/fs/exec.c --- a/fs/exec.c Thu Mar 7 18:17:42 2002 +++ b/fs/exec.c Thu Mar 7 18:17:42 2002 @@ -343,15 +343,11 @@ struct file *open_exec(const char *name) { struct nameidata nd; - struct inode *inode; - struct file *file; - int err = 0; - - if (path_init(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd)) - err = path_walk(name, &nd); - file = ERR_PTR(err); + int err = path_lookup(name, LOOKUP_FOLLOW, &nd); + struct file *file = ERR_PTR(err); + if (!err) { - inode = nd.dentry->d_inode; + struct inode *inode = nd.dentry->d_inode; file = ERR_PTR(-EACCES); if (!(nd.mnt->mnt_flags & MNT_NOEXEC) && S_ISREG(inode->i_mode)) { @@ -513,23 +509,49 @@ /* * An execve() will automatically "de-thread" the process. - * Note: we don't have to hold the tasklist_lock to test - * whether we migth need to do this. If we're not part of - * a thread group, there is no way we can become one - * dynamically. And if we are, we only need to protect the - * unlink - even if we race with the last other thread exit, - * at worst the list_del_init() might end up being a no-op. + * - if a master thread (PID==TGID) is doing this, then all subsidiary threads + * will be killed (otherwise there will end up being two independent thread + * groups with the same TGID). + * - if a subsidary thread is doing this, then it just leaves the thread group */ -static inline void de_thread(struct task_struct *tsk) +static void de_thread(struct task_struct *tsk) { - if (!list_empty(&tsk->thread_group)) { - write_lock_irq(&tasklist_lock); + struct task_struct *sub; + struct list_head *head, *ptr; + struct siginfo info; + int pause; + + write_lock_irq(&tasklist_lock); + + if (tsk->tgid != tsk->pid) { + /* subsidiary thread - just escapes the group */ + list_del_init(&tsk->thread_group); + tsk->tgid = tsk->pid; + pause = 0; + } + else { + /* master thread - kill all subsidiary threads */ + info.si_signo = SIGKILL; + info.si_errno = 0; + info.si_code = SI_DETHREAD; + info.si_pid = current->pid; + info.si_uid = current->uid; + + head = tsk->thread_group.next; list_del_init(&tsk->thread_group); - write_unlock_irq(&tasklist_lock); + + list_for_each(ptr,head) { + sub = list_entry(ptr,struct task_struct,thread_group); + send_sig_info(SIGKILL,&info,sub); + } + + pause = 1; } - /* Minor oddity: this might stay the same. */ - tsk->tgid = tsk->pid; + write_unlock_irq(&tasklist_lock); + + /* give the subsidiary threads a chance to clean themselves up */ + if (pause) yield(); } int flush_old_exec(struct linux_binprm * bprm) @@ -570,7 +592,8 @@ flush_thread(); - de_thread(current); + if (!list_empty(¤t->thread_group)) + de_thread(current); if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || permission(bprm->file->f_dentry->d_inode,MAY_READ)) diff -Nru a/fs/ext2/ext2.h b/fs/ext2/ext2.h --- a/fs/ext2/ext2.h Thu Mar 7 18:17:44 2002 +++ b/fs/ext2/ext2.h Thu Mar 7 18:17:44 2002 @@ -63,7 +63,6 @@ /* fsync.c */ extern int ext2_sync_file (struct file *, struct dentry *, int); -extern int ext2_fsync_inode (struct inode *, int); /* ialloc.c */ extern struct inode * ext2_new_inode (struct inode *, int); diff -Nru a/fs/ext2/fsync.c b/fs/ext2/fsync.c --- a/fs/ext2/fsync.c Thu Mar 7 18:17:42 2002 +++ b/fs/ext2/fsync.c Thu Mar 7 18:17:42 2002 @@ -35,11 +35,6 @@ int ext2_sync_file(struct file * file, struct dentry *dentry, int datasync) { struct inode *inode = dentry->d_inode; - return ext2_fsync_inode(inode, datasync); -} - -int ext2_fsync_inode(struct inode *inode, int datasync) -{ int err; err = fsync_inode_buffers(inode); diff -Nru a/fs/fat/inode.c b/fs/fat/inode.c --- a/fs/fat/inode.c Thu Mar 7 18:17:43 2002 +++ b/fs/fat/inode.c Thu Mar 7 18:17:43 2002 @@ -516,7 +516,9 @@ fh[1] = inode->i_generation; fh[2] = MSDOS_I(inode)->i_location; fh[3] = MSDOS_I(inode)->i_logstart; + read_lock(&dparent_lock); fh[4] = MSDOS_I(de->d_parent->d_inode)->i_logstart; + read_unlock(&dparent_lock); return 3; } @@ -625,6 +627,18 @@ } b = (struct fat_boot_sector *) bh->b_data; + if (!b->reserved) { + if (!silent) + printk("FAT: bogus number of reserved sectors\n"); + brelse(bh); + goto out_invalid; + } + if (!b->fats) { + if (!silent) + printk("FAT: bogus number of FAT structure\n"); + brelse(bh); + goto out_invalid; + } if (!b->secs_track) { if (!silent) printk("FAT: bogus sectors-per-track value\n"); diff -Nru a/fs/file.c b/fs/file.c --- a/fs/file.c Thu Mar 7 18:17:38 2002 +++ b/fs/file.c Thu Mar 7 18:17:38 2002 @@ -1,5 +1,5 @@ /* - * linux/fs/open.c + * linux/fs/file.c * * Copyright (C) 1998-1999, Stephen Tweedie and Bill Hawes * diff -Nru a/fs/hpfs/ea.c b/fs/hpfs/ea.c --- a/fs/hpfs/ea.c Thu Mar 7 18:17:39 2002 +++ b/fs/hpfs/ea.c Thu Mar 7 18:17:39 2002 @@ -9,8 +9,8 @@ #include #include "hpfs_fn.h" -/* Remove external extended attributes. ano specifies wheter a is a - direct sector where eas start or an anode */ +/* Remove external extended attributes. ano specifies whether a is a + direct sector where eas starts or an anode */ void hpfs_ea_ext_remove(struct super_block *s, secno a, int ano, unsigned len) { diff -Nru a/fs/inode.c b/fs/inode.c --- a/fs/inode.c Thu Mar 7 18:17:44 2002 +++ b/fs/inode.c Thu Mar 7 18:17:44 2002 @@ -144,6 +144,8 @@ INIT_LIST_HEAD(&inode->i_devices); sema_init(&inode->i_sem, 1); spin_lock_init(&inode->i_data.i_shared_lock); + INIT_LIST_HEAD(&inode->i_data.i_mmap); + INIT_LIST_HEAD(&inode->i_data.i_mmap_shared); } static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) @@ -289,15 +291,15 @@ static inline void sync_one(struct inode *inode, int sync) { - if (inode->i_state & I_LOCK) { + while (inode->i_state & I_LOCK) { __iget(inode); spin_unlock(&inode_lock); __wait_on_inode(inode); iput(inode); spin_lock(&inode_lock); - } else { - __sync_one(inode, sync); } + + __sync_one(inode, sync); } static inline void sync_list(struct list_head *head) @@ -417,23 +419,15 @@ * writes them out, and puts them back on the normal list. */ -void sync_inodes(kdev_t dev) +void sync_inodes(void) { struct super_block * s; - /* * Search the super_blocks array for the device(s) to sync. */ - if (!kdev_none(dev)) { - if ((s = get_super(dev)) != NULL) { - sync_inodes_sb(s); - drop_super(s); - } - } else { - while ((s = get_super_to_sync()) != NULL) { - sync_inodes_sb(s); - drop_super(s); - } + while ((s = get_super_to_sync()) != NULL) { + sync_inodes_sb(s); + drop_super(s); } } diff -Nru a/fs/intermezzo/cache.c b/fs/intermezzo/cache.c --- a/fs/intermezzo/cache.c Thu Mar 7 18:17:43 2002 +++ b/fs/intermezzo/cache.c Thu Mar 7 18:17:43 2002 @@ -44,16 +44,15 @@ #define CACHES_MASK CACHES_SIZE - 1 static struct list_head presto_caches[CACHES_SIZE]; -static inline int presto_cache_hash(kdev_t dev) +static inline int presto_cache_hash(struct super_block *sb) { - return (CACHES_MASK) & ((0x000F & (kdev_val(dev))) + ((0x0F00 & (kdev_val(dev))) >>8)); + return (CACHES_MASK) & ((unsigned long)sb / L1_CACHE_BYTES); } -inline void presto_cache_add(struct presto_cache *cache, kdev_t dev) +inline void presto_cache_add(struct presto_cache *cache, struct super_block *sb) { list_add(&cache->cache_chain, - &presto_caches[presto_cache_hash(dev)]); - cache->cache_dev = dev; + &presto_caches[presto_cache_hash(sb)]); } inline void presto_init_cache_hash(void) @@ -65,17 +64,16 @@ } /* map a device to a cache */ -struct presto_cache *presto_find_cache(kdev_t dev) +struct presto_cache *presto_find_cache(struct super_block *sb) { struct presto_cache *cache; struct list_head *lh, *tmp; - lh = tmp = &(presto_caches[presto_cache_hash(dev)]); + lh = tmp = &(presto_caches[presto_cache_hash(sb)]); while ( (tmp = lh->next) != lh ) { cache = list_entry(tmp, struct presto_cache, cache_chain); - if ( kdev_same(cache->cache_dev, dev) ) { + if (cache->cache_sb == sb) return cache; - } } return NULL; } @@ -87,10 +85,10 @@ struct presto_cache *cache; /* find the correct presto_cache here, based on the device */ - cache = presto_find_cache(inode->i_dev); + cache = presto_find_cache(inode->i_sb); if ( !cache ) { - printk("WARNING: no presto cache for dev %x, ino %ld\n", - kdev_val(inode->i_dev), inode->i_ino); + printk("WARNING: no presto cache for dev %s, ino %ld\n", + inode->i_sb->s_id, inode->i_ino); EXIT; return NULL; } @@ -174,7 +172,7 @@ cache = presto_get_cache(inode); if ( !cache ) return 0; - return (kdev_same(inode->i_dev, cache->cache_dev)); + return (inode->i_sb == cache->cache_sb); } /* setup a cache structure when we need one */ diff -Nru a/fs/intermezzo/inode.c b/fs/intermezzo/inode.c --- a/fs/intermezzo/inode.c Thu Mar 7 18:17:37 2002 +++ b/fs/intermezzo/inode.c Thu Mar 7 18:17:37 2002 @@ -113,7 +113,7 @@ struct list_head *lh; ENTRY; - cache = presto_find_cache(sb->s_dev); + cache = presto_find_cache(sb); if (!cache) { EXIT; goto exit; diff -Nru a/fs/intermezzo/kml_reint.c b/fs/intermezzo/kml_reint.c --- a/fs/intermezzo/kml_reint.c Thu Mar 7 18:17:46 2002 +++ b/fs/intermezzo/kml_reint.c Thu Mar 7 18:17:46 2002 @@ -280,7 +280,7 @@ old_fs = get_fs(); set_fs (get_ds()); error = lento_mknod (mknod->path, mknod->mode, - MKDEV(mknod->major, mknod->minor), &info); + mk_kdev(mknod->major, mknod->minor), &info); set_fs (old_fs); kmlreint_post_secure (rec); EXIT; @@ -360,8 +360,7 @@ struct dentry *dentry; int error; - if (path_init(pathname, LOOKUP_PARENT, &nd)) - error = path_walk(mtpt, &nd); + error = path_lookup(pathname, LOOKUP_PARENT, &nd); if (error) { CDEBUG (D_KML, "Yean!!!!::Can't find mtpt::%s\n", mtpt); return error; diff -Nru a/fs/intermezzo/presto.c b/fs/intermezzo/presto.c --- a/fs/intermezzo/presto.c Thu Mar 7 18:17:37 2002 +++ b/fs/intermezzo/presto.c Thu Mar 7 18:17:37 2002 @@ -32,7 +32,6 @@ int presto_walk(const char *name, struct nameidata *nd) { - int err; /* we do not follow symlinks to support symlink operations correctly. The vfs should always hand us resolved dentries so we should not be required to use LOOKUP_FOLLOW. At the @@ -40,13 +39,8 @@ resolved pathname and not the symlink. SHP XXX: This code implies that direct symlinks do not work. SHP */ - unsigned int flags = LOOKUP_POSITIVE; - - ENTRY; - err = 0; - if (path_init(name, flags, nd)) - err = path_walk(name, nd); - return err; + unsigned int flags = 0; + return path_lookup(name, flags, nd); } inline struct presto_dentry_data *presto_d2d(struct dentry *dentry) @@ -71,8 +65,8 @@ cache = presto_get_cache(inode); CDEBUG(D_PSDEV, "\n"); if ( !cache ) { - printk("PRESTO: BAD: cannot find cache for dev %x, ino %ld\n", - kdev_val(inode->i_dev), inode->i_ino); + printk("PRESTO: BAD: cannot find cache for dev %s, ino %ld\n", + inode->i_sb->s_id, inode->i_ino); EXIT; return -1; } diff -Nru a/fs/intermezzo/psdev.c b/fs/intermezzo/psdev.c --- a/fs/intermezzo/psdev.c Thu Mar 7 18:17:41 2002 +++ b/fs/intermezzo/psdev.c Thu Mar 7 18:17:41 2002 @@ -789,7 +789,7 @@ } error = lento_mknod(input.name, input.mode, - MKDEV(input.major,input.minor),&input.info); + mk_kdev(input.major,input.minor),&input.info); EXIT; return error; } diff -Nru a/fs/intermezzo/super.c b/fs/intermezzo/super.c --- a/fs/intermezzo/super.c Thu Mar 7 18:17:46 2002 +++ b/fs/intermezzo/super.c Thu Mar 7 18:17:46 2002 @@ -39,7 +39,7 @@ #endif extern struct presto_cache *presto_init_cache(void); -extern inline void presto_cache_add(struct presto_cache *cache, kdev_t dev); +extern inline void presto_cache_add(struct presto_cache *cache, struct super_block *sb); extern inline void presto_init_cache_hash(void); int presto_remount(struct super_block *, int *, char *); @@ -325,7 +325,7 @@ filter_setup_journal_ops(cache->cache_filter, cache->cache_type); /* we now know the dev of the cache: hash the cache */ - presto_cache_add(cache, mysb->s_dev); + presto_cache_add(cache, mysb); /* make sure we have our own super operations: mysb still contains the cache operations */ @@ -402,7 +402,7 @@ } } - cache = presto_find_cache(sb->s_dev); + cache = presto_find_cache(sb); if (!cache) { printk(__FUNCTION__ ": cannot find cache on remount\n"); err = -ENODEV; diff -Nru a/fs/intermezzo/vfs.c b/fs/intermezzo/vfs.c --- a/fs/intermezzo/vfs.c Thu Mar 7 18:17:41 2002 +++ b/fs/intermezzo/vfs.c Thu Mar 7 18:17:41 2002 @@ -538,9 +538,7 @@ } /* this looks up the parent */ -// if (path_init(pathname, LOOKUP_FOLLOW | LOOKUP_POSITIVE, &nd)) - if (path_init(pathname, LOOKUP_PARENT, &nd)) - error = path_walk(pathname, &nd); + error = path_lookup(pathname, LOOKUP_PARENT, &nd); if (error) { EXIT; goto exit; @@ -602,7 +600,7 @@ goto exit_lock; error = -EXDEV; - if (!kdev_same(dir->d_inode->i_dev, inode->i_dev)) + if (dir->d_inode->i_sb != inode->i_sb) goto exit_lock; /* @@ -673,56 +671,47 @@ struct lento_vfs_context *info) { int error; - char * from; + struct dentry *new_dentry; + struct nameidata nd, old_nd; char * to; struct presto_file_set *fset; - from = getname(oldname); - if(IS_ERR(from)) - return PTR_ERR(from); to = getname(newname); - error = PTR_ERR(to); - if (!IS_ERR(to)) { - struct dentry *new_dentry; - struct nameidata nd, old_nd; - - error = 0; - if (path_init(from, LOOKUP_POSITIVE, &old_nd)) - error = path_walk(from, &old_nd); - if (error) - goto exit; - if (path_init(to, LOOKUP_PARENT, &nd)) - error = path_walk(to, &nd); - if (error) - goto out; - error = -EXDEV; - if (old_nd.mnt != nd.mnt) - goto out; - new_dentry = lookup_create(&nd, 0); - error = PTR_ERR(new_dentry); + if(IS_ERR(to)) + return PTR_ERR(to); - if (!IS_ERR(new_dentry)) { - fset = presto_fset(new_dentry); - error = -EINVAL; - if ( !fset ) { - printk("No fileset!\n"); - EXIT; - goto out2; - } - error = presto_do_link(fset, old_nd.dentry, - nd.dentry, - new_dentry, info); - dput(new_dentry); - } - out2: - up(&nd.dentry->d_inode->i_sem); - path_release(&nd); - out: - path_release(&old_nd); - exit: - putname(to); - } - putname(from); + error = __user_walk(oldname, 0, &old_nd); + if (error) + goto exit; + error = path_lookup(to, LOOKUP_PARENT, &nd); + if (error) + goto out; + error = -EXDEV; + if (old_nd.mnt != nd.mnt) + goto out; + new_dentry = lookup_create(&nd, 0); + error = PTR_ERR(new_dentry); + + if (!IS_ERR(new_dentry)) { + fset = presto_fset(new_dentry); + error = -EINVAL; + if ( !fset ) { + printk("No fileset!\n"); + EXIT; + goto out2; + } + error = presto_do_link(fset, old_nd.dentry, + nd.dentry, + new_dentry, info); + dput(new_dentry); + } +out2: + up(&nd.dentry->d_inode->i_sem); + path_release(&nd); +out: + path_release(&old_nd); +exit: + putname(to); return error; } @@ -843,8 +832,7 @@ if(IS_ERR(name)) return PTR_ERR(name); - if (path_init(name, LOOKUP_PARENT, &nd)) - error = path_walk(name, &nd); + error = path_lookup(name, LOOKUP_PARENT, &nd); if (error) goto exit; error = -EISDIR; @@ -1007,8 +995,7 @@ goto exit_from; } - if (path_init(to, LOOKUP_PARENT, &nd)) - error = path_walk(to, &nd); + error = path_lookup(to, LOOKUP_PARENT, &nd); if (error) { EXIT; goto exit_to; @@ -1168,8 +1155,7 @@ return error; } - if (path_init(pathname, LOOKUP_PARENT, &nd)) - error = path_walk(pathname, &nd); + error = path_lookup(pathname, LOOKUP_PARENT, &nd); if (error) goto out_name; @@ -1308,8 +1294,7 @@ if(IS_ERR(name)) return PTR_ERR(name); - if (path_init(name, LOOKUP_PARENT, &nd)) - error = path_walk(name, &nd); + error = path_lookup(name, LOOKUP_PARENT, &nd); if (error) goto exit; @@ -1465,8 +1450,7 @@ if (IS_ERR(tmp)) return PTR_ERR(tmp); - if (path_init(tmp, LOOKUP_PARENT, &nd)) - error = path_walk(tmp, &nd); + error = path_lookup(tmp, LOOKUP_PARENT, &nd); if (error) goto out; dentry = lookup_create(&nd, 0); @@ -1609,7 +1593,7 @@ if (error) return error; - if (!kdev_same(new_dir->i_dev, old_dir->i_dev)) + if (new_dir->i_sb != old_dir->i_sb) return -EXDEV; if (!new_dentry->d_inode) @@ -1690,7 +1674,7 @@ if (error) return error; - if (!kdev_same(new_dir->i_dev, old_dir->i_dev)) + if (new_dir->i_sb != old_dir->i_sb) return -EXDEV; if (!new_dentry->d_inode) @@ -1746,14 +1730,12 @@ ENTRY; - if (path_init(oldname, LOOKUP_PARENT, &oldnd)) - error = path_walk(oldname, &oldnd); + error = path_lookup(oldname, LOOKUP_PARENT, &oldnd); if (error) goto exit; - if (path_init(newname, LOOKUP_PARENT, &newnd)) - error = path_walk(newname, &newnd); + error = path_lookup(newname, LOOKUP_PARENT, &newnd); if (error) goto exit1; diff -Nru a/fs/jffs/intrep.c b/fs/jffs/intrep.c --- a/fs/jffs/intrep.c Thu Mar 7 18:17:45 2002 +++ b/fs/jffs/intrep.c Thu Mar 7 18:17:45 2002 @@ -428,7 +428,7 @@ /* Build a control block for the file system. */ static struct jffs_control * -jffs_create_control(kdev_t dev) +jffs_create_control(struct super_block *sb) { struct jffs_control *c; register int s = sizeof(struct jffs_control); @@ -451,7 +451,7 @@ DJM(no_hash++); for (i = 0; i < c->hash_len; i++) INIT_LIST_HEAD(&c->hash[i]); - if (!(c->fmc = jffs_build_begin(c, dev))) { + if (!(c->fmc = jffs_build_begin(c, sb->s_dev))) { goto fail_fminit; } c->next_ino = JFFS_MIN_INO + 1; @@ -549,7 +549,7 @@ D2(printk("jffs_build_fs()\n")); - if (!(c = jffs_create_control(sb->s_dev))) { + if (!(c = jffs_create_control(sb))) { return -ENOMEM; } c->building_fs = 1; @@ -563,7 +563,7 @@ D1(printk("jffs_build_fs: Cleaning up all control structures," " reallocating them and trying mount again.\n")); jffs_cleanup_control(c); - if (!(c = jffs_create_control(sb->s_dev))) { + if (!(c = jffs_create_control(sb))) { return -ENOMEM; } c->building_fs = 1; @@ -1932,8 +1932,7 @@ the buffer. */ static int jffs_get_node_data(struct jffs_file *f, struct jffs_node *node, - unsigned char *buf,__u32 node_offset, __u32 max_size, - kdev_t dev) + unsigned char *buf,__u32 node_offset, __u32 max_size) { struct jffs_fmcontrol *fmc = f->c->fmc; __u32 pos = node->fm->offset + node->fm_offset + node_offset; @@ -2003,8 +2002,7 @@ } else if ((r = jffs_get_node_data(f, node, &buf[read_data], node_offset, - size - read_data, - f->c->sb->s_dev)) < 0) { + size - read_data)) < 0) { return r; } read_data += r; @@ -2643,7 +2641,7 @@ void jffs_print_memory_allocation_statistics(void) { - static long printout = 0; + static long printout; printk("________ Memory printout #%ld ________\n", ++printout); printk("no_jffs_file = %ld\n", no_jffs_file); printk("no_jffs_node = %ld\n", no_jffs_node); diff -Nru a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c --- a/fs/jffs2/compr_zlib.c Thu Mar 7 18:17:36 2002 +++ b/fs/jffs2/compr_zlib.c Thu Mar 7 18:17:36 2002 @@ -1,7 +1,7 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001 Red Hat, Inc. + * Copyright (C) 2001, 2002 Red Hat, Inc. * * Created by David Woodhouse * @@ -31,36 +31,22 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: compr_zlib.c,v 1.8 2001/09/20 15:28:31 dwmw2 Exp $ + * $Id: compr_zlib.c,v 1.15 2002/03/04 09:35:48 dwmw2 Exp $ * */ -#ifdef __KERNEL__ -#include -#else -#include "zlib.h" +#ifndef __KERNEL__ +#error "The userspace support got too messy and was removed. Update your mkfs.jffs2" #endif -#ifdef __KERNEL__ +#include #include #include /* for min() */ #include #include +#include #include "nodelist.h" -#else -#define min(x,y) ((x)<(y)?(x):(y)) -#ifndef D1 -#define D1(x) -#endif -#define KERN_DEBUG -#define KERN_NOTICE -#define KERN_WARNING -#define printk printf -#include -#include -#endif - /* Plan: call deflate() with avail_in == *sourcelen, avail_out = *dstlen - 12 and flush == Z_FINISH. If it doesn't manage to finish, call it again with @@ -70,8 +56,37 @@ */ #define STREAM_END_SPACE 12 +static DECLARE_MUTEX(deflate_sem); +static DECLARE_MUTEX(inflate_sem); +static void *deflate_workspace; +static void *inflate_workspace; + +int __init jffs2_zlib_init(void) +{ + deflate_workspace = vmalloc(zlib_deflate_workspacesize()); + if (!deflate_workspace) { + printk("Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize()); + return -ENOMEM; + } + D1(printk("Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize())); + inflate_workspace = vmalloc(zlib_inflate_workspacesize()); + if (!inflate_workspace) { + printk("Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize()); + vfree(deflate_workspace); + return -ENOMEM; + } + D1(printk("Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize())); + return 0; +} + +void __exit jffs2_zlib_exit(void) +{ + vfree(deflate_workspace); + vfree(inflate_workspace); +} + int zlib_compress(unsigned char *data_in, unsigned char *cpage_out, - __u32 *sourcelen, __u32 *dstlen) + uint32_t *sourcelen, uint32_t *dstlen) { z_stream strm; int ret; @@ -79,22 +94,15 @@ if (*dstlen <= STREAM_END_SPACE) return -1; -#ifdef __KERNEL__ - strm.workspace = kmalloc(zlib_deflate_workspacesize(), - GFP_KERNEL); - if (strm.workspace == NULL) { - printk(KERN_WARNING "deflateInit alloc of workspace failed\n"); - return -1; - } -#else - strm.zalloc = (void *)0; - strm.zfree = (void *)0; -#endif + down(&deflate_sem); + strm.workspace = deflate_workspace; if (Z_OK != zlib_deflateInit(&strm, 3)) { printk(KERN_WARNING "deflateInit failed\n"); + up(&deflate_sem); return -1; } + strm.next_in = data_in; strm.total_in = 0; @@ -111,56 +119,44 @@ strm.avail_in, strm.avail_out, strm.total_in, strm.total_out)); if (ret != Z_OK) { D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret)); - goto out_err; + zlib_deflateEnd(&strm); + up(&deflate_sem); + return -1; } } strm.avail_out += STREAM_END_SPACE; strm.avail_in = 0; ret = zlib_deflate(&strm, Z_FINISH); + zlib_deflateEnd(&strm); + up(&deflate_sem); if (ret != Z_STREAM_END) { D1(printk(KERN_DEBUG "final deflate returned %d\n", ret)); - goto out_err; - + return -1; } - zlib_deflateEnd(&strm); - kfree(strm.workspace); - D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n", strm.total_in, strm.total_out)); + D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n", + strm.total_in, strm.total_out)); if (strm.total_out >= strm.total_in) return -1; - *dstlen = strm.total_out; *sourcelen = strm.total_in; return 0; - - out_err: - zlib_deflateEnd(&strm); - kfree(strm.workspace); - return -1; } void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, - __u32 srclen, __u32 destlen) + uint32_t srclen, uint32_t destlen) { z_stream strm; int ret; -#ifdef __KERNEL__ - strm.workspace = kmalloc(zlib_inflate_workspacesize(), - GFP_KERNEL); - if (strm.workspace == NULL) { - printk(KERN_WARNING "inflateInit alloc of workspace failed\n"); - return; - } -#else - strm.zalloc = (void *)0; - strm.zfree = (void *)0; -#endif + down(&inflate_sem); + strm.workspace = inflate_workspace; if (Z_OK != zlib_inflateInit(&strm)) { printk(KERN_WARNING "inflateInit failed\n"); + up(&inflate_sem); return; } strm.next_in = data_in; @@ -177,5 +173,5 @@ printk(KERN_NOTICE "inflate returned %d\n", ret); } zlib_inflateEnd(&strm); - kfree(strm.workspace); + up(&inflate_sem); } diff -Nru a/fs/jffs2/dir.c b/fs/jffs2/dir.c --- a/fs/jffs2/dir.c Thu Mar 7 18:17:40 2002 +++ b/fs/jffs2/dir.c Thu Mar 7 18:17:40 2002 @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: dir.c,v 1.42 2001/05/24 22:24:39 dwmw2 Exp $ + * $Id: dir.c,v 1.45.2.5 2002/02/23 14:31:09 dwmw2 Exp $ * */ @@ -39,6 +39,7 @@ #include #include #include +#include /* For completion */ #include #include #include @@ -181,11 +182,11 @@ } D2(printk(KERN_DEBUG "Dirent %ld: \"%s\", ino #%u, type %d\n", offset, fd->name, fd->ino, fd->type)); if (filldir(dirent, fd->name, strlen(fd->name), offset, fd->ino, fd->type) < 0) - goto out; + break; offset++; } - out: up(&f->sem); + out: filp->f_pos = offset; return 0; } @@ -305,7 +306,7 @@ rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); rd->pino = dir_i->i_ino; - rd->version = dir_f->highest_version++; + rd->version = ++dir_f->highest_version; rd->ino = inode->i_ino; rd->mctime = CURRENT_TIME; rd->nsize = namelen; @@ -316,17 +317,21 @@ fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); jffs2_complete_reservation(c); - jffs2_free_raw_dirent(rd); - + if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ + jffs2_free_raw_dirent(rd); up(&dir_f->sem); jffs2_clear_inode(inode); unlock_kernel(); return PTR_ERR(fd); } + dir_i->i_mtime = dir_i->i_ctime = rd->mctime; + + jffs2_free_raw_dirent(rd); + /* Link the fd into the inode's list, obsoleting an old one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); @@ -373,7 +378,7 @@ rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); rd->pino = dir_i->i_ino; - rd->version = dir_f->highest_version++; + rd->version = ++dir_f->highest_version; rd->ino = 0; rd->mctime = CURRENT_TIME; rd->nsize = dentry->d_name.len; @@ -464,7 +469,7 @@ rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); rd->pino = dir_i->i_ino; - rd->version = dir_f->highest_version++; + rd->version = ++dir_f->highest_version; rd->ino = old_dentry->d_inode->i_ino; rd->mctime = CURRENT_TIME; rd->nsize = dentry->d_name.len; @@ -503,6 +508,9 @@ { int ret; + if (S_ISDIR(old_dentry->d_inode->i_mode)) + return -EPERM; + lock_kernel(); ret = jffs2_do_link(old_dentry, dir_i, dentry, 0); if (!ret) { @@ -567,7 +575,7 @@ f = JFFS2_INODE_INFO(inode); - ri->dsize = ri->csize = strlen(target); + inode->i_size = ri->isize = ri->dsize = ri->csize = strlen(target); ri->totlen = sizeof(*ri) + ri->dsize; ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4); @@ -628,7 +636,7 @@ rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); rd->pino = dir_i->i_ino; - rd->version = dir_f->highest_version++; + rd->version = ++dir_f->highest_version; rd->ino = inode->i_ino; rd->mctime = CURRENT_TIME; rd->nsize = namelen; @@ -639,16 +647,19 @@ fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); jffs2_complete_reservation(c); - jffs2_free_raw_dirent(rd); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ + jffs2_free_raw_dirent(rd); up(&dir_f->sem); jffs2_clear_inode(inode); unlock_kernel(); return PTR_ERR(fd); } + dir_i->i_mtime = dir_i->i_ctime = rd->mctime; + + jffs2_free_raw_dirent(rd); /* Link the fd into the inode's list, obsoleting an old one if necessary. */ @@ -768,7 +779,7 @@ rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); rd->pino = dir_i->i_ino; - rd->version = dir_f->highest_version++; + rd->version = ++dir_f->highest_version; rd->ino = inode->i_ino; rd->mctime = CURRENT_TIME; rd->nsize = namelen; @@ -779,17 +790,21 @@ fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); jffs2_complete_reservation(c); - jffs2_free_raw_dirent(rd); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ + jffs2_free_raw_dirent(rd); up(&dir_f->sem); jffs2_clear_inode(inode); unlock_kernel(); return PTR_ERR(fd); } + dir_i->i_mtime = dir_i->i_ctime = rd->mctime; + + jffs2_free_raw_dirent(rd); + /* Link the fd into the inode's list, obsoleting an old one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); @@ -841,8 +856,7 @@ c = JFFS2_SB_INFO(dir_i->i_sb); - if ((mode & S_IFMT) == S_IFBLK || - (mode & S_IFMT) == S_IFCHR) { + if (S_ISBLK(mode) || S_ISCHR(mode)) { dev = (MAJOR(rdev) << 8) | MINOR(rdev); devlen = sizeof(dev); } @@ -933,7 +947,7 @@ rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); rd->pino = dir_i->i_ino; - rd->version = dir_f->highest_version++; + rd->version = ++dir_f->highest_version; rd->ino = inode->i_ino; rd->mctime = CURRENT_TIME; rd->nsize = namelen; @@ -947,17 +961,21 @@ fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); jffs2_complete_reservation(c); - jffs2_free_raw_dirent(rd); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ + jffs2_free_raw_dirent(rd); up(&dir_f->sem); jffs2_clear_inode(inode); unlock_kernel(); return PTR_ERR(fd); } + dir_i->i_mtime = dir_i->i_ctime = rd->mctime; + + jffs2_free_raw_dirent(rd); + /* Link the fd into the inode's list, obsoleting an old one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); @@ -965,6 +983,7 @@ unlock_kernel(); d_instantiate(dentry, inode); + return 0; } @@ -989,20 +1008,16 @@ ret = jffs2_do_unlink(old_dir_i, old_dentry, 1); if (ret) { - /* Try to delete the _new_ link to return a clean failure */ - int ret2 = jffs2_do_unlink(new_dir_i, new_dentry, 1); - if (ret2) { - struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); - down(&f->sem); - old_dentry->d_inode->i_nlink = f->inocache->nlink++; - up(&f->sem); + /* Oh shit. We really ought to make a single node which can do both atomically */ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); + down(&f->sem); + old_dentry->d_inode->i_nlink = f->inocache->nlink++; + up(&f->sem); - printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (old err %d, new err %d). You now have a hard link\n", ret, ret2); - /* Might as well let the VFS know */ - d_instantiate(new_dentry, old_dentry->d_inode); - atomic_inc(&old_dentry->d_inode->i_count); - } - + printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret); + /* Might as well let the VFS know */ + d_instantiate(new_dentry, old_dentry->d_inode); + atomic_inc(&old_dentry->d_inode->i_count); } unlock_kernel(); return ret; diff -Nru a/fs/jffs2/erase.c b/fs/jffs2/erase.c --- a/fs/jffs2/erase.c Thu Mar 7 18:17:38 2002 +++ b/fs/jffs2/erase.c Thu Mar 7 18:17:38 2002 @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: erase.c,v 1.23 2001/09/19 21:51:11 dwmw2 Exp $ + * $Id: erase.c,v 1.24 2001/12/06 16:38:38 dwmw2 Exp $ * */ #include @@ -214,8 +214,8 @@ D2({ int i=0; - struct jffs2_raw_node_ref *this; - printk(KERN_DEBUG "After remove_node_refs_from_ino_list: \n" KERN_DEBUG); + struct jffs2_raw_node_ref *this; + printk(KERN_DEBUG "After remove_node_refs_from_ino_list: \n" KERN_DEBUG); this = ic->nodes; @@ -262,7 +262,11 @@ void jffs2_mark_erased_blocks(struct jffs2_sb_info *c) { - static struct jffs2_unknown_node marker = {JFFS2_MAGIC_BITMASK, JFFS2_NODETYPE_CLEANMARKER, sizeof(struct jffs2_unknown_node)}; + static struct jffs2_unknown_node marker = { + magic: JFFS2_MAGIC_BITMASK, + nodetype: JFFS2_NODETYPE_CLEANMARKER, + totlen: sizeof(struct jffs2_unknown_node) + }; struct jffs2_eraseblock *jeb; struct jffs2_raw_node_ref *marker_ref; unsigned char *ebuf; diff -Nru a/fs/jffs2/file.c b/fs/jffs2/file.c --- a/fs/jffs2/file.c Thu Mar 7 18:17:44 2002 +++ b/fs/jffs2/file.c Thu Mar 7 18:17:44 2002 @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: file.c,v 1.58 2001/09/20 15:28:31 dwmw2 Exp $ + * $Id: file.c,v 1.58.2.1 2002/02/23 14:25:36 dwmw2 Exp $ * */ @@ -102,15 +102,14 @@ must read the original data associated with the node (i.e. the device numbers or the target name) and write it out again with the appropriate data attached */ - if ((inode->i_mode & S_IFMT) == S_IFBLK || - (inode->i_mode & S_IFMT) == S_IFCHR) { + if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { /* For these, we don't actually need to read the old node */ dev = (major(dentry->d_inode->i_rdev) << 8) | minor(dentry->d_inode->i_rdev); mdata = (char *)&dev; mdatalen = sizeof(dev); D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); - } else if ((inode->i_mode & S_IFMT) == S_IFLNK) { + } else if (S_ISLNK(inode->i_mode)) { mdatalen = f->metadata->size; mdata = kmalloc(f->metadata->size, GFP_USER); if (!mdata) @@ -125,7 +124,7 @@ ri = jffs2_alloc_raw_inode(); if (!ri) { - if ((inode->i_mode & S_IFMT) == S_IFLNK) + if (S_ISLNK(inode->i_mode)) kfree(mdata); return -ENOMEM; } @@ -133,7 +132,7 @@ ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL); if (ret) { jffs2_free_raw_inode(ri); - if ((inode->i_mode & S_IFMT) == S_IFLNK) + if (S_ISLNK(inode->i_mode)) kfree(mdata); return ret; } @@ -177,7 +176,7 @@ ri->data_crc = 0; new_metadata = jffs2_write_dnode(inode, ri, mdata, mdatalen, phys_ofs, NULL); - if ((inode->i_mode & S_IFMT) == S_IFLNK) + if (S_ISLNK(inode->i_mode)) kfree(mdata); jffs2_complete_reservation(c); diff -Nru a/fs/jffs2/gc.c b/fs/jffs2/gc.c --- a/fs/jffs2/gc.c Thu Mar 7 18:17:40 2002 +++ b/fs/jffs2/gc.c Thu Mar 7 18:17:40 2002 @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: gc.c,v 1.52 2001/09/19 21:53:47 dwmw2 Exp $ + * $Id: gc.c,v 1.52.2.2 2002/02/23 14:25:36 dwmw2 Exp $ * */ @@ -266,15 +266,14 @@ __u32 alloclen, phys_ofs; int ret; - if ((inode->i_mode & S_IFMT) == S_IFBLK || - (inode->i_mode & S_IFMT) == S_IFCHR) { + if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { /* For these, we don't actually need to read the old node */ dev = (major(inode->i_rdev) << 8) | minor(inode->i_rdev); mdata = (char *)&dev; mdatalen = sizeof(dev); D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen)); - } else if ((inode->i_mode & S_IFMT) == S_IFLNK) { + } else if (S_ISLNK(inode->i_mode)) { mdatalen = fn->size; mdata = kmalloc(fn->size, GFP_KERNEL); if (!mdata) { @@ -331,7 +330,7 @@ jffs2_free_full_dnode(fn); f->metadata = new_fn; out: - if ((inode->i_mode & S_IFMT) == S_IFLNK) + if (S_ISLNK(inode->i_mode)) kfree(mdata); return ret; } @@ -466,8 +465,8 @@ ri.ino = inode->i_ino; ri.version = ++f->highest_version; ri.offset = start; - ri.csize = end - start; - ri.dsize = 0; + ri.dsize = end - start; + ri.csize = 0; ri.compr = JFFS2_COMPR_ZERO; } ri.mode = inode->i_mode; diff -Nru a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c --- a/fs/jffs2/malloc.c Thu Mar 7 18:17:37 2002 +++ b/fs/jffs2/malloc.c Thu Mar 7 18:17:37 2002 @@ -139,7 +139,7 @@ if (!jffs2_inode_cachep) goto err; - inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), 0, JFFS2_SLAB_POISON, NULL, NULL); + inode_cache_slab = kmem_cache_create("jffs2_inode", sizeof(struct jffs2_inode_cache), 0, JFFS2_SLAB_POISON, NULL, NULL); if (inode_cache_slab) return 0; diff -Nru a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c --- a/fs/jffs2/nodelist.c Thu Mar 7 18:17:36 2002 +++ b/fs/jffs2/nodelist.c Thu Mar 7 18:17:36 2002 @@ -1,7 +1,7 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001 Red Hat, Inc. + * Copyright (C) 2001, 2002 Red Hat, Inc. * * Created by David Woodhouse * @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: nodelist.c,v 1.30 2001/11/14 10:35:21 dwmw2 Exp $ + * $Id: nodelist.c,v 1.30.2.3 2002/02/23 14:04:44 dwmw2 Exp $ * */ @@ -94,7 +94,8 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, - __u32 *highest_version) + __u32 *highest_version, __u32 *latest_mctime, + __u32 *mctime_ver) { struct jffs2_raw_node_ref *ref = f->inocache->nodes; struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL; @@ -104,6 +105,8 @@ size_t retlen; int err; + *mctime_ver = 0; + D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino)); if (!f->inocache->nodes) { printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", ino); @@ -115,7 +118,7 @@ D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref->flash_offset &~3)); continue; } - err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), sizeof(node), &retlen, (void *)&node); + err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node); if (err) { printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, (ref->flash_offset) & ~3); goto free_out; @@ -123,7 +126,7 @@ /* Check we've managed to read at least the common node header */ - if (retlen < sizeof(node.u)) { + if (retlen < min(ref->totlen, sizeof(node.u))) { printk(KERN_WARNING "short read in get_inode_nodes()\n"); err = -EIO; goto free_out; @@ -153,15 +156,22 @@ fd->version = node.d.version; fd->ino = node.d.ino; fd->type = node.d.type; - /* memcpy as much of the name as possible from the raw - dirent we've already read from the flash - */ + + /* Pick out the mctime of the latest dirent */ + if(fd->version > *mctime_ver) { + *mctime_ver = fd->version; + *latest_mctime = node.d.mctime; + } + + /* memcpy as much of the name as possible from the raw + dirent we've already read from the flash + */ if (retlen > sizeof(struct jffs2_raw_dirent)) memcpy(&fd->name[0], &node.d.name[0], min((__u32)node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent)))); - /* Do we need to copy any more of the name directly - from the flash? - */ + /* Do we need to copy any more of the name directly + from the flash? + */ if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) { int already = retlen - sizeof(struct jffs2_raw_dirent); @@ -217,7 +227,12 @@ } tn->version = node.i.version; tn->fn->ofs = node.i.offset; - tn->fn->size = node.i.dsize; + /* There was a bug where we wrote hole nodes out with + csize/dsize swapped. Deal with it */ + if (node.i.compr == JFFS2_COMPR_ZERO && !node.i.dsize && node.i.csize) + tn->fn->size = node.i.csize; + else // normal case... + tn->fn->size = node.i.dsize; tn->fn->raw = ref; D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", ref->flash_offset &~3, node.i.version, node.i.offset, node.i.dsize)); jffs2_add_tn_to_list(tn, &ret_tn); @@ -242,6 +257,7 @@ } *tnp = ret_tn; *fdp = ret_fd; + return 0; free_out: diff -Nru a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h --- a/fs/jffs2/nodelist.h Thu Mar 7 18:17:45 2002 +++ b/fs/jffs2/nodelist.h Thu Mar 7 18:17:45 2002 @@ -31,7 +31,8 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: nodelist.h,v 1.46 2001/09/18 23:43:05 dwmw2 Exp $ + * $Id: nodelist.h,v 1.46.2.1 2002/02/23 14:04:44 dwmw2 Exp $ + * + zlib_init calls from v1.65 * */ @@ -248,7 +249,8 @@ void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list); int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, - __u32 *highest_version); + __u32 *highest_version, __u32 *latest_mctime, + __u32 *mctime_ver); struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino); void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new); void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old); @@ -348,3 +350,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c); void jffs2_mark_erased_blocks(struct jffs2_sb_info *c); void jffs2_erase_pending_trigger(struct jffs2_sb_info *c); + +/* compr_zlib.c */ +int jffs2_zlib_init(void); +void jffs2_zlib_exit(void); diff -Nru a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c --- a/fs/jffs2/nodemgmt.c Thu Mar 7 18:17:39 2002 +++ b/fs/jffs2/nodemgmt.c Thu Mar 7 18:17:39 2002 @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: nodemgmt.c,v 1.45 2001/09/20 08:05:05 dwmw2 Exp $ + * $Id: nodemgmt.c,v 1.45.2.1 2002/02/23 14:13:34 dwmw2 Exp $ * */ @@ -318,6 +318,15 @@ ACCT_PARANOIA_CHECK(jeb); + if (c->flags & JFFS2_SB_FLAG_MOUNTING) { + /* Mount in progress. Don't muck about with the block + lists because they're not ready yet, and don't actually + obliterate nodes that look obsolete. If they weren't + marked obsolete on the flash at the time they _became_ + obsolete, there was probably a reason for that. */ + spin_unlock_bh(&c->erase_completion_lock); + return; + } if (jeb == c->nextblock) { D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset)); } else if (jeb == c->gcblock) { diff -Nru a/fs/jffs2/read.c b/fs/jffs2/read.c --- a/fs/jffs2/read.c Thu Mar 7 18:17:41 2002 +++ b/fs/jffs2/read.c Thu Mar 7 18:17:41 2002 @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: read.c,v 1.13 2001/05/01 16:24:44 dwmw2 Exp $ + * $Id: read.c,v 1.13.2.1 2002/02/01 23:32:33 dwmw2 Exp $ * */ @@ -74,6 +74,12 @@ printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", ri->node_crc, crc, fd->raw->flash_offset & ~3); ret = -EIO; goto out_ri; + } + /* There was a bug where we wrote hole nodes out with csize/dsize + swapped. Deal with it */ + if (ri->compr == JFFS2_COMPR_ZERO && !ri->dsize && ri->csize) { + ri->dsize = ri->csize; + ri->csize = 0; } D1(if(ofs + len > ri->dsize) { diff -Nru a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c --- a/fs/jffs2/readinode.c Thu Mar 7 18:17:37 2002 +++ b/fs/jffs2/readinode.c Thu Mar 7 18:17:37 2002 @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: readinode.c,v 1.56 2001/07/26 20:32:39 dwmw2 Exp $ + * $Id: readinode.c,v 1.58.2.2 2002/02/23 14:25:37 dwmw2 Exp $ * */ @@ -173,12 +173,12 @@ jffs2_free_node_frag(newfrag); return -ENOMEM; } - printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size); + D1(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size); if (this->node) printk("phys 0x%08x\n", this->node->raw->flash_offset &~3); else printk("hole\n"); - + ) newfrag2->ofs = fn->ofs + fn->size; newfrag2->size = (this->ofs+this->size) - newfrag2->ofs; newfrag2->next = this->next; @@ -251,6 +251,7 @@ struct jffs2_full_dnode *fn = NULL; struct jffs2_sb_info *c; struct jffs2_raw_inode latest_node; + __u32 latest_mctime, mctime_ver; int ret; ssize_t retlen; @@ -292,7 +293,7 @@ inode->i_nlink = f->inocache->nlink; /* Grab all nodes relevant to this ino */ - ret = jffs2_get_inode_nodes(c, inode->i_ino, f, &tn_list, &fd_list, &f->highest_version); + ret = jffs2_get_inode_nodes(c, inode->i_ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); if (ret) { printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %lu returned %d\n", inode->i_ino, ret); @@ -302,7 +303,7 @@ f->dents = fd_list; while (tn_list) { - static __u32 mdata_ver = 0; + static __u32 mdata_ver; tn = tn_list; @@ -339,6 +340,7 @@ printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n"); } inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO; + latest_node.version = 0; inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; inode->i_nlink = f->inocache->nlink; inode->i_size = 0; @@ -366,7 +368,7 @@ inode->i_uid = latest_node.uid; inode->i_gid = latest_node.gid; inode->i_size = latest_node.isize; - if ((inode->i_mode & S_IFMT) == S_IFREG) + if (S_ISREG(inode->i_mode)) jffs2_truncate_fraglist(c, &f->fraglist, latest_node.isize); inode->i_atime = latest_node.atime; inode->i_mtime = latest_node.mtime; @@ -376,9 +378,8 @@ /* OK, now the special cases. Certain inode types should have only one data node, and it's kept as the metadata node */ - if ((inode->i_mode & S_IFMT) == S_IFBLK || - (inode->i_mode & S_IFMT) == S_IFCHR || - (inode->i_mode & S_IFMT) == S_IFLNK) { + if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode) || + S_ISLNK(inode->i_mode)) { if (f->metadata) { printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had metadata node\n", inode->i_ino, inode->i_mode); jffs2_clear_inode(inode); @@ -393,7 +394,8 @@ } /* ASSERT: f->fraglist != NULL */ if (f->fraglist->next) { - printk(KERN_WARNING "Argh. Special inode #%lu had more than one node\n", inode->i_ino); + printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had more than one node\n", inode->i_ino, inode->i_mode); + /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ jffs2_clear_inode(inode); make_bad_inode(inode); return; @@ -412,9 +414,21 @@ case S_IFLNK: inode->i_op = &jffs2_symlink_inode_operations; + /* Hack to work around broken isize in old symlink code. + Remove this when dwmw2 comes to his senses and stops + symlinks from being an entirely gratuitous special + case. */ + if (!inode->i_size) + inode->i_size = latest_node.dsize; break; case S_IFDIR: + if (mctime_ver > latest_node.version) { + /* The times in the latest_node are actually older than + mctime in the latest dirent. Cheat. */ + inode->i_mtime = inode->i_ctime = inode->i_atime = + latest_mctime; + } inode->i_op = &jffs2_dir_inode_operations; inode->i_fop = &jffs2_dir_operations; break; diff -Nru a/fs/jffs2/scan.c b/fs/jffs2/scan.c --- a/fs/jffs2/scan.c Thu Mar 7 18:17:42 2002 +++ b/fs/jffs2/scan.c Thu Mar 7 18:17:42 2002 @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: scan.c,v 1.51 2001/09/19 00:06:35 dwmw2 Exp $ + * $Id: scan.c,v 1.51.2.2 2002/02/23 13:34:31 dwmw2 Exp $ * */ #include @@ -62,6 +62,9 @@ } \ } while(0) +static uint32_t pseudo_random; +static void jffs2_rotate_lists(struct jffs2_sb_info *c); + static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); /* These helper functions _must_ increase ofs and also do the dirty/used space accounting. @@ -142,6 +145,9 @@ c->nr_erasing_blocks++; } } + /* Rotate the lists by some number to ensure wear levelling */ + jffs2_rotate_lists(c); + if (c->nr_erasing_blocks) { if (!c->used_size && empty_blocks != c->nr_blocks) { printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n"); @@ -444,6 +450,12 @@ *ofs += 4; return 0; } + /* There was a bug where we wrote hole nodes out with csize/dsize + swapped. Deal with it */ + if (ri.compr == JFFS2_COMPR_ZERO && !ri.dsize && ri.csize) { + ri.dsize = ri.csize; + ri.csize = 0; + } if (ri.csize) { /* Check data CRC too */ @@ -474,7 +486,7 @@ *ofs, ri.data_crc, crc); DIRTY_SPACE(PAD(ri.totlen)); *ofs += PAD(ri.totlen); - return -0; + return 0; } } @@ -518,6 +530,8 @@ D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n", ri.ino, ri.version, ri.offset, ri.offset+ri.dsize)); + pseudo_random += ri.version; + for (tn_list = &ic->scan->tmpnodes; *tn_list; tn_list = &((*tn_list)->next)) { if ((*tn_list)->version < ri.version) continue; @@ -613,6 +627,8 @@ return 0; } + pseudo_random += rd.version; + fd = jffs2_alloc_full_dirent(rd.nsize+1); if (!fd) { return -ENOMEM; @@ -685,4 +701,46 @@ } *ofs += PAD(rd.totlen); return 0; +} + +static int count_list(struct list_head *l) +{ + uint32_t count = 0; + struct list_head *tmp; + + list_for_each(tmp, l) { + count++; + } + return count; +} + +/* Note: This breaks if list_empty(head). I don't care. You + might, if you copy this code and use it elsewhere :) */ +static void rotate_list(struct list_head *head, uint32_t count) +{ + struct list_head *n = head->next; + + list_del(head); + while(count--) + n = n->next; + list_add(head, n); +} + +static void jffs2_rotate_lists(struct jffs2_sb_info *c) +{ + uint32_t x; + + x = count_list(&c->clean_list); + if (x) + rotate_list((&c->clean_list), pseudo_random % x); + + x = count_list(&c->dirty_list); + if (x) + rotate_list((&c->dirty_list), pseudo_random % x); + + if (c->nr_erasing_blocks) + rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks); + + if (c->nr_free_blocks) /* Not that it should ever be zero */ + rotate_list((&c->free_list), pseudo_random % c->nr_free_blocks); } diff -Nru a/fs/jffs2/super.c b/fs/jffs2/super.c --- a/fs/jffs2/super.c Thu Mar 7 18:17:41 2002 +++ b/fs/jffs2/super.c Thu Mar 7 18:17:41 2002 @@ -31,7 +31,8 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: super.c,v 1.48 2001/10/02 09:16:23 dwmw2 Exp $ + * $Id: super.c,v 1.48.2.1 2002/02/23 14:13:34 dwmw2 Exp $ + * + zlib_init calls from v1.56 * */ @@ -214,7 +215,7 @@ c->mtd = get_mtd_device(NULL, minor(sb->s_dev)); if (!c->mtd) { - D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev))); + D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", minor(sb->s_dev))); return -EINVAL; } c->sector_size = c->mtd->erasesize; @@ -249,10 +250,15 @@ INIT_LIST_HEAD(&c->bad_used_list); c->highest_ino = 1; + c->flags |= JFFS2_SB_FLAG_MOUNTING; + if (jffs2_build_filesystem(c)) { D1(printk(KERN_DEBUG "build_fs failed\n")); goto out_nodes; } + + c->flags &= ~JFFS2_SB_FLAG_MOUNTING; + sb->s_op = &jffs2_super_operations; D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n")); @@ -368,6 +374,11 @@ } #endif + ret = jffs2_zlib_init(); + if (ret) { + printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n"); + return ret; + } ret = jffs2_create_slab_caches(); if (ret) { printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n"); @@ -384,6 +395,7 @@ static void __exit exit_jffs2_fs(void) { jffs2_destroy_slab_caches(); + jffs2_zlib_exit(); unregister_filesystem(&jffs2_fs_type); } diff -Nru a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c --- a/fs/jffs2/symlink.c Thu Mar 7 18:17:36 2002 +++ b/fs/jffs2/symlink.c Thu Mar 7 18:17:36 2002 @@ -1,7 +1,7 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001 Red Hat, Inc. + * Copyright (C) 2001, 2002 Red Hat, Inc. * * Created by David Woodhouse * @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: symlink.c,v 1.5 2001/03/15 15:38:24 dwmw2 Exp $ + * $Id: symlink.c,v 1.5.2.1 2002/01/15 10:39:06 dwmw2 Exp $ * */ @@ -58,16 +58,21 @@ char *buf; int ret; + down(&f->sem); if (!f->metadata) { + up(&f->sem); printk(KERN_NOTICE "No metadata for symlink inode #%lu\n", dentry->d_inode->i_ino); return ERR_PTR(-EINVAL); } buf = kmalloc(f->metadata->size+1, GFP_USER); - if (!buf) + if (!buf) { + up(&f->sem); return ERR_PTR(-ENOMEM); + } buf[f->metadata->size]=0; ret = jffs2_read_dnode(JFFS2_SB_INFO(dentry->d_inode->i_sb), f->metadata, buf, 0, f->metadata->size); + up(&f->sem); if (ret) { kfree(buf); return ERR_PTR(ret); diff -Nru a/fs/jfs/Makefile b/fs/jfs/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/Makefile Thu Mar 7 18:17:46 2002 @@ -0,0 +1,20 @@ +# +# Makefile for the Linux JFS filesystem routines. +# +# 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 (not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile. + +O_TARGET := jfs.o +obj-y := super.o file.o inode.o namei.o jfs_mount.o jfs_umount.o \ + jfs_xtree.o jfs_imap.o jfs_debug.o jfs_dmap.o \ + jfs_unicode.o jfs_dtree.o jfs_inode.o \ + jfs_extent.o symlink.o jfs_metapage.o \ + jfs_logmgr.o jfs_txnmgr.o jfs_uniupr.o +obj-m := $(O_TARGET) + +EXTRA_CFLAGS += -D_JFS_4K + +include $(TOPDIR)/Rules.make diff -Nru a/fs/jfs/endian24.h b/fs/jfs/endian24.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/endian24.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,50 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ + +#ifndef _H_ENDIAN24 +#define _H_ENDIAN24 + +/* + * fs/jfs/endian24.h: + * + * Endian conversion for 24-byte data + * + */ +#define __swab24(x) \ +({ \ + __u32 __x = (x); \ + ((__u32)( \ + ((__x & (__u32)0x000000ffUL) << 16) | \ + (__x & (__u32)0x0000ff00UL) | \ + ((__x & (__u32)0x00ff0000UL) >> 16) )); \ +}) + +#if (defined(__KERNEL__) && defined(__LITTLE_ENDIAN)) || (defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN)) + #define __cpu_to_le24(x) ((__u32)(x)) + #define __le24_to_cpu(x) ((__u32)(x)) +#else + #define __cpu_to_le24(x) __swab24(x) + #define __le24_to_cpu(x) __swab24(x) +#endif + +#ifdef __KERNEL__ + #define cpu_to_le24 __cpu_to_le24 + #define le24_to_cpu __le24_to_cpu +#endif + +#endif /* !_H_ENDIAN24 */ diff -Nru a/fs/jfs/file.c b/fs/jfs/file.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/file.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,105 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 "jfs_incore.h" +#include "jfs_txnmgr.h" +#include "jfs_debug.h" + + +extern int generic_file_open(struct inode *, struct file *); +extern loff_t generic_file_llseek(struct file *, loff_t, int origin); + +extern int jfs_commit_inode(struct inode *, int); + +int jfs_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + struct inode *inode = dentry->d_inode; + int rc = 0; + + rc = fsync_inode_data_buffers(inode); + + if (!(inode->i_state & I_DIRTY)) + return rc; + if (datasync || !(inode->i_state & I_DIRTY_DATASYNC)) + return rc; + + IWRITE_LOCK(inode); + rc |= jfs_commit_inode(inode, 1); + IWRITE_UNLOCK(inode); + + return rc ? -EIO : 0; +} + +struct file_operations jfs_file_operations = { + open: generic_file_open, + llseek: generic_file_llseek, + write: generic_file_write, + read: generic_file_read, + mmap: generic_file_mmap, + fsync: jfs_fsync, +}; + +/* + * Guts of jfs_truncate. Called with locks already held. Can be called + * with directory for truncating directory index table. + */ +void jfs_truncate_nolock(struct inode *ip, loff_t length) +{ + loff_t newsize; + tid_t tid; + + ASSERT(length >= 0); + + if (test_cflag(COMMIT_Nolink, ip)) { + xtTruncate(0, ip, length, COMMIT_WMAP); + return; + } + + do { + tid = txBegin(ip->i_sb, 0); + + newsize = xtTruncate(tid, ip, length, + COMMIT_TRUNCATE | COMMIT_PWMAP); + if (newsize < 0) { + txEnd(tid); + break; + } + + ip->i_mtime = ip->i_ctime = CURRENT_TIME; + mark_inode_dirty(ip); + + txCommit(tid, 1, &ip, 0); + txEnd(tid); + } while (newsize > length); /* Truncate isn't always atomic */ +} + +static void jfs_truncate(struct inode *ip) +{ + jFYI(1, ("jfs_truncate: size = 0x%lx\n", (ulong) ip->i_size)); + + IWRITE_LOCK(ip); + jfs_truncate_nolock(ip, ip->i_size); + IWRITE_UNLOCK(ip); +} + +struct inode_operations jfs_file_inode_operations = { + truncate: jfs_truncate, +}; diff -Nru a/fs/jfs/inode.c b/fs/jfs/inode.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/inode.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,314 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 "jfs_incore.h" +#include "jfs_filsys.h" +#include "jfs_imap.h" +#include "jfs_extent.h" +#include "jfs_unicode.h" +#include "jfs_debug.h" + + +extern struct inode_operations jfs_dir_inode_operations; +extern struct inode_operations jfs_file_inode_operations; +extern struct inode_operations jfs_symlink_inode_operations; +extern struct file_operations jfs_dir_operations; +extern struct file_operations jfs_file_operations; +struct address_space_operations jfs_aops; +extern int freeZeroLink(struct inode *); + +void jfs_put_inode(struct inode *inode) +{ + jFYI(1, ("In jfs_put_inode, inode = 0x%p\n", inode)); +} + +void jfs_read_inode(struct inode *inode) +{ + jFYI(1, ("In jfs_read_inode, inode = 0x%p\n", inode)); + + if (diRead(inode)) + goto bad_inode; + + if (S_ISREG(inode->i_mode)) { + inode->i_op = &jfs_file_inode_operations; + inode->i_fop = &jfs_file_operations; + inode->i_mapping->a_ops = &jfs_aops; + } else if (S_ISDIR(inode->i_mode)) { + inode->i_op = &jfs_dir_inode_operations; + inode->i_fop = &jfs_dir_operations; + inode->i_mapping->a_ops = &jfs_aops; + inode->i_mapping->gfp_mask = GFP_NOFS; + } else if (S_ISLNK(inode->i_mode)) { + if (inode->i_size > IDATASIZE) { + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &jfs_aops; + } else + inode->i_op = &jfs_symlink_inode_operations; + } else { + init_special_inode(inode, inode->i_mode, + kdev_t_to_nr(inode->i_rdev)); + } + + return; + + bad_inode: + make_bad_inode(inode); +} + +/* This define is from fs/open.c */ +#define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m)) + +/* + * Workhorse of both fsync & write_inode + */ +int jfs_commit_inode(struct inode *inode, int wait) +{ + int rc = 0; + tid_t tid; + static int noisy = 5; + + jFYI(1, ("In jfs_commit_inode, inode = 0x%p\n", inode)); + + /* + * Don't commit if inode has been committed since last being + * marked dirty, or if it has been deleted. + */ + if (test_cflag(COMMIT_Nolink, inode) || + !test_cflag(COMMIT_Dirty, inode)) + return 0; + + if (isReadOnly(inode)) { + /* kernel allows writes to devices on read-only + * partitions and may think inode is dirty + */ + if (!special_file(inode->i_mode) && noisy) { + jERROR(1, ("jfs_commit_inode(0x%p) called on " + "read-only volume\n", inode)); + jERROR(1, ("Is remount racy?\n")); + noisy--; + } + return 0; + } + + tid = txBegin(inode->i_sb, COMMIT_INODE); + rc = txCommit(tid, 1, &inode, wait ? COMMIT_SYNC : 0); + txEnd(tid); + return -rc; +} + +void jfs_write_inode(struct inode *inode, int wait) +{ + /* + * If COMMIT_DIRTY is not set, the inode isn't really dirty. + * It has been committed since the last change, but was still + * on the dirty inode list + */ + if (test_cflag(COMMIT_Nolink, inode) || + !test_cflag(COMMIT_Dirty, inode)) + return; + + IWRITE_LOCK(inode); + + if (jfs_commit_inode(inode, wait)) { + jERROR(1, ("jfs_write_inode: jfs_commit_inode failed!\n")); + } + + IWRITE_UNLOCK(inode); +} + +void jfs_delete_inode(struct inode *inode) +{ + jFYI(1, ("In jfs_delete_inode, inode = 0x%p\n", inode)); + + IWRITE_LOCK(inode); + if (test_cflag(COMMIT_Freewmap, inode)) + freeZeroLink(inode); + + diFree(inode); + IWRITE_UNLOCK(inode); + + clear_inode(inode); +} + +void jfs_dirty_inode(struct inode *inode) +{ + static int noisy = 5; + + if (isReadOnly(inode)) { + if (!special_file(inode->i_mode) && noisy) { + /* kernel allows writes to devices on read-only + * partitions and may try to mark inode dirty + */ + jERROR(1, ("jfs_dirty_inode called on " + "read-only volume\n")); + jERROR(1, ("Is remount racy?\n")); + noisy--; + } + return; + } + + set_cflag(COMMIT_Dirty, inode); +} + +static int jfs_get_block(struct inode *ip, sector_t lblock, + struct buffer_head *bh_result, int create) +{ + s64 lblock64 = lblock; + int no_size_check = 0; + int rc = 0; + int take_locks; + xad_t xad; + s64 xaddr; + int xflag; + s32 xlen; + + /* + * If this is a special inode (imap, dmap) or directory, + * the lock should already be taken + */ + take_locks = ((JFS_IP(ip)->fileset != AGGREGATE_I) && + !S_ISDIR(ip->i_mode)); + /* + * Take appropriate lock on inode + */ + if (take_locks) { + if (create) + IWRITE_LOCK(ip); + else + IREAD_LOCK(ip); + } + + /* + * A directory's "data" is the inode index table, but i_size is the + * size of the d-tree, so don't check the offset against i_size + */ + if (S_ISDIR(ip->i_mode)) + no_size_check = 1; + + if ((no_size_check || + ((lblock64 << ip->i_sb->s_blocksize_bits) < ip->i_size)) && + (xtLookup + (ip, lblock64, 1, &xflag, &xaddr, &xlen, no_size_check) + == 0) && xlen) { + if (xflag & XAD_NOTRECORDED) { + if (!create) + /* + * Allocated but not recorded, read treats + * this as a hole + */ + goto unlock; +#ifdef _JFS_4K + XADoffset(&xad, lblock64); + XADlength(&xad, xlen); + XADaddress(&xad, xaddr); +#else /* _JFS_4K */ + /* + * As long as block size = 4K, this isn't a problem. + * We should mark the whole page not ABNR, but how + * will we know to mark the other blocks BH_New? + */ + BUG(); +#endif /* _JFS_4K */ + rc = extRecord(ip, &xad); + if (rc) + goto unlock; + bh_result->b_state |= (1UL << BH_New); + } + + map_bh(bh_result, ip->i_sb, xaddr); + goto unlock; + } + if (!create) + goto unlock; + + /* + * Allocate a new block + */ +#ifdef _JFS_4K + if ((rc = + extHint(ip, lblock64 << ip->i_sb->s_blocksize_bits, &xad))) + goto unlock; + rc = extAlloc(ip, 1, lblock64, &xad, FALSE); + if (rc) + goto unlock; + + bh_result->b_state |= (1UL << BH_New); + map_bh(bh_result, ip->i_sb, addressXAD(&xad)); + +#else /* _JFS_4K */ + /* + * We need to do whatever it takes to keep all but the last buffers + * in 4K pages - see jfs_write.c + */ + BUG(); +#endif /* _JFS_4K */ + + unlock: + /* + * Release lock on inode + */ + if (take_locks) { + if (create) + IWRITE_UNLOCK(ip); + else + IREAD_UNLOCK(ip); + } + return -rc; +} + +static int jfs_writepage(struct page *page) +{ + return block_write_full_page(page, jfs_get_block); +} + +static int jfs_readpage(struct file *file, struct page *page) +{ + return block_read_full_page(page, jfs_get_block); +} + +static int jfs_prepare_write(struct file *file, + struct page *page, unsigned from, unsigned to) +{ + return block_prepare_write(page, from, to, jfs_get_block); +} + +static int jfs_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping, block, jfs_get_block); +} + +static int jfs_direct_IO(int rw, struct inode *inode, struct kiobuf *iobuf, + unsigned long blocknr, int blocksize) +{ + return generic_direct_IO(rw, inode, iobuf, blocknr, + blocksize, jfs_get_block); +} + +struct address_space_operations jfs_aops = { + readpage: jfs_readpage, + writepage: jfs_writepage, + sync_page: block_sync_page, + prepare_write: jfs_prepare_write, + commit_write: generic_commit_write, + bmap: jfs_bmap, + direct_IO: jfs_direct_IO, +}; diff -Nru a/fs/jfs/jfs_btree.h b/fs/jfs/jfs_btree.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_btree.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,163 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * + */ +#ifndef _H_JFS_BTREE +#define _H_JFS_BTREE +/* + * jfs_btree.h: B+-tree + * + * JFS B+-tree (dtree and xtree) common definitions + */ + +/* + * basic btree page - btpage_t + */ +typedef struct { + s64 next; /* 8: right sibling bn */ + s64 prev; /* 8: left sibling bn */ + + u8 flag; /* 1: */ + u8 rsrvd[7]; /* 7: type specific */ + s64 self; /* 8: self address */ + + u8 entry[4064]; /* 4064: */ +} btpage_t; /* (4096) */ + +/* btpaget_t flag */ +#define BT_TYPE 0x07 /* B+-tree index */ +#define BT_ROOT 0x01 /* root page */ +#define BT_LEAF 0x02 /* leaf page */ +#define BT_INTERNAL 0x04 /* internal page */ +#define BT_RIGHTMOST 0x10 /* rightmost page */ +#define BT_LEFTMOST 0x20 /* leftmost page */ +#define BT_SWAPPED 0x80 /* used by fsck for endian swapping */ + +/* btorder (in inode) */ +#define BT_RANDOM 0x0000 +#define BT_SEQUENTIAL 0x0001 +#define BT_LOOKUP 0x0010 +#define BT_INSERT 0x0020 +#define BT_DELETE 0x0040 + +/* + * btree page buffer cache access + */ +#define BT_IS_ROOT(MP) (((MP)->xflag & COMMIT_PAGE) == 0) + +/* get page from buffer page */ +#define BT_PAGE(IP, MP, TYPE, ROOT)\ + (BT_IS_ROOT(MP) ? (TYPE *)&JFS_IP(IP)->ROOT : (TYPE *)(MP)->data) + +/* get the page buffer and the page for specified block address */ +#define BT_GETPAGE(IP, BN, MP, TYPE, SIZE, P, RC, ROOT)\ +{\ + if ((BN) == 0)\ + {\ + MP = (metapage_t *)&JFS_IP(IP)->bxflag;\ + P = (TYPE *)&JFS_IP(IP)->ROOT;\ + RC = 0;\ + jEVENT(0,("%d BT_GETPAGE returning root\n", __LINE__));\ + }\ + else\ + {\ + jEVENT(0,("%d BT_GETPAGE reading block %d\n", __LINE__,\ + (int)BN));\ + MP = read_metapage((IP), BN, SIZE, 1);\ + if (MP) {\ + RC = 0;\ + P = (MP)->data;\ + } else {\ + P = NULL;\ + jERROR(1,("bread failed!\n"));\ + RC = EIO;\ + }\ + }\ +} + +#define BT_MARK_DIRTY(MP, IP)\ +{\ + if (BT_IS_ROOT(MP))\ + mark_inode_dirty(IP);\ + else\ + mark_metapage_dirty(MP);\ +} + +/* put the page buffer */ +#define BT_PUTPAGE(MP)\ +{\ + if (! BT_IS_ROOT(MP)) \ + release_metapage(MP); \ +} + + +/* + * btree traversal stack + * + * record the path traversed during the search; + * top frame record the leaf page/entry selected. + */ +#define MAXTREEHEIGHT 8 +typedef struct btframe { /* stack frame */ + s64 bn; /* 8: */ + s16 index; /* 2: */ + s16 lastindex; /* 2: */ + struct metapage *mp; /* 4: */ +} btframe_t; /* (16) */ + +typedef struct btstack { + btframe_t *top; /* 4: */ + int nsplit; /* 4: */ + btframe_t stack[MAXTREEHEIGHT]; +} btstack_t; + +#define BT_CLR(btstack)\ + (btstack)->top = (btstack)->stack + +#define BT_PUSH(BTSTACK, BN, INDEX)\ +{\ + (BTSTACK)->top->bn = BN;\ + (BTSTACK)->top->index = INDEX;\ + ++(BTSTACK)->top;\ + assert((BTSTACK)->top != &((BTSTACK)->stack[MAXTREEHEIGHT]));\ +} + +#define BT_POP(btstack)\ + ( (btstack)->top == (btstack)->stack ? NULL : --(btstack)->top ) + +#define BT_STACK(btstack)\ + ( (btstack)->top == (btstack)->stack ? NULL : (btstack)->top ) + +/* retrieve search results */ +#define BT_GETSEARCH(IP, LEAF, BN, MP, TYPE, P, INDEX, ROOT)\ +{\ + BN = (LEAF)->bn;\ + MP = (LEAF)->mp;\ + if (BN)\ + P = (TYPE *)MP->data;\ + else\ + P = (TYPE *)&JFS_IP(IP)->ROOT;\ + INDEX = (LEAF)->index;\ +} + +/* put the page buffer of search */ +#define BT_PUTSEARCH(BTSTACK)\ +{\ + if (! BT_IS_ROOT((BTSTACK)->top->mp))\ + release_metapage((BTSTACK)->top->mp);\ +} +#endif /* _H_JFS_BTREE */ diff -Nru a/fs/jfs/jfs_debug.c b/fs/jfs/jfs_debug.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_debug.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,145 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 "jfs_incore.h" +#include "jfs_filsys.h" +#include "jfs_debug.h" + +#ifdef CONFIG_JFS_DEBUG +void dump_mem(char *label, void *data, int length) +{ + int i, j; + int *intptr = data; + char *charptr = data; + char buf[10], line[80]; + + printk("%s: dump of %d bytes of data at 0x%p\n\n", label, length, + data); + for (i = 0; i < length; i += 16) { + line[0] = 0; + for (j = 0; (j < 4) && (i + j * 4 < length); j++) { + sprintf(buf, " %08x", intptr[i / 4 + j]); + strcat(line, buf); + } + buf[0] = ' '; + buf[2] = 0; + for (j = 0; (j < 16) && (i + j < length); j++) { + buf[1] = + isprint(charptr[i + j]) ? charptr[i + j] : '.'; + strcat(line, buf); + } + printk("%s\n", line); + } +} + +#ifdef CONFIG_PROC_FS +static int loglevel_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = sprintf(page, "%d\n", jfsloglevel); + + len -= off; + *start = page + off; + + if (len > count) + len = count; + else + *eof = 1; + + if (len < 0) + len = 0; + + return len; +} + +static int loglevel_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char c; + + if (get_user(c, buffer)) + return -EFAULT; + + /* yes, I know this is an ASCIIism. --hch */ + if (c < '0' || c > '9') + return -EINVAL; + jfsloglevel = c - '0'; + return count; +} + + +extern read_proc_t jfs_txanchor_read; +#ifdef CONFIG_JFS_STATISTICS +extern read_proc_t jfs_lmstats_read; +extern read_proc_t jfs_xtstat_read; +extern read_proc_t jfs_mpstat_read; +#endif +static struct proc_dir_entry *base; + +static struct { + const char *name; + read_proc_t *read_fn; + write_proc_t *write_fn; +} Entries[] = { + { "TxAnchor", jfs_txanchor_read, }, +#ifdef CONFIG_JFS_STATISTICS + { "lmstats", jfs_lmstats_read, }, + { "xtstat", jfs_xtstat_read, }, + { "mpstat", jfs_mpstat_read, }, +#endif + { "loglevel", loglevel_read, loglevel_write } +}; +#define NPROCENT (sizeof(Entries)/sizeof(Entries[0])) + +void jfs_proc_init(void) +{ + int i; + + if (!(base = proc_mkdir("jfs", proc_root_fs))) + return; + base->owner = THIS_MODULE; + + for (i = 0; i < NPROCENT; i++) { + struct proc_dir_entry *p; + if ((p = create_proc_entry(Entries[i].name, 0, base))) { + p->read_proc = Entries[i].read_fn; + p->write_proc = Entries[i].write_fn; + } + } +} + +void jfs_proc_clean(void) +{ + int i; + + if (base) { + for (i = 0; i < NPROCENT; i++) + remove_proc_entry(Entries[i].name, base); + remove_proc_entry("jfs", base); + } +} + +#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_JFS_DEBUG */ diff -Nru a/fs/jfs/jfs_debug.h b/fs/jfs/jfs_debug.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_debug.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,96 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * +*/ +#ifndef _H_JFS_DEBUG +#define _H_JFS_DEBUG + +/* + * jfs_debug.h + * + * global debug message, data structure/macro definitions + * under control of CONFIG_JFS_DEBUG, CONFIG_JFS_STATISTICS; + */ + +/* + * assert with traditional printf/panic + */ +#ifdef CONFIG_KERNEL_ASSERTS +/* kgdb stuff */ +#define assert(p) KERNEL_ASSERT(#p, p) +#else +#define assert(p) {\ +if (!(p))\ + {\ + printk("assert(%s)\n",#p);\ + BUG();\ + }\ +} +#endif + +/* + * debug ON + * -------- + */ +#ifdef CONFIG_JFS_DEBUG +#define ASSERT(p) assert(p) + +/* dump memory contents */ +extern void dump_mem(char *label, void *data, int length); +extern int jfsloglevel; + +/* information message: e.g., configuration, major event */ +#define jFYI(button, prspec) \ + do { if (button && jfsloglevel > 1) printk prspec; } while (0) + +/* error event message: e.g., i/o error */ +extern int jfsERROR; +#define jERROR(button, prspec) \ + do { if (button && jfsloglevel > 0) { printk prspec; } } while (0) + +/* debug event message: */ +#define jEVENT(button,prspec) \ + do { if (button) printk prspec; } while (0) + +/* + * debug OFF + * --------- + */ +#else /* CONFIG_JFS_DEBUG */ +#define dump_mem(label,data,length) +#define ASSERT(p) +#define jEVENT(button,prspec) +#define jERROR(button,prspec) +#define jFYI(button,prspec) +#endif /* CONFIG_JFS_DEBUG */ + +/* + * statistics + * ---------- + */ +#ifdef CONFIG_JFS_STATISTICS +#define INCREMENT(x) ((x)++) +#define DECREMENT(x) ((x)--) +#define HIGHWATERMARK(x,y) ((x) = max((x), (y))) +#else +#define INCREMENT(x) +#define DECREMENT(x) +#define HIGHWATERMARK(x,y) +#endif /* CONFIG_JFS_STATISTICS */ + +#endif /* _H_JFS_DEBUG */ diff -Nru a/fs/jfs/jfs_defragfs.h b/fs/jfs/jfs_defragfs.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_defragfs.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,55 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * + */ +#ifndef _H_JFS_DEFRAGFS +#define _H_JFS_DEFRAGFS + +/* + * jfs_defragfs.h + */ +/* + * defragfs parameter list + */ +typedef struct { + uint flag; /* 4: */ + u8 dev; /* 1: */ + u8 pad[3]; /* 3: */ + s32 fileset; /* 4: */ + u32 inostamp; /* 4: */ + u32 ino; /* 4: */ + u32 gen; /* 4: */ + s64 xoff; /* 8: */ + s64 old_xaddr; /* 8: */ + s64 new_xaddr; /* 8: */ + s32 xlen; /* 4: */ +} defragfs_t; /* (52) */ + +/* plist flag */ +#define DEFRAGFS_SYNC 0x80000000 +#define DEFRAGFS_COMMIT 0x40000000 +#define DEFRAGFS_RELOCATE 0x10000000 + +#define INODE_TYPE 0x0000F000 /* IFREG or IFDIR */ + +#define EXTENT_TYPE 0x000000ff +#define DTPAGE 0x00000001 +#define XTPAGE 0x00000002 +#define DATAEXT 0x00000004 +#define EAEXT 0x00000008 + +#endif /* _H_JFS_DEFRAGFS */ diff -Nru a/fs/jfs/jfs_dinode.h b/fs/jfs/jfs_dinode.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_dinode.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,157 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * + */ + +#ifndef _H_JFS_DINODE +#define _H_JFS_DINODE + +/* + * jfs_dinode.h: on-disk inode manager + * + */ + +#define INODESLOTSIZE 128 +#define L2INODESLOTSIZE 7 +#define log2INODESIZE 9 /* log2(bytes per dinode) */ + + +/* + * on-disk inode (dinode_t): 512 bytes + * + * note: align 64-bit fields on 8-byte boundary. + */ +struct dinode { + /* + * I. base area (128 bytes) + * ------------------------ + * + * define generic/POSIX attributes + */ + u32 di_inostamp; /* 4: stamp to show inode belongs to fileset */ + s32 di_fileset; /* 4: fileset number */ + u32 di_number; /* 4: inode number, aka file serial number */ + u32 di_gen; /* 4: inode generation number */ + + pxd_t di_ixpxd; /* 8: inode extent descriptor */ + + s64 di_size; /* 8: size */ + s64 di_nblocks; /* 8: number of blocks allocated */ + + u32 di_nlink; /* 4: number of links to the object */ + + u32 di_uid; /* 4: user id of owner */ + u32 di_gid; /* 4: group id of owner */ + + u32 di_mode; /* 4: attribute, format and permission */ + + struct timestruc_t di_atime; /* 8: time last data accessed */ + struct timestruc_t di_ctime; /* 8: time last status changed */ + struct timestruc_t di_mtime; /* 8: time last data modified */ + struct timestruc_t di_otime; /* 8: time created */ + + dxd_t di_acl; /* 16: acl descriptor */ + + dxd_t di_ea; /* 16: ea descriptor */ + + u32 di_next_index; /* 4: Next available dir_table index */ + + s32 di_acltype; /* 4: Type of ACL */ + + /* + * Extension Areas. + * + * Historically, the inode was partitioned into 4 128-byte areas, + * the last 3 being defined as unions which could have multiple + * uses. The first 96 bytes had been completely unused until + * an index table was added to the directory. It is now more + * useful to describe the last 3/4 of the inode as a single + * union. We would probably be better off redesigning the + * entire structure from scratch, but we don't want to break + * commonality with OS/2's JFS at this time. + */ + union { + struct { + /* + * This table contains the information needed to + * find a directory entry from a 32-bit index. + * If the index is small enough, the table is inline, + * otherwise, an x-tree root overlays this table + */ + dir_table_slot_t _table[12]; /* 96: inline */ + + dtroot_t _dtroot; /* 288: dtree root */ + } _dir; /* (384) */ +#define di_dirtable u._dir._table +#define di_dtroot u._dir._dtroot +#define di_parent di_dtroot.header.idotdot +#define di_DASD di_dtroot.header.DASD + + struct { + union { + u8 _data[96]; /* 96: unused */ + struct { + void *_imap; /* 4: unused */ + u32 _gengen; /* 4: generator */ + } _imap; + } _u1; /* 96: */ +#define di_gengen u._file._u1._imap._gengen + + union { + xtpage_t _xtroot; + struct { + u8 unused[16]; /* 16: */ + dxd_t _dxd; /* 16: */ + union { + u32 _rdev; /* 4: */ + u8 _fastsymlink[128]; + } _u; + u8 _inlineea[128]; + } _special; + } _u2; + } _file; +#define di_xtroot u._file._u2._xtroot +#define di_dxd u._file._u2._special._dxd +#define di_btroot di_xtroot +#define di_inlinedata u._file._u2._special._u +#define di_rdev u._file._u2._special._u._rdev +#define di_fastsymlink u._file._u2._special._u._fastsymlink +#define di_inlineea u._file._u2._special._inlineea + } u; +}; + +typedef struct dinode dinode_t; + + +/* extended mode bits (on-disk inode di_mode) */ +#define IFJOURNAL 0x00010000 /* journalled file */ +#define ISPARSE 0x00020000 /* sparse file enabled */ +#define INLINEEA 0x00040000 /* inline EA area free */ +#define ISWAPFILE 0x00800000 /* file open for pager swap space */ + +/* more extended mode bits: attributes for OS/2 */ +#define IREADONLY 0x02000000 /* no write access to file */ +#define IARCHIVE 0x40000000 /* file archive bit */ +#define ISYSTEM 0x08000000 /* system file */ +#define IHIDDEN 0x04000000 /* hidden file */ +#define IRASH 0x4E000000 /* mask for changeable attributes */ +#define INEWNAME 0x80000000 /* non-8.3 filename format */ +#define IDIRECTORY 0x20000000 /* directory (shadow of real bit) */ +#define ATTRSHIFT 25 /* bits to shift to move attribute + specification to mode position */ + +#endif /*_H_JFS_DINODE */ diff -Nru a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_dmap.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,4190 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * + * + * MODULE_NAME: jfs_dmap.c + * + * COMPONENT_NAME: sysjfs + * + * FUNCTION: block allocation map manager + * +*/ + +/* + * Change History : + * + */ + +#include +#include +#include "jfs_incore.h" +#include "jfs_dmap.h" +#include "jfs_imap.h" +#include "jfs_lock.h" +#include "jfs_metapage.h" +#include "jfs_debug.h" + +/* + * Debug code for double-checking block map + */ +/* #define _JFS_DEBUG_DMAP 1 */ + +#ifdef _JFS_DEBUG_DMAP +#define DBINITMAP(size,ipbmap,results) \ + DBinitmap(size,ipbmap,results) +#define DBALLOC(dbmap,mapsize,blkno,nblocks) \ + DBAlloc(dbmap,mapsize,blkno,nblocks) +#define DBFREE(dbmap,mapsize,blkno,nblocks) \ + DBFree(dbmap,mapsize,blkno,nblocks) +#define DBALLOCCK(dbmap,mapsize,blkno,nblocks) \ + DBAllocCK(dbmap,mapsize,blkno,nblocks) +#define DBFREECK(dbmap,mapsize,blkno,nblocks) \ + DBFreeCK(dbmap,mapsize,blkno,nblocks) + +static void DBinitmap(s64, struct inode *, u32 **); +static void DBAlloc(uint *, s64, s64, s64); +static void DBFree(uint *, s64, s64, s64); +static void DBAllocCK(uint *, s64, s64, s64); +static void DBFreeCK(uint *, s64, s64, s64); +#else +#define DBINITMAP(size,ipbmap,results) +#define DBALLOC(dbmap, mapsize, blkno, nblocks) +#define DBFREE(dbmap, mapsize, blkno, nblocks) +#define DBALLOCCK(dbmap, mapsize, blkno, nblocks) +#define DBFREECK(dbmap, mapsize, blkno, nblocks) +#endif /* _JFS_DEBUG_DMAP */ + +/* + * SERIALIZATION of the Block Allocation Map. + * + * the working state of the block allocation map is accessed in + * two directions: + * + * 1) allocation and free requests that start at the dmap + * level and move up through the dmap control pages (i.e. + * the vast majority of requests). + * + * 2) allocation requests that start at dmap control page + * level and work down towards the dmaps. + * + * the serialization scheme used here is as follows. + * + * requests which start at the bottom are serialized against each + * other through buffers and each requests holds onto its buffers + * as it works it way up from a single dmap to the required level + * of dmap control page. + * requests that start at the top are serialized against each other + * and request that start from the bottom by the multiple read/single + * write inode lock of the bmap inode. requests starting at the top + * take this lock in write mode while request starting at the bottom + * take the lock in read mode. a single top-down request may proceed + * exclusively while multiple bottoms-up requests may proceed + * simultaneously (under the protection of busy buffers). + * + * in addition to information found in dmaps and dmap control pages, + * the working state of the block allocation map also includes read/ + * write information maintained in the bmap descriptor (i.e. total + * free block count, allocation group level free block counts). + * a single exclusive lock (BMAP_LOCK) is used to guard this information + * in the face of multiple-bottoms up requests. + * (lock ordering: IREAD_LOCK, BMAP_LOCK); + * + * accesses to the persistent state of the block allocation map (limited + * to the persistent bitmaps in dmaps) is guarded by (busy) buffers. + */ + +#define BMAP_LOCK_INIT(bmp) init_MUTEX(&bmp->db_bmaplock) +#define BMAP_LOCK(bmp) down(&bmp->db_bmaplock) +#define BMAP_UNLOCK(bmp) up(&bmp->db_bmaplock) + +/* + * forward references + */ +static void dbAllocBits(bmap_t * bmp, dmap_t * dp, s64 blkno, int nblocks); +static void dbSplit(dmtree_t * tp, int leafno, int splitsz, int newval); +static void dbBackSplit(dmtree_t * tp, int leafno); +static void dbJoin(dmtree_t * tp, int leafno, int newval); +static void dbAdjTree(dmtree_t * tp, int leafno, int newval); +static int dbAdjCtl(bmap_t * bmp, s64 blkno, int newval, int alloc, + int level); +static int dbAllocAny(bmap_t * bmp, s64 nblocks, int l2nb, s64 * results); +static int dbAllocNext(bmap_t * bmp, dmap_t * dp, s64 blkno, int nblocks); +static int dbAllocNear(bmap_t * bmp, dmap_t * dp, s64 blkno, int nblocks, + int l2nb, s64 * results); +static int dbAllocDmap(bmap_t * bmp, dmap_t * dp, s64 blkno, int nblocks); +static int dbAllocDmapLev(bmap_t * bmp, dmap_t * dp, int nblocks, int l2nb, + s64 * results); +static int dbAllocAG(bmap_t * bmp, int agno, s64 nblocks, int l2nb, + s64 * results); +static int dbAllocCtl(bmap_t * bmp, s64 nblocks, int l2nb, s64 blkno, + s64 * results); +int dbExtend(struct inode *ip, s64 blkno, s64 nblocks, s64 addnblocks); +static int dbFindBits(u32 word, int l2nb); +static int dbFindCtl(bmap_t * bmp, int l2nb, int level, s64 * blkno); +static int dbFindLeaf(dmtree_t * tp, int l2nb, int *leafidx); +static void dbFreeBits(bmap_t * bmp, dmap_t * dp, s64 blkno, int nblocks); +static int dbFreeDmap(bmap_t * bmp, dmap_t * dp, s64 blkno, int nblocks); +static int dbMaxBud(u8 * cp); +s64 dbMapFileSizeToMapSize(struct inode *ipbmap); +int blkstol2(s64 nb); +void fsDirty(void); + +int cntlz(u32 value); +int cnttz(u32 word); + +static int dbAllocDmapBU(bmap_t * bmp, dmap_t * dp, s64 blkno, + int nblocks); +static int dbInitDmap(dmap_t * dp, s64 blkno, int nblocks); +static int dbInitDmapTree(dmap_t * dp); +static int dbInitTree(dmaptree_t * dtp); +static int dbInitDmapCtl(dmapctl_t * dcp, int level, int i); +static int dbGetL2AGSize(s64 nblocks); + +/* + * buddy table + * + * table used for determining buddy sizes within characters of + * dmap bitmap words. the characters themselves serve as indexes + * into the table, with the table elements yielding the maximum + * binary buddy of free bits within the character. + */ +signed char budtab[256] = { + 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1 +}; + + +/* + * NAME: dbMount() + * + * FUNCTION: initializate the block allocation map. + * + * memory is allocated for the in-core bmap descriptor and + * the in-core descriptor is initialized from disk. + * + * PARAMETERS: + * ipbmap - pointer to in-core inode for the block map. + * + * RETURN VALUES: + * 0 - success + * ENOMEM - insufficient memory + * EIO - i/o error + */ +int dbMount(struct inode *ipbmap) +{ + bmap_t *bmp; + dbmap_t *dbmp_le; + metapage_t *mp; + int i; + + /* + * allocate/initialize the in-memory bmap descriptor + */ + /* allocate memory for the in-memory bmap descriptor */ + bmp = kmalloc(sizeof(bmap_t), GFP_KERNEL); + if (bmp == NULL) + return (ENOMEM); + + /* read the on-disk bmap descriptor. */ + mp = read_metapage(ipbmap, + BMAPBLKNO << JFS_SBI(ipbmap->i_sb)->l2nbperpage, + PSIZE, 0); + if (mp == NULL) { + kfree(bmp); + return (EIO); + } + + /* copy the on-disk bmap descriptor to its in-memory version. */ + dbmp_le = (dbmap_t *) mp->data; + bmp->db_mapsize = le64_to_cpu(dbmp_le->dn_mapsize); + bmp->db_nfree = le64_to_cpu(dbmp_le->dn_nfree); + bmp->db_l2nbperpage = le32_to_cpu(dbmp_le->dn_l2nbperpage); + bmp->db_numag = le32_to_cpu(dbmp_le->dn_numag); + bmp->db_maxlevel = le32_to_cpu(dbmp_le->dn_maxlevel); + bmp->db_maxag = le32_to_cpu(dbmp_le->dn_maxag); + bmp->db_agpref = le32_to_cpu(dbmp_le->dn_agpref); + bmp->db_aglevel = le32_to_cpu(dbmp_le->dn_aglevel); + bmp->db_agheigth = le32_to_cpu(dbmp_le->dn_agheigth); + bmp->db_agwidth = le32_to_cpu(dbmp_le->dn_agwidth); + bmp->db_agstart = le32_to_cpu(dbmp_le->dn_agstart); + bmp->db_agl2size = le32_to_cpu(dbmp_le->dn_agl2size); + for (i = 0; i < MAXAG; i++) + bmp->db_agfree[i] = le64_to_cpu(dbmp_le->dn_agfree[i]); + bmp->db_agsize = le64_to_cpu(dbmp_le->dn_agsize); + bmp->db_maxfreebud = dbmp_le->dn_maxfreebud; + + /* release the buffer. */ + release_metapage(mp); + + /* bind the bmap inode and the bmap descriptor to each other. */ + bmp->db_ipbmap = ipbmap; + JFS_SBI(ipbmap->i_sb)->bmap = bmp; + + DBINITMAP(bmp->db_mapsize, ipbmap, &bmp->db_DBmap); + + /* + * allocate/initialize the bmap lock + */ + BMAP_LOCK_INIT(bmp); + + return (0); +} + + +/* + * NAME: dbUnmount() + * + * FUNCTION: terminate the block allocation map in preparation for + * file system unmount. + * + * the in-core bmap descriptor is written to disk and + * the memory for this descriptor is freed. + * + * PARAMETERS: + * ipbmap - pointer to in-core inode for the block map. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error + */ +int dbUnmount(struct inode *ipbmap, int mounterror) +{ + bmap_t *bmp = JFS_SBI(ipbmap->i_sb)->bmap; + + if (!(mounterror || isReadOnly(ipbmap))) + dbSync(ipbmap); + + /* + * Invalidate the page cache buffers + */ + truncate_inode_pages(ipbmap->i_mapping, 0); + + /* free the memory for the in-memory bmap. */ + kfree(bmp); + + return (0); +} + +/* + * dbSync() + */ +int dbSync(struct inode *ipbmap) +{ + dbmap_t *dbmp_le; + bmap_t *bmp = JFS_SBI(ipbmap->i_sb)->bmap; + metapage_t *mp; + int i; + + /* + * write bmap global control page + */ + /* get the buffer for the on-disk bmap descriptor. */ + mp = read_metapage(ipbmap, + BMAPBLKNO << JFS_SBI(ipbmap->i_sb)->l2nbperpage, + PSIZE, 0); + if (mp == NULL) { + jERROR(1,("dbSync: read_metapage failed!\n")); + return (EIO); + } + /* copy the in-memory version of the bmap to the on-disk version */ + dbmp_le = (dbmap_t *) mp->data; + dbmp_le->dn_mapsize = cpu_to_le64(bmp->db_mapsize); + dbmp_le->dn_nfree = cpu_to_le64(bmp->db_nfree); + dbmp_le->dn_l2nbperpage = cpu_to_le32(bmp->db_l2nbperpage); + dbmp_le->dn_numag = cpu_to_le32(bmp->db_numag); + dbmp_le->dn_maxlevel = cpu_to_le32(bmp->db_maxlevel); + dbmp_le->dn_maxag = cpu_to_le32(bmp->db_maxag); + dbmp_le->dn_agpref = cpu_to_le32(bmp->db_agpref); + dbmp_le->dn_aglevel = cpu_to_le32(bmp->db_aglevel); + dbmp_le->dn_agheigth = cpu_to_le32(bmp->db_agheigth); + dbmp_le->dn_agwidth = cpu_to_le32(bmp->db_agwidth); + dbmp_le->dn_agstart = cpu_to_le32(bmp->db_agstart); + dbmp_le->dn_agl2size = cpu_to_le32(bmp->db_agl2size); + for (i = 0; i < MAXAG; i++) + dbmp_le->dn_agfree[i] = cpu_to_le64(bmp->db_agfree[i]); + dbmp_le->dn_agsize = cpu_to_le64(bmp->db_agsize); + dbmp_le->dn_maxfreebud = bmp->db_maxfreebud; + + /* write the buffer */ + write_metapage(mp); + + /* + * write out dirty pages of bmap + */ + fsync_inode_data_buffers(ipbmap); + + ipbmap->i_state |= I_DIRTY; + diWriteSpecial(ipbmap); + + return (0); +} + + +/* + * NAME: dbFree() + * + * FUNCTION: free the specified block range from the working block + * allocation map. + * + * the blocks will be free from the working map one dmap + * at a time. + * + * PARAMETERS: + * ip - pointer to in-core inode; + * blkno - starting block number to be freed. + * nblocks - number of blocks to be freed. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error + */ +int dbFree(struct inode *ip, s64 blkno, s64 nblocks) +{ + metapage_t *mp; + dmap_t *dp; + int nb, rc; + s64 lblkno, rem; + struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap; + bmap_t *bmp = JFS_SBI(ip->i_sb)->bmap; + + IREAD_LOCK(ipbmap); + + /* block to be freed better be within the mapsize. */ + assert(blkno + nblocks <= bmp->db_mapsize); + + /* + * free the blocks a dmap at a time. + */ + mp = NULL; + for (rem = nblocks; rem > 0; rem -= nb, blkno += nb) { + /* release previous dmap if any */ + if (mp) { + write_metapage(mp); + } + + /* get the buffer for the current dmap. */ + lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage); + mp = read_metapage(ipbmap, lblkno, PSIZE, 0); + if (mp == NULL) { + IREAD_UNLOCK(ipbmap); + return (EIO); + } + dp = (dmap_t *) mp->data; + + /* determine the number of blocks to be freed from + * this dmap. + */ + nb = min(rem, BPERDMAP - (blkno & (BPERDMAP - 1))); + + DBALLOCCK(bmp->db_DBmap, bmp->db_mapsize, blkno, nb); + + /* free the blocks. */ + if ((rc = dbFreeDmap(bmp, dp, blkno, nb))) { + release_metapage(mp); + IREAD_UNLOCK(ipbmap); + return (rc); + } + + DBFREE(bmp->db_DBmap, bmp->db_mapsize, blkno, nb); + } + + /* write the last buffer. */ + write_metapage(mp); + + IREAD_UNLOCK(ipbmap); + + return (0); +} + + +/* + * NAME: dbUpdatePMap() + * + * FUNCTION: update the allocation state (free or allocate) of the + * specified block range in the persistent block allocation map. + * + * the blocks will be updated in the persistent map one + * dmap at a time. + * + * PARAMETERS: + * ipbmap - pointer to in-core inode for the block map. + * free - TRUE if block range is to be freed from the persistent + * map; FALSE if it is to be allocated. + * blkno - starting block number of the range. + * nblocks - number of contiguous blocks in the range. + * tblk - transaction block; + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error + */ +int +dbUpdatePMap(struct inode *ipbmap, + int free, s64 blkno, s64 nblocks, tblock_t * tblk) +{ + int nblks, dbitno, wbitno, rbits; + int word, nbits, nwords; + bmap_t *bmp = JFS_SBI(ipbmap->i_sb)->bmap; + s64 lblkno, rem, lastlblkno; + u32 mask; + dmap_t *dp; + metapage_t *mp; + log_t *log; + int lsn, difft, diffp; + + /* the blocks better be within the mapsize. */ + assert(blkno + nblocks <= bmp->db_mapsize); + + /* compute delta of transaction lsn from log syncpt */ + lsn = tblk->lsn; + log = (log_t *) JFS_SBI(tblk->sb)->log; + logdiff(difft, lsn, log); + + /* + * update the block state a dmap at a time. + */ + mp = NULL; + lastlblkno = 0; + for (rem = nblocks; rem > 0; rem -= nblks, blkno += nblks) { + /* get the buffer for the current dmap. */ + lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage); + if (lblkno != lastlblkno) { + if (mp) { + write_metapage(mp); + } + + mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, + 0); + if (mp == NULL) + return (EIO); + } + dp = (dmap_t *) mp->data; + + /* determine the bit number and word within the dmap of + * the starting block. also determine how many blocks + * are to be updated within this dmap. + */ + dbitno = blkno & (BPERDMAP - 1); + word = dbitno >> L2DBWORD; + nblks = min(rem, (s64)BPERDMAP - dbitno); + + /* update the bits of the dmap words. the first and last + * words may only have a subset of their bits updated. if + * this is the case, we'll work against that word (i.e. + * partial first and/or last) only in a single pass. a + * single pass will also be used to update all words that + * are to have all their bits updated. + */ + for (rbits = nblks; rbits > 0; + rbits -= nbits, dbitno += nbits) { + /* determine the bit number within the word and + * the number of bits within the word. + */ + wbitno = dbitno & (DBWORD - 1); + nbits = min(rbits, DBWORD - wbitno); + + /* check if only part of the word is to be updated. */ + if (nbits < DBWORD) { + /* update (free or allocate) the bits + * in this word. + */ + mask = + (ONES << (DBWORD - nbits) >> wbitno); + if (free) + dp->pmap[word] &= + cpu_to_le32(~mask); + else + dp->pmap[word] |= + cpu_to_le32(mask); + + word += 1; + } else { + /* one or more words are to have all + * their bits updated. determine how + * many words and how many bits. + */ + nwords = rbits >> L2DBWORD; + nbits = nwords << L2DBWORD; + + /* update (free or allocate) the bits + * in these words. + */ + if (free) + memset(&dp->pmap[word], 0, + nwords * 4); + else + memset(&dp->pmap[word], (int) ONES, + nwords * 4); + + word += nwords; + } + } + + /* + * update dmap lsn + */ + if (lblkno == lastlblkno) + continue; + + lastlblkno = lblkno; + + if (mp->lsn != 0) { + /* inherit older/smaller lsn */ + logdiff(diffp, mp->lsn, log); + if (difft < diffp) { + mp->lsn = lsn; + + /* move bp after tblock in logsync list */ + LOGSYNC_LOCK(log); + list_del(&mp->synclist); + list_add(&mp->synclist, &tblk->synclist); + LOGSYNC_UNLOCK(log); + } + + /* inherit younger/larger clsn */ + LOGSYNC_LOCK(log); + logdiff(difft, tblk->clsn, log); + logdiff(diffp, mp->clsn, log); + if (difft > diffp) + mp->clsn = tblk->clsn; + LOGSYNC_UNLOCK(log); + } else { + mp->log = log; + mp->lsn = lsn; + + /* insert bp after tblock in logsync list */ + LOGSYNC_LOCK(log); + + log->count++; + list_add(&mp->synclist, &tblk->synclist); + + mp->clsn = tblk->clsn; + LOGSYNC_UNLOCK(log); + } + } + + /* write the last buffer. */ + if (mp) { + write_metapage(mp); + } + + return (0); +} + + +/* + * NAME: dbNextAG() + * + * FUNCTION: find the preferred allocation group for new allocations. + * + * we try to keep the trailing (rightmost) allocation groups + * free for large allocations. we try to do this by targeting + * new inode allocations towards the leftmost or 'active' + * allocation groups while keeping the rightmost or 'inactive' + * allocation groups free. once the active allocation groups + * have dropped to a certain percentage of free space, we add + * the leftmost inactive allocation group to the active set. + * + * within the active allocation groups, we maintain a preferred + * allocation group which consists of a group with at least + * average free space over the active set. it is the preferred + * group that we target new inode allocation towards. the + * tie-in between inode allocation and block allocation occurs + * as we allocate the first (data) block of an inode and specify + * the inode (block) as the allocation hint for this block. + * + * PARAMETERS: + * ipbmap - pointer to in-core inode for the block map. + * + * RETURN VALUES: + * the preferred allocation group number. + * + * note: only called by dbAlloc(); + */ +int dbNextAG(struct inode *ipbmap) +{ + s64 avgfree, inactfree, actfree, rem; + int actags, inactags, l2agsize; + bmap_t *bmp = JFS_SBI(ipbmap->i_sb)->bmap; + + BMAP_LOCK(bmp); + + /* determine the number of active allocation groups (i.e. + * the number of allocation groups up to and including + * the rightmost allocation group with blocks allocated + * in it. + */ + actags = bmp->db_maxag + 1; + assert(actags <= bmp->db_numag); + + /* get the number of inactive allocation groups (i.e. the + * number of allocation group following the rightmost group + * with allocation in it. + */ + inactags = bmp->db_numag - actags; + + /* determine how many blocks are in the inactive allocation + * groups. in doing this, we must account for the fact that + * the rightmost group might be a partial group (i.e. file + * system size is not a multiple of the group size). + */ + l2agsize = bmp->db_agl2size; + rem = bmp->db_mapsize & (bmp->db_agsize - 1); + inactfree = (inactags + && rem) ? ((inactags - 1) << l2agsize) + + rem : inactags << l2agsize; + + /* now determine how many free blocks are in the active + * allocation groups plus the average number of free blocks + * within the active ags. + */ + actfree = bmp->db_nfree - inactfree; + avgfree = (u32) actfree / (u32) actags; + + /* check if not all of the allocation groups are active. + */ + if (actags < bmp->db_numag) { + /* not all of the allocation groups are active. determine + * if we should extend the active set by 1 (i.e. add the + * group following the current active set). we do so if + * the number of free blocks within the active set is less + * than the allocation group set and average free within + * the active set is less than 60%. we activate a new group + * by setting the allocation group preference to the new + * group. + */ + if (actfree < bmp->db_agsize && + ((avgfree * 100) >> l2agsize) < 60) + bmp->db_agpref = actags; + } else { + /* all allocation groups are in the active set. check if + * the preferred allocation group has average free space. + * if not, re-establish the preferred group as the leftmost + * group with average free space. + */ + if (bmp->db_agfree[bmp->db_agpref] < avgfree) { + for (bmp->db_agpref = 0; bmp->db_agpref < actags; + bmp->db_agpref++) { + if (bmp->db_agfree[bmp->db_agpref] <= + avgfree) + break; + } + assert(bmp->db_agpref < bmp->db_numag); + } + } + + BMAP_UNLOCK(bmp); + + /* return the preferred group. + */ + return (bmp->db_agpref); +} + + +/* + * NAME: dbAlloc() + * + * FUNCTION: attempt to allocate a specified number of contiguous free + * blocks from the working allocation block map. + * + * the block allocation policy uses hints and a multi-step + * approach. + * + * for allocation requests smaller than the number of blocks + * per dmap, we first try to allocate the new blocks + * immediately following the hint. if these blocks are not + * available, we try to allocate blocks near the hint. if + * no blocks near the hint are available, we next try to + * allocate within the same dmap as contains the hint. + * + * if no blocks are available in the dmap or the allocation + * request is larger than the dmap size, we try to allocate + * within the same allocation group as contains the hint. if + * this does not succeed, we finally try to allocate anywhere + * within the aggregate. + * + * we also try to allocate anywhere within the aggregate for + * for allocation requests larger than the allocation group + * size or requests that specify no hint value. + * + * PARAMETERS: + * ip - pointer to in-core inode; + * hint - allocation hint. + * nblocks - number of contiguous blocks in the range. + * results - on successful return, set to the starting block number + * of the newly allocated contiguous range. + * + * RETURN VALUES: + * 0 - success + * ENOSPC - insufficient disk resources + * EIO - i/o error + */ +int dbAlloc(struct inode *ip, s64 hint, s64 nblocks, s64 * results) +{ + int rc, agno; + struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap; + bmap_t *bmp; + metapage_t *mp; + s64 lblkno, blkno; + dmap_t *dp; + int l2nb; + s64 mapSize; + + /* assert that nblocks is valid */ + assert(nblocks > 0); + +#ifdef _STILL_TO_PORT + /* DASD limit check F226941 */ + if (OVER_LIMIT(ip, nblocks)) + return ENOSPC; +#endif /* _STILL_TO_PORT */ + + /* get the log2 number of blocks to be allocated. + * if the number of blocks is not a log2 multiple, + * it will be rounded up to the next log2 multiple. + */ + l2nb = BLKSTOL2(nblocks); + + bmp = JFS_SBI(ip->i_sb)->bmap; + +//retry: /* serialize w.r.t.extendfs() */ + mapSize = bmp->db_mapsize; + + /* the hint should be within the map */ + assert(hint < mapSize); + + /* if no hint was specified or the number of blocks to be + * allocated is greater than the allocation group size, try + * to allocate anywhere. + */ + if (hint == 0 || l2nb > bmp->db_agl2size) { + IWRITE_LOCK(ipbmap); + + rc = dbAllocAny(bmp, nblocks, l2nb, results); + if (rc == 0) { + DBALLOC(bmp->db_DBmap, bmp->db_mapsize, *results, + nblocks); + } + + IWRITE_UNLOCK(ipbmap); + return (rc); + } + + /* we would like to allocate close to the hint. adjust the + * hint to the block following the hint since the allocators + * will start looking for free space starting at this point. + * if the hint was the last block of the file system, try to + * allocate in the same allocation group as the hint. + */ + blkno = hint + 1; + if (blkno >= bmp->db_mapsize) { + blkno--; + goto tryag; + } + + /* check if blkno crosses over into a new allocation group. + * if so, check if we should allow allocations within this + * allocation group. we try to keep the trailing (rightmost) + * allocation groups of the file system free for large + * allocations and may want to prevent this allocation from + * spilling over into this space. + */ + if ((blkno & (bmp->db_agsize - 1)) == 0) { + /* check if the AG is beyond the rightmost AG with + * allocations in it. if so, call dbNextAG() to + * determine if the allocation should be allowed + * to proceed within this AG or should be targeted + * to another AG. + */ + agno = blkno >> bmp->db_agl2size; + if (agno > bmp->db_maxag) { + agno = dbNextAG(ipbmap); + blkno = (s64) agno << bmp->db_agl2size; + goto tryag; + } + } + + /* check if the allocation request size can be satisfied from a + * single dmap. if so, try to allocate from the dmap containing + * the hint using a tiered strategy. + */ + if (nblocks <= BPERDMAP) { + IREAD_LOCK(ipbmap); + + /* get the buffer for the dmap containing the hint. + */ + lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage); + mp = read_metapage(ipbmap, lblkno, PSIZE, 0); + if (mp == NULL) { + IREAD_UNLOCK(ipbmap); + return (EIO); + } + dp = (dmap_t *) mp->data; + + /* first, try to satisfy the allocation request with the + * blocks beginning at the hint. + */ + if ((rc = + dbAllocNext(bmp, dp, blkno, + (int) nblocks)) != ENOSPC) { + if (rc == 0) { + *results = blkno; + DBALLOC(bmp->db_DBmap, bmp->db_mapsize, + *results, nblocks); + write_metapage(mp); + } else { + assert(rc == EIO); + release_metapage(mp); + } + + IREAD_UNLOCK(ipbmap); + return (rc); + } + + /* next, try to satisfy the allocation request with blocks + * near the hint. + */ + if ((rc = + dbAllocNear(bmp, dp, blkno, (int) nblocks, l2nb, + results)) + != ENOSPC) { + if (rc == 0) { + DBALLOC(bmp->db_DBmap, bmp->db_mapsize, + *results, nblocks); + mark_metapage_dirty(mp); + } + release_metapage(mp); + + IREAD_UNLOCK(ipbmap); + return (rc); + } + + /* try to satisfy the allocation request with blocks within + * the same allocation group as the hint. + */ + if ((rc = + dbAllocDmapLev(bmp, dp, (int) nblocks, l2nb, results)) + != ENOSPC) { + if (rc == 0) { + DBALLOC(bmp->db_DBmap, bmp->db_mapsize, + *results, nblocks); + mark_metapage_dirty(mp); + } + release_metapage(mp); + + IREAD_UNLOCK(ipbmap); + return (rc); + } + + release_metapage(mp); + IREAD_UNLOCK(ipbmap); + } + + tryag: + IWRITE_LOCK(ipbmap); + + /* determine the allocation group number of the hint and try to + * allocate within this allocation group. if that fails, try to + * allocate anywhere in the map. + */ + agno = blkno >> bmp->db_agl2size; + if ((rc = dbAllocAG(bmp, agno, nblocks, l2nb, results)) == ENOSPC) + rc = dbAllocAny(bmp, nblocks, l2nb, results); + if (rc == 0) { + DBALLOC(bmp->db_DBmap, bmp->db_mapsize, *results, nblocks); + } + + IWRITE_UNLOCK(ipbmap); + + return (rc); +} + + +/* + * NAME: dbAllocExact() + * + * FUNCTION: try to allocate the requested extent; + * + * PARAMETERS: + * ip - pointer to in-core inode; + * blkno - extent address; + * nblocks - extent length; + * + * RETURN VALUES: + * 0 - success + * ENOSPC - insufficient disk resources + * EIO - i/o error + */ +int dbAllocExact(struct inode *ip, s64 blkno, int nblocks) +{ + int rc; + struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap; + bmap_t *bmp = JFS_SBI(ip->i_sb)->bmap; + dmap_t *dp; + s64 lblkno; + metapage_t *mp; + + IREAD_LOCK(ipbmap); + + /* + * validate extent request: + * + * note: defragfs policy: + * max 64 blocks will be moved. + * allocation request size must be satisfied from a single dmap. + */ + if (nblocks <= 0 || nblocks > BPERDMAP || blkno >= bmp->db_mapsize) { + IREAD_UNLOCK(ipbmap); + return EINVAL; + } + + if (nblocks > ((s64) 1 << bmp->db_maxfreebud)) { + /* the free space is no longer available */ + IREAD_UNLOCK(ipbmap); + return ENOSPC; + } + + /* read in the dmap covering the extent */ + lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage); + mp = read_metapage(ipbmap, lblkno, PSIZE, 0); + if (mp == NULL) { + IREAD_UNLOCK(ipbmap); + return (EIO); + } + dp = (dmap_t *) mp->data; + + /* try to allocate the requested extent */ + rc = dbAllocNext(bmp, dp, blkno, nblocks); + + IREAD_UNLOCK(ipbmap); + + if (rc == 0) { + DBALLOC(bmp->db_DBmap, bmp->db_mapsize, blkno, nblocks); + mark_metapage_dirty(mp); + } + release_metapage(mp); + + return (rc); +} + + +/* + * NAME: dbReAlloc() + * + * FUNCTION: attempt to extend a current allocation by a specified + * number of blocks. + * + * this routine attempts to satisfy the allocation request + * by first trying to extend the existing allocation in + * place by allocating the additional blocks as the blocks + * immediately following the current allocation. if these + * blocks are not available, this routine will attempt to + * allocate a new set of contiguous blocks large enough + * to cover the existing allocation plus the additional + * number of blocks required. + * + * PARAMETERS: + * ip - pointer to in-core inode requiring allocation. + * blkno - starting block of the current allocation. + * nblocks - number of contiguous blocks within the current + * allocation. + * addnblocks - number of blocks to add to the allocation. + * results - on successful return, set to the starting block number + * of the existing allocation if the existing allocation + * was extended in place or to a newly allocated contiguous + * range if the existing allocation could not be extended + * in place. + * + * RETURN VALUES: + * 0 - success + * ENOSPC - insufficient disk resources + * EIO - i/o error + */ +int +dbReAlloc(struct inode *ip, + s64 blkno, s64 nblocks, s64 addnblocks, s64 * results) +{ + int rc; + + /* try to extend the allocation in place. + */ + if ((rc = dbExtend(ip, blkno, nblocks, addnblocks)) == 0) { + *results = blkno; + return (0); + } else { + if (rc != ENOSPC) + return (rc); + } + + /* could not extend the allocation in place, so allocate a + * new set of blocks for the entire request (i.e. try to get + * a range of contiguous blocks large enough to cover the + * existing allocation plus the additional blocks.) + */ + return (dbAlloc + (ip, blkno + nblocks - 1, addnblocks + nblocks, results)); +} + + +/* + * NAME: dbExtend() + * + * FUNCTION: attempt to extend a current allocation by a specified + * number of blocks. + * + * this routine attempts to satisfy the allocation request + * by first trying to extend the existing allocation in + * place by allocating the additional blocks as the blocks + * immediately following the current allocation. + * + * PARAMETERS: + * ip - pointer to in-core inode requiring allocation. + * blkno - starting block of the current allocation. + * nblocks - number of contiguous blocks within the current + * allocation. + * addnblocks - number of blocks to add to the allocation. + * + * RETURN VALUES: + * 0 - success + * ENOSPC - insufficient disk resources + * EIO - i/o error + */ +int dbExtend(struct inode *ip, s64 blkno, s64 nblocks, s64 addnblocks) +{ + struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb); + s64 lblkno, lastblkno, extblkno; + uint rel_block; + metapage_t *mp; + dmap_t *dp; + int rc; + struct inode *ipbmap = sbi->ipbmap; + bmap_t *bmp; + + /* + * We don't want a non-aligned extent to cross a page boundary + */ + if (((rel_block = blkno & (sbi->nbperpage - 1))) && + (rel_block + nblocks + addnblocks > sbi->nbperpage)) + return (ENOSPC); + + /* get the last block of the current allocation */ + lastblkno = blkno + nblocks - 1; + + /* determine the block number of the block following + * the existing allocation. + */ + extblkno = lastblkno + 1; + + IREAD_LOCK(ipbmap); + + /* better be within the file system */ + bmp = sbi->bmap; + assert(lastblkno >= 0 && lastblkno < bmp->db_mapsize); + + /* we'll attempt to extend the current allocation in place by + * allocating the additional blocks as the blocks immediately + * following the current allocation. we only try to extend the + * current allocation in place if the number of additional blocks + * can fit into a dmap, the last block of the current allocation + * is not the last block of the file system, and the start of the + * inplace extension is not on an allocation group boundry. + */ + if (addnblocks > BPERDMAP || extblkno >= bmp->db_mapsize || + (extblkno & (bmp->db_agsize - 1)) == 0) { + IREAD_UNLOCK(ipbmap); + return (ENOSPC); + } + + /* get the buffer for the dmap containing the first block + * of the extension. + */ + lblkno = BLKTODMAP(extblkno, bmp->db_l2nbperpage); + mp = read_metapage(ipbmap, lblkno, PSIZE, 0); + if (mp == NULL) { + IREAD_UNLOCK(ipbmap); + return (EIO); + } + + DBALLOCCK(bmp->db_DBmap, bmp->db_mapsize, blkno, nblocks); + dp = (dmap_t *) mp->data; + + /* try to allocate the blocks immediately following the + * current allocation. + */ + rc = dbAllocNext(bmp, dp, extblkno, (int) addnblocks); + + IREAD_UNLOCK(ipbmap); + + /* were we successful ? */ + if (rc == 0) { + DBALLOC(bmp->db_DBmap, bmp->db_mapsize, extblkno, + addnblocks); + write_metapage(mp); + } else { + /* we were not successful */ + release_metapage(mp); + assert(rc == ENOSPC || rc == EIO); + } + + return (rc); +} + + +/* + * NAME: dbAllocNext() + * + * FUNCTION: attempt to allocate the blocks of the specified block + * range within a dmap. + * + * PARAMETERS: + * bmp - pointer to bmap descriptor + * dp - pointer to dmap. + * blkno - starting block number of the range. + * nblocks - number of contiguous free blocks of the range. + * + * RETURN VALUES: + * 0 - success + * ENOSPC - insufficient disk resources + * EIO - i/o error + * + * serialization: IREAD_LOCK(ipbmap) held on entry/exit; + */ +static int dbAllocNext(bmap_t * bmp, dmap_t * dp, s64 blkno, int nblocks) +{ + int dbitno, word, rembits, nb, nwords, wbitno, nw; + int l2size; + s8 *leaf; + u32 mask; + + /* pick up a pointer to the leaves of the dmap tree. + */ + leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx); + + /* determine the bit number and word within the dmap of the + * starting block. + */ + dbitno = blkno & (BPERDMAP - 1); + word = dbitno >> L2DBWORD; + + /* check if the specified block range is contained within + * this dmap. + */ + if (dbitno + nblocks > BPERDMAP) + return (ENOSPC); + + /* check if the starting leaf indicates that anything + * is free. + */ + if (leaf[word] == NOFREE) + return (ENOSPC); + + /* check the dmaps words corresponding to block range to see + * if the block range is free. not all bits of the first and + * last words may be contained within the block range. if this + * is the case, we'll work against those words (i.e. partial first + * and/or last) on an individual basis (a single pass) and examine + * the actual bits to determine if they are free. a single pass + * will be used for all dmap words fully contained within the + * specified range. within this pass, the leaves of the dmap + * tree will be examined to determine if the blocks are free. a + * single leaf may describe the free space of multiple dmap + * words, so we may visit only a subset of the actual leaves + * corresponding to the dmap words of the block range. + */ + for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) { + /* determine the bit number within the word and + * the number of bits within the word. + */ + wbitno = dbitno & (DBWORD - 1); + nb = min(rembits, DBWORD - wbitno); + + /* check if only part of the word is to be examined. + */ + if (nb < DBWORD) { + /* check if the bits are free. + */ + mask = (ONES << (DBWORD - nb) >> wbitno); + if ((mask & ~le32_to_cpu(dp->wmap[word])) != mask) + return (ENOSPC); + + word += 1; + } else { + /* one or more dmap words are fully contained + * within the block range. determine how many + * words and how many bits. + */ + nwords = rembits >> L2DBWORD; + nb = nwords << L2DBWORD; + + /* now examine the appropriate leaves to determine + * if the blocks are free. + */ + while (nwords > 0) { + /* does the leaf describe any free space ? + */ + if (leaf[word] < BUDMIN) + return (ENOSPC); + + /* determine the l2 number of bits provided + * by this leaf. + */ + l2size = + min((int)leaf[word], NLSTOL2BSZ(nwords)); + + /* determine how many words were handled. + */ + nw = BUDSIZE(l2size, BUDMIN); + + nwords -= nw; + word += nw; + } + } + } + + /* allocate the blocks. + */ + return (dbAllocDmap(bmp, dp, blkno, nblocks)); +} + + +/* + * NAME: dbAllocNear() + * + * FUNCTION: attempt to allocate a number of contiguous free blocks near + * a specified block (hint) within a dmap. + * + * starting with the dmap leaf that covers the hint, we'll + * check the next four contiguous leaves for sufficient free + * space. if sufficient free space is found, we'll allocate + * the desired free space. + * + * PARAMETERS: + * bmp - pointer to bmap descriptor + * dp - pointer to dmap. + * blkno - block number to allocate near. + * nblocks - actual number of contiguous free blocks desired. + * l2nb - log2 number of contiguous free blocks desired. + * results - on successful return, set to the starting block number + * of the newly allocated range. + * + * RETURN VALUES: + * 0 - success + * ENOSPC - insufficient disk resources + * EIO - i/o error + * + * serialization: IREAD_LOCK(ipbmap) held on entry/exit; + */ +static int +dbAllocNear(bmap_t * bmp, + dmap_t * dp, s64 blkno, int nblocks, int l2nb, s64 * results) +{ + int word, lword, rc; + s8 *leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx); + + /* determine the word within the dmap that holds the hint + * (i.e. blkno). also, determine the last word in the dmap + * that we'll include in our examination. + */ + word = (blkno & (BPERDMAP - 1)) >> L2DBWORD; + lword = min(word + 4, LPERDMAP); + + /* examine the leaves for sufficient free space. + */ + for (; word < lword; word++) { + /* does the leaf describe sufficient free space ? + */ + if (leaf[word] < l2nb) + continue; + + /* determine the block number within the file system + * of the first block described by this dmap word. + */ + blkno = le64_to_cpu(dp->start) + (word << L2DBWORD); + + /* if not all bits of the dmap word are free, get the + * starting bit number within the dmap word of the required + * string of free bits and adjust the block number with the + * value. + */ + if (leaf[word] < BUDMIN) + blkno += + dbFindBits(le32_to_cpu(dp->wmap[word]), l2nb); + + /* allocate the blocks. + */ + if ((rc = dbAllocDmap(bmp, dp, blkno, nblocks)) == 0) + *results = blkno; + + return (rc); + } + + return (ENOSPC); +} + + +/* + * NAME: dbAllocAG() + * + * FUNCTION: attempt to allocate the specified number of contiguous + * free blocks within the specified allocation group. + * + * unless the allocation group size is equal to the number + * of blocks per dmap, the dmap control pages will be used to + * find the required free space, if available. we start the + * search at the highest dmap control page level which + * distinctly describes the allocation group's free space + * (i.e. the highest level at which the allocation group's + * free space is not mixed in with that of any other group). + * in addition, we start the search within this level at a + * height of the dmapctl dmtree at which the nodes distinctly + * describe the allocation group's free space. at this height, + * the allocation group's free space may be represented by 1 + * or two sub-trees, depending on the allocation group size. + * we search the top nodes of these subtrees left to right for + * sufficient free space. if sufficient free space is found, + * the subtree is searched to find the leftmost leaf that + * has free space. once we have made it to the leaf, we + * move the search to the next lower level dmap control page + * corresponding to this leaf. we continue down the dmap control + * pages until we find the dmap that contains or starts the + * sufficient free space and we allocate at this dmap. + * + * if the allocation group size is equal to the dmap size, + * we'll start at the dmap corresponding to the allocation + * group and attempt the allocation at this level. + * + * the dmap control page search is also not performed if the + * allocation group is completely free and we go to the first + * dmap of the allocation group to do the allocation. this is + * done because the allocation group may be part (not the first + * part) of a larger binary buddy system, causing the dmap + * control pages to indicate no free space (NOFREE) within + * the allocation group. + * + * PARAMETERS: + * bmp - pointer to bmap descriptor + * agno - allocation group number. + * nblocks - actual number of contiguous free blocks desired. + * l2nb - log2 number of contiguous free blocks desired. + * results - on successful return, set to the starting block number + * of the newly allocated range. + * + * RETURN VALUES: + * 0 - success + * ENOSPC - insufficient disk resources + * EIO - i/o error + * + * note: IWRITE_LOCK(ipmap) held on entry/exit; + */ +static int +dbAllocAG(bmap_t * bmp, int agno, s64 nblocks, int l2nb, s64 * results) +{ + metapage_t *mp; + dmapctl_t *dcp; + int rc, ti, i, k, m, n, agperlev; + s64 blkno, lblkno; + int budmin; + + /* allocation request should not be for more than the + * allocation group size. + */ + assert(l2nb <= bmp->db_agl2size); + + /* determine the starting block number of the allocation + * group. + */ + blkno = (s64) agno << bmp->db_agl2size; + + /* check if the allocation group size is the minimum allocation + * group size or if the allocation group is completely free. if + * the allocation group size is the minimum size of BPERDMAP (i.e. + * 1 dmap), there is no need to search the dmap control page (below) + * that fully describes the allocation group since the allocation + * group is already fully described by a dmap. in this case, we + * just call dbAllocCtl() to search the dmap tree and allocate the + * required space if available. + * + * if the allocation group is completely free, dbAllocCtl() is + * also called to allocate the required space. this is done for + * two reasons. first, it makes no sense searching the dmap control + * pages for free space when we know that free space exists. second, + * the dmap control pages may indicate that the allocation group + * has no free space if the allocation group is part (not the first + * part) of a larger binary buddy system. + */ + if (bmp->db_agsize == BPERDMAP + || bmp->db_agfree[agno] == bmp->db_agsize) { + rc = dbAllocCtl(bmp, nblocks, l2nb, blkno, results); + /* assert(!(rc == ENOSPC && bmp->db_agfree[agno] == bmp->db_agsize)); */ + if ((rc == ENOSPC) && + (bmp->db_agfree[agno] == bmp->db_agsize)) { + jERROR(1, + ("dbAllocAG: removed assert, but still need to debug here\nblkno = 0x%Lx, nblocks = 0x%Lx\n", + (unsigned long long) blkno, + (unsigned long long) nblocks)); + } + return (rc); + } + + /* the buffer for the dmap control page that fully describes the + * allocation group. + */ + lblkno = BLKTOCTL(blkno, bmp->db_l2nbperpage, bmp->db_aglevel); + mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0); + if (mp == NULL) + return (EIO); + dcp = (dmapctl_t *) mp->data; + budmin = dcp->budmin; + + /* search the subtree(s) of the dmap control page that describes + * the allocation group, looking for sufficient free space. to begin, + * determine how many allocation groups are represented in a dmap + * control page at the control page level (i.e. L0, L1, L2) that + * fully describes an allocation group. next, determine the starting + * tree index of this allocation group within the control page. + */ + agperlev = + (1 << (L2LPERCTL - (bmp->db_agheigth << 1))) / bmp->db_agwidth; + ti = bmp->db_agstart + bmp->db_agwidth * (agno & (agperlev - 1)); + + /* dmap control page trees fan-out by 4 and a single allocation + * group may be described by 1 or 2 subtrees within the ag level + * dmap control page, depending upon the ag size. examine the ag's + * subtrees for sufficient free space, starting with the leftmost + * subtree. + */ + for (i = 0; i < bmp->db_agwidth; i++, ti++) { + /* is there sufficient free space ? + */ + if (l2nb > dcp->stree[ti]) + continue; + + /* sufficient free space found in a subtree. now search down + * the subtree to find the leftmost leaf that describes this + * free space. + */ + for (k = bmp->db_agheigth; k > 0; k--) { + for (n = 0, m = (ti << 2) + 1; n < 4; n++) { + if (l2nb <= dcp->stree[m + n]) { + ti = m + n; + break; + } + } + assert(n < 4); + } + + /* determine the block number within the file system + * that corresponds to this leaf. + */ + if (bmp->db_aglevel == 2) + blkno = 0; + else if (bmp->db_aglevel == 1) + blkno &= ~(MAXL1SIZE - 1); + else /* bmp->db_aglevel == 0 */ + blkno &= ~(MAXL0SIZE - 1); + + blkno += + ((s64) (ti - le32_to_cpu(dcp->leafidx))) << budmin; + + /* release the buffer in preparation for going down + * the next level of dmap control pages. + */ + release_metapage(mp); + + /* check if we need to continue to search down the lower + * level dmap control pages. we need to if the number of + * blocks required is less than maximum number of blocks + * described at the next lower level. + */ + if (l2nb < budmin) { + + /* search the lower level dmap control pages to get + * the starting block number of the the dmap that + * contains or starts off the free space. + */ + if ((rc = + dbFindCtl(bmp, l2nb, bmp->db_aglevel - 1, + &blkno))) { + assert(rc != ENOSPC); + return (rc); + } + } + + /* allocate the blocks. + */ + rc = dbAllocCtl(bmp, nblocks, l2nb, blkno, results); + assert(rc != ENOSPC); + return (rc); + } + + /* no space in the allocation group. release the buffer and + * return ENOSPC. + */ + release_metapage(mp); + + return (ENOSPC); +} + + +/* + * NAME: dbAllocAny() + * + * FUNCTION: attempt to allocate the specified number of contiguous + * free blocks anywhere in the file system. + * + * dbAllocAny() attempts to find the sufficient free space by + * searching down the dmap control pages, starting with the + * highest level (i.e. L0, L1, L2) control page. if free space + * large enough to satisfy the desired free space is found, the + * desired free space is allocated. + * + * PARAMETERS: + * bmp - pointer to bmap descriptor + * nblocks - actual number of contiguous free blocks desired. + * l2nb - log2 number of contiguous free blocks desired. + * results - on successful return, set to the starting block number + * of the newly allocated range. + * + * RETURN VALUES: + * 0 - success + * ENOSPC - insufficient disk resources + * EIO - i/o error + * + * serialization: IWRITE_LOCK(ipbmap) held on entry/exit; + */ +static int dbAllocAny(bmap_t * bmp, s64 nblocks, int l2nb, s64 * results) +{ + int rc; + s64 blkno = 0; + + /* starting with the top level dmap control page, search + * down the dmap control levels for sufficient free space. + * if free space is found, dbFindCtl() returns the starting + * block number of the dmap that contains or starts off the + * range of free space. + */ + if ((rc = dbFindCtl(bmp, l2nb, bmp->db_maxlevel, &blkno))) + return (rc); + + /* allocate the blocks. + */ + rc = dbAllocCtl(bmp, nblocks, l2nb, blkno, results); + assert(rc != ENOSPC); + return (rc); +} + + +/* + * NAME: dbFindCtl() + * + * FUNCTION: starting at a specified dmap control page level and block + * number, search down the dmap control levels for a range of + * contiguous free blocks large enough to satisfy an allocation + * request for the specified number of free blocks. + * + * if sufficient contiguous free blocks are found, this routine + * returns the starting block number within a dmap page that + * contains or starts a range of contiqious free blocks that + * is sufficient in size. + * + * PARAMETERS: + * bmp - pointer to bmap descriptor + * level - starting dmap control page level. + * l2nb - log2 number of contiguous free blocks desired. + * *blkno - on entry, starting block number for conducting the search. + * on successful return, the first block within a dmap page + * that contains or starts a range of contiguous free blocks. + * + * RETURN VALUES: + * 0 - success + * ENOSPC - insufficient disk resources + * EIO - i/o error + * + * serialization: IWRITE_LOCK(ipbmap) held on entry/exit; + */ +static int dbFindCtl(bmap_t * bmp, int l2nb, int level, s64 * blkno) +{ + int rc, leafidx, lev; + s64 b, lblkno; + dmapctl_t *dcp; + int budmin; + metapage_t *mp; + + /* starting at the specified dmap control page level and block + * number, search down the dmap control levels for the starting + * block number of a dmap page that contains or starts off + * sufficient free blocks. + */ + for (lev = level, b = *blkno; lev >= 0; lev--) { + /* get the buffer of the dmap control page for the block + * number and level (i.e. L0, L1, L2). + */ + lblkno = BLKTOCTL(b, bmp->db_l2nbperpage, lev); + mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0); + if (mp == NULL) + return (EIO); + dcp = (dmapctl_t *) mp->data; + budmin = dcp->budmin; + + /* search the tree within the dmap control page for + * sufficent free space. if sufficient free space is found, + * dbFindLeaf() returns the index of the leaf at which + * free space was found. + */ + rc = dbFindLeaf((dmtree_t *) dcp, l2nb, &leafidx); + + /* release the buffer. + */ + release_metapage(mp); + + /* space found ? + */ + if (rc) { + assert(lev == level); + return (ENOSPC); + } + + /* adjust the block number to reflect the location within + * the dmap control page (i.e. the leaf) at which free + * space was found. + */ + b += (((s64) leafidx) << budmin); + + /* we stop the search at this dmap control page level if + * the number of blocks required is greater than or equal + * to the maximum number of blocks described at the next + * (lower) level. + */ + if (l2nb >= budmin) + break; + } + + *blkno = b; + return (0); +} + + +/* + * NAME: dbAllocCtl() + * + * FUNCTION: attempt to allocate a specified number of contiguous + * blocks starting within a specific dmap. + * + * this routine is called by higher level routines that search + * the dmap control pages above the actual dmaps for contiguous + * free space. the result of successful searches by these + * routines are the starting block numbers within dmaps, with + * the dmaps themselves containing the desired contiguous free + * space or starting a contiguous free space of desired size + * that is made up of the blocks of one or more dmaps. these + * calls should not fail due to insufficent resources. + * + * this routine is called in some cases where it is not known + * whether it will fail due to insufficient resources. more + * specifically, this occurs when allocating from an allocation + * group whose size is equal to the number of blocks per dmap. + * in this case, the dmap control pages are not examined prior + * to calling this routine (to save pathlength) and the call + * might fail. + * + * for a request size that fits within a dmap, this routine relies + * upon the dmap's dmtree to find the requested contiguous free + * space. for request sizes that are larger than a dmap, the + * requested free space will start at the first block of the + * first dmap (i.e. blkno). + * + * PARAMETERS: + * bmp - pointer to bmap descriptor + * nblocks - actual number of contiguous free blocks to allocate. + * l2nb - log2 number of contiguous free blocks to allocate. + * blkno - starting block number of the dmap to start the allocation + * from. + * results - on successful return, set to the starting block number + * of the newly allocated range. + * + * RETURN VALUES: + * 0 - success + * ENOSPC - insufficient disk resources + * EIO - i/o error + * + * serialization: IWRITE_LOCK(ipbmap) held on entry/exit; + */ +static int +dbAllocCtl(bmap_t * bmp, s64 nblocks, int l2nb, s64 blkno, s64 * results) +{ + int rc, nb; + s64 b, lblkno, n; + metapage_t *mp; + dmap_t *dp; + + /* check if the allocation request is confined to a single dmap. + */ + if (l2nb <= L2BPERDMAP) { + /* get the buffer for the dmap. + */ + lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage); + mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0); + if (mp == NULL) + return (EIO); + dp = (dmap_t *) mp->data; + + /* try to allocate the blocks. + */ + rc = dbAllocDmapLev(bmp, dp, (int) nblocks, l2nb, results); + if (rc == 0) + mark_metapage_dirty(mp); + + release_metapage(mp); + + return (rc); + } + + /* allocation request involving multiple dmaps. it must start on + * a dmap boundary. + */ + assert((blkno & (BPERDMAP - 1)) == 0); + + /* allocate the blocks dmap by dmap. + */ + for (n = nblocks, b = blkno; n > 0; n -= nb, b += nb) { + /* get the buffer for the dmap. + */ + lblkno = BLKTODMAP(b, bmp->db_l2nbperpage); + mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0); + if (mp == NULL) { + rc = EIO; + goto backout; + } + dp = (dmap_t *) mp->data; + + /* the dmap better be all free. + */ + assert(dp->tree.stree[ROOT] == L2BPERDMAP); + + /* determine how many blocks to allocate from this dmap. + */ + nb = min(n, (s64)BPERDMAP); + + /* allocate the blocks from the dmap. + */ + if ((rc = dbAllocDmap(bmp, dp, b, nb))) { + release_metapage(mp); + goto backout; + } + + /* write the buffer. + */ + write_metapage(mp); + } + + /* set the results (starting block number) and return. + */ + *results = blkno; + return (0); + + /* something failed in handling an allocation request involving + * multiple dmaps. we'll try to clean up by backing out any + * allocation that has already happened for this request. if + * we fail in backing out the allocation, we'll mark the file + * system to indicate that blocks have been leaked. + */ + backout: + + /* try to backout the allocations dmap by dmap. + */ + for (n = nblocks - n, b = blkno; n > 0; + n -= BPERDMAP, b += BPERDMAP) { + /* get the buffer for this dmap. + */ + lblkno = BLKTODMAP(b, bmp->db_l2nbperpage); + mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0); + if (mp == NULL) { + /* could not back out. mark the file system + * to indicate that we have leaked blocks. + */ + fsDirty(); /* !!! */ + jERROR(1, + ("dbAllocCtl: I/O Error: Block Leakage.\n")); + continue; + } + dp = (dmap_t *) mp->data; + + /* free the blocks is this dmap. + */ + if (dbFreeDmap(bmp, dp, b, BPERDMAP)) { + /* could not back out. mark the file system + * to indicate that we have leaked blocks. + */ + release_metapage(mp); + fsDirty(); /* !!! */ + jERROR(1, ("dbAllocCtl: Block Leakage.\n")); + continue; + } + + /* write the buffer. + */ + write_metapage(mp); + } + + return (rc); +} + + +/* + * NAME: dbAllocDmapLev() + * + * FUNCTION: attempt to allocate a specified number of contiguous blocks + * from a specified dmap. + * + * this routine checks if the contiguous blocks are available. + * if so, nblocks of blocks are allocated; otherwise, ENOSPC is + * returned. + * + * PARAMETERS: + * mp - pointer to bmap descriptor + * dp - pointer to dmap to attempt to allocate blocks from. + * l2nb - log2 number of contiguous block desired. + * nblocks - actual number of contiguous block desired. + * results - on successful return, set to the starting block number + * of the newly allocated range. + * + * RETURN VALUES: + * 0 - success + * ENOSPC - insufficient disk resources + * EIO - i/o error + * + * serialization: IREAD_LOCK(ipbmap), e.g., from dbAlloc(), or + * IWRITE_LOCK(ipbmap), e.g., dbAllocCtl(), held on entry/exit; + */ +static int +dbAllocDmapLev(bmap_t * bmp, + dmap_t * dp, int nblocks, int l2nb, s64 * results) +{ + s64 blkno; + int leafidx, rc; + + /* can't be more than a dmaps worth of blocks */ + assert(l2nb <= L2BPERDMAP); + + /* search the tree within the dmap page for sufficient + * free space. if sufficient free space is found, dbFindLeaf() + * returns the index of the leaf at which free space was found. + */ + if (dbFindLeaf((dmtree_t *) & dp->tree, l2nb, &leafidx)) + return (ENOSPC); + + /* determine the block number within the file system corresponding + * to the leaf at which free space was found. + */ + blkno = le64_to_cpu(dp->start) + (leafidx << L2DBWORD); + + /* if not all bits of the dmap word are free, get the starting + * bit number within the dmap word of the required string of free + * bits and adjust the block number with this value. + */ + if (dp->tree.stree[leafidx + LEAFIND] < BUDMIN) + blkno += dbFindBits(le32_to_cpu(dp->wmap[leafidx]), l2nb); + + /* allocate the blocks */ + if ((rc = dbAllocDmap(bmp, dp, blkno, nblocks)) == 0) + *results = blkno; + + return (rc); +} + + +/* + * NAME: dbAllocDmap() + * + * FUNCTION: adjust the disk allocation map to reflect the allocation + * of a specified block range within a dmap. + * + * this routine allocates the specified blocks from the dmap + * through a call to dbAllocBits(). if the allocation of the + * block range causes the maximum string of free blocks within + * the dmap to change (i.e. the value of the root of the dmap's + * dmtree), this routine will cause this change to be reflected + * up through the appropriate levels of the dmap control pages + * by a call to dbAdjCtl() for the L0 dmap control page that + * covers this dmap. + * + * PARAMETERS: + * bmp - pointer to bmap descriptor + * dp - pointer to dmap to allocate the block range from. + * blkno - starting block number of the block to be allocated. + * nblocks - number of blocks to be allocated. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error + * + * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit; + */ +static int dbAllocDmap(bmap_t * bmp, dmap_t * dp, s64 blkno, int nblocks) +{ + s8 oldroot; + int rc; + + /* save the current value of the root (i.e. maximum free string) + * of the dmap tree. + */ + oldroot = dp->tree.stree[ROOT]; + + /* allocate the specified (blocks) bits */ + dbAllocBits(bmp, dp, blkno, nblocks); + + /* if the root has not changed, done. */ + if (dp->tree.stree[ROOT] == oldroot) + return (0); + + /* root changed. bubble the change up to the dmap control pages. + * if the adjustment of the upper level control pages fails, + * backout the bit allocation (thus making everything consistent). + */ + if ((rc = dbAdjCtl(bmp, blkno, dp->tree.stree[ROOT], 1, 0))) + dbFreeBits(bmp, dp, blkno, nblocks); + + return (rc); +} + + +/* + * NAME: dbFreeDmap() + * + * FUNCTION: adjust the disk allocation map to reflect the allocation + * of a specified block range within a dmap. + * + * this routine frees the specified blocks from the dmap through + * a call to dbFreeBits(). if the deallocation of the block range + * causes the maximum string of free blocks within the dmap to + * change (i.e. the value of the root of the dmap's dmtree), this + * routine will cause this change to be reflected up through the + * appropriate levels of the dmap control pages by a call to + * dbAdjCtl() for the L0 dmap control page that covers this dmap. + * + * PARAMETERS: + * bmp - pointer to bmap descriptor + * dp - pointer to dmap to free the block range from. + * blkno - starting block number of the block to be freed. + * nblocks - number of blocks to be freed. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error + * + * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit; + */ +static int dbFreeDmap(bmap_t * bmp, dmap_t * dp, s64 blkno, int nblocks) +{ + s8 oldroot; + int rc, word; + + /* save the current value of the root (i.e. maximum free string) + * of the dmap tree. + */ + oldroot = dp->tree.stree[ROOT]; + + /* free the specified (blocks) bits */ + dbFreeBits(bmp, dp, blkno, nblocks); + + /* if the root has not changed, done. */ + if (dp->tree.stree[ROOT] == oldroot) + return (0); + + /* root changed. bubble the change up to the dmap control pages. + * if the adjustment of the upper level control pages fails, + * backout the deallocation. + */ + if ((rc = dbAdjCtl(bmp, blkno, dp->tree.stree[ROOT], 0, 0))) { + word = (blkno & (BPERDMAP - 1)) >> L2DBWORD; + + /* as part of backing out the deallocation, we will have + * to back split the dmap tree if the deallocation caused + * the freed blocks to become part of a larger binary buddy + * system. + */ + if (dp->tree.stree[word] == NOFREE) + dbBackSplit((dmtree_t *) & dp->tree, word); + + dbAllocBits(bmp, dp, blkno, nblocks); + } + + return (rc); +} + + +/* + * NAME: dbAllocBits() + * + * FUNCTION: allocate a specified block range from a dmap. + * + * this routine updates the dmap to reflect the working + * state allocation of the specified block range. it directly + * updates the bits of the working map and causes the adjustment + * of the binary buddy system described by the dmap's dmtree + * leaves to reflect the bits allocated. it also causes the + * dmap's dmtree, as a whole, to reflect the allocated range. + * + * PARAMETERS: + * bmp - pointer to bmap descriptor + * dp - pointer to dmap to allocate bits from. + * blkno - starting block number of the bits to be allocated. + * nblocks - number of bits to be allocated. + * + * RETURN VALUES: none + * + * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit; + */ +static void dbAllocBits(bmap_t * bmp, dmap_t * dp, s64 blkno, int nblocks) +{ + int dbitno, word, rembits, nb, nwords, wbitno, nw, agno; + dmtree_t *tp = (dmtree_t *) & dp->tree; + int size; + s8 *leaf; + + /* pick up a pointer to the leaves of the dmap tree */ + leaf = dp->tree.stree + LEAFIND; + + /* determine the bit number and word within the dmap of the + * starting block. + */ + dbitno = blkno & (BPERDMAP - 1); + word = dbitno >> L2DBWORD; + + /* block range better be within the dmap */ + assert(dbitno + nblocks <= BPERDMAP); + + /* allocate the bits of the dmap's words corresponding to the block + * range. not all bits of the first and last words may be contained + * within the block range. if this is the case, we'll work against + * those words (i.e. partial first and/or last) on an individual basis + * (a single pass), allocating the bits of interest by hand and + * updating the leaf corresponding to the dmap word. a single pass + * will be used for all dmap words fully contained within the + * specified range. within this pass, the bits of all fully contained + * dmap words will be marked as free in a single shot and the leaves + * will be updated. a single leaf may describe the free space of + * multiple dmap words, so we may update only a subset of the actual + * leaves corresponding to the dmap words of the block range. + */ + for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) { + /* determine the bit number within the word and + * the number of bits within the word. + */ + wbitno = dbitno & (DBWORD - 1); + nb = min(rembits, DBWORD - wbitno); + + /* check if only part of a word is to be allocated. + */ + if (nb < DBWORD) { + /* allocate (set to 1) the appropriate bits within + * this dmap word. + */ + dp->wmap[word] |= cpu_to_le32(ONES << (DBWORD - nb) + >> wbitno); + + /* update the leaf for this dmap word. in addition + * to setting the leaf value to the binary buddy max + * of the updated dmap word, dbSplit() will split + * the binary system of the leaves if need be. + */ + dbSplit(tp, word, BUDMIN, + dbMaxBud((u8 *) & dp->wmap[word])); + + word += 1; + } else { + /* one or more dmap words are fully contained + * within the block range. determine how many + * words and allocate (set to 1) the bits of these + * words. + */ + nwords = rembits >> L2DBWORD; + memset(&dp->wmap[word], (int) ONES, nwords * 4); + + /* determine how many bits. + */ + nb = nwords << L2DBWORD; + + /* now update the appropriate leaves to reflect + * the allocated words. + */ + for (; nwords > 0; nwords -= nw) { + assert(leaf[word] >= BUDMIN); + + /* determine what the leaf value should be + * updated to as the minimum of the l2 number + * of bits being allocated and the l2 number + * of bits currently described by this leaf. + */ + size = min((int)leaf[word], NLSTOL2BSZ(nwords)); + + /* update the leaf to reflect the allocation. + * in addition to setting the leaf value to + * NOFREE, dbSplit() will split the binary + * system of the leaves to reflect the current + * allocation (size). + */ + dbSplit(tp, word, size, NOFREE); + + /* get the number of dmap words handled */ + nw = BUDSIZE(size, BUDMIN); + word += nw; + } + } + } + + /* update the free count for this dmap */ + dp->nfree = cpu_to_le32(le32_to_cpu(dp->nfree) - nblocks); + + BMAP_LOCK(bmp); + + /* if this allocation group is completely free, + * update the maximum allocation group number if this allocation + * group is the new max. + */ + agno = blkno >> bmp->db_agl2size; + if (agno > bmp->db_maxag) + bmp->db_maxag = agno; + + /* update the free count for the allocation group and map */ + bmp->db_agfree[agno] -= nblocks; + bmp->db_nfree -= nblocks; + + BMAP_UNLOCK(bmp); +} + + +/* + * NAME: dbFreeBits() + * + * FUNCTION: free a specified block range from a dmap. + * + * this routine updates the dmap to reflect the working + * state allocation of the specified block range. it directly + * updates the bits of the working map and causes the adjustment + * of the binary buddy system described by the dmap's dmtree + * leaves to reflect the bits freed. it also causes the dmap's + * dmtree, as a whole, to reflect the deallocated range. + * + * PARAMETERS: + * bmp - pointer to bmap descriptor + * dp - pointer to dmap to free bits from. + * blkno - starting block number of the bits to be freed. + * nblocks - number of bits to be freed. + * + * RETURN VALUES: none + * + * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit; + */ +static void dbFreeBits(bmap_t * bmp, dmap_t * dp, s64 blkno, int nblocks) +{ + int dbitno, word, rembits, nb, nwords, wbitno, nw, agno; + dmtree_t *tp = (dmtree_t *) & dp->tree; + int size; + + /* determine the bit number and word within the dmap of the + * starting block. + */ + dbitno = blkno & (BPERDMAP - 1); + word = dbitno >> L2DBWORD; + + /* block range better be within the dmap. + */ + assert(dbitno + nblocks <= BPERDMAP); + + /* free the bits of the dmaps words corresponding to the block range. + * not all bits of the first and last words may be contained within + * the block range. if this is the case, we'll work against those + * words (i.e. partial first and/or last) on an individual basis + * (a single pass), freeing the bits of interest by hand and updating + * the leaf corresponding to the dmap word. a single pass will be used + * for all dmap words fully contained within the specified range. + * within this pass, the bits of all fully contained dmap words will + * be marked as free in a single shot and the leaves will be updated. a + * single leaf may describe the free space of multiple dmap words, + * so we may update only a subset of the actual leaves corresponding + * to the dmap words of the block range. + * + * dbJoin() is used to update leaf values and will join the binary + * buddy system of the leaves if the new leaf values indicate this + * should be done. + */ + for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) { + /* determine the bit number within the word and + * the number of bits within the word. + */ + wbitno = dbitno & (DBWORD - 1); + nb = min(rembits, DBWORD - wbitno); + + /* check if only part of a word is to be freed. + */ + if (nb < DBWORD) { + /* free (zero) the appropriate bits within this + * dmap word. + */ + dp->wmap[word] &= + cpu_to_le32(~(ONES << (DBWORD - nb) + >> wbitno)); + + /* update the leaf for this dmap word. + */ + dbJoin(tp, word, + dbMaxBud((u8 *) & dp->wmap[word])); + + word += 1; + } else { + /* one or more dmap words are fully contained + * within the block range. determine how many + * words and free (zero) the bits of these words. + */ + nwords = rembits >> L2DBWORD; + memset(&dp->wmap[word], 0, nwords * 4); + + /* determine how many bits. + */ + nb = nwords << L2DBWORD; + + /* now update the appropriate leaves to reflect + * the freed words. + */ + for (; nwords > 0; nwords -= nw) { + /* determine what the leaf value should be + * updated to as the minimum of the l2 number + * of bits being freed and the l2 (max) number + * of bits that can be described by this leaf. + */ + size = + min(LITOL2BSZ + (word, L2LPERDMAP, BUDMIN), + NLSTOL2BSZ(nwords)); + + /* update the leaf. + */ + dbJoin(tp, word, size); + + /* get the number of dmap words handled. + */ + nw = BUDSIZE(size, BUDMIN); + word += nw; + } + } + } + + /* update the free count for this dmap. + */ + dp->nfree = cpu_to_le32(le32_to_cpu(dp->nfree) + nblocks); + + BMAP_LOCK(bmp); + + /* update the free count for the allocation group and + * map. + */ + agno = blkno >> bmp->db_agl2size; + bmp->db_nfree += nblocks; + bmp->db_agfree[agno] += nblocks; + + /* check if this allocation group is not completely free and + * if it is currently the maximum (rightmost) allocation group. + * if so, establish the new maximum allocation group number by + * searching left for the first allocation group with allocation. + */ + if ((bmp->db_agfree[agno] == bmp->db_agsize + && agno == bmp->db_maxag) || (agno == bmp->db_numag - 1 + && bmp->db_agfree[agno] == + (bmp-> db_mapsize & + (BPERDMAP - 1)))) { + while (bmp->db_maxag > 0) { + bmp->db_maxag -= 1; + if (bmp->db_agfree[bmp->db_maxag] != + bmp->db_agsize) + break; + } + + /* re-establish the allocation group preference if the + * current preference is right of the maximum allocation + * group. + */ + if (bmp->db_agpref > bmp->db_maxag) + bmp->db_agpref = bmp->db_maxag; + } + + BMAP_UNLOCK(bmp); +} + + +/* + * NAME: dbAdjCtl() + * + * FUNCTION: adjust a dmap control page at a specified level to reflect + * the change in a lower level dmap or dmap control page's + * maximum string of free blocks (i.e. a change in the root + * of the lower level object's dmtree) due to the allocation + * or deallocation of a range of blocks with a single dmap. + * + * on entry, this routine is provided with the new value of + * the lower level dmap or dmap control page root and the + * starting block number of the block range whose allocation + * or deallocation resulted in the root change. this range + * is respresented by a single leaf of the current dmapctl + * and the leaf will be updated with this value, possibly + * causing a binary buddy system within the leaves to be + * split or joined. the update may also cause the dmapctl's + * dmtree to be updated. + * + * if the adjustment of the dmap control page, itself, causes its + * root to change, this change will be bubbled up to the next dmap + * control level by a recursive call to this routine, specifying + * the new root value and the next dmap control page level to + * be adjusted. + * PARAMETERS: + * bmp - pointer to bmap descriptor + * blkno - the first block of a block range within a dmap. it is + * the allocation or deallocation of this block range that + * requires the dmap control page to be adjusted. + * newval - the new value of the lower level dmap or dmap control + * page root. + * alloc - TRUE if adjustment is due to an allocation. + * level - current level of dmap control page (i.e. L0, L1, L2) to + * be adjusted. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error + * + * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit; + */ +static int +dbAdjCtl(bmap_t * bmp, s64 blkno, int newval, int alloc, int level) +{ + metapage_t *mp; + s8 oldroot; + int oldval; + s64 lblkno; + dmapctl_t *dcp; + int rc, leafno, ti; + + /* get the buffer for the dmap control page for the specified + * block number and control page level. + */ + lblkno = BLKTOCTL(blkno, bmp->db_l2nbperpage, level); + mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0); + if (mp == NULL) + return (EIO); + dcp = (dmapctl_t *) mp->data; + + /* determine the leaf number corresponding to the block and + * the index within the dmap control tree. + */ + leafno = BLKTOCTLLEAF(blkno, dcp->budmin); + ti = leafno + le32_to_cpu(dcp->leafidx); + + /* save the current leaf value and the current root level (i.e. + * maximum l2 free string described by this dmapctl). + */ + oldval = dcp->stree[ti]; + oldroot = dcp->stree[ROOT]; + + /* check if this is a control page update for an allocation. + * if so, update the leaf to reflect the new leaf value using + * dbSplit(); otherwise (deallocation), use dbJoin() to udpate + * the leaf with the new value. in addition to updating the + * leaf, dbSplit() will also split the binary buddy system of + * the leaves, if required, and bubble new values within the + * dmapctl tree, if required. similarly, dbJoin() will join + * the binary buddy system of leaves and bubble new values up + * the dmapctl tree as required by the new leaf value. + */ + if (alloc) { + /* check if we are in the middle of a binary buddy + * system. this happens when we are performing the + * first allocation out of an allocation group that + * is part (not the first part) of a larger binary + * buddy system. if we are in the middle, back split + * the system prior to calling dbSplit() which assumes + * that it is at the front of a binary buddy system. + */ + if (oldval == NOFREE) { + dbBackSplit((dmtree_t *) dcp, leafno); + oldval = dcp->stree[ti]; + } + dbSplit((dmtree_t *) dcp, leafno, dcp->budmin, newval); + } else { + dbJoin((dmtree_t *) dcp, leafno, newval); + } + + /* check if the root of the current dmap control page changed due + * to the update and if the current dmap control page is not at + * the current top level (i.e. L0, L1, L2) of the map. if so (i.e. + * root changed and this is not the top level), call this routine + * again (recursion) for the next higher level of the mapping to + * reflect the change in root for the current dmap control page. + */ + if (dcp->stree[ROOT] != oldroot) { + /* are we below the top level of the map. if so, + * bubble the root up to the next higher level. + */ + if (level < bmp->db_maxlevel) { + /* bubble up the new root of this dmap control page to + * the next level. + */ + if ((rc = + dbAdjCtl(bmp, blkno, dcp->stree[ROOT], alloc, + level + 1))) { + /* something went wrong in bubbling up the new + * root value, so backout the changes to the + * current dmap control page. + */ + if (alloc) { + dbJoin((dmtree_t *) dcp, leafno, + oldval); + } else { + /* the dbJoin() above might have + * caused a larger binary buddy system + * to form and we may now be in the + * middle of it. if this is the case, + * back split the buddies. + */ + if (dcp->stree[ti] == NOFREE) + dbBackSplit((dmtree_t *) + dcp, leafno); + dbSplit((dmtree_t *) dcp, leafno, + dcp->budmin, oldval); + } + + /* release the buffer and return the error. + */ + release_metapage(mp); + return (rc); + } + } else { + /* we're at the top level of the map. update + * the bmap control page to reflect the size + * of the maximum free buddy system. + */ + assert(level == bmp->db_maxlevel); + assert(bmp->db_maxfreebud == oldroot); + bmp->db_maxfreebud = dcp->stree[ROOT]; + } + } + + /* write the buffer. + */ + write_metapage(mp); + + return (0); +} + + +/* + * NAME: dbSplit() + * + * FUNCTION: update the leaf of a dmtree with a new value, splitting + * the leaf from the binary buddy system of the dmtree's + * leaves, as required. + * + * PARAMETERS: + * tp - pointer to the tree containing the leaf. + * leafno - the number of the leaf to be updated. + * splitsz - the size the binary buddy system starting at the leaf + * must be split to, specified as the log2 number of blocks. + * newval - the new value for the leaf. + * + * RETURN VALUES: none + * + * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit; + */ +static void dbSplit(dmtree_t * tp, int leafno, int splitsz, int newval) +{ + int budsz; + int cursz; + s8 *leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx); + + /* check if the leaf needs to be split. + */ + if (leaf[leafno] > tp->dmt_budmin) { + /* the split occurs by cutting the buddy system in half + * at the specified leaf until we reach the specified + * size. pick up the starting split size (current size + * - 1 in l2) and the corresponding buddy size. + */ + cursz = leaf[leafno] - 1; + budsz = BUDSIZE(cursz, tp->dmt_budmin); + + /* split until we reach the specified size. + */ + while (cursz >= splitsz) { + /* update the buddy's leaf with its new value. + */ + dbAdjTree(tp, leafno ^ budsz, cursz); + + /* on to the next size and buddy. + */ + cursz -= 1; + budsz >>= 1; + } + } + + /* adjust the dmap tree to reflect the specified leaf's new + * value. + */ + dbAdjTree(tp, leafno, newval); +} + + +/* + * NAME: dbBackSplit() + * + * FUNCTION: back split the binary buddy system of dmtree leaves + * that hold a specified leaf until the specified leaf + * starts its own binary buddy system. + * + * the allocators typically perform allocations at the start + * of binary buddy systems and dbSplit() is used to accomplish + * any required splits. in some cases, however, allocation + * may occur in the middle of a binary system and requires a + * back split, with the split proceeding out from the middle of + * the system (less efficient) rather than the start of the + * system (more efficient). the cases in which a back split + * is required are rare and are limited to the first allocation + * within an allocation group which is a part (not first part) + * of a larger binary buddy system and a few exception cases + * in which a previous join operation must be backed out. + * + * PARAMETERS: + * tp - pointer to the tree containing the leaf. + * leafno - the number of the leaf to be updated. + * + * RETURN VALUES: none + * + * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit; + */ +static void dbBackSplit(dmtree_t * tp, int leafno) +{ + int budsz, bud, w, bsz, size; + int cursz; + s8 *leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx); + + /* leaf should be part (not first part) of a binary + * buddy system. + */ + assert(leaf[leafno] == NOFREE); + + /* the back split is accomplished by iteratively finding the leaf + * that starts the buddy system that contains the specified leaf and + * splitting that system in two. this iteration continues until + * the specified leaf becomes the start of a buddy system. + * + * determine maximum possible l2 size for the specified leaf. + */ + size = + LITOL2BSZ(leafno, le32_to_cpu(tp->dmt_l2nleafs), + tp->dmt_budmin); + + /* determine the number of leaves covered by this size. this + * is the buddy size that we will start with as we search for + * the buddy system that contains the specified leaf. + */ + budsz = BUDSIZE(size, tp->dmt_budmin); + + /* back split. + */ + while (leaf[leafno] == NOFREE) { + /* find the leftmost buddy leaf. + */ + for (w = leafno, bsz = budsz;; bsz <<= 1, + w = (w < bud) ? w : bud) { + assert(bsz < le32_to_cpu(tp->dmt_nleafs)); + + /* determine the buddy. + */ + bud = w ^ bsz; + + /* check if this buddy is the start of the system. + */ + if (leaf[bud] != NOFREE) { + /* split the leaf at the start of the + * system in two. + */ + cursz = leaf[bud] - 1; + dbSplit(tp, bud, cursz, cursz); + break; + } + } + } + + assert(leaf[leafno] == size); +} + + +/* + * NAME: dbJoin() + * + * FUNCTION: update the leaf of a dmtree with a new value, joining + * the leaf with other leaves of the dmtree into a multi-leaf + * binary buddy system, as required. + * + * PARAMETERS: + * tp - pointer to the tree containing the leaf. + * leafno - the number of the leaf to be updated. + * newval - the new value for the leaf. + * + * RETURN VALUES: none + */ +static void dbJoin(dmtree_t * tp, int leafno, int newval) +{ + int budsz, buddy; + s8 *leaf; + + /* can the new leaf value require a join with other leaves ? + */ + if (newval >= tp->dmt_budmin) { + /* pickup a pointer to the leaves of the tree. + */ + leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx); + + /* try to join the specified leaf into a large binary + * buddy system. the join proceeds by attempting to join + * the specified leafno with its buddy (leaf) at new value. + * if the join occurs, we attempt to join the left leaf + * of the joined buddies with its buddy at new value + 1. + * we continue to join until we find a buddy that cannot be + * joined (does not have a value equal to the size of the + * last join) or until all leaves have been joined into a + * single system. + * + * get the buddy size (number of words covered) of + * the new value. + */ + budsz = BUDSIZE(newval, tp->dmt_budmin); + + /* try to join. + */ + while (budsz < le32_to_cpu(tp->dmt_nleafs)) { + /* get the buddy leaf. + */ + buddy = leafno ^ budsz; + + /* if the leaf's new value is greater than its + * buddy's value, we join no more. + */ + if (newval > leaf[buddy]) + break; + + assert(newval == leaf[buddy]); + + /* check which (leafno or buddy) is the left buddy. + * the left buddy gets to claim the blocks resulting + * from the join while the right gets to claim none. + * the left buddy is also eligable to participate in + * a join at the next higher level while the right + * is not. + * + */ + if (leafno < buddy) { + /* leafno is the left buddy. + */ + dbAdjTree(tp, buddy, NOFREE); + } else { + /* buddy is the left buddy and becomes + * leafno. + */ + dbAdjTree(tp, leafno, NOFREE); + leafno = buddy; + } + + /* on to try the next join. + */ + newval += 1; + budsz <<= 1; + } + } + + /* update the leaf value. + */ + dbAdjTree(tp, leafno, newval); +} + + +/* + * NAME: dbAdjTree() + * + * FUNCTION: update a leaf of a dmtree with a new value, adjusting + * the dmtree, as required, to reflect the new leaf value. + * the combination of any buddies must already be done before + * this is called. + * + * PARAMETERS: + * tp - pointer to the tree to be adjusted. + * leafno - the number of the leaf to be updated. + * newval - the new value for the leaf. + * + * RETURN VALUES: none + */ +static void dbAdjTree(dmtree_t * tp, int leafno, int newval) +{ + int lp, pp, k; + int max; + + /* pick up the index of the leaf for this leafno. + */ + lp = leafno + le32_to_cpu(tp->dmt_leafidx); + + /* is the current value the same as the old value ? if so, + * there is nothing to do. + */ + if (tp->dmt_stree[lp] == newval) + return; + + /* set the new value. + */ + tp->dmt_stree[lp] = newval; + + /* bubble the new value up the tree as required. + */ + for (k = 0; k < le32_to_cpu(tp->dmt_height); k++) { + /* get the index of the first leaf of the 4 leaf + * group containing the specified leaf (leafno). + */ + lp = ((lp - 1) & ~0x03) + 1; + + /* get the index of the parent of this 4 leaf group. + */ + pp = (lp - 1) >> 2; + + /* determine the maximum of the 4 leaves. + */ + max = TREEMAX(&tp->dmt_stree[lp]); + + /* if the maximum of the 4 is the same as the + * parent's value, we're done. + */ + if (tp->dmt_stree[pp] == max) + break; + + /* parent gets new value. + */ + tp->dmt_stree[pp] = max; + + /* parent becomes leaf for next go-round. + */ + lp = pp; + } +} + + +/* + * NAME: dbFindLeaf() + * + * FUNCTION: search a dmtree_t for sufficient free blocks, returning + * the index of a leaf describing the free blocks if + * sufficient free blocks are found. + * + * the search starts at the top of the dmtree_t tree and + * proceeds down the tree to the leftmost leaf with sufficient + * free space. + * + * PARAMETERS: + * tp - pointer to the tree to be searched. + * l2nb - log2 number of free blocks to search for. + * leafidx - return pointer to be set to the index of the leaf + * describing at least l2nb free blocks if sufficient + * free blocks are found. + * + * RETURN VALUES: + * 0 - success + * ENOSPC - insufficient free blocks. + */ +static int dbFindLeaf(dmtree_t * tp, int l2nb, int *leafidx) +{ + int ti, n = 0, k, x = 0; + + /* first check the root of the tree to see if there is + * sufficient free space. + */ + if (l2nb > tp->dmt_stree[ROOT]) + return (ENOSPC); + + /* sufficient free space available. now search down the tree + * starting at the next level for the leftmost leaf that + * describes sufficient free space. + */ + for (k = le32_to_cpu(tp->dmt_height), ti = 1; + k > 0; k--, ti = ((ti + n) << 2) + 1) { + /* search the four nodes at this level, starting from + * the left. + */ + for (x = ti, n = 0; n < 4; n++) { + /* sufficient free space found. move to the next + * level (or quit if this is the last level). + */ + if (l2nb <= tp->dmt_stree[x + n]) + break; + } + + /* better have found something since the higher + * levels of the tree said it was here. + */ + assert(n < 4); + } + + /* set the return to the leftmost leaf describing sufficient + * free space. + */ + *leafidx = x + n - le32_to_cpu(tp->dmt_leafidx); + + return (0); +} + + +/* + * NAME: dbFindBits() + * + * FUNCTION: find a specified number of binary buddy free bits within a + * dmap bitmap word value. + * + * this routine searches the bitmap value for (1 << l2nb) free + * bits at (1 << l2nb) alignments within the value. + * + * PARAMETERS: + * word - dmap bitmap word value. + * l2nb - number of free bits specified as a log2 number. + * + * RETURN VALUES: + * starting bit number of free bits. + */ +static int dbFindBits(u32 word, int l2nb) +{ + int bitno, nb; + u32 mask; + + /* get the number of bits. + */ + nb = 1 << l2nb; + assert(nb <= DBWORD); + + /* complement the word so we can use a mask (i.e. 0s represent + * free bits) and compute the mask. + */ + word = ~word; + mask = ONES << (DBWORD - nb); + + /* scan the word for nb free bits at nb alignments. + */ + for (bitno = 0; mask != 0; bitno += nb, mask >>= nb) { + if ((mask & word) == mask) + break; + } + + ASSERT(bitno < 32); + + /* return the bit number. + */ + return (bitno); +} + + +/* + * NAME: dbMaxBud(u8 *cp) + * + * FUNCTION: determine the largest binary buddy string of free + * bits within 32-bits of the map. + * + * PARAMETERS: + * cp - pointer to the 32-bit value. + * + * RETURN VALUES: + * largest binary buddy of free bits within a dmap word. + */ +static int dbMaxBud(u8 * cp) +{ + signed char tmp1, tmp2; + + /* check if the wmap word is all free. if so, the + * free buddy size is BUDMIN. + */ + if (*((uint *) cp) == 0) + return (BUDMIN); + + /* check if the wmap word is half free. if so, the + * free buddy size is BUDMIN-1. + */ + if (*((u16 *) cp) == 0 || *((u16 *) cp + 1) == 0) + return (BUDMIN - 1); + + /* not all free or half free. determine the free buddy + * size thru table lookup using quarters of the wmap word. + */ + tmp1 = max(budtab[cp[2]], budtab[cp[3]]); + tmp2 = max(budtab[cp[0]], budtab[cp[1]]); + return (max(tmp1, tmp2)); +} + + +/* + * NAME: cnttz(uint word) + * + * FUNCTION: determine the number of trailing zeros within a 32-bit + * value. + * + * PARAMETERS: + * value - 32-bit value to be examined. + * + * RETURN VALUES: + * count of trailing zeros + */ +int cnttz(u32 word) +{ + int n; + + for (n = 0; n < 32; n++, word >>= 1) { + if (word & 0x01) + break; + } + + return (n); +} + + +/* + * NAME: cntlz(u32 value) + * + * FUNCTION: determine the number of leading zeros within a 32-bit + * value. + * + * PARAMETERS: + * value - 32-bit value to be examined. + * + * RETURN VALUES: + * count of leading zeros + */ +int cntlz(u32 value) +{ + int n; + + for (n = 0; n < 32; n++, value <<= 1) { + if (value & HIGHORDER) + break; + } + return (n); +} + + +/* + * NAME: blkstol2(s64 nb) + * + * FUNCTION: convert a block count to its log2 value. if the block + * count is not a l2 multiple, it is rounded up to the next + * larger l2 multiple. + * + * PARAMETERS: + * nb - number of blocks + * + * RETURN VALUES: + * log2 number of blocks + */ +int blkstol2(s64 nb) +{ + int l2nb; + s64 mask; /* meant to be signed */ + + mask = (s64) 1 << (64 - 1); + + /* count the leading bits. + */ + for (l2nb = 0; l2nb < 64; l2nb++, mask >>= 1) { + /* leading bit found. + */ + if (nb & mask) { + /* determine the l2 value. + */ + l2nb = (64 - 1) - l2nb; + + /* check if we need to round up. + */ + if (~mask & nb) + l2nb++; + + return (l2nb); + } + } + assert(0); + return 0; /* fix compiler warning */ +} + + +/* + * NAME: fsDirty() + * + * FUNCTION: xxx + * + * PARAMETERS: + * ipmnt - mount inode + * + * RETURN VALUES: + * none + */ +void fsDirty() +{ + printk("fsDirty(): bye-bye\n"); + assert(0); +} + + +/* + * NAME: dbAllocBottomUp() + * + * FUNCTION: alloc the specified block range from the working block + * allocation map. + * + * the blocks will be alloc from the working map one dmap + * at a time. + * + * PARAMETERS: + * ip - pointer to in-core inode; + * blkno - starting block number to be freed. + * nblocks - number of blocks to be freed. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error + */ +int dbAllocBottomUp(struct inode *ip, s64 blkno, s64 nblocks) +{ + metapage_t *mp; + dmap_t *dp; + int nb, rc; + s64 lblkno, rem; + struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap; + bmap_t *bmp = JFS_SBI(ip->i_sb)->bmap; + + IREAD_LOCK(ipbmap); + + /* block to be allocated better be within the mapsize. */ + ASSERT(nblocks <= bmp->db_mapsize - blkno); + + /* + * allocate the blocks a dmap at a time. + */ + mp = NULL; + for (rem = nblocks; rem > 0; rem -= nb, blkno += nb) { + /* release previous dmap if any */ + if (mp) { + write_metapage(mp); + } + + /* get the buffer for the current dmap. */ + lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage); + mp = read_metapage(ipbmap, lblkno, PSIZE, 0); + if (mp == NULL) { + IREAD_UNLOCK(ipbmap); + return (EIO); + } + dp = (dmap_t *) mp->data; + + /* determine the number of blocks to be allocated from + * this dmap. + */ + nb = min(rem, BPERDMAP - (blkno & (BPERDMAP - 1))); + + DBFREECK(bmp->db_DBmap, bmp->db_mapsize, blkno, nb); + + /* allocate the blocks. */ + if ((rc = dbAllocDmapBU(bmp, dp, blkno, nb))) { + release_metapage(mp); + IREAD_UNLOCK(ipbmap); + return (rc); + } + + DBALLOC(bmp->db_DBmap, bmp->db_mapsize, blkno, nb); + } + + /* write the last buffer. */ + write_metapage(mp); + + IREAD_UNLOCK(ipbmap); + + return (0); +} + + +static int dbAllocDmapBU(bmap_t * bmp, dmap_t * dp, s64 blkno, int nblocks) +{ + int rc; + int dbitno, word, rembits, nb, nwords, wbitno, agno; + s8 oldroot, *leaf; + dmaptree_t *tp = (dmaptree_t *) & dp->tree; + + /* save the current value of the root (i.e. maximum free string) + * of the dmap tree. + */ + oldroot = tp->stree[ROOT]; + + /* pick up a pointer to the leaves of the dmap tree */ + leaf = tp->stree + LEAFIND; + + /* determine the bit number and word within the dmap of the + * starting block. + */ + dbitno = blkno & (BPERDMAP - 1); + word = dbitno >> L2DBWORD; + + /* block range better be within the dmap */ + assert(dbitno + nblocks <= BPERDMAP); + + /* allocate the bits of the dmap's words corresponding to the block + * range. not all bits of the first and last words may be contained + * within the block range. if this is the case, we'll work against + * those words (i.e. partial first and/or last) on an individual basis + * (a single pass), allocating the bits of interest by hand and + * updating the leaf corresponding to the dmap word. a single pass + * will be used for all dmap words fully contained within the + * specified range. within this pass, the bits of all fully contained + * dmap words will be marked as free in a single shot and the leaves + * will be updated. a single leaf may describe the free space of + * multiple dmap words, so we may update only a subset of the actual + * leaves corresponding to the dmap words of the block range. + */ + for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) { + /* determine the bit number within the word and + * the number of bits within the word. + */ + wbitno = dbitno & (DBWORD - 1); + nb = min(rembits, DBWORD - wbitno); + + /* check if only part of a word is to be allocated. + */ + if (nb < DBWORD) { + /* allocate (set to 1) the appropriate bits within + * this dmap word. + */ + dp->wmap[word] |= cpu_to_le32(ONES << (DBWORD - nb) + >> wbitno); + + word += 1; + } else { + /* one or more dmap words are fully contained + * within the block range. determine how many + * words and allocate (set to 1) the bits of these + * words. + */ + nwords = rembits >> L2DBWORD; + memset(&dp->wmap[word], (int) ONES, nwords * 4); + + /* determine how many bits */ + nb = nwords << L2DBWORD; + } + } + + /* update the free count for this dmap */ + dp->nfree = cpu_to_le32(le32_to_cpu(dp->nfree) - nblocks); + + /* reconstruct summary tree */ + dbInitDmapTree(dp); + + BMAP_LOCK(bmp); + + /* if this allocation group is completely free, + * update the highest active allocation group number + * if this allocation group is the new max. + */ + agno = blkno >> bmp->db_agl2size; + if (agno > bmp->db_maxag) + bmp->db_maxag = agno; + + /* update the free count for the allocation group and map */ + bmp->db_agfree[agno] -= nblocks; + bmp->db_nfree -= nblocks; + + BMAP_UNLOCK(bmp); + + /* if the root has not changed, done. */ + if (tp->stree[ROOT] == oldroot) + return (0); + + /* root changed. bubble the change up to the dmap control pages. + * if the adjustment of the upper level control pages fails, + * backout the bit allocation (thus making everything consistent). + */ + if ((rc = dbAdjCtl(bmp, blkno, tp->stree[ROOT], 1, 0))) + dbFreeBits(bmp, dp, blkno, nblocks); + + return (rc); +} + + +/* + * NAME: dbExtendFS() + * + * FUNCTION: extend bmap from blkno for nblocks; + * dbExtendFS() updates bmap ready for dbAllocBottomUp(); + * + * L2 + * | + * L1---------------------------------L1 + * | | + * L0---------L0---------L0 L0---------L0---------L0 + * | | | | | | + * d0,...,dn d0,...,dn d0,...,dn d0,...,dn d0,...,dn d0,.,dm; + * L2L1L0d0,...,dnL0d0,...,dnL0d0,...,dnL1L0d0,...,dnL0d0,...,dnL0d0,..dm + * + * <---old---><----------------------------extend-----------------------> + */ +int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks) +{ + struct jfs_sb_info *sbi = JFS_SBI(ipbmap->i_sb); + int nbperpage = sbi->nbperpage; + int i, i0 = TRUE, j, j0 = TRUE, k, n; + s64 newsize; + s64 p; + metapage_t *mp, *l2mp, *l1mp, *l0mp; + dmapctl_t *l2dcp, *l1dcp, *l0dcp; + dmap_t *dp; + s8 *l0leaf, *l1leaf, *l2leaf; + bmap_t *bmp = sbi->bmap; + int agno, l2agsize, oldl2agsize; + s64 ag_rem; + + newsize = blkno + nblocks; + + jEVENT(0, ("dbExtendFS: blkno:%Ld nblocks:%Ld newsize:%Ld\n", + (long long) blkno, (long long) nblocks, + (long long) newsize)); + + /* + * initialize bmap control page. + * + * all the data in bmap control page should exclude + * the mkfs hidden dmap page. + */ + + /* update mapsize */ + bmp->db_mapsize = newsize; + bmp->db_maxlevel = BMAPSZTOLEV(bmp->db_mapsize); + + /* compute new AG size */ + l2agsize = dbGetL2AGSize(newsize); + oldl2agsize = bmp->db_agl2size; + + bmp->db_agl2size = l2agsize; + bmp->db_agsize = 1 << l2agsize; + + /* compute new number of AG */ + agno = bmp->db_numag; + bmp->db_numag = newsize >> l2agsize; + bmp->db_numag += ((u32) newsize % (u32) bmp->db_agsize) ? 1 : 0; + + /* + * reconfigure db_agfree[] + * from old AG configuration to new AG configuration; + * + * coalesce contiguous k (newAGSize/oldAGSize) AGs; + * i.e., (AGi, ..., AGj) where i = k*n and j = k*(n+1) - 1 to AGn; + * note: new AG size = old AG size * (2**x). + */ + if (l2agsize == oldl2agsize) + goto extend; + k = 1 << (l2agsize - oldl2agsize); + ag_rem = bmp->db_agfree[0]; /* save agfree[0] */ + for (i = 0, n = 0; i < agno; n++) { + bmp->db_agfree[n] = 0; /* init collection point */ + + /* coalesce cotiguous k AGs; */ + for (j = 0; j < k && i < agno; j++, i++) { + /* merge AGi to AGn */ + bmp->db_agfree[n] += bmp->db_agfree[i]; + } + } + bmp->db_agfree[0] += ag_rem; /* restore agfree[0] */ + + for (; n < MAXAG; n++) + bmp->db_agfree[n] = 0; + + /* + * update highest active ag number + */ + + bmp->db_maxag = bmp->db_maxag / k; + + /* + * extend bmap + * + * update bit maps and corresponding level control pages; + * global control page db_nfree, db_agfree[agno], db_maxfreebud; + */ + extend: + /* get L2 page */ + p = BMAPBLKNO + nbperpage; /* L2 page */ + l2mp = read_metapage(ipbmap, p, PSIZE, 0); + assert(l2mp); + l2dcp = (dmapctl_t *) l2mp->data; + + /* compute start L1 */ + k = blkno >> L2MAXL1SIZE; + l2leaf = l2dcp->stree + CTLLEAFIND + k; + p = BLKTOL1(blkno, sbi->l2nbperpage); /* L1 page */ + + /* + * extend each L1 in L2 + */ + for (; k < LPERCTL; k++, p += nbperpage) { + /* get L1 page */ + if (j0) { + /* read in L1 page: (blkno & (MAXL1SIZE - 1)) */ + l1mp = read_metapage(ipbmap, p, PSIZE, 0); + if (l1mp == NULL) + goto errout; + l1dcp = (dmapctl_t *) l1mp->data; + + /* compute start L0 */ + j = (blkno & (MAXL1SIZE - 1)) >> L2MAXL0SIZE; + l1leaf = l1dcp->stree + CTLLEAFIND + j; + p = BLKTOL0(blkno, sbi->l2nbperpage); + j0 = FALSE; + } else { + /* assign/init L1 page */ + l1mp = get_metapage(ipbmap, p, PSIZE, 0); + if (l1mp == NULL) + goto errout; + + l1dcp = (dmapctl_t *) l1mp->data; + + /* compute start L0 */ + j = 0; + l1leaf = l1dcp->stree + CTLLEAFIND; + p += nbperpage; /* 1st L0 of L1.k */ + } + + /* + * extend each L0 in L1 + */ + for (; j < LPERCTL; j++) { + /* get L0 page */ + if (i0) { + /* read in L0 page: (blkno & (MAXL0SIZE - 1)) */ + + l0mp = read_metapage(ipbmap, p, PSIZE, 0); + if (l0mp == NULL) + goto errout; + l0dcp = (dmapctl_t *) l0mp->data; + + /* compute start dmap */ + i = (blkno & (MAXL0SIZE - 1)) >> + L2BPERDMAP; + l0leaf = l0dcp->stree + CTLLEAFIND + i; + p = BLKTODMAP(blkno, + sbi->l2nbperpage); + i0 = FALSE; + } else { + /* assign/init L0 page */ + l0mp = get_metapage(ipbmap, p, PSIZE, 0); + if (l0mp == NULL) + goto errout; + + l0dcp = (dmapctl_t *) l0mp->data; + + /* compute start dmap */ + i = 0; + l0leaf = l0dcp->stree + CTLLEAFIND; + p += nbperpage; /* 1st dmap of L0.j */ + } + + /* + * extend each dmap in L0 + */ + for (; i < LPERCTL; i++) { + /* + * reconstruct the dmap page, and + * initialize corresponding parent L0 leaf + */ + if ((n = blkno & (BPERDMAP - 1))) { + /* read in dmap page: */ + mp = read_metapage(ipbmap, p, + PSIZE, 0); + if (mp == NULL) + goto errout; + n = min(nblocks, (s64)BPERDMAP - n); + } else { + /* assign/init dmap page */ + mp = read_metapage(ipbmap, p, + PSIZE, 0); + if (mp == NULL) + goto errout; + + n = min(nblocks, (s64)BPERDMAP); + } + + dp = (dmap_t *) mp->data; + *l0leaf = dbInitDmap(dp, blkno, n); + + bmp->db_nfree += n; + agno = le64_to_cpu(dp->start) >> l2agsize; + bmp->db_agfree[agno] += n; + + write_metapage(mp); + + l0leaf++; + p += nbperpage; + + blkno += n; + nblocks -= n; + if (nblocks == 0) + break; + } /* for each dmap in a L0 */ + + /* + * build current L0 page from its leaves, and + * initialize corresponding parent L1 leaf + */ + *l1leaf = dbInitDmapCtl(l0dcp, 0, ++i); + write_metapage(l0mp); + + if (nblocks) + l1leaf++; /* continue for next L0 */ + else { + /* more than 1 L0 ? */ + if (j > 0) + break; /* build L1 page */ + else { + /* summarize in global bmap page */ + bmp->db_maxfreebud = *l1leaf; + release_metapage(l1mp); + release_metapage(l2mp); + goto finalize; + } + } + } /* for each L0 in a L1 */ + + /* + * build current L1 page from its leaves, and + * initialize corresponding parent L2 leaf + */ + *l2leaf = dbInitDmapCtl(l1dcp, 1, ++j); + write_metapage(l1mp); + + if (nblocks) + l2leaf++; /* continue for next L1 */ + else { + /* more than 1 L1 ? */ + if (k > 0) + break; /* build L2 page */ + else { + /* summarize in global bmap page */ + bmp->db_maxfreebud = *l2leaf; + release_metapage(l2mp); + goto finalize; + } + } + } /* for each L1 in a L2 */ + + assert(0); + + /* + * finalize bmap control page + */ + finalize: + + return 0; + + errout: + return EIO; +} + + +/* + * dbFinalizeBmap() + */ +void dbFinalizeBmap(struct inode *ipbmap) +{ + bmap_t *bmp = JFS_SBI(ipbmap->i_sb)->bmap; + int actags, inactags, l2nl; + s64 ag_rem, actfree, inactfree, avgfree; + int i, n; + + /* + * finalize bmap control page + */ +//finalize: + /* + * compute db_agpref: preferred ag to allocate from + * (the leftmost ag with average free space in it); + */ +//agpref: + /* get the number of active ags and inacitve ags */ + actags = bmp->db_maxag + 1; + inactags = bmp->db_numag - actags; + ag_rem = bmp->db_mapsize & (bmp->db_agsize - 1); /* ??? */ + + /* determine how many blocks are in the inactive allocation + * groups. in doing this, we must account for the fact that + * the rightmost group might be a partial group (i.e. file + * system size is not a multiple of the group size). + */ + inactfree = (inactags && ag_rem) ? + ((inactags - 1) << bmp->db_agl2size) + ag_rem + : inactags << bmp->db_agl2size; + + /* determine how many free blocks are in the active + * allocation groups plus the average number of free blocks + * within the active ags. + */ + actfree = bmp->db_nfree - inactfree; + avgfree = (u32) actfree / (u32) actags; + + /* if the preferred allocation group has not average free space. + * re-establish the preferred group as the leftmost + * group with average free space. + */ + if (bmp->db_agfree[bmp->db_agpref] < avgfree) { + for (bmp->db_agpref = 0; bmp->db_agpref < actags; + bmp->db_agpref++) { + if (bmp->db_agfree[bmp->db_agpref] >= avgfree) + break; + } + assert(bmp->db_agpref < bmp->db_numag); + } + + /* + * compute db_aglevel, db_agheigth, db_width, db_agstart: + * an ag is covered in aglevel dmapctl summary tree, + * at agheight level height (from leaf) with agwidth number of nodes + * each, which starts at agstart index node of the smmary tree node + * array; + */ + bmp->db_aglevel = BMAPSZTOLEV(bmp->db_agsize); + l2nl = + bmp->db_agl2size - (L2BPERDMAP + bmp->db_aglevel * L2LPERCTL); + bmp->db_agheigth = l2nl >> 1; + bmp->db_agwidth = 1 << (l2nl - (bmp->db_agheigth << 1)); + for (i = 5 - bmp->db_agheigth, bmp->db_agstart = 0, n = 1; i > 0; + i--) { + bmp->db_agstart += n; + n <<= 2; + } + +/* +printk("bmap: agpref:%d aglevel:%d agheigth:%d agwidth:%d\n", + bmp->db_agpref, bmp->db_aglevel, bmp->db_agheigth, bmp->db_agwidth); +*/ +} + + +/* + * NAME: dbInitDmap()/ujfs_idmap_page() + * + * FUNCTION: initialize working/persistent bitmap of the dmap page + * for the specified number of blocks: + * + * at entry, the bitmaps had been initialized as free (ZEROS); + * The number of blocks will only account for the actually + * existing blocks. Blocks which don't actually exist in + * the aggregate will be marked as allocated (ONES); + * + * PARAMETERS: + * dp - pointer to page of map + * nblocks - number of blocks this page + * + * RETURNS: NONE + */ +static int dbInitDmap(dmap_t * dp, s64 Blkno, int nblocks) +{ + int blkno, w, b, r, nw, nb, i; +/* +printk("sbh_dmap: in dbInitDmap blkno:%Ld nblocks:%ld\n", Blkno, nblocks); +*/ + + /* starting block number within the dmap */ + blkno = Blkno & (BPERDMAP - 1); + + if (blkno == 0) { + dp->nblocks = dp->nfree = cpu_to_le32(nblocks); + dp->start = cpu_to_le64(Blkno); + + if (nblocks == BPERDMAP) { + memset(&dp->wmap[0], 0, LPERDMAP * 4); + memset(&dp->pmap[0], 0, LPERDMAP * 4); + goto initTree; + } + } else { + dp->nblocks = + cpu_to_le32(le32_to_cpu(dp->nblocks) + nblocks); + dp->nfree = cpu_to_le32(le32_to_cpu(dp->nfree) + nblocks); + } + + /* word number containing start block number */ + w = blkno >> L2DBWORD; + + /* + * free the bits corresponding to the block range (ZEROS): + * note: not all bits of the first and last words may be contained + * within the block range. + */ + for (r = nblocks; r > 0; r -= nb, blkno += nb) { + /* number of bits preceding range to be freed in the word */ + b = blkno & (DBWORD - 1); + /* number of bits to free in the word */ + nb = min(r, DBWORD - b); + + /* is partial word to be freed ? */ + if (nb < DBWORD) { + /* free (set to 0) from the bitmap word */ + dp->wmap[w] &= cpu_to_le32(~(ONES << (DBWORD - nb) + >> b)); + dp->pmap[w] &= cpu_to_le32(~(ONES << (DBWORD - nb) + >> b)); + + /* skip the word freed */ + w++; + } else { + /* free (set to 0) contiguous bitmap words */ + nw = r >> L2DBWORD; + memset(&dp->wmap[w], 0, nw * 4); + memset(&dp->pmap[w], 0, nw * 4); + + /* skip the words freed */ + nb = nw << L2DBWORD; + w += nw; + } + } + + /* + * mark bits following the range to be freed (non-existing + * blocks) as allocated (ONES) + */ +/* +printk("sbh_dmap: in dbInitDmap, preparing to mark unbacked, blkno:%ld nblocks:%ld\n", + blkno, nblocks); +*/ + + if (blkno == BPERDMAP) + goto initTree; + + /* the first word beyond the end of existing blocks */ + w = blkno >> L2DBWORD; + + /* does nblocks fall on a 32-bit boundary ? */ + b = blkno & (DBWORD - 1); +/* +printk("sbh_dmap: in dbInitDmap, b:%ld w:%ld mask: %lx\n", b, w, (ONES>>b)); +*/ + if (b) { + /* mark a partial word allocated */ + dp->wmap[w] = dp->pmap[w] = cpu_to_le32(ONES >> b); + w++; + } + + /* set the rest of the words in the page to allocated (ONES) */ + for (i = w; i < LPERDMAP; i++) + dp->pmap[i] = dp->wmap[i] = ONES; + + /* + * init tree + */ + initTree: + return (dbInitDmapTree(dp)); +} + + +/* + * NAME: dbInitDmapTree()/ujfs_complete_dmap() + * + * FUNCTION: initialize summary tree of the specified dmap: + * + * at entry, bitmap of the dmap has been initialized; + * + * PARAMETERS: + * dp - dmap to complete + * blkno - starting block number for this dmap + * treemax - will be filled in with max free for this dmap + * + * RETURNS: max free string at the root of the tree + */ +static int dbInitDmapTree(dmap_t * dp) +{ + dmaptree_t *tp; + s8 *cp; + int i; + + /* init fixed info of tree */ + tp = &dp->tree; + tp->nleafs = cpu_to_le32(LPERDMAP); + tp->l2nleafs = cpu_to_le32(L2LPERDMAP); + tp->leafidx = cpu_to_le32(LEAFIND); + tp->height = cpu_to_le32(4); + tp->budmin = BUDMIN; + + /* init each leaf from corresponding wmap word: + * note: leaf is set to NOFREE(-1) if all blocks of corresponding + * bitmap word are allocated. + */ + cp = tp->stree + le32_to_cpu(tp->leafidx); + for (i = 0; i < LPERDMAP; i++) + *cp++ = dbMaxBud((u8 *) & dp->wmap[i]); + + /* build the dmap's binary buddy summary tree */ + return (dbInitTree(tp)); +} + + +/* + * NAME: dbInitTree()/ujfs_adjtree() + * + * FUNCTION: initialize binary buddy summary tree of a dmap or dmapctl. + * + * at entry, the leaves of the tree has been initialized + * from corresponding bitmap word or root of summary tree + * of the child control page; + * configure binary buddy system at the leaf level, then + * bubble up the values of the leaf nodes up the tree. + * + * PARAMETERS: + * cp - Pointer to the root of the tree + * l2leaves- Number of leaf nodes as a power of 2 + * l2min - Number of blocks that can be covered by a leaf + * as a power of 2 + * + * RETURNS: max free string at the root of the tree + */ +static int dbInitTree(dmaptree_t * dtp) +{ + int l2max, l2free, bsize, nextb, i; + int child, parent, nparent; + s8 *tp, *cp, *cp1; + + tp = dtp->stree; + + /* Determine the maximum free string possible for the leaves */ + l2max = le32_to_cpu(dtp->l2nleafs) + dtp->budmin; + + /* + * configure the leaf levevl into binary buddy system + * + * Try to combine buddies starting with a buddy size of 1 + * (i.e. two leaves). At a buddy size of 1 two buddy leaves + * can be combined if both buddies have a maximum free of l2min; + * the combination will result in the left-most buddy leaf having + * a maximum free of l2min+1. + * After processing all buddies for a given size, process buddies + * at the next higher buddy size (i.e. current size * 2) and + * the next maximum free (current free + 1). + * This continues until the maximum possible buddy combination + * yields maximum free. + */ + for (l2free = dtp->budmin, bsize = 1; l2free < l2max; + l2free++, bsize = nextb) { + /* get next buddy size == current buddy pair size */ + nextb = bsize << 1; + + /* scan each adjacent buddy pair at current buddy size */ + for (i = 0, cp = tp + le32_to_cpu(dtp->leafidx); + i < le32_to_cpu(dtp->nleafs); + i += nextb, cp += nextb) { + /* coalesce if both adjacent buddies are max free */ + if (*cp == l2free && *(cp + bsize) == l2free) { + *cp = l2free + 1; /* left take right */ + *(cp + bsize) = -1; /* right give left */ + } + } + } + + /* + * bubble summary information of leaves up the tree. + * + * Starting at the leaf node level, the four nodes described by + * the higher level parent node are compared for a maximum free and + * this maximum becomes the value of the parent node. + * when all lower level nodes are processed in this fashion then + * move up to the next level (parent becomes a lower level node) and + * continue the process for that level. + */ + for (child = le32_to_cpu(dtp->leafidx), + nparent = le32_to_cpu(dtp->nleafs) >> 2; + nparent > 0; nparent >>= 2, child = parent) { + /* get index of 1st node of parent level */ + parent = (child - 1) >> 2; + + /* set the value of the parent node as the maximum + * of the four nodes of the current level. + */ + for (i = 0, cp = tp + child, cp1 = tp + parent; + i < nparent; i++, cp += 4, cp1++) + *cp1 = TREEMAX(cp); + } + + return (*tp); +} + + +/* + * dbInitDmapCtl() + * + * function: initialize dmapctl page + */ +static int dbInitDmapCtl(dmapctl_t * dcp, int level, int i) +{ /* start leaf index not covered by range */ + s8 *cp; + + dcp->nleafs = cpu_to_le32(LPERCTL); + dcp->l2nleafs = cpu_to_le32(L2LPERCTL); + dcp->leafidx = cpu_to_le32(CTLLEAFIND); + dcp->height = cpu_to_le32(5); + dcp->budmin = L2BPERDMAP + L2LPERCTL * level; + + /* + * initialize the leaves of current level that were not covered + * by the specified input block range (i.e. the leaves have no + * low level dmapctl or dmap). + */ + cp = &dcp->stree[CTLLEAFIND + i]; + for (; i < LPERCTL; i++) + *cp++ = NOFREE; + + /* build the dmap's binary buddy summary tree */ + return (dbInitTree((dmaptree_t *) dcp)); +} + + +/* + * NAME: dbGetL2AGSize()/ujfs_getagl2size() + * + * FUNCTION: Determine log2(allocation group size) from aggregate size + * + * PARAMETERS: + * nblocks - Number of blocks in aggregate + * + * RETURNS: log2(allocation group size) in aggregate blocks + */ +static int dbGetL2AGSize(s64 nblocks) +{ + s64 sz; + s64 m; + int l2sz; + + if (nblocks < BPERDMAP * MAXAG) + return (L2BPERDMAP); + + /* round up aggregate size to power of 2 */ + m = ((u64) 1 << (64 - 1)); + for (l2sz = 64; l2sz >= 0; l2sz--, m >>= 1) { + if (m & nblocks) + break; + } + + sz = (s64) 1 << l2sz; + if (sz < nblocks) + l2sz += 1; + + /* agsize = roundupSize/max_number_of_ag */ + return (l2sz - L2MAXAG); +} + + +/* + * NAME: dbMapFileSizeToMapSize() + * + * FUNCTION: compute number of blocks the block allocation map file + * can cover from the map file size; + * + * RETURNS: Number of blocks which can be covered by this block map file; + */ + +/* + * maximum number of map pages at each level including control pages + */ +#define MAXL0PAGES (1 + LPERCTL) +#define MAXL1PAGES (1 + LPERCTL * MAXL0PAGES) +#define MAXL2PAGES (1 + LPERCTL * MAXL1PAGES) + +/* + * convert number of map pages to the zero origin top dmapctl level + */ +#define BMAPPGTOLEV(npages) \ + (((npages) <= 3 + MAXL0PAGES) ? 0 \ + : ((npages) <= 2 + MAXL1PAGES) ? 1 : 2) + +s64 dbMapFileSizeToMapSize(struct inode * ipbmap) +{ + struct super_block *sb = ipbmap->i_sb; + s64 nblocks; + s64 npages, ndmaps; + int level, i; + int complete, factor; + + nblocks = ipbmap->i_size >> JFS_SBI(sb)->l2bsize; + npages = nblocks >> JFS_SBI(sb)->l2nbperpage; + level = BMAPPGTOLEV(npages); + + /* At each level, accumulate the number of dmap pages covered by + * the number of full child levels below it; + * repeat for the last incomplete child level. + */ + ndmaps = 0; + npages--; /* skip the first global control page */ + /* skip higher level control pages above top level covered by map */ + npages -= (2 - level); + npages--; /* skip top level's control page */ + for (i = level; i >= 0; i--) { + factor = + (i == 2) ? MAXL1PAGES : ((i == 1) ? MAXL0PAGES : 1); + complete = (u32) npages / factor; + ndmaps += complete * ((i == 2) ? LPERCTL * LPERCTL + : ((i == 1) ? LPERCTL : 1)); + + /* pages in last/incomplete child */ + npages = (u32) npages % factor; + /* skip incomplete child's level control page */ + npages--; + } + + /* convert the number of dmaps into the number of blocks + * which can be covered by the dmaps; + */ + nblocks = ndmaps << L2BPERDMAP; + + return (nblocks); +} + + +#ifdef _JFS_DEBUG_DMAP +/* + * DBinitmap() + */ +static void DBinitmap(s64 size, struct inode *ipbmap, u32 ** results) +{ + int npages; + u32 *dbmap, *d; + int n; + s64 lblkno, cur_block; + dmap_t *dp; + metapage_t *mp; + + npages = size / 32768; + npages += (size % 32768) ? 1 : 0; + + dbmap = (u32 *) xmalloc(npages * 4096, L2PSIZE, kernel_heap); + if (dbmap == NULL) + assert(0); + + for (n = 0, d = dbmap; n < npages; n++, d += 1024) + bzero(d, 4096); + + /* Need to initialize from disk map pages + */ + for (d = dbmap, cur_block = 0; cur_block < size; + cur_block += BPERDMAP, d += LPERDMAP) { + lblkno = BLKTODMAP(cur_block, + JFS_SBI(ipbmap->i_sb)->bmap-> + db_l2nbperpage); + mp = read_metapage(ipbmap, lblkno, PSIZE, 0); + if (mp == NULL) { + assert(0); + } + dp = (dmap_t *) mp->data; + + for (n = 0; n < LPERDMAP; n++) + d[n] = le32_to_cpu(dp->wmap[n]); + + release_metapage(mp); + } + + *results = dbmap; +} + + +/* + * DBAlloc() + */ +void DBAlloc(uint * dbmap, s64 mapsize, s64 blkno, s64 nblocks) +{ + int word, nb, bitno; + u32 mask; + + assert(blkno > 0 && blkno < mapsize); + assert(nblocks > 0 && nblocks <= mapsize); + + assert(blkno + nblocks <= mapsize); + + dbmap += (blkno / 32); + while (nblocks > 0) { + bitno = blkno & (32 - 1); + nb = min(nblocks, 32 - bitno); + + mask = (0xffffffff << (32 - nb) >> bitno); + assert((mask & *dbmap) == 0); + *dbmap |= mask; + + dbmap++; + blkno += nb; + nblocks -= nb; + } +} + + +/* + * DBFree() + */ +static void DBFree(uint * dbmap, s64 mapsize, s64 blkno, s64 nblocks) +{ + int word, nb, bitno; + u32 mask; + + assert(blkno > 0 && blkno < mapsize); + assert(nblocks > 0 && nblocks <= mapsize); + + assert(blkno + nblocks <= mapsize); + + dbmap += (blkno / 32); + while (nblocks > 0) { + bitno = blkno & (32 - 1); + nb = min(nblocks, 32 - bitno); + + mask = (0xffffffff << (32 - nb) >> bitno); + assert((mask & *dbmap) == mask); + *dbmap &= ~mask; + + dbmap++; + blkno += nb; + nblocks -= nb; + } +} + + +/* + * DBAllocCK() + */ +static void DBAllocCK(uint * dbmap, s64 mapsize, s64 blkno, s64 nblocks) +{ + int word, nb, bitno; + u32 mask; + + assert(blkno > 0 && blkno < mapsize); + assert(nblocks > 0 && nblocks <= mapsize); + + assert(blkno + nblocks <= mapsize); + + dbmap += (blkno / 32); + while (nblocks > 0) { + bitno = blkno & (32 - 1); + nb = min(nblocks, 32 - bitno); + + mask = (0xffffffff << (32 - nb) >> bitno); + assert((mask & *dbmap) == mask); + + dbmap++; + blkno += nb; + nblocks -= nb; + } +} + + +/* + * DBFreeCK() + */ +static void DBFreeCK(uint * dbmap, s64 mapsize, s64 blkno, s64 nblocks) +{ + int word, nb, bitno; + u32 mask; + + assert(blkno > 0 && blkno < mapsize); + assert(nblocks > 0 && nblocks <= mapsize); + + assert(blkno + nblocks <= mapsize); + + dbmap += (blkno / 32); + while (nblocks > 0) { + bitno = blkno & (32 - 1); + nb = min(nblocks, 32 - bitno); + + mask = (0xffffffff << (32 - nb) >> bitno); + assert((mask & *dbmap) == 0); + + dbmap++; + blkno += nb; + nblocks -= nb; + } +} + + +/* + * dbPrtMap() + */ +static void dbPrtMap(bmap_t * bmp) +{ + printk(" mapsize: %d%d\n", bmp->db_mapsize); + printk(" nfree: %d%d\n", bmp->db_nfree); + printk(" numag: %d\n", bmp->db_numag); + printk(" agsize: %d%d\n", bmp->db_agsize); + printk(" agl2size: %d\n", bmp->db_agl2size); + printk(" agwidth: %d\n", bmp->db_agwidth); + printk(" agstart: %d\n", bmp->db_agstart); + printk(" agheigth: %d\n", bmp->db_agheigth); + printk(" aglevel: %d\n", bmp->db_aglevel); + printk(" maxlevel: %d\n", bmp->db_maxlevel); + printk(" maxag: %d\n", bmp->db_maxag); + printk(" agpref: %d\n", bmp->db_agpref); + printk(" l2nbppg: %d\n", bmp->db_l2nbperpage); +} + + +/* + * dbPrtCtl() + */ +static void dbPrtCtl(dmapctl_t * dcp) +{ + int i, j, n; + + printk(" height: %08x\n", le32_to_cpu(dcp->height)); + printk(" leafidx: %08x\n", le32_to_cpu(dcp->leafidx)); + printk(" budmin: %08x\n", dcp->budmin); + printk(" nleafs: %08x\n", le32_to_cpu(dcp->nleafs)); + printk(" l2nleafs: %08x\n", le32_to_cpu(dcp->l2nleafs)); + + printk("\n Tree:\n"); + for (i = 0; i < CTLLEAFIND; i += 8) { + n = min(8, CTLLEAFIND - i); + + for (j = 0; j < n; j++) + printf(" [%03x]: %02x", i + j, + (char) dcp->stree[i + j]); + printf("\n"); + } + + printk("\n Tree Leaves:\n"); + for (i = 0; i < LPERCTL; i += 8) { + n = min(8, LPERCTL - i); + + for (j = 0; j < n; j++) + printf(" [%03x]: %02x", + i + j, + (char) dcp->stree[i + j + CTLLEAFIND]); + printf("\n"); + } +} +#endif /* _JFS_DEBUG_DMAP */ diff -Nru a/fs/jfs/jfs_dmap.h b/fs/jfs/jfs_dmap.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_dmap.h Thu Mar 7 18:17:47 2002 @@ -0,0 +1,301 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * + * jfs_dmap.h: block allocation map manager + */ + +#ifndef _H_JFS_DMAP +#define _H_JFS_DMAP + +#include "jfs_txnmgr.h" + +#define BMAPVERSION 1 /* version number */ +#define TREESIZE (256+64+16+4+1) /* size of a dmap tree */ +#define LEAFIND (64+16+4+1) /* index of 1st leaf of a dmap tree */ +#define LPERDMAP 256 /* num leaves per dmap tree */ +#define L2LPERDMAP 8 /* l2 number of leaves per dmap tree */ +#define DBWORD 32 /* # of blks covered by a map word */ +#define L2DBWORD 5 /* l2 # of blks covered by a mword */ +#define BUDMIN L2DBWORD /* max free string in a map word */ +#define BPERDMAP (LPERDMAP * DBWORD) /* num of blks per dmap */ +#define L2BPERDMAP 13 /* l2 num of blks per dmap */ +#define CTLTREESIZE (1024+256+64+16+4+1) /* size of a dmapctl tree */ +#define CTLLEAFIND (256+64+16+4+1) /* idx of 1st leaf of a dmapctl tree */ +#define LPERCTL 1024 /* num of leaves per dmapctl tree */ +#define L2LPERCTL 10 /* l2 num of leaves per dmapctl tree */ +#define ROOT 0 /* index of the root of a tree */ +#define NOFREE ((s8) -1) /* no blocks free */ +#define MAXAG 128 /* max number of allocation groups */ +#define L2MAXAG 7 /* l2 max num of AG */ +#define L2MINAGSZ 25 /* l2 of minimum AG size in bytes */ +#define BMAPBLKNO 0 /* lblkno of bmap within the map */ + +/* + * maximum l2 number of disk blocks at the various dmapctl levels. + */ +#define L2MAXL0SIZE (L2BPERDMAP + 1 * L2LPERCTL) +#define L2MAXL1SIZE (L2BPERDMAP + 2 * L2LPERCTL) +#define L2MAXL2SIZE (L2BPERDMAP + 3 * L2LPERCTL) + +/* + * maximum number of disk blocks at the various dmapctl levels. + */ +#define MAXL0SIZE ((s64)1 << L2MAXL0SIZE) +#define MAXL1SIZE ((s64)1 << L2MAXL1SIZE) +#define MAXL2SIZE ((s64)1 << L2MAXL2SIZE) + +#define MAXMAPSIZE MAXL2SIZE /* maximum aggregate map size */ + +/* + * determine the maximum free string for four (lower level) nodes + * of the tree. + */ +static __inline signed char TREEMAX(signed char *cp) +{ + signed char tmp1, tmp2; + + tmp1 = max(*(cp+2), *(cp+3)); + tmp2 = max(*(cp), *(cp+1)); + + return max(tmp1, tmp2); +} + +/* + * convert disk block number to the logical block number of the dmap + * describing the disk block. s is the log2(number of logical blocks per page) + * + * The calculation figures out how many logical pages are in front of the dmap. + * - the number of dmaps preceding it + * - the number of L0 pages preceding its L0 page + * - the number of L1 pages preceding its L1 page + * - 3 is added to account for the L2, L1, and L0 page for this dmap + * - 1 is added to account for the control page of the map. + */ +#define BLKTODMAP(b,s) \ + ((((b) >> 13) + ((b) >> 23) + ((b) >> 33) + 3 + 1) << (s)) + +/* + * convert disk block number to the logical block number of the LEVEL 0 + * dmapctl describing the disk block. s is the log2(number of logical blocks + * per page) + * + * The calculation figures out how many logical pages are in front of the L0. + * - the number of dmap pages preceding it + * - the number of L0 pages preceding it + * - the number of L1 pages preceding its L1 page + * - 2 is added to account for the L2, and L1 page for this L0 + * - 1 is added to account for the control page of the map. + */ +#define BLKTOL0(b,s) \ + (((((b) >> 23) << 10) + ((b) >> 23) + ((b) >> 33) + 2 + 1) << (s)) + +/* + * convert disk block number to the logical block number of the LEVEL 1 + * dmapctl describing the disk block. s is the log2(number of logical blocks + * per page) + * + * The calculation figures out how many logical pages are in front of the L1. + * - the number of dmap pages preceding it + * - the number of L0 pages preceding it + * - the number of L1 pages preceding it + * - 1 is added to account for the L2 page + * - 1 is added to account for the control page of the map. + */ +#define BLKTOL1(b,s) \ + (((((b) >> 33) << 20) + (((b) >> 33) << 10) + ((b) >> 33) + 1 + 1) << (s)) + +/* + * convert disk block number to the logical block number of the dmapctl + * at the specified level which describes the disk block. + */ +#define BLKTOCTL(b,s,l) \ + (((l) == 2) ? 1 : ((l) == 1) ? BLKTOL1((b),(s)) : BLKTOL0((b),(s))) + +/* + * convert aggregate map size to the zero origin dmapctl level of the + * top dmapctl. + */ +#define BMAPSZTOLEV(size) \ + (((size) <= MAXL0SIZE) ? 0 : ((size) <= MAXL1SIZE) ? 1 : 2) + +/* convert disk block number to allocation group number. + */ +#define BLKTOAG(b,sbi) ((b) >> ((sbi)->bmap->db_agl2size)) + +/* convert allocation group number to starting disk block + * number. + */ +#define AGTOBLK(a,ip) \ + ((s64)(a) << (JFS_SBI((ip)->i_sb)->bmap->db_agl2size)) + +/* + * dmap summary tree + * + * dmaptree_t must be consistent with dmapctl_t. + */ +typedef struct { + s32 nleafs; /* 4: number of tree leafs */ + s32 l2nleafs; /* 4: l2 number of tree leafs */ + s32 leafidx; /* 4: index of first tree leaf */ + s32 height; /* 4: height of the tree */ + s8 budmin; /* 1: min l2 tree leaf value to combine */ + s8 stree[TREESIZE]; /* TREESIZE: tree */ + u8 pad[2]; /* 2: pad to word boundary */ +} dmaptree_t; /* - 360 - */ + +/* + * dmap page per 8K blocks bitmap + */ +typedef struct { + s32 nblocks; /* 4: num blks covered by this dmap */ + s32 nfree; /* 4: num of free blks in this dmap */ + s64 start; /* 8: starting blkno for this dmap */ + dmaptree_t tree; /* 360: dmap tree */ + u8 pad[1672]; /* 1672: pad to 2048 bytes */ + u32 wmap[LPERDMAP]; /* 1024: bits of the working map */ + u32 pmap[LPERDMAP]; /* 1024: bits of the persistent map */ +} dmap_t; /* - 4096 - */ + +/* + * disk map control page per level. + * + * dmapctl_t must be consistent with dmaptree_t. + */ +typedef struct { + s32 nleafs; /* 4: number of tree leafs */ + s32 l2nleafs; /* 4: l2 number of tree leafs */ + s32 leafidx; /* 4: index of the first tree leaf */ + s32 height; /* 4: height of tree */ + s8 budmin; /* 1: minimum l2 tree leaf value */ + s8 stree[CTLTREESIZE]; /* CTLTREESIZE: dmapctl tree */ + u8 pad[2714]; /* 2714: pad to 4096 */ +} dmapctl_t; /* - 4096 - */ + +/* + * common definition for dmaptree_t within dmap and dmapctl + */ +typedef union { + dmaptree_t t1; + dmapctl_t t2; +} dmtree_t; + +/* macros for accessing fields within dmtree_t */ +#define dmt_nleafs t1.nleafs +#define dmt_l2nleafs t1.l2nleafs +#define dmt_leafidx t1.leafidx +#define dmt_height t1.height +#define dmt_budmin t1.budmin +#define dmt_stree t1.stree + +/* + * on-disk aggregate disk allocation map descriptor. + */ +typedef struct { + s64 dn_mapsize; /* 8: number of blocks in aggregate */ + s64 dn_nfree; /* 8: num free blks in aggregate map */ + s32 dn_l2nbperpage; /* 4: number of blks per page */ + s32 dn_numag; /* 4: total number of ags */ + s32 dn_maxlevel; /* 4: number of active ags */ + s32 dn_maxag; /* 4: max active alloc group number */ + s32 dn_agpref; /* 4: preferred alloc group (hint) */ + s32 dn_aglevel; /* 4: dmapctl level holding the AG */ + s32 dn_agheigth; /* 4: height in dmapctl of the AG */ + s32 dn_agwidth; /* 4: width in dmapctl of the AG */ + s32 dn_agstart; /* 4: start tree index at AG height */ + s32 dn_agl2size; /* 4: l2 num of blks per alloc group */ + s64 dn_agfree[MAXAG]; /* 8*MAXAG: per AG free count */ + s64 dn_agsize; /* 8: num of blks per alloc group */ + s8 dn_maxfreebud; /* 1: max free buddy system */ + u8 pad[3007]; /* 3007: pad to 4096 */ +} dbmap_t; /* - 4096 - */ + +/* + * in-memory aggregate disk allocation map descriptor. + */ +typedef struct bmap { + dbmap_t db_bmap; /* on-disk aggregate map descriptor */ + struct inode *db_ipbmap; /* ptr to aggregate map incore inode */ + struct semaphore db_bmaplock; /* aggregate map lock */ + u32 *db_DBmap; +} bmap_t; + +/* macros for accessing fields within in-memory aggregate map descriptor */ +#define db_mapsize db_bmap.dn_mapsize +#define db_nfree db_bmap.dn_nfree +#define db_agfree db_bmap.dn_agfree +#define db_agsize db_bmap.dn_agsize +#define db_agl2size db_bmap.dn_agl2size +#define db_agwidth db_bmap.dn_agwidth +#define db_agheigth db_bmap.dn_agheigth +#define db_agstart db_bmap.dn_agstart +#define db_numag db_bmap.dn_numag +#define db_maxlevel db_bmap.dn_maxlevel +#define db_aglevel db_bmap.dn_aglevel +#define db_agpref db_bmap.dn_agpref +#define db_maxag db_bmap.dn_maxag +#define db_maxfreebud db_bmap.dn_maxfreebud +#define db_l2nbperpage db_bmap.dn_l2nbperpage + +/* + * macros for various conversions needed by the allocators. + * blkstol2(), cntlz(), and cnttz() are operating system dependent functions. + */ +/* convert number of blocks to log2 number of blocks, rounding up to + * the next log2 value if blocks is not a l2 multiple. + */ +#define BLKSTOL2(d) (blkstol2(d)) + +/* convert number of leafs to log2 leaf value */ +#define NLSTOL2BSZ(n) (31 - cntlz((n)) + BUDMIN) + +/* convert leaf index to log2 leaf value */ +#define LITOL2BSZ(n,m,b) ((((n) == 0) ? (m) : cnttz((n))) + (b)) + +/* convert a block number to a dmap control leaf index */ +#define BLKTOCTLLEAF(b,m) \ + (((b) & (((s64)1 << ((m) + L2LPERCTL)) - 1)) >> (m)) + +/* convert log2 leaf value to buddy size */ +#define BUDSIZE(s,m) (1 << ((s) - (m))) + +/* + * external references. + */ +extern int dbMount(struct inode *ipbmap); + +extern int dbUnmount(struct inode *ipbmap, int mounterror); + +extern int dbFree(struct inode *ipbmap, s64 blkno, s64 nblocks); + +extern int dbUpdatePMap(struct inode *ipbmap, + int free, s64 blkno, s64 nblocks, tblock_t * tblk); + +extern int dbNextAG(struct inode *ipbmap); + +extern int dbAlloc(struct inode *ipbmap, s64 hint, s64 nblocks, s64 * results); + +extern int dbAllocExact(struct inode *ip, s64 blkno, int nblocks); + +extern int dbReAlloc(struct inode *ipbmap, + s64 blkno, s64 nblocks, s64 addnblocks, s64 * results); + +extern int dbSync(struct inode *ipbmap); +extern int dbAllocBottomUp(struct inode *ip, s64 blkno, s64 nblocks); +extern int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks); +extern void dbFinalizeBmap(struct inode *ipbmap); +extern s64 dbMapFileSizeToMapSize(struct inode *ipbmap); +#endif /* _H_JFS_DMAP */ diff -Nru a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_dtree.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,4517 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * + * +*/ + +/* + * jfs_dtree.c: directory B+-tree manager + * + * B+-tree with variable length key directory: + * + * each directory page is structured as an array of 32-byte + * directory entry slots initialized as a freelist + * to avoid search/compaction of free space at insertion. + * when an entry is inserted, a number of slots are allocated + * from the freelist as required to store variable length data + * of the entry; when the entry is deleted, slots of the entry + * are returned to freelist. + * + * leaf entry stores full name as key and file serial number + * (aka inode number) as data. + * internal/router entry stores sufffix compressed name + * as key and simple extent descriptor as data. + * + * each directory page maintains a sorted entry index table + * which stores the start slot index of sorted entries + * to allow binary search on the table. + * + * directory starts as a root/leaf page in on-disk inode + * inline data area. + * when it becomes full, it starts a leaf of a external extent + * of length of 1 block. each time the first leaf becomes full, + * it is extended rather than split (its size is doubled), + * until its length becoms 4 KBytes, from then the extent is split + * with new 4 Kbyte extent when it becomes full + * to reduce external fragmentation of small directories. + * + * blah, blah, blah, for linear scan of directory in pieces by + * readdir(). + * + * + * case-insensitive directory file system + * + * names are stored in case-sensitive way in leaf entry. + * but stored, searched and compared in case-insensitive (uppercase) order + * (i.e., both search key and entry key are folded for search/compare): + * (note that case-sensitive order is BROKEN in storage, e.g., + * sensitive: Ad, aB, aC, aD -> insensitive: aB, aC, aD, Ad + * + * entries which folds to the same key makes up a equivalent class + * whose members are stored as contiguous cluster (may cross page boundary) + * but whose order is arbitrary and acts as duplicate, e.g., + * abc, Abc, aBc, abC) + * + * once match is found at leaf, requires scan forward/backward + * either for, in case-insensitive search, duplicate + * or for, in case-sensitive search, for exact match + * + * router entry must be created/stored in case-insensitive way + * in internal entry: + * (right most key of left page and left most key of right page + * are folded, and its suffix compression is propagated as router + * key in parent) + * (e.g., if split occurs and , trather than + * should be made the router key for the split) + * + * case-insensitive search: + * + * fold search key; + * + * case-insensitive search of B-tree: + * for internal entry, router key is already folded; + * for leaf entry, fold the entry key before comparison. + * + * if (leaf entry case-insensitive match found) + * if (next entry satisfies case-insensitive match) + * return EDUPLICATE; + * if (prev entry satisfies case-insensitive match) + * return EDUPLICATE; + * return match; + * else + * return no match; + * + * serialization: + * target directory inode lock is being held on entry/exit + * of all main directory service routines. + * + * log based recovery: + */ + +#include +#include +#include +#include "jfs_incore.h" +#include "jfs_superblock.h" +#include "jfs_filsys.h" +#include "jfs_metapage.h" +#include "jfs_dmap.h" +#include "jfs_unicode.h" +#include "jfs_debug.h" + +/* dtree split parameter */ +typedef struct { + metapage_t *mp; + s16 index; + s16 nslot; + component_t *key; + ddata_t *data; + pxdlist_t *pxdlist; +} dtsplit_t; + +#define DT_PAGE(IP, MP) BT_PAGE(IP, MP, dtpage_t, i_dtroot) + +/* get page buffer for specified block address */ +#define DT_GETPAGE(IP, BN, MP, SIZE, P, RC)\ +{\ + BT_GETPAGE(IP, BN, MP, dtpage_t, SIZE, P, RC, i_dtroot)\ + if (!(RC))\ + {\ + if (((P)->header.nextindex > (((BN)==0)?DTROOTMAXSLOT:(P)->header.maxslot)) ||\ + ((BN) && ((P)->header.maxslot > DTPAGEMAXSLOT)))\ + {\ + jERROR(1,("DT_GETPAGE: dtree page corrupt\n"));\ + BT_PUTPAGE(MP);\ + updateSuper((IP)->i_sb, FM_DIRTY);\ + MP = NULL;\ + RC = EIO;\ + }\ + }\ +} + +/* for consistency */ +#define DT_PUTPAGE(MP) BT_PUTPAGE(MP) + +#define DT_GETSEARCH(IP, LEAF, BN, MP, P, INDEX) \ + BT_GETSEARCH(IP, LEAF, BN, MP, dtpage_t, P, INDEX, i_dtroot) + +/* + * forward references + */ +static int dtSplitUp(tid_t tid, struct inode *ip, + dtsplit_t * split, btstack_t * btstack); + +static int dtSplitPage(tid_t tid, struct inode *ip, dtsplit_t * split, + metapage_t ** rmpp, dtpage_t ** rpp, pxd_t * rxdp); + +static int dtExtendPage(tid_t tid, struct inode *ip, + dtsplit_t * split, btstack_t * btstack); + +static int dtSplitRoot(tid_t tid, struct inode *ip, + dtsplit_t * split, metapage_t ** rmpp); + +static int dtDeleteUp(tid_t tid, struct inode *ip, metapage_t * fmp, + dtpage_t * fp, btstack_t * btstack); + +static int dtSearchNode(struct inode *ip, + s64 lmxaddr, pxd_t * kpxd, btstack_t * btstack); + +static int dtRelink(tid_t tid, struct inode *ip, dtpage_t * p); + +static int dtReadFirst(struct inode *ip, btstack_t * btstack); + +static int dtReadNext(struct inode *ip, + loff_t * offset, btstack_t * btstack); + +static int dtCompare(component_t * key, dtpage_t * p, int si); + +static int ciCompare(component_t * key, dtpage_t * p, int si, int flag); + +static void dtGetKey(dtpage_t * p, int i, component_t * key, int flag); + +static void ciGetLeafPrefixKey(dtpage_t * lp, int li, dtpage_t * rp, + int ri, component_t * key, int flag); + +static void dtInsertEntry(dtpage_t * p, int index, component_t * key, + ddata_t * data, dtlock_t ** dtlock); + +static void dtMoveEntry(dtpage_t * sp, int si, dtpage_t * dp, + dtlock_t ** sdtlock, dtlock_t ** ddtlock, + int do_index); + +static void dtDeleteEntry(dtpage_t * p, int fi, dtlock_t ** dtlock); + +static void dtTruncateEntry(dtpage_t * p, int ti, dtlock_t ** dtlock); + +static void dtLinelockFreelist(dtpage_t * p, int m, dtlock_t ** dtlock); + +#define ciToUpper(c) UniStrupr((c)->name) + +/* + * find_index() + * + * Returns dtree page containing directory table entry for specified + * index and pointer to its entry. + * + * mp must be released by caller. + */ +static dir_table_slot_t *find_index(struct inode *ip, u32 index, + metapage_t ** mp) +{ + struct jfs_inode_info *jfs_ip = JFS_IP(ip); + s64 blkno; + s64 offset; + int page_offset; + dir_table_slot_t *slot; + static int maxWarnings = 10; + + if (index < 2) { + if (maxWarnings) { + jERROR(1, ("find_entry called with index = %d\n", + index)); + maxWarnings--; + } + return 0; + } + + if (index >= jfs_ip->next_index) { + jFYI(1, ("find_entry called with index >= next_index\n")); + return 0; + } + + if (jfs_ip->next_index <= (MAX_INLINE_DIRTABLE_ENTRY + 1)) { + /* + * Inline directory table + */ + *mp = 0; + slot = &jfs_ip->i_dirtable[index - 2]; + } else { + offset = (index - 2) * sizeof(dir_table_slot_t); + page_offset = offset & (PSIZE - 1); + blkno = ((offset + 1) >> L2PSIZE) << + JFS_SBI(ip->i_sb)->l2nbperpage; + + if (*mp && ((*mp)->index != blkno)) { + release_metapage(*mp); + *mp = 0; + } + if (*mp == 0) + *mp = read_metapage(ip, blkno, PSIZE, 0); + if (*mp == 0) { + jERROR(1, + ("free_index: error reading directory table\n")); + return 0; + } + + slot = + (dir_table_slot_t *) ((char *) (*mp)->data + + page_offset); + } + return slot; +} + +static inline void lock_index(tid_t tid, struct inode *ip, metapage_t * mp, + u32 index) +{ + tlock_t *tlck; + linelock_t *llck; + lv_t *lv; + + tlck = txLock(tid, ip, mp, tlckDATA); + llck = (linelock_t *) tlck->lock; + + if (llck->index >= llck->maxcnt) + llck = txLinelock(llck); + lv = &llck->lv[llck->index]; + + /* + * Linelock slot size is twice the size of directory table + * slot size. 512 entries per page. + */ + lv->offset = ((index - 2) & 511) >> 1; + lv->length = 1; + llck->index++; +} + +/* + * add_index() + * + * Adds an entry to the directory index table. This is used to provide + * each directory entry with a persistent index in which to resume + * directory traversals + */ +static u32 add_index(tid_t tid, struct inode *ip, s64 bn, int slot) +{ + struct super_block *sb = ip->i_sb; + struct jfs_sb_info *sbi = JFS_SBI(sb); + struct jfs_inode_info *jfs_ip = JFS_IP(ip); + u64 blkno; + dir_table_slot_t *dirtab_slot; + u32 index; + linelock_t *llck; + lv_t *lv; + metapage_t *mp; + s64 offset; + uint page_offset; + int rc; + tlock_t *tlck; + s64 xaddr; + + ASSERT(DO_INDEX(ip)); + + if (jfs_ip->next_index < 2) { + jERROR(1, ("next_index = %d. Please fix this!\n", + jfs_ip->next_index)); + jfs_ip->next_index = 2; + } + + index = jfs_ip->next_index++; + + if (index <= MAX_INLINE_DIRTABLE_ENTRY) { + /* + * i_size reflects size of index table, or 8 bytes per entry. + */ + ip->i_size = (loff_t) (index - 1) << 3; + + /* + * dir table fits inline within inode + */ + dirtab_slot = &jfs_ip->i_dirtable[index-2]; + dirtab_slot->flag = DIR_INDEX_VALID; + dirtab_slot->slot = slot; + DTSaddress(dirtab_slot, bn); + + set_cflag(COMMIT_Dirtable, ip); + + return index; + } + if (index == (MAX_INLINE_DIRTABLE_ENTRY + 1)) { + /* + * It's time to move the inline table to an external + * page and begin to build the xtree + */ + + /* + * Save the table, we're going to overwrite it with the + * xtree root + */ + dir_table_slot_t temp_table[12]; + memcpy(temp_table, &jfs_ip->i_dirtable, sizeof(temp_table)); + + /* + * Initialize empty x-tree + */ + xtInitRoot(tid, ip); + + /* + * Allocate the first block & add it to the xtree + */ + xaddr = 0; + if ((rc = + xtInsert(tid, ip, 0, 0, sbi->nbperpage, + &xaddr, 0))) { + jFYI(1, ("add_index: xtInsert failed!\n")); + return -1; + } + ip->i_size = PSIZE; + ip->i_blocks += LBLK2PBLK(sb, sbi->nbperpage); + + if ((mp = get_metapage(ip, 0, ip->i_blksize, 0)) == 0) { + jERROR(1, ("add_index: get_metapage failed!\n")); + xtTruncate(tid, ip, 0, COMMIT_PWMAP); + return -1; + } + tlck = txLock(tid, ip, mp, tlckDATA); + llck = (linelock_t *) & tlck->lock; + ASSERT(llck->index == 0); + lv = &llck->lv[0]; + + lv->offset = 0; + lv->length = 6; /* tlckDATA slot size is 16 bytes */ + llck->index++; + + memcpy(mp->data, temp_table, sizeof(temp_table)); + + mark_metapage_dirty(mp); + release_metapage(mp); + + /* + * Logging is now directed by xtree tlocks + */ + clear_cflag(COMMIT_Dirtable, ip); + } + + offset = (index - 2) * sizeof(dir_table_slot_t); + page_offset = offset & (PSIZE - 1); + blkno = ((offset + 1) >> L2PSIZE) << sbi->l2nbperpage; + if (page_offset == 0) { + /* + * This will be the beginning of a new page + */ + xaddr = 0; + if ((rc = + xtInsert(tid, ip, 0, blkno, sbi->nbperpage, + &xaddr, 0))) { + jFYI(1, ("add_index: xtInsert failed!\n")); + jfs_ip->next_index--; + return -1; + } + ip->i_size += PSIZE; + ip->i_blocks += LBLK2PBLK(sb, sbi->nbperpage); + + if ((mp = get_metapage(ip, blkno, PSIZE, 0))) + memset(mp->data, 0, PSIZE); /* Just looks better */ + else + xtTruncate(tid, ip, offset, COMMIT_PWMAP); + } else + mp = read_metapage(ip, blkno, PSIZE, 0); + + if (mp == 0) { + jERROR(1, ("add_index: get/read_metapage failed!\n")); + return -1; + } + + lock_index(tid, ip, mp, index); + + dirtab_slot = + (dir_table_slot_t *) ((char *) mp->data + page_offset); + dirtab_slot->flag = DIR_INDEX_VALID; + dirtab_slot->slot = slot; + DTSaddress(dirtab_slot, bn); + + mark_metapage_dirty(mp); + release_metapage(mp); + + return index; +} + +/* + * free_index() + * + * Marks an entry to the directory index table as free. + */ +static void free_index(tid_t tid, struct inode *ip, u32 index, u32 next) +{ + dir_table_slot_t *dirtab_slot; + metapage_t *mp = 0; + + dirtab_slot = find_index(ip, index, &mp); + + if (dirtab_slot == 0) + return; + + dirtab_slot->flag = DIR_INDEX_FREE; + dirtab_slot->slot = dirtab_slot->addr1 = 0; + dirtab_slot->addr2 = cpu_to_le32(next); + + if (mp) { + lock_index(tid, ip, mp, index); + mark_metapage_dirty(mp); + release_metapage(mp); + } else + set_cflag(COMMIT_Dirtable, ip); +} + +/* + * modify_index() + * + * Changes an entry in the directory index table + */ +static void modify_index(tid_t tid, struct inode *ip, u32 index, s64 bn, + int slot, metapage_t ** mp) +{ + dir_table_slot_t *dirtab_slot; + + dirtab_slot = find_index(ip, index, mp); + + if (dirtab_slot == 0) + return; + + DTSaddress(dirtab_slot, bn); + dirtab_slot->slot = slot; + + if (*mp) { + lock_index(tid, ip, *mp, index); + mark_metapage_dirty(*mp); + } else + set_cflag(COMMIT_Dirtable, ip); +} + +/* + * get_index() + * + * reads a directory table slot + */ +static int get_index(struct inode *ip, u32 index, + dir_table_slot_t * dirtab_slot) +{ + metapage_t *mp = 0; + dir_table_slot_t *slot; + + slot = find_index(ip, index, &mp); + if (slot == 0) { + return -EIO; + } + + memcpy(dirtab_slot, slot, sizeof(dir_table_slot_t)); + + if (mp) + release_metapage(mp); + + return 0; +} + +/* + * dtSearch() + * + * function: + * Search for the entry with specified key + * + * parameter: + * + * return: 0 - search result on stack, leaf page pinned; + * errno - I/O error + */ +int dtSearch(struct inode *ip, + component_t * key, ino_t * data, btstack_t * btstack, int flag) +{ + int rc = 0; + int cmp = 1; /* init for empty page */ + s64 bn; + metapage_t *mp; + dtpage_t *p; + s8 *stbl; + int base, index, lim; + btframe_t *btsp; + pxd_t *pxd; + int psize = 288; /* initial in-line directory */ + ino_t inumber; + component_t ciKey; + struct super_block *sb = ip->i_sb; + + ciKey.name = + (wchar_t *) kmalloc((JFS_NAME_MAX + 1) * sizeof(wchar_t), + GFP_NOFS); + if (ciKey.name == 0) { + rc = ENOMEM; + goto dtSearch_Exit2; + } + + + /* uppercase search key for c-i directory */ + UniStrcpy(ciKey.name, key->name); + ciKey.namlen = key->namlen; + + /* only uppercase if case-insensitive support is on */ + if ((JFS_SBI(sb)->mntflag & JFS_OS2) == JFS_OS2) { + ciToUpper(&ciKey); + } + BT_CLR(btstack); /* reset stack */ + + /* init level count for max pages to split */ + btstack->nsplit = 1; + + /* + * search down tree from root: + * + * between two consecutive entries of and of + * internal page, child page Pi contains entry with k, Ki <= K < Kj. + * + * if entry with search key K is not found + * internal page search find the entry with largest key Ki + * less than K which point to the child page to search; + * leaf page search find the entry with smallest key Kj + * greater than K so that the returned index is the position of + * the entry to be shifted right for insertion of new entry. + * for empty tree, search key is greater than any key of the tree. + * + * by convention, root bn = 0. + */ + for (bn = 0;;) { + /* get/pin the page to search */ + DT_GETPAGE(ip, bn, mp, psize, p, rc); + if (rc) + goto dtSearch_Exit1; + + /* get sorted entry table of the page */ + stbl = DT_GETSTBL(p); + + /* + * binary search with search key K on the current page. + */ + for (base = 0, lim = p->header.nextindex; lim; lim >>= 1) { + index = base + (lim >> 1); + + if (p->header.flag & BT_LEAF) { + /* uppercase leaf name to compare */ + cmp = + ciCompare(&ciKey, p, stbl[index], + JFS_SBI(sb)->mntflag); + } else { + /* router key is in uppercase */ + + cmp = dtCompare(&ciKey, p, stbl[index]); + + + } + if (cmp == 0) { + /* + * search hit + */ + /* search hit - leaf page: + * return the entry found + */ + if (p->header.flag & BT_LEAF) { + inumber = le32_to_cpu( + ((ldtentry_t *) & p->slot[stbl[index]])->inumber); + + /* + * search for JFS_LOOKUP + */ + if (flag == JFS_LOOKUP) { + *data = inumber; + rc = 0; + goto out; + } + + /* + * search for JFS_CREATE + */ + if (flag == JFS_CREATE) { + *data = inumber; + rc = EEXIST; + goto out; + } + + /* + * search for JFS_REMOVE or JFS_RENAME + */ + if ((flag == JFS_REMOVE || + flag == JFS_RENAME) && + *data != inumber) { + rc = ESTALE; + goto out; + } + + /* + * JFS_REMOVE|JFS_FINDDIR|JFS_RENAME + */ + /* save search result */ + *data = inumber; + btsp = btstack->top; + btsp->bn = bn; + btsp->index = index; + btsp->mp = mp; + + rc = 0; + goto dtSearch_Exit1; + } + + /* search hit - internal page: + * descend/search its child page + */ + goto getChild; + } + + if (cmp > 0) { + base = index + 1; + --lim; + } + } + + /* + * search miss + * + * base is the smallest index with key (Kj) greater than + * search key (K) and may be zero or (maxindex + 1) index. + */ + /* + * search miss - leaf page + * + * return location of entry (base) where new entry with + * search key K is to be inserted. + */ + if (p->header.flag & BT_LEAF) { + /* + * search for JFS_LOOKUP, JFS_REMOVE, or JFS_RENAME + */ + if (flag == JFS_LOOKUP || flag == JFS_REMOVE || + flag == JFS_RENAME) { + rc = ENOENT; + goto out; + } + + /* + * search for JFS_CREATE|JFS_FINDDIR: + * + * save search result + */ + *data = 0; + btsp = btstack->top; + btsp->bn = bn; + btsp->index = base; + btsp->mp = mp; + + rc = 0; + goto dtSearch_Exit1; + } + + /* + * search miss - internal page + * + * if base is non-zero, decrement base by one to get the parent + * entry of the child page to search. + */ + index = base ? base - 1 : base; + + /* + * go down to child page + */ + getChild: + /* update max. number of pages to split */ + if (btstack->nsplit >= 8) { + /* Something's corrupted, mark filesytem dirty so + * chkdsk will fix it. + */ + jERROR(1, ("stack overrun in dtSearch!\n")); + updateSuper(sb, FM_DIRTY); + rc = EIO; + goto out; + } + btstack->nsplit++; + + /* push (bn, index) of the parent page/entry */ + BT_PUSH(btstack, bn, index); + + /* get the child page block number */ + pxd = (pxd_t *) & p->slot[stbl[index]]; + bn = addressPXD(pxd); + psize = lengthPXD(pxd) << JFS_SBI(ip->i_sb)->l2bsize; + + /* unpin the parent page */ + DT_PUTPAGE(mp); + } + + out: + DT_PUTPAGE(mp); + + dtSearch_Exit1: + + kfree(ciKey.name); + + dtSearch_Exit2: + + return rc; +} + + +/* + * dtInsert() + * + * function: insert an entry to directory tree + * + * parameter: + * + * return: 0 - success; + * errno - failure; + */ +int dtInsert(tid_t tid, struct inode *ip, + component_t * name, ino_t * fsn, btstack_t * btstack) +{ + int rc = 0; + metapage_t *mp; /* meta-page buffer */ + dtpage_t *p; /* base B+-tree index page */ + s64 bn; + int index; + dtsplit_t split; /* split information */ + ddata_t data; + dtlock_t *dtlck; + int n; + tlock_t *tlck; + lv_t *lv; + + /* + * retrieve search result + * + * dtSearch() returns (leaf page pinned, index at which to insert). + * n.b. dtSearch() may return index of (maxindex + 1) of + * the full page. + */ + DT_GETSEARCH(ip, btstack->top, bn, mp, p, index); + + /* + * insert entry for new key + */ + if (DO_INDEX(ip)) { + if (JFS_IP(ip)->next_index == DIREND) { + DT_PUTPAGE(mp); + return EMLINK; + } + n = NDTLEAF(name->namlen); + data.leaf.tid = tid; + data.leaf.ip = ip; + } else { + n = NDTLEAF_LEGACY(name->namlen); + data.leaf.ip = 0; /* signifies legacy directory format */ + } + data.leaf.ino = cpu_to_le32(*fsn); + + /* + * leaf page does not have enough room for new entry: + * + * extend/split the leaf page; + * + * dtSplitUp() will insert the entry and unpin the leaf page. + */ + if (n > p->header.freecnt) { + split.mp = mp; + split.index = index; + split.nslot = n; + split.key = name; + split.data = &data; + rc = dtSplitUp(tid, ip, &split, btstack); + return rc; + } + + /* + * leaf page does have enough room for new entry: + * + * insert the new data entry into the leaf page; + */ + BT_MARK_DIRTY(mp, ip); + /* + * acquire a transaction lock on the leaf page + */ + tlck = txLock(tid, ip, mp, tlckDTREE | tlckENTRY); + dtlck = (dtlock_t *) & tlck->lock; + ASSERT(dtlck->index == 0); + lv = (lv_t *) & dtlck->lv[0]; + + /* linelock header */ + lv->offset = 0; + lv->length = 1; + dtlck->index++; + + dtInsertEntry(p, index, name, &data, &dtlck); + + /* linelock stbl of non-root leaf page */ + if (!(p->header.flag & BT_ROOT)) { + if (dtlck->index >= dtlck->maxcnt) + dtlck = (dtlock_t *) txLinelock(dtlck); + lv = (lv_t *) & dtlck->lv[dtlck->index]; + n = index >> L2DTSLOTSIZE; + lv->offset = p->header.stblindex + n; + lv->length = + ((p->header.nextindex - 1) >> L2DTSLOTSIZE) - n + 1; + dtlck->index++; + } + + /* unpin the leaf page */ + DT_PUTPAGE(mp); + + return 0; +} + + +/* + * dtSplitUp() + * + * function: propagate insertion bottom up; + * + * parameter: + * + * return: 0 - success; + * errno - failure; + * leaf page unpinned; + */ +static int dtSplitUp(tid_t tid, + struct inode *ip, dtsplit_t * split, btstack_t * btstack) +{ + struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb); + int rc = 0; + metapage_t *smp; + dtpage_t *sp; /* split page */ + metapage_t *rmp; + dtpage_t *rp; /* new right page split from sp */ + pxd_t rpxd; /* new right page extent descriptor */ + metapage_t *lmp; + dtpage_t *lp; /* left child page */ + int skip; /* index of entry of insertion */ + btframe_t *parent; /* parent page entry on traverse stack */ + s64 xaddr, nxaddr; + int xlen, xsize; + pxdlist_t pxdlist; + pxd_t *pxd; + component_t key = { 0, 0 }; + ddata_t *data = split->data; + int n; + dtlock_t *dtlck; + tlock_t *tlck; + lv_t *lv; + + /* get split page */ + smp = split->mp; + sp = DT_PAGE(ip, smp); + + key.name = + (wchar_t *) kmalloc((JFS_NAME_MAX + 2) * sizeof(wchar_t), + GFP_NOFS); + if (key.name == 0) { + DT_PUTPAGE(smp); + rc = ENOMEM; + goto dtSplitUp_Exit; + } + + /* + * split leaf page + * + * The split routines insert the new entry, and + * acquire txLock as appropriate. + */ + /* + * split root leaf page: + */ + if (sp->header.flag & BT_ROOT) { + /* + * allocate a single extent child page + */ + xlen = 1; + n = sbi->bsize >> L2DTSLOTSIZE; + n -= (n + 31) >> L2DTSLOTSIZE; /* stbl size */ + n -= DTROOTMAXSLOT - sp->header.freecnt; /* header + entries */ + if (n <= split->nslot) + xlen++; + if ((rc = dbAlloc(ip, 0, (s64) xlen, &xaddr))) + goto freeKeyName; + + pxdlist.maxnpxd = 1; + pxdlist.npxd = 0; + pxd = &pxdlist.pxd[0]; + PXDaddress(pxd, xaddr); + PXDlength(pxd, xlen); + split->pxdlist = &pxdlist; + rc = dtSplitRoot(tid, ip, split, &rmp); + + DT_PUTPAGE(rmp); + DT_PUTPAGE(smp); + + goto freeKeyName; + } + + /* + * extend first leaf page + * + * extend the 1st extent if less than buffer page size + * (dtExtendPage() reurns leaf page unpinned) + */ + pxd = &sp->header.self; + xlen = lengthPXD(pxd); + xsize = xlen << sbi->l2bsize; + if (xsize < PSIZE) { + xaddr = addressPXD(pxd); + n = xsize >> L2DTSLOTSIZE; + n -= (n + 31) >> L2DTSLOTSIZE; /* stbl size */ + if ((n + sp->header.freecnt) <= split->nslot) + n = xlen + (xlen << 1); + else + n = xlen; + if ((rc = dbReAlloc(sbi->ipbmap, xaddr, (s64) xlen, + (s64) n, &nxaddr))) + goto extendOut; + + pxdlist.maxnpxd = 1; + pxdlist.npxd = 0; + pxd = &pxdlist.pxd[0]; + PXDaddress(pxd, nxaddr) + PXDlength(pxd, xlen + n); + split->pxdlist = &pxdlist; + if ((rc = dtExtendPage(tid, ip, split, btstack))) { + nxaddr = addressPXD(pxd); + if (xaddr != nxaddr) { + /* free relocated extent */ + xlen = lengthPXD(pxd); + dbFree(ip, nxaddr, (s64) xlen); + } else { + /* free extended delta */ + xlen = lengthPXD(pxd) - n; + xaddr = addressPXD(pxd) + xlen; + dbFree(ip, xaddr, (s64) n); + } + } + + extendOut: + DT_PUTPAGE(smp); + goto freeKeyName; + } + + /* + * split leaf page into and a new right page . + * + * return pinned and its extent descriptor + */ + /* + * allocate new directory page extent and + * new index page(s) to cover page split(s) + * + * allocation hint: ? + */ + n = btstack->nsplit; + pxdlist.maxnpxd = pxdlist.npxd = 0; + xlen = sbi->nbperpage; + for (pxd = pxdlist.pxd; n > 0; n--, pxd++) { + if ((rc = dbAlloc(ip, 0, (s64) xlen, &xaddr)) == 0) { + PXDaddress(pxd, xaddr); + PXDlength(pxd, xlen); + pxdlist.maxnpxd++; + continue; + } + + DT_PUTPAGE(smp); + + /* undo allocation */ + goto splitOut; + } + + split->pxdlist = &pxdlist; + if ((rc = dtSplitPage(tid, ip, split, &rmp, &rp, &rpxd))) { + DT_PUTPAGE(smp); + + /* undo allocation */ + goto splitOut; + } + + /* + * propagate up the router entry for the leaf page just split + * + * insert a router entry for the new page into the parent page, + * propagate the insert/split up the tree by walking back the stack + * of (bn of parent page, index of child page entry in parent page) + * that were traversed during the search for the page that split. + * + * the propagation of insert/split up the tree stops if the root + * splits or the page inserted into doesn't have to split to hold + * the new entry. + * + * the parent entry for the split page remains the same, and + * a new entry is inserted at its right with the first key and + * block number of the new right page. + * + * There are a maximum of 4 pages pinned at any time: + * two children, left parent and right parent (when the parent splits). + * keep the child pages pinned while working on the parent. + * make sure that all pins are released at exit. + */ + while ((parent = BT_POP(btstack)) != NULL) { + /* parent page specified by stack frame */ + + /* keep current child pages (, ) pinned */ + lmp = smp; + lp = sp; + + /* + * insert router entry in parent for new right child page + */ + /* get the parent page */ + DT_GETPAGE(ip, parent->bn, smp, PSIZE, sp, rc); + if (rc) { + DT_PUTPAGE(lmp); + DT_PUTPAGE(rmp); + goto splitOut; + } + + /* + * The new key entry goes ONE AFTER the index of parent entry, + * because the split was to the right. + */ + skip = parent->index + 1; + + /* + * compute the key for the router entry + * + * key suffix compression: + * for internal pages that have leaf pages as children, + * retain only what's needed to distinguish between + * the new entry and the entry on the page to its left. + * If the keys compare equal, retain the entire key. + * + * note that compression is performed only at computing + * router key at the lowest internal level. + * further compression of the key between pairs of higher + * level internal pages loses too much information and + * the search may fail. + * (e.g., two adjacent leaf pages of {a, ..., x} {xx, ...,} + * results in two adjacent parent entries (a)(xx). + * if split occurs between these two entries, and + * if compression is applied, the router key of parent entry + * of right page (x) will divert search for x into right + * subtree and miss x in the left subtree.) + * + * the entire key must be retained for the next-to-leftmost + * internal key at any level of the tree, or search may fail + * (e.g., ?) + */ + switch (rp->header.flag & BT_TYPE) { + case BT_LEAF: + /* + * compute the length of prefix for suffix compression + * between last entry of left page and first entry + * of right page + */ + if ((sp->header.flag & BT_ROOT && skip > 1) || + sp->header.prev != 0 || skip > 1) { + /* compute uppercase router prefix key */ + ciGetLeafPrefixKey(lp, + lp->header.nextindex - 1, + rp, 0, &key, sbi->mntflag); + } else { + /* next to leftmost entry of + lowest internal level */ + + /* compute uppercase router key */ + dtGetKey(rp, 0, &key, sbi->mntflag); + key.name[key.namlen] = 0; + + if ((sbi->mntflag & JFS_OS2) == JFS_OS2) + ciToUpper(&key); + } + + n = NDTINTERNAL(key.namlen); + break; + + case BT_INTERNAL: + dtGetKey(rp, 0, &key, sbi->mntflag); + n = NDTINTERNAL(key.namlen); + break; + + default: + jERROR(2, ("dtSplitUp(): UFO!\n")); + break; + } + + /* unpin left child page */ + DT_PUTPAGE(lmp); + + /* + * compute the data for the router entry + */ + data->xd = rpxd; /* child page xd */ + + /* + * parent page is full - split the parent page + */ + if (n > sp->header.freecnt) { + /* init for parent page split */ + split->mp = smp; + split->index = skip; /* index at insert */ + split->nslot = n; + split->key = &key; + /* split->data = data; */ + + /* unpin right child page */ + DT_PUTPAGE(rmp); + + /* The split routines insert the new entry, + * acquire txLock as appropriate. + * return pinned and its block number . + */ + rc = (sp->header.flag & BT_ROOT) ? + dtSplitRoot(tid, ip, split, &rmp) : + dtSplitPage(tid, ip, split, &rmp, &rp, &rpxd); + if (rc) { + DT_PUTPAGE(smp); + goto splitOut; + } + + /* smp and rmp are pinned */ + } + /* + * parent page is not full - insert router entry in parent page + */ + else { + BT_MARK_DIRTY(smp, ip); + /* + * acquire a transaction lock on the parent page + */ + tlck = txLock(tid, ip, smp, tlckDTREE | tlckENTRY); + dtlck = (dtlock_t *) & tlck->lock; + ASSERT(dtlck->index == 0); + lv = (lv_t *) & dtlck->lv[0]; + + /* linelock header */ + lv->offset = 0; + lv->length = 1; + dtlck->index++; + + /* linelock stbl of non-root parent page */ + if (!(sp->header.flag & BT_ROOT)) { + lv++; + n = skip >> L2DTSLOTSIZE; + lv->offset = sp->header.stblindex + n; + lv->length = + ((sp->header.nextindex - + 1) >> L2DTSLOTSIZE) - n + 1; + dtlck->index++; + } + + dtInsertEntry(sp, skip, &key, data, &dtlck); + + /* exit propagate up */ + break; + } + } + + /* unpin current split and its right page */ + DT_PUTPAGE(smp); + DT_PUTPAGE(rmp); + + /* + * free remaining extents allocated for split + */ + splitOut: + n = pxdlist.npxd; + pxd = &pxdlist.pxd[n]; + for (; n < pxdlist.maxnpxd; n++, pxd++) + dbFree(ip, addressPXD(pxd), (s64) lengthPXD(pxd)); + + freeKeyName: + kfree(key.name); + + dtSplitUp_Exit: + + return rc; +} + + +/* + * dtSplitPage() + * + * function: Split a non-root page of a btree. + * + * parameter: + * + * return: 0 - success; + * errno - failure; + * return split and new page pinned; + */ +static int dtSplitPage(tid_t tid, struct inode *ip, dtsplit_t * split, + metapage_t ** rmpp, dtpage_t ** rpp, pxd_t * rpxdp) +{ + struct super_block *sb = ip->i_sb; + int rc = 0; + metapage_t *smp; + dtpage_t *sp; + metapage_t *rmp; + dtpage_t *rp; /* new right page allocated */ + s64 rbn; /* new right page block number */ + metapage_t *mp; + dtpage_t *p; + s64 nextbn; + pxdlist_t *pxdlist; + pxd_t *pxd; + int skip, nextindex, half, left, nxt, off, si; + ldtentry_t *ldtentry; + idtentry_t *idtentry; + u8 *stbl; + dtslot_t *f; + int fsi, stblsize; + int n; + dtlock_t *sdtlck, *rdtlck; + tlock_t *tlck; + dtlock_t *dtlck; + lv_t *slv, *rlv, *lv; + + /* get split page */ + smp = split->mp; + sp = DT_PAGE(ip, smp); + + /* + * allocate the new right page for the split + */ + pxdlist = split->pxdlist; + pxd = &pxdlist->pxd[pxdlist->npxd]; + pxdlist->npxd++; + rbn = addressPXD(pxd); + rmp = get_metapage(ip, rbn, PSIZE, 1); + if (rmp == NULL) + return EIO; + + jEVENT(0, + ("dtSplitPage: ip:0x%p smp:0x%p rmp:0x%p\n", ip, smp, rmp)); + + BT_MARK_DIRTY(rmp, ip); + /* + * acquire a transaction lock on the new right page + */ + tlck = txLock(tid, ip, rmp, tlckDTREE | tlckNEW); + rdtlck = (dtlock_t *) & tlck->lock; + + rp = (dtpage_t *) rmp->data; + *rpp = rp; + rp->header.self = *pxd; + + BT_MARK_DIRTY(smp, ip); + /* + * acquire a transaction lock on the split page + * + * action: + */ + tlck = txLock(tid, ip, smp, tlckDTREE | tlckENTRY); + sdtlck = (dtlock_t *) & tlck->lock; + + /* linelock header of split page */ + ASSERT(sdtlck->index == 0); + slv = (lv_t *) & sdtlck->lv[0]; + slv->offset = 0; + slv->length = 1; + sdtlck->index++; + + /* + * initialize/update sibling pointers between sp and rp + */ + nextbn = le64_to_cpu(sp->header.next); + rp->header.next = cpu_to_le64(nextbn); + rp->header.prev = cpu_to_le64(addressPXD(&sp->header.self)); + sp->header.next = cpu_to_le64(rbn); + + /* + * initialize new right page + */ + rp->header.flag = sp->header.flag; + + /* compute sorted entry table at start of extent data area */ + rp->header.nextindex = 0; + rp->header.stblindex = 1; + + n = PSIZE >> L2DTSLOTSIZE; + rp->header.maxslot = n; + stblsize = (n + 31) >> L2DTSLOTSIZE; /* in unit of slot */ + + /* init freelist */ + fsi = rp->header.stblindex + stblsize; + rp->header.freelist = fsi; + rp->header.freecnt = rp->header.maxslot - fsi; + + /* + * sequential append at tail: append without split + * + * If splitting the last page on a level because of appending + * a entry to it (skip is maxentry), it's likely that the access is + * sequential. Adding an empty page on the side of the level is less + * work and can push the fill factor much higher than normal. + * If we're wrong it's no big deal, we'll just do the split the right + * way next time. + * (It may look like it's equally easy to do a similar hack for + * reverse sorted data, that is, split the tree left, + * but it's not. Be my guest.) + */ + if (nextbn == 0 && split->index == sp->header.nextindex) { + /* linelock header + stbl (first slot) of new page */ + rlv = (lv_t *) & rdtlck->lv[rdtlck->index]; + rlv->offset = 0; + rlv->length = 2; + rdtlck->index++; + + /* + * initialize freelist of new right page + */ + f = &rp->slot[fsi]; + for (fsi++; fsi < rp->header.maxslot; f++, fsi++) + f->next = fsi; + f->next = -1; + + /* insert entry at the first entry of the new right page */ + dtInsertEntry(rp, 0, split->key, split->data, &rdtlck); + + goto out; + } + + /* + * non-sequential insert (at possibly middle page) + */ + + /* + * update prev pointer of previous right sibling page; + */ + if (nextbn != 0) { + DT_GETPAGE(ip, nextbn, mp, PSIZE, p, rc); + if (rc) + return rc; + + BT_MARK_DIRTY(mp, ip); + /* + * acquire a transaction lock on the next page + */ + tlck = txLock(tid, ip, mp, tlckDTREE | tlckRELINK); + jEVENT(0, + ("dtSplitPage: tlck = 0x%p, ip = 0x%p, mp=0x%p\n", + tlck, ip, mp)); + dtlck = (dtlock_t *) & tlck->lock; + + /* linelock header of previous right sibling page */ + lv = (lv_t *) & dtlck->lv[dtlck->index]; + lv->offset = 0; + lv->length = 1; + dtlck->index++; + + p->header.prev = cpu_to_le64(rbn); + + DT_PUTPAGE(mp); + } + + /* + * split the data between the split and right pages. + */ + skip = split->index; + half = (PSIZE >> L2DTSLOTSIZE) >> 1; /* swag */ + left = 0; + + /* + * compute fill factor for split pages + * + * traces the next entry to move to rp + * traces the next entry to stay in sp + */ + stbl = (u8 *) & sp->slot[sp->header.stblindex]; + nextindex = sp->header.nextindex; + for (nxt = off = 0; nxt < nextindex; ++off) { + if (off == skip) + /* check for fill factor with new entry size */ + n = split->nslot; + else { + si = stbl[nxt]; + switch (sp->header.flag & BT_TYPE) { + case BT_LEAF: + ldtentry = (ldtentry_t *) & sp->slot[si]; + if (DO_INDEX(ip)) + n = NDTLEAF(ldtentry->namlen); + else + n = NDTLEAF_LEGACY(ldtentry-> + namlen); + break; + + case BT_INTERNAL: + idtentry = (idtentry_t *) & sp->slot[si]; + n = NDTINTERNAL(idtentry->namlen); + break; + + default: + break; + } + + ++nxt; /* advance to next entry to move in sp */ + } + + left += n; + if (left >= half) + break; + } + + /* poins to the 1st entry to move */ + + /* + * move entries to right page + * + * dtMoveEntry() initializes rp and reserves entry for insertion + * + * split page moved out entries are linelocked; + * new/right page moved in entries are linelocked; + */ + /* linelock header + stbl of new right page */ + rlv = (lv_t *) & rdtlck->lv[rdtlck->index]; + rlv->offset = 0; + rlv->length = 5; + rdtlck->index++; + + dtMoveEntry(sp, nxt, rp, &sdtlck, &rdtlck, DO_INDEX(ip)); + + sp->header.nextindex = nxt; + + /* + * finalize freelist of new right page + */ + fsi = rp->header.freelist; + f = &rp->slot[fsi]; + for (fsi++; fsi < rp->header.maxslot; f++, fsi++) + f->next = fsi; + f->next = -1; + + /* + * Update directory index table for entries now in right page + */ + if ((rp->header.flag & BT_LEAF) && DO_INDEX(ip)) { + mp = 0; + stbl = DT_GETSTBL(rp); + for (n = 0; n < rp->header.nextindex; n++) { + ldtentry = (ldtentry_t *) & rp->slot[stbl[n]]; + modify_index(tid, ip, le32_to_cpu(ldtentry->index), + rbn, n, &mp); + } + if (mp) + release_metapage(mp); + } + + /* + * the skipped index was on the left page, + */ + if (skip <= off) { + /* insert the new entry in the split page */ + dtInsertEntry(sp, skip, split->key, split->data, &sdtlck); + + /* linelock stbl of split page */ + if (sdtlck->index >= sdtlck->maxcnt) + sdtlck = (dtlock_t *) txLinelock(sdtlck); + slv = (lv_t *) & sdtlck->lv[sdtlck->index]; + n = skip >> L2DTSLOTSIZE; + slv->offset = sp->header.stblindex + n; + slv->length = + ((sp->header.nextindex - 1) >> L2DTSLOTSIZE) - n + 1; + sdtlck->index++; + } + /* + * the skipped index was on the right page, + */ + else { + /* adjust the skip index to reflect the new position */ + skip -= nxt; + + /* insert the new entry in the right page */ + dtInsertEntry(rp, skip, split->key, split->data, &rdtlck); + } + + out: + *rmpp = rmp; + *rpxdp = *pxd; + + ip->i_blocks += LBLK2PBLK(sb, lengthPXD(pxd)); + + jEVENT(0, ("dtSplitPage: ip:0x%p sp:0x%p rp:0x%p\n", ip, sp, rp)); + return 0; +} + + +/* + * dtExtendPage() + * + * function: extend 1st/only directory leaf page + * + * parameter: + * + * return: 0 - success; + * errno - failure; + * return extended page pinned; + */ +static int dtExtendPage(tid_t tid, + struct inode *ip, dtsplit_t * split, btstack_t * btstack) +{ + struct super_block *sb = ip->i_sb; + int rc; + metapage_t *smp, *pmp, *mp; + dtpage_t *sp, *pp; + pxdlist_t *pxdlist; + pxd_t *pxd, *tpxd; + int xlen, xsize; + int newstblindex, newstblsize; + int oldstblindex, oldstblsize; + int fsi, last; + dtslot_t *f; + btframe_t *parent; + int n; + dtlock_t *dtlck; + s64 xaddr, txaddr; + tlock_t *tlck; + pxdlock_t *pxdlock; + lv_t *lv; + uint type; + ldtentry_t *ldtentry; + u8 *stbl; + + /* get page to extend */ + smp = split->mp; + sp = DT_PAGE(ip, smp); + + /* get parent/root page */ + parent = BT_POP(btstack); + DT_GETPAGE(ip, parent->bn, pmp, PSIZE, pp, rc); + if (rc) + return (rc); + + /* + * extend the extent + */ + pxdlist = split->pxdlist; + pxd = &pxdlist->pxd[pxdlist->npxd]; + pxdlist->npxd++; + + xaddr = addressPXD(pxd); + tpxd = &sp->header.self; + txaddr = addressPXD(tpxd); + /* in-place extension */ + if (xaddr == txaddr) { + type = tlckEXTEND; + } + /* relocation */ + else { + type = tlckNEW; + + /* save moved extent descriptor for later free */ + tlck = txMaplock(tid, ip, tlckDTREE | tlckRELOCATE); + pxdlock = (pxdlock_t *) & tlck->lock; + pxdlock->flag = mlckFREEPXD; + pxdlock->pxd = sp->header.self; + pxdlock->index = 1; + + /* + * Update directory index table to reflect new page address + */ + if (DO_INDEX(ip)) { + mp = 0; + stbl = DT_GETSTBL(sp); + for (n = 0; n < sp->header.nextindex; n++) { + ldtentry = + (ldtentry_t *) & sp->slot[stbl[n]]; + modify_index(tid, ip, + le32_to_cpu(ldtentry->index), + xaddr, n, &mp); + } + if (mp) + release_metapage(mp); + } + } + + /* + * extend the page + */ + sp->header.self = *pxd; + + jEVENT(0, + ("dtExtendPage: ip:0x%p smp:0x%p sp:0x%p\n", ip, smp, sp)); + + BT_MARK_DIRTY(smp, ip); + /* + * acquire a transaction lock on the extended/leaf page + */ + tlck = txLock(tid, ip, smp, tlckDTREE | type); + dtlck = (dtlock_t *) & tlck->lock; + lv = (lv_t *) & dtlck->lv[0]; + + /* update buffer extent descriptor of extended page */ + xlen = lengthPXD(pxd); + xsize = xlen << JFS_SBI(sb)->l2bsize; +#ifdef _STILL_TO_PORT + bmSetXD(smp, xaddr, xsize); +#endif /* _STILL_TO_PORT */ + + /* + * copy old stbl to new stbl at start of extended area + */ + oldstblindex = sp->header.stblindex; + oldstblsize = (sp->header.maxslot + 31) >> L2DTSLOTSIZE; + newstblindex = sp->header.maxslot; + n = xsize >> L2DTSLOTSIZE; + newstblsize = (n + 31) >> L2DTSLOTSIZE; + memcpy(&sp->slot[newstblindex], &sp->slot[oldstblindex], + sp->header.nextindex); + + /* + * in-line extension: linelock old area of extended page + */ + if (type == tlckEXTEND) { + /* linelock header */ + lv->offset = 0; + lv->length = 1; + dtlck->index++; + lv++; + + /* linelock new stbl of extended page */ + lv->offset = newstblindex; + lv->length = newstblsize; + } + /* + * relocation: linelock whole relocated area + */ + else { + lv->offset = 0; + lv->length = sp->header.maxslot + newstblsize; + } + + dtlck->index++; + + sp->header.maxslot = n; + sp->header.stblindex = newstblindex; + /* sp->header.nextindex remains the same */ + + /* + * add old stbl region at head of freelist + */ + fsi = oldstblindex; + f = &sp->slot[fsi]; + last = sp->header.freelist; + for (n = 0; n < oldstblsize; n++, fsi++, f++) { + f->next = last; + last = fsi; + } + sp->header.freelist = last; + sp->header.freecnt += oldstblsize; + + /* + * append free region of newly extended area at tail of freelist + */ + /* init free region of newly extended area */ + fsi = n = newstblindex + newstblsize; + f = &sp->slot[fsi]; + for (fsi++; fsi < sp->header.maxslot; f++, fsi++) + f->next = fsi; + f->next = -1; + + /* append new free region at tail of old freelist */ + fsi = sp->header.freelist; + if (fsi == -1) + sp->header.freelist = n; + else { + do { + f = &sp->slot[fsi]; + fsi = f->next; + } while (fsi != -1); + + f->next = n; + } + + sp->header.freecnt += sp->header.maxslot - n; + + /* + * insert the new entry + */ + dtInsertEntry(sp, split->index, split->key, split->data, &dtlck); + + BT_MARK_DIRTY(pmp, ip); + /* + * linelock any freeslots residing in old extent + */ + if (type == tlckEXTEND) { + n = sp->header.maxslot >> 2; + if (sp->header.freelist < n) + dtLinelockFreelist(sp, n, &dtlck); + } + + /* + * update parent entry on the parent/root page + */ + /* + * acquire a transaction lock on the parent/root page + */ + tlck = txLock(tid, ip, pmp, tlckDTREE | tlckENTRY); + dtlck = (dtlock_t *) & tlck->lock; + lv = (lv_t *) & dtlck->lv[dtlck->index]; + + /* linelock parent entry - 1st slot */ + lv->offset = 1; + lv->length = 1; + dtlck->index++; + + /* update the parent pxd for page extension */ + tpxd = (pxd_t *) & pp->slot[1]; + *tpxd = *pxd; + + /* Since the directory might have an EA and/or ACL associated with it + * we need to make sure we take that into account when setting the + * i_nblocks + */ + ip->i_blocks = LBLK2PBLK(ip->i_sb, xlen + + ((JFS_IP(ip)->ea.flag & DXD_EXTENT) ? + lengthDXD(&JFS_IP(ip)->ea) : 0) + + ((JFS_IP(ip)->acl.flag & DXD_EXTENT) ? + lengthDXD(&JFS_IP(ip)->acl) : 0)); + + jEVENT(0, + ("dtExtendPage: ip:0x%p smp:0x%p sp:0x%p\n", ip, smp, sp)); + + + DT_PUTPAGE(pmp); + return 0; +} + + +/* + * dtSplitRoot() + * + * function: + * split the full root page into + * original/root/split page and new right page + * i.e., root remains fixed in tree anchor (inode) and + * the root is copied to a single new right child page + * since root page << non-root page, and + * the split root page contains a single entry for the + * new right child page. + * + * parameter: + * + * return: 0 - success; + * errno - failure; + * return new page pinned; + */ +static int dtSplitRoot(tid_t tid, + struct inode *ip, dtsplit_t * split, metapage_t ** rmpp) +{ + struct super_block *sb = ip->i_sb; + metapage_t *smp; + dtroot_t *sp; + metapage_t *rmp; + dtpage_t *rp; + s64 rbn; + int xlen; + int xsize; + dtslot_t *f; + s8 *stbl; + int fsi, stblsize, n; + idtentry_t *s; + pxd_t *ppxd; + pxdlist_t *pxdlist; + pxd_t *pxd; + dtlock_t *dtlck; + tlock_t *tlck; + lv_t *lv; + + /* get split root page */ + smp = split->mp; + sp = &JFS_IP(ip)->i_dtroot; + + /* + * allocate/initialize a single (right) child page + * + * N.B. at first split, a one (or two) block to fit new entry + * is allocated; at subsequent split, a full page is allocated; + */ + pxdlist = split->pxdlist; + pxd = &pxdlist->pxd[pxdlist->npxd]; + pxdlist->npxd++; + rbn = addressPXD(pxd); + xlen = lengthPXD(pxd); + xsize = xlen << JFS_SBI(sb)->l2bsize; + rmp = get_metapage(ip, rbn, xsize, 1); + rp = rmp->data; + + BT_MARK_DIRTY(rmp, ip); + /* + * acquire a transaction lock on the new right page + */ + tlck = txLock(tid, ip, rmp, tlckDTREE | tlckNEW); + dtlck = (dtlock_t *) & tlck->lock; + + rp->header.flag = + (sp->header.flag & BT_LEAF) ? BT_LEAF : BT_INTERNAL; + rp->header.self = *pxd; + + /* initialize sibling pointers */ + rp->header.next = 0; + rp->header.prev = 0; + + /* + * move in-line root page into new right page extent + */ + /* linelock header + copied entries + new stbl (1st slot) in new page */ + ASSERT(dtlck->index == 0); + lv = (lv_t *) & dtlck->lv[0]; + lv->offset = 0; + lv->length = 10; /* 1 + 8 + 1 */ + dtlck->index++; + + n = xsize >> L2DTSLOTSIZE; + rp->header.maxslot = n; + stblsize = (n + 31) >> L2DTSLOTSIZE; + + /* copy old stbl to new stbl at start of extended area */ + rp->header.stblindex = DTROOTMAXSLOT; + stbl = (s8 *) & rp->slot[DTROOTMAXSLOT]; + memcpy(stbl, sp->header.stbl, sp->header.nextindex); + rp->header.nextindex = sp->header.nextindex; + + /* copy old data area to start of new data area */ + memcpy(&rp->slot[1], &sp->slot[1], IDATASIZE); + + /* + * append free region of newly extended area at tail of freelist + */ + /* init free region of newly extended area */ + fsi = n = DTROOTMAXSLOT + stblsize; + f = &rp->slot[fsi]; + for (fsi++; fsi < rp->header.maxslot; f++, fsi++) + f->next = fsi; + f->next = -1; + + /* append new free region at tail of old freelist */ + fsi = sp->header.freelist; + if (fsi == -1) + rp->header.freelist = n; + else { + rp->header.freelist = fsi; + + do { + f = &rp->slot[fsi]; + fsi = f->next; + } while (fsi != -1); + + f->next = n; + } + + rp->header.freecnt = sp->header.freecnt + rp->header.maxslot - n; + + /* + * Update directory index table for entries now in right page + */ + if ((rp->header.flag & BT_LEAF) && DO_INDEX(ip)) { + metapage_t *mp = 0; + ldtentry_t *ldtentry; + + stbl = DT_GETSTBL(rp); + for (n = 0; n < rp->header.nextindex; n++) { + ldtentry = (ldtentry_t *) & rp->slot[stbl[n]]; + modify_index(tid, ip, le32_to_cpu(ldtentry->index), + rbn, n, &mp); + } + if (mp) + release_metapage(mp); + } + /* + * insert the new entry into the new right/child page + * (skip index in the new right page will not change) + */ + dtInsertEntry(rp, split->index, split->key, split->data, &dtlck); + + /* + * reset parent/root page + * + * set the 1st entry offset to 0, which force the left-most key + * at any level of the tree to be less than any search key. + * + * The btree comparison code guarantees that the left-most key on any + * level of the tree is never used, so it doesn't need to be filled in. + */ + BT_MARK_DIRTY(smp, ip); + /* + * acquire a transaction lock on the root page (in-memory inode) + */ + tlck = txLock(tid, ip, smp, tlckDTREE | tlckNEW | tlckBTROOT); + dtlck = (dtlock_t *) & tlck->lock; + + /* linelock root */ + ASSERT(dtlck->index == 0); + lv = (lv_t *) & dtlck->lv[0]; + lv->offset = 0; + lv->length = DTROOTMAXSLOT; + dtlck->index++; + + /* update page header of root */ + if (sp->header.flag & BT_LEAF) { + sp->header.flag &= ~BT_LEAF; + sp->header.flag |= BT_INTERNAL; + } + + /* init the first entry */ + s = (idtentry_t *) & sp->slot[DTENTRYSTART]; + ppxd = (pxd_t *) s; + *ppxd = *pxd; + s->next = -1; + s->namlen = 0; + + stbl = sp->header.stbl; + stbl[0] = DTENTRYSTART; + sp->header.nextindex = 1; + + /* init freelist */ + fsi = DTENTRYSTART + 1; + f = &sp->slot[fsi]; + + /* init free region of remaining area */ + for (fsi++; fsi < DTROOTMAXSLOT; f++, fsi++) + f->next = fsi; + f->next = -1; + + sp->header.freelist = DTENTRYSTART + 1; + sp->header.freecnt = DTROOTMAXSLOT - (DTENTRYSTART + 1); + + *rmpp = rmp; + + ip->i_blocks += LBLK2PBLK(ip->i_sb, lengthPXD(pxd)); + return 0; +} + + +/* + * dtDelete() + * + * function: delete the entry(s) referenced by a key. + * + * parameter: + * + * return: + */ +int dtDelete(tid_t tid, + struct inode *ip, component_t * key, ino_t * ino, int flag) +{ + int rc = 0; + s64 bn; + metapage_t *mp, *imp; + dtpage_t *p; + int index; + btstack_t btstack; + dtlock_t *dtlck; + tlock_t *tlck; + lv_t *lv; + int i; + ldtentry_t *ldtentry; + u8 *stbl; + u32 table_index, next_index; + metapage_t *nmp; + dtpage_t *np; + + /* + * search for the entry to delete: + * + * dtSearch() returns (leaf page pinned, index at which to delete). + */ + if ((rc = dtSearch(ip, key, ino, &btstack, flag))) + return rc; + + /* retrieve search result */ + DT_GETSEARCH(ip, btstack.top, bn, mp, p, index); + + /* + * We need to find put the index of the next entry into the + * directory index table in order to resume a readdir from this + * entry. + */ + if (DO_INDEX(ip)) { + stbl = DT_GETSTBL(p); + ldtentry = (ldtentry_t *) & p->slot[stbl[index]]; + table_index = le32_to_cpu(ldtentry->index); + if (index == (p->header.nextindex - 1)) { + /* + * Last entry in this leaf page + */ + if ((p->header.flag & BT_ROOT) + || (p->header.next == 0)) + next_index = -1; + else { + /* Read next leaf page */ + DT_GETPAGE(ip, le64_to_cpu(p->header.next), + nmp, PSIZE, np, rc); + if (rc) + next_index = -1; + else { + stbl = DT_GETSTBL(np); + ldtentry = + (ldtentry_t *) & np-> + slot[stbl[0]]; + next_index = + le32_to_cpu(ldtentry->index); + DT_PUTPAGE(nmp); + } + } + } else { + ldtentry = + (ldtentry_t *) & p->slot[stbl[index + 1]]; + next_index = le32_to_cpu(ldtentry->index); + } + free_index(tid, ip, table_index, next_index); + } + /* + * the leaf page becomes empty, delete the page + */ + if (p->header.nextindex == 1) { + /* delete empty page */ + rc = dtDeleteUp(tid, ip, mp, p, &btstack); + } + /* + * the leaf page has other entries remaining: + * + * delete the entry from the leaf page. + */ + else { + BT_MARK_DIRTY(mp, ip); + /* + * acquire a transaction lock on the leaf page + */ + tlck = txLock(tid, ip, mp, tlckDTREE | tlckENTRY); + dtlck = (dtlock_t *) & tlck->lock; + + /* + * Do not assume that dtlck->index will be zero. During a + * rename within a directory, this transaction may have + * modified this page already when adding the new entry. + */ + + /* linelock header */ + if (dtlck->index >= dtlck->maxcnt) + dtlck = (dtlock_t *) txLinelock(dtlck); + lv = (lv_t *) & dtlck->lv[dtlck->index]; + lv->offset = 0; + lv->length = 1; + dtlck->index++; + + /* linelock stbl of non-root leaf page */ + if (!(p->header.flag & BT_ROOT)) { + if (dtlck->index >= dtlck->maxcnt) + dtlck = (dtlock_t *) txLinelock(dtlck); + lv = (lv_t *) & dtlck->lv[dtlck->index]; + i = index >> L2DTSLOTSIZE; + lv->offset = p->header.stblindex + i; + lv->length = + ((p->header.nextindex - 1) >> L2DTSLOTSIZE) - + i + 1; + dtlck->index++; + } + + /* free the leaf entry */ + dtDeleteEntry(p, index, &dtlck); + + /* + * Update directory index table for entries moved in stbl + */ + if (DO_INDEX(ip) && index < p->header.nextindex) { + imp = 0; + stbl = DT_GETSTBL(p); + for (i = index; i < p->header.nextindex; i++) { + ldtentry = + (ldtentry_t *) & p->slot[stbl[i]]; + modify_index(tid, ip, + le32_to_cpu(ldtentry->index), + bn, i, &imp); + } + if (imp) + release_metapage(imp); + } + + DT_PUTPAGE(mp); + } + + return rc; +} + + +/* + * dtDeleteUp() + * + * function: + * free empty pages as propagating deletion up the tree + * + * parameter: + * + * return: + */ +static int dtDeleteUp(tid_t tid, struct inode *ip, + metapage_t * fmp, dtpage_t * fp, btstack_t * btstack) +{ + int rc = 0; + metapage_t *mp; + dtpage_t *p; + int index, nextindex; + int xlen; + btframe_t *parent; + dtlock_t *dtlck; + tlock_t *tlck; + lv_t *lv; + pxdlock_t *pxdlock; + int i; + + /* + * keep the root leaf page which has become empty + */ + if (BT_IS_ROOT(fmp)) { + /* + * reset the root + * + * dtInitRoot() acquires txlock on the root + */ + dtInitRoot(tid, ip, PARENT(ip)); + + DT_PUTPAGE(fmp); + + return 0; + } + + /* + * free the non-root leaf page + */ + /* + * acquire a transaction lock on the page + * + * write FREEXTENT|NOREDOPAGE log record + * N.B. linelock is overlaid as freed extent descriptor, and + * the buffer page is freed; + */ + tlck = txMaplock(tid, ip, tlckDTREE | tlckFREE); + pxdlock = (pxdlock_t *) & tlck->lock; + pxdlock->flag = mlckFREEPXD; + pxdlock->pxd = fp->header.self; + pxdlock->index = 1; + + /* update sibling pointers */ + if ((rc = dtRelink(tid, ip, fp))) + return rc; + + xlen = lengthPXD(&fp->header.self); + ip->i_blocks -= LBLK2PBLK(ip->i_sb, xlen); + + /* free/invalidate its buffer page */ + discard_metapage(fmp); + + /* + * propagate page deletion up the directory tree + * + * If the delete from the parent page makes it empty, + * continue all the way up the tree. + * stop if the root page is reached (which is never deleted) or + * if the entry deletion does not empty the page. + */ + while ((parent = BT_POP(btstack)) != NULL) { + /* pin the parent page */ + DT_GETPAGE(ip, parent->bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + /* + * free the extent of the child page deleted + */ + index = parent->index; + + /* + * delete the entry for the child page from parent + */ + nextindex = p->header.nextindex; + + /* + * the parent has the single entry being deleted: + * + * free the parent page which has become empty. + */ + if (nextindex == 1) { + /* + * keep the root internal page which has become empty + */ + if (p->header.flag & BT_ROOT) { + /* + * reset the root + * + * dtInitRoot() acquires txlock on the root + */ + dtInitRoot(tid, ip, PARENT(ip)); + + DT_PUTPAGE(mp); + + return 0; + } + /* + * free the parent page + */ + else { + /* + * acquire a transaction lock on the page + * + * write FREEXTENT|NOREDOPAGE log record + */ + tlck = + txMaplock(tid, ip, + tlckDTREE | tlckFREE); + pxdlock = (pxdlock_t *) & tlck->lock; + pxdlock->flag = mlckFREEPXD; + pxdlock->pxd = p->header.self; + pxdlock->index = 1; + + /* update sibling pointers */ + if ((rc = dtRelink(tid, ip, p))) + return rc; + + xlen = lengthPXD(&p->header.self); + ip->i_blocks -= LBLK2PBLK(ip->i_sb, xlen); + + /* free/invalidate its buffer page */ + discard_metapage(mp); + + /* propagate up */ + continue; + } + } + + /* + * the parent has other entries remaining: + * + * delete the router entry from the parent page. + */ + BT_MARK_DIRTY(mp, ip); + /* + * acquire a transaction lock on the page + * + * action: router entry deletion + */ + tlck = txLock(tid, ip, mp, tlckDTREE | tlckENTRY); + dtlck = (dtlock_t *) & tlck->lock; + + /* linelock header */ + if (dtlck->index >= dtlck->maxcnt) + dtlck = (dtlock_t *) txLinelock(dtlck); + lv = (lv_t *) & dtlck->lv[dtlck->index]; + lv->offset = 0; + lv->length = 1; + dtlck->index++; + + /* linelock stbl of non-root leaf page */ + if (!(p->header.flag & BT_ROOT)) { + if (dtlck->index < dtlck->maxcnt) + lv++; + else { + dtlck = (dtlock_t *) txLinelock(dtlck); + lv = (lv_t *) & dtlck->lv[0]; + } + i = index >> L2DTSLOTSIZE; + lv->offset = p->header.stblindex + i; + lv->length = + ((p->header.nextindex - 1) >> L2DTSLOTSIZE) - + i + 1; + dtlck->index++; + } + + /* free the router entry */ + dtDeleteEntry(p, index, &dtlck); + + /* reset key of new leftmost entry of level (for consistency) */ + if (index == 0 && + ((p->header.flag & BT_ROOT) || p->header.prev == 0)) + dtTruncateEntry(p, 0, &dtlck); + + /* unpin the parent page */ + DT_PUTPAGE(mp); + + /* exit propagation up */ + break; + } + + return 0; +} + + +/* + * NAME: dtRelocate() + * + * FUNCTION: relocate dtpage (internal or leaf) of directory; + * This function is mainly used by defragfs utility. + */ +int dtRelocate(tid_t tid, struct inode *ip, s64 lmxaddr, pxd_t * opxd, + s64 nxaddr) +{ + int rc = 0; + metapage_t *mp, *pmp, *lmp, *rmp; + dtpage_t *p, *pp, *rp = 0, *lp= 0; + s64 bn; + int index; + btstack_t btstack; + pxd_t *pxd; + s64 oxaddr, nextbn, prevbn; + int xlen, xsize; + tlock_t *tlck; + dtlock_t *dtlck; + pxdlock_t *pxdlock; + s8 *stbl; + lv_t *lv; + + oxaddr = addressPXD(opxd); + xlen = lengthPXD(opxd); + + jEVENT(0, ("dtRelocate: lmxaddr:%Ld xaddr:%Ld:%Ld xlen:%d\n", + lmxaddr, oxaddr, nxaddr, xlen)); + + /* + * 1. get the internal parent dtpage covering + * router entry for the tartget page to be relocated; + */ + rc = dtSearchNode(ip, lmxaddr, opxd, &btstack); + if (rc) + return rc; + + /* retrieve search result */ + DT_GETSEARCH(ip, btstack.top, bn, pmp, pp, index); + jEVENT(0, ("dtRelocate: parent router entry validated.\n")); + + /* + * 2. relocate the target dtpage + */ + /* read in the target page from src extent */ + DT_GETPAGE(ip, oxaddr, mp, PSIZE, p, rc); + if (rc) { + /* release the pinned parent page */ + DT_PUTPAGE(pmp); + return rc; + } + + /* + * read in sibling pages if any to update sibling pointers; + */ + rmp = NULL; + if (p->header.next) { + nextbn = le64_to_cpu(p->header.next); + DT_GETPAGE(ip, nextbn, rmp, PSIZE, rp, rc); + if (rc) { + DT_PUTPAGE(mp); + DT_PUTPAGE(pmp); + return (rc); + } + } + + lmp = NULL; + if (p->header.prev) { + prevbn = le64_to_cpu(p->header.prev); + DT_GETPAGE(ip, prevbn, lmp, PSIZE, lp, rc); + if (rc) { + DT_PUTPAGE(mp); + DT_PUTPAGE(pmp); + if (rmp) + DT_PUTPAGE(rmp); + return (rc); + } + } + + /* at this point, all xtpages to be updated are in memory */ + + /* + * update sibling pointers of sibling dtpages if any; + */ + if (lmp) { + tlck = txLock(tid, ip, lmp, tlckDTREE | tlckRELINK); + dtlck = (dtlock_t *) & tlck->lock; + /* linelock header */ + ASSERT(dtlck->index == 0); + lv = (lv_t *) & dtlck->lv[0]; + lv->offset = 0; + lv->length = 1; + dtlck->index++; + + lp->header.next = cpu_to_le64(nxaddr); + DT_PUTPAGE(lmp); + } + + if (rmp) { + tlck = txLock(tid, ip, rmp, tlckDTREE | tlckRELINK); + dtlck = (dtlock_t *) & tlck->lock; + /* linelock header */ + ASSERT(dtlck->index == 0); + lv = (lv_t *) & dtlck->lv[0]; + lv->offset = 0; + lv->length = 1; + dtlck->index++; + + rp->header.prev = cpu_to_le64(nxaddr); + DT_PUTPAGE(rmp); + } + + /* + * update the target dtpage to be relocated + * + * write LOG_REDOPAGE of LOG_NEW type for dst page + * for the whole target page (logredo() will apply + * after image and update bmap for allocation of the + * dst extent), and update bmap for allocation of + * the dst extent; + */ + tlck = txLock(tid, ip, mp, tlckDTREE | tlckNEW); + dtlck = (dtlock_t *) & tlck->lock; + /* linelock header */ + ASSERT(dtlck->index == 0); + lv = (lv_t *) & dtlck->lv[0]; + + /* update the self address in the dtpage header */ + pxd = &p->header.self; + PXDaddress(pxd, nxaddr); + + /* the dst page is the same as the src page, i.e., + * linelock for afterimage of the whole page; + */ + lv->offset = 0; + lv->length = p->header.maxslot; + dtlck->index++; + + /* update the buffer extent descriptor of the dtpage */ + xsize = xlen << JFS_SBI(ip->i_sb)->l2bsize; +#ifdef _STILL_TO_PORT + bmSetXD(mp, nxaddr, xsize); +#endif /* _STILL_TO_PORT */ + /* unpin the relocated page */ + DT_PUTPAGE(mp); + jEVENT(0, ("dtRelocate: target dtpage relocated.\n")); + + /* the moved extent is dtpage, then a LOG_NOREDOPAGE log rec + * needs to be written (in logredo(), the LOG_NOREDOPAGE log rec + * will also force a bmap update ). + */ + + /* + * 3. acquire maplock for the source extent to be freed; + */ + /* for dtpage relocation, write a LOG_NOREDOPAGE record + * for the source dtpage (logredo() will init NoRedoPage + * filter and will also update bmap for free of the source + * dtpage), and upadte bmap for free of the source dtpage; + */ + tlck = txMaplock(tid, ip, tlckDTREE | tlckFREE); + pxdlock = (pxdlock_t *) & tlck->lock; + pxdlock->flag = mlckFREEPXD; + PXDaddress(&pxdlock->pxd, oxaddr); + PXDlength(&pxdlock->pxd, xlen); + pxdlock->index = 1; + + /* + * 4. update the parent router entry for relocation; + * + * acquire tlck for the parent entry covering the target dtpage; + * write LOG_REDOPAGE to apply after image only; + */ + jEVENT(0, ("dtRelocate: update parent router entry.\n")); + tlck = txLock(tid, ip, pmp, tlckDTREE | tlckENTRY); + dtlck = (dtlock_t *) & tlck->lock; + lv = (lv_t *) & dtlck->lv[dtlck->index]; + + /* update the PXD with the new address */ + stbl = DT_GETSTBL(pp); + pxd = (pxd_t *) & pp->slot[stbl[index]]; + PXDaddress(pxd, nxaddr); + lv->offset = stbl[index]; + lv->length = 1; + dtlck->index++; + + /* unpin the parent dtpage */ + DT_PUTPAGE(pmp); + + return rc; +} + + +/* + * NAME: dtSearchNode() + * + * FUNCTION: Search for an dtpage containing a specified address + * This function is mainly used by defragfs utility. + * + * NOTE: Search result on stack, the found page is pinned at exit. + * The result page must be an internal dtpage. + * lmxaddr give the address of the left most page of the + * dtree level, in which the required dtpage resides. + */ +static int dtSearchNode(struct inode *ip, s64 lmxaddr, pxd_t * kpxd, + btstack_t * btstack) +{ + int rc = 0; + s64 bn; + metapage_t *mp; + dtpage_t *p; + int psize = 288; /* initial in-line directory */ + s8 *stbl; + int i; + pxd_t *pxd; + btframe_t *btsp; + + BT_CLR(btstack); /* reset stack */ + + /* + * descend tree to the level with specified leftmost page + * + * by convention, root bn = 0. + */ + for (bn = 0;;) { + /* get/pin the page to search */ + DT_GETPAGE(ip, bn, mp, psize, p, rc); + if (rc) + return rc; + + /* does the xaddr of leftmost page of the levevl + * matches levevl search key ? + */ + if (p->header.flag & BT_ROOT) { + if (lmxaddr == 0) + break; + } else if (addressPXD(&p->header.self) == lmxaddr) + break; + + /* + * descend down to leftmost child page + */ + if (p->header.flag & BT_LEAF) + return ESTALE; + + /* get the leftmost entry */ + stbl = DT_GETSTBL(p); + pxd = (pxd_t *) & p->slot[stbl[0]]; + + /* get the child page block address */ + bn = addressPXD(pxd); + psize = lengthPXD(pxd) << JFS_SBI(ip->i_sb)->l2bsize; + /* unpin the parent page */ + DT_PUTPAGE(mp); + } + + /* + * search each page at the current levevl + */ + loop: + stbl = DT_GETSTBL(p); + for (i = 0; i < p->header.nextindex; i++) { + pxd = (pxd_t *) & p->slot[stbl[i]]; + + /* found the specified router entry */ + if (addressPXD(pxd) == addressPXD(kpxd) && + lengthPXD(pxd) == lengthPXD(kpxd)) { + btsp = btstack->top; + btsp->bn = bn; + btsp->index = i; + btsp->mp = mp; + + return 0; + } + } + + /* get the right sibling page if any */ + if (p->header.next) + bn = le64_to_cpu(p->header.next); + else { + DT_PUTPAGE(mp); + return ESTALE; + } + + /* unpin current page */ + DT_PUTPAGE(mp); + + /* get the right sibling page */ + DT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + goto loop; +} + + +/* + * dtRelink() + * + * function: + * link around a freed page. + * + * parameter: + * fp: page to be freed + * + * return: + */ +static int dtRelink(tid_t tid, struct inode *ip, dtpage_t * p) +{ + int rc; + metapage_t *mp; + s64 nextbn, prevbn; + tlock_t *tlck; + dtlock_t *dtlck; + lv_t *lv; + + nextbn = le64_to_cpu(p->header.next); + prevbn = le64_to_cpu(p->header.prev); + + /* update prev pointer of the next page */ + if (nextbn != 0) { + DT_GETPAGE(ip, nextbn, mp, PSIZE, p, rc); + if (rc) + return rc; + + BT_MARK_DIRTY(mp, ip); + /* + * acquire a transaction lock on the next page + * + * action: update prev pointer; + */ + tlck = txLock(tid, ip, mp, tlckDTREE | tlckRELINK); + jEVENT(0, + ("dtRelink nextbn: tlck = 0x%p, ip = 0x%p, mp=0x%p\n", + tlck, ip, mp)); + dtlck = (dtlock_t *) & tlck->lock; + + /* linelock header */ + if (dtlck->index >= dtlck->maxcnt) + dtlck = (dtlock_t *) txLinelock(dtlck); + lv = (lv_t *) & dtlck->lv[dtlck->index]; + lv->offset = 0; + lv->length = 1; + dtlck->index++; + + p->header.prev = cpu_to_le64(prevbn); + DT_PUTPAGE(mp); + } + + /* update next pointer of the previous page */ + if (prevbn != 0) { + DT_GETPAGE(ip, prevbn, mp, PSIZE, p, rc); + if (rc) + return rc; + + BT_MARK_DIRTY(mp, ip); + /* + * acquire a transaction lock on the prev page + * + * action: update next pointer; + */ + tlck = txLock(tid, ip, mp, tlckDTREE | tlckRELINK); + jEVENT(0, + ("dtRelink prevbn: tlck = 0x%p, ip = 0x%p, mp=0x%p\n", + tlck, ip, mp)); + dtlck = (dtlock_t *) & tlck->lock; + + /* linelock header */ + if (dtlck->index >= dtlck->maxcnt) + dtlck = (dtlock_t *) txLinelock(dtlck); + lv = (lv_t *) & dtlck->lv[dtlck->index]; + lv->offset = 0; + lv->length = 1; + dtlck->index++; + + p->header.next = cpu_to_le64(nextbn); + DT_PUTPAGE(mp); + } + + return 0; +} + + +/* + * dtInitRoot() + * + * initialize directory root (inline in inode) + */ +void dtInitRoot(tid_t tid, struct inode *ip, u32 idotdot) +{ + struct jfs_inode_info *jfs_ip = JFS_IP(ip); + dtroot_t *p; + int fsi; + dtslot_t *f; + tlock_t *tlck; + dtlock_t *dtlck; + lv_t *lv; + u16 xflag_save; + + /* + * If this was previously an non-empty directory, we need to remove + * the old directory table. + */ + if (DO_INDEX(ip)) { + if (jfs_ip->next_index > (MAX_INLINE_DIRTABLE_ENTRY + 1)) { + tblock_t *tblk = tid_to_tblock(tid); + /* + * We're playing games with the tid's xflag. If + * we're removing a regular file, the file's xtree + * is committed with COMMIT_PMAP, but we always + * commit the directories xtree with COMMIT_PWMAP. + */ + xflag_save = tblk->xflag; + tblk->xflag = 0; + /* + * xtTruncate isn't guaranteed to fully truncate + * the xtree. The caller needs to check i_size + * after committing the transaction to see if + * additional truncation is needed. The + * COMMIT_Stale flag tells caller that we + * initiated the truncation. + */ + xtTruncate(tid, ip, 0, COMMIT_PWMAP); + set_cflag(COMMIT_Stale, ip); + + tblk->xflag = xflag_save; + /* + * Tells jfs_metapage code that the metadata pages + * for the index table are no longer useful, and + * remove them from page cache. + */ + invalidate_inode_metapages(ip); + } else + ip->i_size = 1; + + jfs_ip->next_index = 2; + } else + ip->i_size = IDATASIZE; + + /* + * acquire a transaction lock on the root + * + * action: directory initialization; + */ + tlck = txLock(tid, ip, (metapage_t *) & jfs_ip->bxflag, + tlckDTREE | tlckENTRY | tlckBTROOT); + dtlck = (dtlock_t *) & tlck->lock; + + /* linelock root */ + ASSERT(dtlck->index == 0); + lv = (lv_t *) & dtlck->lv[0]; + lv->offset = 0; + lv->length = DTROOTMAXSLOT; + dtlck->index++; + + p = &jfs_ip->i_dtroot; + + p->header.flag = DXD_INDEX | BT_ROOT | BT_LEAF; + + p->header.nextindex = 0; + + /* init freelist */ + fsi = 1; + f = &p->slot[fsi]; + + /* init data area of root */ + for (fsi++; fsi < DTROOTMAXSLOT; f++, fsi++) + f->next = fsi; + f->next = -1; + + p->header.freelist = 1; + p->header.freecnt = 8; + + /* init '..' entry */ + p->header.idotdot = cpu_to_le32(idotdot); + +#if 0 + ip->i_blocks = LBLK2PBLK(ip->i_sb, + ((jfs_ip->ea.flag & DXD_EXTENT) ? + lengthDXD(&jfs_ip->ea) : 0) + + ((jfs_ip->acl.flag & DXD_EXTENT) ? + lengthDXD(&jfs_ip->acl) : 0)); +#endif + + return; +} + +/* + * jfs_readdir() + * + * function: read directory entries sequentially + * from the specified entry offset + * + * parameter: + * + * return: offset = (pn, index) of start entry + * of next jfs_readdir()/dtRead() + */ +int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *ip = filp->f_dentry->d_inode; + struct nls_table *codepage = JFS_SBI(ip->i_sb)->nls_tab; + int rc = 0; + struct dtoffset { + s16 pn; + s16 index; + s32 unused; + } *dtoffset = (struct dtoffset *) &filp->f_pos; + s64 bn; + metapage_t *mp; + dtpage_t *p; + int index; + s8 *stbl; + btstack_t btstack; + int i, next; + ldtentry_t *d; + dtslot_t *t; + int d_namleft, d_namlen, len, outlen; + char *d_name, *name_ptr; + int dtlhdrdatalen; + u32 dir_index; + int do_index = 0; + uint loop_count = 0; + + if (filp->f_pos == DIREND) + return 0; + + if (DO_INDEX(ip)) { + /* + * persistent index is stored in directory entries. + * Special cases: 0 = . + * 1 = .. + * -1 = End of directory + */ + do_index = 1; + dtlhdrdatalen = DTLHDRDATALEN; + + dir_index = (u32) filp->f_pos; + + if (dir_index > 1) { + dir_table_slot_t dirtab_slot; + + if (dtEmpty(ip)) { + filp->f_pos = DIREND; + return 0; + } + repeat: + rc = get_index(ip, dir_index, &dirtab_slot); + if (rc) { + filp->f_pos = DIREND; + return rc; + } + if (dirtab_slot.flag == DIR_INDEX_FREE) { + if (loop_count++ > JFS_IP(ip)->next_index) { + jERROR(1, ("jfs_readdir detected " + "infinite loop!\n")); + filp->f_pos = DIREND; + return 0; + } + dir_index = le32_to_cpu(dirtab_slot.addr2); + if (dir_index == -1) { + filp->f_pos = DIREND; + return 0; + } + goto repeat; + } + bn = addressDTS(&dirtab_slot); + index = dirtab_slot.slot; + DT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) { + filp->f_pos = DIREND; + return 0; + } + if (p->header.flag & BT_INTERNAL) { + jERROR(1,("jfs_readdir: bad index table\n")); + DT_PUTPAGE(mp); + filp->f_pos = -1; + return 0; + } + } else { + if (dir_index == 0) { + /* + * self "." + */ + filp->f_pos = 0; + if (filldir(dirent, ".", 1, 0, ip->i_ino, + DT_DIR)) + return 0; + } + /* + * parent ".." + */ + filp->f_pos = 1; + if (filldir + (dirent, "..", 2, 1, PARENT(ip), DT_DIR)) + return 0; + + /* + * Find first entry of left-most leaf + */ + if (dtEmpty(ip)) { + filp->f_pos = DIREND; + return 0; + } + + if ((rc = dtReadFirst(ip, &btstack))) + return -rc; + + DT_GETSEARCH(ip, btstack.top, bn, mp, p, index); + } + } else { + /* + * Legacy filesystem - OS/2 & Linux JFS < 0.3.6 + * + * pn = index = 0: First entry "." + * pn = 0; index = 1: Second entry ".." + * pn > 0: Real entries, pn=1 -> leftmost page + * pn = index = -1: No more entries + */ + dtlhdrdatalen = DTLHDRDATALEN_LEGACY; + + if (filp->f_pos == 0) { + /* build "." entry */ + + if (filldir(dirent, ".", 1, filp->f_pos, ip->i_ino, + DT_DIR)) + return 0; + dtoffset->index = 1; + } + + if (dtoffset->pn == 0) { + if (dtoffset->index == 1) { + /* build ".." entry */ + + if (filldir(dirent, "..", 2, filp->f_pos, + PARENT(ip), DT_DIR)) + return 0; + } else { + jERROR(1, + ("jfs_readdir called with invalid offset!\n")); + } + dtoffset->pn = 1; + dtoffset->index = 0; + } + + if (dtEmpty(ip)) { + filp->f_pos = DIREND; + return 0; + } + + if ((rc = dtReadNext(ip, &filp->f_pos, &btstack))) { + jERROR(1, + ("jfs_readdir: unexpected rc = %d from dtReadNext\n", + rc)); + filp->f_pos = DIREND; + return 0; + } + /* get start leaf page and index */ + DT_GETSEARCH(ip, btstack.top, bn, mp, p, index); + + /* offset beyond directory eof ? */ + if (bn < 0) { + filp->f_pos = DIREND; + return 0; + } + } + + d_name = kmalloc((JFS_NAME_MAX + 1) * sizeof(wchar_t), GFP_NOFS); + if (d_name == NULL) { + DT_PUTPAGE(mp); + jERROR(1, ("jfs_readdir: kmalloc failed!\n")); + filp->f_pos = DIREND; + return 0; + } + while (1) { + stbl = DT_GETSTBL(p); + + for (i = index; i < p->header.nextindex; i++) { + d = (ldtentry_t *) & p->slot[stbl[i]]; + + d_namleft = d->namlen; + name_ptr = d_name; + + if (do_index) { + filp->f_pos = le32_to_cpu(d->index); + len = min(d_namleft, DTLHDRDATALEN); + } else + len = min(d_namleft, DTLHDRDATALEN_LEGACY); + + /* copy the name of head/only segment */ + outlen = jfs_strfromUCS_le(name_ptr, d->name, len, + codepage); + d_namlen = outlen; + + /* copy name in the additional segment(s) */ + next = d->next; + while (next >= 0) { + t = (dtslot_t *) & p->slot[next]; + name_ptr += outlen; + d_namleft -= len; + len = min(d_namleft, DTSLOTDATALEN); + outlen = jfs_strfromUCS_le(name_ptr, t->name, + len, codepage); + d_namlen+= outlen; + + next = t->next; + } + + if (filldir(dirent, d_name, d_namlen, filp->f_pos, + le32_to_cpu(d->inumber), DT_UNKNOWN)) + goto out; + if (!do_index) + dtoffset->index++; + } + + /* + * get next leaf page + */ + + if (p->header.flag & BT_ROOT) { + filp->f_pos = DIREND; + break; + } + + bn = le64_to_cpu(p->header.next); + if (bn == 0) { + filp->f_pos = DIREND; + break; + } + + /* unpin previous leaf page */ + DT_PUTPAGE(mp); + + /* get next leaf page */ + DT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) { + kfree(d_name); + return -rc; + } + + /* update offset (pn:index) for new page */ + index = 0; + if (!do_index) { + dtoffset->pn++; + dtoffset->index = 0; + } + + } + + out: + kfree(d_name); + DT_PUTPAGE(mp); + + return rc; +} + + +/* + * dtReadFirst() + * + * function: get the leftmost page of the directory + */ +static int dtReadFirst(struct inode *ip, btstack_t * btstack) +{ + int rc = 0; + s64 bn; + int psize = 288; /* initial in-line directory */ + metapage_t *mp; + dtpage_t *p; + s8 *stbl; + btframe_t *btsp; + pxd_t *xd; + + BT_CLR(btstack); /* reset stack */ + + /* + * descend leftmost path of the tree + * + * by convention, root bn = 0. + */ + for (bn = 0;;) { + DT_GETPAGE(ip, bn, mp, psize, p, rc); + if (rc) + return rc; + + /* + * leftmost leaf page + */ + if (p->header.flag & BT_LEAF) { + /* return leftmost entry */ + btsp = btstack->top; + btsp->bn = bn; + btsp->index = 0; + btsp->mp = mp; + + return 0; + } + + /* + * descend down to leftmost child page + */ + /* push (bn, index) of the parent page/entry */ + BT_PUSH(btstack, bn, 0); + + /* get the leftmost entry */ + stbl = DT_GETSTBL(p); + xd = (pxd_t *) & p->slot[stbl[0]]; + + /* get the child page block address */ + bn = addressPXD(xd); + psize = lengthPXD(xd) << JFS_SBI(ip->i_sb)->l2bsize; + + /* unpin the parent page */ + DT_PUTPAGE(mp); + } +} + + +/* + * dtReadNext() + * + * function: get the page of the specified offset (pn:index) + * + * return: if (offset > eof), bn = -1; + * + * note: if index > nextindex of the target leaf page, + * start with 1st entry of next leaf page; + */ +static int dtReadNext(struct inode *ip, loff_t * offset, btstack_t * btstack) +{ + int rc = 0; + struct dtoffset { + s16 pn; + s16 index; + s32 unused; + } *dtoffset = (struct dtoffset *) offset; + s64 bn; + metapage_t *mp; + dtpage_t *p; + int index; + int pn; + s8 *stbl; + btframe_t *btsp, *parent; + pxd_t *xd; + + /* + * get leftmost leaf page pinned + */ + if ((rc = dtReadFirst(ip, btstack))) + return rc; + + /* get leaf page */ + DT_GETSEARCH(ip, btstack->top, bn, mp, p, index); + + /* get the start offset (pn:index) */ + pn = dtoffset->pn - 1; /* Now pn = 0 represents leftmost leaf */ + index = dtoffset->index; + + /* start at leftmost page ? */ + if (pn == 0) { + /* offset beyond eof ? */ + if (index < p->header.nextindex) + goto out; + + if (p->header.flag & BT_ROOT) { + bn = -1; + goto out; + } + + /* start with 1st entry of next leaf page */ + dtoffset->pn++; + dtoffset->index = index = 0; + goto a; + } + + /* start at non-leftmost page: scan parent pages for large pn */ + if (p->header.flag & BT_ROOT) { + bn = -1; + goto out; + } + + /* start after next leaf page ? */ + if (pn > 1) + goto b; + + /* get leaf page pn = 1 */ + a: + bn = le64_to_cpu(p->header.next); + + /* unpin leaf page */ + DT_PUTPAGE(mp); + + /* offset beyond eof ? */ + if (bn == 0) { + bn = -1; + goto out; + } + + goto c; + + /* + * scan last internal page level to get target leaf page + */ + b: + /* unpin leftmost leaf page */ + DT_PUTPAGE(mp); + + /* get left most parent page */ + btsp = btstack->top; + parent = btsp - 1; + bn = parent->bn; + DT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + /* scan parent pages at last internal page level */ + while (pn >= p->header.nextindex) { + pn -= p->header.nextindex; + + /* get next parent page address */ + bn = le64_to_cpu(p->header.next); + + /* unpin current parent page */ + DT_PUTPAGE(mp); + + /* offset beyond eof ? */ + if (bn == 0) { + bn = -1; + goto out; + } + + /* get next parent page */ + DT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + /* update parent page stack frame */ + parent->bn = bn; + } + + /* get leaf page address */ + stbl = DT_GETSTBL(p); + xd = (pxd_t *) & p->slot[stbl[pn]]; + bn = addressPXD(xd); + + /* unpin parent page */ + DT_PUTPAGE(mp); + + /* + * get target leaf page + */ + c: + DT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + /* + * leaf page has been completed: + * start with 1st entry of next leaf page + */ + if (index >= p->header.nextindex) { + bn = le64_to_cpu(p->header.next); + + /* unpin leaf page */ + DT_PUTPAGE(mp); + + /* offset beyond eof ? */ + if (bn == 0) { + bn = -1; + goto out; + } + + /* get next leaf page */ + DT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + /* start with 1st entry of next leaf page */ + dtoffset->pn++; + dtoffset->index = 0; + } + + out: + /* return target leaf page pinned */ + btsp = btstack->top; + btsp->bn = bn; + btsp->index = dtoffset->index; + btsp->mp = mp; + + return 0; +} + + +/* + * dtCompare() + * + * function: compare search key with an internal entry + * + * return: + * < 0 if k is < record + * = 0 if k is = record + * > 0 if k is > record + */ +static int dtCompare(component_t * key, /* search key */ + dtpage_t * p, /* directory page */ + int si) +{ /* entry slot index */ + register int rc; + register wchar_t *kname, *name; + register int klen, namlen, len; + idtentry_t *ih; + dtslot_t *t; + + /* + * force the left-most key on internal pages, at any level of + * the tree, to be less than any search key. + * this obviates having to update the leftmost key on an internal + * page when the user inserts a new key in the tree smaller than + * anything that has been stored. + * + * (? if/when dtSearch() narrows down to 1st entry (index = 0), + * at any internal page at any level of the tree, + * it descends to child of the entry anyway - + * ? make the entry as min size dummy entry) + * + * if (e->index == 0 && h->prevpg == P_INVALID && !(h->flags & BT_LEAF)) + * return (1); + */ + + kname = key->name; + klen = key->namlen; + + ih = (idtentry_t *) & p->slot[si]; + si = ih->next; + name = ih->name; + namlen = ih->namlen; + len = min(namlen, DTIHDRDATALEN); + + /* compare with head/only segment */ + len = min(klen, len); + if ((rc = UniStrncmp_le(kname, name, len))) + return rc; + + klen -= len; + namlen -= len; + + /* compare with additional segment(s) */ + kname += len; + while (klen > 0 && namlen > 0) { + /* compare with next name segment */ + t = (dtslot_t *) & p->slot[si]; + len = min(namlen, DTSLOTDATALEN); + len = min(klen, len); + name = t->name; + if ((rc = UniStrncmp_le(kname, name, len))) + return rc; + + klen -= len; + namlen -= len; + kname += len; + si = t->next; + } + + return (klen - namlen); +} + + + + +/* + * ciCompare() + * + * function: compare search key with an (leaf/internal) entry + * + * return: + * < 0 if k is < record + * = 0 if k is = record + * > 0 if k is > record + */ +static int ciCompare(component_t * key, /* search key */ + dtpage_t * p, /* directory page */ + int si, /* entry slot index */ + int flag) +{ + register int rc; + register wchar_t *kname, *name, x; + register int klen, namlen, len; + ldtentry_t *lh; + idtentry_t *ih; + dtslot_t *t; + int i; + + /* + * force the left-most key on internal pages, at any level of + * the tree, to be less than any search key. + * this obviates having to update the leftmost key on an internal + * page when the user inserts a new key in the tree smaller than + * anything that has been stored. + * + * (? if/when dtSearch() narrows down to 1st entry (index = 0), + * at any internal page at any level of the tree, + * it descends to child of the entry anyway - + * ? make the entry as min size dummy entry) + * + * if (e->index == 0 && h->prevpg == P_INVALID && !(h->flags & BT_LEAF)) + * return (1); + */ + + kname = key->name; + klen = key->namlen; + + /* + * leaf page entry + */ + if (p->header.flag & BT_LEAF) { + lh = (ldtentry_t *) & p->slot[si]; + si = lh->next; + name = lh->name; + namlen = lh->namlen; + if (flag & JFS_DIR_INDEX) + len = min(namlen, DTLHDRDATALEN); + else + len = min(namlen, DTLHDRDATALEN_LEGACY); + } + /* + * internal page entry + */ + else { + ih = (idtentry_t *) & p->slot[si]; + si = ih->next; + name = ih->name; + namlen = ih->namlen; + len = min(namlen, DTIHDRDATALEN); + } + + /* compare with head/only segment */ + len = min(klen, len); + for (i = 0; i < len; i++, kname++, name++) { + /* only uppercase if case-insensitive support is on */ + if ((flag & JFS_OS2) == JFS_OS2) + x = UniToupper(le16_to_cpu(*name)); + else + x = le16_to_cpu(*name); + if ((rc = *kname - x)) + return rc; + } + + klen -= len; + namlen -= len; + + /* compare with additional segment(s) */ + while (klen > 0 && namlen > 0) { + /* compare with next name segment */ + t = (dtslot_t *) & p->slot[si]; + len = min(namlen, DTSLOTDATALEN); + len = min(klen, len); + name = t->name; + for (i = 0; i < len; i++, kname++, name++) { + /* only uppercase if case-insensitive support is on */ + if ((flag & JFS_OS2) == JFS_OS2) + x = UniToupper(le16_to_cpu(*name)); + else + x = le16_to_cpu(*name); + + if ((rc = *kname - x)) + return rc; + } + + klen -= len; + namlen -= len; + si = t->next; + } + + return (klen - namlen); +} + + +/* + * ciGetLeafPrefixKey() + * + * function: compute prefix of suffix compression + * from two adjacent leaf entries + * across page boundary + * + * return: + * Number of prefix bytes needed to distinguish b from a. + */ +static void ciGetLeafPrefixKey(dtpage_t * lp, int li, dtpage_t * rp, + int ri, component_t * key, int flag) +{ + register int klen, namlen; + register wchar_t *pl, *pr, *kname; + wchar_t lname[JFS_NAME_MAX + 1]; + component_t lkey = { 0, lname }; + wchar_t rname[JFS_NAME_MAX + 1]; + component_t rkey = { 0, rname }; + + /* get left and right key */ + dtGetKey(lp, li, &lkey, flag); + lkey.name[lkey.namlen] = 0; + + if ((flag & JFS_OS2) == JFS_OS2) + ciToUpper(&lkey); + + dtGetKey(rp, ri, &rkey, flag); + rkey.name[rkey.namlen] = 0; + + + if ((flag & JFS_OS2) == JFS_OS2) + ciToUpper(&rkey); + + /* compute prefix */ + klen = 0; + kname = key->name; + namlen = min(lkey.namlen, rkey.namlen); + for (pl = lkey.name, pr = rkey.name; + namlen; pl++, pr++, namlen--, klen++, kname++) { + *kname = *pr; + if (*pl != *pr) { + key->namlen = klen + 1; + return; + } + } + + /* l->namlen <= r->namlen since l <= r */ + if (lkey.namlen < rkey.namlen) { + *kname = *pr; + key->namlen = klen + 1; + } else /* l->namelen == r->namelen */ + key->namlen = klen; + + return; +} + + + +/* + * dtGetKey() + * + * function: get key of the entry + */ +static void dtGetKey(dtpage_t * p, int i, /* entry index */ + component_t * key, int flag) +{ + int si; + s8 *stbl; + ldtentry_t *lh; + idtentry_t *ih; + dtslot_t *t; + int namlen, len; + wchar_t *name, *kname; + + /* get entry */ + stbl = DT_GETSTBL(p); + si = stbl[i]; + if (p->header.flag & BT_LEAF) { + lh = (ldtentry_t *) & p->slot[si]; + si = lh->next; + namlen = lh->namlen; + name = lh->name; + if (flag & JFS_DIR_INDEX) + len = min(namlen, DTLHDRDATALEN); + else + len = min(namlen, DTLHDRDATALEN_LEGACY); + } else { + ih = (idtentry_t *) & p->slot[si]; + si = ih->next; + namlen = ih->namlen; + name = ih->name; + len = min(namlen, DTIHDRDATALEN); + } + + key->namlen = namlen; + kname = key->name; + + /* + * move head/only segment + */ + UniStrncpy_le(kname, name, len); + + /* + * move additional segment(s) + */ + while (si >= 0) { + /* get next segment */ + t = &p->slot[si]; + kname += len; + namlen -= len; + len = min(namlen, DTSLOTDATALEN); + UniStrncpy_le(kname, t->name, len); + + si = t->next; + } +} + + +/* + * dtInsertEntry() + * + * function: allocate free slot(s) and + * write a leaf/internal entry + * + * return: entry slot index + */ +static void dtInsertEntry(dtpage_t * p, int index, component_t * key, + ddata_t * data, dtlock_t ** dtlock) +{ + dtslot_t *h, *t; + ldtentry_t *lh = 0; + idtentry_t *ih = 0; + int hsi, fsi, klen, len, nextindex; + wchar_t *kname, *name; + s8 *stbl; + pxd_t *xd; + dtlock_t *dtlck = *dtlock; + lv_t *lv; + int xsi, n; + s64 bn = 0; + metapage_t *mp = 0; + + klen = key->namlen; + kname = key->name; + + /* allocate a free slot */ + hsi = fsi = p->header.freelist; + h = &p->slot[fsi]; + p->header.freelist = h->next; + --p->header.freecnt; + + /* open new linelock */ + if (dtlck->index >= dtlck->maxcnt) + dtlck = (dtlock_t *) txLinelock(dtlck); + + lv = (lv_t *) & dtlck->lv[dtlck->index]; + lv->offset = hsi; + + /* write head/only segment */ + if (p->header.flag & BT_LEAF) { + lh = (ldtentry_t *) h; + lh->next = h->next; + lh->inumber = data->leaf.ino; /* little-endian */ + lh->namlen = klen; + name = lh->name; + if (data->leaf.ip) { + len = min(klen, DTLHDRDATALEN); + if (!(p->header.flag & BT_ROOT)) + bn = addressPXD(&p->header.self); + lh->index = cpu_to_le32(add_index(data->leaf.tid, + data->leaf.ip, + bn, index)); + } else + len = min(klen, DTLHDRDATALEN_LEGACY); + } else { + ih = (idtentry_t *) h; + ih->next = h->next; + xd = (pxd_t *) ih; + *xd = data->xd; + ih->namlen = klen; + name = ih->name; + len = min(klen, DTIHDRDATALEN); + } + + UniStrncpy_le(name, kname, len); + + n = 1; + xsi = hsi; + + /* write additional segment(s) */ + t = h; + klen -= len; + while (klen) { + /* get free slot */ + fsi = p->header.freelist; + t = &p->slot[fsi]; + p->header.freelist = t->next; + --p->header.freecnt; + + /* is next slot contiguous ? */ + if (fsi != xsi + 1) { + /* close current linelock */ + lv->length = n; + dtlck->index++; + + /* open new linelock */ + if (dtlck->index < dtlck->maxcnt) + lv++; + else { + dtlck = (dtlock_t *) txLinelock(dtlck); + lv = (lv_t *) & dtlck->lv[0]; + } + + lv->offset = fsi; + n = 0; + } + + kname += len; + len = min(klen, DTSLOTDATALEN); + UniStrncpy_le(t->name, kname, len); + + n++; + xsi = fsi; + klen -= len; + } + + /* close current linelock */ + lv->length = n; + dtlck->index++; + + *dtlock = dtlck; + + /* terminate last/only segment */ + if (h == t) { + /* single segment entry */ + if (p->header.flag & BT_LEAF) + lh->next = -1; + else + ih->next = -1; + } else + /* multi-segment entry */ + t->next = -1; + + /* if insert into middle, shift right succeeding entries in stbl */ + stbl = DT_GETSTBL(p); + nextindex = p->header.nextindex; + if (index < nextindex) { + memmove(stbl + index + 1, stbl + index, nextindex - index); + + if ((p->header.flag & BT_LEAF) && data->leaf.ip) { + /* + * Need to update slot number for entries that moved + * in the stbl + */ + mp = 0; + for (n = index + 1; n <= nextindex; n++) { + lh = (ldtentry_t *) & (p->slot[stbl[n]]); + modify_index(data->leaf.tid, data->leaf.ip, + le32_to_cpu(lh->index), bn, n, + &mp); + } + if (mp) + release_metapage(mp); + } + } + + stbl[index] = hsi; + + /* advance next available entry index of stbl */ + ++p->header.nextindex; +} + + +/* + * dtMoveEntry() + * + * function: move entries from split/left page to new/right page + * + * nextindex of dst page and freelist/freecnt of both pages + * are updated. + */ +static void dtMoveEntry(dtpage_t * sp, int si, dtpage_t * dp, + dtlock_t ** sdtlock, dtlock_t ** ddtlock, + int do_index) +{ + int ssi, next; /* src slot index */ + int di; /* dst entry index */ + int dsi; /* dst slot index */ + s8 *sstbl, *dstbl; /* sorted entry table */ + int snamlen, len; + ldtentry_t *slh, *dlh = 0; + idtentry_t *sih, *dih = 0; + dtslot_t *h, *s, *d; + dtlock_t *sdtlck = *sdtlock, *ddtlck = *ddtlock; + lv_t *slv, *dlv; + int xssi, ns, nd; + int sfsi; + + sstbl = (s8 *) & sp->slot[sp->header.stblindex]; + dstbl = (s8 *) & dp->slot[dp->header.stblindex]; + + dsi = dp->header.freelist; /* first (whole page) free slot */ + sfsi = sp->header.freelist; + + /* linelock destination entry slot */ + dlv = (lv_t *) & ddtlck->lv[ddtlck->index]; + dlv->offset = dsi; + + /* linelock source entry slot */ + slv = (lv_t *) & sdtlck->lv[sdtlck->index]; + slv->offset = sstbl[si]; + xssi = slv->offset - 1; + + /* + * move entries + */ + ns = nd = 0; + for (di = 0; si < sp->header.nextindex; si++, di++) { + ssi = sstbl[si]; + dstbl[di] = dsi; + + /* is next slot contiguous ? */ + if (ssi != xssi + 1) { + /* close current linelock */ + slv->length = ns; + sdtlck->index++; + + /* open new linelock */ + if (sdtlck->index < sdtlck->maxcnt) + slv++; + else { + sdtlck = (dtlock_t *) txLinelock(sdtlck); + slv = (lv_t *) & sdtlck->lv[0]; + } + + slv->offset = ssi; + ns = 0; + } + + /* + * move head/only segment of an entry + */ + /* get dst slot */ + h = d = &dp->slot[dsi]; + + /* get src slot and move */ + s = &sp->slot[ssi]; + if (sp->header.flag & BT_LEAF) { + /* get source entry */ + slh = (ldtentry_t *) s; + dlh = (ldtentry_t *) h; + snamlen = slh->namlen; + + if (do_index) { + len = min(snamlen, DTLHDRDATALEN); + dlh->index = slh->index; /* little-endian */ + } else + len = min(snamlen, DTLHDRDATALEN_LEGACY); + + memcpy(dlh, slh, 6 + len * 2); + + next = slh->next; + + /* update dst head/only segment next field */ + dsi++; + dlh->next = dsi; + } else { + sih = (idtentry_t *) s; + snamlen = sih->namlen; + + len = min(snamlen, DTIHDRDATALEN); + dih = (idtentry_t *) h; + memcpy(dih, sih, 10 + len * 2); + next = sih->next; + + dsi++; + dih->next = dsi; + } + + /* free src head/only segment */ + s->next = sfsi; + s->cnt = 1; + sfsi = ssi; + + ns++; + nd++; + xssi = ssi; + + /* + * move additional segment(s) of the entry + */ + snamlen -= len; + while ((ssi = next) >= 0) { + /* is next slot contiguous ? */ + if (ssi != xssi + 1) { + /* close current linelock */ + slv->length = ns; + sdtlck->index++; + + /* open new linelock */ + if (sdtlck->index < sdtlck->maxcnt) + slv++; + else { + sdtlck = + (dtlock_t *) + txLinelock(sdtlck); + slv = (lv_t *) & sdtlck->lv[0]; + } + + slv->offset = ssi; + ns = 0; + } + + /* get next source segment */ + s = &sp->slot[ssi]; + + /* get next destination free slot */ + d++; + + len = min(snamlen, DTSLOTDATALEN); + UniStrncpy(d->name, s->name, len); + + ns++; + nd++; + xssi = ssi; + + dsi++; + d->next = dsi; + + /* free source segment */ + next = s->next; + s->next = sfsi; + s->cnt = 1; + sfsi = ssi; + + snamlen -= len; + } /* end while */ + + /* terminate dst last/only segment */ + if (h == d) { + /* single segment entry */ + if (dp->header.flag & BT_LEAF) + dlh->next = -1; + else + dih->next = -1; + } else + /* multi-segment entry */ + d->next = -1; + } /* end for */ + + /* close current linelock */ + slv->length = ns; + sdtlck->index++; + *sdtlock = sdtlck; + + dlv->length = nd; + ddtlck->index++; + *ddtlock = ddtlck; + + /* update source header */ + sp->header.freelist = sfsi; + sp->header.freecnt += nd; + + /* update destination header */ + dp->header.nextindex = di; + + dp->header.freelist = dsi; + dp->header.freecnt -= nd; +} + + +/* + * dtDeleteEntry() + * + * function: free a (leaf/internal) entry + * + * log freelist header, stbl, and each segment slot of entry + * (even though last/only segment next field is modified, + * physical image logging requires all segment slots of + * the entry logged to avoid applying previous updates + * to the same slots) + */ +static void dtDeleteEntry(dtpage_t * p, int fi, dtlock_t ** dtlock) +{ + int fsi; /* free entry slot index */ + s8 *stbl; + dtslot_t *t; + int si, freecnt; + dtlock_t *dtlck = *dtlock; + lv_t *lv; + int xsi, n; + + /* get free entry slot index */ + stbl = DT_GETSTBL(p); + fsi = stbl[fi]; + + /* open new linelock */ + if (dtlck->index >= dtlck->maxcnt) + dtlck = (dtlock_t *) txLinelock(dtlck); + lv = (lv_t *) & dtlck->lv[dtlck->index]; + + lv->offset = fsi; + + /* get the head/only segment */ + t = &p->slot[fsi]; + if (p->header.flag & BT_LEAF) + si = ((ldtentry_t *) t)->next; + else + si = ((idtentry_t *) t)->next; + t->next = si; + t->cnt = 1; + + n = freecnt = 1; + xsi = fsi; + + /* find the last/only segment */ + while (si >= 0) { + /* is next slot contiguous ? */ + if (si != xsi + 1) { + /* close current linelock */ + lv->length = n; + dtlck->index++; + + /* open new linelock */ + if (dtlck->index < dtlck->maxcnt) + lv++; + else { + dtlck = (dtlock_t *) txLinelock(dtlck); + lv = (lv_t *) & dtlck->lv[0]; + } + + lv->offset = si; + n = 0; + } + + n++; + xsi = si; + freecnt++; + + t = &p->slot[si]; + t->cnt = 1; + si = t->next; + } + + /* close current linelock */ + lv->length = n; + dtlck->index++; + + *dtlock = dtlck; + + /* update freelist */ + t->next = p->header.freelist; + p->header.freelist = fsi; + p->header.freecnt += freecnt; + + /* if delete from middle, + * shift left the succedding entries in the stbl + */ + si = p->header.nextindex; + if (fi < si - 1) + memmove(&stbl[fi], &stbl[fi + 1], si - fi - 1); + + p->header.nextindex--; +} + + +/* + * dtTruncateEntry() + * + * function: truncate a (leaf/internal) entry + * + * log freelist header, stbl, and each segment slot of entry + * (even though last/only segment next field is modified, + * physical image logging requires all segment slots of + * the entry logged to avoid applying previous updates + * to the same slots) + */ +static void dtTruncateEntry(dtpage_t * p, int ti, dtlock_t ** dtlock) +{ + int tsi; /* truncate entry slot index */ + s8 *stbl; + dtslot_t *t; + int si, freecnt; + dtlock_t *dtlck = *dtlock; + lv_t *lv; + int fsi, xsi, n; + + /* get free entry slot index */ + stbl = DT_GETSTBL(p); + tsi = stbl[ti]; + + /* open new linelock */ + if (dtlck->index >= dtlck->maxcnt) + dtlck = (dtlock_t *) txLinelock(dtlck); + lv = (lv_t *) & dtlck->lv[dtlck->index]; + + lv->offset = tsi; + + /* get the head/only segment */ + t = &p->slot[tsi]; + ASSERT(p->header.flag & BT_INTERNAL); + ((idtentry_t *) t)->namlen = 0; + si = ((idtentry_t *) t)->next; + ((idtentry_t *) t)->next = -1; + + n = 1; + freecnt = 0; + fsi = si; + xsi = tsi; + + /* find the last/only segment */ + while (si >= 0) { + /* is next slot contiguous ? */ + if (si != xsi + 1) { + /* close current linelock */ + lv->length = n; + dtlck->index++; + + /* open new linelock */ + if (dtlck->index < dtlck->maxcnt) + lv++; + else { + dtlck = (dtlock_t *) txLinelock(dtlck); + lv = (lv_t *) & dtlck->lv[0]; + } + + lv->offset = si; + n = 0; + } + + n++; + xsi = si; + freecnt++; + + t = &p->slot[si]; + t->cnt = 1; + si = t->next; + } + + /* close current linelock */ + lv->length = n; + dtlck->index++; + + *dtlock = dtlck; + + /* update freelist */ + if (freecnt == 0) + return; + t->next = p->header.freelist; + p->header.freelist = fsi; + p->header.freecnt += freecnt; +} + + +/* + * dtLinelockFreelist() + */ +static void dtLinelockFreelist(dtpage_t * p, /* directory page */ + int m, /* max slot index */ + dtlock_t ** dtlock) +{ + int fsi; /* free entry slot index */ + dtslot_t *t; + int si; + dtlock_t *dtlck = *dtlock; + lv_t *lv; + int xsi, n; + + /* get free entry slot index */ + fsi = p->header.freelist; + + /* open new linelock */ + if (dtlck->index >= dtlck->maxcnt) + dtlck = (dtlock_t *) txLinelock(dtlck); + lv = (lv_t *) & dtlck->lv[dtlck->index]; + + lv->offset = fsi; + + n = 1; + xsi = fsi; + + t = &p->slot[fsi]; + si = t->next; + + /* find the last/only segment */ + while (si < m && si >= 0) { + /* is next slot contiguous ? */ + if (si != xsi + 1) { + /* close current linelock */ + lv->length = n; + dtlck->index++; + + /* open new linelock */ + if (dtlck->index < dtlck->maxcnt) + lv++; + else { + dtlck = (dtlock_t *) txLinelock(dtlck); + lv = (lv_t *) & dtlck->lv[0]; + } + + lv->offset = si; + n = 0; + } + + n++; + xsi = si; + + t = &p->slot[si]; + si = t->next; + } + + /* close current linelock */ + lv->length = n; + dtlck->index++; + + *dtlock = dtlck; +} + + +/* + * NAME: dtModify + * + * FUNCTION: Modify the inode number part of a directory entry + * + * PARAMETERS: + * tid - Transaction id + * ip - Inode of parent directory + * key - Name of entry to be modified + * orig_ino - Original inode number expected in entry + * new_ino - New inode number to put into entry + * flag - JFS_RENAME + * + * RETURNS: + * ESTALE - If entry found does not match orig_ino passed in + * ENOENT - If no entry can be found to match key + * 0 - If successfully modified entry + */ +int dtModify(tid_t tid, struct inode *ip, + component_t * key, ino_t * orig_ino, ino_t new_ino, int flag) +{ + int rc; + s64 bn; + metapage_t *mp; + dtpage_t *p; + int index; + btstack_t btstack; + tlock_t *tlck; + dtlock_t *dtlck; + lv_t *lv; + s8 *stbl; + int entry_si; /* entry slot index */ + ldtentry_t *entry; + + /* + * search for the entry to modify: + * + * dtSearch() returns (leaf page pinned, index at which to modify). + */ + if ((rc = dtSearch(ip, key, orig_ino, &btstack, flag))) + return rc; + + /* retrieve search result */ + DT_GETSEARCH(ip, btstack.top, bn, mp, p, index); + + BT_MARK_DIRTY(mp, ip); + /* + * acquire a transaction lock on the leaf page of named entry + */ + tlck = txLock(tid, ip, mp, tlckDTREE | tlckENTRY); + dtlck = (dtlock_t *) & tlck->lock; + + /* get slot index of the entry */ + stbl = DT_GETSTBL(p); + entry_si = stbl[index]; + + /* linelock entry */ + ASSERT(dtlck->index == 0); + lv = (lv_t *) & dtlck->lv[0]; + lv->offset = entry_si; + lv->length = 1; + dtlck->index++; + + /* get the head/only segment */ + entry = (ldtentry_t *) & p->slot[entry_si]; + + /* substitute the inode number of the entry */ + entry->inumber = cpu_to_le32(new_ino); + + /* unpin the leaf page */ + DT_PUTPAGE(mp); + + return 0; +} + +#ifdef _JFS_DEBUG_DTREE +/* + * dtDisplayTree() + * + * function: traverse forward + */ +int dtDisplayTree(struct inode *ip) +{ + int rc; + metapage_t *mp; + dtpage_t *p; + s64 bn, pbn; + int index, lastindex, v, h; + pxd_t *xd; + btstack_t btstack; + btframe_t *btsp; + btframe_t *parent; + u8 *stbl; + int psize = 256; + + printk("display B+-tree.\n"); + + /* clear stack */ + btsp = btstack.stack; + + /* + * start with root + * + * root resides in the inode + */ + bn = 0; + v = h = 0; + + /* + * first access of each page: + */ + newPage: + DT_GETPAGE(ip, bn, mp, psize, p, rc); + if (rc) + return rc; + + /* process entries forward from first index */ + index = 0; + lastindex = p->header.nextindex - 1; + + if (p->header.flag & BT_INTERNAL) { + /* + * first access of each internal page + */ + printf("internal page "); + dtDisplayPage(ip, bn, p); + + goto getChild; + } else { /* (p->header.flag & BT_LEAF) */ + + /* + * first access of each leaf page + */ + printf("leaf page "); + dtDisplayPage(ip, bn, p); + + /* + * process leaf page entries + * + for ( ; index <= lastindex; index++) + { + } + */ + + /* unpin the leaf page */ + DT_PUTPAGE(mp); + } + + /* + * go back up to the parent page + */ + getParent: + /* pop/restore parent entry for the current child page */ + if ((parent = (btsp == btstack.stack ? NULL : --btsp)) == NULL) + /* current page must have been root */ + return; + + /* + * parent page scan completed + */ + if ((index = parent->index) == (lastindex = parent->lastindex)) { + /* go back up to the parent page */ + goto getParent; + } + + /* + * parent page has entries remaining + */ + /* get back the parent page */ + bn = parent->bn; + /* v = parent->level; */ + DT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + /* get next parent entry */ + index++; + + /* + * internal page: go down to child page of current entry + */ + getChild: + /* push/save current parent entry for the child page */ + btsp->bn = pbn = bn; + btsp->index = index; + btsp->lastindex = lastindex; + /* btsp->level = v; */ + /* btsp->node = h; */ + ++btsp; + + /* get current entry for the child page */ + stbl = DT_GETSTBL(p); + xd = (pxd_t *) & p->slot[stbl[index]]; + + /* + * first access of each internal entry: + */ + + /* get child page */ + bn = addressPXD(xd); + psize = lengthPXD(xd) << ip->i_ipmnt->i_l2bsize; + + printk("traverse down 0x%Lx[%d]->0x%Lx\n", pbn, index, bn); + v++; + h = index; + + /* release parent page */ + DT_PUTPAGE(mp); + + /* process the child page */ + goto newPage; +} + + +/* + * dtDisplayPage() + * + * function: display page + */ +int dtDisplayPage(struct inode *ip, s64 bn, dtpage_t * p) +{ + int rc; + metapage_t *mp; + ldtentry_t *lh; + idtentry_t *ih; + pxd_t *xd; + int i, j; + u8 *stbl; + wchar_t name[JFS_NAME_MAX + 1]; + component_t key = { 0, name }; + int freepage = 0; + + if (p == NULL) { + freepage = 1; + DT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + } + + /* display page control */ + printk("bn:0x%Lx flag:0x%08x nextindex:%d\n", + bn, p->header.flag, p->header.nextindex); + + /* display entries */ + stbl = DT_GETSTBL(p); + for (i = 0, j = 1; i < p->header.nextindex; i++, j++) { + dtGetKey(p, i, &key, JFS_SBI(ip->i_sb)->mntflag); + key.name[key.namlen] = '\0'; + if (p->header.flag & BT_LEAF) { + lh = (ldtentry_t *) & p->slot[stbl[i]]; + printf("\t[%d] %s:%d", i, key.name, + le32_to_cpu(lh->inumber)); + } else { + ih = (idtentry_t *) & p->slot[stbl[i]]; + xd = (pxd_t *) ih; + bn = addressPXD(xd); + printf("\t[%d] %s:0x%Lx", i, key.name, bn); + } + + if (j == 4) { + printf("\n"); + j = 0; + } + } + + printf("\n"); + + if (freepage) + DT_PUTPAGE(mp); + + return 0; +} +#endif /* _JFS_DEBUG_DTREE */ diff -Nru a/fs/jfs/jfs_dtree.h b/fs/jfs/jfs_dtree.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_dtree.h Thu Mar 7 18:17:47 2002 @@ -0,0 +1,288 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ + +/* + * Change History : + * + */ + +#ifndef _H_JFS_DTREE +#define _H_JFS_DTREE + +/* + * jfs_dtree.h: directory B+-tree manager + */ + +#include "jfs_btree.h" + +typedef union { + struct { + tid_t tid; + struct inode *ip; + u32 ino; + } leaf; + pxd_t xd; +} ddata_t; + + +/* + * entry segment/slot + * + * an entry consists of type dependent head/only segment/slot and + * additional segments/slots linked vi next field; + * N.B. last/only segment of entry is terminated by next = -1; + */ +/* + * directory page slot + */ +typedef struct { + s8 next; /* 1: */ + s8 cnt; /* 1: */ + wchar_t name[15]; /* 30: */ +} dtslot_t; /* (32) */ + + +#define DATASLOTSIZE 16 +#define L2DATASLOTSIZE 4 +#define DTSLOTSIZE 32 +#define L2DTSLOTSIZE 5 +#define DTSLOTHDRSIZE 2 +#define DTSLOTDATASIZE 30 +#define DTSLOTDATALEN 15 + +/* + * internal node entry head/only segment + */ +typedef struct { + pxd_t xd; /* 8: child extent descriptor */ + + s8 next; /* 1: */ + u8 namlen; /* 1: */ + wchar_t name[11]; /* 22: 2-byte aligned */ +} idtentry_t; /* (32) */ + +#define DTIHDRSIZE 10 +#define DTIHDRDATALEN 11 + +/* compute number of slots for entry */ +#define NDTINTERNAL(klen) ( ((4 + (klen)) + (15 - 1)) / 15 ) + + +/* + * leaf node entry head/only segment + * + * For legacy filesystems, name contains 13 wchars -- no index field + */ +typedef struct { + u32 inumber; /* 4: 4-byte aligned */ + s8 next; /* 1: */ + u8 namlen; /* 1: */ + wchar_t name[11]; /* 22: 2-byte aligned */ + u32 index; /* 4: index into dir_table */ +} ldtentry_t; /* (32) */ + +#define DTLHDRSIZE 6 +#define DTLHDRDATALEN_LEGACY 13 /* Old (OS/2) format */ +#define DTLHDRDATALEN 11 + +/* + * dir_table used for directory traversal during readdir + */ + +/* + * Keep persistent index for directory entries + */ +#define DO_INDEX(INODE) (JFS_SBI((INODE)->i_sb)->mntflag & JFS_DIR_INDEX) + +/* + * Maximum entry in inline directory table + */ +#define MAX_INLINE_DIRTABLE_ENTRY 13 + +typedef struct dir_table_slot { + u8 rsrvd; /* 1: */ + u8 flag; /* 1: 0 if free */ + u8 slot; /* 1: slot within leaf page of entry */ + u8 addr1; /* 1: upper 8 bits of leaf page address */ + u32 addr2; /* 4: lower 32 bits of leaf page address -OR- + index of next entry when this entry was deleted */ +} dir_table_slot_t; /* (8) */ + +/* + * flag values + */ +#define DIR_INDEX_VALID 1 +#define DIR_INDEX_FREE 0 + +#define DTSaddress(dir_table_slot, address64)\ +{\ + (dir_table_slot)->addr1 = ((u64)address64) >> 32;\ + (dir_table_slot)->addr2 = __cpu_to_le32((address64) & 0xffffffff);\ +} + +#define addressDTS(dts)\ + ( ((s64)((dts)->addr1)) << 32 | __le32_to_cpu((dts)->addr2) ) + +/* compute number of slots for entry */ +#define NDTLEAF_LEGACY(klen) ( ((2 + (klen)) + (15 - 1)) / 15 ) +#define NDTLEAF NDTINTERNAL + + +/* + * directory root page (in-line in on-disk inode): + * + * cf. dtpage_t below. + */ +typedef union { + struct { + dasd_t DASD; /* 16: DASD limit/usage info F226941 */ + + u8 flag; /* 1: */ + u8 nextindex; /* 1: next free entry in stbl */ + s8 freecnt; /* 1: free count */ + s8 freelist; /* 1: freelist header */ + + u32 idotdot; /* 4: parent inode number */ + + s8 stbl[8]; /* 8: sorted entry index table */ + } header; /* (32) */ + + dtslot_t slot[9]; +} dtroot_t; + +#define PARENT(IP) \ + (le32_to_cpu(JFS_IP(IP)->i_dtroot.header.idotdot)) + +#define DTROOTMAXSLOT 9 + +#define dtEmpty(IP) (JFS_IP(IP)->i_dtroot.header.nextindex == 0) + + +/* + * directory regular page: + * + * entry slot array of 32 byte slot + * + * sorted entry slot index table (stbl): + * contiguous slots at slot specified by stblindex, + * 1-byte per entry + * 512 byte block: 16 entry tbl (1 slot) + * 1024 byte block: 32 entry tbl (1 slot) + * 2048 byte block: 64 entry tbl (2 slot) + * 4096 byte block: 128 entry tbl (4 slot) + * + * data area: + * 512 byte block: 16 - 2 = 14 slot + * 1024 byte block: 32 - 2 = 30 slot + * 2048 byte block: 64 - 3 = 61 slot + * 4096 byte block: 128 - 5 = 123 slot + * + * N.B. index is 0-based; index fields refer to slot index + * except nextindex which refers to entry index in stbl; + * end of entry stot list or freelist is marked with -1. + */ +typedef union { + struct { + s64 next; /* 8: next sibling */ + s64 prev; /* 8: previous sibling */ + + u8 flag; /* 1: */ + u8 nextindex; /* 1: next entry index in stbl */ + s8 freecnt; /* 1: */ + s8 freelist; /* 1: slot index of head of freelist */ + + u8 maxslot; /* 1: number of slots in page slot[] */ + u8 stblindex; /* 1: slot index of start of stbl */ + u8 rsrvd[2]; /* 2: */ + + pxd_t self; /* 8: self pxd */ + } header; /* (32) */ + + dtslot_t slot[128]; +} dtpage_t; + +#define DTPAGEMAXSLOT 128 + +#define DT8THPGNODEBYTES 512 +#define DT8THPGNODETSLOTS 1 +#define DT8THPGNODESLOTS 16 + +#define DTQTRPGNODEBYTES 1024 +#define DTQTRPGNODETSLOTS 1 +#define DTQTRPGNODESLOTS 32 + +#define DTHALFPGNODEBYTES 2048 +#define DTHALFPGNODETSLOTS 2 +#define DTHALFPGNODESLOTS 64 + +#define DTFULLPGNODEBYTES 4096 +#define DTFULLPGNODETSLOTS 4 +#define DTFULLPGNODESLOTS 128 + +#define DTENTRYSTART 1 + +/* get sorted entry table of the page */ +#define DT_GETSTBL(p) ( ((p)->header.flag & BT_ROOT) ?\ + ((dtroot_t *)(p))->header.stbl : \ + (s8 *)&(p)->slot[(p)->header.stblindex] ) + +/* + * Flags for dtSearch + */ +#define JFS_CREATE 1 +#define JFS_LOOKUP 2 +#define JFS_REMOVE 3 +#define JFS_RENAME 4 + +#define DIRENTSIZ(namlen) \ + ( (sizeof(struct dirent) - 2*(JFS_NAME_MAX+1) + 2*((namlen)+1) + 3) &~ 3 ) + +/* + * Maximum file offset for directories. + */ +#define DIREND INT_MAX + +/* + * external declarations + */ +extern void dtInitRoot(tid_t tid, struct inode *ip, u32 idotdot); + +extern int dtSearch(struct inode *ip, component_t * key, + ino_t * data, btstack_t * btstack, int flag); + +extern int dtInsert(tid_t tid, struct inode *ip, + component_t * key, ino_t * ino, btstack_t * btstack); + +extern int dtDelete(tid_t tid, + struct inode *ip, component_t * key, ino_t * data, int flag); + +extern int dtRelocate(tid_t tid, + struct inode *ip, s64 lmxaddr, pxd_t * opxd, s64 nxaddr); + +extern int dtModify(tid_t tid, struct inode *ip, + component_t * key, ino_t * orig_ino, ino_t new_ino, int flag); + +extern int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir); + +#ifdef _JFS_DEBUG_DTREE +extern int dtDisplayTree(struct inode *ip); + +extern int dtDisplayPage(struct inode *ip, s64 bn, dtpage_t * p); +#endif /* _JFS_DEBUG_DTREE */ + +#endif /* !_H_JFS_DTREE */ diff -Nru a/fs/jfs/jfs_extendfs.h b/fs/jfs/jfs_extendfs.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_extendfs.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,39 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ +#ifndef _H_JFS_EXTENDFS +#define _H_JFS_EXTENDFS + +/* + * jfs_extendfs.h + */ +/* + * extendfs parameter list + */ +typedef struct { + u32 flag; /* 4: */ + u8 dev; /* 1: */ + u8 pad[3]; /* 3: */ + s64 LVSize; /* 8: LV size in LV block */ + s64 FSSize; /* 8: FS size in LV block */ + s32 LogSize; /* 4: inlinelog size in LV block */ +} extendfs_t; /* (28) */ + +/* plist flag */ +#define EXTENDFS_QUERY 0x00000001 + +#endif /* _H_JFS_EXTENDFS */ diff -Nru a/fs/jfs/jfs_extent.c b/fs/jfs/jfs_extent.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_extent.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,637 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * + * + * + * Module: jfs_extent.c: + */ + +#include +#include "jfs_incore.h" +#include "jfs_dmap.h" +#include "jfs_extent.h" +#include "jfs_debug.h" + +/* + * forward references + */ +static int extBalloc(struct inode *, s64, s64 *, s64 *); +static int extBrealloc(struct inode *, s64, s64, s64 *, s64 *); +int extRecord(struct inode *, xad_t *); +static s64 extRoundDown(s64 nb); + +/* + * external references + */ +extern int dbExtend(struct inode *, s64, s64, s64); +extern int jfs_commit_inode(struct inode *, int); + + +#define DPD(a) (printk("(a): %d\n",(a))) +#define DPC(a) (printk("(a): %c\n",(a))) +#define DPL1(a) \ +{ \ + if ((a) >> 32) \ + printk("(a): %x%08x ",(a)); \ + else \ + printk("(a): %x ",(a) << 32); \ +} +#define DPL(a) \ +{ \ + if ((a) >> 32) \ + printk("(a): %x%08x\n",(a)); \ + else \ + printk("(a): %x\n",(a) << 32); \ +} + +#define DPD1(a) (printk("(a): %d ",(a))) +#define DPX(a) (printk("(a): %08x\n",(a))) +#define DPX1(a) (printk("(a): %08x ",(a))) +#define DPS(a) (printk("%s\n",(a))) +#define DPE(a) (printk("\nENTERING: %s\n",(a))) +#define DPE1(a) (printk("\nENTERING: %s",(a))) +#define DPS1(a) (printk(" %s ",(a))) + + +/* + * NAME: extAlloc() + * + * FUNCTION: allocate an extent for a specified page range within a + * file. + * + * PARAMETERS: + * ip - the inode of the file. + * xlen - requested extent length. + * pno - the starting page number with the file. + * xp - pointer to an xad. on entry, xad describes an + * extent that is used as an allocation hint if the + * xaddr of the xad is non-zero. on successful exit, + * the xad describes the newly allocated extent. + * abnr - boolean_t indicating whether the newly allocated extent + * should be marked as allocated but not recorded. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error. + * ENOSPC - insufficient disk resources. + */ +int +extAlloc(struct inode *ip, s64 xlen, s64 pno, xad_t * xp, boolean_t abnr) +{ + struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb); + s64 nxlen, nxaddr, xoff, hint, xaddr = 0; + int rc, nbperpage; + int xflag; + + /* This blocks if we are low on resources */ + txBeginAnon(ip->i_sb); + + /* validate extent length */ + if (xlen > MAXXLEN) + xlen = MAXXLEN; + + /* get the number of blocks per page */ + nbperpage = sbi->nbperpage; + + /* get the page's starting extent offset */ + xoff = pno << sbi->l2nbperpage; + + /* check if an allocation hint was provided */ + if ((hint = addressXAD(xp))) { + /* get the size of the extent described by the hint */ + nxlen = lengthXAD(xp); + + /* check if the hint is for the portion of the file + * immediately previous to the current allocation + * request and if hint extent has the same abnr + * value as the current request. if so, we can + * extend the hint extent to include the current + * extent if we can allocate the blocks immediately + * following the hint extent. + */ + if (offsetXAD(xp) + nxlen == xoff && + abnr == ((xp->flag & XAD_NOTRECORDED) ? TRUE : FALSE)) + xaddr = hint + nxlen; + + /* adjust the hint to the last block of the extent */ + hint += (nxlen - 1); + } + + /* allocate the disk blocks for the extent. initially, extBalloc() + * will try to allocate disk blocks for the requested size (xlen). + * if this fails (xlen contigious free blocks not avaliable), it'll + * try to allocate a smaller number of blocks (producing a smaller + * extent), with this smaller number of blocks consisting of the + * requested number of blocks rounded down to the next smaller + * power of 2 number (i.e. 16 -> 8). it'll continue to round down + * and retry the allocation until the number of blocks to allocate + * is smaller than the number of blocks per page. + */ + nxlen = xlen; + if ((rc = + extBalloc(ip, hint ? hint : INOHINT(ip), &nxlen, &nxaddr))) { + return (rc); + } + + /* determine the value of the extent flag */ + xflag = (abnr == TRUE) ? XAD_NOTRECORDED : 0; + + /* if we can extend the hint extent to cover the current request, + * extend it. otherwise, insert a new extent to + * cover the current request. + */ + if (xaddr && xaddr == nxaddr) + rc = xtExtend(0, ip, xoff, (int) nxlen, 0); + else + rc = xtInsert(0, ip, xflag, xoff, (int) nxlen, &nxaddr, 0); + + /* if the extend or insert failed, + * free the newly allocated blocks and return the error. + */ + if (rc) { + dbFree(ip, nxaddr, nxlen); + return (rc); + } + + /* update the number of blocks allocated to the file */ + ip->i_blocks += LBLK2PBLK(ip->i_sb, nxlen); + + /* set the results of the extent allocation */ + XADaddress(xp, nxaddr); + XADlength(xp, nxlen); + XADoffset(xp, xoff); + xp->flag = xflag; + + mark_inode_dirty(ip); + + /* + * COMMIT_SyncList flags an anonymous tlock on page that is on + * sync list. + * We need to commit the inode to get the page written disk. + */ + if (test_and_clear_cflag(COMMIT_Synclist,ip)) + jfs_commit_inode(ip, 0); + + return (0); +} + + +/* + * NAME: extRealloc() + * + * FUNCTION: extend the allocation of a file extent containing a + * partial back last page. + * + * PARAMETERS: + * ip - the inode of the file. + * cp - cbuf for the partial backed last page. + * xlen - request size of the resulting extent. + * xp - pointer to an xad. on successful exit, the xad + * describes the newly allocated extent. + * abnr - boolean_t indicating whether the newly allocated extent + * should be marked as allocated but not recorded. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error. + * ENOSPC - insufficient disk resources. + */ +int extRealloc(struct inode *ip, s64 nxlen, xad_t * xp, boolean_t abnr) +{ + struct super_block *sb = ip->i_sb; + s64 xaddr, xlen, nxaddr, delta, xoff; + s64 ntail, nextend, ninsert; + int rc, nbperpage = JFS_SBI(sb)->nbperpage; + int xflag; + + /* This blocks if we are low on resources */ + txBeginAnon(ip->i_sb); + + /* validate extent length */ + if (nxlen > MAXXLEN) + nxlen = MAXXLEN; + + /* get the extend (partial) page's disk block address and + * number of blocks. + */ + xaddr = addressXAD(xp); + xlen = lengthXAD(xp); + xoff = offsetXAD(xp); + + /* if the extend page is abnr and if the request is for + * the extent to be allocated and recorded, + * make the page allocated and recorded. + */ + if ((xp->flag & XAD_NOTRECORDED) && !abnr) { + xp->flag = 0; + if ((rc = xtUpdate(0, ip, xp))) + return (rc); + } + + /* try to allocated the request number of blocks for the + * extent. dbRealloc() first tries to satisfy the request + * by extending the allocation in place. otherwise, it will + * try to allocate a new set of blocks large enough for the + * request. in satisfying a request, dbReAlloc() may allocate + * less than what was request but will always allocate enough + * space as to satisfy the extend page. + */ + if ((rc = extBrealloc(ip, xaddr, xlen, &nxlen, &nxaddr))) + return (rc); + + delta = nxlen - xlen; + + /* check if the extend page is not abnr but the request is abnr + * and the allocated disk space is for more than one page. if this + * is the case, there is a miss match of abnr between the extend page + * and the one or more pages following the extend page. as a result, + * two extents will have to be manipulated. the first will be that + * of the extent of the extend page and will be manipulated thru + * an xtExtend() or an xtTailgate(), depending upon whether the + * disk allocation occurred as an inplace extension. the second + * extent will be manipulated (created) through an xtInsert() and + * will be for the pages following the extend page. + */ + if (abnr && (!(xp->flag & XAD_NOTRECORDED)) && (nxlen > nbperpage)) { + ntail = nbperpage; + nextend = ntail - xlen; + ninsert = nxlen - nbperpage; + + xflag = XAD_NOTRECORDED; + } else { + ntail = nxlen; + nextend = delta; + ninsert = 0; + + xflag = xp->flag; + } + + /* if we were able to extend the disk allocation in place, + * extend the extent. otherwise, move the extent to a + * new disk location. + */ + if (xaddr == nxaddr) { + /* extend the extent */ + if ((rc = xtExtend(0, ip, xoff + xlen, (int) nextend, 0))) { + dbFree(ip, xaddr + xlen, delta); + return (rc); + } + } else { + /* + * move the extent to a new location: + * + * xtTailgate() accounts for relocated tail extent; + */ + if ((rc = xtTailgate(0, ip, xoff, (int) ntail, nxaddr, 0))) { + dbFree(ip, nxaddr, nxlen); + return (rc); + } + } + + + /* check if we need to also insert a new extent */ + if (ninsert) { + /* perform the insert. if it fails, free the blocks + * to be inserted and make it appear that we only did + * the xtExtend() or xtTailgate() above. + */ + xaddr = nxaddr + ntail; + if (xtInsert (0, ip, xflag, xoff + ntail, (int) ninsert, + &xaddr, 0)) { + dbFree(ip, xaddr, (s64) ninsert); + delta = nextend; + nxlen = ntail; + xflag = 0; + } + } + + /* update the inode with the number of blocks allocated */ + ip->i_blocks += LBLK2PBLK(sb, delta); + + /* set the return results */ + XADaddress(xp, nxaddr); + XADlength(xp, nxlen); + XADoffset(xp, xoff); + xp->flag = xflag; + + mark_inode_dirty(ip); + + return (0); +} + + +/* + * NAME: extHint() + * + * FUNCTION: produce an extent allocation hint for a file offset. + * + * PARAMETERS: + * ip - the inode of the file. + * offset - file offset for which the hint is needed. + * xp - pointer to the xad that is to be filled in with + * the hint. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error. + */ +int extHint(struct inode *ip, s64 offset, xad_t * xp) +{ + struct super_block *sb = ip->i_sb; + xadlist_t xadl; + lxdlist_t lxdl; + lxd_t lxd; + s64 prev; + int rc, nbperpage = JFS_SBI(sb)->nbperpage; + + /* init the hint as "no hint provided" */ + XADaddress(xp, 0); + + /* determine the starting extent offset of the page previous + * to the page containing the offset. + */ + prev = ((offset & ~POFFSET) >> JFS_SBI(sb)->l2bsize) - nbperpage; + + /* if the offsets in the first page of the file, + * no hint provided. + */ + if (prev < 0) + return (0); + + /* prepare to lookup the previous page's extent info */ + lxdl.maxnlxd = 1; + lxdl.nlxd = 1; + lxdl.lxd = &lxd; + LXDoffset(&lxd, prev) + LXDlength(&lxd, nbperpage); + + xadl.maxnxad = 1; + xadl.nxad = 0; + xadl.xad = xp; + + /* perform the lookup */ + if ((rc = xtLookupList(ip, &lxdl, &xadl, 0))) + return (rc); + + /* check if not extent exists for the previous page. + * this is possible for sparse files. + */ + if (xadl.nxad == 0) { +// assert(ISSPARSE(ip)); + return (0); + } + + /* only preserve the abnr flag within the xad flags + * of the returned hint. + */ + xp->flag &= XAD_NOTRECORDED; + + assert(xadl.nxad == 1); + assert(lengthXAD(xp) == nbperpage); + + return (0); +} + + +/* + * NAME: extRecord() + * + * FUNCTION: change a page with a file from not recorded to recorded. + * + * PARAMETERS: + * ip - inode of the file. + * cp - cbuf of the file page. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error. + * ENOSPC - insufficient disk resources. + */ +int extRecord(struct inode *ip, xad_t * xp) +{ + int rc; + + txBeginAnon(ip->i_sb); + + /* update the extent */ + if ((rc = xtUpdate(0, ip, xp))) + return (rc); + +#ifdef _STILL_TO_PORT + /* no longer abnr */ + cp->cm_abnr = FALSE; + + /* mark the cbuf as modified */ + cp->cm_modified = TRUE; +#endif /* _STILL_TO_PORT */ + + return (0); +} + + +/* + * NAME: extFill() + * + * FUNCTION: allocate disk space for a file page that represents + * a file hole. + * + * PARAMETERS: + * ip - the inode of the file. + * cp - cbuf of the file page represent the hole. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error. + * ENOSPC - insufficient disk resources. + */ +int extFill(struct inode *ip, xad_t * xp) +{ + int rc, nbperpage = JFS_SBI(ip->i_sb)->nbperpage; + s64 blkno = offsetXAD(xp) >> ip->i_blksize; + +// assert(ISSPARSE(ip)); + + /* initialize the extent allocation hint */ + XADaddress(xp, 0); + + /* allocate an extent to fill the hole */ + if ((rc = extAlloc(ip, nbperpage, blkno, xp, FALSE))) + return (rc); + + assert(lengthPXD(xp) == nbperpage); + + return (0); +} + + +/* + * NAME: extBalloc() + * + * FUNCTION: allocate disk blocks to form an extent. + * + * initially, we will try to allocate disk blocks for the + * requested size (nblocks). if this fails (nblocks + * contigious free blocks not avaliable), we'll try to allocate + * a smaller number of blocks (producing a smaller extent), with + * this smaller number of blocks consisting of the requested + * number of blocks rounded down to the next smaller power of 2 + * number (i.e. 16 -> 8). we'll continue to round down and + * retry the allocation until the number of blocks to allocate + * is smaller than the number of blocks per page. + * + * PARAMETERS: + * ip - the inode of the file. + * hint - disk block number to be used as an allocation hint. + * *nblocks - pointer to an s64 value. on entry, this value specifies + * the desired number of block to be allocated. on successful + * exit, this value is set to the number of blocks actually + * allocated. + * blkno - pointer to a block address that is filled in on successful + * return with the starting block number of the newly + * allocated block range. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error. + * ENOSPC - insufficient disk resources. + */ +static int +extBalloc(struct inode *ip, s64 hint, s64 * nblocks, s64 * blkno) +{ + s64 nb, nblks, daddr, max; + int rc, nbperpage = JFS_SBI(ip->i_sb)->nbperpage; + bmap_t *mp = JFS_SBI(ip->i_sb)->bmap; + + /* get the number of blocks to initially attempt to allocate. + * we'll first try the number of blocks requested unless this + * number is greater than the maximum number of contigious free + * blocks in the map. in that case, we'll start off with the + * maximum free. + */ + max = (s64) 1 << mp->db_maxfreebud; + if (*nblocks >= max && *nblocks > nbperpage) + nb = nblks = (max > nbperpage) ? max : nbperpage; + else + nb = nblks = *nblocks; + + /* try to allocate blocks */ + while ((rc = dbAlloc(ip, hint, nb, &daddr))) { + /* if something other than an out of space error, + * stop and return this error. + */ + if (rc != ENOSPC) + return (rc); + + /* decrease the allocation request size */ + nb = min(nblks, extRoundDown(nb)); + + /* give up if we cannot cover a page */ + if (nb < nbperpage) + return (rc); + } + + *nblocks = nb; + *blkno = daddr; + + return (0); +} + + +/* + * NAME: extBrealloc() + * + * FUNCTION: attempt to extend an extent's allocation. + * + * initially, we will try to extend the extent's allocation + * in place. if this fails, we'll try to move the extent + * to a new set of blocks. if moving the extent, we initially + * will try to allocate disk blocks for the requested size + * (nnew). if this fails (nnew contigious free blocks not + * avaliable), we'll try to allocate a smaller number of + * blocks (producing a smaller extent), with this smaller + * number of blocks consisting of the requested number of + * blocks rounded down to the next smaller power of 2 + * number (i.e. 16 -> 8). we'll continue to round down and + * retry the allocation until the number of blocks to allocate + * is smaller than the number of blocks per page. + * + * PARAMETERS: + * ip - the inode of the file. + * blkno - starting block number of the extents current allocation. + * nblks - number of blocks within the extents current allocation. + * newnblks - pointer to a s64 value. on entry, this value is the + * the new desired extent size (number of blocks). on + * successful exit, this value is set to the extent's actual + * new size (new number of blocks). + * newblkno - the starting block number of the extents new allocation. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error. + * ENOSPC - insufficient disk resources. + */ +static int +extBrealloc(struct inode *ip, + s64 blkno, s64 nblks, s64 * newnblks, s64 * newblkno) +{ + int rc; + + /* try to extend in place */ + if ((rc = dbExtend(ip, blkno, nblks, *newnblks - nblks)) == 0) { + *newblkno = blkno; + return (0); + } else { + if (rc != ENOSPC) + return (rc); + } + + /* in place extension not possible. + * try to move the extent to a new set of blocks. + */ + return (extBalloc(ip, blkno, newnblks, newblkno)); +} + + +/* + * NAME: extRoundDown() + * + * FUNCTION: round down a specified number of blocks to the next + * smallest power of 2 number. + * + * PARAMETERS: + * nb - the inode of the file. + * + * RETURN VALUES: + * next smallest power of 2 number. + */ +static s64 extRoundDown(s64 nb) +{ + int i; + u64 m, k; + + for (i = 0, m = (u64) 1 << 63; i < 64; i++, m >>= 1) { + if (m & nb) + break; + } + + i = 63 - i; + k = (u64) 1 << i; + k = ((k - 1) & nb) ? k : k >> 1; + + return (k); +} diff -Nru a/fs/jfs/jfs_extent.h b/fs/jfs/jfs_extent.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_extent.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,31 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ +#ifndef _H_JFS_EXTENT +#define _H_JFS_EXTENT + +/* get block allocation allocation hint as location of disk inode */ +#define INOHINT(ip) \ + (addressPXD(&(JFS_IP(ip)->ixpxd)) + lengthPXD(&(JFS_IP(ip)->ixpxd)) - 1) + +extern int extAlloc(struct inode *, s64, s64, xad_t *, boolean_t); +extern int extFill(struct inode *, xad_t *); +extern int extHint(struct inode *, s64, xad_t *); +extern int extRealloc(struct inode *, s64, xad_t *, boolean_t); +extern int extRecord(struct inode *, xad_t *); + +#endif /* _H_JFS_EXTENT */ diff -Nru a/fs/jfs/jfs_filsys.h b/fs/jfs/jfs_filsys.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_filsys.h Thu Mar 7 18:17:47 2002 @@ -0,0 +1,274 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * +*/ + +#ifndef _H_JFS_FILSYS +#define _H_JFS_FILSYS + +/* + * jfs_filsys.h + * + * file system (implementation-dependent) constants + * + * refer to for system wide implementation-dependent constants + */ + +/* + * file system option (superblock flag) + */ +/* platform option (conditional compilation) */ +#define JFS_AIX 0x80000000 /* AIX support */ +/* POSIX name/directory support */ + +#define JFS_OS2 0x40000000 /* OS/2 support */ +/* case-insensitive name/directory support */ + +#define JFS_DFS 0x20000000 /* DCE DFS LFS support */ + +#define JFS_LINUX 0x10000000 /* Linux support */ +/* case-sensitive name/directory support */ + +/* directory option */ +#define JFS_UNICODE 0x00000001 /* unicode name */ + +/* commit option */ +#define JFS_COMMIT 0x00000f00 /* commit option mask */ +#define JFS_GROUPCOMMIT 0x00000100 /* group (of 1) commit */ +#define JFS_LAZYCOMMIT 0x00000200 /* lazy commit */ +#define JFS_TMPFS 0x00000400 /* temporary file system - + * do not log/commit: + */ + +/* log logical volume option */ +#define JFS_INLINELOG 0x00000800 /* inline log within file system */ +#define JFS_INLINEMOVE 0x00001000 /* inline log being moved */ + +/* Secondary aggregate inode table */ +#define JFS_BAD_SAIT 0x00010000 /* current secondary ait is bad */ + +/* sparse regular file support */ +#define JFS_SPARSE 0x00020000 /* sparse regular file */ + +/* DASD Limits F226941 */ +#define JFS_DASD_ENABLED 0x00040000 /* DASD limits enabled */ +#define JFS_DASD_PRIME 0x00080000 /* Prime DASD usage on boot */ + +/* big endian flag */ +#define JFS_SWAP_BYTES 0x00100000 /* running on big endian computer */ + +/* Directory index */ +#define JFS_DIR_INDEX 0x00200000 /* Persistant index for */ + /* directory entries */ + + +/* + * buffer cache configuration + */ +/* page size */ +#ifdef PSIZE +#undef PSIZE +#endif +#define PSIZE 4096 /* page size (in byte) */ +#define L2PSIZE 12 /* log2(PSIZE) */ +#define POFFSET 4095 /* offset within page */ + +/* buffer page size */ +#define BPSIZE PSIZE + +/* + * fs fundamental size + * + * PSIZE >= file system block size >= PBSIZE >= DISIZE + */ +#define PBSIZE 512 /* physical block size (in byte) */ +#define L2PBSIZE 9 /* log2(PBSIZE) */ + +#define DISIZE 512 /* on-disk inode size (in byte) */ +#define L2DISIZE 9 /* log2(DISIZE) */ + +#define IDATASIZE 256 /* inode inline data size */ +#define IXATTRSIZE 128 /* inode inline extended attribute size */ + +#define XTPAGE_SIZE 4096 +#define log2_PAGESIZE 12 + +#define IAG_SIZE 4096 +#define IAG_EXTENT_SIZE 4096 +#define INOSPERIAG 4096 /* number of disk inodes per iag */ +#define L2INOSPERIAG 12 /* l2 number of disk inodes per iag */ +#define INOSPEREXT 32 /* number of disk inode per extent */ +#define L2INOSPEREXT 5 /* l2 number of disk inode per extent */ +#define IXSIZE (DISIZE * INOSPEREXT) /* inode extent size */ +#define INOSPERPAGE 8 /* number of disk inodes per 4K page */ +#define L2INOSPERPAGE 3 /* log2(INOSPERPAGE) */ + +#define IAGFREELIST_LWM 64 + +#define INODE_EXTENT_SIZE IXSIZE /* inode extent size */ +#define NUM_INODE_PER_EXTENT INOSPEREXT +#define NUM_INODE_PER_IAG INOSPERIAG + +#define MINBLOCKSIZE 512 +#define MAXBLOCKSIZE 4096 +#define MAXFILESIZE ((s64)1 << 52) + +#define JFS_LINK_MAX 65535 /* nlink_t is unsigned short */ + +/* Minimum number of bytes supported for a JFS partition */ +#define MINJFS (0x1000000) +#define MINJFSTEXT "16" + +/* + * file system block size -> physical block size + */ +#define LBOFFSET(x) ((x) & (PBSIZE - 1)) +#define LBNUMBER(x) ((x) >> L2PBSIZE) +#define LBLK2PBLK(sb,b) ((b) << (sb->s_blocksize_bits - L2PBSIZE)) +#define PBLK2LBLK(sb,b) ((b) >> (sb->s_blocksize_bits - L2PBSIZE)) +/* size in byte -> last page number */ +#define SIZE2PN(size) ( ((s64)((size) - 1)) >> (L2PSIZE) ) +/* size in byte -> last file system block number */ +#define SIZE2BN(size, l2bsize) ( ((s64)((size) - 1)) >> (l2bsize) ) + +/* + * fixed physical block address (physical block size = 512 byte) + * + * NOTE: since we can't guarantee a physical block size of 512 bytes the use of + * these macros should be removed and the byte offset macros used instead. + */ +#define SUPER1_B 64 /* primary superblock */ +#define AIMAP_B (SUPER1_B + 8) /* 1st extent of aggregate inode map */ +#define AITBL_B (AIMAP_B + 16) /* + * 1st extent of aggregate inode table + */ +#define SUPER2_B (AITBL_B + 32) /* 2ndary superblock pbn */ +#define BMAP_B (SUPER2_B + 8) /* block allocation map */ + +/* + * SIZE_OF_SUPER defines the total amount of space reserved on disk for the + * superblock. This is not the same as the superblock structure, since all of + * this space is not currently being used. + */ +#define SIZE_OF_SUPER PSIZE + +/* + * SIZE_OF_AG_TABLE defines the amount of space reserved to hold the AG table + */ +#define SIZE_OF_AG_TABLE PSIZE + +/* + * SIZE_OF_MAP_PAGE defines the amount of disk space reserved for each page of + * the inode allocation map (to hold iag) + */ +#define SIZE_OF_MAP_PAGE PSIZE + +/* + * fixed byte offset address + */ +#define SUPER1_OFF 0x8000 /* primary superblock */ +#define AIMAP_OFF (SUPER1_OFF + SIZE_OF_SUPER) + /* + * Control page of aggregate inode map + * followed by 1st extent of map + */ +#define AITBL_OFF (AIMAP_OFF + (SIZE_OF_MAP_PAGE << 1)) + /* + * 1st extent of aggregate inode table + */ +#define SUPER2_OFF (AITBL_OFF + INODE_EXTENT_SIZE) + /* + * secondary superblock + */ +#define BMAP_OFF (SUPER2_OFF + SIZE_OF_SUPER) + /* + * block allocation map + */ + +/* + * The following macro is used to indicate the number of reserved disk blocks at + * the front of an aggregate, in terms of physical blocks. This value is + * currently defined to be 32K. This turns out to be the same as the primary + * superblock's address, since it directly follows the reserved blocks. + */ +#define AGGR_RSVD_BLOCKS SUPER1_B + +/* + * The following macro is used to indicate the number of reserved bytes at the + * front of an aggregate. This value is currently defined to be 32K. This + * turns out to be the same as the primary superblock's byte offset, since it + * directly follows the reserved blocks. + */ +#define AGGR_RSVD_BYTES SUPER1_OFF + +/* + * The following macro defines the byte offset for the first inode extent in + * the aggregate inode table. This allows us to find the self inode to find the + * rest of the table. Currently this value is 44K. + */ +#define AGGR_INODE_TABLE_START AITBL_OFF + +/* + * fixed reserved inode number + */ +/* aggregate inode */ +#define AGGR_RESERVED_I 0 /* aggregate inode (reserved) */ +#define AGGREGATE_I 1 /* aggregate inode map inode */ +#define BMAP_I 2 /* aggregate block allocation map inode */ +#define LOG_I 3 /* aggregate inline log inode */ +#define BADBLOCK_I 4 /* aggregate bad block inode */ +#define FILESYSTEM_I 16 /* 1st/only fileset inode in ait: + * fileset inode map inode + */ + +/* per fileset inode */ +#define FILESET_RSVD_I 0 /* fileset inode (reserved) */ +#define FILESET_EXT_I 1 /* fileset inode extension */ +#define ROOT_I 2 /* fileset root inode */ +#define ACL_I 3 /* fileset ACL inode */ + +#define FILESET_OBJECT_I 4 /* the first fileset inode available for a file + * or directory or link... + */ +#define FIRST_FILESET_INO 16 /* the first aggregate inode which describes + * an inode. (To fsck this is also the first + * inode in part 2 of the agg inode table.) + */ + +/* + * directory configuration + */ +#define JFS_NAME_MAX 255 +#define JFS_PATH_MAX BPSIZE + + +/* + * file system state (superblock state) + */ +#define FM_CLEAN 0x00000000 /* file system is unmounted and clean */ +#define FM_MOUNT 0x00000001 /* file system is mounted cleanly */ +#define FM_DIRTY 0x00000002 /* file system was not unmounted and clean + * when mounted or + * commit failure occurred while being mounted: + * fsck() must be run to repair + */ +#define FM_LOGREDO 0x00000004 /* log based recovery (logredo()) failed: + * fsck() must be run to repair + */ +#define FM_EXTENDFS 0x00000008 /* file system extendfs() in progress */ + +#endif /* _H_JFS_FILSYS */ diff -Nru a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_imap.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,3212 @@ +/* + + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * + */ + +/* + * Change History : + * + */ + +/* + * jfs_imap.c: inode allocation map manager + * + * Serialization: + * Each AG has a simple lock which is used to control the serialization of + * the AG level lists. This lock should be taken first whenever an AG + * level list will be modified or accessed. + * + * Each IAG is locked by obtaining the buffer for the IAG page. + * + * There is also a inode lock for the inode map inode. A read lock needs to + * be taken whenever an IAG is read from the map or the global level + * information is read. A write lock needs to be taken whenever the global + * level information is modified or an atomic operation needs to be used. + * + * If more than one IAG is read at one time, the read lock may not + * be given up until all of the IAG's are read. Otherwise, a deadlock + * may occur when trying to obtain the read lock while another thread + * holding the read lock is waiting on the IAG already being held. + * + * The control page of the inode map is read into memory by diMount(). + * Thereafter it should only be modified in memory and then it will be + * written out when the filesystem is unmounted by diUnmount(). + */ + +#include +#include +#include +#include "jfs_incore.h" +#include "jfs_filsys.h" +#include "jfs_dinode.h" +#include "jfs_dmap.h" +#include "jfs_imap.h" +#include "jfs_metapage.h" +#include "jfs_superblock.h" +#include "jfs_debug.h" + +/* + * imap locks + */ +/* iag free list lock */ +#define IAGFREE_LOCK_INIT(imap) init_MUTEX(&imap->im_freelock) +#define IAGFREE_LOCK(imap) down(&imap->im_freelock) +#define IAGFREE_UNLOCK(imap) up(&imap->im_freelock) + +/* per ag iag list locks */ +#define AG_LOCK_INIT(imap,index) init_MUTEX(&(imap->im_aglock[index])) +#define AG_LOCK(imap,agno) down(&imap->im_aglock[agno]) +#define AG_UNLOCK(imap,agno) up(&imap->im_aglock[agno]) + +/* + * external references + */ +extern struct address_space_operations jfs_aops; + +/* + * forward references + */ +static int diAllocAG(imap_t *, int, boolean_t, struct inode *); +static int diAllocAny(imap_t *, int, boolean_t, struct inode *); +static int diAllocBit(imap_t *, iag_t *, int); +static int diAllocExt(imap_t *, int, struct inode *); +static int diAllocIno(imap_t *, int, struct inode *); +static int diFindFree(u32, int); +static int diNewExt(imap_t *, iag_t *, int); +static int diNewIAG(imap_t *, int *, int, metapage_t **); +static void duplicateIXtree(struct super_block *, s64, int, s64 *); + +static int diIAGRead(imap_t * imap, int, metapage_t **); +static int copy_from_dinode(dinode_t *, struct inode *); +static void copy_to_dinode(dinode_t *, struct inode *); + +/* + * debug code for double-checking inode map + */ +/* #define _JFS_DEBUG_IMAP 1 */ + +#ifdef _JFS_DEBUG_IMAP +#define DBG_DIINIT(imap) DBGdiInit(imap) +#define DBG_DIALLOC(imap, ino) DBGdiAlloc(imap, ino) +#define DBG_DIFREE(imap, ino) DBGdiFree(imap, ino) + +static void *DBGdiInit(imap_t * imap); +static void DBGdiAlloc(imap_t * imap, ino_t ino); +static void DBGdiFree(imap_t * imap, ino_t ino); +#else +#define DBG_DIINIT(imap) +#define DBG_DIALLOC(imap, ino) +#define DBG_DIFREE(imap, ino) +#endif /* _JFS_DEBUG_IMAP */ + +/* + * NAME: diMount() + * + * FUNCTION: initialize the incore inode map control structures for + * a fileset or aggregate init time. + * + * the inode map's control structure (dinomap_t) is + * brought in from disk and placed in virtual memory. + * + * PARAMETERS: + * ipimap - pointer to inode map inode for the aggregate or fileset. + * + * RETURN VALUES: + * 0 - success + * ENOMEM - insufficient free virtual memory. + * EIO - i/o error. + */ +int diMount(struct inode *ipimap) +{ + imap_t *imap; + metapage_t *mp; + int index; + dinomap_t *dinom_le; + + /* + * allocate/initialize the in-memory inode map control structure + */ + /* allocate the in-memory inode map control structure. */ + imap = (imap_t *) kmalloc(sizeof(imap_t), GFP_KERNEL); + if (imap == NULL) { + jERROR(1, ("diMount: kmalloc returned NULL!\n")); + return (ENOMEM); + } + + /* read the on-disk inode map control structure. */ + + mp = read_metapage(ipimap, + IMAPBLKNO << JFS_SBI(ipimap->i_sb)->l2nbperpage, + PSIZE, 0); + if (mp == NULL) { + kfree(imap); + return (EIO); + } + + /* copy the on-disk version to the in-memory version. */ + dinom_le = (dinomap_t *) mp->data; + imap->im_freeiag = le32_to_cpu(dinom_le->in_freeiag); + imap->im_nextiag = le32_to_cpu(dinom_le->in_nextiag); + atomic_set(&imap->im_numinos, le32_to_cpu(dinom_le->in_numinos)); + atomic_set(&imap->im_numfree, le32_to_cpu(dinom_le->in_numfree)); + imap->im_nbperiext = le32_to_cpu(dinom_le->in_nbperiext); + imap->im_l2nbperiext = le32_to_cpu(dinom_le->in_l2nbperiext); + for (index = 0; index < MAXAG; index++) { + imap->im_agctl[index].inofree = + le32_to_cpu(dinom_le->in_agctl[index].inofree); + imap->im_agctl[index].extfree = + le32_to_cpu(dinom_le->in_agctl[index].extfree); + imap->im_agctl[index].numinos = + le32_to_cpu(dinom_le->in_agctl[index].numinos); + imap->im_agctl[index].numfree = + le32_to_cpu(dinom_le->in_agctl[index].numfree); + } + + /* release the buffer. */ + release_metapage(mp); + + /* + * allocate/initialize inode allocation map locks + */ + /* allocate and init iag free list lock */ + IAGFREE_LOCK_INIT(imap); + + /* allocate and init ag list locks */ + for (index = 0; index < MAXAG; index++) { + AG_LOCK_INIT(imap, index); + } + + /* bind the inode map inode and inode map control structure + * to each other. + */ + imap->im_ipimap = ipimap; + JFS_IP(ipimap)->i_imap = imap; + +// DBG_DIINIT(imap); + + return (0); +} + + +/* + * NAME: diUnmount() + * + * FUNCTION: write to disk the incore inode map control structures for + * a fileset or aggregate at unmount time. + * + * PARAMETERS: + * ipimap - pointer to inode map inode for the aggregate or fileset. + * + * RETURN VALUES: + * 0 - success + * ENOMEM - insufficient free virtual memory. + * EIO - i/o error. + */ +int diUnmount(struct inode *ipimap, int mounterror) +{ + imap_t *imap = JFS_IP(ipimap)->i_imap; + + /* + * update the on-disk inode map control structure + */ + + if (!(mounterror || isReadOnly(ipimap))) + diSync(ipimap); + + /* + * Invalidate the page cache buffers + */ + truncate_inode_pages(ipimap->i_mapping, 0); + + /* + * free in-memory control structure + */ + kfree(imap); + + return (0); +} + + +/* + * diSync() + */ +int diSync(struct inode *ipimap) +{ + dinomap_t *dinom_le; + imap_t *imp = JFS_IP(ipimap)->i_imap; + metapage_t *mp; + int index; + + /* + * write imap global conrol page + */ + /* read the on-disk inode map control structure */ + mp = get_metapage(ipimap, + IMAPBLKNO << JFS_SBI(ipimap->i_sb)->l2nbperpage, + PSIZE, 0); + if (mp == NULL) { + jERROR(1,("diSync: get_metapage failed!\n")); + return EIO; + } + + /* copy the in-memory version to the on-disk version */ + //memcpy(mp->data, &imp->im_imap,sizeof(dinomap_t)); + dinom_le = (dinomap_t *) mp->data; + dinom_le->in_freeiag = cpu_to_le32(imp->im_freeiag); + dinom_le->in_nextiag = cpu_to_le32(imp->im_nextiag); + dinom_le->in_numinos = cpu_to_le32(atomic_read(&imp->im_numinos)); + dinom_le->in_numfree = cpu_to_le32(atomic_read(&imp->im_numfree)); + dinom_le->in_nbperiext = cpu_to_le32(imp->im_nbperiext); + dinom_le->in_l2nbperiext = cpu_to_le32(imp->im_l2nbperiext); + for (index = 0; index < MAXAG; index++) { + dinom_le->in_agctl[index].inofree = + cpu_to_le32(imp->im_agctl[index].inofree); + dinom_le->in_agctl[index].extfree = + cpu_to_le32(imp->im_agctl[index].extfree); + dinom_le->in_agctl[index].numinos = + cpu_to_le32(imp->im_agctl[index].numinos); + dinom_le->in_agctl[index].numfree = + cpu_to_le32(imp->im_agctl[index].numfree); + } + + /* write out the control structure */ + write_metapage(mp); + + /* + * write out dirty pages of imap + */ + fsync_inode_data_buffers(ipimap); + + diWriteSpecial(ipimap); + + return (0); +} + + +/* + * NAME: diRead() + * + * FUNCTION: initialize an incore inode from disk. + * + * on entry, the specifed incore inode should itself + * specify the disk inode number corresponding to the + * incore inode (i.e. i_number should be initialized). + * + * this routine handles incore inode initialization for + * both "special" and "regular" inodes. special inodes + * are those required early in the mount process and + * require special handling since much of the file system + * is not yet initialized. these "special" inodes are + * identified by a NULL inode map inode pointer and are + * actually initialized by a call to diReadSpecial(). + * + * for regular inodes, the iag describing the disk inode + * is read from disk to determine the inode extent address + * for the disk inode. with the inode extent address in + * hand, the page of the extent that contains the disk + * inode is read and the disk inode is copied to the + * incore inode. + * + * PARAMETERS: + * ip - pointer to incore inode to be initialized from disk. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error. + * ENOMEM - insufficient memory + * + */ +int diRead(struct inode *ip) +{ + struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb); + int iagno, ino, extno, rc; + struct inode *ipimap; + dinode_t *dp; + iag_t *iagp; + metapage_t *mp; + s64 blkno, agstart; + imap_t *imap; + int block_offset; + int inodes_left; + uint pageno; + int rel_inode; + + jFYI(1, ("diRead: ino = %ld\n", ip->i_ino)); + + ipimap = sbi->ipimap; + JFS_IP(ip)->ipimap = ipimap; + + /* determine the iag number for this inode (number) */ + iagno = INOTOIAG(ip->i_ino); + + /* read the iag */ + imap = JFS_IP(ipimap)->i_imap; + IREAD_LOCK(ipimap); + rc = diIAGRead(imap, iagno, &mp); + IREAD_UNLOCK(ipimap); + if (rc) { + jERROR(1, ("diRead: diIAGRead returned %d\n", rc)); + return (rc); + } + + iagp = (iag_t *) mp->data; + + /* determine inode extent that holds the disk inode */ + ino = ip->i_ino & (INOSPERIAG - 1); + extno = ino >> L2INOSPEREXT; + + if ((lengthPXD(&iagp->inoext[extno]) != imap->im_nbperiext) || + (addressPXD(&iagp->inoext[extno]) == 0)) { + jERROR(1, ("diRead: Bad inoext: 0x%lx, 0x%lx\n", + (ulong) addressPXD(&iagp->inoext[extno]), + (ulong) lengthPXD(&iagp->inoext[extno]))); + release_metapage(mp); + updateSuper(ip->i_sb, FM_DIRTY); + return ESTALE; + } + + /* get disk block number of the page within the inode extent + * that holds the disk inode. + */ + blkno = INOPBLK(&iagp->inoext[extno], ino, sbi->l2nbperpage); + + /* get the ag for the iag */ + agstart = le64_to_cpu(iagp->agstart); + + release_metapage(mp); + + rel_inode = (ino & (INOSPERPAGE - 1)); + pageno = blkno >> sbi->l2nbperpage; + + if ((block_offset = ((u32) blkno & (sbi->nbperpage - 1)))) { + /* + * OS/2 didn't always align inode extents on page boundaries + */ + inodes_left = + (sbi->nbperpage - block_offset) << sbi->l2niperblk; + + if (rel_inode < inodes_left) + rel_inode += block_offset << sbi->l2niperblk; + else { + pageno += 1; + rel_inode -= inodes_left; + } + } + + /* read the page of disk inode */ + mp = read_metapage(ipimap, pageno << sbi->l2nbperpage, PSIZE, 1); + if (mp == 0) { + jERROR(1, ("diRead: read_metapage failed\n")); + return EIO; + } + + /* locate the the disk inode requested */ + dp = (dinode_t *) mp->data; + dp += rel_inode; + + if (ip->i_ino != le32_to_cpu(dp->di_number)) { + jERROR(1, ("diRead: i_ino != di_number\n")); + updateSuper(ip->i_sb, FM_DIRTY); + rc = EIO; + } else if (le32_to_cpu(dp->di_nlink) == 0) { + jERROR(1, + ("diRead: di_nlink is zero. ino=%ld\n", ip->i_ino)); + updateSuper(ip->i_sb, FM_DIRTY); + rc = ESTALE; + } else + /* copy the disk inode to the in-memory inode */ + rc = copy_from_dinode(dp, ip); + + release_metapage(mp); + + /* set the ag for the inode */ + JFS_IP(ip)->agno = BLKTOAG(agstart, sbi); + + return (rc); +} + + +/* + * NAME: diReadSpecial() + * + * FUNCTION: initialize a 'special' inode from disk. + * + * this routines handles aggregate level inodes. The + * inode cache cannot differentiate between the + * aggregate inodes and the filesystem inodes, so we + * handle these here. We don't actually use the aggregate + * inode map, since these inodes are at a fixed location + * and in some cases the aggregate inode map isn't initialized + * yet. + * + * PARAMETERS: + * sb - filesystem superblock + * inum - aggregate inode number + * + * RETURN VALUES: + * new inode - success + * NULL - i/o error. + */ +struct inode *diReadSpecial(struct super_block *sb, ino_t inum) +{ + struct jfs_sb_info *sbi = JFS_SBI(sb); + uint address; + dinode_t *dp; + struct inode *ip; + metapage_t *mp; + + ip = new_inode(sb); + if (ip == NULL) { + jERROR(1, + ("diReadSpecial: new_inode returned NULL!\n")); + return ip; + } + + /* + * If ip->i_number >= 32 (INOSPEREXT), then read from secondary + * aggregate inode table. + */ + + if (inum >= INOSPEREXT) { + address = + addressPXD(&sbi->ait2) >> sbi->l2nbperpage; + inum -= INOSPEREXT; + ASSERT(inum < INOSPEREXT); + JFS_IP(ip)->ipimap = sbi->ipaimap2; + } else { + address = AITBL_OFF >> L2PSIZE; + JFS_IP(ip)->ipimap = sbi->ipaimap; + } + ip->i_ino = inum; + + address += inum >> 3; /* 8 inodes per 4K page */ + + /* read the page of fixed disk inode (AIT) in raw mode */ + jEVENT(0, + ("Reading aggregate inode %d from block %d\n", (uint) inum, + address)); + mp = read_metapage(ip, address << sbi->l2nbperpage, PSIZE, 1); + if (mp == NULL) { + ip->i_sb = NULL; + ip->i_nlink = 1; /* Don't want iput() deleting it */ + iput(ip); + return (NULL); + } + + /* get the pointer to the disk inode of interest */ + dp = (dinode_t *) (mp->data); + dp += inum % 8; /* 8 inodes per 4K page */ + + /* copy on-disk inode to in-memory inode */ + if ((copy_from_dinode(dp, ip)) != 0) { + /* handle bad return by returning NULL for ip */ + ip->i_sb = NULL; + ip->i_nlink = 1; /* Don't want iput() deleting it */ + iput(ip); + /* release the page */ + release_metapage(mp); + return (NULL); + + } + + ip->i_mapping->a_ops = &jfs_aops; + ip->i_mapping->gfp_mask = GFP_NOFS; + + if ((inum == FILESYSTEM_I) && (JFS_IP(ip)->ipimap == sbi->ipaimap)) { + sbi->gengen = le32_to_cpu(dp->di_gengen); + sbi->inostamp = le32_to_cpu(dp->di_inostamp); + } + + /* release the page */ + release_metapage(mp); + + return (ip); +} + +/* + * NAME: diWriteSpecial() + * + * FUNCTION: Write the special inode to disk + * + * PARAMETERS: + * ip - special inode + * + * RETURN VALUES: none + */ + +void diWriteSpecial(struct inode *ip) +{ + struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb); + uint address; + dinode_t *dp; + ino_t inum = ip->i_ino; + metapage_t *mp; + + /* + * If ip->i_number >= 32 (INOSPEREXT), then write to secondary + * aggregate inode table. + */ + + if (!(ip->i_state & I_DIRTY)) + return; + + ip->i_state &= ~I_DIRTY; + + if (inum >= INOSPEREXT) { + address = + addressPXD(&sbi->ait2) >> sbi->l2nbperpage; + inum -= INOSPEREXT; + ASSERT(inum < INOSPEREXT); + } else { + address = AITBL_OFF >> L2PSIZE; + } + + address += inum >> 3; /* 8 inodes per 4K page */ + + /* read the page of fixed disk inode (AIT) in raw mode */ + jEVENT(0, + ("Reading aggregate inode %d from block %d\n", (uint) inum, + address)); + mp = read_metapage(ip, address << sbi->l2nbperpage, PSIZE, 1); + if (mp == NULL) { + jERROR(1, + ("diWriteSpecial: failed to read aggregate inode extent!\n")); + return; + } + + /* get the pointer to the disk inode of interest */ + dp = (dinode_t *) (mp->data); + dp += inum % 8; /* 8 inodes per 4K page */ + + /* copy on-disk inode to in-memory inode */ + copy_to_dinode(dp, ip); + memcpy(&dp->di_xtroot, &JFS_IP(ip)->i_xtroot, 288); + + if (inum == FILESYSTEM_I) + dp->di_gengen = cpu_to_le32(sbi->gengen); + + /* write the page */ + write_metapage(mp); +} + +/* + * NAME: diFreeSpecial() + * + * FUNCTION: Free allocated space for special inode + */ +void diFreeSpecial(struct inode *ip) +{ + if (ip == NULL) { + jERROR(1, ("diFreeSpecial called with NULL ip!\n")); + return; + } + fsync_inode_data_buffers(ip); + truncate_inode_pages(ip->i_mapping, 0); + iput(ip); +} + + + +/* + * NAME: diWrite() + * + * FUNCTION: write the on-disk inode portion of the in-memory inode + * to its corresponding on-disk inode. + * + * on entry, the specifed incore inode should itself + * specify the disk inode number corresponding to the + * incore inode (i.e. i_number should be initialized). + * + * the inode contains the inode extent address for the disk + * inode. with the inode extent address in hand, the + * page of the extent that contains the disk inode is + * read and the disk inode portion of the incore inode + * is copied to the disk inode. + * + * PARAMETERS: + * tid - transacation id + * ip - pointer to incore inode to be written to the inode extent. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error. + */ +int diWrite(tid_t tid, struct inode *ip) +{ + struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb); + struct jfs_inode_info *jfs_ip = JFS_IP(ip); + int rc = 0; + s32 ino; + dinode_t *dp; + s64 blkno; + int block_offset; + int inodes_left; + metapage_t *mp; + uint pageno; + int rel_inode; + int dioffset; + struct inode *ipimap; + uint type; + lid_t lid; + tlock_t *ditlck, *tlck; + linelock_t *dilinelock, *ilinelock; + lv_t *lv; + int n; + + ipimap = jfs_ip->ipimap; + + ino = ip->i_ino & (INOSPERIAG - 1); + + assert(lengthPXD(&(jfs_ip->ixpxd)) == + JFS_IP(ipimap)->i_imap->im_nbperiext); + assert(addressPXD(&(jfs_ip->ixpxd))); + + /* + * read the page of disk inode containing the specified inode: + */ + /* compute the block address of the page */ + blkno = INOPBLK(&(jfs_ip->ixpxd), ino, sbi->l2nbperpage); + + rel_inode = (ino & (INOSPERPAGE - 1)); + pageno = blkno >> sbi->l2nbperpage; + + if ((block_offset = ((u32) blkno & (sbi->nbperpage - 1)))) { + /* + * OS/2 didn't always align inode extents on page boundaries + */ + inodes_left = + (sbi->nbperpage - block_offset) << sbi->l2niperblk; + + if (rel_inode < inodes_left) + rel_inode += block_offset << sbi->l2niperblk; + else { + pageno += 1; + rel_inode -= inodes_left; + } + } + /* read the page of disk inode */ + retry: + mp = read_metapage(ipimap, pageno << sbi->l2nbperpage, PSIZE, 1); + if (mp == 0) + return (EIO); + + /* get the pointer to the disk inode */ + dp = (dinode_t *) mp->data; + dp += rel_inode; + + dioffset = (ino & (INOSPERPAGE - 1)) << L2DISIZE; + + /* + * acquire transaction lock on the on-disk inode; + * N.B. tlock is acquired on ipimap not ip; + */ + if ((ditlck = + txLock(tid, ipimap, mp, tlckINODE | tlckENTRY)) == NULL) + goto retry; + dilinelock = (linelock_t *) & ditlck->lock; + + /* + * copy btree root from in-memory inode to on-disk inode + * + * (tlock is taken from inline B+-tree root in in-memory + * inode when the B+-tree root is updated, which is pointed + * by jfs_ip->blid as well as being on tx tlock list) + * + * further processing of btree root is based on the copy + * in in-memory inode, where txLog() will log from, and, + * for xtree root, txUpdateMap() will update map and reset + * XAD_NEW bit; + */ + + if (S_ISDIR(ip->i_mode) && (lid = jfs_ip->xtlid)) { + /* + * This is the special xtree inside the directory for storing + * the directory table + */ + xtpage_t *p, *xp; + xad_t *xad; + + jfs_ip->xtlid = 0; + tlck = lid_to_tlock(lid); + assert(tlck->type & tlckXTREE); + tlck->type |= tlckBTROOT; + tlck->mp = mp; + ilinelock = (linelock_t *) & tlck->lock; + + /* + * copy xtree root from inode to dinode: + */ + p = &jfs_ip->i_xtroot; + xp = (xtpage_t *) &dp->di_dirtable; + lv = (lv_t *) & ilinelock->lv; + for (n = 0; n < ilinelock->index; n++, lv++) { + memcpy(&xp->xad[lv->offset], &p->xad[lv->offset], + lv->length << L2XTSLOTSIZE); + } + + /* reset on-disk (metadata page) xtree XAD_NEW bit */ + xad = &xp->xad[XTENTRYSTART]; + for (n = XTENTRYSTART; + n < le16_to_cpu(xp->header.nextindex); n++, xad++) + if (xad->flag & (XAD_NEW | XAD_EXTENDED)) + xad->flag &= ~(XAD_NEW | XAD_EXTENDED); + } + + if ((lid = jfs_ip->blid) == 0) + goto inlineData; + jfs_ip->blid = 0; + + tlck = lid_to_tlock(lid); + type = tlck->type; + tlck->type |= tlckBTROOT; + tlck->mp = mp; + ilinelock = (linelock_t *) & tlck->lock; + + /* + * regular file: 16 byte (XAD slot) granularity + */ + if (type & tlckXTREE) { + xtpage_t *p, *xp; + xad_t *xad; + + /* + * copy xtree root from inode to dinode: + */ + p = &jfs_ip->i_xtroot; + xp = &dp->di_xtroot; + lv = (lv_t *) & ilinelock->lv; + for (n = 0; n < ilinelock->index; n++, lv++) { + memcpy(&xp->xad[lv->offset], &p->xad[lv->offset], + lv->length << L2XTSLOTSIZE); + } + + /* reset on-disk (metadata page) xtree XAD_NEW bit */ + xad = &xp->xad[XTENTRYSTART]; + for (n = XTENTRYSTART; + n < le16_to_cpu(xp->header.nextindex); n++, xad++) + if (xad->flag & (XAD_NEW | XAD_EXTENDED)) + xad->flag &= ~(XAD_NEW | XAD_EXTENDED); + } + /* + * directory: 32 byte (directory entry slot) granularity + */ + else if (type & tlckDTREE) { + dtpage_t *p, *xp; + + /* + * copy dtree root from inode to dinode: + */ + p = (dtpage_t *) &jfs_ip->i_dtroot; + xp = (dtpage_t *) & dp->di_dtroot; + lv = (lv_t *) & ilinelock->lv; + for (n = 0; n < ilinelock->index; n++, lv++) { + memcpy(&xp->slot[lv->offset], &p->slot[lv->offset], + lv->length << L2DTSLOTSIZE); + } + } else { + jERROR(1, ("diWrite: UFO tlock\n")); + } + + inlineData: + /* + * copy inline symlink from in-memory inode to on-disk inode + */ + if (S_ISLNK(ip->i_mode) && ip->i_size < IDATASIZE) { + lv = (lv_t *) & dilinelock->lv[dilinelock->index]; + lv->offset = (dioffset + 2 * 128) >> L2INODESLOTSIZE; + lv->length = 2; + memcpy(&dp->di_fastsymlink, jfs_ip->i_inline, IDATASIZE); + dilinelock->index++; + } +#ifdef _STILL_TO_PORT + /* + * copy inline data from in-memory inode to on-disk inode: + * 128 byte slot granularity + */ + if (test_cflag(COMMIT_Inlineea, ip)) + lv = (lv_t *) & dilinelock->lv[dilinelock->index]; + lv->offset = (dioffset + 3 * 128) >> L2INODESLOTSIZE; + lv->length = 1; + memcpy(&dp->di_inlineea, &ip->i_inlineea, INODESLOTSIZE); + dilinelock->index++; + + clear_cflag(COMMIT_Inlineea, ip); + } +#endif /* _STILL_TO_PORT */ + + /* + * lock/copy inode base: 128 byte slot granularity + */ +// baseDinode: + lv = (lv_t *) & dilinelock->lv[dilinelock->index]; + lv->offset = dioffset >> L2INODESLOTSIZE; + copy_to_dinode(dp, ip); + if (test_and_clear_cflag(COMMIT_Dirtable, ip)) { + lv->length = 2; + memcpy(&dp->di_dirtable, &jfs_ip->i_dirtable, 96); + } else + lv->length = 1; + dilinelock->index++; + +#ifdef _JFS_FASTDASD + /* + * We aren't logging changes to the DASD used in directory inodes, + * but we need to write them to disk. If we don't unmount cleanly, + * mount will recalculate the DASD used. + */ + if (S_ISDIR(ip->i_mode) + && (ip->i_ipmnt->i_mntflag & JFS_DASD_ENABLED)) + bcopy(&ip->i_DASD, &dp->di_DASD, sizeof(dasd_t)); +#endif /* _JFS_FASTDASD */ + + /* release the buffer holding the updated on-disk inode. + * the buffer will be later written by commit processing. + */ + write_metapage(mp); + + return (rc); +} + + +/* + * NAME: diFree(ip) + * + * FUNCTION: free a specified inode from the inode working map + * for a fileset or aggregate. + * + * if the inode to be freed represents the first (only) + * free inode within the iag, the iag will be placed on + * the ag free inode list. + * + * freeing the inode will cause the inode extent to be + * freed if the inode is the only allocated inode within + * the extent. in this case all the disk resource backing + * up the inode extent will be freed. in addition, the iag + * will be placed on the ag extent free list if the extent + * is the first free extent in the iag. if freeing the + * extent also means that no free inodes will exist for + * the iag, the iag will also be removed from the ag free + * inode list. + * + * the iag describing the inode will be freed if the extent + * is to be freed and it is the only backed extent within + * the iag. in this case, the iag will be removed from the + * ag free extent list and ag free inode list and placed on + * the inode map's free iag list. + * + * a careful update approach is used to provide consistency + * in the face of updates to multiple buffers. under this + * approach, all required buffers are obtained before making + * any updates and are held until all updates are complete. + * + * PARAMETERS: + * ip - inode to be freed. + * + * RETURN VALUES: + * 0 - success + * EIO - i/o error. + */ +int diFree(struct inode *ip) +{ + int rc; + ino_t inum = ip->i_ino; + iag_t *iagp, *aiagp, *biagp, *ciagp, *diagp; + metapage_t *mp, *amp, *bmp, *cmp, *dmp; + int iagno, ino, extno, bitno, sword, agno; + int back, fwd; + u32 bitmap, mask; + struct inode *ipimap = JFS_SBI(ip->i_sb)->ipimap; + imap_t *imap = JFS_IP(ipimap)->i_imap; + s64 xaddr; + s64 xlen; + pxd_t freepxd; + tid_t tid; + struct inode *iplist[3]; + tlock_t *tlck; + pxdlock_t *pxdlock; + + /* + * This is just to suppress compiler warnings. The same logic that + * references these variables is used to initialize them. + */ + aiagp = biagp = ciagp = diagp = NULL; + + /* get the iag number containing the inode. + */ + iagno = INOTOIAG(inum); + + /* make sure that the iag is contained within + * the map. + */ + //assert(iagno < imap->im_nextiag); + if (iagno >= imap->im_nextiag) { + jERROR(1, ("diFree: inum = %d, iagno = %d, nextiag = %d\n", + (uint) inum, iagno, imap->im_nextiag)); + dump_mem("imap", imap, 32); + updateSuper(ip->i_sb, FM_DIRTY); + return EIO; + } + + /* get the allocation group for this ino. + */ + agno = JFS_IP(ip)->agno; + + /* Lock the AG specific inode map information + */ + AG_LOCK(imap, agno); + + /* Obtain read lock in imap inode. Don't release it until we have + * read all of the IAG's that we are going to. + */ + IREAD_LOCK(ipimap); + + /* read the iag. + */ + if ((rc = diIAGRead(imap, iagno, &mp))) { + IREAD_UNLOCK(ipimap); + AG_UNLOCK(imap, agno); + return (rc); + } + iagp = (iag_t *) mp->data; + + /* get the inode number and extent number of the inode within + * the iag and the inode number within the extent. + */ + ino = inum & (INOSPERIAG - 1); + extno = ino >> L2INOSPEREXT; + bitno = ino & (INOSPEREXT - 1); + mask = HIGHORDER >> bitno; + + assert(le32_to_cpu(iagp->wmap[extno]) & mask); +#ifdef _STILL_TO_PORT + assert((le32_to_cpu(iagp->pmap[extno]) & mask) == 0); +#endif /* _STILL_TO_PORT */ + assert(addressPXD(&iagp->inoext[extno])); + + /* compute the bitmap for the extent reflecting the freed inode. + */ + bitmap = le32_to_cpu(iagp->wmap[extno]) & ~mask; + + if (imap->im_agctl[agno].numfree > imap->im_agctl[agno].numinos) { + jERROR(1,("diFree: numfree > numinos\n")); + release_metapage(mp); + IREAD_UNLOCK(ipimap); + AG_UNLOCK(imap, agno); + updateSuper(ip->i_sb, FM_DIRTY); + return EIO; + } + /* + * inode extent still has some inodes or below low water mark: + * keep the inode extent; + */ + if (bitmap || + imap->im_agctl[agno].numfree < 96 || + (imap->im_agctl[agno].numfree < 288 && + (((imap->im_agctl[agno].numfree * 100) / + imap->im_agctl[agno].numinos) <= 25))) { + /* if the iag currently has no free inodes (i.e., + * the inode being freed is the first free inode of iag), + * insert the iag at head of the inode free list for the ag. + */ + if (iagp->nfreeinos == 0) { + /* check if there are any iags on the ag inode + * free list. if so, read the first one so that + * we can link the current iag onto the list at + * the head. + */ + if ((fwd = imap->im_agctl[agno].inofree) >= 0) { + /* read the iag that currently is the head + * of the list. + */ + if ((rc = diIAGRead(imap, fwd, &))) { + IREAD_UNLOCK(ipimap); + AG_UNLOCK(imap, agno); + release_metapage(mp); + return (rc); + } + aiagp = (iag_t *) amp->data; + + /* make current head point back to the iag. + */ + aiagp->inofreeback = cpu_to_le32(iagno); + + write_metapage(amp); + } + + /* iag points forward to current head and iag + * becomes the new head of the list. + */ + iagp->inofreefwd = + cpu_to_le32(imap->im_agctl[agno].inofree); + iagp->inofreeback = -1; + imap->im_agctl[agno].inofree = iagno; + } + IREAD_UNLOCK(ipimap); + + /* update the free inode summary map for the extent if + * freeing the inode means the extent will now have free + * inodes (i.e., the inode being freed is the first free + * inode of extent), + */ + if (iagp->wmap[extno] == ONES) { + sword = extno >> L2EXTSPERSUM; + bitno = extno & (EXTSPERSUM - 1); + iagp->inosmap[sword] &= + cpu_to_le32(~(HIGHORDER >> bitno)); + } + + /* update the bitmap. + */ + iagp->wmap[extno] = cpu_to_le32(bitmap); + DBG_DIFREE(imap, inum); + + /* update the free inode counts at the iag, ag and + * map level. + */ + iagp->nfreeinos = + cpu_to_le32(le32_to_cpu(iagp->nfreeinos) + 1); + imap->im_agctl[agno].numfree += 1; + atomic_inc(&imap->im_numfree); + + /* release the AG inode map lock + */ + AG_UNLOCK(imap, agno); + + /* write the iag */ + write_metapage(mp); + + return (0); + } + + + /* + * inode extent has become free and above low water mark: + * free the inode extent; + */ + + /* + * prepare to update iag list(s) (careful update step 1) + */ + amp = bmp = cmp = dmp = NULL; + fwd = back = -1; + + /* check if the iag currently has no free extents. if so, + * it will be placed on the head of the ag extent free list. + */ + if (iagp->nfreeexts == 0) { + /* check if the ag extent free list has any iags. + * if so, read the iag at the head of the list now. + * this (head) iag will be updated later to reflect + * the addition of the current iag at the head of + * the list. + */ + if ((fwd = imap->im_agctl[agno].extfree) >= 0) { + if ((rc = diIAGRead(imap, fwd, &))) + goto error_out; + aiagp = (iag_t *) amp->data; + } + } else { + /* iag has free extents. check if the addition of a free + * extent will cause all extents to be free within this + * iag. if so, the iag will be removed from the ag extent + * free list and placed on the inode map's free iag list. + */ + if (iagp->nfreeexts == cpu_to_le32(EXTSPERIAG - 1)) { + /* in preparation for removing the iag from the + * ag extent free list, read the iags preceeding + * and following the iag on the ag extent free + * list. + */ + if ((fwd = le32_to_cpu(iagp->extfreefwd)) >= 0) { + if ((rc = diIAGRead(imap, fwd, &))) + goto error_out; + aiagp = (iag_t *) amp->data; + } + + if ((back = le32_to_cpu(iagp->extfreeback)) >= 0) { + if ((rc = diIAGRead(imap, back, &bmp))) + goto error_out; + biagp = (iag_t *) bmp->data; + } + } + } + + /* remove the iag from the ag inode free list if freeing + * this extent cause the iag to have no free inodes. + */ + if (iagp->nfreeinos == cpu_to_le32(INOSPEREXT - 1)) { + int inofreeback = le32_to_cpu(iagp->inofreeback); + int inofreefwd = le32_to_cpu(iagp->inofreefwd); + + /* in preparation for removing the iag from the + * ag inode free list, read the iags preceeding + * and following the iag on the ag inode free + * list. before reading these iags, we must make + * sure that we already don't have them in hand + * from up above, since re-reading an iag (buffer) + * we are currently holding would cause a deadlock. + */ + if (inofreefwd >= 0) { + + if (inofreefwd == fwd) + ciagp = (iag_t *) amp->data; + else if (inofreefwd == back) + ciagp = (iag_t *) bmp->data; + else { + if ((rc = + diIAGRead(imap, inofreefwd, &cmp))) + goto error_out; + assert(cmp != NULL); + ciagp = (iag_t *) cmp->data; + } + assert(ciagp != NULL); + } + + if (inofreeback >= 0) { + if (inofreeback == fwd) + diagp = (iag_t *) amp->data; + else if (inofreeback == back) + diagp = (iag_t *) bmp->data; + else { + if ((rc = + diIAGRead(imap, inofreeback, &dmp))) + goto error_out; + assert(dmp != NULL); + diagp = (iag_t *) dmp->data; + } + assert(diagp != NULL); + } + } + + IREAD_UNLOCK(ipimap); + + /* + * invalidate any page of the inode extent freed from buffer cache; + */ + freepxd = iagp->inoext[extno]; + xaddr = addressPXD(&iagp->inoext[extno]); + xlen = lengthPXD(&iagp->inoext[extno]); + invalidate_metapages(JFS_SBI(ip->i_sb)->direct_inode, xaddr, xlen); + + /* + * update iag list(s) (careful update step 2) + */ + /* add the iag to the ag extent free list if this is the + * first free extent for the iag. + */ + if (iagp->nfreeexts == 0) { + if (fwd >= 0) + aiagp->extfreeback = cpu_to_le32(iagno); + + iagp->extfreefwd = + cpu_to_le32(imap->im_agctl[agno].extfree); + iagp->extfreeback = -1; + imap->im_agctl[agno].extfree = iagno; + } else { + /* remove the iag from the ag extent list if all extents + * are now free and place it on the inode map iag free list. + */ + if (iagp->nfreeexts == cpu_to_le32(EXTSPERIAG - 1)) { + if (fwd >= 0) + aiagp->extfreeback = iagp->extfreeback; + + if (back >= 0) + biagp->extfreefwd = iagp->extfreefwd; + else + imap->im_agctl[agno].extfree = + le32_to_cpu(iagp->extfreefwd); + + iagp->extfreefwd = iagp->extfreeback = -1; + + IAGFREE_LOCK(imap); + iagp->iagfree = cpu_to_le32(imap->im_freeiag); + imap->im_freeiag = iagno; + IAGFREE_UNLOCK(imap); + } + } + + /* remove the iag from the ag inode free list if freeing + * this extent causes the iag to have no free inodes. + */ + if (iagp->nfreeinos == cpu_to_le32(INOSPEREXT - 1)) { + if ((int) le32_to_cpu(iagp->inofreefwd) >= 0) + ciagp->inofreeback = iagp->inofreeback; + + if ((int) le32_to_cpu(iagp->inofreeback) >= 0) + diagp->inofreefwd = iagp->inofreefwd; + else + imap->im_agctl[agno].inofree = + le32_to_cpu(iagp->inofreefwd); + + iagp->inofreefwd = iagp->inofreeback = -1; + } + + /* update the inode extent address and working map + * to reflect the free extent. + * the permanent map should have been updated already + * for the inode being freed. + */ + assert(iagp->pmap[extno] == 0); + iagp->wmap[extno] = 0; + DBG_DIFREE(imap, inum); + PXDlength(&iagp->inoext[extno], 0); + PXDaddress(&iagp->inoext[extno], 0); + + /* update the free extent and free inode summary maps + * to reflect the freed extent. + * the inode summary map is marked to indicate no inodes + * available for the freed extent. + */ + sword = extno >> L2EXTSPERSUM; + bitno = extno & (EXTSPERSUM - 1); + mask = HIGHORDER >> bitno; + iagp->inosmap[sword] |= cpu_to_le32(mask); + iagp->extsmap[sword] &= cpu_to_le32(~mask); + + /* update the number of free inodes and number of free extents + * for the iag. + */ + iagp->nfreeinos = cpu_to_le32(le32_to_cpu(iagp->nfreeinos) - + (INOSPEREXT - 1)); + iagp->nfreeexts = cpu_to_le32(le32_to_cpu(iagp->nfreeexts) + 1); + + /* update the number of free inodes and backed inodes + * at the ag and inode map level. + */ + imap->im_agctl[agno].numfree -= (INOSPEREXT - 1); + imap->im_agctl[agno].numinos -= INOSPEREXT; + atomic_sub(INOSPEREXT - 1, &imap->im_numfree); + atomic_sub(INOSPEREXT, &imap->im_numinos); + + if (amp) + write_metapage(amp); + if (bmp) + write_metapage(bmp); + if (cmp) + write_metapage(cmp); + if (dmp) + write_metapage(dmp); + + /* + * start transaction to update block allocation map + * for the inode extent freed; + * + * N.B. AG_LOCK is released and iag will be released below, and + * other thread may allocate inode from/reusing the ixad freed + * BUT with new/different backing inode extent from the extent + * to be freed by the transaction; + */ + tid = txBegin(ipimap->i_sb, COMMIT_FORCE); + + /* acquire tlock of the iag page of the freed ixad + * to force the page NOHOMEOK (even though no data is + * logged from the iag page) until NOREDOPAGE|FREEXTENT log + * for the free of the extent is committed; + * write FREEXTENT|NOREDOPAGE log record + * N.B. linelock is overlaid as freed extent descriptor; + */ + tlck = txLock(tid, ipimap, mp, tlckINODE | tlckFREE); + pxdlock = (pxdlock_t *) & tlck->lock; + pxdlock->flag = mlckFREEPXD; + pxdlock->pxd = freepxd; + pxdlock->index = 1; + + write_metapage(mp); + + iplist[0] = ipimap; + + /* + * logredo needs the IAG number and IAG extent index in order + * to ensure that the IMap is consistent. The least disruptive + * way to pass these values through to the transaction manager + * is in the iplist array. + * + * It's not pretty, but it works. + */ + iplist[1] = (struct inode *) (size_t)iagno; + iplist[2] = (struct inode *) (size_t)extno; + + rc = txCommit(tid, 1, &iplist[0], COMMIT_FORCE); // D233382 + + txEnd(tid); + + /* unlock the AG inode map information */ + AG_UNLOCK(imap, agno); + + return (0); + + error_out: + IREAD_UNLOCK(ipimap); + + if (amp) + release_metapage(amp); + if (bmp) + release_metapage(bmp); + if (cmp) + release_metapage(cmp); + if (dmp) + release_metapage(dmp); + + AG_UNLOCK(imap, agno); + + release_metapage(mp); + + return (rc); +} + +/* + * There are several places in the diAlloc* routines where we initialize + * the inode. + */ +static inline void +diInitInode(struct inode *ip, int iagno, int ino, int extno, iag_t * iagp) +{ + struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb); + struct jfs_inode_info *jfs_ip = JFS_IP(ip); + + ip->i_ino = (iagno << L2INOSPERIAG) + ino; + DBG_DIALLOC(JFS_IP(ipimap)->i_imap, ip->i_ino); + jfs_ip->ixpxd = iagp->inoext[extno]; + jfs_ip->agno = BLKTOAG(le64_to_cpu(iagp->agstart), sbi); +} + + +/* + * NAME: diAlloc(pip,dir,ip) + * + * FUNCTION: allocate a disk inode from the inode working map + * for a fileset or aggregate. + * + * PARAMETERS: + * pip - pointer to incore inode for the parent inode. + * dir - TRUE if the new disk inode is for a directory. + * ip - pointer to a new inode + * + * RETURN VALUES: + * 0 - success. + * ENOSPC - insufficient disk resources. + * EIO - i/o error. + */ +int diAlloc(struct inode *pip, boolean_t dir, struct inode *ip) +{ + int rc, ino, iagno, addext, extno, bitno, sword; + int nwords, rem, i, agno; + u32 mask, inosmap, extsmap; + struct inode *ipimap; + metapage_t *mp; + ino_t inum; + iag_t *iagp; + imap_t *imap; + + /* get the pointers to the inode map inode and the + * corresponding imap control structure. + */ + ipimap = JFS_SBI(pip->i_sb)->ipimap; + imap = JFS_IP(ipimap)->i_imap; + JFS_IP(ip)->ipimap = ipimap; + JFS_IP(ip)->fileset = FILESYSTEM_I; + + /* for a directory, the allocation policy is to start + * at the ag level using the preferred ag. + */ + if (dir == TRUE) { + agno = dbNextAG(JFS_SBI(pip->i_sb)->ipbmap); + AG_LOCK(imap, agno); + goto tryag; + } + + /* for files, the policy starts off by trying to allocate from + * the same iag containing the parent disk inode: + * try to allocate the new disk inode close to the parent disk + * inode, using parent disk inode number + 1 as the allocation + * hint. (we use a left-to-right policy to attempt to avoid + * moving backward on the disk.) compute the hint within the + * file system and the iag. + */ + inum = pip->i_ino + 1; + ino = inum & (INOSPERIAG - 1); + + /* back off the the hint if it is outside of the iag */ + if (ino == 0) + inum = pip->i_ino; + + /* get the ag number of this iag */ + agno = JFS_IP(pip)->agno; + + /* lock the AG inode map information */ + AG_LOCK(imap, agno); + + /* Get read lock on imap inode */ + IREAD_LOCK(ipimap); + + /* get the iag number and read the iag */ + iagno = INOTOIAG(inum); + if ((rc = diIAGRead(imap, iagno, &mp))) { + IREAD_UNLOCK(ipimap); + return (rc); + } + iagp = (iag_t *) mp->data; + + /* determine if new inode extent is allowed to be added to the iag. + * new inode extent can be added to the iag if the ag + * has less than 32 free disk inodes and the iag has free extents. + */ + addext = (imap->im_agctl[agno].numfree < 32 && iagp->nfreeexts); + + /* + * try to allocate from the IAG + */ + /* check if the inode may be allocated from the iag + * (i.e. the inode has free inodes or new extent can be added). + */ + if (iagp->nfreeinos || addext) { + /* determine the extent number of the hint. + */ + extno = ino >> L2INOSPEREXT; + + /* check if the extent containing the hint has backed + * inodes. if so, try to allocate within this extent. + */ + if (addressPXD(&iagp->inoext[extno])) { + bitno = ino & (INOSPEREXT - 1); + if ((bitno = + diFindFree(le32_to_cpu(iagp->wmap[extno]), + bitno)) + < INOSPEREXT) { + ino = (extno << L2INOSPEREXT) + bitno; + + /* a free inode (bit) was found within this + * extent, so allocate it. + */ + rc = diAllocBit(imap, iagp, ino); + IREAD_UNLOCK(ipimap); + if (rc) { + assert(rc == EIO); + } else { + /* set the results of the allocation + * and write the iag. + */ + diInitInode(ip, iagno, ino, extno, + iagp); + mark_metapage_dirty(mp); + } + release_metapage(mp); + + /* free the AG lock and return. + */ + AG_UNLOCK(imap, agno); + return (rc); + } + + if (!addext) + extno = + (extno == + EXTSPERIAG - 1) ? 0 : extno + 1; + } + + /* + * no free inodes within the extent containing the hint. + * + * try to allocate from the backed extents following + * hint or, if appropriate (i.e. addext is true), allocate + * an extent of free inodes at or following the extent + * containing the hint. + * + * the free inode and free extent summary maps are used + * here, so determine the starting summary map position + * and the number of words we'll have to examine. again, + * the approach is to allocate following the hint, so we + * might have to initially ignore prior bits of the summary + * map that represent extents prior to the extent containing + * the hint and later revisit these bits. + */ + bitno = extno & (EXTSPERSUM - 1); + nwords = (bitno == 0) ? SMAPSZ : SMAPSZ + 1; + sword = extno >> L2EXTSPERSUM; + + /* mask any prior bits for the starting words of the + * summary map. + */ + mask = ONES << (EXTSPERSUM - bitno); + inosmap = le32_to_cpu(iagp->inosmap[sword]) | mask; + extsmap = le32_to_cpu(iagp->extsmap[sword]) | mask; + + /* scan the free inode and free extent summary maps for + * free resources. + */ + for (i = 0; i < nwords; i++) { + /* check if this word of the free inode summary + * map describes an extent with free inodes. + */ + if (~inosmap) { + /* an extent with free inodes has been + * found. determine the extent number + * and the inode number within the extent. + */ + rem = diFindFree(inosmap, 0); + extno = (sword << L2EXTSPERSUM) + rem; + rem = + diFindFree(le32_to_cpu + (iagp->wmap[extno]), 0); + assert(rem < INOSPEREXT); + + /* determine the inode number within the + * iag and allocate the inode from the + * map. + */ + ino = (extno << L2INOSPEREXT) + rem; + rc = diAllocBit(imap, iagp, ino); + IREAD_UNLOCK(ipimap); + if (rc) { + assert(rc == EIO); + } else { + /* set the results of the allocation + * and write the iag. + */ + diInitInode(ip, iagno, ino, extno, + iagp); + mark_metapage_dirty(mp); + } + release_metapage(mp); + + /* free the AG lock and return. + */ + AG_UNLOCK(imap, agno); + return (rc); + + } + + /* check if we may allocate an extent of free + * inodes and whether this word of the free + * extents summary map describes a free extent. + */ + if (addext && ~extsmap) { + /* a free extent has been found. determine + * the extent number. + */ + rem = diFindFree(extsmap, 0); + extno = (sword << L2EXTSPERSUM) + rem; + + /* allocate an extent of free inodes. + */ + if ((rc = diNewExt(imap, iagp, extno))) { + /* if there is no disk space for a + * new extent, try to allocate the + * disk inode from somewhere else. + */ + if (rc == ENOSPC) + break; + + assert(rc == EIO); + } else { + /* set the results of the allocation + * and write the iag. + */ + diInitInode(ip, iagno, + extno << L2INOSPEREXT, + extno, iagp); + mark_metapage_dirty(mp); + } + release_metapage(mp); + /* free the imap inode & the AG lock & return. + */ + IREAD_UNLOCK(ipimap); + AG_UNLOCK(imap, agno); + return (rc); + } + + /* move on to the next set of summary map words. + */ + sword = (sword == SMAPSZ - 1) ? 0 : sword + 1; + inosmap = le32_to_cpu(iagp->inosmap[sword]); + extsmap = le32_to_cpu(iagp->extsmap[sword]); + } + } + /* unlock imap inode */ + IREAD_UNLOCK(ipimap); + + /* nothing doing in this iag, so release it. */ + release_metapage(mp); + + tryag: + /* + * try to allocate anywhere within the same AG as the parent inode. + */ + rc = diAllocAG(imap, agno, dir, ip); + + AG_UNLOCK(imap, agno); + + if (rc != ENOSPC) + return (rc); + + /* + * try to allocate in any AG. + */ + return (diAllocAny(imap, agno, dir, ip)); +} + + +/* + * NAME: diAllocAG(imap,agno,dir,ip) + * + * FUNCTION: allocate a disk inode from the allocation group. + * + * this routine first determines if a new extent of free + * inodes should be added for the allocation group, with + * the current request satisfied from this extent. if this + * is the case, an attempt will be made to do just that. if + * this attempt fails or it has been determined that a new + * extent should not be added, an attempt is made to satisfy + * the request by allocating an existing (backed) free inode + * from the allocation group. + * + * PRE CONDITION: Already have the AG lock for this AG. + * + * PARAMETERS: + * imap - pointer to inode map control structure. + * agno - allocation group to allocate from. + * dir - TRUE if the new disk inode is for a directory. + * ip - pointer to the new inode to be filled in on successful return + * with the disk inode number allocated, its extent address + * and the start of the ag. + * + * RETURN VALUES: + * 0 - success. + * ENOSPC - insufficient disk resources. + * EIO - i/o error. + */ +static int +diAllocAG(imap_t * imap, int agno, boolean_t dir, struct inode *ip) +{ + int rc, addext, numfree, numinos; + + /* get the number of free and the number of backed disk + * inodes currently within the ag. + */ + numfree = imap->im_agctl[agno].numfree; + numinos = imap->im_agctl[agno].numinos; + + if (numfree > numinos) { + jERROR(1,("diAllocAG: numfree > numinos\n")); + updateSuper(ip->i_sb, FM_DIRTY); + return EIO; + } + + /* determine if we should allocate a new extent of free inodes + * within the ag: for directory inodes, add a new extent + * if there are a small number of free inodes or number of free + * inodes is a small percentage of the number of backed inodes. + */ + if (dir == TRUE) + addext = (numfree < 64 || + (numfree < 256 + && ((numfree * 100) / numinos) <= 20)); + else + addext = (numfree == 0); + + /* + * try to allocate a new extent of free inodes. + */ + if (addext) { + /* if free space is not avaliable for this new extent, try + * below to allocate a free and existing (already backed) + * inode from the ag. + */ + if ((rc = diAllocExt(imap, agno, ip)) != ENOSPC) + return (rc); + } + + /* + * try to allocate an existing free inode from the ag. + */ + return (diAllocIno(imap, agno, ip)); +} + + +/* + * NAME: diAllocAny(imap,agno,dir,iap) + * + * FUNCTION: allocate a disk inode from any other allocation group. + * + * this routine is called when an allocation attempt within + * the primary allocation group has failed. if attempts to + * allocate an inode from any allocation group other than the + * specified primary group. + * + * PARAMETERS: + * imap - pointer to inode map control structure. + * agno - primary allocation group (to avoid). + * dir - TRUE if the new disk inode is for a directory. + * ip - pointer to a new inode to be filled in on successful return + * with the disk inode number allocated, its extent address + * and the start of the ag. + * + * RETURN VALUES: + * 0 - success. + * ENOSPC - insufficient disk resources. + * EIO - i/o error. + */ +static int +diAllocAny(imap_t * imap, int agno, boolean_t dir, struct inode *ip) +{ + int ag, rc; + int maxag = JFS_SBI(imap->im_ipimap->i_sb)->bmap->db_maxag; + + + /* try to allocate from the ags following agno up to + * the maximum ag number. + */ + for (ag = agno + 1; ag <= maxag; ag++) { + AG_LOCK(imap, ag); + + rc = diAllocAG(imap, ag, dir, ip); + + AG_UNLOCK(imap, ag); + + if (rc != ENOSPC) + return (rc); + } + + /* try to allocate from the ags in front of agno. + */ + for (ag = 0; ag < agno; ag++) { + AG_LOCK(imap, ag); + + rc = diAllocAG(imap, ag, dir, ip); + + AG_UNLOCK(imap, ag); + + if (rc != ENOSPC) + return (rc); + } + + /* no free disk inodes. + */ + return (ENOSPC); +} + + +/* + * NAME: diAllocIno(imap,agno,ip) + * + * FUNCTION: allocate a disk inode from the allocation group's free + * inode list, returning an error if this free list is + * empty (i.e. no iags on the list). + * + * allocation occurs from the first iag on the list using + * the iag's free inode summary map to find the leftmost + * free inode in the iag. + * + * PRE CONDITION: Already have AG lock for this AG. + * + * PARAMETERS: + * imap - pointer to inode map control structure. + * agno - allocation group. + * ip - pointer to new inode to be filled in on successful return + * with the disk inode number allocated, its extent address + * and the start of the ag. + * + * RETURN VALUES: + * 0 - success. + * ENOSPC - insufficient disk resources. + * EIO - i/o error. + */ +static int diAllocIno(imap_t * imap, int agno, struct inode *ip) +{ + int iagno, ino, rc, rem, extno, sword; + metapage_t *mp; + iag_t *iagp; + + /* check if there are iags on the ag's free inode list. + */ + if ((iagno = imap->im_agctl[agno].inofree) < 0) + return (ENOSPC); + + /* obtain read lock on imap inode */ + IREAD_LOCK(imap->im_ipimap); + + /* read the iag at the head of the list. + */ + if ((rc = diIAGRead(imap, iagno, &mp))) { + IREAD_UNLOCK(imap->im_ipimap); + return (rc); + } + iagp = (iag_t *) mp->data; + + /* better be free inodes in this iag if it is on the + * list. + */ + //assert(iagp->nfreeinos); + if (!iagp->nfreeinos) { + jERROR(1, + ("diAllocIno: nfreeinos = 0, but iag on freelist\n")); + jERROR(1, (" agno = %d, iagno = %d\n", agno, iagno)); + dump_mem("iag", iagp, 64); + updateSuper(ip->i_sb, FM_DIRTY); + return EIO; + } + + /* scan the free inode summary map to find an extent + * with free inodes. + */ + for (sword = 0;; sword++) { + assert(sword < SMAPSZ); + + if (~iagp->inosmap[sword]) + break; + } + + /* found a extent with free inodes. determine + * the extent number. + */ + rem = diFindFree(le32_to_cpu(iagp->inosmap[sword]), 0); + assert(rem < EXTSPERSUM); + extno = (sword << L2EXTSPERSUM) + rem; + + /* find the first free inode in the extent. + */ + rem = diFindFree(le32_to_cpu(iagp->wmap[extno]), 0); + assert(rem < INOSPEREXT); + + /* compute the inode number within the iag. + */ + ino = (extno << L2INOSPEREXT) + rem; + + /* allocate the inode. + */ + rc = diAllocBit(imap, iagp, ino); + IREAD_UNLOCK(imap->im_ipimap); + if (rc) { + release_metapage(mp); + return (rc); + } + + /* set the results of the allocation and write the iag. + */ + diInitInode(ip, iagno, ino, extno, iagp); + write_metapage(mp); + + return (0); +} + + +/* + * NAME: diAllocExt(imap,agno,ip) + * + * FUNCTION: add a new extent of free inodes to an iag, allocating + * an inode from this extent to satisfy the current allocation + * request. + * + * this routine first tries to find an existing iag with free + * extents through the ag free extent list. if list is not + * empty, the head of the list will be selected as the home + * of the new extent of free inodes. otherwise (the list is + * empty), a new iag will be allocated for the ag to contain + * the extent. + * + * once an iag has been selected, the free extent summary map + * is used to locate a free extent within the iag and diNewExt() + * is called to initialize the extent, with initialization + * including the allocation of the first inode of the extent + * for the purpose of satisfying this request. + * + * PARAMETERS: + * imap - pointer to inode map control structure. + * agno - allocation group number. + * ip - pointer to new inode to be filled in on successful return + * with the disk inode number allocated, its extent address + * and the start of the ag. + * + * RETURN VALUES: + * 0 - success. + * ENOSPC - insufficient disk resources. + * EIO - i/o error. + */ +static int diAllocExt(imap_t * imap, int agno, struct inode *ip) +{ + int rem, iagno, sword, extno, rc; + metapage_t *mp; + iag_t *iagp; + + /* check if the ag has any iags with free extents. if not, + * allocate a new iag for the ag. + */ + if ((iagno = imap->im_agctl[agno].extfree) < 0) { + /* If successful, diNewIAG will obtain the read lock on the + * imap inode. + */ + if ((rc = diNewIAG(imap, &iagno, agno, &mp))) { + return (rc); + } + iagp = (iag_t *) mp->data; + + /* set the ag number if this a brand new iag + */ + iagp->agstart = + cpu_to_le64(AGTOBLK(agno, imap->im_ipimap)); + } else { + /* read the iag. + */ + IREAD_LOCK(imap->im_ipimap); + if ((rc = diIAGRead(imap, iagno, &mp))) { + assert(0); + } + iagp = (iag_t *) mp->data; + } + + /* using the free extent summary map, find a free extent. + */ + for (sword = 0;; sword++) { + assert(sword < SMAPSZ); + if (~iagp->extsmap[sword]) + break; + } + + /* determine the extent number of the free extent. + */ + rem = diFindFree(le32_to_cpu(iagp->extsmap[sword]), 0); + assert(rem < EXTSPERSUM); + extno = (sword << L2EXTSPERSUM) + rem; + + /* initialize the new extent. + */ + rc = diNewExt(imap, iagp, extno); + IREAD_UNLOCK(imap->im_ipimap); + if (rc) { + /* something bad happened. if a new iag was allocated, + * place it back on the inode map's iag free list, and + * clear the ag number information. + */ + if (iagp->nfreeexts == cpu_to_le32(EXTSPERIAG)) { + IAGFREE_LOCK(imap); + iagp->iagfree = cpu_to_le32(imap->im_freeiag); + imap->im_freeiag = iagno; + IAGFREE_UNLOCK(imap); + } + write_metapage(mp); + return (rc); + } + + /* set the results of the allocation and write the iag. + */ + diInitInode(ip, iagno, extno << L2INOSPEREXT, extno, iagp); + + write_metapage(mp); + + return (0); +} + + +/* + * NAME: diAllocBit(imap,iagp,ino) + * + * FUNCTION: allocate a backed inode from an iag. + * + * this routine performs the mechanics of allocating a + * specified inode from a backed extent. + * + * if the inode to be allocated represents the last free + * inode within the iag, the iag will be removed from the + * ag free inode list. + * + * a careful update approach is used to provide consistency + * in the face of updates to multiple buffers. under this + * approach, all required buffers are obtained before making + * any updates and are held all are updates are complete. + * + * PRE CONDITION: Already have buffer lock on iagp. Already have AG lock on + * this AG. Must have read lock on imap inode. + * + * PARAMETERS: + * imap - pointer to inode map control structure. + * iagp - pointer to iag. + * ino - inode number to be allocated within the iag. + * + * RETURN VALUES: + * 0 - success. + * ENOSPC - insufficient disk resources. + * EIO - i/o error. + */ +static int diAllocBit(imap_t * imap, iag_t * iagp, int ino) +{ + int extno, bitno, agno, sword, rc; + metapage_t *amp, *bmp; + iag_t *aiagp = 0, *biagp = 0; + u32 mask; + + /* check if this is the last free inode within the iag. + * if so, it will have to be removed from the ag free + * inode list, so get the iags preceeding and following + * it on the list. + */ + if (iagp->nfreeinos == cpu_to_le32(1)) { + amp = bmp = NULL; + + if ((int) le32_to_cpu(iagp->inofreefwd) >= 0) { + if ((rc = + diIAGRead(imap, le32_to_cpu(iagp->inofreefwd), + &))) + return (rc); + aiagp = (iag_t *) amp->data; + } + + if ((int) le32_to_cpu(iagp->inofreeback) >= 0) { + if ((rc = + diIAGRead(imap, + le32_to_cpu(iagp->inofreeback), + &bmp))) { + if (amp) + release_metapage(amp); + return (rc); + } + biagp = (iag_t *) bmp->data; + } + } + + /* get the ag number, extent number, inode number within + * the extent. + */ + agno = BLKTOAG(le64_to_cpu(iagp->agstart), JFS_SBI(imap->im_ipimap->i_sb)); + extno = ino >> L2INOSPEREXT; + bitno = ino & (INOSPEREXT - 1); + + /* compute the mask for setting the map. + */ + mask = HIGHORDER >> bitno; + + /* the inode should be free and backed. + */ + assert((le32_to_cpu(iagp->pmap[extno]) & mask) == 0); + assert((le32_to_cpu(iagp->wmap[extno]) & mask) == 0); + assert(addressPXD(&iagp->inoext[extno]) != 0); + + /* mark the inode as allocated in the working map. + */ + iagp->wmap[extno] |= cpu_to_le32(mask); + + /* check if all inodes within the extent are now + * allocated. if so, update the free inode summary + * map to reflect this. + */ + if (iagp->wmap[extno] == ONES) { + sword = extno >> L2EXTSPERSUM; + bitno = extno & (EXTSPERSUM - 1); + iagp->inosmap[sword] |= cpu_to_le32(HIGHORDER >> bitno); + } + + /* if this was the last free inode in the iag, remove the + * iag from the ag free inode list. + */ + if (iagp->nfreeinos == cpu_to_le32(1)) { + if (amp) { + aiagp->inofreeback = iagp->inofreeback; + write_metapage(amp); + } + + if (bmp) { + biagp->inofreefwd = iagp->inofreefwd; + write_metapage(bmp); + } else { + imap->im_agctl[agno].inofree = + le32_to_cpu(iagp->inofreefwd); + } + iagp->inofreefwd = iagp->inofreeback = -1; + } + + /* update the free inode count at the iag, ag, inode + * map levels. + */ + iagp->nfreeinos = cpu_to_le32(le32_to_cpu(iagp->nfreeinos) - 1); + imap->im_agctl[agno].numfree -= 1; + atomic_dec(&imap->im_numfree); + + return (0); +} + + +/* + * NAME: diNewExt(imap,iagp,extno) + * + * FUNCTION: initialize a new extent of inodes for an iag, allocating + * the first inode of the extent for use for the current + * allocation request. + * + * disk resources are allocated for the new extent of inodes + * and the inodes themselves are initialized to reflect their + * existence within the extent (i.e. their inode numbers and + * inode extent addresses are set) and their initial state + * (mode and link count are set to zero). + * + * if the iag is new, it is not yet on an ag extent free list + * but will now be placed on this list. + * + * if the allocation of the new extent causes the iag to + * have no free extent, the iag will be removed from the + * ag extent free list. + * + * if the iag has no free backed inodes, it will be placed + * on the ag free inode list, since the addition of the new + * extent will now cause it to have free inodes. + * + * a careful update approach is used to provide consistency + * (i.e. list consistency) in the face of updates to multiple + * buffers. under this approach, all required buffers are + * obtained before making any updates and are held until all + * updates are complete. + * + * PRE CONDITION: Already have buffer lock on iagp. Already have AG lock on + * this AG. Must have read lock on imap inode. + * + * PARAMETERS: + * imap - pointer to inode map control structure. + * iagp - pointer to iag. + * extno - extent number. + * + * RETURN VALUES: + * 0 - success. + * ENOSPC - insufficient disk resources. + * EIO - i/o error. + */ +static int diNewExt(imap_t * imap, iag_t * iagp, int extno) +{ + int agno, iagno, fwd, back, freei = 0, sword, rc; + iag_t *aiagp = 0, *biagp = 0, *ciagp = 0; + metapage_t *amp, *bmp, *cmp, *dmp; + struct inode *ipimap; + s64 blkno, hint; + int i, j; + u32 mask; + ino_t ino; + dinode_t *dp; + struct jfs_sb_info *sbi; + + /* better have free extents. + */ + assert(iagp->nfreeexts); + + /* get the inode map inode. + */ + ipimap = imap->im_ipimap; + sbi = JFS_SBI(ipimap->i_sb); + + amp = bmp = cmp = NULL; + + /* get the ag and iag numbers for this iag. + */ + agno = BLKTOAG(le64_to_cpu(iagp->agstart), sbi); + iagno = le32_to_cpu(iagp->iagnum); + + /* check if this is the last free extent within the + * iag. if so, the iag must be removed from the ag + * free extent list, so get the iags preceeding and + * following the iag on this list. + */ + if (iagp->nfreeexts == cpu_to_le32(1)) { + if ((fwd = le32_to_cpu(iagp->extfreefwd)) >= 0) { + if ((rc = diIAGRead(imap, fwd, &))) + return (rc); + aiagp = (iag_t *) amp->data; + } + + if ((back = le32_to_cpu(iagp->extfreeback)) >= 0) { + if ((rc = diIAGRead(imap, back, &bmp))) + goto error_out; + biagp = (iag_t *) bmp->data; + } + } else { + /* the iag has free extents. if all extents are free + * (as is the case for a newly allocated iag), the iag + * must be added to the ag free extent list, so get + * the iag at the head of the list in preparation for + * adding this iag to this list. + */ + fwd = back = -1; + if (iagp->nfreeexts == cpu_to_le32(EXTSPERIAG)) { + if ((fwd = imap->im_agctl[agno].extfree) >= 0) { + if ((rc = diIAGRead(imap, fwd, &))) + goto error_out; + aiagp = (iag_t *) amp->data; + } + } + } + + /* check if the iag has no free inodes. if so, the iag + * will have to be added to the ag free inode list, so get + * the iag at the head of the list in preparation for + * adding this iag to this list. in doing this, we must + * check if we already have the iag at the head of + * the list in hand. + */ + if (iagp->nfreeinos == 0) { + freei = imap->im_agctl[agno].inofree; + + if (freei >= 0) { + if (freei == fwd) { + ciagp = aiagp; + } else if (freei == back) { + ciagp = biagp; + } else { + if ((rc = diIAGRead(imap, freei, &cmp))) + goto error_out; + ciagp = (iag_t *) cmp->data; + } + assert(ciagp != NULL); + } + } + + /* allocate disk space for the inode extent. + */ + if ((extno == 0) || (addressPXD(&iagp->inoext[extno - 1]) == 0)) + hint = ((s64) agno << sbi->bmap->db_agl2size) - 1; + else + hint = addressPXD(&iagp->inoext[extno - 1]) + + lengthPXD(&iagp->inoext[extno - 1]) - 1; + + if ((rc = dbAlloc(ipimap, hint, (s64) imap->im_nbperiext, &blkno))) + goto error_out; + + /* compute the inode number of the first inode within the + * extent. + */ + ino = (iagno << L2INOSPERIAG) + (extno << L2INOSPEREXT); + + /* initialize the inodes within the newly allocated extent a + * page at a time. + */ + for (i = 0; i < imap->im_nbperiext; i += sbi->nbperpage) { + /* get a buffer for this page of disk inodes. + */ + dmp = get_metapage(ipimap, blkno + i, PSIZE, 1); + if (dmp == NULL) { + rc = EIO; + goto error_out; + } + dp = (dinode_t *) dmp->data; + + /* initialize the inode number, mode, link count and + * inode extent address. + */ + for (j = 0; j < INOSPERPAGE; j++, dp++, ino++) { + dp->di_inostamp = cpu_to_le32(sbi->inostamp); + dp->di_number = cpu_to_le32(ino); + dp->di_fileset = cpu_to_le32(FILESYSTEM_I); + dp->di_mode = 0; + dp->di_nlink = 0; + PXDaddress(&(dp->di_ixpxd), blkno); + PXDlength(&(dp->di_ixpxd), imap->im_nbperiext); + } + write_metapage(dmp); + } + + /* if this is the last free extent within the iag, remove the + * iag from the ag free extent list. + */ + if (iagp->nfreeexts == cpu_to_le32(1)) { + if (fwd >= 0) + aiagp->extfreeback = iagp->extfreeback; + + if (back >= 0) + biagp->extfreefwd = iagp->extfreefwd; + else + imap->im_agctl[agno].extfree = + le32_to_cpu(iagp->extfreefwd); + + iagp->extfreefwd = iagp->extfreeback = -1; + } else { + /* if the iag has all free extents (newly allocated iag), + * add the iag to the ag free extent list. + */ + if (iagp->nfreeexts == cpu_to_le32(EXTSPERIAG)) { + if (fwd >= 0) + aiagp->extfreeback = cpu_to_le32(iagno); + + iagp->extfreefwd = cpu_to_le32(fwd); + iagp->extfreeback = -1; + imap->im_agctl[agno].extfree = iagno; + } + } + + /* if the iag has no free inodes, add the iag to the + * ag free inode list. + */ + if (iagp->nfreeinos == 0) { + if (freei >= 0) + ciagp->inofreeback = cpu_to_le32(iagno); + + iagp->inofreefwd = + cpu_to_le32(imap->im_agctl[agno].inofree); + iagp->inofreeback = -1; + imap->im_agctl[agno].inofree = iagno; + } + + /* initialize the extent descriptor of the extent. */ + PXDlength(&iagp->inoext[extno], imap->im_nbperiext); + PXDaddress(&iagp->inoext[extno], blkno); + + /* initialize the working and persistent map of the extent. + * the working map will be initialized such that + * it indicates the first inode of the extent is allocated. + */ + iagp->wmap[extno] = cpu_to_le32(HIGHORDER); + iagp->pmap[extno] = 0; + + /* update the free inode and free extent summary maps + * for the extent to indicate the extent has free inodes + * and no longer represents a free extent. + */ + sword = extno >> L2EXTSPERSUM; + mask = HIGHORDER >> (extno & (EXTSPERSUM - 1)); + iagp->extsmap[sword] |= cpu_to_le32(mask); + iagp->inosmap[sword] &= cpu_to_le32(~mask); + + /* update the free inode and free extent counts for the + * iag. + */ + iagp->nfreeinos = cpu_to_le32(le32_to_cpu(iagp->nfreeinos) + + (INOSPEREXT - 1)); + iagp->nfreeexts = cpu_to_le32(le32_to_cpu(iagp->nfreeexts) - 1); + + /* update the free and backed inode counts for the ag. + */ + imap->im_agctl[agno].numfree += (INOSPEREXT - 1); + imap->im_agctl[agno].numinos += INOSPEREXT; + + /* update the free and backed inode counts for the inode map. + */ + atomic_add(INOSPEREXT - 1, &imap->im_numfree); + atomic_add(INOSPEREXT, &imap->im_numinos); + + /* write the iags. + */ + if (amp) + write_metapage(amp); + if (bmp) + write_metapage(bmp); + if (cmp) + write_metapage(cmp); + + return (0); + + error_out: + + /* release the iags. + */ + if (amp) + release_metapage(amp); + if (bmp) + release_metapage(bmp); + if (cmp) + release_metapage(cmp); + + return (rc); +} + + +/* + * NAME: diNewIAG(imap,iagnop,agno) + * + * FUNCTION: allocate a new iag for an allocation group. + * + * first tries to allocate the iag from the inode map + * iagfree list: + * if the list has free iags, the head of the list is removed + * and returned to satisfy the request. + * if the inode map's iag free list is empty, the inode map + * is extended to hold a new iag. this new iag is initialized + * and returned to satisfy the request. + * + * PARAMETERS: + * imap - pointer to inode map control structure. + * iagnop - pointer to an iag number set with the number of the + * newly allocated iag upon successful return. + * agno - allocation group number. + * bpp - Buffer pointer to be filled in with new IAG's buffer + * + * RETURN VALUES: + * 0 - success. + * ENOSPC - insufficient disk resources. + * EIO - i/o error. + * + * serialization: + * AG lock held on entry/exit; + * write lock on the map is held inside; + * read lock on the map is held on successful completion; + * + * note: new iag transaction: + * . synchronously write iag; + * . write log of xtree and inode of imap; + * . commit; + * . synchronous write of xtree (right to left, bottom to top); + * . at start of logredo(): init in-memory imap with one additional iag page; + * . at end of logredo(): re-read imap inode to determine + * new imap size; + */ +static int +diNewIAG(imap_t * imap, int *iagnop, int agno, metapage_t ** mpp) +{ + int rc; + int iagno, i, xlen; + struct inode *ipimap; + struct super_block *sb; + struct jfs_sb_info *sbi; + metapage_t *mp; + iag_t *iagp; + s64 xaddr = 0; + s64 blkno; + tid_t tid; +#ifdef _STILL_TO_PORT + xad_t xad; +#endif /* _STILL_TO_PORT */ + struct inode *iplist[1]; + + /* pick up pointers to the inode map and mount inodes */ + ipimap = imap->im_ipimap; + sb = ipimap->i_sb; + sbi = JFS_SBI(sb); + + /* acquire the free iag lock */ + IAGFREE_LOCK(imap); + + /* if there are any iags on the inode map free iag list, + * allocate the iag from the head of the list. + */ + if (imap->im_freeiag >= 0) { + /* pick up the iag number at the head of the list */ + iagno = imap->im_freeiag; + + /* determine the logical block number of the iag */ + blkno = IAGTOLBLK(iagno, sbi->l2nbperpage); + } else { + /* no free iags. the inode map will have to be extented + * to include a new iag. + */ + + /* acquire inode map lock */ + IWRITE_LOCK(ipimap); + + assert(ipimap->i_size >> L2PSIZE == imap->im_nextiag + 1); + + /* get the next avaliable iag number */ + iagno = imap->im_nextiag; + + /* make sure that we have not exceeded the maximum inode + * number limit. + */ + if (iagno > (MAXIAGS - 1)) { + /* release the inode map lock */ + IWRITE_UNLOCK(ipimap); + + rc = ENOSPC; + goto out; + } + + /* + * synchronously append new iag page. + */ + /* determine the logical address of iag page to append */ + blkno = IAGTOLBLK(iagno, sbi->l2nbperpage); + + /* Allocate extent for new iag page */ + xlen = sbi->nbperpage; + if ((rc = dbAlloc(ipimap, 0, (s64) xlen, &xaddr))) { + /* release the inode map lock */ + IWRITE_UNLOCK(ipimap); + + goto out; + } + + /* assign a buffer for the page */ + mp = get_metapage(ipimap, xaddr, PSIZE, 1); + //bp = bmAssign(ipimap, blkno, xaddr, PSIZE, bmREAD_PAGE); + if (!mp) { + /* Free the blocks allocated for the iag since it was + * not successfully added to the inode map + */ + dbFree(ipimap, xaddr, (s64) xlen); + + /* release the inode map lock */ + IWRITE_UNLOCK(ipimap); + + rc = EIO; + goto out; + } + iagp = (iag_t *) mp->data; + + /* init the iag */ + memset(iagp, 0, sizeof(iag_t)); + iagp->iagnum = cpu_to_le32(iagno); + iagp->inofreefwd = iagp->inofreeback = -1; + iagp->extfreefwd = iagp->extfreeback = -1; + iagp->iagfree = -1; + iagp->nfreeinos = 0; + iagp->nfreeexts = cpu_to_le32(EXTSPERIAG); + + /* initialize the free inode summary map (free extent + * summary map initialization handled by bzero). + */ + for (i = 0; i < SMAPSZ; i++) + iagp->inosmap[i] = ONES; + + flush_metapage(mp); +#ifdef _STILL_TO_PORT + /* synchronously write the iag page */ + if (bmWrite(bp)) { + /* Free the blocks allocated for the iag since it was + * not successfully added to the inode map + */ + dbFree(ipimap, xaddr, (s64) xlen); + + /* release the inode map lock */ + IWRITE_UNLOCK(ipimap); + + rc = EIO; + goto out; + } + + /* Now the iag is on disk */ + + /* + * start tyransaction of update of the inode map + * addressing structure pointing to the new iag page; + */ +#endif /* _STILL_TO_PORT */ + tid = txBegin(sb, COMMIT_FORCE); + + /* update the inode map addressing structure to point to it */ + if ((rc = + xtInsert(tid, ipimap, 0, blkno, xlen, &xaddr, 0))) { + /* Free the blocks allocated for the iag since it was + * not successfully added to the inode map + */ + dbFree(ipimap, xaddr, (s64) xlen); + + /* release the inode map lock */ + IWRITE_UNLOCK(ipimap); + + goto out; + } + + /* update the inode map's inode to reflect the extension */ + ipimap->i_size += PSIZE; + ipimap->i_blocks += LBLK2PBLK(sb, xlen); + + /* + * txCommit(COMMIT_FORCE) will synchronously write address + * index pages and inode after commit in careful update order + * of address index pages (right to left, bottom up); + */ + iplist[0] = ipimap; + rc = txCommit(tid, 1, &iplist[0], COMMIT_FORCE); + + txEnd(tid); + + duplicateIXtree(sb, blkno, xlen, &xaddr); + + /* update the next avaliable iag number */ + imap->im_nextiag += 1; + + /* Add the iag to the iag free list so we don't lose the iag + * if a failure happens now. + */ + imap->im_freeiag = iagno; + + /* Until we have logredo working, we want the imap inode & + * control page to be up to date. + */ + diSync(ipimap); + + /* release the inode map lock */ + IWRITE_UNLOCK(ipimap); + } + + /* obtain read lock on map */ + IREAD_LOCK(ipimap); + + /* read the iag */ + if ((rc = diIAGRead(imap, iagno, &mp))) { + IREAD_UNLOCK(ipimap); + rc = EIO; + goto out; + } + iagp = (iag_t *) mp->data; + + /* remove the iag from the iag free list */ + imap->im_freeiag = le32_to_cpu(iagp->iagfree); + iagp->iagfree = -1; + + /* set the return iag number and buffer pointer */ + *iagnop = iagno; + *mpp = mp; + + out: + /* release the iag free lock */ + IAGFREE_UNLOCK(imap); + + return (rc); +} + +/* + * NAME: diIAGRead() + * + * FUNCTION: get the buffer for the specified iag within a fileset + * or aggregate inode map. + * + * PARAMETERS: + * imap - pointer to inode map control structure. + * iagno - iag number. + * bpp - point to buffer pointer to be filled in on successful + * exit. + * + * SERIALIZATION: + * must have read lock on imap inode + * (When called by diExtendFS, the filesystem is quiesced, therefore + * the read lock is unnecessary.) + * + * RETURN VALUES: + * 0 - success. + * EIO - i/o error. + */ +static int diIAGRead(imap_t * imap, int iagno, metapage_t ** mpp) +{ + struct inode *ipimap = imap->im_ipimap; + s64 blkno; + + /* compute the logical block number of the iag. */ + blkno = IAGTOLBLK(iagno, JFS_SBI(ipimap->i_sb)->l2nbperpage); + + /* read the iag. */ + *mpp = read_metapage(ipimap, blkno, PSIZE, 0); + if (*mpp == NULL) { + return (EIO); + } + + return (0); +} + +/* + * NAME: diFindFree() + * + * FUNCTION: find the first free bit in a word starting at + * the specified bit position. + * + * PARAMETERS: + * word - word to be examined. + * start - starting bit position. + * + * RETURN VALUES: + * bit position of first free bit in the word or 32 if + * no free bits were found. + */ +static int diFindFree(u32 word, int start) +{ + int bitno; + assert(start < 32); + /* scan the word for the first free bit. */ + for (word <<= start, bitno = start; bitno < 32; + bitno++, word <<= 1) { + if ((word & HIGHORDER) == 0) + break; + } + return (bitno); +} + +/* + * NAME: diUpdatePMap() + * + * FUNCTION: Update the persistent map in an IAG for the allocation or + * freeing of the specified inode. + * + * PRE CONDITIONS: Working map has already been updated for allocate. + * + * PARAMETERS: + * ipimap - Incore inode map inode + * inum - Number of inode to mark in permanent map + * is_free - If TRUE indicates inode should be marked freed, otherwise + * indicates inode should be marked allocated. + * + * RETURNS: 0 for success + */ +int +diUpdatePMap(struct inode *ipimap, + unsigned long inum, boolean_t is_free, tblock_t * tblk) +{ + int rc; + iag_t *iagp; + metapage_t *mp; + int iagno, ino, extno, bitno; + imap_t *imap; + u32 mask; + log_t *log; + int lsn, difft, diffp; + + imap = JFS_IP(ipimap)->i_imap; + /* get the iag number containing the inode */ + iagno = INOTOIAG(inum); + /* make sure that the iag is contained within the map */ + assert(iagno < imap->im_nextiag); + /* read the iag */ + IREAD_LOCK(ipimap); + rc = diIAGRead(imap, iagno, &mp); + IREAD_UNLOCK(ipimap); + if (rc) + return (rc); + iagp = (iag_t *) mp->data; + /* get the inode number and extent number of the inode within + * the iag and the inode number within the extent. + */ + ino = inum & (INOSPERIAG - 1); + extno = ino >> L2INOSPEREXT; + bitno = ino & (INOSPEREXT - 1); + mask = HIGHORDER >> bitno; + /* + * mark the inode free in persistent map: + */ + if (is_free == TRUE) { + /* The inode should have been allocated both in working + * map and in persistent map; + * the inode will be freed from working map at the release + * of last reference release; + */ +// assert(le32_to_cpu(iagp->wmap[extno]) & mask); + if (!(le32_to_cpu(iagp->wmap[extno]) & mask)) { + jERROR(1, + ("diUpdatePMap: inode %ld not marked as allocated in wmap!\n", + inum)); + updateSuper(ipimap->i_sb, FM_DIRTY); + } +// assert(le32_to_cpu(iagp->pmap[extno]) & mask); + if (!(le32_to_cpu(iagp->pmap[extno]) & mask)) { + jERROR(1, + ("diUpdatePMap: inode %ld not marked as allocated in pmap!\n", + inum)); + updateSuper(ipimap->i_sb, FM_DIRTY); + } + /* update the bitmap for the extent of the freed inode */ + iagp->pmap[extno] &= cpu_to_le32(~mask); + } + /* + * mark the inode allocated in persistent map: + */ + else { + /* The inode should be already allocated in the working map + * and should be free in persistent map; + */ + assert(le32_to_cpu(iagp->wmap[extno]) & mask); + assert((le32_to_cpu(iagp->pmap[extno]) & mask) == 0); + /* update the bitmap for the extent of the allocated inode */ + iagp->pmap[extno] |= cpu_to_le32(mask); + } + /* + * update iag lsn + */ + lsn = tblk->lsn; + log = JFS_SBI(tblk->sb)->log; + if (mp->lsn != 0) { + /* inherit older/smaller lsn */ + logdiff(difft, lsn, log); + logdiff(diffp, mp->lsn, log); + if (difft < diffp) { + mp->lsn = lsn; + /* move mp after tblock in logsync list */ + LOGSYNC_LOCK(log); + list_del(&mp->synclist); + list_add(&mp->synclist, &tblk->synclist); + LOGSYNC_UNLOCK(log); + } + /* inherit younger/larger clsn */ + LOGSYNC_LOCK(log); + assert(mp->clsn); + logdiff(difft, tblk->clsn, log); + logdiff(diffp, mp->clsn, log); + if (difft > diffp) + mp->clsn = tblk->clsn; + LOGSYNC_UNLOCK(log); + } else { + mp->log = log; + mp->lsn = lsn; + /* insert mp after tblock in logsync list */ + LOGSYNC_LOCK(log); + log->count++; + list_add(&mp->synclist, &tblk->synclist); + mp->clsn = tblk->clsn; + LOGSYNC_UNLOCK(log); + } +// bmLazyWrite(mp, log->flag & JFS_COMMIT); + write_metapage(mp); + return (0); +} + +/* + * diExtendFS() + * + * function: update imap for extendfs(); + * + * note: AG size has been increased s.t. each k old contiguous AGs are + * coalesced into a new AG; + */ +int diExtendFS(struct inode *ipimap, struct inode *ipbmap) +{ + int rc, rcx = 0; + imap_t *imap = JFS_IP(ipimap)->i_imap; + iag_t *iagp = 0, *hiagp = 0; + bmap_t *mp = JFS_SBI(ipbmap->i_sb)->bmap; + metapage_t *bp, *hbp; + int i, n, head; + int numinos, xnuminos = 0, xnumfree = 0; + s64 agstart; + + jEVENT(0, ("diExtendFS: nextiag:%d numinos:%d numfree:%d\n", + imap->im_nextiag, atomic_read(&imap->im_numinos), + atomic_read(&imap->im_numfree))); + + /* + * reconstruct imap + * + * coalesce contiguous k (newAGSize/oldAGSize) AGs; + * i.e., (AGi, ..., AGj) where i = k*n and j = k*(n+1) - 1 to AGn; + * note: new AG size = old AG size * (2**x). + */ + + /* init per AG control information im_agctl[] */ + for (i = 0; i < MAXAG; i++) { + imap->im_agctl[i].inofree = -1; /* free inode list */ + imap->im_agctl[i].extfree = -1; /* free extent list */ + imap->im_agctl[i].numinos = 0; /* number of backed inodes */ + imap->im_agctl[i].numfree = 0; /* number of free backed inodes */ + } + + /* + * process each iag_t page of the map. + * + * rebuild AG Free Inode List, AG Free Inode Extent List; + */ + for (i = 0; i < imap->im_nextiag; i++) { + if ((rc = diIAGRead(imap, i, &bp))) { + rcx = rc; + continue; + } + iagp = (iag_t *) bp->data; + assert(le32_to_cpu(iagp->iagnum) == i); + + /* leave free iag in the free iag list */ + if (iagp->nfreeexts == cpu_to_le32(EXTSPERIAG)) { + release_metapage(bp); + continue; + } + + /* agstart that computes to the same ag is treated as same; */ + agstart = le64_to_cpu(iagp->agstart); + /* iagp->agstart = agstart & ~(mp->db_agsize - 1); */ + n = agstart >> mp->db_agl2size; +/* +printf("diExtendFS: iag:%d agstart:%Ld agno:%d\n", i, agstart, n); +*/ + + /* compute backed inodes */ + numinos = (EXTSPERIAG - le32_to_cpu(iagp->nfreeexts)) + << L2INOSPEREXT; + if (numinos > 0) { + /* merge AG backed inodes */ + imap->im_agctl[n].numinos += numinos; + xnuminos += numinos; + } + + /* if any backed free inodes, insert at AG free inode list */ + if ((int) le32_to_cpu(iagp->nfreeinos) > 0) { + if ((head = imap->im_agctl[n].inofree) == -1) + iagp->inofreefwd = iagp->inofreeback = -1; + else { + if ((rc = diIAGRead(imap, head, &hbp))) { + rcx = rc; + goto nextiag; + } + hiagp = (iag_t *) hbp->data; + hiagp->inofreeback = + le32_to_cpu(iagp->iagnum); + iagp->inofreefwd = cpu_to_le32(head); + iagp->inofreeback = -1; + write_metapage(hbp); + } + + imap->im_agctl[n].inofree = + le32_to_cpu(iagp->iagnum); + + /* merge AG backed free inodes */ + imap->im_agctl[n].numfree += + le32_to_cpu(iagp->nfreeinos); + xnumfree += le32_to_cpu(iagp->nfreeinos); + } + + /* if any free extents, insert at AG free extent list */ + if (le32_to_cpu(iagp->nfreeexts) > 0) { + if ((head = imap->im_agctl[n].extfree) == -1) + iagp->extfreefwd = iagp->extfreeback = -1; + else { + if ((rc = diIAGRead(imap, head, &hbp))) { + rcx = rc; + goto nextiag; + } + hiagp = (iag_t *) hbp->data; + hiagp->extfreeback = iagp->iagnum; + iagp->extfreefwd = cpu_to_le32(head); + iagp->extfreeback = -1; + write_metapage(hbp); + } + + imap->im_agctl[n].extfree = + le32_to_cpu(iagp->iagnum); + } + + nextiag: + write_metapage(bp); + } + + ASSERT(xnuminos == atomic_read(&imap->im_numinos) && + xnumfree == atomic_read(&imap->im_numfree)); + + return rcx; +} + + +/* + * duplicateIXtree() + * + * serialization: IWRITE_LOCK held on entry/exit + * + * note: shadow page with regular inode (rel.2); + */ +static void +duplicateIXtree(struct super_block *sb, s64 blkno, int xlen, s64 * xaddr) +{ + int rc; + tid_t tid; + struct inode *ip; + metapage_t *mpsuper; + struct jfs_superblock *j_sb; + + /* if AIT2 ipmap2 is bad, do not try to update it */ + if (JFS_SBI(sb)->mntflag & JFS_BAD_SAIT) /* s_flag */ + return; + ip = diReadSpecial(sb, FILESYSTEM_I + INOSPEREXT); + if (ip == 0) { + JFS_SBI(sb)->mntflag |= JFS_BAD_SAIT; + if ((rc = readSuper(sb, &mpsuper))) + return; + j_sb = (struct jfs_superblock *) (mpsuper->data); + j_sb->s_flag |= JFS_BAD_SAIT; + write_metapage(mpsuper); + return; + } + + /* start transaction */ + tid = txBegin(sb, COMMIT_FORCE); + /* update the inode map addressing structure to point to it */ + if ((rc = xtInsert(tid, ip, 0, blkno, xlen, xaddr, 0))) { + JFS_SBI(sb)->mntflag |= JFS_BAD_SAIT; + txAbort(tid, 1); + goto cleanup; + + } + /* update the inode map's inode to reflect the extension */ + ip->i_size += PSIZE; + ip->i_blocks += LBLK2PBLK(sb, xlen); + rc = txCommit(tid, 1, &ip, COMMIT_FORCE); + cleanup: + txEnd(tid); + diFreeSpecial(ip); +} + +/* + * NAME: copy_from_dinode() + * + * FUNCTION: Copies inode info from disk inode to in-memory inode + * + * RETURN VALUES: + * 0 - success + * ENOMEM - insufficient memory + */ +static int copy_from_dinode(dinode_t * dip, struct inode *ip) +{ + struct jfs_inode_info *jfs_ip = JFS_IP(ip); + + jfs_ip->fileset = le32_to_cpu(dip->di_fileset); + jfs_ip->mode2 = le32_to_cpu(dip->di_mode); + + ip->i_mode = le32_to_cpu(dip->di_mode) & 0xffff; + ip->i_nlink = le32_to_cpu(dip->di_nlink); + ip->i_uid = le32_to_cpu(dip->di_uid); + ip->i_gid = le32_to_cpu(dip->di_gid); + ip->i_size = le64_to_cpu(dip->di_size); + ip->i_atime = le32_to_cpu(dip->di_atime.tv_sec); + ip->i_mtime = le32_to_cpu(dip->di_mtime.tv_sec); + ip->i_ctime = le32_to_cpu(dip->di_ctime.tv_sec); + ip->i_blksize = ip->i_sb->s_blocksize; + ip->i_blocks = LBLK2PBLK(ip->i_sb, le64_to_cpu(dip->di_nblocks)); + ip->i_version = ++event; + ip->i_generation = le32_to_cpu(dip->di_gen); + + jfs_ip->ixpxd = dip->di_ixpxd; /* in-memory pxd's are little-endian */ + jfs_ip->acl = dip->di_acl; /* as are dxd's */ + jfs_ip->ea = dip->di_ea; + jfs_ip->next_index = le32_to_cpu(dip->di_next_index); + jfs_ip->otime = le32_to_cpu(dip->di_otime.tv_sec); + jfs_ip->acltype = le32_to_cpu(dip->di_acltype); + /* + * We may only need to do this for "special" inodes (dmap, imap) + */ + if (S_ISCHR(ip->i_mode) || S_ISBLK(ip->i_mode)) + ip->i_rdev = to_kdev_t(le32_to_cpu(dip->di_rdev)); + else if (S_ISDIR(ip->i_mode)) { + memcpy(&jfs_ip->i_dirtable, &dip->di_dirtable, 384); + } else if (!S_ISFIFO(ip->i_mode)) { + memcpy(&jfs_ip->i_xtroot, &dip->di_xtroot, 288); + } + /* Zero the in-memory-only stuff */ + jfs_ip->cflag = 0; + jfs_ip->btindex = 0; + jfs_ip->btorder = 0; + jfs_ip->bxflag = 0; + jfs_ip->blid = 0; + jfs_ip->atlhead = 0; + jfs_ip->atltail = 0; + jfs_ip->xtlid = 0; + return (0); +} + +/* + * NAME: copy_to_dinode() + * + * FUNCTION: Copies inode info from in-memory inode to disk inode + */ +static void copy_to_dinode(dinode_t * dip, struct inode *ip) +{ + struct jfs_inode_info *jfs_ip = JFS_IP(ip); + + dip->di_fileset = cpu_to_le32(jfs_ip->fileset); + dip->di_inostamp = cpu_to_le32(JFS_SBI(ip->i_sb)->inostamp); + dip->di_number = cpu_to_le32(ip->i_ino); + dip->di_gen = cpu_to_le32(ip->i_generation); + dip->di_size = cpu_to_le64(ip->i_size); + dip->di_nblocks = cpu_to_le64(PBLK2LBLK(ip->i_sb, ip->i_blocks)); + dip->di_nlink = cpu_to_le32(ip->i_nlink); + dip->di_uid = cpu_to_le32(ip->i_uid); + dip->di_gid = cpu_to_le32(ip->i_gid); + /* + * mode2 is only needed for storing the higher order bits. + * Trust i_mode for the lower order ones + */ + dip->di_mode = cpu_to_le32((jfs_ip->mode2 & 0xffff0000) | ip->i_mode); + dip->di_atime.tv_sec = cpu_to_le32(ip->i_atime); + dip->di_atime.tv_nsec = 0; + dip->di_ctime.tv_sec = cpu_to_le32(ip->i_ctime); + dip->di_ctime.tv_nsec = 0; + dip->di_mtime.tv_sec = cpu_to_le32(ip->i_mtime); + dip->di_mtime.tv_nsec = 0; + dip->di_ixpxd = jfs_ip->ixpxd; /* in-memory pxd's are little-endian */ + dip->di_acl = jfs_ip->acl; /* as are dxd's */ + dip->di_ea = jfs_ip->ea; + dip->di_next_index = cpu_to_le32(jfs_ip->next_index); + dip->di_otime.tv_sec = cpu_to_le32(jfs_ip->otime); + dip->di_otime.tv_nsec = 0; + dip->di_acltype = cpu_to_le32(jfs_ip->acltype); + + if (S_ISCHR(ip->i_mode) || S_ISBLK(ip->i_mode)) + dip->di_rdev = cpu_to_le32(kdev_t_to_nr(ip->i_rdev)); +} + +#ifdef _JFS_DEBUG_IMAP +/* + * DBGdiInit() + */ +static void *DBGdiInit(imap_t * imap) +{ + u32 *dimap; + int size; + size = 64 * 1024; + if ((dimap = (u32 *) xmalloc(size, L2PSIZE, kernel_heap)) == NULL) + assert(0); + bzero((void *) dimap, size); + imap->im_DBGdimap = dimap; +} + +/* + * DBGdiAlloc() + */ +static void DBGdiAlloc(imap_t * imap, ino_t ino) +{ + u32 *dimap = imap->im_DBGdimap; + int w, b; + u32 m; + w = ino >> 5; + b = ino & 31; + m = 0x80000000 >> b; + assert(w < 64 * 256); + if (dimap[w] & m) { + printk("DEBUG diAlloc: duplicate alloc ino:0x%x\n", ino); + } + dimap[w] |= m; +} + +/* + * DBGdiFree() + */ +static void DBGdiFree(imap_t * imap, ino_t ino) +{ + u32 *dimap = imap->im_DBGdimap; + int w, b; + u32 m; + w = ino >> 5; + b = ino & 31; + m = 0x80000000 >> b; + assert(w < 64 * 256); + if ((dimap[w] & m) == 0) { + printk("DEBUG diFree: duplicate free ino:0x%x\n", ino); + } + dimap[w] &= ~m; +} + +static void dump_cp(imap_t * ipimap, char *function, int line) +{ + printk("\n* ********* *\nControl Page %s %d\n", function, line); + printk("FreeIAG %d\tNextIAG %d\n", ipimap->im_freeiag, + ipimap->im_nextiag); + printk("NumInos %d\tNumFree %d\n", + atomic_read(&ipimap->im_numinos), + atomic_read(&ipimap->im_numfree)); + printk("AG InoFree %d\tAG ExtFree %d\n", + ipimap->im_agctl[0].inofree, ipimap->im_agctl[0].extfree); + printk("AG NumInos %d\tAG NumFree %d\n", + ipimap->im_agctl[0].numinos, ipimap->im_agctl[0].numfree); +} + +static void dump_iag(iag_t * iag, char *function, int line) +{ + printk("\n* ********* *\nIAG %s %d\n", function, line); + printk("IagNum %d\tIAG Free %d\n", le32_to_cpu(iag->iagnum), + le32_to_cpu(iag->iagfree)); + printk("InoFreeFwd %d\tInoFreeBack %d\n", + le32_to_cpu(iag->inofreefwd), + le32_to_cpu(iag->inofreeback)); + printk("ExtFreeFwd %d\tExtFreeBack %d\n", + le32_to_cpu(iag->extfreefwd), + le32_to_cpu(iag->extfreeback)); + printk("NFreeInos %d\tNFreeExts %d\n", le32_to_cpu(iag->nfreeinos), + le32_to_cpu(iag->nfreeexts)); +} +#endif /* _JFS_DEBUG_IMAP */ diff -Nru a/fs/jfs/jfs_imap.h b/fs/jfs/jfs_imap.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_imap.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,161 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ +#ifndef _H_JFS_IMAP +#define _H_JFS_IMAP + +#include "jfs_txnmgr.h" + +/* + * jfs_imap.h: disk inode manager + */ + +#define EXTSPERIAG 128 /* number of disk inode extent per iag */ +#define IMAPBLKNO 0 /* lblkno of dinomap within inode map */ +#define SMAPSZ 4 /* number of words per summary map */ +#define EXTSPERSUM 32 /* number of extents per summary map entry */ +#define L2EXTSPERSUM 5 /* l2 number of extents per summary map */ +#define PGSPERIEXT 4 /* number of 4K pages per dinode extent */ +#define MAXIAGS ((1<<20)-1) /* maximum number of iags */ +#define MAXAG 128 /* maximum number of allocation groups */ + +#define AMAPSIZE 512 /* bytes in the IAG allocation maps */ +#define SMAPSIZE 16 /* bytes in the IAG summary maps */ + +/* convert inode number to iag number */ +#define INOTOIAG(ino) ((ino) >> L2INOSPERIAG) + +/* convert iag number to logical block number of the iag page */ +#define IAGTOLBLK(iagno,l2nbperpg) (((iagno) + 1) << (l2nbperpg)) + +/* get the starting block number of the 4K page of an inode extent + * that contains ino. + */ +#define INOPBLK(pxd,ino,l2nbperpg) (addressPXD((pxd)) + \ + ((((ino) & (INOSPEREXT-1)) >> L2INOSPERPAGE) << (l2nbperpg))) + +/* + * inode allocation map: + * + * inode allocation map consists of + * . the inode map control page and + * . inode allocation group pages (per 4096 inodes) + * which are addressed by standard JFS xtree. + */ +/* + * inode allocation group page (per 4096 inodes of an AG) + */ +typedef struct { + s64 agstart; /* 8: starting block of ag */ + s32 iagnum; /* 4: inode allocation group number */ + s32 inofreefwd; /* 4: ag inode free list forward */ + s32 inofreeback; /* 4: ag inode free list back */ + s32 extfreefwd; /* 4: ag inode extent free list forward */ + s32 extfreeback; /* 4: ag inode extent free list back */ + s32 iagfree; /* 4: iag free list */ + + /* summary map: 1 bit per inode extent */ + s32 inosmap[SMAPSZ]; /* 16: sum map of mapwords w/ free inodes; + * note: this indicates free and backed + * inodes, if the extent is not backed the + * value will be 1. if the extent is + * backed but all inodes are being used the + * value will be 1. if the extent is + * backed but at least one of the inodes is + * free the value will be 0. + */ + s32 extsmap[SMAPSZ]; /* 16: sum map of mapwords w/ free extents */ + s32 nfreeinos; /* 4: number of free inodes */ + s32 nfreeexts; /* 4: number of free extents */ + /* (72) */ + u8 pad[1976]; /* 1976: pad to 2048 bytes */ + /* allocation bit map: 1 bit per inode (0 - free, 1 - allocated) */ + u32 wmap[EXTSPERIAG]; /* 512: working allocation map */ + u32 pmap[EXTSPERIAG]; /* 512: persistent allocation map */ + pxd_t inoext[EXTSPERIAG]; /* 1024: inode extent addresses */ +} iag_t; /* (4096) */ + +/* + * per AG control information (in inode map control page) + */ +typedef struct { + s32 inofree; /* 4: free inode list anchor */ + s32 extfree; /* 4: free extent list anchor */ + s32 numinos; /* 4: number of backed inodes */ + s32 numfree; /* 4: number of free inodes */ +} iagctl_t; /* (16) */ + +/* + * per fileset/aggregate inode map control page + */ +typedef struct { + s32 in_freeiag; /* 4: free iag list anchor */ + s32 in_nextiag; /* 4: next free iag number */ + s32 in_numinos; /* 4: num of backed inodes */ + s32 in_numfree; /* 4: num of free backed inodes */ + s32 in_nbperiext; /* 4: num of blocks per inode extent */ + s32 in_l2nbperiext; /* 4: l2 of in_nbperiext */ + s32 in_diskblock; /* 4: for standalone test driver */ + s32 in_maxag; /* 4: for standalone test driver */ + u8 pad[2016]; /* 2016: pad to 2048 */ + iagctl_t in_agctl[MAXAG]; /* 2048: AG control information */ +} dinomap_t; /* (4096) */ + + +/* + * In-core inode map control page + */ +typedef struct inomap { + dinomap_t im_imap; /* 4096: inode allocation control */ + struct inode *im_ipimap; /* 4: ptr to inode for imap */ + struct semaphore im_freelock; /* 4: iag free list lock */ + struct semaphore im_aglock[MAXAG]; /* 512: per AG locks */ + u32 *im_DBGdimap; + atomic_t im_numinos; /* num of backed inodes */ + atomic_t im_numfree; /* num of free backed inodes */ +} imap_t; + +#define im_freeiag im_imap.in_freeiag +#define im_nextiag im_imap.in_nextiag +#define im_agctl im_imap.in_agctl +#define im_nbperiext im_imap.in_nbperiext +#define im_l2nbperiext im_imap.in_l2nbperiext + +/* for standalone testdriver + */ +#define im_diskblock im_imap.in_diskblock +#define im_maxag im_imap.in_maxag + +extern int diFree(struct inode *); +extern int diAlloc(struct inode *, boolean_t, struct inode *); +extern int diSync(struct inode *); +/* external references */ +extern int diUpdatePMap(struct inode *ipimap, unsigned long inum, + boolean_t is_free, tblock_t * tblk); +#ifdef _STILL_TO_PORT +extern int diExtendFS(inode_t * ipimap, inode_t * ipbmap); +#endif /* _STILL_TO_PORT */ + +extern int diMount(struct inode *); +extern int diUnmount(struct inode *, int); +extern int diRead(struct inode *); +extern void diClearExtension(struct inode *); +extern struct inode *diReadSpecial(struct super_block *, ino_t); +extern void diWriteSpecial(struct inode *); +extern void diFreeSpecial(struct inode *); +extern int diWrite(tid_t tid, struct inode *); +#endif /* _H_JFS_IMAP */ diff -Nru a/fs/jfs/jfs_incore.h b/fs/jfs/jfs_incore.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_incore.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,149 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * +*/ +#ifndef _H_JFS_INCORE +#define _H_JFS_INCORE + +#include +#include +#include "jfs_types.h" +#include "jfs_xtree.h" +#include "jfs_dtree.h" + +/* + * JFS magic number + */ +#define JFS_SUPER_MAGIC 0x3153464a /* "JFS1" */ + +/* + * Due to header ordering problems this can't be in jfs_lock.h + */ +typedef struct jfs_rwlock { + struct rw_semaphore rw_sem; + atomic_t in_use; /* for hacked implementation of trylock */ +} jfs_rwlock_t; + +/* + * JFS-private inode information + */ +struct jfs_inode_info { + int fileset; /* fileset number (always 16)*/ + uint mode2; /* jfs-specific mode */ + pxd_t ixpxd; /* inode extent descriptor */ + dxd_t acl; /* dxd describing acl */ + dxd_t ea; /* dxd describing ea */ + time_t otime; /* time created */ + uint next_index; /* next available directory entry index */ + int acltype; /* Type of ACL */ + short btorder; /* access order */ + short btindex; /* btpage entry index*/ + struct inode *ipimap; /* inode map */ + long cflag; /* commit flags */ + u16 bxflag; /* xflag of pseudo buffer? */ + unchar agno; /* ag number */ + unchar pad; /* pad */ + lid_t blid; /* lid of pseudo buffer? */ + lid_t atlhead; /* anonymous tlock list head */ + lid_t atltail; /* anonymous tlock list tail */ + struct list_head anon_inode_list; /* inodes having anonymous txns */ + struct list_head mp_list; /* metapages in inode's address space */ + jfs_rwlock_t rdwrlock; /* read/write lock */ + lid_t xtlid; /* lid of xtree lock on directory */ + union { + struct { + xtpage_t _xtroot; /* 288: xtree root */ + struct inomap *_imap; /* 4: inode map header */ + } file; + struct { + dir_table_slot_t _table[12]; /* 96: directory index */ + dtroot_t _dtroot; /* 288: dtree root */ + } dir; + struct { + unchar _unused[16]; /* 16: */ + dxd_t _dxd; /* 16: */ + unchar _inline[128]; /* 128: inline symlink */ + } link; + } u; + struct inode vfs_inode; +}; +#define i_xtroot u.file._xtroot +#define i_imap u.file._imap +#define i_dirtable u.dir._table +#define i_dtroot u.dir._dtroot +#define i_inline u.link._inline + +/* + * cflag + */ +enum cflags { + COMMIT_New, /* never committed inode */ + COMMIT_Nolink, /* inode committed with zero link count */ + COMMIT_Inlineea, /* commit inode inline EA */ + COMMIT_Freewmap, /* free WMAP at iClose() */ + COMMIT_Dirty, /* Inode is really dirty */ + COMMIT_Holdlock, /* Hold the IWRITE_LOCK until commit is done */ + COMMIT_Dirtable, /* commit changes to di_dirtable */ + COMMIT_Stale, /* data extent is no longer valid */ + COMMIT_Synclist, /* metadata pages on group commit synclist */ +}; + +#define set_cflag(flag, ip) set_bit(flag, &(JFS_IP(ip)->cflag)) +#define clear_cflag(flag, ip) clear_bit(flag, &(JFS_IP(ip)->cflag)) +#define test_cflag(flag, ip) test_bit(flag, &(JFS_IP(ip)->cflag)) +#define test_and_clear_cflag(flag, ip) \ + test_and_clear_bit(flag, &(JFS_IP(ip)->cflag)) +/* + * JFS-private superblock information. + */ +struct jfs_sb_info { + unsigned long mntflag; /* 4: aggregate attributes */ + struct inode *ipbmap; /* 4: block map inode */ + struct inode *ipaimap; /* 4: aggregate inode map inode */ + struct inode *ipaimap2; /* 4: secondary aimap inode */ + struct inode *ipimap; /* 4: aggregate inode map inode */ + struct jfs_log *log; /* 4: log */ + short bsize; /* 2: logical block size */ + short l2bsize; /* 2: log2 logical block size */ + short nbperpage; /* 2: blocks per page */ + short l2nbperpage; /* 2: log2 blocks per page */ + short l2niperblk; /* 2: log2 inodes per page */ + short reserved; /* 2: log2 inodes per page */ + pxd_t logpxd; /* 8: pxd describing log */ + pxd_t ait2; /* 8: pxd describing AIT copy */ + /* Formerly in ipimap */ + uint gengen; /* 4: inode generation generator*/ + uint inostamp; /* 4: shows inode belongs to fileset*/ + + /* Formerly in ipbmap */ + struct bmap *bmap; /* 4: incore bmap descriptor */ + struct nls_table *nls_tab; /* 4: current codepage */ + struct inode *direct_inode; /* 4: inode for physical I/O */ + struct address_space *direct_mapping; /* 4: mapping for physical I/O */ + uint state; /* 4: mount/recovery state */ +}; + +static inline struct jfs_inode_info *JFS_IP(struct inode *inode) +{ + return list_entry(inode, struct jfs_inode_info, vfs_inode); +} +#define JFS_SBI(sb) ((struct jfs_sb_info *)(sb)->u.generic_sbp) + +#define isReadOnly(ip) ((JFS_SBI((ip)->i_sb)->log) ? 0 : 1) + +#endif /* _H_JFS_INCORE */ diff -Nru a/fs/jfs/jfs_inode.c b/fs/jfs/jfs_inode.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_inode.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,132 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 "jfs_incore.h" +#include "jfs_filsys.h" +#include "jfs_imap.h" +#include "jfs_dinode.h" +#include "jfs_debug.h" + +/* + * NAME: ialloc() + * + * FUNCTION: Allocate a new inode + * + */ +struct inode *ialloc(struct inode *parent, umode_t mode) +{ + struct super_block *sb = parent->i_sb; + struct inode *inode; + struct jfs_inode_info *jfs_inode; + int rc; + + inode = new_inode(sb); + if (!inode) { + jERROR(1, ("ialloc: new_inode returned NULL!\n")); + return inode; + } + + jfs_inode = JFS_IP(inode); + + rc = diAlloc(parent, S_ISDIR(mode), inode); + if (rc) { + jERROR(1, ("ialloc: diAlloc returned %d!\n", rc)); + make_bad_inode(inode); + iput(inode); + return NULL; + } + + inode->i_uid = current->fsuid; + if (parent->i_mode & S_ISGID) { + inode->i_gid = parent->i_gid; + if (S_ISDIR(mode)) + mode |= S_ISGID; + } else + inode->i_gid = current->fsgid; + + inode->i_mode = mode; + if (S_ISDIR(mode)) + jfs_inode->mode2 = IDIRECTORY | mode; + else + jfs_inode->mode2 = INLINEEA | ISPARSE | mode; + inode->i_blksize = sb->s_blocksize; + inode->i_blocks = 0; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + jfs_inode->otime = inode->i_ctime; + inode->i_version = ++event; + inode->i_generation = JFS_SBI(sb)->gengen++; + + jfs_inode->cflag = 0; + set_cflag(COMMIT_New, inode); + + /* Zero remaining fields */ + memset(&jfs_inode->acl, 0, sizeof(dxd_t)); + memset(&jfs_inode->ea, 0, sizeof(dxd_t)); + jfs_inode->next_index = 0; + jfs_inode->acltype = 0; + jfs_inode->btorder = 0; + jfs_inode->btindex = 0; + jfs_inode->bxflag = 0; + jfs_inode->blid = 0; + jfs_inode->atlhead = 0; + jfs_inode->atltail = 0; + jfs_inode->xtlid = 0; + + jFYI(1, ("ialloc returns inode = 0x%p\n", inode)); + + return inode; +} + +/* + * NAME: iwritelocklist() + * + * FUNCTION: Lock multiple inodes in sorted order to avoid deadlock + * + */ +void iwritelocklist(int n, ...) +{ + va_list ilist; + struct inode *sort[4]; + struct inode *ip; + int k, m; + + va_start(ilist, n); + for (k = 0; k < n; k++) + sort[k] = va_arg(ilist, struct inode *); + va_end(ilist); + + /* Bubble sort in descending order */ + do { + m = 0; + for (k = 0; k < n; k++) + if ((k + 1) < n + && sort[k + 1]->i_ino > sort[k]->i_ino) { + ip = sort[k]; + sort[k] = sort[k + 1]; + sort[k + 1] = ip; + m++; + } + } while (m); + + /* Lock them */ + for (k = 0; k < n; k++) { + IWRITE_LOCK(sort[k]); + } +} diff -Nru a/fs/jfs/jfs_inode.h b/fs/jfs/jfs_inode.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_inode.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,23 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ +#ifndef _H_JFS_INODE +#define _H_JFS_INODE + +extern struct inode *ialloc(struct inode *, umode_t); + +#endif /* _H_JFS_INODE */ diff -Nru a/fs/jfs/jfs_lock.h b/fs/jfs/jfs_lock.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_lock.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,106 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ +#ifndef _H_JFS_LOCK +#define _H_JFS_LOCK + +#include +#include + +/* + * jfs_lock.h + * + * JFS lock definition for globally referenced locks + */ + +/* readers/writer lock: thread-thread */ + +/* + * RW semaphores do not currently have a trylock function. Since the + * implementation varies by platform, I have implemented a platform-independent + * wrapper around the rw_semaphore routines. If this turns out to be the best + * way of avoiding our locking problems, I will push to get a trylock + * implemented in the kernel, but I'd rather find a way to avoid having to + * use it. + */ +#define RDWRLOCK_T jfs_rwlock_t +static inline void RDWRLOCK_INIT(jfs_rwlock_t * Lock) +{ + init_rwsem(&Lock->rw_sem); + atomic_set(&Lock->in_use, 0); +} +static inline void READ_LOCK(jfs_rwlock_t * Lock) +{ + atomic_inc(&Lock->in_use); + down_read(&Lock->rw_sem); +} +static inline void READ_UNLOCK(jfs_rwlock_t * Lock) +{ + up_read(&Lock->rw_sem); + atomic_dec(&Lock->in_use); +} +static inline void WRITE_LOCK(jfs_rwlock_t * Lock) +{ + atomic_inc(&Lock->in_use); + down_write(&Lock->rw_sem); +} + +static inline int WRITE_TRYLOCK(jfs_rwlock_t * Lock) +{ + if (atomic_read(&Lock->in_use)) + return 0; + WRITE_LOCK(Lock); + return 1; +} +static inline void WRITE_UNLOCK(jfs_rwlock_t * Lock) +{ + up_write(&Lock->rw_sem); + atomic_dec(&Lock->in_use); +} + +#define IREAD_LOCK(ip) READ_LOCK(&JFS_IP(ip)->rdwrlock) +#define IREAD_UNLOCK(ip) READ_UNLOCK(&JFS_IP(ip)->rdwrlock) +#define IWRITE_LOCK(ip) WRITE_LOCK(&JFS_IP(ip)->rdwrlock) +#define IWRITE_TRYLOCK(ip) WRITE_TRYLOCK(&JFS_IP(ip)->rdwrlock) +#define IWRITE_UNLOCK(ip) WRITE_UNLOCK(&JFS_IP(ip)->rdwrlock) +#define IWRITE_LOCK_LIST iwritelocklist + +extern void iwritelocklist(int, ...); + +/* + * Conditional sleep where condition is protected by spinlock + * + * lock_cmd and unlock_cmd take and release the spinlock + */ +#define __SLEEP_COND(wq, cond, lock_cmd, unlock_cmd) \ +do { \ + DECLARE_WAITQUEUE(__wait, current); \ + \ + add_wait_queue(&wq, &__wait); \ + for (;;) { \ + set_current_state(TASK_UNINTERRUPTIBLE);\ + if (cond) \ + break; \ + unlock_cmd; \ + schedule(); \ + lock_cmd; \ + } \ + current->state = TASK_RUNNING; \ + remove_wait_queue(&wq, &__wait); \ +} while (0) + +#endif /* _H_JFS_LOCK */ diff -Nru a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_logmgr.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,2490 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * +*/ + +/* + * jfs_logmgr.c: log manager + * + * for related information, see transaction manager (jfs_txnmgr.c), and + * recovery manager (jfs_logredo.c). + * + * note: for detail, RTFS. + * + * log buffer manager: + * special purpose buffer manager supporting log i/o requirements. + * per log serial pageout of logpage + * queuing i/o requests and redrive i/o at iodone + * maintain current logpage buffer + * no caching since append only + * appropriate jfs buffer cache buffers as needed + * + * group commit: + * transactions which wrote COMMIT records in the same in-memory + * log page during the pageout of previous/current log page(s) are + * committed together by the pageout of the page. + * + * TBD lazy commit: + * transactions are committed asynchronously when the log page + * containing it COMMIT is paged out when it becomes full; + * + * serialization: + * . a per log lock serialize log write. + * . a per log lock serialize group commit. + * . a per log lock serialize log open/close; + * + * TBD log integrity: + * careful-write (ping-pong) of last logpage to recover from crash + * in overwrite. + * detection of split (out-of-order) write of physical sectors + * of last logpage via timestamp at end of each sector + * with its mirror data array at trailer). + * + * alternatives: + * lsn - 64-bit monotonically increasing integer vs + * 32-bit lspn and page eor. + */ + +#include +#include +#include +#include +#include +#include +#include "jfs_incore.h" +#include "jfs_filsys.h" +#include "jfs_metapage.h" +#include "jfs_txnmgr.h" +#include "jfs_debug.h" + + +/* + * lbuf's ready to be redriven. Protected by log_redrive_lock (jfsIOtask) + */ +static lbuf_t *log_redrive_list; +static spinlock_t log_redrive_lock = SPIN_LOCK_UNLOCKED; + + +/* + * log read/write serialization (per log) + */ +#define LOG_LOCK_INIT(log) init_MUTEX(&(log)->loglock) +#define LOG_LOCK(log) down(&((log)->loglock)) +#define LOG_UNLOCK(log) up(&((log)->loglock)) + + +/* + * log group commit serialization (per log) + */ + +#define LOGGC_LOCK_INIT(log) spin_lock_init(&(log)->gclock) +#define LOGGC_LOCK(log) spin_lock_irq(&(log)->gclock) +#define LOGGC_UNLOCK(log) spin_unlock_irq(&(log)->gclock) +#define LOGGC_WAKEUP(tblk) wake_up(&(tblk)->gcwait) + +/* + * log sync serialization (per log) + */ +#define LOGSYNC_DELTA(logsize) min((logsize)/8, 128*LOGPSIZE) +#define LOGSYNC_BARRIER(logsize) ((logsize)/4) +/* +#define LOGSYNC_DELTA(logsize) min((logsize)/4, 256*LOGPSIZE) +#define LOGSYNC_BARRIER(logsize) ((logsize)/2) +*/ + + +/* + * log buffer cache synchronization + */ +static spinlock_t jfsLCacheLock = SPIN_LOCK_UNLOCKED; + +#define LCACHE_LOCK(flags) spin_lock_irqsave(&jfsLCacheLock, flags) +#define LCACHE_UNLOCK(flags) spin_unlock_irqrestore(&jfsLCacheLock, flags) + +/* + * See __SLEEP_COND in jfs_locks.h + */ +#define LCACHE_SLEEP_COND(wq, cond, flags) \ +do { \ + if (cond) \ + break; \ + __SLEEP_COND(wq, cond, LCACHE_LOCK(flags), LCACHE_UNLOCK(flags)); \ +} while (0) + +#define LCACHE_WAKEUP(event) wake_up(event) + + +/* + * lbuf buffer cache (lCache) control + */ +/* log buffer manager pageout control (cumulative, inclusive) */ +#define lbmREAD 0x0001 +#define lbmWRITE 0x0002 /* enqueue at tail of write queue; + * init pageout if at head of queue; + */ +#define lbmRELEASE 0x0004 /* remove from write queue + * at completion of pageout; + * do not free/recycle it yet: + * caller will free it; + */ +#define lbmSYNC 0x0008 /* do not return to freelist + * when removed from write queue; + */ +#define lbmFREE 0x0010 /* return to freelist + * at completion of pageout; + * the buffer may be recycled; + */ +#define lbmDONE 0x0020 +#define lbmERROR 0x0040 +#define lbmGC 0x0080 /* lbmIODone to perform post-GC processing + * of log page + */ +#define lbmDIRECT 0x0100 + +/* + * external references + */ +extern void vPut(struct inode *ip); +extern void txLazyUnlock(tblock_t * tblk); +extern int jfs_thread_stopped(void); +extern struct task_struct *jfsIOtask; +extern struct completion jfsIOwait; + +/* + * forward references + */ +static int lmWriteRecord(log_t * log, tblock_t * tblk, lrd_t * lrd, + tlock_t * tlck); + +static int lmNextPage(log_t * log); +static int lmLogInit(log_t * log); +static int lmLogShutdown(log_t * log); + +static int lbmLogInit(log_t * log); +static void lbmLogShutdown(log_t * log); +static lbuf_t *lbmAllocate(log_t * log, int); +static void lbmFree(lbuf_t * bp); +static void lbmfree(lbuf_t * bp); +static int lbmRead(log_t * log, int pn, lbuf_t ** bpp); +static void lbmWrite(log_t * log, lbuf_t * bp, int flag, int cant_block); +static void lbmDirectWrite(log_t * log, lbuf_t * bp, int flag); +static int lbmIOWait(lbuf_t * bp, int flag); +static bio_end_io_t lbmIODone; +#ifdef _STILL_TO_PORT +static void lbmDirectIODone(iobuf_t * ddbp); +#endif /* _STILL_TO_PORT */ +void lbmStartIO(lbuf_t * bp); +void lmGCwrite(log_t * log, int cant_block); + + +/* + * statistics + */ +#ifdef CONFIG_JFS_STATISTICS +struct lmStat { + uint commit; /* # of commit */ + uint pagedone; /* # of page written */ + uint submitted; /* # of pages submitted */ +} lmStat; +#endif + + +/* + * NAME: lmLog() + * + * FUNCTION: write a log record; + * + * PARAMETER: + * + * RETURN: lsn - offset to the next log record to write (end-of-log); + * -1 - error; + * + * note: todo: log error handler + */ +int lmLog(log_t * log, tblock_t * tblk, lrd_t * lrd, tlock_t * tlck) +{ + int lsn; + int diffp, difft; + metapage_t *mp = NULL; + + jFYI(1, ("lmLog: log:0x%p tblk:0x%p, lrd:0x%p tlck:0x%p\n", + log, tblk, lrd, tlck)); + + LOG_LOCK(log); + + /* log by (out-of-transaction) JFS ? */ + if (tblk == NULL) + goto writeRecord; + + /* log from page ? */ + if (tlck == NULL || + tlck->type & tlckBTROOT || (mp = tlck->mp) == NULL) + goto writeRecord; + + /* + * initialize/update page/transaction recovery lsn + */ + lsn = log->lsn; + + LOGSYNC_LOCK(log); + + /* + * initialize page lsn if first log write of the page + */ + if (mp->lsn == 0) { + mp->log = log; + mp->lsn = lsn; + log->count++; + + /* insert page at tail of logsynclist */ + list_add_tail(&mp->synclist, &log->synclist); + } + + /* + * initialize/update lsn of tblock of the page + * + * transaction inherits oldest lsn of pages associated + * with allocation/deallocation of resources (their + * log records are used to reconstruct allocation map + * at recovery time: inode for inode allocation map, + * B+-tree index of extent descriptors for block + * allocation map); + * allocation map pages inherit transaction lsn at + * commit time to allow forwarding log syncpt past log + * records associated with allocation/deallocation of + * resources only after persistent map of these map pages + * have been updated and propagated to home. + */ + /* + * initialize transaction lsn: + */ + if (tblk->lsn == 0) { + /* inherit lsn of its first page logged */ + tblk->lsn = mp->lsn; + log->count++; + + /* insert tblock after the page on logsynclist */ + list_add(&tblk->synclist, &mp->synclist); + } + /* + * update transaction lsn: + */ + else { + /* inherit oldest/smallest lsn of page */ + logdiff(diffp, mp->lsn, log); + logdiff(difft, tblk->lsn, log); + if (diffp < difft) { + /* update tblock lsn with page lsn */ + tblk->lsn = mp->lsn; + + /* move tblock after page on logsynclist */ + list_del(&tblk->synclist); + list_add(&tblk->synclist, &mp->synclist); + } + } + + LOGSYNC_UNLOCK(log); + + /* + * write the log record + */ + writeRecord: + lsn = lmWriteRecord(log, tblk, lrd, tlck); + + /* + * forward log syncpt if log reached next syncpt trigger + */ + logdiff(diffp, lsn, log); + if (diffp >= log->nextsync) + lsn = lmLogSync(log, 0); + + /* update end-of-log lsn */ + log->lsn = lsn; + + LOG_UNLOCK(log); + + /* return end-of-log address */ + return lsn; +} + + +/* + * NAME: lmWriteRecord() + * + * FUNCTION: move the log record to current log page + * + * PARAMETER: cd - commit descriptor + * + * RETURN: end-of-log address + * + * serialization: LOG_LOCK() held on entry/exit + */ +static int +lmWriteRecord(log_t * log, tblock_t * tblk, lrd_t * lrd, tlock_t * tlck) +{ + int lsn = 0; /* end-of-log address */ + lbuf_t *bp; /* dst log page buffer */ + logpage_t *lp; /* dst log page */ + caddr_t dst; /* destination address in log page */ + int dstoffset; /* end-of-log offset in log page */ + int freespace; /* free space in log page */ + caddr_t p; /* src meta-data page */ + caddr_t src; + int srclen; + int nbytes; /* number of bytes to move */ + int i; + int len; + linelock_t *linelock; + lv_t *lv; + lvd_t *lvd; + int l2linesize; + + len = 0; + + /* retrieve destination log page to write */ + bp = (lbuf_t *) log->bp; + lp = (logpage_t *) bp->l_ldata; + dstoffset = log->eor; + + /* any log data to write ? */ + if (tlck == NULL) + goto moveLrd; + + /* + * move log record data + */ + /* retrieve source meta-data page to log */ + if (tlck->flag & tlckPAGELOCK) { + p = (caddr_t) (tlck->mp->data); + linelock = (linelock_t *) & tlck->lock; + } + /* retrieve source in-memory inode to log */ + else if (tlck->flag & tlckINODELOCK) { + if (tlck->type & tlckDTREE) + p = (caddr_t) &JFS_IP(tlck->ip)->i_dtroot; + else + p = (caddr_t) &JFS_IP(tlck->ip)->i_xtroot; + linelock = (linelock_t *) & tlck->lock; + } +#ifdef _JFS_WIP + else if (tlck->flag & tlckINLINELOCK) { + + inlinelock = (inlinelock_t *) & tlck; + p = (caddr_t) & inlinelock->pxd; + linelock = (linelock_t *) & tlck; + } +#endif /* _JFS_WIP */ + else { + jERROR(2, ("lmWriteRecord: UFO tlck:0x%p\n", tlck)); + return 0; /* Probably should trap */ + } + l2linesize = linelock->l2linesize; + + moveData: + ASSERT(linelock->index <= linelock->maxcnt); + + lv = (lv_t *) & linelock->lv; + for (i = 0; i < linelock->index; i++, lv++) { + if (lv->length == 0) + continue; + + /* is page full ? */ + if (dstoffset >= LOGPSIZE - LOGPTLRSIZE) { + /* page become full: move on to next page */ + lmNextPage(log); + + bp = log->bp; + lp = (logpage_t *) bp->l_ldata; + dstoffset = LOGPHDRSIZE; + } + + /* + * move log vector data + */ + src = (u8 *) p + (lv->offset << l2linesize); + srclen = lv->length << l2linesize; + len += srclen; + while (srclen > 0) { + freespace = (LOGPSIZE - LOGPTLRSIZE) - dstoffset; + nbytes = min(freespace, srclen); + dst = (caddr_t) lp + dstoffset; + memcpy(dst, src, nbytes); + dstoffset += nbytes; + + /* is page not full ? */ + if (dstoffset < LOGPSIZE - LOGPTLRSIZE) + break; + + /* page become full: move on to next page */ + lmNextPage(log); + + bp = (lbuf_t *) log->bp; + lp = (logpage_t *) bp->l_ldata; + dstoffset = LOGPHDRSIZE; + + srclen -= nbytes; + src += nbytes; + } + + /* + * move log vector descriptor + */ + len += 4; + lvd = (lvd_t *) ((caddr_t) lp + dstoffset); + lvd->offset = cpu_to_le16(lv->offset); + lvd->length = cpu_to_le16(lv->length); + dstoffset += 4; + jFYI(1, + ("lmWriteRecord: lv offset:%d length:%d\n", + lv->offset, lv->length)); + } + + if ((i = linelock->next)) { + linelock = (linelock_t *) lid_to_tlock(i); + goto moveData; + } + + /* + * move log record descriptor + */ + moveLrd: + lrd->length = cpu_to_le16(len); + + src = (caddr_t) lrd; + srclen = LOGRDSIZE; + + while (srclen > 0) { + freespace = (LOGPSIZE - LOGPTLRSIZE) - dstoffset; + nbytes = min(freespace, srclen); + dst = (caddr_t) lp + dstoffset; + memcpy(dst, src, nbytes); + + dstoffset += nbytes; + srclen -= nbytes; + + /* are there more to move than freespace of page ? */ + if (srclen) + goto pageFull; + + /* + * end of log record descriptor + */ + + /* update last log record eor */ + log->eor = dstoffset; + bp->l_eor = dstoffset; + lsn = (log->page << L2LOGPSIZE) + dstoffset; + + if (lrd->type & cpu_to_le16(LOG_COMMIT)) { + tblk->clsn = lsn; + jFYI(1, + ("wr: tclsn:0x%x, beor:0x%x\n", tblk->clsn, + bp->l_eor)); + + INCREMENT(lmStat.commit); /* # of commit */ + + /* + * enqueue tblock for group commit: + * + * enqueue tblock of non-trivial/synchronous COMMIT + * at tail of group commit queue + * (trivial/asynchronous COMMITs are ignored by + * group commit.) + */ + LOGGC_LOCK(log); + + /* init tblock gc state */ + tblk->flag = tblkGC_QUEUE; + tblk->bp = log->bp; + tblk->pn = log->page; + tblk->eor = log->eor; + init_waitqueue_head(&tblk->gcwait); + + /* enqueue transaction to commit queue */ + tblk->cqnext = NULL; + if (log->cqueue.head) { + log->cqueue.tail->cqnext = tblk; + log->cqueue.tail = tblk; + } else + log->cqueue.head = log->cqueue.tail = tblk; + + LOGGC_UNLOCK(log); + } + + jFYI(1, + ("lmWriteRecord: lrd:0x%04x bp:0x%p pn:%d eor:0x%x\n", + le16_to_cpu(lrd->type), log->bp, log->page, + dstoffset)); + + /* page not full ? */ + if (dstoffset < LOGPSIZE - LOGPTLRSIZE) + return lsn; + + pageFull: + /* page become full: move on to next page */ + lmNextPage(log); + + bp = (lbuf_t *) log->bp; + lp = (logpage_t *) bp->l_ldata; + dstoffset = LOGPHDRSIZE; + src += nbytes; + } + + return lsn; +} + + +/* + * NAME: lmNextPage() + * + * FUNCTION: write current page and allocate next page. + * + * PARAMETER: log + * + * RETURN: 0 + * + * serialization: LOG_LOCK() held on entry/exit + */ +static int lmNextPage(log_t * log) +{ + logpage_t *lp; + int lspn; /* log sequence page number */ + int pn; /* current page number */ + lbuf_t *bp; + lbuf_t *nextbp; + tblock_t *tblk; + + jFYI(1, ("lmNextPage\n")); + + /* get current log page number and log sequence page number */ + pn = log->page; + bp = log->bp; + lp = (logpage_t *) bp->l_ldata; + lspn = le32_to_cpu(lp->h.page); + + LOGGC_LOCK(log); + + /* + * write or queue the full page at the tail of write queue + */ + /* get the tail tblk on commit queue */ + tblk = log->cqueue.tail; + + /* every tblk who has COMMIT record on the current page, + * and has not been committed, must be on commit queue + * since tblk is queued at commit queueu at the time + * of writing its COMMIT record on the page before + * page becomes full (even though the tblk thread + * who wrote COMMIT record may have been suspended + * currently); + */ + + /* is page bound with outstanding tail tblk ? */ + if (tblk && tblk->pn == pn) { + /* mark tblk for end-of-page */ + tblk->flag |= tblkGC_EOP; + + /* if page is not already on write queue, + * just enqueue (no lbmWRITE to prevent redrive) + * buffer to wqueue to ensure correct serial order + * of the pages since log pages will be added + * continuously (tblk bound with the page hasn't + * got around to init write of the page, either + * preempted or the page got filled by its COMMIT + * record); + * pages with COMMIT are paged out explicitly by + * tblk in lmGroupCommit(); + */ + if (bp->l_wqnext == NULL) { + /* bp->l_ceor = bp->l_eor; */ + /* lp->h.eor = lp->t.eor = bp->l_ceor; */ + lbmWrite(log, bp, 0, 0); + } + } + /* page is not bound with outstanding tblk: + * init write or mark it to be redriven (lbmWRITE) + */ + else { + /* finalize the page */ + bp->l_ceor = bp->l_eor; + lp->h.eor = lp->t.eor = cpu_to_le16(bp->l_ceor); + lbmWrite(log, bp, lbmWRITE | lbmRELEASE | lbmFREE, 0); + } + LOGGC_UNLOCK(log); + + /* + * allocate/initialize next page + */ + /* if log wraps, the first data page of log is 2 + * (0 never used, 1 is superblock). + */ + log->page = (pn == log->size - 1) ? 2 : pn + 1; + log->eor = LOGPHDRSIZE; /* ? valid page empty/full at logRedo() */ + + /* allocate/initialize next log page buffer */ + nextbp = lbmAllocate(log, log->page); + nextbp->l_eor = log->eor; + log->bp = nextbp; + + /* initialize next log page */ + lp = (logpage_t *) nextbp->l_ldata; + lp->h.page = lp->t.page = cpu_to_le32(lspn + 1); + lp->h.eor = lp->t.eor = cpu_to_le16(LOGPHDRSIZE); + + jFYI(1, ("lmNextPage done\n")); + return 0; +} + + +/* + * NAME: lmGroupCommit() + * + * FUNCTION: group commit + * initiate pageout of the pages with COMMIT in the order of + * page number - redrive pageout of the page at the head of + * pageout queue until full page has been written. + * + * RETURN: + * + * NOTE: + * LOGGC_LOCK serializes log group commit queue, and + * transaction blocks on the commit queue. + * N.B. LOG_LOCK is NOT held during lmGroupCommit(). + */ +int lmGroupCommit(log_t * log, tblock_t * tblk) +{ + int rc = 0; + + LOGGC_LOCK(log); + + /* group committed already ? */ + if (tblk->flag & tblkGC_COMMITTED) { + if (tblk->flag & tblkGC_ERROR) + rc = EIO; + + LOGGC_UNLOCK(log); + return rc; + } + jFYI(1, + ("lmGroup Commit: tblk = 0x%p, gcrtc = %d\n", tblk, + log->gcrtc)); + + /* + * group commit pageout in progress + */ + if ((!(log->cflag & logGC_PAGEOUT)) && log->cqueue.head) { + /* + * only transaction in the commit queue: + * + * start one-transaction group commit as + * its group leader. + */ + log->cflag |= logGC_PAGEOUT; + + lmGCwrite(log, 0); + } + /* lmGCwrite gives up LOGGC_LOCK, check again */ + + if (tblk->flag & tblkGC_COMMITTED) { + if (tblk->flag & tblkGC_ERROR) + rc = EIO; + + LOGGC_UNLOCK(log); + return rc; + } + + /* upcount transaction waiting for completion + */ + log->gcrtc++; + + if (tblk->xflag & COMMIT_LAZY) { + tblk->flag |= tblkGC_LAZY; + LOGGC_UNLOCK(log); + return 0; + } + tblk->flag |= tblkGC_READY; + + __SLEEP_COND(tblk->gcwait, (tblk->flag & tblkGC_COMMITTED), + LOGGC_LOCK(log), LOGGC_UNLOCK(log)); + + /* removed from commit queue */ + if (tblk->flag & tblkGC_ERROR) + rc = EIO; + + LOGGC_UNLOCK(log); + return rc; +} + +/* + * NAME: lmGCwrite() + * + * FUNCTION: group commit write + * initiate write of log page, building a group of all transactions + * with commit records on that page. + * + * RETURN: None + * + * NOTE: + * LOGGC_LOCK must be held by caller. + * N.B. LOG_LOCK is NOT held during lmGroupCommit(). + */ +void lmGCwrite(log_t * log, int cant_write) +{ + lbuf_t *bp; + logpage_t *lp; + int gcpn; /* group commit page number */ + tblock_t *tblk; + tblock_t *xtblk; + + /* + * build the commit group of a log page + * + * scan commit queue and make a commit group of all + * transactions with COMMIT records on the same log page. + */ + /* get the head tblk on the commit queue */ + tblk = xtblk = log->cqueue.head; + gcpn = tblk->pn; + + while (tblk && tblk->pn == gcpn) { + xtblk = tblk; + + /* state transition: (QUEUE, READY) -> COMMIT */ + tblk->flag |= tblkGC_COMMIT; + tblk = tblk->cqnext; + } + tblk = xtblk; /* last tblk of the page */ + + /* + * pageout to commit transactions on the log page. + */ + bp = (lbuf_t *) tblk->bp; + lp = (logpage_t *) bp->l_ldata; + /* is page already full ? */ + if (tblk->flag & tblkGC_EOP) { + /* mark page to free at end of group commit of the page */ + tblk->flag &= ~tblkGC_EOP; + tblk->flag |= tblkGC_FREE; + bp->l_ceor = bp->l_eor; + lp->h.eor = lp->t.eor = cpu_to_le16(bp->l_ceor); + jEVENT(0, + ("gc: tclsn:0x%x, bceor:0x%x\n", tblk->clsn, + bp->l_ceor)); + lbmWrite(log, bp, lbmWRITE | lbmRELEASE | lbmGC, + cant_write); + } + /* page is not yet full */ + else { + bp->l_ceor = tblk->eor; /* ? bp->l_ceor = bp->l_eor; */ + lp->h.eor = lp->t.eor = cpu_to_le16(bp->l_ceor); + jEVENT(0, + ("gc: tclsn:0x%x, bceor:0x%x\n", tblk->clsn, + bp->l_ceor)); + lbmWrite(log, bp, lbmWRITE | lbmGC, cant_write); + } +} + +/* + * NAME: lmPostGC() + * + * FUNCTION: group commit post-processing + * Processes transactions after their commit records have been written + * to disk, redriving log I/O if necessary. + * + * RETURN: None + * + * NOTE: + * This routine is called a interrupt time by lbmIODone + */ +void lmPostGC(lbuf_t * bp) +{ + unsigned long flags; + log_t *log = bp->l_log; + logpage_t *lp; + tblock_t *tblk; + + //LOGGC_LOCK(log); + spin_lock_irqsave(&log->gclock, flags); + /* + * current pageout of group commit completed. + * + * remove/wakeup transactions from commit queue who were + * group committed with the current log page + */ + while ((tblk = log->cqueue.head) && (tblk->flag & tblkGC_COMMIT)) { + /* if transaction was marked GC_COMMIT then + * it has been shipped in the current pageout + * and made it to disk - it is committed. + */ + + if (bp->l_flag & lbmERROR) + tblk->flag |= tblkGC_ERROR; + + /* remove it from the commit queue */ + log->cqueue.head = tblk->cqnext; + if (log->cqueue.head == NULL) + log->cqueue.tail = NULL; + tblk->flag &= ~tblkGC_QUEUE; + tblk->cqnext = 0; + + jEVENT(0, + ("lmPostGC: tblk = 0x%p, flag = 0x%x\n", tblk, + tblk->flag)); + + if (!(tblk->xflag & COMMIT_FORCE)) + /* + * Hand tblk over to lazy commit thread + */ + txLazyUnlock(tblk); + else { + /* state transition: COMMIT -> COMMITTED */ + tblk->flag |= tblkGC_COMMITTED; + + if (tblk->flag & tblkGC_READY) { + log->gcrtc--; + LOGGC_WAKEUP(tblk); + } + } + + /* was page full before pageout ? + * (and this is the last tblk bound with the page) + */ + if (tblk->flag & tblkGC_FREE) + lbmFree(bp); + /* did page become full after pageout ? + * (and this is the last tblk bound with the page) + */ + else if (tblk->flag & tblkGC_EOP) { + /* finalize the page */ + lp = (logpage_t *) bp->l_ldata; + bp->l_ceor = bp->l_eor; + lp->h.eor = lp->t.eor = cpu_to_le16(bp->l_eor); + jEVENT(0, ("lmPostGC: calling lbmWrite\n")); + lbmWrite(log, bp, lbmWRITE | lbmRELEASE | lbmFREE, + 1); + } + + } + + /* are there any transactions who have entered lnGroupCommit() + * (whose COMMITs are after that of the last log page written. + * They are waiting for new group commit (above at (SLEEP 1)): + * select the latest ready transaction as new group leader and + * wake her up to lead her group. + */ + if ((log->gcrtc > 0) && log->cqueue.head) + /* + * Call lmGCwrite with new group leader + */ + lmGCwrite(log, 1); + + /* no transaction are ready yet (transactions are only just + * queued (GC_QUEUE) and not entered for group commit yet). + * let the first transaction entering group commit + * will elect hetself as new group leader. + */ + else + log->cflag &= ~logGC_PAGEOUT; + + //LOGGC_UNLOCK(log); + spin_unlock_irqrestore(&log->gclock, flags); + return; +} + +/* + * NAME: lmLogSync() + * + * FUNCTION: write log SYNCPT record for specified log + * if new sync address is available + * (normally the case if sync() is executed by back-ground + * process). + * if not, explicitly run jfs_blogsync() to initiate + * getting of new sync address. + * calculate new value of i_nextsync which determines when + * this code is called again. + * + * this is called only from lmLog(). + * + * PARAMETER: ip - pointer to logs inode. + * + * RETURN: 0 + * + * serialization: LOG_LOCK() held on entry/exit + */ +int lmLogSync(log_t * log, int nosyncwait) +{ + int logsize; + int written; /* written since last syncpt */ + int free; /* free space left available */ + int delta; /* additional delta to write normally */ + int more; /* additional write granted */ + lrd_t lrd; + int lsn; + struct logsyncblk *lp; + + /* + * forward syncpt + */ + /* if last sync is same as last syncpt, + * invoke sync point forward processing to update sync. + */ + + if (log->sync == log->syncpt) { + LOGSYNC_LOCK(log); + /* ToDo: push dirty metapages out to disk */ +// bmLogSync(log); + + if (list_empty(&log->synclist)) + log->sync = log->lsn; + else { + lp = list_entry(log->synclist.next, + struct logsyncblk, synclist); + log->sync = lp->lsn; + } + LOGSYNC_UNLOCK(log); + + } + + /* if sync is different from last syncpt, + * write a SYNCPT record with syncpt = sync. + * reset syncpt = sync + */ + if (log->sync != log->syncpt) { + struct jfs_sb_info *sbi = JFS_SBI(log->sb); + /* + * We need to make sure all of the "written" metapages + * actually make it to disk + */ + fsync_inode_data_buffers(sbi->ipbmap); + fsync_inode_data_buffers(sbi->ipimap); + fsync_inode_data_buffers(sbi->direct_inode); + + lrd.logtid = 0; + lrd.backchain = 0; + lrd.type = cpu_to_le16(LOG_SYNCPT); + lrd.length = 0; + lrd.log.syncpt.sync = cpu_to_le32(log->sync); + lsn = lmWriteRecord(log, NULL, &lrd, NULL); + + log->syncpt = log->sync; + } else + lsn = log->lsn; + + /* + * setup next syncpt trigger (SWAG) + */ + logsize = log->logsize; + + logdiff(written, lsn, log); + free = logsize - written; + delta = LOGSYNC_DELTA(logsize); + more = min(free / 2, delta); + if (more < 2 * LOGPSIZE) { + jEVENT(1, + ("\n ... Log Wrap ... Log Wrap ... Log Wrap ...\n\n")); + /* + * log wrapping + * + * option 1 - panic ? No.! + * option 2 - shutdown file systems + * associated with log ? + * option 3 - extend log ? + */ + /* + * option 4 - second chance + * + * mark log wrapped, and continue. + * when all active transactions are completed, + * mark log vaild for recovery. + * if crashed during invalid state, log state + * implies invald log, forcing fsck(). + */ + /* mark log state log wrap in log superblock */ + /* log->state = LOGWRAP; */ + + /* reset sync point computation */ + log->syncpt = log->sync = lsn; + log->nextsync = delta; + } else + /* next syncpt trigger = written + more */ + log->nextsync = written + more; + + /* return if lmLogSync() from outside of transaction, e.g., sync() */ + if (nosyncwait) + return lsn; + + /* if number of bytes written from last sync point is more + * than 1/4 of the log size, stop new transactions from + * starting until all current transactions are completed + * by setting syncbarrier flag. + */ + if (written > LOGSYNC_BARRIER(logsize) && logsize > 32 * LOGPSIZE) { + log->syncbarrier = 1; + jFYI(1, ("log barrier on: lsn=0x%x syncpt=0x%x\n", lsn, + log->syncpt)); + } + + return lsn; +} + + +/* + * NAME: lmLogOpen() + * + * FUNCTION: open the log on first open; + * insert filesystem in the active list of the log. + * + * PARAMETER: ipmnt - file system mount inode + * iplog - log inode (out) + * + * RETURN: + * + * serialization: + */ +int lmLogOpen(struct super_block *sb, log_t ** logptr) +{ + int rc; + kdev_t logdev; /* dev_t of log device */ + log_t *log; + + logdev = sb->s_dev; + +#ifdef _STILL_TO_PORT + /* + * open the inode representing the log device (aka log inode) + */ + if (logdev != fsdev) + goto externalLog; +#endif /* _STILL_TO_PORT */ + + /* + * in-line log in host file system + * + * file system to log have 1-to-1 relationship; + */ +// inlineLog: + + *logptr = log = kmalloc(sizeof(log_t), GFP_KERNEL); + if (log == 0) + return ENOMEM; + + memset(log, 0, sizeof(log_t)); + log->sb = sb; /* This should be a list */ + log->flag = JFS_INLINELOG; + log->dev = logdev; + log->base = addressPXD(&JFS_SBI(sb)->logpxd); + log->size = lengthPXD(&JFS_SBI(sb)->logpxd) >> + (L2LOGPSIZE - sb->s_blocksize_bits); + log->l2bsize = sb->s_blocksize_bits; + ASSERT(L2LOGPSIZE >= sb->s_blocksize_bits); + /* + * initialize log. + */ + if ((rc = lmLogInit(log))) + goto errout10; + +#ifdef _STILL_TO_PORT + goto out; + + /* + * external log as separate logical volume + * + * file systems to log may have n-to-1 relationship; + */ + externalLog: + /* + * open log inode + * + * log inode is reserved inode of (dev_t = log device, + * fileset number = 0, i_number = 0), which acquire + * one i_count for each open by file system. + * + * hand craft dummy vfs to force iget() the special case of + * an in-memory inode allocation without on-disk inode + */ + memset(&dummyvfs, 0, sizeof(struct vfs)); + dummyvfs.filesetvfs.vfs_data = NULL; + dummyvfs.dummyvfs.dev = logdev; + dummyvfs.dummyvfs.ipmnt = NULL; + ICACHE_LOCK(); + rc = iget((struct vfs *) &dummyvfs, 0, (inode_t **) & log, 0); + ICACHE_UNLOCK(); + if (rc) + return rc; + + log->flag = 0; + log->dev = logdev; + log->base = 0; + log->size = 0; + + /* + * serialize open/close between multiple file systems + * bound with the log; + */ + ip = (inode_t *) log; + IWRITE_LOCK(ip); + + /* + * subsequent open: add file system to log active file system list + */ +#ifdef _JFS_OS2 + if (log->strat2p) +#endif /* _JFS_OS2 */ + { + if (rc = lmLogFileSystem(log, fsdev, 1)) + goto errout10; + + IWRITE_UNLOCK(ip); + + *iplog = ip; + jFYI(1, ("lmLogOpen: exit(0)\n")); + return 0; + } + + /* decouple log inode from dummy vfs */ + vPut(ip); + + /* + * first open: + */ +#ifdef _JFS_OS2 + /* + * establish access to the single/shared (already open) log device + */ + logdevfp = (void *) logStrat2; + log->strat2p = logStrat2; + log->strat3p = logStrat3; + + log->l2pbsize = 9; /* todo: when OS/2 have multiple external log */ +#endif /* _JFS_OS2 */ + + /* + * initialize log: + */ + if (rc = lmLogInit(log)) + goto errout20; + + /* + * add file system to log active file system list + */ + if (rc = lmLogFileSystem(log, fsdev, 1)) + goto errout30; + + /* + * insert log device into log device list + */ + out: +#endif /* _STILL_TO_PORT */ + jFYI(1, ("lmLogOpen: exit(0)\n")); + return 0; + + /* + * unwind on error + */ +#ifdef _STILL_TO_PORT + errout30: /* unwind lbmLogInit() */ + lbmLogShutdown(log); + + errout20: /* close external log device */ + +#endif /* _STILL_TO_PORT */ + errout10: /* free log inode */ + kfree(log); + + jFYI(1, ("lmLogOpen: exit(%d)\n", rc)); + return rc; +} + + +/* + * NAME: lmLogInit() + * + * FUNCTION: log initialization at first log open. + * + * logredo() (or logformat()) should have been run previously. + * initialize the log inode from log superblock. + * set the log state in the superblock to LOGMOUNT and + * write SYNCPT log record. + * + * PARAMETER: log - log structure + * + * RETURN: 0 - if ok + * EINVAL - bad log magic number or superblock dirty + * error returned from logwait() + * + * serialization: single first open thread + */ +static int lmLogInit(log_t * log) +{ + int rc = 0; + lrd_t lrd; + logsuper_t *logsuper; + lbuf_t *bpsuper; + lbuf_t *bp; + logpage_t *lp; + int lsn; + + jFYI(1, ("lmLogInit: log:0x%p\n", log)); + + /* + * log inode is overlaid on generic inode where + * dinode have been zeroed out by iRead(); + */ + + /* + * initialize log i/o + */ + if ((rc = lbmLogInit(log))) + return rc; + + /* + * validate log superblock + */ + if ((rc = lbmRead(log, 1, &bpsuper))) + goto errout10; + + logsuper = (logsuper_t *) bpsuper->l_ldata; + + if (logsuper->magic != cpu_to_le32(LOGMAGIC)) { + jERROR(1, ("*** Log Format Error ! ***\n")); + rc = EINVAL; + goto errout20; + } + + /* logredo() should have been run successfully. */ + if (logsuper->state != cpu_to_le32(LOGREDONE)) { + jERROR(1, ("*** Log Is Dirty ! ***\n")); + rc = EINVAL; + goto errout20; + } + + /* initialize log inode from log superblock */ + if (log->flag & JFS_INLINELOG) { + if (log->size != le32_to_cpu(logsuper->size)) { + rc = EINVAL; + goto errout20; + } + jFYI(0, + ("lmLogInit: inline log:0x%p base:0x%Lx size:0x%x\n", + log, (unsigned long long) log->base, log->size)); + } else { + log->size = le32_to_cpu(logsuper->size); + jFYI(0, + ("lmLogInit: external log:0x%p base:0x%Lx size:0x%x\n", + log, (unsigned long long) log->base, log->size)); + } + + log->flag |= JFS_GROUPCOMMIT; +/* + log->flag |= JFS_LAZYCOMMIT; +*/ + log->page = le32_to_cpu(logsuper->end) / LOGPSIZE; + log->eor = le32_to_cpu(logsuper->end) - (LOGPSIZE * log->page); + + /* + * initialize for log append write mode + */ + /* establish current/end-of-log page/buffer */ + if ((rc = lbmRead(log, log->page, &bp))) + goto errout20; + + lp = (logpage_t *) bp->l_ldata; + + jFYI(1, ("lmLogInit: lsn:0x%x page:%d eor:%d:%d\n", + le32_to_cpu(logsuper->end), log->page, log->eor, + le16_to_cpu(lp->h.eor))); + +// ASSERT(log->eor == lp->h.eor); + + log->bp = bp; + bp->l_pn = log->page; + bp->l_eor = log->eor; + + /* initialize the group commit serialization lock */ + LOGGC_LOCK_INIT(log); + + /* if current page is full, move on to next page */ + if (log->eor >= LOGPSIZE - LOGPTLRSIZE) + lmNextPage(log); + + /* allocate/initialize the log write serialization lock */ + LOG_LOCK_INIT(log); + + /* + * initialize log syncpoint + */ + /* + * write the first SYNCPT record with syncpoint = 0 + * (i.e., log redo up to HERE !); + * remove current page from lbm write queue at end of pageout + * (to write log superblock update), but do not release to freelist; + */ + lrd.logtid = 0; + lrd.backchain = 0; + lrd.type = cpu_to_le16(LOG_SYNCPT); + lrd.length = 0; + lrd.log.syncpt.sync = 0; + lsn = lmWriteRecord(log, NULL, &lrd, NULL); + bp = log->bp; + bp->l_ceor = bp->l_eor; + lp = (logpage_t *) bp->l_ldata; + lp->h.eor = lp->t.eor = cpu_to_le16(bp->l_eor); + lbmWrite(log, bp, lbmWRITE | lbmSYNC, 0); + if ((rc = lbmIOWait(bp, 0))) + goto errout30; + + /* initialize logsync parameters */ + log->logsize = (log->size - 2) << L2LOGPSIZE; + log->lsn = lsn; + log->syncpt = lsn; + log->sync = log->syncpt; + log->nextsync = LOGSYNC_DELTA(log->logsize); + init_waitqueue_head(&log->syncwait); + + jFYI(1, ("lmLogInit: lsn:0x%x syncpt:0x%x sync:0x%x\n", + log->lsn, log->syncpt, log->sync)); + + LOGSYNC_LOCK_INIT(log); + + INIT_LIST_HEAD(&log->synclist); + + log->cqueue.head = log->cqueue.tail = 0; + + log->count = 0; + + /* + * initialize for lazy/group commit + */ + log->clsn = lsn; + + /* + * update/write superblock + */ + logsuper->state = cpu_to_le32(LOGMOUNT); + log->serial = le32_to_cpu(logsuper->serial) + 1; + logsuper->serial = cpu_to_le32(log->serial); + lbmDirectWrite(log, bpsuper, lbmWRITE | lbmRELEASE | lbmSYNC); + if ((rc = lbmIOWait(bpsuper, lbmFREE))) + goto errout30; + + jFYI(1, ("lmLogInit: exit(%d)\n", rc)); + return 0; + + /* + * unwind on error + */ + errout30: /* release log page */ + lbmFree(bp); + + errout20: /* release log superblock */ + lbmFree(bpsuper); + + errout10: /* unwind lbmLogInit() */ + lbmLogShutdown(log); + + jFYI(1, ("lmLogInit: exit(%d)\n", rc)); + return rc; +} + + +/* + * NAME: lmLogClose() + * + * FUNCTION: remove file system from active list of log + * and close it on last close. + * + * PARAMETER: sb - superblock + * log - log inode + * + * RETURN: errors from subroutines + * + * serialization: + */ +int lmLogClose(struct super_block *sb, log_t * log) +{ + int rc; + + jFYI(1, ("lmLogClose: log:0x%p\n", log)); + + /* + * in-line log in host file system + */ +// inlineLog: +#ifdef _STILL_TO_PORT + if (log->flag & JFS_INLINELOG) { + rc = lmLogShutdown(log); + + goto out1; + } + + /* + * external log as separate logical volume + */ + externalLog: + + /* serialize open/close between multiple file systems + * associated with the log + */ + IWRITE_LOCK(iplog); + + /* + * remove file system from log active file system list + */ + rc = lmLogFileSystem(log, fsdev, 0); + + if (iplog->i_count > 1) + goto out2; + + /* + * last close: shut down log + */ + rc = ((rc1 = lmLogShutdown(log)) && rc == 0) ? rc1 : rc; + + out1: +#else /* _STILL_TO_PORT */ + rc = lmLogShutdown(log); +#endif /* _STILL_TO_PORT */ + +// out2: + + jFYI(0, ("lmLogClose: exit(%d)\n", rc)); + return rc; +} + + +/* + * NAME: lmLogShutdown() + * + * FUNCTION: log shutdown at last LogClose(). + * + * write log syncpt record. + * update super block to set redone flag to 0. + * + * PARAMETER: log - log inode + * + * RETURN: 0 - success + * + * serialization: single last close thread + */ +static int lmLogShutdown(log_t * log) +{ + int rc; + lrd_t lrd; + int lsn; + logsuper_t *logsuper; + lbuf_t *bpsuper; + lbuf_t *bp; + logpage_t *lp; + + jFYI(1, ("lmLogShutdown: log:0x%p\n", log)); + + if (log->cqueue.head || !list_empty(&log->synclist)) { + /* + * If there was very recent activity, we may need to wait + * for the lazycommit thread to catch up + */ + int i; + + for (i = 0; i < 800; i++) { /* Too much? */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ / 4); + if ((log->cqueue.head == NULL) && + list_empty(&log->synclist)) + break; + } + } + assert(log->cqueue.head == NULL); + assert(list_empty(&log->synclist)); + + /* + * We need to make sure all of the "written" metapages + * actually make it to disk + */ + fsync_no_super(log->sb->s_bdev); + + /* + * write the last SYNCPT record with syncpoint = 0 + * (i.e., log redo up to HERE !) + */ + lrd.logtid = 0; + lrd.backchain = 0; + lrd.type = cpu_to_le16(LOG_SYNCPT); + lrd.length = 0; + lrd.log.syncpt.sync = 0; + lsn = lmWriteRecord(log, NULL, &lrd, NULL); + bp = log->bp; + lp = (logpage_t *) bp->l_ldata; + lp->h.eor = lp->t.eor = cpu_to_le16(bp->l_eor); + lbmWrite(log, log->bp, lbmWRITE | lbmRELEASE | lbmSYNC, 0); + lbmIOWait(log->bp, lbmFREE); + + /* + * synchronous update log superblock + * mark log state as shutdown cleanly + * (i.e., Log does not need to be replayed). + */ + if ((rc = lbmRead(log, 1, &bpsuper))) + goto out; + + logsuper = (logsuper_t *) bpsuper->l_ldata; + logsuper->state = cpu_to_le32(LOGREDONE); + logsuper->end = cpu_to_le32(lsn); + lbmDirectWrite(log, bpsuper, lbmWRITE | lbmRELEASE | lbmSYNC); + rc = lbmIOWait(bpsuper, lbmFREE); + + jFYI(1, ("lmLogShutdown: lsn:0x%x page:%d eor:%d\n", + lsn, log->page, log->eor)); + + out: + /* + * shutdown per log i/o + */ + lbmLogShutdown(log); + + if (rc) { + jFYI(1, ("lmLogShutdown: exit(%d)\n", rc)); + } + return rc; +} + + +#ifdef _STILL_TO_PORT +/* + * NAME: lmLogFileSystem() + * + * FUNCTION: insert ( = true)/remove ( = false) + * file system into/from log active file system list. + * + * PARAMETE: log - pointer to logs inode. + * fsdev - dev_t of filesystem. + * serial - pointer to returned log serial number + * activate - insert/remove device from active list. + * + * RETURN: 0 - success + * errors returned by vms_iowait(). + * + * serialization: IWRITE_LOCK(log inode) held on entry/exit + */ +static int lmLogFileSystem(log_t * log, dev_t fsdev, int activate) +{ + int rc = 0; + int bit, word; + logsuper_t *logsuper; + lbuf_t *bpsuper; + + /* + * insert/remove file system device to log active file system list. + */ + if ((rc = lbmRead(log, 1, &bpsuper))) + return rc; + + logsuper = (logsuper_t *) bpsuper->l_ldata; + bit = MINOR(fsdev); + word = bit / 32; + bit -= 32 * word; + if (activate) + logsuper->active[word] |= + cpu_to_le32((LEFTMOSTONE >> bit)); + else + logsuper->active[word] &= + cpu_to_le32((~(LEFTMOSTONE >> bit))); + + /* + * synchronous write log superblock: + * + * write sidestream bypassing write queue: + * at file system mount, log super block is updated for + * activation of the file system before any log record + * (MOUNT record) of the file system, and at file system + * unmount, all meta data for the file system has been + * flushed before log super block is updated for deactivation + * of the file system. + */ + lbmDirectWrite(log, bpsuper, lbmWRITE | lbmRELEASE | lbmSYNC); + rc = lbmIOWait(bpsuper, lbmFREE); + + return rc; +} +#endif /* _STILL_TO_PORT */ + + +/* + * lmLogQuiesce() + */ +int lmLogQuiesce(log_t * log) +{ + int rc; + + rc = lmLogShutdown(log); + + return rc; +} + + +/* + * lmLogResume() + */ +int lmLogResume(log_t * log, struct super_block *sb) +{ + struct jfs_sb_info *sbi = JFS_SBI(sb); + int rc; + + log->base = addressPXD(&sbi->logpxd); + log->size = + (lengthPXD(&sbi->logpxd) << sb->s_blocksize_bits) >> L2LOGPSIZE; + rc = lmLogInit(log); + + return rc; +} + + +/* + * log buffer manager (lbm) + * ------------------------ + * + * special purpose buffer manager supporting log i/o requirements. + * + * per log write queue: + * log pageout occurs in serial order by fifo write queue and + * restricting to a single i/o in pregress at any one time. + * a circular singly-linked list + * (log->wrqueue points to the tail, and buffers are linked via + * bp->wrqueue field), and + * maintains log page in pageout ot waiting for pageout in serial pageout. + */ + +/* + * lbmLogInit() + * + * initialize per log I/O setup at lmLogInit() + */ +static int lbmLogInit(log_t * log) +{ /* log inode */ + int i; + lbuf_t *lbuf; + + jFYI(1, ("lbmLogInit: log:0x%p\n", log)); + + /* initialize current buffer cursor */ + log->bp = NULL; + + /* initialize log device write queue */ + log->wqueue = NULL; + + /* + * Each log has its own buffer pages allocated to it. These are + * not managed by the page cache. This ensures that a transaction + * writing to the log does not block trying to allocate a page from + * the page cache (for the log). This would be bad, since page + * allocation waits on the kswapd thread that may be committing inodes + * which would cause log activity. Was that clear? I'm trying to + * avoid deadlock here. + */ + init_waitqueue_head(&log->free_wait); + + log->lbuf_free = NULL; + + for (i = 0; i < LOGPAGES; i++) { + lbuf = kmalloc(sizeof(lbuf_t), GFP_KERNEL); + if (lbuf == 0) + goto error; + lbuf->l_ldata = (char *) __get_free_page(GFP_KERNEL); + if (lbuf->l_ldata == 0) { + kfree(lbuf); + goto error; + } + lbuf->l_log = log; + init_waitqueue_head(&lbuf->l_ioevent); + + lbuf->l_freelist = log->lbuf_free; + log->lbuf_free = lbuf; + } + + return (0); + + error: + lbmLogShutdown(log); + return (ENOMEM); +} + + +/* + * lbmLogShutdown() + * + * finalize per log I/O setup at lmLogShutdown() + */ +static void lbmLogShutdown(log_t * log) +{ + lbuf_t *lbuf; + + jFYI(1, ("lbmLogShutdown: log:0x%p\n", log)); + + lbuf = log->lbuf_free; + while (lbuf) { + lbuf_t *next = lbuf->l_freelist; + free_page((unsigned long) lbuf->l_ldata); + kfree(lbuf); + lbuf = next; + } + + log->bp = NULL; +} + + +/* + * lbmAllocate() + * + * allocate an empty log buffer + */ +static lbuf_t *lbmAllocate(log_t * log, int pn) +{ + lbuf_t *bp; + unsigned long flags; + + /* + * recycle from log buffer freelist if any + */ + LCACHE_LOCK(flags); + LCACHE_SLEEP_COND(log->free_wait, (bp = log->lbuf_free), flags); + log->lbuf_free = bp->l_freelist; + LCACHE_UNLOCK(flags); + + bp->l_flag = 0; + + bp->l_wqnext = NULL; + bp->l_freelist = NULL; + + bp->l_pn = pn; + bp->l_blkno = log->base + (pn << (L2LOGPSIZE - log->l2bsize)); + bp->l_ceor = 0; + + return bp; +} + + +/* + * lbmFree() + * + * release a log buffer to freelist + */ +static void lbmFree(lbuf_t * bp) +{ + unsigned long flags; + + LCACHE_LOCK(flags); + + lbmfree(bp); + + LCACHE_UNLOCK(flags); +} + +static void lbmfree(lbuf_t * bp) +{ + log_t *log = bp->l_log; + + assert(bp->l_wqnext == NULL); + + /* + * return the buffer to head of freelist + */ + bp->l_freelist = log->lbuf_free; + log->lbuf_free = bp; + + wake_up(&log->free_wait); + return; +} + + +#ifdef _THIS_IS_NOT_USED +/* + * lbmRelease() + * + * remove the log buffer from log device write queue; + */ +static void lbmRelease(log_t * log, uint flag) +{ + lbuf_t *bp, *tail; + unsigned long flags; + + bp = log->bp; + + LCACHE_LOCK(flags); + + tail = log->wqueue; + + /* single element queue */ + if (bp == tail) { + log->wqueue = NULL; + bp->l_wqnext = NULL; + } + /* multi element queue */ + else { + tail->l_wqnext = bp->l_wqnext; + bp->l_wqnext = NULL; + } + + if (flag & lbmFREE) + lbmfree(bp); + + LCACHE_UNLOCK(flags); +} +#endif /* _THIS_IS_NOT_USED */ + + +/* + * NAME: lbmRedrive + * + * FUNCTION: add a log buffer to the the log redrive list + * + * PARAMETER: + * bp - log buffer + * + * NOTES: + * Takes log_redrive_lock. + */ +static inline void lbmRedrive(lbuf_t *bp) +{ + unsigned long flags; + + spin_lock_irqsave(&log_redrive_lock, flags); + bp->l_redrive_next = log_redrive_list; + log_redrive_list = bp; + spin_unlock_irqrestore(&log_redrive_lock, flags); + + wake_up_process(jfsIOtask); +} + + +/* + * lbmRead() + */ +static int lbmRead(log_t * log, int pn, lbuf_t ** bpp) +{ + struct bio *bio; + lbuf_t *bp; + + /* + * allocate a log buffer + */ + *bpp = bp = lbmAllocate(log, pn); + jFYI(1, ("lbmRead: bp:0x%p pn:0x%x\n", bp, pn)); + + bp->l_flag |= lbmREAD; + + bio = bio_alloc(GFP_NOFS, 1); + + bio->bi_sector = bp->l_blkno << (log->l2bsize - 9); + bio->bi_dev = log->dev; + bio->bi_io_vec[0].bv_page = virt_to_page(bp->l_ldata); + bio->bi_io_vec[0].bv_len = LOGPSIZE; + bio->bi_io_vec[0].bv_offset = 0; + + bio->bi_vcnt = 1; + bio->bi_idx = 0; + bio->bi_size = LOGPSIZE; + + bio->bi_end_io = lbmIODone; + bio->bi_private = bp; + submit_bio(READ, bio); + run_task_queue(&tq_disk); + + wait_event(bp->l_ioevent, (bp->l_flag != lbmREAD)); + + return 0; +} + + +/* + * lbmWrite() + * + * buffer at head of pageout queue stays after completion of + * partial-page pageout and redriven by explicit initiation of + * pageout by caller until full-page pageout is completed and + * released. + * + * device driver i/o done redrives pageout of new buffer at + * head of pageout queue when current buffer at head of pageout + * queue is released at the completion of its full-page pageout. + * + * LOGGC_LOCK() serializes lbmWrite() by lmNextPage() and lmGroupCommit(). + * LCACHE_LOCK() serializes xflag between lbmWrite() and lbmIODone() + */ +static void lbmWrite(log_t * log, lbuf_t * bp, int flag, int cant_block) +{ + lbuf_t *tail; + unsigned long flags; + + jFYI(1, ("lbmWrite: bp:0x%p flag:0x%x pn:0x%x\n", + bp, flag, bp->l_pn)); + + /* map the logical block address to physical block address */ + bp->l_blkno = + log->base + (bp->l_pn << (L2LOGPSIZE - log->l2bsize)); + + LCACHE_LOCK(flags); /* disable+lock */ + + /* + * initialize buffer for device driver + */ + bp->l_flag = flag; + + /* + * insert bp at tail of write queue associated with log + * + * (request is either for bp already/currently at head of queue + * or new bp to be inserted at tail) + */ + tail = log->wqueue; + + /* is buffer not already on write queue ? */ + if (bp->l_wqnext == NULL) { + /* insert at tail of wqueue */ + if (tail == NULL) { + log->wqueue = bp; + bp->l_wqnext = bp; + } else { + log->wqueue = bp; + bp->l_wqnext = tail->l_wqnext; + tail->l_wqnext = bp; + } + + tail = bp; + } + + /* is buffer at head of wqueue and for write ? */ + if ((bp != tail->l_wqnext) || !(flag & lbmWRITE)) { + LCACHE_UNLOCK(flags); /* unlock+enable */ + return; + } + + LCACHE_UNLOCK(flags); /* unlock+enable */ + + if (cant_block) + lbmRedrive(bp); + else if (flag & lbmSYNC) + lbmStartIO(bp); + else { + LOGGC_UNLOCK(log); + lbmStartIO(bp); + LOGGC_LOCK(log); + } +} + + +/* + * lbmDirectWrite() + * + * initiate pageout bypassing write queue for sidestream + * (e.g., log superblock) write; + */ +static void lbmDirectWrite(log_t * log, lbuf_t * bp, int flag) +{ + jEVENT(0, ("lbmDirectWrite: bp:0x%p flag:0x%x pn:0x%x\n", + bp, flag, bp->l_pn)); + + /* + * initialize buffer for device driver + */ + bp->l_flag = flag | lbmDIRECT; + + /* map the logical block address to physical block address */ + bp->l_blkno = + log->base + (bp->l_pn << (L2LOGPSIZE - log->l2bsize)); + + /* + * initiate pageout of the page + */ + lbmStartIO(bp); +} + + +/* + * NAME: lbmStartIO() + * + * FUNCTION: Interface to DD strategy routine + * + * RETURN: none + * + * serialization: LCACHE_LOCK() is NOT held during log i/o; + */ +void lbmStartIO(lbuf_t * bp) +{ + struct bio *bio; + log_t *log = bp->l_log; + + jFYI(1, ("lbmStartIO\n")); + + bio = bio_alloc(GFP_NOFS, 1); + bio->bi_sector = bp->l_blkno << (log->l2bsize - 9); + bio->bi_dev = log->dev; + bio->bi_io_vec[0].bv_page = virt_to_page(bp->l_ldata); + bio->bi_io_vec[0].bv_len = LOGPSIZE; + bio->bi_io_vec[0].bv_offset = 0; + + bio->bi_vcnt = 1; + bio->bi_idx = 0; + bio->bi_size = LOGPSIZE; + + bio->bi_end_io = lbmIODone; + bio->bi_private = bp; + + submit_bio(WRITE, bio); + + INCREMENT(lmStat.submitted); + run_task_queue(&tq_disk); + + jFYI(1, ("lbmStartIO done\n")); +} + + +/* + * lbmIOWait() + */ +static int lbmIOWait(lbuf_t * bp, int flag) +{ + unsigned long flags; + int rc = 0; + + jFYI(1, + ("lbmIOWait1: bp:0x%p flag:0x%x:0x%x\n", bp, bp->l_flag, + flag)); + + LCACHE_LOCK(flags); /* disable+lock */ + + LCACHE_SLEEP_COND(bp->l_ioevent, (bp->l_flag & lbmDONE), flags); + + rc = (bp->l_flag & lbmERROR) ? EIO : 0; + + if (flag & lbmFREE) + lbmfree(bp); + + LCACHE_UNLOCK(flags); /* unlock+enable */ + + jFYI(1, + ("lbmIOWait2: bp:0x%p flag:0x%x:0x%x\n", bp, bp->l_flag, + flag)); + return rc; +} + +/* + * lbmIODone() + * + * executed at INTIODONE level + */ +static void lbmIODone(struct bio *bio) +{ + lbuf_t *bp = bio->bi_private; + lbuf_t *nextbp, *tail; + log_t *log; + unsigned long flags; + + /* + * get back jfs buffer bound to the i/o buffer + */ + jEVENT(0, ("lbmIODone: bp:0x%p flag:0x%x\n", bp, bp->l_flag)); + + LCACHE_LOCK(flags); /* disable+lock */ + + bp->l_flag |= lbmDONE; + + if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) { + bp->l_flag |= lbmERROR; + + jERROR(1, ("lbmIODone: I/O error in JFS log\n")); + } + bio_put(bio); + + /* + * pagein completion + */ + if (bp->l_flag & lbmREAD) { + bp->l_flag &= ~lbmREAD; + + LCACHE_UNLOCK(flags); /* unlock+enable */ + + /* wakeup I/O initiator */ + LCACHE_WAKEUP(&bp->l_ioevent); + + return; + } + + /* + * pageout completion + * + * the bp at the head of write queue has completed pageout. + * + * if single-commit/full-page pageout, remove the current buffer + * from head of pageout queue, and redrive pageout with + * the new buffer at head of pageout queue; + * otherwise, the partial-page pageout buffer stays at + * the head of pageout queue to be redriven for pageout + * by lmGroupCommit() until full-page pageout is completed. + */ + bp->l_flag &= ~lbmWRITE; + INCREMENT(lmStat.pagedone); + + /* update committed lsn */ + log = bp->l_log; + log->clsn = (bp->l_pn << L2LOGPSIZE) + bp->l_ceor; + + if (bp->l_flag & lbmDIRECT) { + LCACHE_WAKEUP(&bp->l_ioevent); + LCACHE_UNLOCK(flags); + return; + } + + tail = log->wqueue; + + /* single element queue */ + if (bp == tail) { + /* remove head buffer of full-page pageout + * from log device write queue + */ + if (bp->l_flag & lbmRELEASE) { + log->wqueue = NULL; + bp->l_wqnext = NULL; + } + } + /* multi element queue */ + else { + /* remove head buffer of full-page pageout + * from log device write queue + */ + if (bp->l_flag & lbmRELEASE) { + nextbp = tail->l_wqnext = bp->l_wqnext; + bp->l_wqnext = NULL; + + /* + * redrive pageout of next page at head of write queue: + * redrive next page without any bound tblk + * (i.e., page w/o any COMMIT records), or + * first page of new group commit which has been + * queued after current page (subsequent pageout + * is performed synchronously, except page without + * any COMMITs) by lmGroupCommit() as indicated + * by lbmWRITE flag; + */ + if (nextbp->l_flag & lbmWRITE) { + /* + * We can't do the I/O at interrupt time. + * The jfsIO thread can do it + */ + lbmRedrive(nextbp); + } + } + } + + /* + * synchronous pageout: + * + * buffer has not necessarily been removed from write queue + * (e.g., synchronous write of partial-page with COMMIT): + * leave buffer for i/o initiator to dispose + */ + if (bp->l_flag & lbmSYNC) { + LCACHE_UNLOCK(flags); /* unlock+enable */ + + /* wakeup I/O initiator */ + LCACHE_WAKEUP(&bp->l_ioevent); + } + + /* + * Group Commit pageout: + */ + else if (bp->l_flag & lbmGC) { + LCACHE_UNLOCK(flags); + lmPostGC(bp); + } + + /* + * asynchronous pageout: + * + * buffer must have been removed from write queue: + * insert buffer at head of freelist where it can be recycled + */ + else { + assert(bp->l_flag & lbmRELEASE); + assert(bp->l_flag & lbmFREE); + lbmfree(bp); + + LCACHE_UNLOCK(flags); /* unlock+enable */ + } + return; +} + +int jfsIOWait(void *arg) +{ + lbuf_t *bp; + + jFYI(1, ("jfsIOWait is here!\n")); + + lock_kernel(); + + daemonize(); + current->tty = NULL; + strcpy(current->comm, "jfsIO"); + + unlock_kernel(); + + jfsIOtask = current; + + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv(¤t->blocked, + sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) + | sigmask(SIGCONT)); + spin_unlock_irq(¤t->sigmask_lock); + + complete(&jfsIOwait); + + do { + spin_lock_irq(&log_redrive_lock); + while ((bp = log_redrive_list)) { + log_redrive_list = bp->l_redrive_next; + bp->l_redrive_next = NULL; + spin_unlock_irq(&log_redrive_lock); + lbmStartIO(bp); + spin_lock_irq(&log_redrive_lock); + } + spin_unlock_irq(&log_redrive_lock); + + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } while (!jfs_thread_stopped()); + + jFYI(1,("jfsIOWait being killed!\n")); + complete(&jfsIOwait); + return 0; +} + + +#ifdef _STILL_TO_PORT +/* + * lbmDirectIODone() + * + * iodone() for lbmDirectWrite() to bypass write queue; + * executed at INTIODONE level; + */ +static void lbmDirectIODone(iobuf_t * iobp) +{ + lbuf_t *bp; + unsigned long flags; + + /* + * get back jfs buffer bound to the io buffer + */ + bp = (lbuf_t *) iobp->b_jfsbp; + jEVENT(0, + ("lbmDirectIODone: bp:0x%p flag:0x%x\n", bp, bp->l_flag)); + + LCACHE_LOCK(flags); /* disable+lock */ + + bp->l_flag |= lbmDONE; + + if (iobp->b_flags & B_ERROR) { + bp->l_flag |= lbmERROR; +#ifdef _JFS_OS2 + SysLogError(); +#endif + } + + /* + * pageout completion + */ + bp->l_flag &= ~lbmWRITE; + + /* + * synchronous pageout: + */ + if (bp->l_flag & lbmSYNC) { + LCACHE_UNLOCK(flags); /* unlock+enable */ + + /* wakeup I/O initiator */ + LCACHE_WAKEUP(&bp->l_ioevent); + } + /* + * asynchronous pageout: + */ + else { + assert(bp->l_flag & lbmRELEASE); + assert(bp->l_flag & lbmFREE); + lbmfree(bp); + + LCACHE_UNLOCK(flags); /* unlock+enable */ + } +} +#endif /* _STILL_TO_PORT */ + +#ifdef _STILL_TO_PORT +/* + * NAME: lmLogFormat()/jfs_logform() + * + * FUNCTION: format file system log (ref. jfs_logform()). + * + * PARAMETERS: + * log - log inode (with common mount inode base); + * logAddress - start address of log space in FS block; + * logSize - length of log space in FS block; + * + * RETURN: 0 - success + * -1 - i/o error + */ +int lmLogFormat(inode_t * ipmnt, s64 logAddress, int logSize) +{ + int rc = 0; + cbuf_t *bp; + logsuper_t *logsuper; + logpage_t *lp; + int lspn; /* log sequence page number */ + struct lrd *lrd_ptr; + int npbperpage, npages; + + jFYI(0, ("lmLogFormat: logAddress:%Ld logSize:%d\n", + logAddress, logSize)); + + /* allocate a JFS buffer */ + bp = rawAllocate(); + + /* map the logical block address to physical block address */ + bp->cm_blkno = logAddress << ipmnt->i_l2bfactor; + + npbperpage = LOGPSIZE >> ipmnt->i_l2pbsize; + npages = logSize / (LOGPSIZE >> ipmnt->i_l2bsize); + + /* + * log space: + * + * page 0 - reserved; + * page 1 - log superblock; + * page 2 - log data page: A SYNC log record is written + * into this page at logform time; + * pages 3-N - log data page: set to empty log data pages; + */ + /* + * init log superblock: log page 1 + */ + logsuper = (logsuper_t *) bp->cm_cdata; + + logsuper->magic = cpu_to_le32(LOGMAGIC); + logsuper->version = cpu_to_le32(LOGVERSION); + logsuper->state = cpu_to_le32(LOGREDONE); + logsuper->flag = cpu_to_le32(ipmnt->i_mntflag); /* ? */ + logsuper->size = cpu_to_le32(npages); + logsuper->bsize = cpu_to_le32(ipmnt->i_bsize); + logsuper->l2bsize = cpu_to_le32(ipmnt->i_l2bsize); + logsuper->end = + cpu_to_le32(2 * LOGPSIZE + LOGPHDRSIZE + LOGRDSIZE); + + bp->cm_blkno += npbperpage; + rawWrite(ipmnt, bp, 0); + + /* + * init pages 2 to npages-1 as log data pages: + * + * log page sequence number (lpsn) initialization: + * + * pn: 0 1 2 3 n-1 + * +-----+-----+=====+=====+===.....===+=====+ + * lspn: N-1 0 1 N-2 + * <--- N page circular file ----> + * + * the N (= npages-2) data pages of the log is maintained as + * a circular file for the log records; + * lpsn grows by 1 monotonically as each log page is written + * to the circular file of the log; + * Since the AIX DUMMY log record is dropped for this XJFS, + * and setLogpage() will not reset the page number even if + * the eor is equal to LOGPHDRSIZE. In order for binary search + * still work in find log end process, we have to simulate the + * log wrap situation at the log format time. + * The 1st log page written will have the highest lpsn. Then + * the succeeding log pages will have ascending order of + * the lspn starting from 0, ... (N-2) + */ + lp = (logpage_t *) bp->cm_cdata; + + /* + * initialize 1st log page to be written: lpsn = N - 1, + * write a SYNCPT log record is written to this page + */ + lp->h.page = lp->t.page = cpu_to_le32(npages - 3); + lp->h.eor = lp->t.eor = cpu_to_le16(LOGPHDRSIZE + LOGRDSIZE); + + lrd_ptr = (struct lrd *) &lp->data; + lrd_ptr->logtid = 0; + lrd_ptr->backchain = 0; + lrd_ptr->type = cpu_to_le16(LOG_SYNCPT); + lrd_ptr->length = 0; + lrd_ptr->log.syncpt.sync = 0; + + bp->cm_blkno += npbperpage; + rawWrite(ipmnt, bp, 0); + + /* + * initialize succeeding log pages: lpsn = 0, 1, ..., (N-2) + */ + for (lspn = 0; lspn < npages - 3; lspn++) { + lp->h.page = lp->t.page = cpu_to_le32(lspn); + lp->h.eor = lp->t.eor = cpu_to_le16(LOGPHDRSIZE); + + bp->cm_blkno += npbperpage; + rawWrite(ipmnt, bp, 0); + } + + /* + * finalize log + */ + /* release the buffer */ + rawRelease(bp); + + return rc; +} +#endif /* _STILL_TO_PORT */ + + +#ifdef CONFIG_JFS_STATISTICS +int jfs_lmstats_read(char *buffer, char **start, off_t offset, int length, + int *eof, void *data) +{ + int len = 0; + off_t begin; + + len += sprintf(buffer, + "JFS Logmgr stats\n" + "================\n" + "commits = %d\n" + "writes submitted = %d\n" + "writes completed = %d\n", + lmStat.commit, + lmStat.submitted, + lmStat.pagedone); + + begin = offset; + *start = buffer + begin; + len -= begin; + + if (len > length) + len = length; + else + *eof = 1; + + if (len < 0) + len = 0; + + return len; +} +#endif /* CONFIG_JFS_STATISTICS */ diff -Nru a/fs/jfs/jfs_logmgr.h b/fs/jfs/jfs_logmgr.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_logmgr.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,499 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ + +#ifndef _H_JFS_LOGMGR +#define _H_JFS_LOGMGR + + +#include "jfs_filsys.h" +#include "jfs_lock.h" + +/* + * log manager configuration parameters + */ + +/* log page size */ +#define LOGPSIZE 4096 +#define L2LOGPSIZE 12 + +#define LOGPAGES 16 /* Log pages per mounted file system */ + +/* + * log logical volume + * + * a log is used to make the commit operation on journalled + * files within the same logical volume group atomic. + * a log is implemented with a logical volume. + * there is one log per logical volume group. + * + * block 0 of the log logical volume is not used (ipl etc). + * block 1 contains a log "superblock" and is used by logFormat(), + * lmLogInit(), lmLogShutdown(), and logRedo() to record status + * of the log but is not otherwise used during normal processing. + * blocks 2 - (N-1) are used to contain log records. + * + * when a volume group is varied-on-line, logRedo() must have + * been executed before the file systems (logical volumes) in + * the volume group can be mounted. + */ +/* + * log superblock (block 1 of logical volume) + */ +#define LOGSUPER_B 1 +#define LOGSTART_B 2 + +#define LOGMAGIC 0x87654321 +#define LOGVERSION 1 + +typedef struct { + u32 magic; /* 4: log lv identifier */ + s32 version; /* 4: version number */ + s32 serial; /* 4: log open/mount counter */ + s32 size; /* 4: size in number of LOGPSIZE blocks */ + s32 bsize; /* 4: logical block size in byte */ + s32 l2bsize; /* 4: log2 of bsize */ + + u32 flag; /* 4: option */ + u32 state; /* 4: state - see below */ + + s32 end; /* 4: addr of last log record set by logredo */ + u32 active[8]; /* 32: active file systems bit vector */ + s32 rsrvd[LOGPSIZE / 4 - 17]; +} logsuper_t; + +/* log flag: commit option (see jfs_filsys.h) */ + +/* log state */ +#define LOGMOUNT 0 /* log mounted by lmLogInit() */ +#define LOGREDONE 1 /* log shutdown by lmLogShutdown(). + * log redo completed by logredo(). + */ +#define LOGWRAP 2 /* log wrapped */ +#define LOGREADERR 3 /* log read error detected in logredo() */ + + +/* + * log logical page + * + * (this comment should be rewritten !) + * the header and trailer structures (h,t) will normally have + * the same page and eor value. + * An exception to this occurs when a complete page write is not + * accomplished on a power failure. Since the hardware may "split write" + * sectors in the page, any out of order sequence may occur during powerfail + * and needs to be recognized during log replay. The xor value is + * an "exclusive or" of all log words in the page up to eor. This + * 32 bit eor is stored with the top 16 bits in the header and the + * bottom 16 bits in the trailer. logredo can easily recognize pages + * that were not completed by reconstructing this eor and checking + * the log page. + * + * Previous versions of the operating system did not allow split + * writes and detected partially written records in logredo by + * ordering the updates to the header, trailer, and the move of data + * into the logdata area. The order: (1) data is moved (2) header + * is updated (3) trailer is updated. In logredo, when the header + * differed from the trailer, the header and trailer were reconciled + * as follows: if h.page != t.page they were set to the smaller of + * the two and h.eor and t.eor set to 8 (i.e. empty page). if (only) + * h.eor != t.eor they were set to the smaller of their two values. + */ +typedef struct { + struct { /* header */ + s32 page; /* 4: log sequence page number */ + s16 rsrvd; /* 2: */ + s16 eor; /* 2: end-of-log offset of lasrt record write */ + } h; + + s32 data[LOGPSIZE / 4 - 4]; /* log record area */ + + struct { /* trailer */ + s32 page; /* 4: normally the same as h.page */ + s16 rsrvd; /* 2: */ + s16 eor; /* 2: normally the same as h.eor */ + } t; +} logpage_t; + +#define LOGPHDRSIZE 8 /* log page header size */ +#define LOGPTLRSIZE 8 /* log page trailer size */ + + +/* + * log record + * + * (this comment should be rewritten !) + * jfs uses only "after" log records (only a single writer is allowed + * in a page, pages are written to temporary paging space if + * if they must be written to disk before commit, and i/o is + * scheduled for modified pages to their home location after + * the log records containing the after values and the commit + * record is written to the log on disk, undo discards the copy + * in main-memory.) + * + * a log record consists of a data area of variable length followed by + * a descriptor of fixed size LOGRDSIZE bytes. + * the data area is rounded up to an integral number of 4-bytes and + * must be no longer than LOGPSIZE. + * the descriptor is of size of multiple of 4-bytes and aligned on a + * 4-byte boundary. + * records are packed one after the other in the data area of log pages. + * (sometimes a DUMMY record is inserted so that at least one record ends + * on every page or the longest record is placed on at most two pages). + * the field eor in page header/trailer points to the byte following + * the last record on a page. + */ + +/* log record types */ +#define LOG_COMMIT 0x8000 +#define LOG_SYNCPT 0x4000 +#define LOG_MOUNT 0x2000 +#define LOG_REDOPAGE 0x0800 +#define LOG_NOREDOPAGE 0x0080 +#define LOG_NOREDOINOEXT 0x0040 +#define LOG_UPDATEMAP 0x0008 +#define LOG_NOREDOFILE 0x0001 + +/* REDOPAGE/NOREDOPAGE log record data type */ +#define LOG_INODE 0x0001 +#define LOG_XTREE 0x0002 +#define LOG_DTREE 0x0004 +#define LOG_BTROOT 0x0010 +#define LOG_EA 0x0020 +#define LOG_ACL 0x0040 +#define LOG_DATA 0x0080 +#define LOG_NEW 0x0100 +#define LOG_EXTEND 0x0200 +#define LOG_RELOCATE 0x0400 +#define LOG_DIR_XTREE 0x0800 /* Xtree is in directory inode */ + +/* UPDATEMAP log record descriptor type */ +#define LOG_ALLOCXADLIST 0x0080 +#define LOG_ALLOCPXDLIST 0x0040 +#define LOG_ALLOCXAD 0x0020 +#define LOG_ALLOCPXD 0x0010 +#define LOG_FREEXADLIST 0x0008 +#define LOG_FREEPXDLIST 0x0004 +#define LOG_FREEXAD 0x0002 +#define LOG_FREEPXD 0x0001 + + +typedef struct lrd { + /* + * type independent area + */ + s32 logtid; /* 4: log transaction identifier */ + s32 backchain; /* 4: ptr to prev record of same transaction */ + u16 type; /* 2: record type */ + s16 length; /* 2: length of data in record (in byte) */ + s32 aggregate; /* 4: file system lv/aggregate */ + /* (16) */ + + /* + * type dependent area (20) + */ + union { + + /* + * COMMIT: commit + * + * transaction commit: no type-dependent information; + */ + + /* + * REDOPAGE: after-image + * + * apply after-image; + * + * N.B. REDOPAGE, NOREDOPAGE, and UPDATEMAP must be same format; + */ + struct { + u32 fileset; /* 4: fileset number */ + u32 inode; /* 4: inode number */ + u16 type; /* 2: REDOPAGE record type */ + s16 l2linesize; /* 2: log2 of line size */ + pxd_t pxd; /* 8: on-disk page pxd */ + } redopage; /* (20) */ + + /* + * NOREDOPAGE: the page is freed + * + * do not apply after-image records which precede this record + * in the log with the same page block number to this page. + * + * N.B. REDOPAGE, NOREDOPAGE, and UPDATEMAP must be same format; + */ + struct { + s32 fileset; /* 4: fileset number */ + u32 inode; /* 4: inode number */ + u16 type; /* 2: NOREDOPAGE record type */ + s16 rsrvd; /* 2: reserved */ + pxd_t pxd; /* 8: on-disk page pxd */ + } noredopage; /* (20) */ + + /* + * UPDATEMAP: update block allocation map + * + * either in-line PXD, + * or out-of-line XADLIST; + * + * N.B. REDOPAGE, NOREDOPAGE, and UPDATEMAP must be same format; + */ + struct { + u32 fileset; /* 4: fileset number */ + u32 inode; /* 4: inode number */ + u16 type; /* 2: UPDATEMAP record type */ + s16 nxd; /* 2: number of extents */ + pxd_t pxd; /* 8: pxd */ + } updatemap; /* (20) */ + + /* + * NOREDOINOEXT: the inode extent is freed + * + * do not apply after-image records which precede this + * record in the log with the any of the 4 page block + * numbers in this inode extent. + * + * NOTE: The fileset and pxd fields MUST remain in + * the same fields in the REDOPAGE record format. + * + */ + struct { + s32 fileset; /* 4: fileset number */ + s32 iagnum; /* 4: IAG number */ + s32 inoext_idx; /* 4: inode extent index */ + pxd_t pxd; /* 8: on-disk page pxd */ + } noredoinoext; /* (20) */ + + /* + * SYNCPT: log sync point + * + * replay log upto syncpt address specified; + */ + struct { + s32 sync; /* 4: syncpt address (0 = here) */ + } syncpt; + + /* + * MOUNT: file system mount + * + * file system mount: no type-dependent information; + */ + + /* + * ? FREEXTENT: free specified extent(s) + * + * free specified extent(s) from block allocation map + * N.B.: nextents should be length of data/sizeof(xad_t) + */ + struct { + s32 type; /* 4: FREEXTENT record type */ + s32 nextent; /* 4: number of extents */ + + /* data: PXD or XAD list */ + } freextent; + + /* + * ? NOREDOFILE: this file is freed + * + * do not apply records which precede this record in the log + * with the same inode number. + * + * NOREDILE must be the first to be written at commit + * (last to be read in logredo()) - it prevents + * replay of preceding updates of all preceding generations + * of the inumber esp. the on-disk inode itself, + * but does NOT prevent + * replay of the + */ + struct { + s32 fileset; /* 4: fileset number */ + u32 inode; /* 4: inode number */ + } noredofile; + + /* + * ? NEWPAGE: + * + * metadata type dependent + */ + struct { + s32 fileset; /* 4: fileset number */ + u32 inode; /* 4: inode number */ + s32 type; /* 4: NEWPAGE record type */ + pxd_t pxd; /* 8: on-disk page pxd */ + } newpage; + + /* + * ? DUMMY: filler + * + * no type-dependent information + */ + } log; +} lrd_t; /* (36) */ + +#define LOGRDSIZE (sizeof(struct lrd)) + +/* + * line vector descriptor + */ +typedef struct { + s16 offset; + s16 length; +} lvd_t; + + +/* + * log logical volume + */ +typedef struct jfs_log { + + struct super_block *sb; /* 4: This is used to sync metadata + * before writing syncpt. Will + * need to be a list if we share + * the log between fs's + */ + kdev_t dev; /* 4: log lv number */ + struct file *devfp; /* 4: log device file */ + s32 serial; /* 4: log mount serial number */ + + s64 base; /* @8: log extent address (inline log ) */ + int size; /* 4: log size in log page (in page) */ + int l2bsize; /* 4: log2 of bsize */ + + uint flag; /* 4: flag */ + uint state; /* 4: state */ + + struct lbuf *lbuf_free; /* 4: free lbufs */ + wait_queue_head_t free_wait; /* 4: */ + + /* log write */ + int logtid; /* 4: log tid */ + int page; /* 4: page number of eol page */ + int eor; /* 4: eor of last record in eol page */ + struct lbuf *bp; /* 4: current log page buffer */ + + struct semaphore loglock; /* 4: log write serialization lock */ + + /* syncpt */ + int nextsync; /* 4: bytes to write before next syncpt */ + int active; /* 4: */ + int syncbarrier; /* 4: */ + wait_queue_head_t syncwait; /* 4: */ + + /* commit */ + uint cflag; /* 4: */ + struct { /* 8: FIFO commit queue header */ + struct tblock *head; + struct tblock *tail; + } cqueue; + int gcrtc; /* 4: GC_READY transaction count */ + struct tblock *gclrt; /* 4: latest GC_READY transaction */ + spinlock_t gclock; /* 4: group commit lock */ + int logsize; /* 4: log data area size in byte */ + int lsn; /* 4: end-of-log */ + int clsn; /* 4: clsn */ + int syncpt; /* 4: addr of last syncpt record */ + int sync; /* 4: addr from last logsync() */ + struct list_head synclist; /* 8: logsynclist anchor */ + spinlock_t synclock; /* 4: synclist lock */ + struct lbuf *wqueue; /* 4: log pageout queue */ + int count; /* 4: count */ +} log_t; + +/* + * group commit flag + */ +/* log_t */ +#define logGC_PAGEOUT 0x00000001 + +/* tblock_t/lbuf_t */ +#define tblkGC_QUEUE 0x0001 +#define tblkGC_READY 0x0002 +#define tblkGC_COMMIT 0x0004 +#define tblkGC_COMMITTED 0x0008 +#define tblkGC_EOP 0x0010 +#define tblkGC_FREE 0x0020 +#define tblkGC_LEADER 0x0040 +#define tblkGC_ERROR 0x0080 +#define tblkGC_LAZY 0x0100 // D230860 +#define tblkGC_UNLOCKED 0x0200 // D230860 + +/* + * log cache buffer header + */ +typedef struct lbuf { + log_t *l_log; /* 4: log associated with buffer */ + + /* + * data buffer base area + */ + uint l_flag; /* 4: pageout control flags */ + + struct lbuf *l_wqnext; /* 4: write queue link */ + struct lbuf *l_freelist; /* 4: freelistlink */ + + int l_pn; /* 4: log page number */ + int l_eor; /* 4: log record eor */ + int l_ceor; /* 4: committed log record eor */ + + s64 l_blkno; /* 8: log page block number */ + caddr_t l_ldata; /* 4: data page */ + + wait_queue_head_t l_ioevent; /* 4: i/o done event */ + struct page *l_page; /* The page itself */ +} lbuf_t; + +/* Reuse l_freelist for redrive list */ +#define l_redrive_next l_freelist + +/* + * logsynclist block + * + * common logsyncblk prefix for jbuf_t and tblock_t + */ +typedef struct logsyncblk { + u16 xflag; /* flags */ + u16 flag; /* only meaninful in tblock_t */ + lid_t lid; /* lock id */ + s32 lsn; /* log sequence number */ + struct list_head synclist; /* log sync list link */ +} logsyncblk_t; + +/* + * logsynclist serialization (per log) + */ + +#define LOGSYNC_LOCK_INIT(log) spin_lock_init(&(log)->synclock) +#define LOGSYNC_LOCK(log) spin_lock(&(log)->synclock) +#define LOGSYNC_UNLOCK(log) spin_unlock(&(log)->synclock) + +/* compute the difference in bytes of lsn from sync point */ +#define logdiff(diff, lsn, log)\ +{\ + diff = (lsn) - (log)->syncpt;\ + if (diff < 0)\ + diff += (log)->logsize;\ +} + +extern int lmLogOpen(struct super_block *sb, log_t ** log); +extern int lmLogClose(struct super_block *sb, log_t * log); +extern int lmLogSync(log_t * log, int nosyncwait); +extern int lmLogQuiesce(log_t * log); +extern int lmLogResume(log_t * log, struct super_block *sb); +extern int lmLogFormat(struct super_block *sb, s64 logAddress, int logSize); + +#endif /* _H_JFS_LOGMGR */ diff -Nru a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_metapage.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,686 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * + * Module: jfs/jfs_metapage.c + * + */ + +#include +#include +#include "jfs_incore.h" +#include "jfs_filsys.h" +#include "jfs_metapage.h" +#include "jfs_txnmgr.h" +#include "jfs_debug.h" + +extern struct task_struct *jfsCommitTask; +static unsigned int metapages = 1024; /* ??? Need a better number */ +static unsigned int free_metapages; +static metapage_t *metapage_buf; +static unsigned long meta_order; +static metapage_t *meta_free_list = NULL; +static spinlock_t meta_lock = SPIN_LOCK_UNLOCKED; +static wait_queue_head_t meta_wait; + +#ifdef CONFIG_JFS_STATISTICS +struct { + uint pagealloc; /* # of page allocations */ + uint pagefree; /* # of page frees */ + uint lockwait; /* # of sleeping lock_metapage() calls */ + uint allocwait; /* # of sleeping alloc_metapage() calls */ +} mpStat; +#endif + + +#define HASH_BITS 10 /* This makes hash_table 1 4K page */ +#define HASH_SIZE (1 << HASH_BITS) +static metapage_t **hash_table = NULL; +static unsigned long hash_order; + + +static inline int metapage_locked(struct metapage *mp) +{ + return test_bit(META_locked, &mp->flag); +} + +static inline int trylock_metapage(struct metapage *mp) +{ + return test_and_set_bit(META_locked, &mp->flag); +} + +static inline void unlock_metapage(struct metapage *mp) +{ + clear_bit(META_locked, &mp->flag); + wake_up(&mp->wait); +} + +static void __lock_metapage(struct metapage *mp) +{ + DECLARE_WAITQUEUE(wait, current); + + INCREMENT(mpStat.lockwait); + + add_wait_queue_exclusive(&mp->wait, &wait); + do { + set_current_state(TASK_UNINTERRUPTIBLE); + if (metapage_locked(mp)) { + spin_unlock(&meta_lock); + schedule(); + spin_lock(&meta_lock); + } + } while (trylock_metapage(mp)); + __set_current_state(TASK_RUNNING); + remove_wait_queue(&mp->wait, &wait); +} + +/* needs meta_lock */ +static inline void lock_metapage(struct metapage *mp) +{ + if (trylock_metapage(mp)) + __lock_metapage(mp); +} + +/* We're currently re-evaluating the method we use to write metadata + * pages. Currently, we have to make sure there no dirty buffer_heads + * hanging around after we free the metadata page, since the same + * physical disk blocks may be used in a different address space and we + * can't write old data over the good data. + * + * The best way to do this now is with block_invalidate_page. However, + * this is only available in the newer kernels and is not exported + * to modules. block_flushpage is the next best, but it too is not exported + * to modules. + * + * In a module, about the best we have is generic_buffer_fdatasync. This + * synchronously writes any dirty buffers. This is not optimal, but it will + * keep old dirty buffers from overwriting newer data. + */ +static inline void invalidate_page(metapage_t *mp) +{ +#ifdef MODULE + generic_buffer_fdatasync(mp->mapping->host, mp->index, mp->index + 1); +#else + lock_page(mp->page); + block_flushpage(mp->page, 0); + UnlockPage(mp->page); +#endif +} + +int __init metapage_init(void) +{ + int i; + metapage_t *last = NULL; + metapage_t *mp; + + /* + * Initialize wait queue + */ + init_waitqueue_head(&meta_wait); + + /* + * Allocate the metapage structures + */ + for (meta_order = 0; + ((PAGE_SIZE << meta_order) / sizeof(metapage_t)) < metapages; + meta_order++); + metapages = (PAGE_SIZE << meta_order) / sizeof(metapage_t); + + jFYI(1, ("metapage_init: metapage size = %Zd, metapages = %d\n", + sizeof(metapage_t), metapages)); + + metapage_buf = + (metapage_t *) __get_free_pages(GFP_KERNEL, meta_order); + assert(metapage_buf); + memset(metapage_buf, 0, PAGE_SIZE << meta_order); + + mp = metapage_buf; + for (i = 0; i < metapages; i++, mp++) { + mp->flag = 0; + set_bit(META_free, &mp->flag); + init_waitqueue_head(&mp->wait); + mp->hash_next = last; + last = mp; + } + meta_free_list = last; + free_metapages = metapages; + + /* + * Now the hash list + */ + for (hash_order = 0; + ((PAGE_SIZE << hash_order) / sizeof(void *)) < HASH_SIZE; + hash_order++); + hash_table = + (metapage_t **) __get_free_pages(GFP_KERNEL, hash_order); + assert(hash_table); + memset(hash_table, 0, PAGE_SIZE << hash_order); + + return 0; +} + +void metapage_exit(void) +{ + free_pages((unsigned long) metapage_buf, meta_order); + free_pages((unsigned long) hash_table, hash_order); + metapage_buf = 0; /* This is a signal to the jfsIOwait thread */ +} + +/* + * Get metapage structure from freelist + * + * Caller holds meta_lock + */ +static metapage_t *alloc_metapage(int *dropped_lock) +{ + metapage_t *new; + + *dropped_lock = FALSE; + + /* + * Reserve two metapages for the lazy commit thread. Otherwise + * we may deadlock with holders of metapages waiting for tlocks + * that lazy thread should be freeing. + */ + if ((free_metapages < 3) && (current != jfsCommitTask)) { + INCREMENT(mpStat.allocwait); + *dropped_lock = TRUE; + __SLEEP_COND(meta_wait, (free_metapages > 2), + spin_lock(&meta_lock), spin_unlock(&meta_lock)); + } + + assert(meta_free_list); + + new = meta_free_list; + meta_free_list = new->hash_next; + free_metapages--; + + return new; +} + +/* + * Put metapage on freelist (holding meta_lock) + */ +static inline void __free_metapage(metapage_t * mp) +{ + mp->flag = 0; + set_bit(META_free, &mp->flag); + mp->hash_next = meta_free_list; + meta_free_list = mp; + free_metapages++; + wake_up(&meta_wait); +} + +/* + * Put metapage on freelist (not holding meta_lock) + */ +static inline void free_metapage(metapage_t * mp) +{ + spin_lock(&meta_lock); + __free_metapage(mp); + spin_unlock(&meta_lock); +} + +/* + * Basically same hash as in pagemap.h, but using our hash table + */ +static metapage_t **meta_hash(struct address_space *mapping, + unsigned long index) +{ +#define i (((unsigned long)mapping)/ \ + (sizeof(struct inode) & ~(sizeof(struct inode) -1 ))) +#define s(x) ((x) + ((x) >> HASH_BITS)) + return hash_table + (s(i + index) & (HASH_SIZE - 1)); +#undef i +#undef s +} + +static metapage_t *search_hash(metapage_t ** hash_ptr, + struct address_space *mapping, + unsigned long index) +{ + metapage_t *ptr; + + for (ptr = *hash_ptr; ptr; ptr = ptr->hash_next) { + if ((ptr->mapping == mapping) && (ptr->index == index)) + return ptr; + } + + return NULL; +} + +static void add_to_hash(metapage_t * mp, metapage_t ** hash_ptr) +{ + if (*hash_ptr) + (*hash_ptr)->hash_prev = mp; + + mp->hash_prev = NULL; + mp->hash_next = *hash_ptr; + *hash_ptr = mp; + list_add(&mp->inode_list, &JFS_IP(mp->mapping->host)->mp_list); +} + +static void remove_from_hash(metapage_t * mp, metapage_t ** hash_ptr) +{ + list_del(&mp->inode_list); + + if (mp->hash_prev) + mp->hash_prev->hash_next = mp->hash_next; + else { + assert(*hash_ptr == mp); + *hash_ptr = mp->hash_next; + } + + if (mp->hash_next) + mp->hash_next->hash_prev = mp->hash_prev; +} + +/* + * Direct address space operations + */ + +static int direct_get_block(struct inode *ip, sector_t lblock, + struct buffer_head *bh_result, int create) +{ + if (create) + bh_result->b_state |= (1UL << BH_New); + + map_bh(bh_result, ip->i_sb, lblock); + + return 0; +} + +static int direct_writepage(struct page *page) +{ + return block_write_full_page(page, direct_get_block); +} + +static int direct_readpage(struct file *fp, struct page *page) +{ + return block_read_full_page(page, direct_get_block); +} + +static int direct_prepare_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ + return block_prepare_write(page, from, to, direct_get_block); +} + +static int direct_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping, block, direct_get_block); +} + +struct address_space_operations direct_aops = { + readpage: direct_readpage, + writepage: direct_writepage, + sync_page: block_sync_page, + prepare_write: direct_prepare_write, + commit_write: generic_commit_write, + bmap: direct_bmap, +}; + +metapage_t *__get_metapage(struct inode *inode, + unsigned long lblock, unsigned int size, + int absolute, unsigned long new) +{ + int dropped_lock; + metapage_t **hash_ptr; + int l2BlocksPerPage; + int l2bsize; + struct address_space *mapping; + metapage_t *mp; + unsigned long page_index; + unsigned long page_offset; + + jFYI(1, ("__get_metapage: inode = 0x%p, lblock = 0x%lx\n", + inode, lblock)); + + if (absolute) + mapping = JFS_SBI(inode->i_sb)->direct_mapping; + else + mapping = inode->i_mapping; + + spin_lock(&meta_lock); + + hash_ptr = meta_hash(mapping, lblock); + + mp = search_hash(hash_ptr, mapping, lblock); + if (mp) { + page_found: + if (test_bit(META_discard, &mp->flag)) { + assert(new); /* It's okay to reuse a discarded + * if we expect it to be empty + */ + clear_bit(META_discard, &mp->flag); + } + mp->count++; + jFYI(1, ("__get_metapage: found 0x%p, in hash\n", mp)); + assert(mp->logical_size == size); + lock_metapage(mp); + spin_unlock(&meta_lock); + } else { + l2bsize = inode->i_sb->s_blocksize_bits; + l2BlocksPerPage = PAGE_CACHE_SHIFT - l2bsize; + page_index = lblock >> l2BlocksPerPage; + page_offset = (lblock - (page_index << l2BlocksPerPage)) << + l2bsize; + if ((page_offset + size) > PAGE_SIZE) { + spin_unlock(&meta_lock); + jERROR(1, ("MetaData crosses page boundary!!\n")); + return NULL; + } + + mp = alloc_metapage(&dropped_lock); + if (dropped_lock) { + /* alloc_metapage blocked, we need to search the hash + * again. (The goto is ugly, maybe we'll clean this + * up in the future.) + */ + metapage_t *mp2; + mp2 = search_hash(hash_ptr, mapping, lblock); + if (mp2) { + __free_metapage(mp); + mp = mp2; + goto page_found; + } + } + mp->flag = 0; + lock_metapage(mp); + if (absolute) + set_bit(META_absolute, &mp->flag); + mp->xflag = COMMIT_PAGE; + mp->count = 1; + atomic_set(&mp->nohomeok,0); + mp->mapping = mapping; + mp->index = lblock; + mp->page = 0; + mp->logical_size = size; + add_to_hash(mp, hash_ptr); + spin_unlock(&meta_lock); + + if (new) { + jFYI(1, + ("__get_metapage: Calling grab_cache_page\n")); + mp->page = grab_cache_page(mapping, page_index); + if (!mp->page) { + jERROR(1, ("grab_cache_page failed!\n")); + spin_lock(&meta_lock); + remove_from_hash(mp, hash_ptr); + __free_metapage(mp); + spin_unlock(&meta_lock); + return NULL; + } else + INCREMENT(mpStat.pagealloc); + } else { + jFYI(1, + ("__get_metapage: Calling read_cache_page\n")); + mp->page = + read_cache_page(mapping, lblock, + (filler_t *) mapping->a_ops-> + readpage, NULL); + if (IS_ERR(mp->page)) { + jERROR(1, ("read_cache_page failed!\n")); + spin_lock(&meta_lock); + remove_from_hash(mp, hash_ptr); + __free_metapage(mp); + spin_unlock(&meta_lock); + return NULL; + } else + INCREMENT(mpStat.pagealloc); + lock_page(mp->page); + } + mp->data = (void *) (kmap(mp->page) + page_offset); + } + jFYI(1, ("__get_metapage: returning = 0x%p\n", mp)); + return mp; +} + +void hold_metapage(metapage_t * mp, int force) +{ + spin_lock(&meta_lock); + + mp->count++; + + if (force) { + ASSERT (!(test_bit(META_forced, &mp->flag))); + if (trylock_metapage(mp)) + set_bit(META_forced, &mp->flag); + } else + lock_metapage(mp); + + spin_unlock(&meta_lock); +} + +static void __write_metapage(metapage_t * mp) +{ + struct inode *ip = (struct inode *) mp->mapping->host; + unsigned long page_index; + unsigned long page_offset; + int rc; + int l2bsize = ip->i_sb->s_blocksize_bits; + int l2BlocksPerPage = PAGE_CACHE_SHIFT - l2bsize; + + jFYI(1, ("__write_metapage: mp = 0x%p\n", mp)); + + if (test_bit(META_discard, &mp->flag)) { + /* + * This metadata is no longer valid + */ + clear_bit(META_dirty, &mp->flag); + return; + } + + page_index = mp->page->index; + page_offset = + (mp->index - (page_index << l2BlocksPerPage)) << l2bsize; + + rc = mp->mapping->a_ops->prepare_write(NULL, mp->page, page_offset, + page_offset + + mp->logical_size); + if (rc) { + jERROR(1, ("prepare_write return %d!\n", rc)); + ClearPageUptodate(mp->page); + kunmap(mp->page); + clear_bit(META_dirty, &mp->flag); + return; + } + rc = mp->mapping->a_ops->commit_write(NULL, mp->page, page_offset, + page_offset + + mp->logical_size); + if (rc) { + jERROR(1, ("commit_write returned %d\n", rc)); + } + + clear_bit(META_dirty, &mp->flag); + + jFYI(1, ("__write_metapage done\n")); +} + +void release_metapage(metapage_t * mp) +{ + log_t *log; + struct inode *ip; + + jFYI(1, + ("release_metapage: mp = 0x%p, flag = 0x%lx\n", mp, + mp->flag)); + + spin_lock(&meta_lock); + if (test_bit(META_forced, &mp->flag)) { + clear_bit(META_forced, &mp->flag); + mp->count--; + spin_unlock(&meta_lock); + return; + } + + ip = (struct inode *) mp->mapping->host; + + assert(mp->count); + if (--mp->count || atomic_read(&mp->nohomeok)) { + unlock_metapage(mp); + spin_unlock(&meta_lock); + } else { + remove_from_hash(mp, meta_hash(mp->mapping, mp->index)); + spin_unlock(&meta_lock); + + if (mp->page) { + kunmap(mp->page); + mp->data = 0; + if (test_bit(META_dirty, &mp->flag)) + __write_metapage(mp); + UnlockPage(mp->page); + if (test_bit(META_sync, &mp->flag)) { + sync_metapage(mp); + clear_bit(META_sync, &mp->flag); + } + + if (test_bit(META_discard, &mp->flag)) + invalidate_page(mp); + + page_cache_release(mp->page); + INCREMENT(mpStat.pagefree); + } + + if (mp->lsn) { + /* + * Remove metapage from logsynclist. + */ + log = mp->log; + LOGSYNC_LOCK(log); + mp->log = 0; + mp->lsn = 0; + mp->clsn = 0; + log->count--; + list_del(&mp->synclist); + LOGSYNC_UNLOCK(log); + } + + free_metapage(mp); + } + jFYI(1, ("release_metapage: done\n")); +} + +void invalidate_metapages(struct inode *ip, unsigned long addr, + unsigned long len) +{ + metapage_t **hash_ptr; + unsigned long lblock; + int l2BlocksPerPage = PAGE_CACHE_SHIFT - ip->i_sb->s_blocksize_bits; + struct address_space *mapping = ip->i_mapping; + metapage_t *mp; +#ifndef MODULE + struct page *page; +#endif + + /* + * First, mark metapages to discard. They will eventually be + * released, but should not be written. + */ + for (lblock = addr; lblock < addr + len; + lblock += 1 << l2BlocksPerPage) { + hash_ptr = meta_hash(mapping, lblock); + spin_lock(&meta_lock); + mp = search_hash(hash_ptr, mapping, lblock); + if (mp) { + set_bit(META_discard, &mp->flag); + spin_unlock(&meta_lock); + /* + * If in the metapage cache, we've got the page locked + */ +#ifdef MODULE + UnlockPage(mp->page); + generic_buffer_fdatasync(mp->mapping->host, mp->index, + mp->index+1); + lock_page(mp->page); +#else + block_flushpage(mp->page, 0); +#endif + } else { + spin_unlock(&meta_lock); +#ifdef MODULE + generic_buffer_fdatasync(ip, lblock << l2BlocksPerPage, + (lblock + 1) << l2BlocksPerPage); +#else + page = find_lock_page(mapping, + lblock >> l2BlocksPerPage); + if (page) { + block_flushpage(page, 0); + UnlockPage(page); + } +#endif + } + } +} + +void invalidate_inode_metapages(struct inode *inode) +{ + struct list_head *ptr; + metapage_t *mp; + + spin_lock(&meta_lock); + list_for_each(ptr, &JFS_IP(inode)->mp_list) { + mp = list_entry(ptr, metapage_t, inode_list); + clear_bit(META_dirty, &mp->flag); + set_bit(META_discard, &mp->flag); + kunmap(mp->page); + UnlockPage(mp->page); + page_cache_release(mp->page); + INCREMENT(mpStat.pagefree); + mp->data = 0; + mp->page = 0; + } + spin_unlock(&meta_lock); + truncate_inode_pages(inode->i_mapping, 0); +} + +#ifdef CONFIG_JFS_STATISTICS +int jfs_mpstat_read(char *buffer, char **start, off_t offset, int length, + int *eof, void *data) +{ + int len = 0; + off_t begin; + + len += sprintf(buffer, + "JFS Metapage statistics\n" + "=======================\n" + "metapages in use = %d\n" + "page allocations = %d\n" + "page frees = %d\n" + "lock waits = %d\n" + "allocation waits = %d\n", + metapages - free_metapages, + mpStat.pagealloc, + mpStat.pagefree, + mpStat.lockwait, + mpStat.allocwait); + + begin = offset; + *start = buffer + begin; + len -= begin; + + if (len > length) + len = length; + else + *eof = 1; + + if (len < 0) + len = 0; + + return len; +} +#endif diff -Nru a/fs/jfs/jfs_metapage.h b/fs/jfs/jfs_metapage.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_metapage.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,123 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ +#ifndef _H_JFS_METAPAGE +#define _H_JFS_METAPAGE + +#include + +typedef struct metapage { + /* Common logsyncblk prefix (see jfs_logmgr.h) */ + u16 xflag; + u16 unused; + lid_t lid; + int lsn; + struct list_head synclist; + /* End of logsyncblk prefix */ + + unsigned long flag; /* See Below */ + unsigned long count; /* Reference count */ + void *data; /* Data pointer */ + + /* list management stuff */ + struct metapage *hash_prev; + struct metapage *hash_next; /* Also used for free list */ + + struct list_head inode_list; /* per-inode metapage list */ + /* + * mapping & index become redundant, but we need these here to + * add the metapage to the hash before we have the real page + */ + struct address_space *mapping; + unsigned long index; + wait_queue_head_t wait; + + /* implementation */ + struct page *page; + unsigned long logical_size; + + /* Journal management */ + int clsn; + atomic_t nohomeok; + struct jfs_log *log; +} metapage_t; + +/* + * Direct-access address space operations + */ +extern struct address_space_operations direct_aops; + +/* metapage flag */ +#define META_locked 0 +#define META_absolute 1 +#define META_free 2 +#define META_dirty 3 +#define META_sync 4 +#define META_discard 5 +#define META_forced 6 + +#define mark_metapage_dirty(mp) set_bit(META_dirty, &(mp)->flag) + +/* function prototypes */ +extern metapage_t *__get_metapage(struct inode *inode, + unsigned long lblock, unsigned int size, + int absolute, unsigned long new); + +#define read_metapage(inode, lblock, size, absolute)\ + __get_metapage(inode, lblock, size, absolute, FALSE) + +#define get_metapage(inode, lblock, size, absolute)\ + __get_metapage(inode, lblock, size, absolute, TRUE) + +extern void release_metapage(metapage_t *); + +#define flush_metapage(mp) \ +{\ + set_bit(META_dirty, &(mp)->flag);\ + set_bit(META_sync, &(mp)->flag);\ + release_metapage(mp);\ +} + +#define sync_metapage(mp) \ + generic_buffer_fdatasync((struct inode *)mp->mapping->host,\ + mp->page->index, mp->page->index + 1) + +#define write_metapage(mp) \ +{\ + set_bit(META_dirty, &(mp)->flag);\ + release_metapage(mp);\ +} + +#define discard_metapage(mp) \ +{\ + clear_bit(META_dirty, &(mp)->flag);\ + set_bit(META_discard, &(mp)->flag);\ + release_metapage(mp);\ +} + +extern void hold_metapage(metapage_t *, int); + +/* + * This routine uses hash to explicitly find small number of pages + */ +extern void invalidate_metapages(struct inode *, unsigned long, unsigned long); + +/* + * This one uses mp_list to invalidate all pages for an inode + */ +extern void invalidate_inode_metapages(struct inode *inode); +#endif /* _H_JFS_METAPAGE */ diff -Nru a/fs/jfs/jfs_mount.c b/fs/jfs/jfs_mount.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_mount.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,541 @@ +/* + * MODULE_NAME: jfs_mount.c + * + * COMPONENT_NAME: sysjfs + * + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ + +/* + * Change History : + * + */ + +/* + * Module: jfs_mount.c + * + * note: file system in transition to aggregate/fileset: + * + * file system mount is interpreted as the mount of aggregate, + * if not already mounted, and mount of the single/only fileset in + * the aggregate; + * + * a file system/aggregate is represented by an internal inode + * (aka mount inode) initialized with aggregate superblock; + * each vfs represents a fileset, and points to its "fileset inode + * allocation map inode" (aka fileset inode): + * (an aggregate itself is structured recursively as a filset: + * an internal vfs is constructed and points to its "fileset inode + * allocation map inode" (aka aggregate inode) where each inode + * represents a fileset inode) so that inode number is mapped to + * on-disk inode in uniform way at both aggregate and fileset level; + * + * each vnode/inode of a fileset is linked to its vfs (to facilitate + * per fileset inode operations, e.g., unmount of a fileset, etc.); + * each inode points to the mount inode (to facilitate access to + * per aggregate information, e.g., block size, etc.) as well as + * its file set inode. + * + * aggregate + * ipmnt + * mntvfs -> fileset ipimap+ -> aggregate ipbmap -> aggregate ipaimap; + * fileset vfs -> vp(1) <-> ... <-> vp(n) <->vproot; + */ + +#include +#include "jfs_incore.h" +#include "jfs_filsys.h" +#include "jfs_superblock.h" +#include "jfs_dmap.h" +#include "jfs_imap.h" +#include "jfs_metapage.h" +#include "jfs_debug.h" + + +/* + * forward references + */ +static int chkSuper(struct super_block *); +static int logMOUNT(struct super_block *sb); + +/* + * NAME: jfs_mount(sb) + * + * FUNCTION: vfs_mount() + * + * PARAMETER: sb - super block + * + * RETURN: EBUSY - device already mounted or open for write + * EBUSY - cvrdvp already mounted; + * EBUSY - mount table full + * ENOTDIR - cvrdvp not directory on a device mount + * ENXIO - device open failure + */ +int jfs_mount(struct super_block *sb) +{ + int rc = 0; /* Return code */ + struct jfs_sb_info *sbi = JFS_SBI(sb); + struct inode *ipaimap = NULL; + struct inode *ipaimap2 = NULL; + struct inode *ipimap = NULL; + struct inode *ipbmap = NULL; + + jFYI(1, ("\nMount JFS\n")); + + /* + * read/validate superblock + * (initialize mount inode from the superblock) + */ + if ((rc = chkSuper(sb))) { + goto errout20; + } + + ipaimap = diReadSpecial(sb, AGGREGATE_I); + if (ipaimap == NULL) { + jERROR(1, ("jfs_mount: Faild to read AGGREGATE_I\n")); + rc = EIO; + goto errout20; + } + sbi->ipaimap = ipaimap; + + jFYI(1, ("jfs_mount: ipaimap:0x%p\n", ipaimap)); + + /* + * initialize aggregate inode allocation map + */ + if ((rc = diMount(ipaimap))) { + jERROR(1, + ("jfs_mount: diMount(ipaimap) failed w/rc = %d\n", + rc)); + goto errout21; + } + + /* + * open aggregate block allocation map + */ + ipbmap = diReadSpecial(sb, BMAP_I); + if (ipbmap == NULL) { + rc = EIO; + goto errout22; + } + + jFYI(1, ("jfs_mount: ipbmap:0x%p\n", ipbmap)); + + sbi->ipbmap = ipbmap; + + /* + * initialize aggregate block allocation map + */ + if ((rc = dbMount(ipbmap))) { + jERROR(1, ("jfs_mount: dbMount failed w/rc = %d\n", rc)); + goto errout22; + } + + /* + * open the secondary aggregate inode allocation map + * + * This is a duplicate of the aggregate inode allocation map. + * + * hand craft a vfs in the same fashion as we did to read ipaimap. + * By adding INOSPEREXT (32) to the inode number, we are telling + * diReadSpecial that we are reading from the secondary aggregate + * inode table. This also creates a unique entry in the inode hash + * table. + */ + if ((sbi->mntflag & JFS_BAD_SAIT) == 0) { + ipaimap2 = diReadSpecial(sb, AGGREGATE_I + INOSPEREXT); + if (ipaimap2 == 0) { + jERROR(1, + ("jfs_mount: Faild to read AGGREGATE_I\n")); + rc = EIO; + goto errout35; + } + sbi->ipaimap2 = ipaimap2; + + jFYI(1, ("jfs_mount: ipaimap2:0x%p\n", ipaimap2)); + + /* + * initialize secondary aggregate inode allocation map + */ + if ((rc = diMount(ipaimap2))) { + jERROR(1, + ("jfs_mount: diMount(ipaimap2) failed, rc = %d\n", + rc)); + goto errout35; + } + } else + /* Secondary aggregate inode table is not valid */ + sbi->ipaimap2 = 0; + + /* + * mount (the only/single) fileset + */ + /* + * open fileset inode allocation map (aka fileset inode) + */ + ipimap = diReadSpecial(sb, FILESYSTEM_I); + if (ipimap == NULL) { + jERROR(1, ("jfs_mount: Failed to read FILESYSTEM_I\n")); + /* open fileset secondary inode allocation map */ + rc = EIO; + goto errout40; + } + jFYI(1, ("jfs_mount: ipimap:0x%p\n", ipimap)); + + /* map further access of per fileset inodes by the fileset inode */ + sbi->ipimap = ipimap; + + /* initialize fileset inode allocation map */ + if ((rc = diMount(ipimap))) { + jERROR(1, ("jfs_mount: diMount failed w/rc = %d\n", rc)); + goto errout41; + } + + jFYI(1, ("Mount JFS Complete.\n")); + goto out; + + /* + * unwind on error + */ +//errout42: /* close fileset inode allocation map */ + diUnmount(ipimap, 1); + + errout41: /* close fileset inode allocation map inode */ + diFreeSpecial(ipimap); + + errout40: /* fileset closed */ + + /* close secondary aggregate inode allocation map */ + if (ipaimap2) { + diUnmount(ipaimap2, 1); + diFreeSpecial(ipaimap2); + } + + errout35: + + /* close aggregate block allocation map */ + dbUnmount(ipbmap, 1); + diFreeSpecial(ipbmap); + + errout22: /* close aggregate inode allocation map */ + + diUnmount(ipaimap, 1); + + errout21: /* close aggregate inodes */ + diFreeSpecial(ipaimap); + errout20: /* aggregate closed */ + + out: + + if (rc) { + jERROR(1, ("Mount JFS Failure: %d\n", rc)); + } + return rc; +} + +/* + * NAME: jfs_mount_rw(sb, remount) + * + * FUNCTION: Completes read-write mount, or remounts read-only volume + * as read-write + */ +int jfs_mount_rw(struct super_block *sb, int remount) +{ + struct jfs_sb_info *sbi = JFS_SBI(sb); + log_t *log; + int rc; + + /* + * If we are re-mounting a previously read-only volume, we want to + * re-read the inode and block maps, since fsck.jfs may have updated + * them. + */ + if (remount) { + if (chkSuper(sb) || (sbi->state != FM_CLEAN)) + return -EINVAL; + + truncate_inode_pages(sbi->ipimap->i_mapping, 0); + truncate_inode_pages(sbi->ipbmap->i_mapping, 0); + diUnmount(sbi->ipimap, 1); + if ((rc = diMount(sbi->ipimap))) { + jERROR(1,("jfs_mount_rw: diMount failed!\n")); + return rc; + } + + dbUnmount(sbi->ipbmap, 1); + if ((rc = dbMount(sbi->ipbmap))) { + jERROR(1,("jfs_mount_rw: dbMount failed!\n")); + return rc; + } + } +#ifdef _STILL_TO_PORT + /* + * get log device associated with the fs being mounted; + */ + if (ipmnt->i_mntflag & JFS_INLINELOG) { + vfsp->vfs_logVPB = vfsp->vfs_hVPB; + vfsp->vfs_logvpfs = vfsp->vfs_vpfsi; + } else if (vfsp->vfs_logvpfs == NULL) { + /* + * XXX: there's only one external log per system; + */ + jERROR(1, ("jfs_mount: Mount Failure! No Log Device.\n")); + goto errout30; + } + + logdev = vfsp->vfs_logvpfs->vpi_unit; + ipmnt->i_logdev = logdev; +#endif /* _STILL_TO_PORT */ + + /* + * open/initialize log + */ + if ((rc = lmLogOpen(sb, &log))) + return rc; + + JFS_SBI(sb)->log = log; + + /* + * update file system superblock; + */ + if ((rc = updateSuper(sb, FM_MOUNT))) { + jERROR(1, + ("jfs_mount: updateSuper failed w/rc = %d\n", rc)); + lmLogClose(sb, log); + JFS_SBI(sb)->log = 0; + return rc; + } + + /* + * write MOUNT log record of the file system + */ + logMOUNT(sb); + + return rc; +} + +/* + * chkSuper() + * + * validate the superblock of the file system to be mounted and + * get the file system parameters. + * + * returns + * 0 with fragsize set if check successful + * error code if not successful + */ +static int chkSuper(struct super_block *sb) +{ + int rc = 0; + metapage_t *mp; + struct jfs_sb_info *sbi = JFS_SBI(sb); + struct jfs_superblock *j_sb; + int AIM_bytesize, AIT_bytesize; + int expected_AIM_bytesize, expected_AIT_bytesize; + s64 AIM_byte_addr, AIT_byte_addr, fsckwsp_addr; + s64 byte_addr_diff0, byte_addr_diff1; + s32 bsize; + + if ((rc = readSuper(sb, &mp))) + return rc; + j_sb = (struct jfs_superblock *) (mp->data); + + /* + * validate superblock + */ + /* validate fs signature */ + if (strncmp(j_sb->s_magic, JFS_MAGIC, 4) || + j_sb->s_version != cpu_to_le32(JFS_VERSION)) { + //rc = EFORMAT; + rc = EINVAL; + goto out; + } + + bsize = le32_to_cpu(j_sb->s_bsize); +#ifdef _JFS_4K + if (bsize != PSIZE) { + jERROR(1, ("Currently only 4K block size supported!\n")); + rc = EINVAL; + goto out; + } +#endif /* _JFS_4K */ + + jFYI(1, ("superblock: flag:0x%08x state:0x%08x size:0x%Lx\n", + le32_to_cpu(j_sb->s_flag), le32_to_cpu(j_sb->s_state), + (unsigned long long) le64_to_cpu(j_sb->s_size))); + + /* validate the descriptors for Secondary AIM and AIT */ + if ((j_sb->s_flag & cpu_to_le32(JFS_BAD_SAIT)) != + cpu_to_le32(JFS_BAD_SAIT)) { + expected_AIM_bytesize = 2 * PSIZE; + AIM_bytesize = lengthPXD(&(j_sb->s_aim2)) * bsize; + expected_AIT_bytesize = 4 * PSIZE; + AIT_bytesize = lengthPXD(&(j_sb->s_ait2)) * bsize; + AIM_byte_addr = addressPXD(&(j_sb->s_aim2)) * bsize; + AIT_byte_addr = addressPXD(&(j_sb->s_ait2)) * bsize; + byte_addr_diff0 = AIT_byte_addr - AIM_byte_addr; + fsckwsp_addr = addressPXD(&(j_sb->s_fsckpxd)) * bsize; + byte_addr_diff1 = fsckwsp_addr - AIT_byte_addr; + if ((AIM_bytesize != expected_AIM_bytesize) || + (AIT_bytesize != expected_AIT_bytesize) || + (byte_addr_diff0 != AIM_bytesize) || + (byte_addr_diff1 <= AIT_bytesize)) + j_sb->s_flag |= cpu_to_le32(JFS_BAD_SAIT); + } + + /* in release 1, the flag MUST reflect inline log, and group commit */ + if ((j_sb->s_flag & cpu_to_le32(JFS_INLINELOG)) != + cpu_to_le32(JFS_INLINELOG)) + j_sb->s_flag |= cpu_to_le32(JFS_INLINELOG); + if ((j_sb->s_flag & cpu_to_le32(JFS_GROUPCOMMIT)) != + cpu_to_le32(JFS_GROUPCOMMIT)) + j_sb->s_flag |= cpu_to_le32(JFS_GROUPCOMMIT); + jFYI(0, ("superblock: flag:0x%08x state:0x%08x size:0x%Lx\n", + le32_to_cpu(j_sb->s_flag), le32_to_cpu(j_sb->s_state), + (unsigned long long) le64_to_cpu(j_sb->s_size))); + + /* validate fs state */ + if (j_sb->s_state != cpu_to_le32(FM_CLEAN) && + !(sb->s_flags & MS_RDONLY)) { + jERROR(1, + ("jfs_mount: Mount Failure: File System Dirty.\n")); + rc = EINVAL; + goto out; + } + + sbi->state = le32_to_cpu(j_sb->s_state); + sbi->mntflag = le32_to_cpu(j_sb->s_flag); + + /* + * JFS always does I/O by 4K pages. Don't tell the buffer cache + * that we use anything else (leave s_blocksize alone). + */ + sbi->bsize = bsize; + sbi->l2bsize = le16_to_cpu(j_sb->s_l2bsize); + + /* + * For now, ignore s_pbsize, l2bfactor. All I/O going through buffer + * cache. + */ + sbi->nbperpage = PSIZE >> sbi->l2bsize; + sbi->l2nbperpage = L2PSIZE - sbi->l2bsize; + sbi->l2niperblk = sbi->l2bsize - L2DISIZE; + if (sbi->mntflag & JFS_INLINELOG) + sbi->logpxd = j_sb->s_logpxd; + sbi->ait2 = j_sb->s_ait2; + + out: + release_metapage(mp); + + return rc; +} + + +/* + * updateSuper() + * + * update synchronously superblock if it is mounted read-write. + */ +int updateSuper(struct super_block *sb, uint state) +{ + int rc; + metapage_t *mp; + struct jfs_superblock *j_sb; + + /* + * Only fsck can fix dirty state + */ + if (JFS_SBI(sb)->state == FM_DIRTY) + return 0; + + if ((rc = readSuper(sb, &mp))) + return rc; + + j_sb = (struct jfs_superblock *) (mp->data); + + j_sb->s_state = cpu_to_le32(state); + JFS_SBI(sb)->state = state; + + if (state == FM_MOUNT) { + /* record log's dev_t and mount serial number */ + j_sb->s_logdev = + cpu_to_le32(kdev_t_to_nr(JFS_SBI(sb)->log->dev)); + j_sb->s_logserial = cpu_to_le32(JFS_SBI(sb)->log->serial); + } else if (state == FM_CLEAN) { + /* + * If this volume is shared with OS/2, OS/2 will need to + * recalculate DASD usage, since we don't deal with it. + */ + if (j_sb->s_flag & cpu_to_le32(JFS_DASD_ENABLED)) + j_sb->s_flag |= cpu_to_le32(JFS_DASD_PRIME); + } + + flush_metapage(mp); + + return 0; +} + + +/* + * readSuper() + * + * read superblock by raw sector address + */ +int readSuper(struct super_block *sb, metapage_t ** mpp) +{ + /* read in primary superblock */ + *mpp = read_metapage(JFS_SBI(sb)->direct_inode, + SUPER1_OFF >> sb->s_blocksize_bits, PSIZE, 1); + if (*mpp == NULL) { + /* read in secondary/replicated superblock */ + *mpp = read_metapage(JFS_SBI(sb)->direct_inode, + SUPER2_OFF >> sb->s_blocksize_bits, + PSIZE, 1); + } + return *mpp ? 0 : 1; +} + + +/* + * logMOUNT() + * + * function: write a MOUNT log record for file system. + * + * MOUNT record keeps logredo() from processing log records + * for this file system past this point in log. + * it is harmless if mount fails. + * + * note: MOUNT record is at aggregate level, not at fileset level, + * since log records of previous mounts of a fileset + * (e.g., AFTER record of extent allocation) have to be processed + * to update block allocation map at aggregate level. + */ +static int logMOUNT(struct super_block *sb) +{ + log_t *log = JFS_SBI(sb)->log; + lrd_t lrd; + + lrd.logtid = 0; + lrd.backchain = 0; + lrd.type = cpu_to_le16(LOG_MOUNT); + lrd.length = 0; + lrd.aggregate = cpu_to_le32(kdev_t_to_nr(sb->s_dev)); + lmLog(log, NULL, &lrd, NULL); + + return 0; +} diff -Nru a/fs/jfs/jfs_superblock.h b/fs/jfs/jfs_superblock.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_superblock.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,143 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * + */ +#ifndef _H_JFS_SUPERBLOCK +#define _H_JFS_SUPERBLOCK +/* + * jfs_superblock.h + */ + +/* + * make the magic number something a human could read + */ +#define JFS_MAGIC "JFS1" /* Magic word: Version 1 */ + +#define JFS_VERSION 1 /* Version number: Version 1 */ + +#define LV_NAME_SIZE 11 /* MUST BE 11 for OS/2 boot sector */ + +/* + * aggregate superblock + * + * The name superblock is too close to super_block, so the name has been + * changed to jfs_superblock. The utilities are still using the old name. + */ +struct jfs_superblock { + char s_magic[4]; /* 4: magic number */ + u32 s_version; /* 4: version number */ + + s64 s_size; /* 8: aggregate size in hardware/LVM blocks; + * VFS: number of blocks + */ + s32 s_bsize; /* 4: aggregate block size in bytes; + * VFS: fragment size + */ + s16 s_l2bsize; /* 2: log2 of s_bsize */ + s16 s_l2bfactor; /* 2: log2(s_bsize/hardware block size) */ + s32 s_pbsize; /* 4: hardware/LVM block size in bytes */ + s16 s_l2pbsize; /* 2: log2 of s_pbsize */ + s16 pad; /* 2: padding necessary for alignment */ + + u32 s_agsize; /* 4: allocation group size in aggr. blocks */ + + u32 s_flag; /* 4: aggregate attributes: + * see jfs_filsys.h + */ + u32 s_state; /* 4: mount/unmount/recovery state: + * see jfs_filsys.h + */ + s32 s_compress; /* 4: > 0 if data compression */ + + pxd_t s_ait2; /* 8: first extent of secondary + * aggregate inode table + */ + + pxd_t s_aim2; /* 8: first extent of secondary + * aggregate inode map + */ + u32 s_logdev; /* 4: device address of log */ + s32 s_logserial; /* 4: log serial number at aggregate mount */ + pxd_t s_logpxd; /* 8: inline log extent */ + + pxd_t s_fsckpxd; /* 8: inline fsck work space extent */ + + struct timestruc_t s_time; /* 8: time last updated */ + + s32 s_fsckloglen; /* 4: Number of filesystem blocks reserved for + * the fsck service log. + * N.B. These blocks are divided among the + * versions kept. This is not a per + * version size. + * N.B. These blocks are included in the + * length field of s_fsckpxd. + */ + s8 s_fscklog; /* 1: which fsck service log is most recent + * 0 => no service log data yet + * 1 => the first one + * 2 => the 2nd one + */ + char s_fpack[11]; /* 11: file system volume name + * N.B. This must be 11 bytes to + * conform with the OS/2 BootSector + * requirements + */ + + /* extendfs() parameter under s_state & FM_EXTENDFS */ + s64 s_xsize; /* 8: extendfs s_size */ + pxd_t s_xfsckpxd; /* 8: extendfs fsckpxd */ + pxd_t s_xlogpxd; /* 8: extendfs logpxd */ + /* - 128 byte boundary - */ + + /* + * DFS VFS support (preliminary) + */ + char s_attach; /* 1: VFS: flag: set when aggregate is attached + */ + u8 rsrvd4[7]; /* 7: reserved - set to 0 */ + + u64 totalUsable; /* 8: VFS: total of 1K blocks which are + * available to "normal" (non-root) users. + */ + u64 minFree; /* 8: VFS: # of 1K blocks held in reserve for + * exclusive use of root. This value can be 0, + * and if it is then totalUsable will be equal + * to # of blocks in aggregate. I believe this + * means that minFree + totalUsable = # blocks. + * In that case, we don't need to store both + * totalUsable and minFree since we can compute + * one from the other. I would guess minFree + * would be the one we should store, and + * totalUsable would be the one we should + * compute. (Just a guess...) + */ + + u64 realFree; /* 8: VFS: # of free 1K blocks can be used by + * "normal" users. It may be this is something + * we should compute when asked for instead of + * storing in the superblock. I don't know how + * often this information is needed. + */ + /* + * graffiti area + */ +}; + +extern int readSuper(struct super_block *, struct metapage **); +extern int updateSuper(struct super_block *, uint); + +#endif /*_H_JFS_SUPERBLOCK */ diff -Nru a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_txnmgr.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,3021 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ + +/* + * jfs_txnmgr.c: transaction manager + * + * notes: + * transaction starts with txBegin() and ends with txCommit() + * or txAbort(). + * + * tlock is acquired at the time of update; + * (obviate scan at commit time for xtree and dtree) + * tlock and mp points to each other; + * (no hashlist for mp -> tlock). + * + * special cases: + * tlock on in-memory inode: + * in-place tlock in the in-memory inode itself; + * converted to page lock by iWrite() at commit time. + * + * tlock during write()/mmap() under anonymous transaction (tid = 0): + * transferred (?) to transaction at commit time. + * + * use the page itself to update allocation maps + * (obviate intermediate replication of allocation/deallocation data) + * hold on to mp+lock thru update of maps + */ + + +#include +#include +#include +#include +#include "jfs_incore.h" +#include "jfs_filsys.h" +#include "jfs_metapage.h" +#include "jfs_dinode.h" +#include "jfs_imap.h" +#include "jfs_dmap.h" +#include "jfs_superblock.h" +#include "jfs_debug.h" + +/* + * transaction management structures + */ +static struct { + /* tblock */ + int freetid; /* 4: index of a free tid structure */ + wait_queue_head_t freewait; /* 4: eventlist of free tblock */ + + /* tlock */ + int freelock; /* 4: index first free lock word */ + wait_queue_head_t freelockwait; /* 4: eventlist of free tlock */ + wait_queue_head_t lowlockwait; /* 4: eventlist of ample tlocks */ + int tlocksInUse; /* 4: Number of tlocks in use */ + spinlock_t LazyLock; /* 4: synchronize sync_queue & unlock_queue */ +/* tblock_t *sync_queue; * 4: Transactions waiting for data sync */ + tblock_t *unlock_queue; /* 4: Transactions waiting to be released */ + tblock_t *unlock_tail; /* 4: Tail of unlock_queue */ + struct list_head anon_list; /* inodes having anonymous txns */ + struct list_head anon_list2; /* inodes having anonymous txns + that couldn't be sync'ed */ +} TxAnchor; + +static int nTxBlock = 512; /* number of transaction blocks */ +struct tblock *TxBlock; /* transaction block table */ + +static int nTxLock = 2048; /* number of transaction locks */ +static int TxLockLWM = 2048*.4; /* Low water mark for number of txLocks used */ +static int TxLockHWM = 2048*.8; /* High water mark for number of txLocks used */ +struct tlock *TxLock; /* transaction lock table */ +static int TlocksLow = 0; /* Indicates low number of available tlocks */ + + +/* + * transaction management lock + */ +static spinlock_t jfsTxnLock = SPIN_LOCK_UNLOCKED; + +#define TXN_LOCK() spin_lock(&jfsTxnLock) +#define TXN_UNLOCK() spin_unlock(&jfsTxnLock) + +#define LAZY_LOCK_INIT() spin_lock_init(&TxAnchor.LazyLock); +#define LAZY_LOCK(flags) spin_lock_irqsave(&TxAnchor.LazyLock, flags) +#define LAZY_UNLOCK(flags) spin_unlock_irqrestore(&TxAnchor.LazyLock, flags) + +/* + * Retry logic exist outside these macros to protect from spurrious wakeups. + */ +static inline void TXN_SLEEP_DROP_LOCK(wait_queue_head_t * event) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(event, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + TXN_UNLOCK(); + schedule(); + current->state = TASK_RUNNING; + remove_wait_queue(event, &wait); +} + +#define TXN_SLEEP(event)\ +{\ + TXN_SLEEP_DROP_LOCK(event);\ + TXN_LOCK();\ +} + +#define TXN_WAKEUP(event) wake_up_all(event) + + +/* + * statistics + */ +struct { + tid_t maxtid; /* 4: biggest tid ever used */ + lid_t maxlid; /* 4: biggest lid ever used */ + int ntid; /* 4: # of transactions performed */ + int nlid; /* 4: # of tlocks acquired */ + int waitlock; /* 4: # of tlock wait */ +} stattx; + + +/* + * external references + */ +extern int lmGroupCommit(log_t * log, tblock_t * tblk); +extern void lmSync(log_t *); +extern int readSuper(struct super_block *sb, metapage_t ** bpp); +extern int jfs_commit_inode(struct inode *, int); +extern int jfs_thread_stopped(void); + +extern struct task_struct *jfsCommitTask; +extern struct completion jfsIOwait; +extern struct task_struct *jfsSyncTask; + +/* + * forward references + */ +int diLog(log_t * log, tblock_t * tblk, lrd_t * lrd, tlock_t * tlck, + commit_t * cd); +int dataLog(log_t * log, tblock_t * tblk, lrd_t * lrd, tlock_t * tlck); +void dtLog(log_t * log, tblock_t * tblk, lrd_t * lrd, tlock_t * tlck); +void inlineLog(log_t * log, tblock_t * tblk, lrd_t * lrd, tlock_t * tlck); +void mapLog(log_t * log, tblock_t * tblk, lrd_t * lrd, tlock_t * tlck); +void txAbortCommit(commit_t * cd, int exval); +static void txAllocPMap(struct inode *ip, maplock_t * maplock, + tblock_t * tblk); +void txForce(tblock_t * tblk); +static int txLog(log_t * log, tblock_t * tblk, commit_t * cd); +int txMoreLock(void); +static void txUpdateMap(tblock_t * tblk); +static void txRelease(tblock_t * tblk); +void xtLog(log_t * log, tblock_t * tblk, lrd_t * lrd, tlock_t * tlck); +static void LogSyncRelease(metapage_t * mp); + +/* + * transaction block/lock management + * --------------------------------- + */ + +/* + * Get a transaction lock from the free list. If the number in use is + * greater than the high water mark, wake up the sync daemon. This should + * free some anonymous transaction locks. (TXN_LOCK must be held.) + */ +static lid_t txLockAlloc(void) +{ + lid_t lid; + + while (!(lid = TxAnchor.freelock)) + TXN_SLEEP(&TxAnchor.freelockwait); + TxAnchor.freelock = TxLock[lid].next; + HIGHWATERMARK(stattx.maxlid, lid); + if ((++TxAnchor.tlocksInUse > TxLockHWM) && (TlocksLow == 0)) { + jEVENT(0,("txLockAlloc TlocksLow\n")); + TlocksLow = 1; + wake_up_process(jfsSyncTask); + } + + return lid; +} + +static void txLockFree(lid_t lid) +{ + TxLock[lid].next = TxAnchor.freelock; + TxAnchor.freelock = lid; + TxAnchor.tlocksInUse--; + if (TlocksLow && (TxAnchor.tlocksInUse < TxLockLWM)) { + jEVENT(0,("txLockFree TlocksLow no more\n")); + TlocksLow = 0; + TXN_WAKEUP(&TxAnchor.lowlockwait); + } + TXN_WAKEUP(&TxAnchor.freelockwait); +} + +/* + * NAME: txInit() + * + * FUNCTION: initialize transaction management structures + * + * RETURN: + * + * serialization: single thread at jfs_init() + */ +int txInit(void) +{ + int k, size; + + /* + * initialize transaction block (tblock) table + * + * transaction id (tid) = tblock index + * tid = 0 is reserved. + */ + size = sizeof(tblock_t) * nTxBlock; + TxBlock = (tblock_t *) vmalloc(size); + if (TxBlock == NULL) + return ENOMEM; + + for (k = 1; k < nTxBlock - 1; k++) { + TxBlock[k].next = k + 1; + init_waitqueue_head(&TxBlock[k].gcwait); + init_waitqueue_head(&TxBlock[k].waitor); + } + TxBlock[k].next = 0; + init_waitqueue_head(&TxBlock[k].gcwait); + init_waitqueue_head(&TxBlock[k].waitor); + + TxAnchor.freetid = 1; + init_waitqueue_head(&TxAnchor.freewait); + + stattx.maxtid = 1; /* statistics */ + + /* + * initialize transaction lock (tlock) table + * + * transaction lock id = tlock index + * tlock id = 0 is reserved. + */ + size = sizeof(tlock_t) * nTxLock; + TxLock = (tlock_t *) vmalloc(size); + if (TxLock == NULL) { + vfree(TxBlock); + return ENOMEM; + } + + /* initialize tlock table */ + for (k = 1; k < nTxLock - 1; k++) + TxLock[k].next = k + 1; + TxLock[k].next = 0; + init_waitqueue_head(&TxAnchor.freelockwait); + init_waitqueue_head(&TxAnchor.lowlockwait); + + TxAnchor.freelock = 1; + TxAnchor.tlocksInUse = 0; + INIT_LIST_HEAD(&TxAnchor.anon_list); + INIT_LIST_HEAD(&TxAnchor.anon_list2); + + stattx.maxlid = 1; /* statistics */ + + return 0; +} + +/* + * NAME: txExit() + * + * FUNCTION: clean up when module is unloaded + */ +void txExit(void) +{ + vfree(TxLock); + TxLock = 0; + vfree(TxBlock); + TxBlock = 0; +} + + +/* + * NAME: txBegin() + * + * FUNCTION: start a transaction. + * + * PARAMETER: sb - superblock + * flag - force for nested tx; + * + * RETURN: tid - transaction id + * + * note: flag force allows to start tx for nested tx + * to prevent deadlock on logsync barrier; + */ +tid_t txBegin(struct super_block *sb, int flag) +{ + tid_t t; + tblock_t *tblk; + log_t *log; + + jFYI(1, ("txBegin: flag = 0x%x\n", flag)); + log = (log_t *) JFS_SBI(sb)->log; + + TXN_LOCK(); + + retry: + if (flag != COMMIT_FORCE) { + /* + * synchronize with logsync barrier + */ + if (log->syncbarrier) { + TXN_SLEEP(&log->syncwait); + goto retry; + } + } + if (flag == 0) { + /* + * Don't begin transaction if we're getting starved for tlocks + * unless COMMIT_FORCE (imap changes) or COMMIT_INODE (which + * may ultimately free tlocks) + */ + if (TlocksLow) { + TXN_SLEEP(&TxAnchor.lowlockwait); + goto retry; + } + } + + /* + * allocate transaction id/block + */ + if ((t = TxAnchor.freetid) == 0) { + jFYI(1, ("txBegin: waiting for free tid\n")); + TXN_SLEEP(&TxAnchor.freewait); + goto retry; + } + + tblk = tid_to_tblock(t); + + if ((tblk->next == 0) && (current != jfsCommitTask)) { + /* Save one tblk for jfsCommit thread */ + jFYI(1, ("txBegin: waiting for free tid\n")); + TXN_SLEEP(&TxAnchor.freewait); + goto retry; + } + + TxAnchor.freetid = tblk->next; + + /* + * initialize transaction + */ + + /* + * We can't zero the whole thing or we screw up another thread being + * awakened after sleeping on tblk->waitor + * + * memset(tblk, 0, sizeof(tblock_t)); + */ + tblk->next = tblk->last = tblk->xflag = tblk->flag = tblk->lsn = 0; + + tblk->sb = sb; + ++log->logtid; + tblk->logtid = log->logtid; + + ++log->active; + + HIGHWATERMARK(stattx.maxtid, t); /* statistics */ + INCREMENT(stattx.ntid); /* statistics */ + + TXN_UNLOCK(); + + jFYI(1, ("txBegin: returning tid = %d\n", t)); + + return t; +} + + +/* + * NAME: txBeginAnon() + * + * FUNCTION: start an anonymous transaction. + * Blocks if logsync or available tlocks are low to prevent + * anonymous tlocks from depleting supply. + * + * PARAMETER: sb - superblock + * + * RETURN: none + */ +void txBeginAnon(struct super_block *sb) +{ + log_t *log; + + log = (log_t *) JFS_SBI(sb)->log; + + TXN_LOCK(); + + retry: + /* + * synchronize with logsync barrier + */ + if (log->syncbarrier) { + TXN_SLEEP(&log->syncwait); + goto retry; + } + + /* + * Don't begin transaction if we're getting starved for tlocks + */ + if (TlocksLow) { + TXN_SLEEP(&TxAnchor.lowlockwait); + goto retry; + } + TXN_UNLOCK(); +} + + +/* + * txEnd() + * + * function: free specified transaction block. + * + * logsync barrier processing: + * + * serialization: + */ +void txEnd(tid_t tid) +{ + tblock_t *tblk = tid_to_tblock(tid); + log_t *log; + + jFYI(1, ("txEnd: tid = %d\n", tid)); + TXN_LOCK(); + + /* + * wakeup transactions waiting on the page locked + * by the current transaction + */ + TXN_WAKEUP(&tblk->waitor); + + log = (log_t *) JFS_SBI(tblk->sb)->log; + + /* + * Lazy commit thread can't free this guy until we mark it UNLOCKED, + * otherwise, we would be left with a transaction that may have been + * reused. + * + * Lazy commit thread will turn off tblkGC_LAZY before calling this + * routine. + */ + if (tblk->flag & tblkGC_LAZY) { + jFYI(1, + ("txEnd called w/lazy tid: %d, tblk = 0x%p\n", + tid, tblk)); + TXN_UNLOCK(); + + spin_lock_irq(&log->gclock); // LOGGC_LOCK + tblk->flag |= tblkGC_UNLOCKED; + spin_unlock_irq(&log->gclock); // LOGGC_UNLOCK + return; + } + + jFYI(1, ("txEnd: tid: %d, tblk = 0x%p\n", tid, tblk)); + + assert(tblk->next == 0); + + /* + * insert tblock back on freelist + */ + tblk->next = TxAnchor.freetid; + TxAnchor.freetid = tid; + + /* + * mark the tblock not active + */ + --log->active; + + /* + * synchronize with logsync barrier + */ + if (log->syncbarrier && log->active == 0) { + /* forward log syncpt */ + /* lmSync(log); */ + + jFYI(1, (" log barrier off: 0x%x\n", log->lsn)); + + /* enable new transactions start */ + log->syncbarrier = 0; + + /* wakeup all waitors for logsync barrier */ + TXN_WAKEUP(&log->syncwait); + } + + /* + * wakeup all waitors for a free tblock + */ + TXN_WAKEUP(&TxAnchor.freewait); + + TXN_UNLOCK(); + jFYI(1, ("txEnd: exitting\n")); +} + + +/* + * txLock() + * + * function: acquire a transaction lock on the specified + * + * parameter: + * + * return: transaction lock id + * + * serialization: + */ +tlock_t *txLock(tid_t tid, struct inode *ip, metapage_t * mp, int type) +{ + struct jfs_inode_info *jfs_ip = JFS_IP(ip); + int dir_xtree = 0; + lid_t lid; + tid_t xtid; + tlock_t *tlck; + xtlock_t *xtlck; + linelock_t *linelock; + xtpage_t *p; + tblock_t *tblk; + + TXN_LOCK(); + + if (S_ISDIR(ip->i_mode) && (type & tlckXTREE) && + !(mp->xflag & COMMIT_PAGE)) { + /* + * Directory inode is special. It can have both an xtree tlock + * and a dtree tlock associated with it. + */ + dir_xtree = 1; + lid = jfs_ip->xtlid; + } else + lid = mp->lid; + + /* is page not locked by a transaction ? */ + if (lid == 0) + goto allocateLock; + + jFYI(1, ("txLock: tid:%d ip:0x%p mp:0x%p lid:%d\n", + tid, ip, mp, lid)); + + /* is page locked by the requester transaction ? */ + tlck = lid_to_tlock(lid); + if ((xtid = tlck->tid) == tid) + goto grantLock; + + /* + * is page locked by anonymous transaction/lock ? + * + * (page update without transaction (i.e., file write) is + * locked under anonymous transaction tid = 0: + * anonymous tlocks maintained on anonymous tlock list of + * the inode of the page and available to all anonymous + * transactions until txCommit() time at which point + * they are transferred to the transaction tlock list of + * the commiting transaction of the inode) + */ + if (xtid == 0) { + tlck->tid = tid; + tblk = tid_to_tblock(tid); + /* + * The order of the tlocks in the transaction is important + * (during truncate, child xtree pages must be freed before + * parent's tlocks change the working map). + * Take tlock off anonymous list and add to tail of + * transaction list + * + * Note: We really need to get rid of the tid & lid and + * use list_head's. This code is getting UGLY! + */ + if (jfs_ip->atlhead == lid) { + if (jfs_ip->atltail == lid) { + /* only anonymous txn. + * Remove from anon_list + */ + list_del_init(&jfs_ip->anon_inode_list); + } + jfs_ip->atlhead = tlck->next; + } else { + lid_t last; + for (last = jfs_ip->atlhead; + lid_to_tlock(last)->next != lid; + last = lid_to_tlock(last)->next) { + assert(last); + } + lid_to_tlock(last)->next = tlck->next; + if (jfs_ip->atltail == lid) + jfs_ip->atltail = last; + } + + /* insert the tlock at tail of transaction tlock list */ + + if (tblk->next) + lid_to_tlock(tblk->last)->next = lid; + else + tblk->next = lid; + tlck->next = 0; + tblk->last = lid; + + goto grantLock; + } + + goto waitLock; + + /* + * allocate a tlock + */ + allocateLock: + lid = txLockAlloc(); + tlck = lid_to_tlock(lid); + + /* + * initialize tlock + */ + tlck->tid = tid; + + /* mark tlock for meta-data page */ + if (mp->xflag & COMMIT_PAGE) { + + tlck->flag = tlckPAGELOCK; + + /* mark the page dirty and nohomeok */ + mark_metapage_dirty(mp); + atomic_inc(&mp->nohomeok); + + jFYI(1, + ("locking mp = 0x%p, nohomeok = %d tid = %d tlck = 0x%p\n", + mp, atomic_read(&mp->nohomeok), tid, tlck)); + + /* if anonymous transaction, and buffer is on the group + * commit synclist, mark inode to show this. This will + * prevent the buffer from being marked nohomeok for too + * long a time. + */ + if ((tid == 0) && mp->lsn) + set_cflag(COMMIT_Synclist, ip); + } + /* mark tlock for in-memory inode */ + else + tlck->flag = tlckINODELOCK; + + tlck->type = 0; + + /* bind the tlock and the page */ + tlck->ip = ip; + tlck->mp = mp; + if (dir_xtree) + jfs_ip->xtlid = lid; + else + mp->lid = lid; + + /* + * enqueue transaction lock to transaction/inode + */ + /* insert the tlock at tail of transaction tlock list */ + if (tid) { + tblk = tid_to_tblock(tid); + if (tblk->next) + lid_to_tlock(tblk->last)->next = lid; + else + tblk->next = lid; + tlck->next = 0; + tblk->last = lid; + } + /* anonymous transaction: + * insert the tlock at head of inode anonymous tlock list + */ + else { + tlck->next = jfs_ip->atlhead; + jfs_ip->atlhead = lid; + if (tlck->next == 0) { + /* This inode's first anonymous transaction */ + jfs_ip->atltail = lid; + list_add_tail(&jfs_ip->anon_inode_list, + &TxAnchor.anon_list); + } + } + + /* initialize type dependent area for linelock */ + linelock = (linelock_t *) & tlck->lock; + linelock->next = 0; + linelock->flag = tlckLINELOCK; + linelock->maxcnt = TLOCKSHORT; + linelock->index = 0; + + switch (type & tlckTYPE) { + case tlckDTREE: + linelock->l2linesize = L2DTSLOTSIZE; + break; + + case tlckXTREE: + linelock->l2linesize = L2XTSLOTSIZE; + + xtlck = (xtlock_t *) linelock; + xtlck->header.offset = 0; + xtlck->header.length = 2; + + if (type & tlckNEW) { + xtlck->lwm.offset = XTENTRYSTART; + } else { + if (mp->xflag & COMMIT_PAGE) + p = (xtpage_t *) mp->data; + else + p = &jfs_ip->i_xtroot; + xtlck->lwm.offset = + le16_to_cpu(p->header.nextindex); + } + xtlck->lwm.length = 0; /* ! */ + + xtlck->index = 2; + break; + + case tlckINODE: + linelock->l2linesize = L2INODESLOTSIZE; + break; + + case tlckDATA: + linelock->l2linesize = L2DATASLOTSIZE; + break; + + default: + jERROR(1, ("UFO tlock:0x%p\n", tlck)); + } + + /* + * update tlock vector + */ + grantLock: + tlck->type |= type; + + TXN_UNLOCK(); + + return tlck; + + /* + * page is being locked by another transaction: + */ + waitLock: + /* Only locks on ipimap or ipaimap should reach here */ + /* assert(jfs_ip->fileset == AGGREGATE_I); */ + if (jfs_ip->fileset != AGGREGATE_I) { + jERROR(1, ("txLock: trying to lock locked page!\n")); + dump_mem("ip", ip, sizeof(struct inode)); + dump_mem("mp", mp, sizeof(metapage_t)); + dump_mem("Locker's tblk", tid_to_tblock(tid), + sizeof(tblock_t)); + dump_mem("Tlock", tlck, sizeof(tlock_t)); + BUG(); + } + INCREMENT(stattx.waitlock); /* statistics */ + release_metapage(mp); + + jEVENT(0, ("txLock: in waitLock, tid = %d, xtid = %d, lid = %d\n", + tid, xtid, lid)); + TXN_SLEEP_DROP_LOCK(&tid_to_tblock(xtid)->waitor); + jEVENT(0, ("txLock: awakened tid = %d, lid = %d\n", tid, lid)); + + return NULL; +} + + +/* + * NAME: txRelease() + * + * FUNCTION: Release buffers associated with transaction locks, but don't + * mark homeok yet. The allows other transactions to modify + * buffers, but won't let them go to disk until commit record + * actually gets written. + * + * PARAMETER: + * tblk - + * + * RETURN: Errors from subroutines. + */ +static void txRelease(tblock_t * tblk) +{ + metapage_t *mp; + lid_t lid; + tlock_t *tlck; + + TXN_LOCK(); + + for (lid = tblk->next; lid; lid = tlck->next) { + tlck = lid_to_tlock(lid); + if ((mp = tlck->mp) != NULL && + (tlck->type & tlckBTROOT) == 0) { + assert(mp->xflag & COMMIT_PAGE); + mp->lid = 0; + } + } + + /* + * wakeup transactions waiting on a page locked + * by the current transaction + */ + TXN_WAKEUP(&tblk->waitor); + + TXN_UNLOCK(); +} + + +/* + * NAME: txUnlock() + * + * FUNCTION: Initiates pageout of pages modified by tid in journalled + * objects and frees their lockwords. + * + * PARAMETER: + * flag - + * + * RETURN: Errors from subroutines. + */ +static void txUnlock(tblock_t * tblk, int flag) +{ + tlock_t *tlck; + linelock_t *linelock; + lid_t lid, next, llid, k; + metapage_t *mp; + log_t *log; + int force; + int difft, diffp; + + jFYI(1, ("txUnlock: tblk = 0x%p\n", tblk)); + log = (log_t *) JFS_SBI(tblk->sb)->log; + force = flag & COMMIT_FLUSH; + if (log->syncbarrier) + force |= COMMIT_FORCE; + + /* + * mark page under tlock homeok (its log has been written): + * if caller has specified FORCE (e.g., iRecycle()), or + * if syncwait for the log is set (i.e., the log sync point + * has fallen behind), or + * if syncpt is set for the page, or + * if the page is new, initiate pageout; + * otherwise, leave the page in memory. + */ + for (lid = tblk->next; lid; lid = next) { + tlck = lid_to_tlock(lid); + next = tlck->next; + + jFYI(1, ("unlocking lid = %d, tlck = 0x%p\n", lid, tlck)); + + /* unbind page from tlock */ + if ((mp = tlck->mp) != NULL && + (tlck->type & tlckBTROOT) == 0) { + assert(mp->xflag & COMMIT_PAGE); + + /* hold buffer + * + * It's possible that someone else has the metapage. + * The only things were changing are nohomeok, which + * is handled atomically, and clsn which is protected + * by the LOGSYNC_LOCK. + */ + hold_metapage(mp, 1); + + assert(atomic_read(&mp->nohomeok) > 0); + atomic_dec(&mp->nohomeok); + + /* inherit younger/larger clsn */ + LOGSYNC_LOCK(log); + if (mp->clsn) { + logdiff(difft, tblk->clsn, log); + logdiff(diffp, mp->clsn, log); + if (difft > diffp) + mp->clsn = tblk->clsn; + } else + mp->clsn = tblk->clsn; + LOGSYNC_UNLOCK(log); + + assert(!(tlck->flag & tlckFREEPAGE)); + + if (tlck->flag & tlckWRITEPAGE) { + write_metapage(mp); + } else { + /* release page which has been forced */ + release_metapage(mp); + } + } + + /* insert tlock, and linelock(s) of the tlock if any, + * at head of freelist + */ + TXN_LOCK(); + + llid = ((linelock_t *) & tlck->lock)->next; + while (llid) { + linelock = (linelock_t *) lid_to_tlock(llid); + k = linelock->next; + txLockFree(llid); + llid = k; + } + txLockFree(lid); + + TXN_UNLOCK(); + } + tblk->next = tblk->last = 0; + + /* + * remove tblock from logsynclist + * (allocation map pages inherited lsn of tblk and + * has been inserted in logsync list at txUpdateMap()) + */ + if (tblk->lsn) { + LOGSYNC_LOCK(log); + log->count--; + list_del(&tblk->synclist); + LOGSYNC_UNLOCK(log); + } +} + + +/* + * txMaplock() + * + * function: allocate a transaction lock for freed page/entry; + * for freed page, maplock is used as xtlock/dtlock type; + */ +tlock_t *txMaplock(tid_t tid, struct inode *ip, int type) +{ + struct jfs_inode_info *jfs_ip = JFS_IP(ip); + lid_t lid; + tblock_t *tblk; + tlock_t *tlck; + maplock_t *maplock; + + TXN_LOCK(); + + /* + * allocate a tlock + */ + lid = txLockAlloc(); + tlck = lid_to_tlock(lid); + + /* + * initialize tlock + */ + tlck->tid = tid; + + /* bind the tlock and the object */ + tlck->flag = tlckINODELOCK; + tlck->ip = ip; + tlck->mp = NULL; + + tlck->type = type; + + /* + * enqueue transaction lock to transaction/inode + */ + /* insert the tlock at tail of transaction tlock list */ + if (tid) { + tblk = tid_to_tblock(tid); + if (tblk->next) + lid_to_tlock(tblk->last)->next = lid; + else + tblk->next = lid; + tlck->next = 0; + tblk->last = lid; + } + /* anonymous transaction: + * insert the tlock at head of inode anonymous tlock list + */ + else { + tlck->next = jfs_ip->atlhead; + jfs_ip->atlhead = lid; + if (tlck->next == 0) { + /* This inode's first anonymous transaction */ + jfs_ip->atltail = lid; + list_add_tail(&jfs_ip->anon_inode_list, + &TxAnchor.anon_list); + } + } + + TXN_UNLOCK(); + + /* initialize type dependent area for maplock */ + maplock = (maplock_t *) & tlck->lock; + maplock->next = 0; + maplock->maxcnt = 0; + maplock->index = 0; + + return tlck; +} + + +/* + * txLinelock() + * + * function: allocate a transaction lock for log vector list + */ +linelock_t *txLinelock(linelock_t * tlock) +{ + lid_t lid; + tlock_t *tlck; + linelock_t *linelock; + + TXN_LOCK(); + + /* allocate a TxLock structure */ + lid = txLockAlloc(); + tlck = lid_to_tlock(lid); + + TXN_UNLOCK(); + + /* initialize linelock */ + linelock = (linelock_t *) tlck; + linelock->next = 0; + linelock->flag = tlckLINELOCK; + linelock->maxcnt = TLOCKLONG; + linelock->index = 0; + + /* append linelock after tlock */ + linelock->next = tlock->next; + tlock->next = lid; + + return linelock; +} + + + +/* + * transaction commit management + * ----------------------------- + */ + +/* + * NAME: txCommit() + * + * FUNCTION: commit the changes to the objects specified in + * clist. For journalled segments only the + * changes of the caller are committed, ie by tid. + * for non-journalled segments the data are flushed to + * disk and then the change to the disk inode and indirect + * blocks committed (so blocks newly allocated to the + * segment will be made a part of the segment atomically). + * + * all of the segments specified in clist must be in + * one file system. no more than 6 segments are needed + * to handle all unix svcs. + * + * if the i_nlink field (i.e. disk inode link count) + * is zero, and the type of inode is a regular file or + * directory, or symbolic link , the inode is truncated + * to zero length. the truncation is committed but the + * VM resources are unaffected until it is closed (see + * iput and iclose). + * + * PARAMETER: + * + * RETURN: + * + * serialization: + * on entry the inode lock on each segment is assumed + * to be held. + * + * i/o error: + */ +int txCommit(tid_t tid, /* transaction identifier */ + int nip, /* number of inodes to commit */ + struct inode **iplist, /* list of inode to commit */ + int flag) +{ + int rc = 0, rc1 = 0; + commit_t cd; + log_t *log; + tblock_t *tblk; + lrd_t *lrd; + int lsn; + struct inode *ip; + struct jfs_inode_info *jfs_ip; + int k, n; + ino_t top; + struct super_block *sb; + + jFYI(1, ("txCommit, tid = %d, flag = %d\n", tid, flag)); + /* is read-only file system ? */ + if (isReadOnly(iplist[0])) { + rc = EROFS; + goto TheEnd; + } + + sb = cd.sb = iplist[0]->i_sb; + cd.tid = tid; + + if (tid == 0) + tid = txBegin(sb, 0); + tblk = tid_to_tblock(tid); + + /* + * initialize commit structure + */ + log = (log_t *) JFS_SBI(sb)->log; + cd.log = log; + + /* initialize log record descriptor in commit */ + lrd = &cd.lrd; + lrd->logtid = cpu_to_le32(tblk->logtid); + lrd->backchain = 0; + + tblk->xflag |= flag; + + if ((flag & (COMMIT_FORCE | COMMIT_SYNC)) == 0) + tblk->xflag |= COMMIT_LAZY; + /* + * prepare non-journaled objects for commit + * + * flush data pages of non-journaled file + * to prevent the file getting non-initialized disk blocks + * in case of crash. + * (new blocks - ) + */ + cd.iplist = iplist; + cd.nip = nip; + + /* + * acquire transaction lock on (on-disk) inodes + * + * update on-disk inode from in-memory inode + * acquiring transaction locks for AFTER records + * on the on-disk inode of file object + * + * sort the inodes array by inode number in descending order + * to prevent deadlock when acquiring transaction lock + * of on-disk inodes on multiple on-disk inode pages by + * multiple concurrent transactions + */ + for (k = 0; k < cd.nip; k++) { + top = (cd.iplist[k])->i_ino; + for (n = k + 1; n < cd.nip; n++) { + ip = cd.iplist[n]; + if (ip->i_ino > top) { + top = ip->i_ino; + cd.iplist[n] = cd.iplist[k]; + cd.iplist[k] = ip; + } + } + + ip = cd.iplist[k]; + jfs_ip = JFS_IP(ip); + + /* + * BUGBUG - Should we call filemap_fdatasync here instead + * of fsync_inode_data? + * If we do, we have a deadlock condition since we may end + * up recursively calling jfs_get_block with the IWRITELOCK + * held. We may be able to do away with IWRITELOCK while + * committing transactions and use i_sem instead. + */ + if ((!S_ISDIR(ip->i_mode)) + && (tblk->flag & COMMIT_DELETE) == 0) + fsync_inode_data_buffers(ip); + + /* + * Mark inode as not dirty. It will still be on the dirty + * inode list, but we'll know not to commit it again unless + * it gets marked dirty again + */ + clear_cflag(COMMIT_Dirty, ip); + + /* inherit anonymous tlock(s) of inode */ + if (jfs_ip->atlhead) { + lid_to_tlock(jfs_ip->atltail)->next = tblk->next; + tblk->next = jfs_ip->atlhead; + if (!tblk->last) + tblk->last = jfs_ip->atltail; + jfs_ip->atlhead = jfs_ip->atltail = 0; + TXN_LOCK(); + list_del_init(&jfs_ip->anon_inode_list); + TXN_UNLOCK(); + } + + /* + * acquire transaction lock on on-disk inode page + * (become first tlock of the tblk's tlock list) + */ + if (((rc = diWrite(tid, ip)))) + goto out; + } + + /* + * write log records from transaction locks + * + * txUpdateMap() resets XAD_NEW in XAD. + */ + if ((rc = txLog(log, tblk, &cd))) + goto TheEnd; + + /* + * Ensure that inode isn't reused before + * lazy commit thread finishes processing + */ + if (tblk->xflag & (COMMIT_CREATE | COMMIT_DELETE)) + atomic_inc(&tblk->ip->i_count); + if (tblk->xflag & COMMIT_DELETE) { + ip = tblk->ip; + assert((ip->i_nlink == 0) && !test_cflag(COMMIT_Nolink, ip)); + set_cflag(COMMIT_Nolink, ip); + } + + /* + * write COMMIT log record + */ + lrd->type = cpu_to_le16(LOG_COMMIT); + lrd->length = 0; + lsn = lmLog(log, tblk, lrd, NULL); + + lmGroupCommit(log, tblk); + + /* + * - transaction is now committed - + */ + + /* + * force pages in careful update + * (imap addressing structure update) + */ + if (flag & COMMIT_FORCE) + txForce(tblk); + + /* + * update allocation map. + * + * update inode allocation map and inode: + * free pager lock on memory object of inode if any. + * update block allocation map. + * + * txUpdateMap() resets XAD_NEW in XAD. + */ + if (tblk->xflag & COMMIT_FORCE) + txUpdateMap(tblk); + + /* + * free transaction locks and pageout/free pages + */ + txRelease(tblk); + + if ((tblk->flag & tblkGC_LAZY) == 0) + txUnlock(tblk, flag); + + + /* + * reset in-memory object state + */ + for (k = 0; k < cd.nip; k++) { + ip = cd.iplist[k]; + jfs_ip = JFS_IP(ip); + + /* + * reset in-memory inode state + */ + jfs_ip->bxflag = 0; + jfs_ip->blid = 0; + } + + out: + if (rc != 0) + txAbortCommit(&cd, rc); + else + rc = rc1; + + TheEnd: + jFYI(1, ("txCommit: tid = %d, returning %d\n", tid, rc)); + return rc; +} + + +/* + * NAME: txLog() + * + * FUNCTION: Writes AFTER log records for all lines modified + * by tid for segments specified by inodes in comdata. + * Code assumes only WRITELOCKS are recorded in lockwords. + * + * PARAMETERS: + * + * RETURN : + */ +static int txLog(log_t * log, tblock_t * tblk, commit_t * cd) +{ + int rc = 0; + struct inode *ip; + lid_t lid; + tlock_t *tlck; + lrd_t *lrd = &cd->lrd; + + /* + * write log record(s) for each tlock of transaction, + */ + for (lid = tblk->next; lid; lid = tlck->next) { + tlck = lid_to_tlock(lid); + + tlck->flag |= tlckLOG; + + /* initialize lrd common */ + ip = tlck->ip; + lrd->aggregate = cpu_to_le32(kdev_t_to_nr(ip->i_dev)); + lrd->log.redopage.fileset = cpu_to_le32(JFS_IP(ip)->fileset); + lrd->log.redopage.inode = cpu_to_le32(ip->i_ino); + + if (tlck->mp) + hold_metapage(tlck->mp, 0); + + /* write log record of page from the tlock */ + switch (tlck->type & tlckTYPE) { + case tlckXTREE: + xtLog(log, tblk, lrd, tlck); + break; + + case tlckDTREE: + dtLog(log, tblk, lrd, tlck); + break; + + case tlckINODE: + diLog(log, tblk, lrd, tlck, cd); + break; + + case tlckMAP: + mapLog(log, tblk, lrd, tlck); + break; + + case tlckDATA: + dataLog(log, tblk, lrd, tlck); + break; + + default: + jERROR(1, ("UFO tlock:0x%p\n", tlck)); + } + if (tlck->mp) + release_metapage(tlck->mp); + } + + return rc; +} + + +/* + * diLog() + * + * function: log inode tlock and format maplock to update bmap; + */ +int diLog(log_t * log, + tblock_t * tblk, lrd_t * lrd, tlock_t * tlck, commit_t * cd) +{ + int rc = 0; + metapage_t *mp; + pxd_t *pxd; + pxdlock_t *pxdlock; + + mp = tlck->mp; + + /* initialize as REDOPAGE record format */ + lrd->log.redopage.type = cpu_to_le16(LOG_INODE); + lrd->log.redopage.l2linesize = cpu_to_le16(L2INODESLOTSIZE); + + pxd = &lrd->log.redopage.pxd; + + /* + * inode after image + */ + if (tlck->type & tlckENTRY) { + /* log after-image for logredo(): */ + lrd->type = cpu_to_le16(LOG_REDOPAGE); +// *pxd = mp->cm_pxd; + PXDaddress(pxd, mp->index); + PXDlength(pxd, + mp->logical_size >> tblk->sb->s_blocksize_bits); + lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, tlck)); + + /* mark page as homeward bound */ + tlck->flag |= tlckWRITEPAGE; + } else if (tlck->type & tlckFREE) { + /* + * free inode extent + * + * (pages of the freed inode extent have been invalidated and + * a maplock for free of the extent has been formatted at + * txLock() time); + * + * the tlock had been acquired on the inode allocation map page + * (iag) that specifies the freed extent, even though the map + * page is not itself logged, to prevent pageout of the map + * page before the log; + */ + assert(tlck->type & tlckFREE); + + /* log LOG_NOREDOINOEXT of the freed inode extent for + * logredo() to start NoRedoPage filters, and to update + * imap and bmap for free of the extent; + */ + lrd->type = cpu_to_le16(LOG_NOREDOINOEXT); + /* + * For the LOG_NOREDOINOEXT record, we need + * to pass the IAG number and inode extent + * index (within that IAG) from which the + * the extent being released. These have been + * passed to us in the iplist[1] and iplist[2]. + */ + lrd->log.noredoinoext.iagnum = + cpu_to_le32((u32) (size_t) cd->iplist[1]); + lrd->log.noredoinoext.inoext_idx = + cpu_to_le32((u32) (size_t) cd->iplist[2]); + + pxdlock = (pxdlock_t *) & tlck->lock; + *pxd = pxdlock->pxd; + lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, NULL)); + + /* update bmap */ + tlck->flag |= tlckUPDATEMAP; + + /* mark page as homeward bound */ + tlck->flag |= tlckWRITEPAGE; + } else { + jERROR(2, ("diLog: UFO type tlck:0x%p\n", tlck)); + } +#ifdef _JFS_WIP + /* + * alloc/free external EA extent + * + * a maplock for txUpdateMap() to update bPWMAP for alloc/free + * of the extent has been formatted at txLock() time; + */ + else { + assert(tlck->type & tlckEA); + + /* log LOG_UPDATEMAP for logredo() to update bmap for + * alloc of new (and free of old) external EA extent; + */ + lrd->type = cpu_to_le16(LOG_UPDATEMAP); + pxdlock = (pxdlock_t *) & tlck->lock; + nlock = pxdlock->index; + for (i = 0; i < nlock; i++, pxdlock++) { + if (pxdlock->flag & mlckALLOCPXD) + lrd->log.updatemap.type = + cpu_to_le16(LOG_ALLOCPXD); + else + lrd->log.updatemap.type = + cpu_to_le16(LOG_FREEPXD); + lrd->log.updatemap.nxd = cpu_to_le16(1); + lrd->log.updatemap.pxd = pxdlock->pxd; + lrd->backchain = + cpu_to_le32(lmLog(log, tblk, lrd, NULL)); + } + + /* update bmap */ + tlck->flag |= tlckUPDATEMAP; + } +#endif /* _JFS_WIP */ + + return rc; +} + + +/* + * dataLog() + * + * function: log data tlock + */ +int dataLog(log_t * log, tblock_t * tblk, lrd_t * lrd, tlock_t * tlck) +{ + metapage_t *mp; + pxd_t *pxd; + int rc; + s64 xaddr; + int xflag; + s32 xlen; + + mp = tlck->mp; + + /* initialize as REDOPAGE record format */ + lrd->log.redopage.type = cpu_to_le16(LOG_DATA); + lrd->log.redopage.l2linesize = cpu_to_le16(L2DATASLOTSIZE); + + pxd = &lrd->log.redopage.pxd; + + /* log after-image for logredo(): */ + lrd->type = cpu_to_le16(LOG_REDOPAGE); + + if (JFS_IP(tlck->ip)->next_index < MAX_INLINE_DIRTABLE_ENTRY) { + /* + * The table has been truncated, we've must have deleted + * the last entry, so don't bother logging this + */ + mp->lid = 0; + atomic_dec(&mp->nohomeok); + discard_metapage(mp); + tlck->mp = 0; + return 0; + } + + rc = xtLookup(tlck->ip, mp->index, 1, &xflag, &xaddr, &xlen, 1); + if (rc || (xlen == 0)) { + jERROR(1, ("dataLog: can't find physical address\n")); + return 0; + } + + PXDaddress(pxd, xaddr); + PXDlength(pxd, mp->logical_size >> tblk->sb->s_blocksize_bits); + + lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, tlck)); + + /* mark page as homeward bound */ + tlck->flag |= tlckWRITEPAGE; + + return 0; +} + + +/* + * dtLog() + * + * function: log dtree tlock and format maplock to update bmap; + */ +void dtLog(log_t * log, tblock_t * tblk, lrd_t * lrd, tlock_t * tlck) +{ + struct inode *ip; + metapage_t *mp; + pxdlock_t *pxdlock; + pxd_t *pxd; + + ip = tlck->ip; + mp = tlck->mp; + + /* initialize as REDOPAGE/NOREDOPAGE record format */ + lrd->log.redopage.type = cpu_to_le16(LOG_DTREE); + lrd->log.redopage.l2linesize = cpu_to_le16(L2DTSLOTSIZE); + + pxd = &lrd->log.redopage.pxd; + + if (tlck->type & tlckBTROOT) + lrd->log.redopage.type |= cpu_to_le16(LOG_BTROOT); + + /* + * page extension via relocation: entry insertion; + * page extension in-place: entry insertion; + * new right page from page split, reinitialized in-line + * root from root page split: entry insertion; + */ + if (tlck->type & (tlckNEW | tlckEXTEND)) { + /* log after-image of the new page for logredo(): + * mark log (LOG_NEW) for logredo() to initialize + * freelist and update bmap for alloc of the new page; + */ + lrd->type = cpu_to_le16(LOG_REDOPAGE); + if (tlck->type & tlckEXTEND) + lrd->log.redopage.type |= cpu_to_le16(LOG_EXTEND); + else + lrd->log.redopage.type |= cpu_to_le16(LOG_NEW); +// *pxd = mp->cm_pxd; + PXDaddress(pxd, mp->index); + PXDlength(pxd, + mp->logical_size >> tblk->sb->s_blocksize_bits); + lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, tlck)); + + /* format a maplock for txUpdateMap() to update bPMAP for + * alloc of the new page; + */ + if (tlck->type & tlckBTROOT) + return; + tlck->flag |= tlckUPDATEMAP; + pxdlock = (pxdlock_t *) & tlck->lock; + pxdlock->flag = mlckALLOCPXD; + pxdlock->pxd = *pxd; + + pxdlock->index = 1; + + /* mark page as homeward bound */ + tlck->flag |= tlckWRITEPAGE; + return; + } + + /* + * entry insertion/deletion, + * sibling page link update (old right page before split); + */ + if (tlck->type & (tlckENTRY | tlckRELINK)) { + /* log after-image for logredo(): */ + lrd->type = cpu_to_le16(LOG_REDOPAGE); + PXDaddress(pxd, mp->index); + PXDlength(pxd, + mp->logical_size >> tblk->sb->s_blocksize_bits); + lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, tlck)); + + /* mark page as homeward bound */ + tlck->flag |= tlckWRITEPAGE; + return; + } + + /* + * page deletion: page has been invalidated + * page relocation: source extent + * + * a maplock for free of the page has been formatted + * at txLock() time); + */ + if (tlck->type & (tlckFREE | tlckRELOCATE)) { + /* log LOG_NOREDOPAGE of the deleted page for logredo() + * to start NoRedoPage filter and to update bmap for free + * of the deletd page + */ + lrd->type = cpu_to_le16(LOG_NOREDOPAGE); + pxdlock = (pxdlock_t *) & tlck->lock; + *pxd = pxdlock->pxd; + lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, NULL)); + + /* a maplock for txUpdateMap() for free of the page + * has been formatted at txLock() time; + */ + tlck->flag |= tlckUPDATEMAP; + } + return; +} + + +/* + * xtLog() + * + * function: log xtree tlock and format maplock to update bmap; + */ +void xtLog(log_t * log, tblock_t * tblk, lrd_t * lrd, tlock_t * tlck) +{ + struct inode *ip; + metapage_t *mp; + xtpage_t *p; + xtlock_t *xtlck; + maplock_t *maplock; + xdlistlock_t *xadlock; + pxdlock_t *pxdlock; + pxd_t *pxd; + int next, lwm, hwm; + + ip = tlck->ip; + mp = tlck->mp; + + /* initialize as REDOPAGE/NOREDOPAGE record format */ + lrd->log.redopage.type = cpu_to_le16(LOG_XTREE); + lrd->log.redopage.l2linesize = cpu_to_le16(L2XTSLOTSIZE); + + pxd = &lrd->log.redopage.pxd; + + if (tlck->type & tlckBTROOT) { + lrd->log.redopage.type |= cpu_to_le16(LOG_BTROOT); + p = &JFS_IP(ip)->i_xtroot; + if (S_ISDIR(ip->i_mode)) + lrd->log.redopage.type |= + cpu_to_le16(LOG_DIR_XTREE); + } else + p = (xtpage_t *) mp->data; + next = le16_to_cpu(p->header.nextindex); + + xtlck = (xtlock_t *) & tlck->lock; + + maplock = (maplock_t *) & tlck->lock; + xadlock = (xdlistlock_t *) maplock; + + /* + * entry insertion/extension; + * sibling page link update (old right page before split); + */ + if (tlck->type & (tlckNEW | tlckGROW | tlckRELINK)) { + /* log after-image for logredo(): + * logredo() will update bmap for alloc of new/extended + * extents (XAD_NEW|XAD_EXTEND) of XAD[lwm:next) from + * after-image of XADlist; + * logredo() resets (XAD_NEW|XAD_EXTEND) flag when + * applying the after-image to the meta-data page. + */ + lrd->type = cpu_to_le16(LOG_REDOPAGE); +// *pxd = mp->cm_pxd; + PXDaddress(pxd, mp->index); + PXDlength(pxd, + mp->logical_size >> tblk->sb->s_blocksize_bits); + lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, tlck)); + + /* format a maplock for txUpdateMap() to update bPMAP + * for alloc of new/extended extents of XAD[lwm:next) + * from the page itself; + * txUpdateMap() resets (XAD_NEW|XAD_EXTEND) flag. + */ + lwm = xtlck->lwm.offset; + if (lwm == 0) + lwm = XTPAGEMAXSLOT; + + if (lwm == next) + goto out; + assert(lwm < next); + tlck->flag |= tlckUPDATEMAP; + xadlock->flag = mlckALLOCXADLIST; + xadlock->count = next - lwm; + if ((xadlock->count <= 2) && (tblk->xflag & COMMIT_LAZY)) { + int i; + /* + * Lazy commit may allow xtree to be modified before + * txUpdateMap runs. Copy xad into linelock to + * preserve correct data. + */ + xadlock->xdlist = &xtlck->pxdlock; + memcpy(xadlock->xdlist, &p->xad[lwm], + sizeof(xad_t) * xadlock->count); + + for (i = 0; i < xadlock->count; i++) + p->xad[lwm + i].flag &= + ~(XAD_NEW | XAD_EXTENDED); + } else { + /* + * xdlist will point to into inode's xtree, ensure + * that transaction is not committed lazily. + */ + xadlock->xdlist = &p->xad[lwm]; + tblk->xflag &= ~COMMIT_LAZY; + } + jFYI(1, + ("xtLog: alloc ip:0x%p mp:0x%p tlck:0x%p lwm:%d count:%d\n", + tlck->ip, mp, tlck, lwm, xadlock->count)); + + maplock->index = 1; + + out: + /* mark page as homeward bound */ + tlck->flag |= tlckWRITEPAGE; + + return; + } + + /* + * page deletion: file deletion/truncation (ref. xtTruncate()) + * + * (page will be invalidated after log is written and bmap + * is updated from the page); + */ + if (tlck->type & tlckFREE) { + /* LOG_NOREDOPAGE log for NoRedoPage filter: + * if page free from file delete, NoRedoFile filter from + * inode image of zero link count will subsume NoRedoPage + * filters for each page; + * if page free from file truncattion, write NoRedoPage + * filter; + * + * upadte of block allocation map for the page itself: + * if page free from deletion and truncation, LOG_UPDATEMAP + * log for the page itself is generated from processing + * its parent page xad entries; + */ + /* if page free from file truncation, log LOG_NOREDOPAGE + * of the deleted page for logredo() to start NoRedoPage + * filter for the page; + */ + if (tblk->xflag & COMMIT_TRUNCATE) { + /* write NOREDOPAGE for the page */ + lrd->type = cpu_to_le16(LOG_NOREDOPAGE); + PXDaddress(pxd, mp->index); + PXDlength(pxd, + mp->logical_size >> tblk->sb-> + s_blocksize_bits); + lrd->backchain = + cpu_to_le32(lmLog(log, tblk, lrd, NULL)); + + if (tlck->type & tlckBTROOT) { + /* Empty xtree must be logged */ + lrd->type = cpu_to_le16(LOG_REDOPAGE); + lrd->backchain = + cpu_to_le32(lmLog(log, tblk, lrd, tlck)); + } + } + + /* init LOG_UPDATEMAP of the freed extents + * XAD[XTENTRYSTART:hwm) from the deleted page itself + * for logredo() to update bmap; + */ + lrd->type = cpu_to_le16(LOG_UPDATEMAP); + lrd->log.updatemap.type = cpu_to_le16(LOG_FREEXADLIST); + xtlck = (xtlock_t *) & tlck->lock; + hwm = xtlck->hwm.offset; + lrd->log.updatemap.nxd = + cpu_to_le16(hwm - XTENTRYSTART + 1); + /* reformat linelock for lmLog() */ + xtlck->header.offset = XTENTRYSTART; + xtlck->header.length = hwm - XTENTRYSTART + 1; + xtlck->index = 1; + lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, tlck)); + + /* format a maplock for txUpdateMap() to update bmap + * to free extents of XAD[XTENTRYSTART:hwm) from the + * deleted page itself; + */ + tlck->flag |= tlckUPDATEMAP; + xadlock->flag = mlckFREEXADLIST; + xadlock->count = hwm - XTENTRYSTART + 1; + if ((xadlock->count <= 2) && (tblk->xflag & COMMIT_LAZY)) { + /* + * Lazy commit may allow xtree to be modified before + * txUpdateMap runs. Copy xad into linelock to + * preserve correct data. + */ + xadlock->xdlist = &xtlck->pxdlock; + memcpy(xadlock->xdlist, &p->xad[XTENTRYSTART], + sizeof(xad_t) * xadlock->count); + } else { + /* + * xdlist will point to into inode's xtree, ensure + * that transaction is not committed lazily unless + * we're deleting the inode (unlink). In that case + * we have special logic for the inode to be + * unlocked by the lazy commit thread. + */ + xadlock->xdlist = &p->xad[XTENTRYSTART]; + if ((tblk->xflag & COMMIT_LAZY) && + (tblk->xflag & COMMIT_DELETE) && + (tblk->ip == ip)) + set_cflag(COMMIT_Holdlock, ip); + else + tblk->xflag &= ~COMMIT_LAZY; + } + jFYI(1, + ("xtLog: free ip:0x%p mp:0x%p count:%d lwm:2\n", + tlck->ip, mp, xadlock->count)); + + maplock->index = 1; + + /* mark page as invalid */ + if (((tblk->xflag & COMMIT_PWMAP) || S_ISDIR(ip->i_mode)) + && !(tlck->type & tlckBTROOT)) + tlck->flag |= tlckFREEPAGE; + /* + else (tblk->xflag & COMMIT_PMAP) + ? release the page; + */ + return; + } + + /* + * page/entry truncation: file truncation (ref. xtTruncate()) + * + * |----------+------+------+---------------| + * | | | + * | | hwm - hwm before truncation + * | next - truncation point + * lwm - lwm before truncation + * header ? + */ + if (tlck->type & tlckTRUNCATE) { + pxd_t tpxd; /* truncated extent of xad */ + + /* + * For truncation the entire linelock may be used, so it would + * be difficult to store xad list in linelock itself. + * Therefore, we'll just force transaction to be committed + * synchronously, so that xtree pages won't be changed before + * txUpdateMap runs. + */ + tblk->xflag &= ~COMMIT_LAZY; + lwm = xtlck->lwm.offset; + if (lwm == 0) + lwm = XTPAGEMAXSLOT; + hwm = xtlck->hwm.offset; + + /* + * write log records + */ + /* + * allocate entries XAD[lwm:next]: + */ + if (lwm < next) { + /* log after-image for logredo(): + * logredo() will update bmap for alloc of new/extended + * extents (XAD_NEW|XAD_EXTEND) of XAD[lwm:next) from + * after-image of XADlist; + * logredo() resets (XAD_NEW|XAD_EXTEND) flag when + * applying the after-image to the meta-data page. + */ + lrd->type = cpu_to_le16(LOG_REDOPAGE); + PXDaddress(pxd, mp->index); + PXDlength(pxd, + mp->logical_size >> tblk->sb-> + s_blocksize_bits); + lrd->backchain = + cpu_to_le32(lmLog(log, tblk, lrd, tlck)); + } + + /* + * truncate entry XAD[hwm == next - 1]: + */ + if (hwm == next - 1) { + /* init LOG_UPDATEMAP for logredo() to update bmap for + * free of truncated delta extent of the truncated + * entry XAD[next - 1]: + * (xtlck->pxdlock = truncated delta extent); + */ + pxdlock = (pxdlock_t *) & xtlck->pxdlock; + /* assert(pxdlock->type & tlckTRUNCATE); */ + lrd->type = cpu_to_le16(LOG_UPDATEMAP); + lrd->log.updatemap.type = cpu_to_le16(LOG_FREEPXD); + lrd->log.updatemap.nxd = cpu_to_le16(1); + lrd->log.updatemap.pxd = pxdlock->pxd; + tpxd = pxdlock->pxd; /* save to format maplock */ + lrd->backchain = + cpu_to_le32(lmLog(log, tblk, lrd, NULL)); + } + + /* + * free entries XAD[next:hwm]: + */ + if (hwm >= next) { + /* init LOG_UPDATEMAP of the freed extents + * XAD[next:hwm] from the deleted page itself + * for logredo() to update bmap; + */ + lrd->type = cpu_to_le16(LOG_UPDATEMAP); + lrd->log.updatemap.type = + cpu_to_le16(LOG_FREEXADLIST); + xtlck = (xtlock_t *) & tlck->lock; + hwm = xtlck->hwm.offset; + lrd->log.updatemap.nxd = + cpu_to_le16(hwm - next + 1); + /* reformat linelock for lmLog() */ + xtlck->header.offset = next; + xtlck->header.length = hwm - next + 1; + xtlck->index = 1; + lrd->backchain = + cpu_to_le32(lmLog(log, tblk, lrd, tlck)); + } + + /* + * format maplock(s) for txUpdateMap() to update bmap + */ + maplock->index = 0; + + /* + * allocate entries XAD[lwm:next): + */ + if (lwm < next) { + /* format a maplock for txUpdateMap() to update bPMAP + * for alloc of new/extended extents of XAD[lwm:next) + * from the page itself; + * txUpdateMap() resets (XAD_NEW|XAD_EXTEND) flag. + */ + tlck->flag |= tlckUPDATEMAP; + xadlock->flag = mlckALLOCXADLIST; + xadlock->count = next - lwm; + xadlock->xdlist = &p->xad[lwm]; + + jFYI(1, + ("xtLog: alloc ip:0x%p mp:0x%p count:%d lwm:%d next:%d\n", + tlck->ip, mp, xadlock->count, lwm, next)); + maplock->index++; + xadlock++; + } + + /* + * truncate entry XAD[hwm == next - 1]: + */ + if (hwm == next - 1) { + pxdlock_t *pxdlock; + + /* format a maplock for txUpdateMap() to update bmap + * to free truncated delta extent of the truncated + * entry XAD[next - 1]; + * (xtlck->pxdlock = truncated delta extent); + */ + tlck->flag |= tlckUPDATEMAP; + pxdlock = (pxdlock_t *) xadlock; + pxdlock->flag = mlckFREEPXD; + pxdlock->count = 1; + pxdlock->pxd = tpxd; + + jFYI(1, + ("xtLog: truncate ip:0x%p mp:0x%p count:%d hwm:%d\n", + ip, mp, pxdlock->count, hwm)); + maplock->index++; + xadlock++; + } + + /* + * free entries XAD[next:hwm]: + */ + if (hwm >= next) { + /* format a maplock for txUpdateMap() to update bmap + * to free extents of XAD[next:hwm] from thedeleted + * page itself; + */ + tlck->flag |= tlckUPDATEMAP; + xadlock->flag = mlckFREEXADLIST; + xadlock->count = hwm - next + 1; + xadlock->xdlist = &p->xad[next]; + + jFYI(1, + ("xtLog: free ip:0x%p mp:0x%p count:%d next:%d hwm:%d\n", + tlck->ip, mp, xadlock->count, next, hwm)); + maplock->index++; + } + + /* mark page as homeward bound */ + tlck->flag |= tlckWRITEPAGE; + } + return; +} + + +/* + * mapLog() + * + * function: log from maplock of freed data extents; + */ +void mapLog(log_t * log, tblock_t * tblk, lrd_t * lrd, tlock_t * tlck) +{ + pxdlock_t *pxdlock; + int i, nlock; + pxd_t *pxd; + + /* + * page relocation: free the source page extent + * + * a maplock for txUpdateMap() for free of the page + * has been formatted at txLock() time saving the src + * relocated page address; + */ + if (tlck->type & tlckRELOCATE) { + /* log LOG_NOREDOPAGE of the old relocated page + * for logredo() to start NoRedoPage filter; + */ + lrd->type = cpu_to_le16(LOG_NOREDOPAGE); + pxdlock = (pxdlock_t *) & tlck->lock; + pxd = &lrd->log.redopage.pxd; + *pxd = pxdlock->pxd; + lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, NULL)); + + /* (N.B. currently, logredo() does NOT update bmap + * for free of the page itself for (LOG_XTREE|LOG_NOREDOPAGE); + * if page free from relocation, LOG_UPDATEMAP log is + * specifically generated now for logredo() + * to update bmap for free of src relocated page; + * (new flag LOG_RELOCATE may be introduced which will + * inform logredo() to start NORedoPage filter and also + * update block allocation map at the same time, thus + * avoiding an extra log write); + */ + lrd->type = cpu_to_le16(LOG_UPDATEMAP); + lrd->log.updatemap.type = cpu_to_le16(LOG_FREEPXD); + lrd->log.updatemap.nxd = cpu_to_le16(1); + lrd->log.updatemap.pxd = pxdlock->pxd; + lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, NULL)); + + /* a maplock for txUpdateMap() for free of the page + * has been formatted at txLock() time; + */ + tlck->flag |= tlckUPDATEMAP; + return; + } + /* + + * Otherwise it's not a relocate request + * + */ + else { + /* log LOG_UPDATEMAP for logredo() to update bmap for + * free of truncated/relocated delta extent of the data; + * e.g.: external EA extent, relocated/truncated extent + * from xtTailgate(); + */ + lrd->type = cpu_to_le16(LOG_UPDATEMAP); + pxdlock = (pxdlock_t *) & tlck->lock; + nlock = pxdlock->index; + for (i = 0; i < nlock; i++, pxdlock++) { + if (pxdlock->flag & mlckALLOCPXD) + lrd->log.updatemap.type = + cpu_to_le16(LOG_ALLOCPXD); + else + lrd->log.updatemap.type = + cpu_to_le16(LOG_FREEPXD); + lrd->log.updatemap.nxd = cpu_to_le16(1); + lrd->log.updatemap.pxd = pxdlock->pxd; + lrd->backchain = + cpu_to_le32(lmLog(log, tblk, lrd, NULL)); + jFYI(1, ("mapLog: xaddr:0x%lx xlen:0x%x\n", + (ulong) addressPXD(&pxdlock->pxd), + lengthPXD(&pxdlock->pxd))); + } + + /* update bmap */ + tlck->flag |= tlckUPDATEMAP; + } +} + + +/* + * txEA() + * + * function: acquire maplock for EA/ACL extents or + * set COMMIT_INLINE flag; + */ +void txEA(tid_t tid, struct inode *ip, dxd_t * oldea, dxd_t * newea) +{ + tlock_t *tlck = NULL; + pxdlock_t *maplock = NULL, *pxdlock = NULL; + + /* + * format maplock for alloc of new EA extent + */ + if (newea) { + /* Since the newea could be a completely zeroed entry we need to + * check for the two flags which indicate we should actually + * commit new EA data + */ + if (newea->flag & DXD_EXTENT) { + tlck = txMaplock(tid, ip, tlckMAP); + maplock = (pxdlock_t *) & tlck->lock; + pxdlock = (pxdlock_t *) maplock; + pxdlock->flag = mlckALLOCPXD; + PXDaddress(&pxdlock->pxd, addressDXD(newea)); + PXDlength(&pxdlock->pxd, lengthDXD(newea)); + pxdlock++; + maplock->index = 1; + } else if (newea->flag & DXD_INLINE) { + tlck = NULL; + + set_cflag(COMMIT_Inlineea, ip); + } + } + + /* + * format maplock for free of old EA extent + */ + if (!test_cflag(COMMIT_Nolink, ip) && oldea->flag & DXD_EXTENT) { + if (tlck == NULL) { + tlck = txMaplock(tid, ip, tlckMAP); + maplock = (pxdlock_t *) & tlck->lock; + pxdlock = (pxdlock_t *) maplock; + maplock->index = 0; + } + pxdlock->flag = mlckFREEPXD; + PXDaddress(&pxdlock->pxd, addressDXD(oldea)); + PXDlength(&pxdlock->pxd, lengthDXD(oldea)); + maplock->index++; + } +} + + +/* + * txForce() + * + * function: synchronously write pages locked by transaction + * after txLog() but before txUpdateMap(); + */ +void txForce(tblock_t * tblk) +{ + tlock_t *tlck; + lid_t lid, next; + metapage_t *mp; + + /* + * reverse the order of transaction tlocks in + * careful update order of address index pages + * (right to left, bottom up) + */ + tlck = lid_to_tlock(tblk->next); + lid = tlck->next; + tlck->next = 0; + while (lid) { + tlck = lid_to_tlock(lid); + next = tlck->next; + tlck->next = tblk->next; + tblk->next = lid; + lid = next; + } + + /* + * synchronously write the page, and + * hold the page for txUpdateMap(); + */ + for (lid = tblk->next; lid; lid = next) { + tlck = lid_to_tlock(lid); + next = tlck->next; + + if ((mp = tlck->mp) != NULL && + (tlck->type & tlckBTROOT) == 0) { + assert(mp->xflag & COMMIT_PAGE); + + if (tlck->flag & tlckWRITEPAGE) { + tlck->flag &= ~tlckWRITEPAGE; + + /* do not release page to freelist */ + assert(atomic_read(&mp->nohomeok)); + hold_metapage(mp, 0); + write_metapage(mp); + } + } + } +} + + +/* + * txUpdateMap() + * + * function: update persistent allocation map (and working map + * if appropriate); + * + * parameter: + */ +static void txUpdateMap(tblock_t * tblk) +{ + struct inode *ip; + struct inode *ipimap; + lid_t lid; + tlock_t *tlck; + maplock_t *maplock; + pxdlock_t pxdlock; + int maptype; + int k, nlock; + metapage_t *mp = 0; + + ipimap = JFS_SBI(tblk->sb)->ipimap; + + maptype = (tblk->xflag & COMMIT_PMAP) ? COMMIT_PMAP : COMMIT_PWMAP; + + + /* + * update block allocation map + * + * update allocation state in pmap (and wmap) and + * update lsn of the pmap page; + */ + /* + * scan each tlock/page of transaction for block allocation/free: + * + * for each tlock/page of transaction, update map. + * ? are there tlock for pmap and pwmap at the same time ? + */ + for (lid = tblk->next; lid; lid = tlck->next) { + tlck = lid_to_tlock(lid); + + if ((tlck->flag & tlckUPDATEMAP) == 0) + continue; + + if (tlck->flag & tlckFREEPAGE) { + /* + * Another thread may attempt to reuse freed space + * immediately, so we want to get rid of the metapage + * before anyone else has a chance to get it. + * Lock metapage, update maps, then invalidate + * the metapage. + */ + mp = tlck->mp; + ASSERT(mp->xflag & COMMIT_PAGE); + hold_metapage(mp, 0); + } + + /* + * extent list: + * . in-line PXD list: + * . out-of-line XAD list: + */ + maplock = (maplock_t *) & tlck->lock; + nlock = maplock->index; + + for (k = 0; k < nlock; k++, maplock++) { + /* + * allocate blocks in persistent map: + * + * blocks have been allocated from wmap at alloc time; + */ + if (maplock->flag & mlckALLOC) { + txAllocPMap(ipimap, maplock, tblk); + } + /* + * free blocks in persistent and working map: + * blocks will be freed in pmap and then in wmap; + * + * ? tblock specifies the PMAP/PWMAP based upon + * transaction + * + * free blocks in persistent map: + * blocks will be freed from wmap at last reference + * release of the object for regular files; + * + * Alway free blocks from both persistent & working + * maps for directories + */ + else { /* (maplock->flag & mlckFREE) */ + + if (S_ISDIR(tlck->ip->i_mode)) + txFreeMap(ipimap, maplock, + tblk, COMMIT_PWMAP); + else + txFreeMap(ipimap, maplock, + tblk, maptype); + } + } + if (tlck->flag & tlckFREEPAGE) { + if (!(tblk->flag & tblkGC_LAZY)) { + /* This is equivalent to txRelease */ + ASSERT(mp->lid == lid); + tlck->mp->lid = 0; + } + assert(atomic_read(&mp->nohomeok) == 1); + atomic_dec(&mp->nohomeok); + discard_metapage(mp); + tlck->mp = 0; + } + } + /* + * update inode allocation map + * + * update allocation state in pmap and + * update lsn of the pmap page; + * update in-memory inode flag/state + * + * unlock mapper/write lock + */ + if (tblk->xflag & COMMIT_CREATE) { + ip = tblk->ip; + + ASSERT(test_cflag(COMMIT_New, ip)); + clear_cflag(COMMIT_New, ip); + + diUpdatePMap(ipimap, ip->i_ino, FALSE, tblk); + ipimap->i_state |= I_DIRTY; + /* update persistent block allocation map + * for the allocation of inode extent; + */ + pxdlock.flag = mlckALLOCPXD; + pxdlock.pxd = JFS_IP(ip)->ixpxd; + pxdlock.index = 1; + txAllocPMap(ip, (maplock_t *) & pxdlock, tblk); + iput(ip); + } else if (tblk->xflag & COMMIT_DELETE) { + ip = tblk->ip; + diUpdatePMap(ipimap, ip->i_ino, TRUE, tblk); + ipimap->i_state |= I_DIRTY; + if (test_and_clear_cflag(COMMIT_Holdlock, ip)) { + if (tblk->flag & tblkGC_LAZY) + IWRITE_UNLOCK(ip); + } + iput(ip); + } +} + + +/* + * txAllocPMap() + * + * function: allocate from persistent map; + * + * parameter: + * ipbmap - + * malock - + * xad list: + * pxd: + * + * maptype - + * allocate from persistent map; + * free from persistent map; + * (e.g., tmp file - free from working map at releae + * of last reference); + * free from persistent and working map; + * + * lsn - log sequence number; + */ +static void txAllocPMap(struct inode *ip, maplock_t * maplock, + tblock_t * tblk) +{ + struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap; + xdlistlock_t *xadlistlock; + xad_t *xad; + s64 xaddr; + int xlen; + pxdlock_t *pxdlock; + xdlistlock_t *pxdlistlock; + pxd_t *pxd; + int n; + + /* + * allocate from persistent map; + */ + if (maplock->flag & mlckALLOCXADLIST) { + xadlistlock = (xdlistlock_t *) maplock; + xad = xadlistlock->xdlist; + for (n = 0; n < xadlistlock->count; n++, xad++) { + if (xad->flag & (XAD_NEW | XAD_EXTENDED)) { + xaddr = addressXAD(xad); + xlen = lengthXAD(xad); + dbUpdatePMap(ipbmap, FALSE, xaddr, + (s64) xlen, tblk); + xad->flag &= ~(XAD_NEW | XAD_EXTENDED); + jFYI(1, + ("allocPMap: xaddr:0x%lx xlen:%d\n", + (ulong) xaddr, xlen)); + } + } + } else if (maplock->flag & mlckALLOCPXD) { + pxdlock = (pxdlock_t *) maplock; + xaddr = addressPXD(&pxdlock->pxd); + xlen = lengthPXD(&pxdlock->pxd); + dbUpdatePMap(ipbmap, FALSE, xaddr, (s64) xlen, tblk); + jFYI(1, + ("allocPMap: xaddr:0x%lx xlen:%d\n", (ulong) xaddr, + xlen)); + } else { /* (maplock->flag & mlckALLOCPXDLIST) */ + + pxdlistlock = (xdlistlock_t *) maplock; + pxd = pxdlistlock->xdlist; + for (n = 0; n < pxdlistlock->count; n++, pxd++) { + xaddr = addressPXD(pxd); + xlen = lengthPXD(pxd); + dbUpdatePMap(ipbmap, FALSE, xaddr, (s64) xlen, + tblk); + jFYI(1, + ("allocPMap: xaddr:0x%lx xlen:%d\n", + (ulong) xaddr, xlen)); + } + } +} + + +/* + * txFreeMap() + * + * function: free from persistent and/or working map; + * + * todo: optimization + */ +void txFreeMap(struct inode *ip, + maplock_t * maplock, tblock_t * tblk, int maptype) +{ + struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap; + xdlistlock_t *xadlistlock; + xad_t *xad; + s64 xaddr; + int xlen; + pxdlock_t *pxdlock; + xdlistlock_t *pxdlistlock; + pxd_t *pxd; + int n; + + jFYI(1, + ("txFreeMap: tblk:0x%p maplock:0x%p maptype:0x%x\n", + tblk, maplock, maptype)); + + /* + * free from persistent map; + */ + if (maptype == COMMIT_PMAP || maptype == COMMIT_PWMAP) { + if (maplock->flag & mlckFREEXADLIST) { + xadlistlock = (xdlistlock_t *) maplock; + xad = xadlistlock->xdlist; + for (n = 0; n < xadlistlock->count; n++, xad++) { + if (!(xad->flag & XAD_NEW)) { + xaddr = addressXAD(xad); + xlen = lengthXAD(xad); + dbUpdatePMap(ipbmap, TRUE, xaddr, + (s64) xlen, tblk); + jFYI(1, + ("freePMap: xaddr:0x%lx xlen:%d\n", + (ulong) xaddr, xlen)); + } + } + } else if (maplock->flag & mlckFREEPXD) { + pxdlock = (pxdlock_t *) maplock; + xaddr = addressPXD(&pxdlock->pxd); + xlen = lengthPXD(&pxdlock->pxd); + dbUpdatePMap(ipbmap, TRUE, xaddr, (s64) xlen, + tblk); + jFYI(1, + ("freePMap: xaddr:0x%lx xlen:%d\n", + (ulong) xaddr, xlen)); + } else { /* (maplock->flag & mlckALLOCPXDLIST) */ + + pxdlistlock = (xdlistlock_t *) maplock; + pxd = pxdlistlock->xdlist; + for (n = 0; n < pxdlistlock->count; n++, pxd++) { + xaddr = addressPXD(pxd); + xlen = lengthPXD(pxd); + dbUpdatePMap(ipbmap, TRUE, xaddr, + (s64) xlen, tblk); + jFYI(1, + ("freePMap: xaddr:0x%lx xlen:%d\n", + (ulong) xaddr, xlen)); + } + } + } + + /* + * free from working map; + */ + if (maptype == COMMIT_PWMAP || maptype == COMMIT_WMAP) { + if (maplock->flag & mlckFREEXADLIST) { + xadlistlock = (xdlistlock_t *) maplock; + xad = xadlistlock->xdlist; + for (n = 0; n < xadlistlock->count; n++, xad++) { + xaddr = addressXAD(xad); + xlen = lengthXAD(xad); + dbFree(ip, xaddr, (s64) xlen); + xad->flag = 0; + jFYI(1, + ("freeWMap: xaddr:0x%lx xlen:%d\n", + (ulong) xaddr, xlen)); + } + } else if (maplock->flag & mlckFREEPXD) { + pxdlock = (pxdlock_t *) maplock; + xaddr = addressPXD(&pxdlock->pxd); + xlen = lengthPXD(&pxdlock->pxd); + dbFree(ip, xaddr, (s64) xlen); + jFYI(1, + ("freeWMap: xaddr:0x%lx xlen:%d\n", + (ulong) xaddr, xlen)); + } else { /* (maplock->flag & mlckFREEPXDLIST) */ + + pxdlistlock = (xdlistlock_t *) maplock; + pxd = pxdlistlock->xdlist; + for (n = 0; n < pxdlistlock->count; n++, pxd++) { + xaddr = addressPXD(pxd); + xlen = lengthPXD(pxd); + dbFree(ip, xaddr, (s64) xlen); + jFYI(1, + ("freeWMap: xaddr:0x%lx xlen:%d\n", + (ulong) xaddr, xlen)); + } + } + } +} + + +/* + * txFreelock() + * + * function: remove tlock from inode anonymous locklist + */ +void txFreelock(struct inode *ip) +{ + struct jfs_inode_info *jfs_ip = JFS_IP(ip); + tlock_t *xtlck, *tlck; + lid_t xlid = 0, lid; + + if (!jfs_ip->atlhead) + return; + + xtlck = (tlock_t *) &jfs_ip->atlhead; + + while ((lid = xtlck->next)) { + tlck = lid_to_tlock(lid); + if (tlck->flag & tlckFREELOCK) { + xtlck->next = tlck->next; + txLockFree(lid); + } else { + xtlck = tlck; + xlid = lid; + } + } + + if (jfs_ip->atlhead) + jfs_ip->atltail = xlid; + else { + jfs_ip->atltail = 0; + /* + * If inode was on anon_list, remove it + */ + TXN_LOCK(); + list_del_init(&jfs_ip->anon_inode_list); + TXN_UNLOCK(); + } +} + + +/* + * txAbort() + * + * function: abort tx before commit; + * + * frees line-locks and segment locks for all + * segments in comdata structure. + * Optionally sets state of file-system to FM_DIRTY in super-block. + * log age of page-frames in memory for which caller has + * are reset to 0 (to avoid logwarap). + */ +void txAbort(tid_t tid, int dirty) +{ + lid_t lid, next; + metapage_t *mp; + tblock_t *tblk = tid_to_tblock(tid); + + jEVENT(1, ("txAbort: tid:%d dirty:0x%x\n", tid, dirty)); + + /* + * free tlocks of the transaction + */ + for (lid = tblk->next; lid; lid = next) { + next = lid_to_tlock(lid)->next; + + mp = lid_to_tlock(lid)->mp; + + if (mp) { + mp->lid = 0; + + /* + * reset lsn of page to avoid logwarap: + * + * (page may have been previously committed by another + * transaction(s) but has not been paged, i.e., + * it may be on logsync list even though it has not + * been logged for the current tx.) + */ + if (mp->xflag & COMMIT_PAGE && mp->lsn) + LogSyncRelease(mp); + } + /* insert tlock at head of freelist */ + TXN_LOCK(); + txLockFree(lid); + TXN_UNLOCK(); + } + + /* caller will free the transaction block */ + + tblk->next = tblk->last = 0; + + /* + * mark filesystem dirty + */ + if (dirty) + updateSuper(tblk->sb, FM_DIRTY); + + return; +} + + +/* + * txAbortCommit() + * + * function: abort commit. + * + * frees tlocks of transaction; line-locks and segment locks for all + * segments in comdata structure. frees malloc storage + * sets state of file-system to FM_MDIRTY in super-block. + * log age of page-frames in memory for which caller has + * are reset to 0 (to avoid logwarap). + */ +void txAbortCommit(commit_t * cd, int exval) +{ + tblock_t *tblk; + tid_t tid; + lid_t lid, next; + metapage_t *mp; + + assert(exval == EIO || exval == ENOMEM); + jEVENT(1, ("txAbortCommit: cd:0x%p\n", cd)); + + /* + * free tlocks of the transaction + */ + tid = cd->tid; + tblk = tid_to_tblock(tid); + for (lid = tblk->next; lid; lid = next) { + next = lid_to_tlock(lid)->next; + + mp = lid_to_tlock(lid)->mp; + if (mp) { + mp->lid = 0; + + /* + * reset lsn of page to avoid logwarap; + */ + if (mp->xflag & COMMIT_PAGE) + LogSyncRelease(mp); + } + + /* insert tlock at head of freelist */ + TXN_LOCK(); + txLockFree(lid); + TXN_UNLOCK(); + } + + tblk->next = tblk->last = 0; + + /* free the transaction block */ + txEnd(tid); + + /* + * mark filesystem dirty + */ + updateSuper(cd->sb, FM_DIRTY); +} + + +/* + * txLazyCommit(void) + * + * All transactions except those changing ipimap (COMMIT_FORCE) are + * processed by this routine. This insures that the inode and block + * allocation maps are updated in order. For synchronous transactions, + * let the user thread finish processing after txUpdateMap() is called. + */ +void txLazyCommit(tblock_t * tblk) +{ + log_t *log; + + while (((tblk->flag & tblkGC_READY) == 0) && + ((tblk->flag & tblkGC_UNLOCKED) == 0)) { + /* We must have gotten ahead of the user thread + */ + jFYI(1, + ("jfs_lazycommit: tblk 0x%p not unlocked\n", tblk)); + schedule(); + } + + jFYI(1, ("txLazyCommit: processing tblk 0x%p\n", tblk)); + + txUpdateMap(tblk); + + log = (log_t *) JFS_SBI(tblk->sb)->log; + + spin_lock_irq(&log->gclock); // LOGGC_LOCK + + tblk->flag |= tblkGC_COMMITTED; + + if ((tblk->flag & tblkGC_READY) || (tblk->flag & tblkGC_LAZY)) + log->gcrtc--; + + if (tblk->flag & tblkGC_READY) + wake_up(&tblk->gcwait); // LOGGC_WAKEUP + + spin_unlock_irq(&log->gclock); // LOGGC_UNLOCK + + if (tblk->flag & tblkGC_LAZY) { + txUnlock(tblk, 0); + tblk->flag &= ~tblkGC_LAZY; + txEnd(tblk - TxBlock); /* Convert back to tid */ + } + + jFYI(1, ("txLazyCommit: done: tblk = 0x%p\n", tblk)); +} + +/* + * jfs_lazycommit(void) + * + * To be run as a kernel daemon. If lbmIODone is called in an interrupt + * context, or where blocking is not wanted, this routine will process + * committed transactions from the unlock queue. + */ +int jfs_lazycommit(void) +{ + int WorkDone; + tblock_t *tblk; + unsigned long flags; + + lock_kernel(); + + daemonize(); + current->tty = NULL; + strcpy(current->comm, "jfsCommit"); + + unlock_kernel(); + + jfsCommitTask = current; + + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv(¤t->blocked, + sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) + | sigmask(SIGCONT)); + spin_unlock_irq(¤t->sigmask_lock); + + LAZY_LOCK_INIT(); + TxAnchor.unlock_queue = TxAnchor.unlock_tail = 0; + + complete(&jfsIOwait); + + do { +restart: + WorkDone = 0; + while ((tblk = TxAnchor.unlock_queue)) { + /* + * We can't get ahead of user thread. Spinning is + * simpler than blocking/waking. We shouldn't spin + * very long, since user thread shouldn't be blocking + * between lmGroupCommit & txEnd. + */ + WorkDone = 1; + + LAZY_LOCK(flags); + /* + * Remove first transaction from queue + */ + TxAnchor.unlock_queue = tblk->cqnext; + tblk->cqnext = 0; + if (TxAnchor.unlock_tail == tblk) + TxAnchor.unlock_tail = 0; + + LAZY_UNLOCK(flags); + txLazyCommit(tblk); + + /* + * We can be running indefinately if other processors + * are adding transactions to this list + */ + if (need_resched()) { + current->state = TASK_RUNNING; + schedule(); + } + } + + if (WorkDone) + goto restart; + + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } while (!jfs_thread_stopped()); + + if (TxAnchor.unlock_queue) + jERROR(1, ("jfs_lazycommit being killed with pending transactions!\n")); + else + jFYI(1, ("jfs_lazycommit being killed\n")); + complete(&jfsIOwait); + return 0; +} + +void txLazyUnlock(tblock_t * tblk) +{ + unsigned long flags; + + LAZY_LOCK(flags); + + if (TxAnchor.unlock_tail) + TxAnchor.unlock_tail->cqnext = tblk; + else + TxAnchor.unlock_queue = tblk; + TxAnchor.unlock_tail = tblk; + tblk->cqnext = 0; + LAZY_UNLOCK(flags); + wake_up_process(jfsCommitTask); +} + +static void LogSyncRelease(metapage_t * mp) +{ + log_t *log = mp->log; + + assert(atomic_read(&mp->nohomeok)); + assert(log); + atomic_dec(&mp->nohomeok); + + if (atomic_read(&mp->nohomeok)) + return; + + hold_metapage(mp, 0); + + LOGSYNC_LOCK(log); + mp->log = NULL; + mp->lsn = 0; + mp->clsn = 0; + log->count--; + list_del_init(&mp->synclist); + LOGSYNC_UNLOCK(log); + + release_metapage(mp); +} + +/* + * jfs_sync(void) + * + * To be run as a kernel daemon. This is awakened when tlocks run low. + * We write any inodes that have anonymous tlocks so they will become + * available. + */ +int jfs_sync(void) +{ + struct inode *ip; + struct jfs_inode_info *jfs_ip; + + lock_kernel(); + + daemonize(); + current->tty = NULL; + strcpy(current->comm, "jfsSync"); + + unlock_kernel(); + + jfsSyncTask = current; + + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv(¤t->blocked, + sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) + | sigmask(SIGCONT)); + spin_unlock_irq(¤t->sigmask_lock); + + complete(&jfsIOwait); + + do { + /* + * write each inode on the anonymous inode list + */ + TXN_LOCK(); + while (TlocksLow && !list_empty(&TxAnchor.anon_list)) { + jfs_ip = list_entry(TxAnchor.anon_list.next, + struct jfs_inode_info, + anon_inode_list); + ip = &jfs_ip->vfs_inode; + + /* + * We must release the TXN_LOCK since our + * IWRITE_TRYLOCK implementation may still block + */ + TXN_UNLOCK(); + if (IWRITE_TRYLOCK(ip)) { + /* + * inode will be removed from anonymous list + * when it is committed + */ + jfs_commit_inode(ip, 0); + IWRITE_UNLOCK(ip); + /* + * Just to be safe. I don't know how + * long we can run without blocking + */ + if (need_resched()) { + current->state = TASK_RUNNING; + schedule(); + } + TXN_LOCK(); + } else { + /* We can't get the write lock. It may + * be held by a thread waiting for tlock's + * so let's not block here. Save it to + * put back on the anon_list. + */ + + /* + * We released TXN_LOCK, let's make sure + * this inode is still there + */ + TXN_LOCK(); + if (TxAnchor.anon_list.next != + &jfs_ip->anon_inode_list) + continue; + + /* Take off anon_list */ + list_del(&jfs_ip->anon_inode_list); + + /* Put on anon_list2 */ + list_add(&jfs_ip->anon_inode_list, + &TxAnchor.anon_list2); + } + } + /* Add anon_list2 back to anon_list */ + if (!list_empty(&TxAnchor.anon_list2)) { + list_splice(&TxAnchor.anon_list2, &TxAnchor.anon_list); + INIT_LIST_HEAD(&TxAnchor.anon_list2); + } + TXN_UNLOCK(); + + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } while (!jfs_thread_stopped()); + + jFYI(1, ("jfs_sync being killed\n")); + complete(&jfsIOwait); + return 0; +} + +#if CONFIG_PROC_FS +int jfs_txanchor_read(char *buffer, char **start, off_t offset, int length, + int *eof, void *data) +{ + int len = 0; + off_t begin; + char *freewait; + char *freelockwait; + char *lowlockwait; + + freewait = + waitqueue_active(&TxAnchor.freewait) ? "active" : "empty"; + freelockwait = + waitqueue_active(&TxAnchor.freelockwait) ? "active" : "empty"; + lowlockwait = + waitqueue_active(&TxAnchor.lowlockwait) ? "active" : "empty"; + + len += sprintf(buffer, + "JFS TxAnchor\n" + "============\n" + "freetid = %d\n" + "freewait = %s\n" + "freelock = %d\n" + "freelockwait = %s\n" + "lowlockwait = %s\n" + "tlocksInUse = %d\n" + "unlock_queue = 0x%p\n" + "unlock_tail = 0x%p\n", + TxAnchor.freetid, + freewait, + TxAnchor.freelock, + freelockwait, + lowlockwait, + TxAnchor.tlocksInUse, + TxAnchor.unlock_queue, + TxAnchor.unlock_tail); + + begin = offset; + *start = buffer + begin; + len -= begin; + + if (len > length) + len = length; + else + *eof = 1; + + if (len < 0) + len = 0; + + return len; +} +#endif diff -Nru a/fs/jfs/jfs_txnmgr.h b/fs/jfs/jfs_txnmgr.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_txnmgr.h Thu Mar 7 18:17:47 2002 @@ -0,0 +1,315 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ + +/* + * Change History : + * + */ + +#ifndef _H_JFS_TXNMGR +#define _H_JFS_TXNMGR +/* + * jfs_txnmgr.h: transaction manager + */ + +#include "jfs_logmgr.h" + +/* + * Hide implementation of TxBlock and TxLock + */ +#define tid_to_tblock(tid) (&TxBlock[tid]) + +#define lid_to_tlock(lid) (&TxLock[lid]) + +/* + * transaction block + */ +typedef struct tblock { + /* + * tblock_t and jbuf_t common area: struct logsyncblk + * + * the following 5 fields are the same as struct logsyncblk + * which is common to tblock and jbuf to form logsynclist + */ + u16 xflag; /* tx commit type */ + u16 flag; /* tx commit state */ + lid_t dummy; /* Must keep structures common */ + s32 lsn; /* recovery lsn */ + struct list_head synclist; /* logsynclist link */ + + /* lock management */ + struct super_block *sb; /* 4: super block */ + lid_t next; /* 2: index of first tlock of tid */ + lid_t last; /* 2: index of last tlock of tid */ + wait_queue_head_t waitor; /* 4: tids waiting on this tid */ + + /* log management */ + u32 logtid; /* 4: log transaction id */ + /* (32) */ + + /* commit management */ + struct tblock *cqnext; /* 4: commit queue link */ + s32 clsn; /* 4: commit lsn */ + struct lbuf *bp; /* 4: */ + s32 pn; /* 4: commit record log page number */ + s32 eor; /* 4: commit record eor */ + wait_queue_head_t gcwait; /* 4: group commit event list: + * ready transactions wait on this + * event for group commit completion. + */ + struct inode *ip; /* 4: inode being created or deleted */ + s32 rsrvd; /* 4: */ +} tblock_t; /* (64) */ + +extern struct tblock *TxBlock; /* transaction block table */ + +/* commit flags: tblk->xflag */ +#define COMMIT_SYNC 0x0001 /* synchronous commit */ +#define COMMIT_FORCE 0x0002 /* force pageout at end of commit */ +#define COMMIT_FLUSH 0x0004 /* init flush at end of commit */ +#define COMMIT_MAP 0x00f0 +#define COMMIT_PMAP 0x0010 /* update pmap */ +#define COMMIT_WMAP 0x0020 /* update wmap */ +#define COMMIT_PWMAP 0x0040 /* update pwmap */ +#define COMMIT_FREE 0x0f00 +#define COMMIT_DELETE 0x0100 /* inode delete */ +#define COMMIT_TRUNCATE 0x0200 /* file truncation */ +#define COMMIT_CREATE 0x0400 /* inode create */ +#define COMMIT_LAZY 0x0800 /* lazy commit */ +#define COMMIT_PAGE 0x1000 /* Identifies element as metapage */ +#define COMMIT_INODE 0x2000 /* Identifies element as inode */ + +/* group commit flags tblk->flag: see jfs_logmgr.h */ + +/* + * transaction lock + */ +typedef struct tlock { + lid_t next; /* index next lockword on tid locklist + * next lockword on freelist + */ + tid_t tid; /* transaction id holding lock */ + + u16 flag; /* 2: lock control */ + u16 type; /* 2: log type */ + + struct metapage *mp; /* 4: object page buffer locked */ + struct inode *ip; /* 4: object */ + /* (16) */ + + s16 lock[24]; /* 48: overlay area */ +} tlock_t; /* (64) */ + +extern struct tlock *TxLock; /* transaction lock table */ + +/* + * tlock flag + */ +/* txLock state */ +#define tlckPAGELOCK 0x8000 +#define tlckINODELOCK 0x4000 +#define tlckLINELOCK 0x2000 +#define tlckINLINELOCK 0x1000 +/* lmLog state */ +#define tlckLOG 0x0800 +/* updateMap state */ +#define tlckUPDATEMAP 0x0080 +/* freeLock state */ +#define tlckFREELOCK 0x0008 +#define tlckWRITEPAGE 0x0004 +#define tlckFREEPAGE 0x0002 + +/* + * tlock type + */ +#define tlckTYPE 0xfe00 +#define tlckINODE 0x8000 +#define tlckXTREE 0x4000 +#define tlckDTREE 0x2000 +#define tlckMAP 0x1000 +#define tlckEA 0x0800 +#define tlckACL 0x0400 +#define tlckDATA 0x0200 +#define tlckBTROOT 0x0100 + +#define tlckOPERATION 0x00ff +#define tlckGROW 0x0001 /* file grow */ +#define tlckREMOVE 0x0002 /* file delete */ +#define tlckTRUNCATE 0x0004 /* file truncate */ +#define tlckRELOCATE 0x0008 /* file/directory relocate */ +#define tlckENTRY 0x0001 /* directory insert/delete */ +#define tlckEXTEND 0x0002 /* directory extend in-line */ +#define tlckSPLIT 0x0010 /* splited page */ +#define tlckNEW 0x0020 /* new page from split */ +#define tlckFREE 0x0040 /* free page */ +#define tlckRELINK 0x0080 /* update sibling pointer */ + +/* + * linelock for lmLog() + * + * note: linelock_t and its variations are overlaid + * at tlock.lock: watch for alignment; + */ +typedef struct { + u8 offset; /* 1: */ + u8 length; /* 1: */ +} lv_t; /* (2) */ + +#define TLOCKSHORT 20 +#define TLOCKLONG 28 + +typedef struct { + u16 next; /* 2: next linelock */ + + s8 maxcnt; /* 1: */ + s8 index; /* 1: */ + + u16 flag; /* 2: */ + u8 type; /* 1: */ + u8 l2linesize; /* 1: log2 of linesize */ + /* (8) */ + + lv_t lv[20]; /* 40: */ +} linelock_t; /* (48) */ + +#define dtlock_t linelock_t +#define itlock_t linelock_t + +typedef struct { + u16 next; /* 2: */ + + s8 maxcnt; /* 1: */ + s8 index; /* 1: */ + + u16 flag; /* 2: */ + u8 type; /* 1: */ + u8 l2linesize; /* 1: log2 of linesize */ + /* (8) */ + + lv_t header; /* 2: */ + lv_t lwm; /* 2: low water mark */ + lv_t hwm; /* 2: high water mark */ + lv_t twm; /* 2: */ + /* (16) */ + + s32 pxdlock[8]; /* 32: */ +} xtlock_t; /* (48) */ + + +/* + * maplock for txUpdateMap() + * + * note: maplock_t and its variations are overlaid + * at tlock.lock/linelock: watch for alignment; + * N.B. next field may be set by linelock, and should not + * be modified by maplock; + * N.B. index of the first pxdlock specifies index of next + * free maplock (i.e., number of maplock) in the tlock; + */ +typedef struct { + u16 next; /* 2: */ + + u8 maxcnt; /* 2: */ + u8 index; /* 2: next free maplock index */ + + u16 flag; /* 2: */ + u8 type; /* 1: */ + u8 count; /* 1: number of pxd/xad */ + /* (8) */ + + pxd_t pxd; /* 8: */ +} maplock_t; /* (16): */ + +/* maplock flag */ +#define mlckALLOC 0x00f0 +#define mlckALLOCXADLIST 0x0080 +#define mlckALLOCPXDLIST 0x0040 +#define mlckALLOCXAD 0x0020 +#define mlckALLOCPXD 0x0010 +#define mlckFREE 0x000f +#define mlckFREEXADLIST 0x0008 +#define mlckFREEPXDLIST 0x0004 +#define mlckFREEXAD 0x0002 +#define mlckFREEPXD 0x0001 + +#define pxdlock_t maplock_t + +typedef struct { + u16 next; /* 2: */ + + u8 maxcnt; /* 2: */ + u8 index; /* 2: */ + + u16 flag; /* 2: */ + u8 type; /* 1: */ + u8 count; /* 1: number of pxd/xad */ + /* (8) */ + + void *xdlist; /* 4: pxd/xad list */ + s32 rsrvd; /* 4: */ +} xdlistlock_t; /* (16): */ + + +/* + * commit + * + * parameter to the commit manager routines + */ +typedef struct commit { + tid_t tid; /* 4: tid = index of tblock */ + int flag; /* 4: flags */ + log_t *log; /* 4: log */ + struct super_block *sb; /* 4: superblock */ + + int nip; /* 4: number of entries in iplist */ + struct inode **iplist; /* 4: list of pointers to inodes */ + /* (32) */ + + /* log record descriptor on 64-bit boundary */ + lrd_t lrd; /* : log record descriptor */ +} commit_t; + +/* + * external declarations + */ +extern tlock_t *txLock(tid_t tid, struct inode *ip, struct metapage *mp, int flag); + +extern tlock_t *txMaplock(tid_t tid, struct inode *ip, int flag); + +extern int txCommit(tid_t tid, int nip, struct inode **iplist, int flag); + +extern tid_t txBegin(struct super_block *sb, int flag); + +extern void txBeginAnon(struct super_block *sb); + +extern void txEnd(tid_t tid); + +extern void txAbort(tid_t tid, int dirty); + +extern linelock_t *txLinelock(linelock_t * tlock); + +extern void txFreeMap(struct inode *ip, + maplock_t * maplock, tblock_t * tblk, int maptype); + +extern void txEA(tid_t tid, struct inode *ip, dxd_t * oldea, dxd_t * newea); + +extern void txFreelock(struct inode *ip); + +extern int lmLog(log_t * log, tblock_t * tblk, lrd_t * lrd, tlock_t * tlck); + +#endif /* _H_JFS_TXNMGR */ diff -Nru a/fs/jfs/jfs_types.h b/fs/jfs/jfs_types.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_types.h Thu Mar 7 18:17:47 2002 @@ -0,0 +1,187 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ + +#ifndef _H_JFS_TYPES +#define _H_JFS_TYPES + +/* + * jfs_types.h: + * + * basic type/utility definitions + * + * note: this header file must be the 1st include file + * of JFS include list in all JFS .c file. + */ + +#include +#include + +#include "endian24.h" + +/* + * transaction and lock id's + */ +typedef uint tid_t; +typedef uint lid_t; + +/* + * Almost identical to Linux's timespec, but not quite + */ +struct timestruc_t { + u32 tv_sec; + u32 tv_nsec; +}; + +/* + * handy + */ + +#define LEFTMOSTONE 0x80000000 +#define HIGHORDER 0x80000000u /* high order bit on */ +#define ONES 0xffffffffu /* all bit on */ + +typedef int boolean_t; +#define TRUE 1 +#define FALSE 0 + +/* + * logical xd (lxd) + */ +typedef struct { + unsigned len:24; + unsigned off1:8; + u32 off2; +} lxd_t; + +/* lxd_t field construction */ +#define LXDlength(lxd, length32) ( (lxd)->len = length32 ) +#define LXDoffset(lxd, offset64)\ +{\ + (lxd)->off1 = ((s64)offset64) >> 32;\ + (lxd)->off2 = (offset64) & 0xffffffff;\ +} + +/* lxd_t field extraction */ +#define lengthLXD(lxd) ( (lxd)->len ) +#define offsetLXD(lxd)\ + ( ((s64)((lxd)->off1)) << 32 | (lxd)->off2 ) + +/* lxd list */ +typedef struct { + s16 maxnlxd; + s16 nlxd; + lxd_t *lxd; +} lxdlist_t; + +/* + * physical xd (pxd) + */ +typedef struct { + unsigned len:24; + unsigned addr1:8; + u32 addr2; +} pxd_t; + +/* xd_t field construction */ + +#define PXDlength(pxd, length32) ((pxd)->len = __cpu_to_le24(length32)) +#define PXDaddress(pxd, address64)\ +{\ + (pxd)->addr1 = ((s64)address64) >> 32;\ + (pxd)->addr2 = __cpu_to_le32((address64) & 0xffffffff);\ +} + +/* xd_t field extraction */ +#define lengthPXD(pxd) __le24_to_cpu((pxd)->len) +#define addressPXD(pxd)\ + ( ((s64)((pxd)->addr1)) << 32 | __le32_to_cpu((pxd)->addr2)) + +/* pxd list */ +typedef struct { + s16 maxnpxd; + s16 npxd; + pxd_t pxd[8]; +} pxdlist_t; + + +/* + * data extent descriptor (dxd) + */ +typedef struct { + unsigned flag:8; /* 1: flags */ + unsigned rsrvd:24; /* 3: */ + u32 size; /* 4: size in byte */ + unsigned len:24; /* 3: length in unit of fsblksize */ + unsigned addr1:8; /* 1: address in unit of fsblksize */ + u32 addr2; /* 4: address in unit of fsblksize */ +} dxd_t; /* - 16 - */ + +/* dxd_t flags */ +#define DXD_INDEX 0x80 /* B+-tree index */ +#define DXD_INLINE 0x40 /* in-line data extent */ +#define DXD_EXTENT 0x20 /* out-of-line single extent */ +#define DXD_FILE 0x10 /* out-of-line file (inode) */ +#define DXD_CORRUPT 0x08 /* Inconsistency detected */ + +/* dxd_t field construction + * Conveniently, the PXD macros work for DXD + */ +#define DXDlength PXDlength +#define DXDaddress PXDaddress +#define lengthDXD lengthPXD +#define addressDXD addressPXD + +/* + * directory entry argument + */ +typedef struct component_name { + int namlen; + wchar_t *name; +} component_t; + + +/* + * DASD limit information - stored in directory inode + */ +typedef struct dasd { + u8 thresh; /* Alert Threshold (in percent) */ + u8 delta; /* Alert Threshold delta (in percent) */ + u8 rsrvd1; + u8 limit_hi; /* DASD limit (in logical blocks) */ + u32 limit_lo; /* DASD limit (in logical blocks) */ + u8 rsrvd2[3]; + u8 used_hi; /* DASD usage (in logical blocks) */ + u32 used_lo; /* DASD usage (in logical blocks) */ +} dasd_t; + +#define DASDLIMIT(dasdp) \ + (((u64)((dasdp)->limit_hi) << 32) + __le32_to_cpu((dasdp)->limit_lo)) +#define setDASDLIMIT(dasdp, limit)\ +{\ + (dasdp)->limit_hi = ((u64)limit) >> 32;\ + (dasdp)->limit_lo = __cpu_to_le32(limit);\ +} +#define DASDUSED(dasdp) \ + (((u64)((dasdp)->used_hi) << 32) + __le32_to_cpu((dasdp)->used_lo)) +#define setDASDUSED(dasdp, used)\ +{\ + (dasdp)->used_hi = ((u64)used) >> 32;\ + (dasdp)->used_lo = __cpu_to_le32(used);\ +} + +#endif /* !_H_JFS_TYPES */ diff -Nru a/fs/jfs/jfs_umount.c b/fs/jfs/jfs_umount.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_umount.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,158 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ + +/* + * Change History : + */ + +/* + * jfs_umount.c + * + * note: file system in transition to aggregate/fileset: + * (ref. jfs_mount.c) + * + * file system unmount is interpreted as mount of the single/only + * fileset in the aggregate and, if unmount of the last fileset, + * as unmount of the aggerate; + */ + +#include +#include "jfs_incore.h" +#include "jfs_filsys.h" +#include "jfs_superblock.h" +#include "jfs_dmap.h" +#include "jfs_imap.h" +#include "jfs_metapage.h" +#include "jfs_debug.h" + +/* + * NAME: jfs_umount(vfsp, flags, crp) + * + * FUNCTION: vfs_umount() + * + * PARAMETERS: vfsp - virtual file system pointer + * flags - unmount for shutdown + * crp - credential + * + * RETURN : EBUSY - device has open files + */ +int jfs_umount(struct super_block *sb) +{ + int rc = 0; + log_t *log; + struct jfs_sb_info *sbi = JFS_SBI(sb); + struct inode *ipbmap = sbi->ipbmap; + struct inode *ipimap = sbi->ipimap; + struct inode *ipaimap = sbi->ipaimap; + struct inode *ipaimap2 = sbi->ipaimap2; + + jFYI(1, ("\n UnMount JFS: sb:0x%p\n", sb)); + + /* + * update superblock and close log + * + * if mounted read-write and log based recovery was enabled + */ + if ((log = sbi->log)) { + /* + * close log: + * + * remove file system from log active file system list. + */ + log = sbi->log; + rc = lmLogClose(sb, log); + } + + /* + * close fileset inode allocation map (aka fileset inode) + */ + jEVENT(0, ("jfs_umount: close ipimap:0x%p\n", ipimap)); + diUnmount(ipimap, 0); + + diFreeSpecial(ipimap); + sbi->ipimap = NULL; + + /* + * close secondary aggregate inode allocation map + */ + ipaimap2 = sbi->ipaimap2; + if (ipaimap2) { + jEVENT(0, ("jfs_umount: close ipaimap2:0x%p\n", ipaimap2)); + diUnmount(ipaimap2, 0); + diFreeSpecial(ipaimap2); + sbi->ipaimap2 = NULL; + } + + /* + * close aggregate inode allocation map + */ + ipaimap = sbi->ipaimap; + jEVENT(0, ("jfs_umount: close ipaimap:0x%p\n", ipaimap)); + diUnmount(ipaimap, 0); + diFreeSpecial(ipaimap); + sbi->ipaimap = NULL; + + /* + * close aggregate block allocation map + */ + jEVENT(0, ("jfs_umount: close ipbmap:%p\n", ipbmap)); + dbUnmount(ipbmap, 0); + + diFreeSpecial(ipbmap); + sbi->ipimap = NULL; + + /* + * ensure all file system file pages are propagated to their + * home blocks on disk (and their in-memory buffer pages are + * invalidated) BEFORE updating file system superblock state + * (to signify file system is unmounted cleanly, and thus in + * consistent state) and log superblock active file system + * list (to signify skip logredo()). + */ + if (log) /* log = NULL if read-only mount */ + rc = updateSuper(sb, FM_CLEAN); + + + jFYI(0, (" UnMount JFS Complete: %d\n", rc)); + return rc; +} + + +int jfs_umount_rw(struct super_block *sb) +{ + struct jfs_sb_info *sbi = JFS_SBI(sb); + + if (!sbi->log) + return 0; + + /* + * close log: + * + * remove file system from log active file system list. + */ + lmLogClose(sb, sbi->log); + + dbSync(sbi->ipbmap); + diSync(sbi->ipimap); + + sbi->log = 0; + + return updateSuper(sb, FM_CLEAN); + +} diff -Nru a/fs/jfs/jfs_unicode.c b/fs/jfs/jfs_unicode.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_unicode.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,110 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 "jfs_types.h" +#include "jfs_filsys.h" +#include "jfs_unicode.h" +#include "jfs_debug.h" + +/* + * NAME: jfs_strfromUCS() + * + * FUNCTION: Convert little-endian unicode string to character string + * + */ +int jfs_strfromUCS_le(char *to, const wchar_t * from, /* LITTLE ENDIAN */ + int len, struct nls_table *codepage) +{ + int i; + int outlen = 0; + + for (i = 0; (i < len) && from[i]; i++) { + int charlen; + charlen = + codepage->uni2char(le16_to_cpu(from[i]), &to[outlen], + NLS_MAX_CHARSET_SIZE); + if (charlen > 0) { + outlen += charlen; + } else { + to[outlen++] = '?'; + } + } + to[outlen] = 0; + jEVENT(0, ("jfs_strfromUCS returning %d - '%s'\n", outlen, to)); + return outlen; +} + +/* + * NAME: jfs_strtoUCS() + * + * FUNCTION: Convert character string to unicode string + * + */ +int jfs_strtoUCS(wchar_t * to, + const char *from, int len, struct nls_table *codepage) +{ + int charlen; + int i; + + jEVENT(0, ("jfs_strtoUCS - '%s'\n", from)); + + for (i = 0; len && *from; i++, from += charlen, len -= charlen) { + charlen = codepage->char2uni(from, len, &to[i]); + if (charlen < 1) { + jERROR(1, ("jfs_strtoUCS: char2uni returned %d.\n", + charlen)); + jERROR(1, ("charset = %s, char = 0x%x\n", + codepage->charset, (unsigned char) *from)); + to[i] = 0x003f; /* a question mark */ + charlen = 1; + } + } + + jEVENT(0, (" returning %d\n", i)); + + to[i] = 0; + return i; +} + +/* + * NAME: get_UCSname() + * + * FUNCTION: Allocate and translate to unicode string + * + */ +int get_UCSname(component_t * uniName, struct dentry *dentry, + struct nls_table *nls_tab) +{ + int length = dentry->d_name.len; + + if (length > JFS_NAME_MAX) + return ENAMETOOLONG; + + uniName->name = + kmalloc((length + 1) * sizeof(wchar_t), GFP_NOFS); + + if (uniName->name == NULL) + return ENOSPC; + + uniName->namlen = jfs_strtoUCS(uniName->name, dentry->d_name.name, + length, nls_tab); + + return 0; +} diff -Nru a/fs/jfs/jfs_unicode.h b/fs/jfs/jfs_unicode.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_unicode.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,143 @@ +/* + * unistrk: Unicode kernel case support + * + * Function: + * Convert a unicode character to upper or lower case using + * compressed tables. + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 "jfs_types.h" + +typedef struct { + wchar_t start; + wchar_t end; + signed char *table; +} UNICASERANGE; + +extern signed char UniUpperTable[512]; +extern UNICASERANGE UniUpperRange[]; +extern int get_UCSname(component_t *, struct dentry *, struct nls_table *); +extern int jfs_strfromUCS_le(char *, const wchar_t *, int, struct nls_table *); + +#define free_UCSname(COMP) kfree((COMP)->name) + +/* + * UniStrcpy: Copy a string + */ +static inline wchar_t *UniStrcpy(wchar_t * ucs1, const wchar_t * ucs2) +{ + wchar_t *anchor = ucs1; /* save the start of result string */ + + while ((*ucs1++ = *ucs2++)); + return anchor; +} + + + +/* + * UniStrncpy: Copy length limited string with pad + */ +static inline wchar_t *UniStrncpy(wchar_t * ucs1, const wchar_t * ucs2, + size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = *ucs2++; + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrncmp_le: Compare length limited string - native to little-endian + */ +static inline int UniStrncmp_le(const wchar_t * ucs1, const wchar_t * ucs2, + size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int) *ucs1 - (int) __le16_to_cpu(*ucs2); +} + +/* + * UniStrncpy_le: Copy length limited string with pad to little-endian + */ +static inline wchar_t *UniStrncpy_le(wchar_t * ucs1, const wchar_t * ucs2, + size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = __le16_to_cpu(*ucs2++); + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + + +/* + * UniToupper: Convert a unicode character to upper case + */ +static inline wchar_t UniToupper(register wchar_t uc) +{ + register UNICASERANGE *rp; + + if (uc < sizeof(UniUpperTable)) { /* Latin characters */ + return uc + UniUpperTable[uc]; /* Use base tables */ + } else { + rp = UniUpperRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + } + return uc; /* Past last range */ +} + + +/* + * UniStrupr: Upper case a unicode string + */ +static inline wchar_t *UniStrupr(register wchar_t * upin) +{ + register wchar_t *up; + + up = upin; + while (*up) { /* For all characters */ + *up = UniToupper(*up); + up++; + } + return upin; /* Return input pointer */ +} + diff -Nru a/fs/jfs/jfs_uniupr.c b/fs/jfs/jfs_uniupr.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_uniupr.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,137 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * + * jfs_uniupr.c - Unicode compressed case ranges + * +*/ + +#include +#include "jfs_unicode.h" + +/* + * Latin upper case + */ +signed char UniUpperTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */ + 0,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 060-06f */ + -32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */ + -32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 0e0-0ef */ + -32,-32,-32,-32,-32,-32,-32, 0,-32,-32,-32,-32,-32,-32,-32,121, /* 0f0-0ff */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */ + 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */ + 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */ + 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */ + -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1,-79, 0, -1, /* 1d0-1df */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ + 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ +}; + +/* Upper case range - Greek */ +static signed char UniCaseRangeU03a0[47] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-38,-37,-37,-37, /* 3a0-3af */ + 0,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 3b0-3bf */ + -32,-32,-31,-32,-32,-32,-32,-32,-32,-32,-32,-32,-64,-63,-63, +}; + +/* Upper case range - Cyrillic */ +static signed char UniCaseRangeU0430[48] = { + -32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 430-43f */ + -32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 440-44f */ + 0,-80,-80,-80,-80,-80,-80,-80,-80,-80,-80,-80,-80, 0,-80,-80, /* 450-45f */ +}; + +/* Upper case range - Extended cyrillic */ +static signed char UniCaseRangeU0490[61] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */ + 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, +}; + +/* Upper case range - Extended latin and greek */ +static signed char UniCaseRangeU1e00[509] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */ + 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0,-59, 0, -1, 0, -1, /* 1e90-1e9f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */ + 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */ + 74, 74, 86, 86, 86, 86,100,100, 0, 0,112,112,126,126, 0, 0, /* 1f70-1f7f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */ + 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */ + 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */ + 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Upper case range - Wide latin */ +static signed char UniCaseRangeUff40[27] = { + 0,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* ff40-ff4f */ + -32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, +}; + +/* + * Upper Case Range + */ +UNICASERANGE UniUpperRange[] = { + { 0x03a0, 0x03ce, UniCaseRangeU03a0 }, + { 0x0430, 0x045f, UniCaseRangeU0430 }, + { 0x0490, 0x04cc, UniCaseRangeU0490 }, + { 0x1e00, 0x1ffc, UniCaseRangeU1e00 }, + { 0xff40, 0xff5a, UniCaseRangeUff40 }, + { 0, 0, 0 } +}; diff -Nru a/fs/jfs/jfs_xtree.c b/fs/jfs/jfs_xtree.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_xtree.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,4444 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + */ +/* + * jfs_xtree.c: extent allocation descriptor B+-tree manager + */ + +#include +#include +#include "jfs_incore.h" +#include "jfs_filsys.h" +#include "jfs_metapage.h" +#include "jfs_dmap.h" +#include "jfs_dinode.h" +#include "jfs_superblock.h" +#include "jfs_debug.h" + +/* + * xtree local flag + */ +#define XT_INSERT 0x00000001 + +/* + * xtree key/entry comparison: extent offset + * + * return: + * -1: k < start of extent + * 0: start_of_extent <= k <= end_of_extent + * 1: k > end_of_extent + */ +#define XT_CMP(CMP, K, X, OFFSET64)\ +{\ + OFFSET64 = offsetXAD(X);\ + (CMP) = ((K) >= OFFSET64 + lengthXAD(X)) ? 1 :\ + ((K) < OFFSET64) ? -1 : 0;\ +} + +/* write a xad entry */ +#define XT_PUTENTRY(XAD, FLAG, OFF, LEN, ADDR)\ +{\ + (XAD)->flag = (FLAG);\ + XADoffset((XAD), (OFF));\ + XADlength((XAD), (LEN));\ + XADaddress((XAD), (ADDR));\ +} + +#define XT_PAGE(IP, MP) BT_PAGE(IP, MP, xtpage_t, i_xtroot) + +/* get page buffer for specified block address */ +#define XT_GETPAGE(IP, BN, MP, SIZE, P, RC)\ +{\ + BT_GETPAGE(IP, BN, MP, xtpage_t, SIZE, P, RC, i_xtroot)\ + if (!(RC))\ + {\ + if ((le16_to_cpu((P)->header.nextindex) < XTENTRYSTART) ||\ + (le16_to_cpu((P)->header.nextindex) > le16_to_cpu((P)->header.maxentry)) ||\ + (le16_to_cpu((P)->header.maxentry) > (((BN)==0)?XTROOTMAXSLOT:PSIZE>>L2XTSLOTSIZE)))\ + {\ + jERROR(1,("XT_GETPAGE: xtree page corrupt\n"));\ + BT_PUTPAGE(MP);\ + updateSuper((IP)->i_sb, FM_DIRTY);\ + MP = NULL;\ + RC = EIO;\ + }\ + }\ +} + +/* for consistency */ +#define XT_PUTPAGE(MP) BT_PUTPAGE(MP) + +#define XT_GETSEARCH(IP, LEAF, BN, MP, P, INDEX) \ + BT_GETSEARCH(IP, LEAF, BN, MP, xtpage_t, P, INDEX, i_xtroot) +/* xtree entry parameter descriptor */ +typedef struct { + metapage_t *mp; + s16 index; + u8 flag; + s64 off; + s64 addr; + int len; + pxdlist_t *pxdlist; +} xtsplit_t; + + +/* + * statistics + */ +#ifdef CONFIG_JFS_STATISTICS +static struct { + uint search; + uint fastSearch; + uint split; +} xtStat; +#endif + + +/* + * forward references + */ +static int xtSearch(struct inode *ip, + s64 xoff, int *cmpp, btstack_t * btstack, int flag); + +static int xtSplitUp(tid_t tid, + struct inode *ip, + xtsplit_t * split, btstack_t * btstack); + +static int xtSplitPage(tid_t tid, + struct inode *ip, + xtsplit_t * split, metapage_t ** rmpp, s64 * rbnp); + +static int xtSplitRoot(tid_t tid, + struct inode *ip, + xtsplit_t * split, metapage_t ** rmpp); + +#ifdef _STILL_TO_PORT +static int xtDeleteUp(tid_t tid, + struct inode *ip, + metapage_t * fmp, + xtpage_t * fp, btstack_t * btstack); + +static int xtSearchNode(struct inode *ip, + xad_t * xad, + int *cmpp, btstack_t * btstack, int flag); + +static int xtRelink(tid_t tid, struct inode *ip, xtpage_t * fp); +#endif /* _STILL_TO_PORT */ + +/* External references */ + +/* + * debug control + */ +/* #define _JFS_DEBUG_XTREE 1 */ + + +/* + * xtLookup() + * + * function: map a single page into a physical extent; + */ +int xtLookup(struct inode *ip, s64 lstart, + s64 llen, int *pflag, s64 * paddr, s32 * plen, int no_check) +{ + int rc = 0; + btstack_t btstack; + int cmp; + s64 bn; + metapage_t *mp; + xtpage_t *p; + int index; + xad_t *xad; + s64 size, xoff, xend; + int xlen; + s64 xaddr; + + *plen = 0; + + if (!no_check) { + /* is lookup offset beyond eof ? */ + size = ((u64) ip->i_size + (JFS_SBI(ip->i_sb)->bsize - 1)) >> + JFS_SBI(ip->i_sb)->l2bsize; + if (lstart >= size) { + jERROR(1, + ("xtLookup: lstart (0x%lx) >= size (0x%lx)\n", + (ulong) lstart, (ulong) size)); + return 0; + } + } + + /* + * search for the xad entry covering the logical extent + */ +//search: + if ((rc = xtSearch(ip, lstart, &cmp, &btstack, 0))) { + jERROR(1, ("xtLookup: xtSearch returned %d\n", rc)); + return rc; + } + + /* + * compute the physical extent covering logical extent + * + * N.B. search may have failed (e.g., hole in sparse file), + * and returned the index of the next entry. + */ + /* retrieve search result */ + XT_GETSEARCH(ip, btstack.top, bn, mp, p, index); + + /* is xad found covering start of logical extent ? + * lstart is a page start address, + * i.e., lstart cannot start in a hole; + */ + if (cmp) { + jFYI(1, ("xtLookup: cmp = %d\n", cmp)); + goto out; + } + + /* + * lxd covered by xad + */ + xad = &p->xad[index]; + xoff = offsetXAD(xad); + xlen = lengthXAD(xad); + xend = xoff + xlen; + xaddr = addressXAD(xad); + + jEVENT(0, + ("index = %d, xoff = 0x%lx, xlen = 0x%x, xaddr = 0x%lx\n", + index, (ulong) xoff, xlen, (ulong) xaddr)); + + /* initialize new pxd */ + *pflag = xad->flag; + *paddr = xaddr + (lstart - xoff); + /* a page must be fully covered by an xad */ + *plen = min(xend - lstart, llen); + + out: + XT_PUTPAGE(mp); + + return rc; +} + + +/* + * xtLookupList() + * + * function: map a single logical extent into a list of physical extent; + * + * parameter: + * struct inode *ip, + * lxdlist_t *lxdlist, lxd list (in) + * xadlist_t *xadlist, xad list (in/out) + * int flag) + * + * coverage of lxd by xad under assumption of + * . lxd's are ordered and disjoint. + * . xad's are ordered and disjoint. + * + * return: + * 0: success + * + * note: a page being written (even a single byte) is backed fully, + * except the last page which is only backed with blocks + * required to cover the last byte; + * the extent backing a page is fully contained within an xad; + */ +int xtLookupList(struct inode *ip, lxdlist_t * lxdlist, /* lxd list (in) */ + xadlist_t * xadlist, /* xad list (in/out) */ + int flag) +{ + int rc = 0; + btstack_t btstack; + int cmp; + s64 bn; + metapage_t *mp; + xtpage_t *p; + int index; + lxd_t *lxd; + xad_t *xad, *pxd; + s64 size, lstart, lend, xstart, xend, pstart; + s64 llen, xlen, plen; + s64 xaddr, paddr; + int nlxd, npxd, maxnpxd; + + npxd = xadlist->nxad = 0; + maxnpxd = xadlist->maxnxad; + pxd = xadlist->xad; + + nlxd = lxdlist->nlxd; + lxd = lxdlist->lxd; + + lstart = offsetLXD(lxd); + llen = lengthLXD(lxd); + lend = lstart + llen; + + size = (ip->i_size + (JFS_SBI(ip->i_sb)->bsize - 1)) >> + JFS_SBI(ip->i_sb)->l2bsize; + + /* + * search for the xad entry covering the logical extent + */ + search: + if (lstart >= size) + return 0; + + if ((rc = xtSearch(ip, lstart, &cmp, &btstack, 0))) + return rc; + + /* + * compute the physical extent covering logical extent + * + * N.B. search may have failed (e.g., hole in sparse file), + * and returned the index of the next entry. + */ +//map: + /* retrieve search result */ + XT_GETSEARCH(ip, btstack.top, bn, mp, p, index); + + /* is xad on the next sibling page ? */ + if (index == le16_to_cpu(p->header.nextindex)) { + if (p->header.flag & BT_ROOT) + goto mapend; + + if ((bn = le64_to_cpu(p->header.next)) == 0) + goto mapend; + + XT_PUTPAGE(mp); + + /* get next sibling page */ + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + index = XTENTRYSTART; + } + + xad = &p->xad[index]; + + /* + * is lxd covered by xad ? + */ + compare: + xstart = offsetXAD(xad); + xlen = lengthXAD(xad); + xend = xstart + xlen; + xaddr = addressXAD(xad); + + compare1: + if (xstart < lstart) + goto compare2; + + /* (lstart <= xstart) */ + + /* lxd is NOT covered by xad */ + if (lend <= xstart) { + /* + * get next lxd + */ + if (--nlxd == 0) + goto mapend; + lxd++; + + lstart = offsetLXD(lxd); + llen = lengthLXD(lxd); + lend = lstart + llen; + if (lstart >= size) + goto mapend; + + /* compare with the current xad */ + goto compare1; + } + /* lxd is covered by xad */ + else { /* (xstart < lend) */ + + /* initialize new pxd */ + pstart = xstart; + plen = min(lend - xstart, xlen); + paddr = xaddr; + + goto cover; + } + + /* (xstart < lstart) */ + compare2: + /* lxd is covered by xad */ + if (lstart < xend) { + /* initialize new pxd */ + pstart = lstart; + plen = min(xend - lstart, llen); + paddr = xaddr + (lstart - xstart); + + goto cover; + } + /* lxd is NOT covered by xad */ + else { /* (xend <= lstart) */ + + /* + * get next xad + * + * linear search next xad covering lxd on + * the current xad page, and then tree search + */ + if (index == le16_to_cpu(p->header.nextindex) - 1) { + if (p->header.flag & BT_ROOT) + goto mapend; + + XT_PUTPAGE(mp); + goto search; + } else { + index++; + xad++; + + /* compare with new xad */ + goto compare; + } + } + + /* + * lxd is covered by xad and a new pxd has been initialized + * (lstart <= xstart < lend) or (xstart < lstart < xend) + */ + cover: + /* finalize pxd corresponding to current xad */ + XT_PUTENTRY(pxd, xad->flag, pstart, plen, paddr); + + if (++npxd >= maxnpxd) + goto mapend; + pxd++; + + /* + * lxd is fully covered by xad + */ + if (lend <= xend) { + /* + * get next lxd + */ + if (--nlxd == 0) + goto mapend; + lxd++; + + lstart = offsetLXD(lxd); + llen = lengthLXD(lxd); + lend = lstart + llen; + if (lstart >= size) + goto mapend; + + /* + * test for old xad covering new lxd + * (old xstart < new lstart) + */ + goto compare2; + } + /* + * lxd is partially covered by xad + */ + else { /* (xend < lend) */ + + /* + * get next xad + * + * linear search next xad covering lxd on + * the current xad page, and then next xad page search + */ + if (index == le16_to_cpu(p->header.nextindex) - 1) { + if (p->header.flag & BT_ROOT) + goto mapend; + + if ((bn = le64_to_cpu(p->header.next)) == 0) + goto mapend; + + XT_PUTPAGE(mp); + + /* get next sibling page */ + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + index = XTENTRYSTART; + xad = &p->xad[index]; + } else { + index++; + xad++; + } + + /* + * test for new xad covering old lxd + * (old lstart < new xstart) + */ + goto compare; + } + + mapend: + xadlist->nxad = npxd; + +//out: + XT_PUTPAGE(mp); + + return rc; +} + + +/* + * xtSearch() + * + * function: search for the xad entry covering specified offset. + * + * parameters: + * ip - file object; + * xoff - extent offset; + * cmpp - comparison result: + * btstack - traverse stack; + * flag - search process flag (XT_INSERT); + * + * returns: + * btstack contains (bn, index) of search path traversed to the entry. + * *cmpp is set to result of comparison with the entry returned. + * the page containing the entry is pinned at exit. + */ +static int xtSearch(struct inode *ip, s64 xoff, /* offset of extent */ + int *cmpp, btstack_t * btstack, int flag) +{ + struct jfs_inode_info *jfs_ip = JFS_IP(ip); + int rc = 0; + int cmp = 1; /* init for empty page */ + s64 bn; /* block number */ + metapage_t *mp; /* page buffer */ + xtpage_t *p; /* page */ + xad_t *xad; + int base, index, lim, btindex; + btframe_t *btsp; + int nsplit = 0; /* number of pages to split */ + s64 t64; + + INCREMENT(xtStat.search); + + BT_CLR(btstack); + + btstack->nsplit = 0; + + /* + * search down tree from root: + * + * between two consecutive entries of and of + * internal page, child page Pi contains entry with k, Ki <= K < Kj. + * + * if entry with search key K is not found + * internal page search find the entry with largest key Ki + * less than K which point to the child page to search; + * leaf page search find the entry with smallest key Kj + * greater than K so that the returned index is the position of + * the entry to be shifted right for insertion of new entry. + * for empty tree, search key is greater than any key of the tree. + * + * by convention, root bn = 0. + */ + for (bn = 0;;) { + /* get/pin the page to search */ + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + /* try sequential access heuristics with the previous + * access entry in target leaf page: + * once search narrowed down into the target leaf, + * key must either match an entry in the leaf or + * key entry does not exist in the tree; + */ +//fastSearch: + if ((jfs_ip->btorder & BT_SEQUENTIAL) && + (p->header.flag & BT_LEAF) && + (index = jfs_ip->btindex) < + le16_to_cpu(p->header.nextindex)) { + xad = &p->xad[index]; + t64 = offsetXAD(xad); + if (xoff < t64 + lengthXAD(xad)) { + if (xoff >= t64) { + *cmpp = 0; + goto out; + } + + /* stop sequential access heuristics */ + goto binarySearch; + } else { /* (t64 + lengthXAD(xad)) <= xoff */ + + /* try next sequential entry */ + index++; + if (index < + le16_to_cpu(p->header.nextindex)) { + xad++; + t64 = offsetXAD(xad); + if (xoff < t64 + lengthXAD(xad)) { + if (xoff >= t64) { + *cmpp = 0; + goto out; + } + + /* miss: key falls between + * previous and this entry + */ + *cmpp = 1; + goto out; + } + + /* (xoff >= t64 + lengthXAD(xad)); + * matching entry may be further out: + * stop heuristic search + */ + /* stop sequential access heuristics */ + goto binarySearch; + } + + /* (index == p->header.nextindex); + * miss: key entry does not exist in + * the target leaf/tree + */ + *cmpp = 1; + goto out; + } + + /* + * if hit, return index of the entry found, and + * if miss, where new entry with search key is + * to be inserted; + */ + out: + /* compute number of pages to split */ + if (flag & XT_INSERT) { + if (p->header.nextindex == /* little-endian */ + p->header.maxentry) + nsplit++; + else + nsplit = 0; + btstack->nsplit = nsplit; + } + + /* save search result */ + btsp = btstack->top; + btsp->bn = bn; + btsp->index = index; + btsp->mp = mp; + + /* update sequential access heuristics */ + jfs_ip->btindex = index; + + INCREMENT(xtStat.fastSearch); + return 0; + } + + /* well, ... full search now */ + binarySearch: + lim = le16_to_cpu(p->header.nextindex) - XTENTRYSTART; + + /* + * binary search with search key K on the current page + */ + for (base = XTENTRYSTART; lim; lim >>= 1) { + index = base + (lim >> 1); + + XT_CMP(cmp, xoff, &p->xad[index], t64); + if (cmp == 0) { + /* + * search hit + */ + /* search hit - leaf page: + * return the entry found + */ + if (p->header.flag & BT_LEAF) { + *cmpp = cmp; + + /* compute number of pages to split */ + if (flag & XT_INSERT) { + if (p->header.nextindex == + p->header.maxentry) + nsplit++; + else + nsplit = 0; + btstack->nsplit = nsplit; + } + + /* save search result */ + btsp = btstack->top; + btsp->bn = bn; + btsp->index = index; + btsp->mp = mp; + + /* init sequential access heuristics */ + btindex = jfs_ip->btindex; + if (index == btindex || + index == btindex + 1) + jfs_ip->btorder = BT_SEQUENTIAL; + else + jfs_ip->btorder = BT_RANDOM; + jfs_ip->btindex = index; + + return 0; + } + + /* search hit - internal page: + * descend/search its child page + */ + goto next; + } + + if (cmp > 0) { + base = index + 1; + --lim; + } + } + + /* + * search miss + * + * base is the smallest index with key (Kj) greater than + * search key (K) and may be zero or maxentry index. + */ + /* + * search miss - leaf page: + * + * return location of entry (base) where new entry with + * search key K is to be inserted. + */ + if (p->header.flag & BT_LEAF) { + *cmpp = cmp; + + /* compute number of pages to split */ + if (flag & XT_INSERT) { + if (p->header.nextindex == + p->header.maxentry) + nsplit++; + else + nsplit = 0; + btstack->nsplit = nsplit; + } + + /* save search result */ + btsp = btstack->top; + btsp->bn = bn; + btsp->index = base; + btsp->mp = mp; + + /* init sequential access heuristics */ + btindex = jfs_ip->btindex; + if (base == btindex || base == btindex + 1) + jfs_ip->btorder = BT_SEQUENTIAL; + else + jfs_ip->btorder = BT_RANDOM; + jfs_ip->btindex = base; + + return 0; + } + + /* + * search miss - non-leaf page: + * + * if base is non-zero, decrement base by one to get the parent + * entry of the child page to search. + */ + index = base ? base - 1 : base; + + /* + * go down to child page + */ + next: + /* update number of pages to split */ + if (p->header.nextindex == p->header.maxentry) + nsplit++; + else + nsplit = 0; + + /* push (bn, index) of the parent page/entry */ + BT_PUSH(btstack, bn, index); + + /* get the child page block number */ + bn = addressXAD(&p->xad[index]); + + /* unpin the parent page */ + XT_PUTPAGE(mp); + } +} + +/* + * xtInsert() + * + * function: + * + * parameter: + * tid - transaction id; + * ip - file object; + * xflag - extent flag (XAD_NOTRECORDED): + * xoff - extent offset; + * xlen - extent length; + * xaddrp - extent address pointer (in/out): + * if (*xaddrp) + * caller allocated data extent at *xaddrp; + * else + * allocate data extent and return its xaddr; + * flag - + * + * return: + */ +int xtInsert(tid_t tid, /* transaction id */ + struct inode *ip, int xflag, s64 xoff, s32 xlen, s64 * xaddrp, + int flag) +{ + int rc = 0; + s64 xaddr, hint; + metapage_t *mp; /* meta-page buffer */ + xtpage_t *p; /* base B+-tree index page */ + s64 bn; + int index, nextindex; + btstack_t btstack; /* traverse stack */ + xtsplit_t split; /* split information */ + xad_t *xad; + int cmp; + tlock_t *tlck; + xtlock_t *xtlck; + + jFYI(1, + ("xtInsert: nxoff:0x%lx nxlen:0x%x\n", (ulong) xoff, xlen)); + + /* + * search for the entry location at which to insert: + * + * xtFastSearch() and xtSearch() both returns (leaf page + * pinned, index at which to insert). + * n.b. xtSearch() may return index of maxentry of + * the full page. + */ + if ((rc = xtSearch(ip, xoff, &cmp, &btstack, XT_INSERT))) + return rc; + + /* retrieve search result */ + XT_GETSEARCH(ip, btstack.top, bn, mp, p, index); + + /* This test must follow XT_GETSEARCH since mp must be valid if + * we branch to out: */ + if (cmp == 0) { + rc = EEXIST; + goto out; + } + + /* + * allocate data extent requested + * + * allocation hint: last xad + */ + if ((xaddr = *xaddrp) == 0) { + if (index > XTENTRYSTART) { + xad = &p->xad[index - 1]; + hint = addressXAD(xad) + lengthXAD(xad) - 1; + } else + hint = 0; + if ((rc = dbAlloc(ip, hint, (s64) xlen, &xaddr))) + goto out; + } + + /* + * insert entry for new extent + */ + xflag |= XAD_NEW; + + /* + * if the leaf page is full, split the page and + * propagate up the router entry for the new page from split + * + * The xtSplitUp() will insert the entry and unpin the leaf page. + */ + nextindex = le16_to_cpu(p->header.nextindex); + if (nextindex == le16_to_cpu(p->header.maxentry)) { + split.mp = mp; + split.index = index; + split.flag = xflag; + split.off = xoff; + split.len = xlen; + split.addr = xaddr; + split.pxdlist = NULL; + if ((rc = xtSplitUp(tid, ip, &split, &btstack))) { + /* undo data extent allocation */ + if (*xaddrp == 0) + dbFree(ip, xaddr, (s64) xlen); + return rc; + } + + *xaddrp = xaddr; + return 0; + } + + /* + * insert the new entry into the leaf page + */ + /* + * acquire a transaction lock on the leaf page; + * + * action: xad insertion/extension; + */ + BT_MARK_DIRTY(mp, ip); + + /* if insert into middle, shift right remaining entries. */ + if (index < nextindex) + memmove(&p->xad[index + 1], &p->xad[index], + (nextindex - index) * sizeof(xad_t)); + + /* insert the new entry: mark the entry NEW */ + xad = &p->xad[index]; + XT_PUTENTRY(xad, xflag, xoff, xlen, xaddr); + + /* advance next available entry index */ + p->header.nextindex = + cpu_to_le16(le16_to_cpu(p->header.nextindex) + 1); + + /* Don't log it if there are no links to the file */ + if (!test_cflag(COMMIT_Nolink, ip)) { + tlck = txLock(tid, ip, mp, tlckXTREE | tlckGROW); + xtlck = (xtlock_t *) & tlck->lock; + xtlck->lwm.offset = + (xtlck->lwm.offset) ? min(index, + (int)xtlck->lwm.offset) : index; + xtlck->lwm.length = + le16_to_cpu(p->header.nextindex) - xtlck->lwm.offset; + } + + *xaddrp = xaddr; + + out: + /* unpin the leaf page */ + XT_PUTPAGE(mp); + + return rc; +} + + +/* + * xtSplitUp() + * + * function: + * split full pages as propagating insertion up the tree + * + * parameter: + * tid - transaction id; + * ip - file object; + * split - entry parameter descriptor; + * btstack - traverse stack from xtSearch() + * + * return: + */ +static int +xtSplitUp(tid_t tid, + struct inode *ip, xtsplit_t * split, btstack_t * btstack) +{ + int rc = 0; + metapage_t *smp; + xtpage_t *sp; /* split page */ + metapage_t *rmp; + s64 rbn; /* new right page block number */ + metapage_t *rcmp; + xtpage_t *rcp; /* right child page */ + s64 rcbn; /* right child page block number */ + int skip; /* index of entry of insertion */ + int nextindex; /* next available entry index of p */ + btframe_t *parent; /* parent page entry on traverse stack */ + xad_t *xad; + s64 xaddr; + int xlen; + int nsplit; /* number of pages split */ + pxdlist_t pxdlist; + pxd_t *pxd; + tlock_t *tlck; + xtlock_t *xtlck; + + smp = split->mp; + sp = XT_PAGE(ip, smp); + + /* is inode xtree root extension/inline EA area free ? */ + if ((sp->header.flag & BT_ROOT) && (!S_ISDIR(ip->i_mode)) && + (sp->header.maxentry < cpu_to_le16(XTROOTMAXSLOT)) && + (JFS_IP(ip)->mode2 & INLINEEA)) { + sp->header.maxentry = cpu_to_le16(XTROOTMAXSLOT); + JFS_IP(ip)->mode2 &= ~INLINEEA; + + BT_MARK_DIRTY(smp, ip); + /* + * acquire a transaction lock on the leaf page; + * + * action: xad insertion/extension; + */ + + /* if insert into middle, shift right remaining entries. */ + skip = split->index; + nextindex = le16_to_cpu(sp->header.nextindex); + if (skip < nextindex) + memmove(&sp->xad[skip + 1], &sp->xad[skip], + (nextindex - skip) * sizeof(xad_t)); + + /* insert the new entry: mark the entry NEW */ + xad = &sp->xad[skip]; + XT_PUTENTRY(xad, split->flag, split->off, split->len, + split->addr); + + /* advance next available entry index */ + sp->header.nextindex = + cpu_to_le16(le16_to_cpu(sp->header.nextindex) + 1); + + /* Don't log it if there are no links to the file */ + if (!test_cflag(COMMIT_Nolink, ip)) { + tlck = txLock(tid, ip, smp, tlckXTREE | tlckGROW); + xtlck = (xtlock_t *) & tlck->lock; + xtlck->lwm.offset = (xtlck->lwm.offset) ? + min(skip, (int)xtlck->lwm.offset) : skip; + xtlck->lwm.length = + le16_to_cpu(sp->header.nextindex) - + xtlck->lwm.offset; + } + + return 0; + } + + /* + * allocate new index blocks to cover index page split(s) + * + * allocation hint: ? + */ + if (split->pxdlist == NULL) { + nsplit = btstack->nsplit; + split->pxdlist = &pxdlist; + pxdlist.maxnpxd = pxdlist.npxd = 0; + pxd = &pxdlist.pxd[0]; + xlen = JFS_SBI(ip->i_sb)->nbperpage; + for (; nsplit > 0; nsplit--, pxd++) { + if ((rc = dbAlloc(ip, (s64) 0, (s64) xlen, &xaddr)) + == 0) { + PXDaddress(pxd, xaddr); + PXDlength(pxd, xlen); + + pxdlist.maxnpxd++; + + continue; + } + + /* undo allocation */ + + XT_PUTPAGE(smp); + return rc; + } + } + + /* + * Split leaf page into and a new right page . + * + * The split routines insert the new entry into the leaf page, + * and acquire txLock as appropriate. + * return pinned and its block number . + */ + rc = (sp->header.flag & BT_ROOT) ? + xtSplitRoot(tid, ip, split, &rmp) : + xtSplitPage(tid, ip, split, &rmp, &rbn); + if (rc) + return EIO; + + XT_PUTPAGE(smp); + + /* + * propagate up the router entry for the leaf page just split + * + * insert a router entry for the new page into the parent page, + * propagate the insert/split up the tree by walking back the stack + * of (bn of parent page, index of child page entry in parent page) + * that were traversed during the search for the page that split. + * + * the propagation of insert/split up the tree stops if the root + * splits or the page inserted into doesn't have to split to hold + * the new entry. + * + * the parent entry for the split page remains the same, and + * a new entry is inserted at its right with the first key and + * block number of the new right page. + * + * There are a maximum of 3 pages pinned at any time: + * right child, left parent and right parent (when the parent splits) + * to keep the child page pinned while working on the parent. + * make sure that all pins are released at exit. + */ + while ((parent = BT_POP(btstack)) != NULL) { + /* parent page specified by stack frame */ + + /* keep current child pages pinned */ + rcmp = rmp; + rcbn = rbn; + rcp = XT_PAGE(ip, rcmp); + + /* + * insert router entry in parent for new right child page + */ + /* get/pin the parent page */ + XT_GETPAGE(ip, parent->bn, smp, PSIZE, sp, rc); + if (rc) + goto errout2; + + /* + * The new key entry goes ONE AFTER the index of parent entry, + * because the split was to the right. + */ + skip = parent->index + 1; + + /* + * split or shift right remaining entries of the parent page + */ + nextindex = le16_to_cpu(sp->header.nextindex); + /* + * parent page is full - split the parent page + */ + if (nextindex == le16_to_cpu(sp->header.maxentry)) { + /* init for parent page split */ + split->mp = smp; + split->index = skip; /* index at insert */ + split->flag = XAD_NEW; + split->off = offsetXAD(&rcp->xad[XTENTRYSTART]); + split->len = JFS_SBI(ip->i_sb)->nbperpage; + split->addr = rcbn; + + /* unpin previous right child page */ + XT_PUTPAGE(rcmp); + + /* The split routines insert the new entry, + * and acquire txLock as appropriate. + * return pinned and its block number . + */ + rc = (sp->header.flag & BT_ROOT) ? + xtSplitRoot(tid, ip, split, &rmp) : + xtSplitPage(tid, ip, split, &rmp, &rbn); + if (rc) + goto errout1; + + XT_PUTPAGE(smp); + /* keep new child page pinned */ + } + /* + * parent page is not full - insert in parent page + */ + else { + /* + * insert router entry in parent for the right child + * page from the first entry of the right child page: + */ + /* + * acquire a transaction lock on the parent page; + * + * action: router xad insertion; + */ + BT_MARK_DIRTY(smp, ip); + + /* + * if insert into middle, shift right remaining entries + */ + if (skip < nextindex) + memmove(&sp->xad[skip + 1], &sp->xad[skip], + (nextindex - + skip) << L2XTSLOTSIZE); + + /* insert the router entry */ + xad = &sp->xad[skip]; + XT_PUTENTRY(xad, XAD_NEW, + offsetXAD(&rcp->xad[XTENTRYSTART]), + JFS_SBI(ip->i_sb)->nbperpage, rcbn); + + /* advance next available entry index. */ + sp->header.nextindex = + cpu_to_le16(le16_to_cpu(sp->header.nextindex) + + 1); + + /* Don't log it if there are no links to the file */ + if (!test_cflag(COMMIT_Nolink, ip)) { + tlck = txLock(tid, ip, smp, + tlckXTREE | tlckGROW); + xtlck = (xtlock_t *) & tlck->lock; + xtlck->lwm.offset = (xtlck->lwm.offset) ? + min(skip, (int)xtlck->lwm.offset) : skip; + xtlck->lwm.length = + le16_to_cpu(sp->header.nextindex) - + xtlck->lwm.offset; + } + + /* unpin parent page */ + XT_PUTPAGE(smp); + + /* exit propagate up */ + break; + } + } + + /* unpin current right page */ + XT_PUTPAGE(rmp); + + return 0; + + /* + * If something fails in the above loop we were already walking back + * up the tree and the tree is now inconsistent. + * release all pages we're holding. + */ + errout1: + XT_PUTPAGE(smp); + + errout2: + XT_PUTPAGE(rcmp); + + return rc; +} + + +/* + * xtSplitPage() + * + * function: + * split a full non-root page into + * original/split/left page and new right page + * i.e., the original/split page remains as left page. + * + * parameter: + * int tid, + * struct inode *ip, + * xtsplit_t *split, + * metapage_t **rmpp, + * u64 *rbnp, + * + * return: + * Pointer to page in which to insert or NULL on error. + */ +static int +xtSplitPage(tid_t tid, struct inode *ip, + xtsplit_t * split, metapage_t ** rmpp, s64 * rbnp) +{ + int rc = 0; + metapage_t *smp; + xtpage_t *sp; + metapage_t *rmp; + xtpage_t *rp; /* new right page allocated */ + s64 rbn; /* new right page block number */ + metapage_t *mp; + xtpage_t *p; + s64 nextbn; + int skip, maxentry, middle, righthalf, n; + xad_t *xad; + pxdlist_t *pxdlist; + pxd_t *pxd; + tlock_t *tlck; + xtlock_t *sxtlck = 0, *rxtlck = 0; + + smp = split->mp; + sp = XT_PAGE(ip, smp); + + INCREMENT(xtStat.split); + + /* + * allocate the new right page for the split + */ + pxdlist = split->pxdlist; + pxd = &pxdlist->pxd[pxdlist->npxd]; + pxdlist->npxd++; + rbn = addressPXD(pxd); + rmp = get_metapage(ip, rbn, PSIZE, 1); + if (rmp == NULL) + return EIO; + + jEVENT(0, + ("xtSplitPage: ip:0x%p smp:0x%p rmp:0x%p\n", ip, smp, rmp)); + + BT_MARK_DIRTY(rmp, ip); + /* + * action: new page; + */ + + rp = (xtpage_t *) rmp->data; + rp->header.self = *pxd; + rp->header.flag = sp->header.flag & BT_TYPE; + rp->header.maxentry = sp->header.maxentry; /* little-endian */ + rp->header.nextindex = cpu_to_le16(XTENTRYSTART); + + BT_MARK_DIRTY(smp, ip); + /* Don't log it if there are no links to the file */ + if (!test_cflag(COMMIT_Nolink, ip)) { + /* + * acquire a transaction lock on the new right page; + */ + tlck = txLock(tid, ip, rmp, tlckXTREE | tlckNEW); + rxtlck = (xtlock_t *) & tlck->lock; + rxtlck->lwm.offset = XTENTRYSTART; + /* + * acquire a transaction lock on the split page + */ + tlck = txLock(tid, ip, smp, tlckXTREE | tlckGROW); + sxtlck = (xtlock_t *) & tlck->lock; + } + + /* + * initialize/update sibling pointers of and + */ + nextbn = le64_to_cpu(sp->header.next); + rp->header.next = cpu_to_le64(nextbn); + rp->header.prev = cpu_to_le64(addressPXD(&sp->header.self)); + sp->header.next = cpu_to_le64(rbn); + + skip = split->index; + + /* + * sequential append at tail (after last entry of last page) + * + * if splitting the last page on a level because of appending + * a entry to it (skip is maxentry), it's likely that the access is + * sequential. adding an empty page on the side of the level is less + * work and can push the fill factor much higher than normal. + * if we're wrong it's no big deal - we will do the split the right + * way next time. + * (it may look like it's equally easy to do a similar hack for + * reverse sorted data, that is, split the tree left, but it's not. + * Be my guest.) + */ + if (nextbn == 0 && skip == le16_to_cpu(sp->header.maxentry)) { + /* + * acquire a transaction lock on the new/right page; + * + * action: xad insertion; + */ + /* insert entry at the first entry of the new right page */ + xad = &rp->xad[XTENTRYSTART]; + XT_PUTENTRY(xad, split->flag, split->off, split->len, + split->addr); + + rp->header.nextindex = cpu_to_le16(XTENTRYSTART + 1); + + if (!test_cflag(COMMIT_Nolink, ip)) { + /* rxtlck->lwm.offset = XTENTRYSTART; */ + rxtlck->lwm.length = 1; + } + + *rmpp = rmp; + *rbnp = rbn; + + ip->i_blocks += LBLK2PBLK(ip->i_sb, lengthPXD(pxd)); + + jEVENT(0, ("xtSplitPage: sp:0x%p rp:0x%p\n", sp, rp)); + return 0; + } + + /* + * non-sequential insert (at possibly middle page) + */ + + /* + * update previous pointer of old next/right page of + */ + if (nextbn != 0) { + XT_GETPAGE(ip, nextbn, mp, PSIZE, p, rc); + if (rc) { + XT_PUTPAGE(rmp); + return rc; + } + + BT_MARK_DIRTY(mp, ip); + /* + * acquire a transaction lock on the next page; + * + * action:sibling pointer update; + */ + if (!test_cflag(COMMIT_Nolink, ip)) + tlck = txLock(tid, ip, mp, tlckXTREE | tlckRELINK); + + p->header.prev = cpu_to_le64(rbn); + + /* sibling page may have been updated previously, or + * it may be updated later; + */ + + XT_PUTPAGE(mp); + } + + /* + * split the data between the split and new/right pages + */ + maxentry = le16_to_cpu(sp->header.maxentry); + middle = maxentry >> 1; + righthalf = maxentry - middle; + + /* + * skip index in old split/left page - insert into left page: + */ + if (skip <= middle) { + /* move right half of split page to the new right page */ + memmove(&rp->xad[XTENTRYSTART], &sp->xad[middle], + righthalf << L2XTSLOTSIZE); + + /* shift right tail of left half to make room for new entry */ + if (skip < middle) + memmove(&sp->xad[skip + 1], &sp->xad[skip], + (middle - skip) << L2XTSLOTSIZE); + + /* insert new entry */ + xad = &sp->xad[skip]; + XT_PUTENTRY(xad, split->flag, split->off, split->len, + split->addr); + + /* update page header */ + sp->header.nextindex = cpu_to_le16(middle + 1); + if (!test_cflag(COMMIT_Nolink, ip)) { + sxtlck->lwm.offset = (sxtlck->lwm.offset) ? + min(skip, (int)sxtlck->lwm.offset) : skip; + } + + rp->header.nextindex = + cpu_to_le16(XTENTRYSTART + righthalf); + } + /* + * skip index in new right page - insert into right page: + */ + else { + /* move left head of right half to right page */ + n = skip - middle; + memmove(&rp->xad[XTENTRYSTART], &sp->xad[middle], + n << L2XTSLOTSIZE); + + /* insert new entry */ + n += XTENTRYSTART; + xad = &rp->xad[n]; + XT_PUTENTRY(xad, split->flag, split->off, split->len, + split->addr); + + /* move right tail of right half to right page */ + if (skip < maxentry) + memmove(&rp->xad[n + 1], &sp->xad[skip], + (maxentry - skip) << L2XTSLOTSIZE); + + /* update page header */ + sp->header.nextindex = cpu_to_le16(middle); + if (!test_cflag(COMMIT_Nolink, ip)) { + sxtlck->lwm.offset = (sxtlck->lwm.offset) ? + min(middle, (int)sxtlck->lwm.offset) : middle; + } + + rp->header.nextindex = cpu_to_le16(XTENTRYSTART + + righthalf + 1); + } + + if (!test_cflag(COMMIT_Nolink, ip)) { + sxtlck->lwm.length = le16_to_cpu(sp->header.nextindex) - + sxtlck->lwm.offset; + + /* rxtlck->lwm.offset = XTENTRYSTART; */ + rxtlck->lwm.length = le16_to_cpu(rp->header.nextindex) - + XTENTRYSTART; + } + + *rmpp = rmp; + *rbnp = rbn; + + ip->i_blocks += LBLK2PBLK(ip->i_sb, lengthPXD(pxd)); + + jEVENT(0, ("xtSplitPage: sp:0x%p rp:0x%p\n", sp, rp)); + return rc; +} + + +/* + * xtSplitRoot() + * + * function: + * split the full root page into + * original/root/split page and new right page + * i.e., root remains fixed in tree anchor (inode) and + * the root is copied to a single new right child page + * since root page << non-root page, and + * the split root page contains a single entry for the + * new right child page. + * + * parameter: + * int tid, + * struct inode *ip, + * xtsplit_t *split, + * metapage_t **rmpp) + * + * return: + * Pointer to page in which to insert or NULL on error. + */ +static int +xtSplitRoot(tid_t tid, + struct inode *ip, xtsplit_t * split, metapage_t ** rmpp) +{ + xtpage_t *sp; + metapage_t *rmp; + xtpage_t *rp; + s64 rbn; + int skip, nextindex; + xad_t *xad; + pxd_t *pxd; + pxdlist_t *pxdlist; + tlock_t *tlck; + xtlock_t *xtlck; + + sp = &JFS_IP(ip)->i_xtroot; + + INCREMENT(xtStat.split); + + /* + * allocate a single (right) child page + */ + pxdlist = split->pxdlist; + pxd = &pxdlist->pxd[pxdlist->npxd]; + pxdlist->npxd++; + rbn = addressPXD(pxd); + rmp = get_metapage(ip, rbn, PSIZE, 1); + if (rmp == NULL) + return EIO; + + jEVENT(0, ("xtSplitRoot: ip:0x%p rmp:0x%p\n", ip, rmp)); + + /* + * acquire a transaction lock on the new right page; + * + * action: new page; + */ + BT_MARK_DIRTY(rmp, ip); + + rp = (xtpage_t *) rmp->data; + rp->header.flag = + (sp->header.flag & BT_LEAF) ? BT_LEAF : BT_INTERNAL; + rp->header.self = *pxd; + rp->header.nextindex = cpu_to_le16(XTENTRYSTART); + rp->header.maxentry = cpu_to_le16(PSIZE >> L2XTSLOTSIZE); + + /* initialize sibling pointers */ + rp->header.next = 0; + rp->header.prev = 0; + + /* + * copy the in-line root page into new right page extent + */ + nextindex = le16_to_cpu(sp->header.maxentry); + memmove(&rp->xad[XTENTRYSTART], &sp->xad[XTENTRYSTART], + (nextindex - XTENTRYSTART) << L2XTSLOTSIZE); + + /* + * insert the new entry into the new right/child page + * (skip index in the new right page will not change) + */ + skip = split->index; + /* if insert into middle, shift right remaining entries */ + if (skip != nextindex) + memmove(&rp->xad[skip + 1], &rp->xad[skip], + (nextindex - skip) * sizeof(xad_t)); + + xad = &rp->xad[skip]; + XT_PUTENTRY(xad, split->flag, split->off, split->len, split->addr); + + /* update page header */ + rp->header.nextindex = cpu_to_le16(nextindex + 1); + + if (!test_cflag(COMMIT_Nolink, ip)) { + tlck = txLock(tid, ip, rmp, tlckXTREE | tlckNEW); + xtlck = (xtlock_t *) & tlck->lock; + xtlck->lwm.offset = XTENTRYSTART; + xtlck->lwm.length = le16_to_cpu(rp->header.nextindex) - + XTENTRYSTART; + } + + /* + * reset the root + * + * init root with the single entry for the new right page + * set the 1st entry offset to 0, which force the left-most key + * at any level of the tree to be less than any search key. + */ + /* + * acquire a transaction lock on the root page (in-memory inode); + * + * action: root split; + */ + BT_MARK_DIRTY(split->mp, ip); + + xad = &sp->xad[XTENTRYSTART]; + XT_PUTENTRY(xad, XAD_NEW, 0, JFS_SBI(ip->i_sb)->nbperpage, rbn); + + /* update page header of root */ + sp->header.flag &= ~BT_LEAF; + sp->header.flag |= BT_INTERNAL; + + sp->header.nextindex = cpu_to_le16(XTENTRYSTART + 1); + + if (!test_cflag(COMMIT_Nolink, ip)) { + tlck = txLock(tid, ip, split->mp, tlckXTREE | tlckGROW); + xtlck = (xtlock_t *) & tlck->lock; + xtlck->lwm.offset = XTENTRYSTART; + xtlck->lwm.length = 1; + } + + *rmpp = rmp; + + ip->i_blocks += LBLK2PBLK(ip->i_sb, lengthPXD(pxd)); + + jEVENT(0, ("xtSplitRoot: sp:0x%p rp:0x%p\n", sp, rp)); + return 0; +} + + +/* + * xtExtend() + * + * function: extend in-place; + * + * note: existing extent may or may not have been committed. + * caller is responsible for pager buffer cache update, and + * working block allocation map update; + * update pmap: alloc whole extended extent; + */ +int xtExtend(tid_t tid, /* transaction id */ + struct inode *ip, s64 xoff, /* delta extent offset */ + s32 xlen, /* delta extent length */ + int flag) +{ + int rc = 0; + int cmp; + metapage_t *mp; /* meta-page buffer */ + xtpage_t *p; /* base B+-tree index page */ + s64 bn; + int index, nextindex, len; + btstack_t btstack; /* traverse stack */ + xtsplit_t split; /* split information */ + xad_t *xad; + s64 xaddr; + tlock_t *tlck; + xtlock_t *xtlck = 0; + int rootsplit = 0; + + jFYI(1, + ("xtExtend: nxoff:0x%lx nxlen:0x%x\n", (ulong) xoff, xlen)); + + /* there must exist extent to be extended */ + if ((rc = xtSearch(ip, xoff - 1, &cmp, &btstack, XT_INSERT))) + return rc; + assert(cmp == 0); + + /* retrieve search result */ + XT_GETSEARCH(ip, btstack.top, bn, mp, p, index); + + /* extension must be contiguous */ + xad = &p->xad[index]; + jFYI(0, ("xtExtend: xoff:0x%lx xlen:0x%x xaddr:0x%lx\n", + (ulong) offsetXAD(xad), lengthXAD(xad), + (ulong) addressXAD(xad))); + assert((offsetXAD(xad) + lengthXAD(xad)) == xoff); + + /* + * acquire a transaction lock on the leaf page; + * + * action: xad insertion/extension; + */ + BT_MARK_DIRTY(mp, ip); + if (!test_cflag(COMMIT_Nolink, ip)) { + tlck = txLock(tid, ip, mp, tlckXTREE | tlckGROW); + xtlck = (xtlock_t *) & tlck->lock; + } + + /* extend will overflow extent ? */ + xlen = lengthXAD(xad) + xlen; + if ((len = xlen - MAXXLEN) <= 0) + goto extendOld; + + /* + * extent overflow: insert entry for new extent + */ +//insertNew: + xoff = offsetXAD(xad) + MAXXLEN; + xaddr = addressXAD(xad) + MAXXLEN; + nextindex = le16_to_cpu(p->header.nextindex); + + /* + * if the leaf page is full, insert the new entry and + * propagate up the router entry for the new page from split + * + * The xtSplitUp() will insert the entry and unpin the leaf page. + */ + if (nextindex == le16_to_cpu(p->header.maxentry)) { + rootsplit = p->header.flag & BT_ROOT; + + /* xtSpliUp() unpins leaf pages */ + split.mp = mp; + split.index = index + 1; + split.flag = XAD_NEW; + split.off = xoff; /* split offset */ + split.len = len; + split.addr = xaddr; + split.pxdlist = NULL; + if ((rc = xtSplitUp(tid, ip, &split, &btstack))) + return rc; + + /* + * if leaf root has been split, original root has been + * copied to new child page, i.e., original entry now + * resides on the new child page; + */ + if (rootsplit) { + if (p->header.nextindex == + cpu_to_le16(XTENTRYSTART + 1)) { + xad = &p->xad[XTENTRYSTART]; + bn = addressXAD(xad); + + /* get new child page */ + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + + BT_MARK_DIRTY(mp, ip); + if (!test_cflag(COMMIT_Nolink, ip)) { + tlck = txLock(tid, ip, mp, + tlckXTREE | + tlckGROW); + xtlck = (xtlock_t *) & tlck->lock; + } + } + } else + /* get back old page */ + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + } + /* + * insert the new entry into the leaf page + */ + else { + /* insert the new entry: mark the entry NEW */ + xad = &p->xad[index + 1]; + XT_PUTENTRY(xad, XAD_NEW, xoff, len, xaddr); + + /* advance next available entry index */ + p->header.nextindex = + cpu_to_le16(le16_to_cpu(p->header.nextindex) + 1); + } + + /* get back old entry */ + xad = &p->xad[index]; + xlen = MAXXLEN; + + /* + * extend old extent + */ + extendOld: + XADlength(xad, xlen); + if (!(xad->flag & XAD_NEW)) + xad->flag |= XAD_EXTENDED; + + if (!test_cflag(COMMIT_Nolink, ip)) { + xtlck->lwm.offset = + (xtlck->lwm.offset) ? min(index, + (int)xtlck->lwm.offset) : index; + xtlck->lwm.length = + le16_to_cpu(p->header.nextindex) - xtlck->lwm.offset; + } + + /* unpin the leaf page */ + XT_PUTPAGE(mp); + + return rc; +} + + +/* + * xtTailgate() + * + * function: split existing 'tail' extent + * (split offset >= start offset of tail extent), and + * relocate and extend the split tail half; + * + * note: existing extent may or may not have been committed. + * caller is responsible for pager buffer cache update, and + * working block allocation map update; + * update pmap: free old split tail extent, alloc new extent; + */ +int xtTailgate(tid_t tid, /* transaction id */ + struct inode *ip, s64 xoff, /* split/new extent offset */ + s32 xlen, /* new extent length */ + s64 xaddr, /* new extent address */ + int flag) +{ + int rc = 0; + int cmp; + metapage_t *mp; /* meta-page buffer */ + xtpage_t *p; /* base B+-tree index page */ + s64 bn; + int index, nextindex, llen, rlen; + btstack_t btstack; /* traverse stack */ + xtsplit_t split; /* split information */ + xad_t *xad; + tlock_t *tlck; + xtlock_t *xtlck = 0; + tlock_t *mtlck; + maplock_t *pxdlock; + int rootsplit = 0; + +/* +printf("xtTailgate: nxoff:0x%lx nxlen:0x%x nxaddr:0x%lx\n", + (ulong)xoff, xlen, (ulong)xaddr); +*/ + + /* there must exist extent to be tailgated */ + if ((rc = xtSearch(ip, xoff, &cmp, &btstack, XT_INSERT))) + return rc; + assert(cmp == 0); + + /* retrieve search result */ + XT_GETSEARCH(ip, btstack.top, bn, mp, p, index); + + /* entry found must be last entry */ + nextindex = le16_to_cpu(p->header.nextindex); + assert(index == nextindex - 1); + + BT_MARK_DIRTY(mp, ip); + /* + * acquire tlock of the leaf page containing original entry + */ + if (!test_cflag(COMMIT_Nolink, ip)) { + tlck = txLock(tid, ip, mp, tlckXTREE | tlckGROW); + xtlck = (xtlock_t *) & tlck->lock; + } + + /* completely replace extent ? */ + xad = &p->xad[index]; +/* +printf("xtTailgate: xoff:0x%lx xlen:0x%x xaddr:0x%lx\n", + (ulong)offsetXAD(xad), lengthXAD(xad), (ulong)addressXAD(xad)); +*/ + if ((llen = xoff - offsetXAD(xad)) == 0) + goto updateOld; + + /* + * partially replace extent: insert entry for new extent + */ +//insertNew: + /* + * if the leaf page is full, insert the new entry and + * propagate up the router entry for the new page from split + * + * The xtSplitUp() will insert the entry and unpin the leaf page. + */ + if (nextindex == le16_to_cpu(p->header.maxentry)) { + rootsplit = p->header.flag & BT_ROOT; + + /* xtSpliUp() unpins leaf pages */ + split.mp = mp; + split.index = index + 1; + split.flag = XAD_NEW; + split.off = xoff; /* split offset */ + split.len = xlen; + split.addr = xaddr; + split.pxdlist = NULL; + if ((rc = xtSplitUp(tid, ip, &split, &btstack))) + return rc; + + /* + * if leaf root has been split, original root has been + * copied to new child page, i.e., original entry now + * resides on the new child page; + */ + if (rootsplit) { + if (p->header.nextindex == + cpu_to_le16(XTENTRYSTART + 1)) { + xad = &p->xad[XTENTRYSTART]; + bn = addressXAD(xad); + + /* get new child page */ + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + + BT_MARK_DIRTY(mp, ip); + if (!test_cflag(COMMIT_Nolink, ip)) { + tlck = txLock(tid, ip, mp, + tlckXTREE | + tlckGROW); + xtlck = (xtlock_t *) & tlck->lock; + } + } + } else + /* get back old page */ + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + } + /* + * insert the new entry into the leaf page + */ + else { + /* insert the new entry: mark the entry NEW */ + xad = &p->xad[index + 1]; + XT_PUTENTRY(xad, XAD_NEW, xoff, xlen, xaddr); + + /* advance next available entry index */ + p->header.nextindex = + cpu_to_le16(le16_to_cpu(p->header.nextindex) + 1); + } + + /* get back old XAD */ + xad = &p->xad[index]; + + /* + * truncate/relocate old extent at split offset + */ + updateOld: + /* update dmap for old/committed/truncated extent */ + rlen = lengthXAD(xad) - llen; + if (!(xad->flag & XAD_NEW)) { + /* free from PWMAP at commit */ + if (!test_cflag(COMMIT_Nolink, ip)) { + mtlck = txMaplock(tid, ip, tlckMAP); + pxdlock = (maplock_t *) & mtlck->lock; + pxdlock->flag = mlckFREEPXD; + PXDaddress(&pxdlock->pxd, addressXAD(xad) + llen); + PXDlength(&pxdlock->pxd, rlen); + pxdlock->index = 1; + } + jEVENT(0, + ("xtTailgate: free extent xaddr:0x%lx xlen:0x%x\n", + (ulong) addressPXD(&pxdlock->pxd), + lengthPXD(&pxdlock->pxd))); + } else + /* free from WMAP */ + dbFree(ip, addressXAD(xad) + llen, (s64) rlen); + + if (llen) + /* truncate */ + XADlength(xad, llen); + else + /* replace */ + XT_PUTENTRY(xad, XAD_NEW, xoff, xlen, xaddr); + + if (!test_cflag(COMMIT_Nolink, ip)) { + xtlck->lwm.offset = (xtlck->lwm.offset) ? + min(index, (int)xtlck->lwm.offset) : index; + xtlck->lwm.length = le16_to_cpu(p->header.nextindex) - + xtlck->lwm.offset; + } + + /* unpin the leaf page */ + XT_PUTPAGE(mp); + + return rc; +} + + +/* + * xtUpdate() + * + * function: update XAD; + * + * update extent for allocated_but_not_recorded or + * compressed extent; + * + * parameter: + * nxad - new XAD; + * logical extent of the specified XAD must be completely + * contained by an existing XAD; + */ +int xtUpdate(tid_t tid, struct inode *ip, xad_t * nxad) +{ /* new XAD */ + int rc = 0; + int cmp; + metapage_t *mp; /* meta-page buffer */ + xtpage_t *p; /* base B+-tree index page */ + s64 bn; + int index0, index, newindex, nextindex; + btstack_t btstack; /* traverse stack */ + xtsplit_t split; /* split information */ + xad_t *xad, *lxad, *rxad; + int xflag; + s64 nxoff, xoff; + int nxlen, xlen, lxlen, rxlen; + s64 nxaddr, xaddr; + tlock_t *tlck; + xtlock_t *xtlck = 0; + int rootsplit = 0, newpage = 0; + + /* there must exist extent to be tailgated */ + nxoff = offsetXAD(nxad); + nxlen = lengthXAD(nxad); + nxaddr = addressXAD(nxad); +/* +printf("xtUpdate: nxflag:0x%x nxoff:0x%lx nxlen:0x%x nxaddr:0x%lx\n", + nxad->flag, (ulong)nxoff, nxlen, (ulong)nxaddr); +*/ + if ((rc = xtSearch(ip, nxoff, &cmp, &btstack, XT_INSERT))) + return rc; + assert(cmp == 0); + + /* retrieve search result */ + XT_GETSEARCH(ip, btstack.top, bn, mp, p, index0); + + BT_MARK_DIRTY(mp, ip); + /* + * acquire tlock of the leaf page containing original entry + */ + if (!test_cflag(COMMIT_Nolink, ip)) { + tlck = txLock(tid, ip, mp, tlckXTREE | tlckGROW); + xtlck = (xtlock_t *) & tlck->lock; + } + + xad = &p->xad[index0]; + xflag = xad->flag; + xoff = offsetXAD(xad); + xlen = lengthXAD(xad); + xaddr = addressXAD(xad); +/* +printf("xtUpdate: xflag:0x%x xoff:0x%lx xlen:0x%x xaddr:0x%lx\n", + xflag, (ulong)xoff, xlen, (ulong)xaddr); +*/ + + /* nXAD must be completely contained within XAD */ + assert(xoff <= nxoff); + assert(nxoff + nxlen <= xoff + xlen); + + index = index0; + newindex = index + 1; + nextindex = le16_to_cpu(p->header.nextindex); + +#ifdef _JFS_WIP_NOCOALESCE + if (xoff < nxoff) + goto updateRight; + + /* + * replace XAD with nXAD + */ + replace: /* (nxoff == xoff) */ + if (nxlen == xlen) { + /* replace XAD with nXAD:recorded */ + *xad = *nxad; + xad->flag = xflag & ~XAD_NOTRECORDED; + + goto out; + } else /* (nxlen < xlen) */ + goto updateLeft; +#endif /* _JFS_WIP_NOCOALESCE */ + +/* #ifdef _JFS_WIP_COALESCE */ + if (xoff < nxoff) + goto coalesceRight; + + /* + * coalesce with left XAD + */ +//coalesceLeft: /* (xoff == nxoff) */ + /* is XAD first entry of page ? */ + if (index == XTENTRYSTART) + goto replace; + + /* is nXAD logically and physically contiguous with lXAD ? */ + lxad = &p->xad[index - 1]; + lxlen = lengthXAD(lxad); + if (!(lxad->flag & XAD_NOTRECORDED) && + (nxoff == offsetXAD(lxad) + lxlen) && + (nxaddr == addressXAD(lxad) + lxlen) && + (lxlen + nxlen < MAXXLEN)) { + /* extend right lXAD */ + index0 = index - 1; + XADlength(lxad, lxlen + nxlen); + + /* If we just merged two extents together, need to make sure the + * right extent gets logged. If the left one is marked XAD_NEW, + * then we know it will be logged. Otherwise, mark as + * XAD_EXTENDED + */ + if (!(lxad->flag & XAD_NEW)) + lxad->flag |= XAD_EXTENDED; + + if (xlen > nxlen) { + /* truncate XAD */ + XADoffset(xad, xoff + nxlen); + XADlength(xad, xlen - nxlen); + XADaddress(xad, xaddr + nxlen); + goto out; + } else { /* (xlen == nxlen) */ + + /* remove XAD */ + if (index < nextindex - 1) + memmove(&p->xad[index], &p->xad[index + 1], + (nextindex - index - + 1) << L2XTSLOTSIZE); + + p->header.nextindex = + cpu_to_le16(le16_to_cpu(p->header.nextindex) - + 1); + + index = index0; + newindex = index + 1; + nextindex = le16_to_cpu(p->header.nextindex); + xoff = nxoff = offsetXAD(lxad); + xlen = nxlen = lxlen + nxlen; + xaddr = nxaddr = addressXAD(lxad); + goto coalesceRight; + } + } + + /* + * replace XAD with nXAD + */ + replace: /* (nxoff == xoff) */ + if (nxlen == xlen) { + /* replace XAD with nXAD:recorded */ + *xad = *nxad; + xad->flag = xflag & ~XAD_NOTRECORDED; + + goto coalesceRight; + } else /* (nxlen < xlen) */ + goto updateLeft; + + /* + * coalesce with right XAD + */ + coalesceRight: /* (xoff <= nxoff) */ + /* is XAD last entry of page ? */ + if (newindex == nextindex) { + if (xoff == nxoff) + goto out; + goto updateRight; + } + + /* is nXAD logically and physically contiguous with rXAD ? */ + rxad = &p->xad[index + 1]; + rxlen = lengthXAD(rxad); + if (!(rxad->flag & XAD_NOTRECORDED) && + (nxoff + nxlen == offsetXAD(rxad)) && + (nxaddr + nxlen == addressXAD(rxad)) && + (rxlen + nxlen < MAXXLEN)) { + /* extend left rXAD */ + XADoffset(rxad, nxoff); + XADlength(rxad, rxlen + nxlen); + XADaddress(rxad, nxaddr); + + /* If we just merged two extents together, need to make sure + * the left extent gets logged. If the right one is marked + * XAD_NEW, then we know it will be logged. Otherwise, mark as + * XAD_EXTENDED + */ + if (!(rxad->flag & XAD_NEW)) + rxad->flag |= XAD_EXTENDED; + + if (xlen > nxlen) + /* truncate XAD */ + XADlength(xad, xlen - nxlen); + else { /* (xlen == nxlen) */ + + /* remove XAD */ + memmove(&p->xad[index], &p->xad[index + 1], + (nextindex - index - 1) << L2XTSLOTSIZE); + + p->header.nextindex = + cpu_to_le16(le16_to_cpu(p->header.nextindex) - + 1); + } + + goto out; + } else if (xoff == nxoff) + goto out; + + assert(xoff < nxoff); +/* #endif _JFS_WIP_COALESCE */ + + /* + * split XAD into (lXAD, nXAD): + * + * |---nXAD---> + * --|----------XAD----------|-- + * |-lXAD-| + */ + updateRight: /* (xoff < nxoff) */ + /* truncate old XAD as lXAD:not_recorded */ + xad = &p->xad[index]; + XADlength(xad, nxoff - xoff); + + /* insert nXAD:recorded */ + if (nextindex == le16_to_cpu(p->header.maxentry)) { +/* +printf("xtUpdate.updateRight.split p:0x%p\n", p); +*/ + rootsplit = p->header.flag & BT_ROOT; + + /* xtSpliUp() unpins leaf pages */ + split.mp = mp; + split.index = newindex; + split.flag = xflag & ~XAD_NOTRECORDED; + split.off = nxoff; + split.len = nxlen; + split.addr = nxaddr; + split.pxdlist = NULL; + if ((rc = xtSplitUp(tid, ip, &split, &btstack))) + return rc; + + /* + * if leaf root has been split, original root has been + * copied to new child page, i.e., original entry now + * resides on the new child page; + */ + if (rootsplit) { + if (p->header.nextindex == + cpu_to_le16(XTENTRYSTART + 1)) { + xad = &p->xad[XTENTRYSTART]; + bn = addressXAD(xad); + + /* get new child page */ + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + + BT_MARK_DIRTY(mp, ip); + if (!test_cflag(COMMIT_Nolink, ip)) { + tlck = txLock(tid, ip, mp, + tlckXTREE | + tlckGROW); + xtlck = (xtlock_t *) & tlck->lock; + } + } + } else { + /* get back old page */ + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + + /* is nXAD on new page ? */ + if (newindex > + (le16_to_cpu(p->header.maxentry) >> 1)) { + newindex = + newindex - + le16_to_cpu(p->header.nextindex) + + XTENTRYSTART; + newpage = 1; + } + } + } else { + /* if insert into middle, shift right remaining entries */ + if (newindex < nextindex) + memmove(&p->xad[newindex + 1], &p->xad[newindex], + (nextindex - newindex) << L2XTSLOTSIZE); + + /* insert the entry */ + xad = &p->xad[newindex]; + *xad = *nxad; + xad->flag = xflag & ~XAD_NOTRECORDED; + + /* advance next available entry index. */ + p->header.nextindex = + cpu_to_le16(le16_to_cpu(p->header.nextindex) + 1); + } + + /* + * does nXAD force 3-way split ? + * + * |---nXAD--->| + * --|----------XAD-------------|-- + * |-lXAD-| |-rXAD -| + */ + if (nxoff + nxlen == xoff + xlen) + goto out; + + /* reorient nXAD as XAD for further split XAD into (nXAD, rXAD) */ + if (newpage) { + /* close out old page */ + if (!test_cflag(COMMIT_Nolink, ip)) { + xtlck->lwm.offset = (xtlck->lwm.offset) ? + min(index0, (int)xtlck->lwm.offset) : index0; + xtlck->lwm.length = + le16_to_cpu(p->header.nextindex) - + xtlck->lwm.offset; + } + + bn = le64_to_cpu(p->header.next); + XT_PUTPAGE(mp); + + /* get new right page */ + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + + BT_MARK_DIRTY(mp, ip); + if (!test_cflag(COMMIT_Nolink, ip)) { + tlck = txLock(tid, ip, mp, tlckXTREE | tlckGROW); + xtlck = (xtlock_t *) & tlck->lock; + } + + index0 = index = newindex; + } else + index++; + + newindex = index + 1; + nextindex = le16_to_cpu(p->header.nextindex); + xlen = xlen - (nxoff - xoff); + xoff = nxoff; + xaddr = nxaddr; + + /* recompute split pages */ + if (nextindex == le16_to_cpu(p->header.maxentry)) { +/* +printf("xtUpdate: updateRight+Left recompute split pages: p:0x%p\n", p); +*/ + XT_PUTPAGE(mp); + + if ((rc = xtSearch(ip, nxoff, &cmp, &btstack, XT_INSERT))) + return rc; + assert(cmp == 0); + + /* retrieve search result */ + XT_GETSEARCH(ip, btstack.top, bn, mp, p, index0); + assert(index0 == index); + } + + /* + * split XAD into (nXAD, rXAD) + * + * ---nXAD---| + * --|----------XAD----------|-- + * |-rXAD-| + */ + updateLeft: /* (nxoff == xoff) && (nxlen < xlen) */ + /* update old XAD with nXAD:recorded */ + xad = &p->xad[index]; + *xad = *nxad; + xad->flag = xflag & ~XAD_NOTRECORDED; + + /* insert rXAD:not_recorded */ + xoff = xoff + nxlen; + xlen = xlen - nxlen; + xaddr = xaddr + nxlen; + if (nextindex == le16_to_cpu(p->header.maxentry)) { + rootsplit = p->header.flag & BT_ROOT; + +/* +printf("xtUpdate.updateLeft.split p:0x%p\n", p); +*/ + /* xtSpliUp() unpins leaf pages */ + split.mp = mp; + split.index = newindex; + split.flag = xflag; + split.off = xoff; + split.len = xlen; + split.addr = xaddr; + split.pxdlist = NULL; + if ((rc = xtSplitUp(tid, ip, &split, &btstack))) + return rc; + + /* + * if leaf root has been split, original root has been + * copied to new child page, i.e., original entry now + * resides on the new child page; + */ + if (rootsplit) { + if (p->header.nextindex == + cpu_to_le16(XTENTRYSTART + 1)) { + xad = &p->xad[XTENTRYSTART]; + bn = addressXAD(xad); + + /* get new child page */ + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + + BT_MARK_DIRTY(mp, ip); + if (!test_cflag(COMMIT_Nolink, ip)) { + tlck = txLock(tid, ip, mp, + tlckXTREE | + tlckGROW); + xtlck = (xtlock_t *) & tlck->lock; + } + } + } else + /* get back old page */ + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + } else { + /* if insert into middle, shift right remaining entries */ + if (newindex < nextindex) + memmove(&p->xad[newindex + 1], &p->xad[newindex], + (nextindex - newindex) << L2XTSLOTSIZE); + + /* insert the entry */ + xad = &p->xad[newindex]; + XT_PUTENTRY(xad, xflag, xoff, xlen, xaddr); + + /* advance next available entry index. */ + p->header.nextindex = + cpu_to_le16(le16_to_cpu(p->header.nextindex) + 1); + } + + out: + if (!test_cflag(COMMIT_Nolink, ip)) { + xtlck->lwm.offset = (xtlck->lwm.offset) ? + min(index0, (int)xtlck->lwm.offset) : index0; + xtlck->lwm.length = le16_to_cpu(p->header.nextindex) - + xtlck->lwm.offset; + } + + /* unpin the leaf page */ + XT_PUTPAGE(mp); + + return rc; +} + + +#ifdef _STILL_TO_PORT +/* + * xtAppend() + * + * function: grow in append mode from contiguous region specified ; + * + * parameter: + * tid - transaction id; + * ip - file object; + * xflag - extent flag: + * xoff - extent offset; + * maxblocks - max extent length; + * xlen - extent length (in/out); + * xaddrp - extent address pointer (in/out): + * flag - + * + * return: + */ +int xtAppend(tid_t tid, /* transaction id */ + struct inode *ip, int xflag, s64 xoff, s32 maxblocks, /* @GD1 */ + s32 * xlenp, /* (in/out) */ + s64 * xaddrp, /* (in/out) */ + int flag) +{ + int rc = 0; + metapage_t *mp; /* meta-page buffer */ + xtpage_t *p; /* base B+-tree index page */ + s64 bn, xaddr; + int index, nextindex; + btstack_t btstack; /* traverse stack */ + xtsplit_t split; /* split information */ + xad_t *xad; + int cmp; + tlock_t *tlck; + xtlock_t *xtlck; + int nsplit, nblocks, xlen; + pxdlist_t pxdlist; + pxd_t *pxd; + + xaddr = *xaddrp; + xlen = *xlenp; + jEVENT(0, + ("xtAppend: xoff:0x%lx maxblocks:%d xlen:%d xaddr:0x%lx\n", + (ulong) xoff, maxblocks, xlen, (ulong) xaddr)); + + /* + * search for the entry location at which to insert: + * + * xtFastSearch() and xtSearch() both returns (leaf page + * pinned, index at which to insert). + * n.b. xtSearch() may return index of maxentry of + * the full page. + */ + if ((rc = xtSearch(ip, xoff, &cmp, &btstack, XT_INSERT))) + return rc; + + /* retrieve search result */ + XT_GETSEARCH(ip, btstack.top, bn, mp, p, index); + + if (cmp == 0) { + rc = EEXIST; + goto out; + } +//insert: + /* + * insert entry for new extent + */ + xflag |= XAD_NEW; + + /* + * if the leaf page is full, split the page and + * propagate up the router entry for the new page from split + * + * The xtSplitUp() will insert the entry and unpin the leaf page. + */ + nextindex = le16_to_cpu(p->header.nextindex); + if (nextindex < le16_to_cpu(p->header.maxentry)) + goto insertLeaf; + + /* + * allocate new index blocks to cover index page split(s) + */ + nsplit = btstack.nsplit; + split.pxdlist = &pxdlist; + pxdlist.maxnpxd = pxdlist.npxd = 0; + pxd = &pxdlist.pxd[0]; + nblocks = JFS_SBI(ip->i_sb)->nbperpage; + for (; nsplit > 0; nsplit--, pxd++, xaddr += nblocks, maxblocks -= nblocks) { /* @GD1 */ + if ((rc = dbAllocBottomUp(ip, xaddr, (s64) nblocks)) == 0) { + PXDaddress(pxd, xaddr); + PXDlength(pxd, nblocks); + + pxdlist.maxnpxd++; + + continue; + } + + /* undo allocation */ + + goto out; + } + + xlen = min(xlen, maxblocks); /* @GD1 */ + + /* + * allocate data extent requested + */ + if ((rc = dbAllocBottomUp(ip, xaddr, (s64) xlen))) + goto out; + + split.mp = mp; + split.index = index; + split.flag = xflag; + split.off = xoff; + split.len = xlen; + split.addr = xaddr; + if ((rc = xtSplitUp(tid, ip, &split, &btstack))) { + /* undo data extent allocation */ + dbFree(ip, *xaddrp, (s64) * xlenp); + + return rc; + } + + *xaddrp = xaddr; + *xlenp = xlen; + return 0; + + /* + * insert the new entry into the leaf page + */ + insertLeaf: + /* + * allocate data extent requested + */ + if ((rc = dbAllocBottomUp(ip, xaddr, (s64) xlen))) + goto out; + + BT_MARK_DIRTY(mp, ip); + /* + * acquire a transaction lock on the leaf page; + * + * action: xad insertion/extension; + */ + tlck = txLock(tid, ip, mp, tlckXTREE | tlckGROW); + xtlck = (xtlock_t *) & tlck->lock; + + /* insert the new entry: mark the entry NEW */ + xad = &p->xad[index]; + XT_PUTENTRY(xad, xflag, xoff, xlen, xaddr); + + /* advance next available entry index */ + p->header.nextindex = + cpu_to_le16(le16_to_cpu(p->header.nextindex) + 1); + + xtlck->lwm.offset = + (xtlck->lwm.offset) ? min(index, xtlck->lwm.offset) : index; + xtlck->lwm.length = le16_to_cpu(p->header.nextindex) - + xtlck->lwm.offset; + + *xaddrp = xaddr; + *xlenp = xlen; + + out: + /* unpin the leaf page */ + XT_PUTPAGE(mp); + + return rc; +} + + +/* - TBD for defragmentaion/reorganization - + * + * xtDelete() + * + * function: + * delete the entry with the specified key. + * + * N.B.: whole extent of the entry is assumed to be deleted. + * + * parameter: + * + * return: + * ENOENT: if the entry is not found. + * + * exception: + */ +int xtDelete(tid_t tid, struct inode *ip, s64 xoff, s32 xlen, int flag) +{ + int rc = 0; + btstack_t btstack; + int cmp; + s64 bn; + metapage_t *mp; + xtpage_t *p; + int index, nextindex; + tlock_t *tlck; + xtlock_t *xtlck; + + /* + * find the matching entry; xtSearch() pins the page + */ + if ((rc = xtSearch(ip, xoff, &cmp, &btstack, 0))) + return rc; + + XT_GETSEARCH(ip, btstack.top, bn, mp, p, index); + if (cmp) { + /* unpin the leaf page */ + XT_PUTPAGE(mp); + return ENOENT; + } + + /* + * delete the entry from the leaf page + */ + nextindex = le16_to_cpu(p->header.nextindex); + p->header.nextindex = + cpu_to_le16(le16_to_cpu(p->header.nextindex) - 1); + + /* + * if the leaf page bocome empty, free the page + */ + if (p->header.nextindex == cpu_to_le16(XTENTRYSTART)) + return (xtDeleteUp(tid, ip, mp, p, &btstack)); + + BT_MARK_DIRTY(mp, ip); + /* + * acquire a transaction lock on the leaf page; + * + * action:xad deletion; + */ + tlck = txLock(tid, ip, mp, tlckXTREE); + xtlck = (xtlock_t *) & tlck->lock; + xtlck->lwm.offset = + (xtlck->lwm.offset) ? min(index, xtlck->lwm.offset) : index; + + /* if delete from middle, shift left/compact the remaining entries */ + if (index < nextindex - 1) + memmove(&p->xad[index], &p->xad[index + 1], + (nextindex - index - 1) * sizeof(xad_t)); + + XT_PUTPAGE(mp); + + return 0; +} + + +/* - TBD for defragmentaion/reorganization - + * + * xtDeleteUp() + * + * function: + * free empty pages as propagating deletion up the tree + * + * parameter: + * + * return: + */ +static int +xtDeleteUp(tid_t tid, + struct inode *ip, + metapage_t * fmp, xtpage_t * fp, btstack_t * btstack) +{ + int rc = 0; + metapage_t *mp; + xtpage_t *p; + int index, nextindex; + s64 xaddr; + int xlen; + btframe_t *parent; + tlock_t *tlck; + xtlock_t *xtlck; + + /* + * keep root leaf page which has become empty + */ + if (fp->header.flag & BT_ROOT) { + /* keep the root page */ + fp->header.flag &= ~BT_INTERNAL; + fp->header.flag |= BT_LEAF; + fp->header.nextindex = cpu_to_le16(XTENTRYSTART); + + /* XT_PUTPAGE(fmp); */ + + return 0; + } + + /* + * free non-root leaf page + */ + if ((rc = xtRelink(tid, ip, fp))) + return rc; + + xaddr = addressPXD(&fp->header.self); + xlen = lengthPXD(&fp->header.self); + /* free the page extent */ + dbFree(ip, xaddr, (s64) xlen); + + /* free the buffer page */ + discard_metapage(fmp); + + /* + * propagate page deletion up the index tree + * + * If the delete from the parent page makes it empty, + * continue all the way up the tree. + * stop if the root page is reached (which is never deleted) or + * if the entry deletion does not empty the page. + */ + while ((parent = BT_POP(btstack)) != NULL) { + /* get/pin the parent page */ + XT_GETPAGE(ip, parent->bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + index = parent->index; + + /* delete the entry for the freed child page from parent. + */ + nextindex = le16_to_cpu(p->header.nextindex); + + /* + * the parent has the single entry being deleted: + * free the parent page which has become empty. + */ + if (nextindex == 1) { + if (p->header.flag & BT_ROOT) { + /* keep the root page */ + p->header.flag &= ~BT_INTERNAL; + p->header.flag |= BT_LEAF; + p->header.nextindex = + cpu_to_le16(XTENTRYSTART); + + /* XT_PUTPAGE(fmp); */ + + break; + } else { + /* free the parent page */ + if ((rc = xtRelink(tid, ip, p))) + return rc; + + xaddr = addressPXD(&p->header.self); + /* free the page extent */ + dbFree(ip, xaddr, + (s64) JFS_SBI(ip->i_sb)->nbperpage); + + /* unpin/free the buffer page */ + discard_metapage(fmp); + + /* propagate up */ + continue; + } + } + /* + * the parent has other entries remaining: + * delete the router entry from the parent page. + */ + else { + BT_MARK_DIRTY(mp, ip); + /* + * acquire a transaction lock on the leaf page; + * + * action:xad deletion; + */ + tlck = txLock(tid, ip, mp, tlckXTREE); + xtlck = (xtlock_t *) & tlck->lock; + xtlck->lwm.offset = + (xtlck->lwm.offset) ? min(index, + xtlck->lwm. + offset) : index; + + /* if delete from middle, + * shift left/compact the remaining entries in the page + */ + if (index < nextindex - 1) + memmove(&p->xad[index], &p->xad[index + 1], + (nextindex - index - + 1) << L2XTSLOTSIZE); + + p->header.nextindex = + cpu_to_le16(le16_to_cpu(p->header.nextindex) - + 1); + jEVENT(0, + ("xtDeleteUp(entry): 0x%lx[%d]\n", + (ulong) parent->bn, index)); + } + + /* unpin the parent page */ + XT_PUTPAGE(mp); + + /* exit propagation up */ + break; + } + + return 0; +} + + +/* + * NAME: xtRelocate() + * + * FUNCTION: relocate xtpage or data extent of regular file; + * This function is mainly used by defragfs utility. + * + * NOTE: This routine does not have the logic to handle + * uncommitted allocated extent. The caller should call + * txCommit() to commit all the allocation before call + * this routine. + */ +xtRelocate(tid_t tid, struct inode * ip, xad_t * oxad, /* old XAD */ + s64 nxaddr, /* new xaddr */ + int xtype) +{ /* extent type: XTPAGE or DATAEXT */ + int rc = 0; + tblock_t *tblk; + tlock_t *tlck; + xtlock_t *xtlck; + metapage_t *mp, *pmp, *lmp, *rmp; /* meta-page buffer */ + xtpage_t *p, *pp, *rp, *lp; /* base B+-tree index page */ + xad_t *xad; + pxd_t *pxd; + s64 xoff, xsize; + int xlen; + s64 oxaddr, sxaddr, dxaddr, nextbn, prevbn; + cbuf_t *cp; + s64 offset, nbytes, nbrd, pno; + int nb, npages, nblks; + s64 bn; + int cmp; + int index; + pxdlock_t *pxdlock; + btstack_t btstack; /* traverse stack */ + + xtype = xtype & EXTENT_TYPE; + + xoff = offsetXAD(oxad); + oxaddr = addressXAD(oxad); + xlen = lengthXAD(oxad); + + /* validate extent offset */ + offset = xoff << JFS_SBI(ip->i_sb)->l2bsize; + if (offset >= ip->i_size) + return ESTALE; /* stale extent */ + + jEVENT(0, + ("xtRelocate: xtype:%d xoff:0x%lx xlen:0x%x xaddr:0x%lx:0x%lx\n", + xtype, (ulong) xoff, xlen, (ulong) oxaddr, + (ulong) nxaddr)); + + /* + * 1. get and validate the parent xtpage/xad entry + * covering the source extent to be relocated; + */ + if (xtype == DATAEXT) { + /* search in leaf entry */ + rc = xtSearch(ip, xoff, &cmp, &btstack, 0); + if (rc) + return rc; + if (cmp) { + XT_PUTPAGE(pmp); + return ESTALE; + } + + /* retrieve search result */ + XT_GETSEARCH(ip, btstack.top, bn, pmp, pp, index); + + /* validate for exact match with a single entry */ + xad = &pp->xad[index]; + if (addressXAD(xad) != oxaddr || lengthXAD(xad) != xlen) { + XT_PUTPAGE(pmp); + return ESTALE; + } + } else { /* (xtype == XTPAGE) */ + + /* search in internal entry */ + rc = xtSearchNode(ip, oxad, &cmp, &btstack, 0); + if (rc) + return rc; + if (cmp) { + XT_PUTPAGE(pmp); + return ESTALE; + } + + /* retrieve search result */ + XT_GETSEARCH(ip, btstack.top, bn, pmp, pp, index); + + /* xtSearchNode() validated for exact match with a single entry + */ + xad = &pp->xad[index]; + } + jEVENT(0, ("xtRelocate: parent xad entry validated.\n")); + + /* + * 2. relocate the extent + */ + if (xtype == DATAEXT) { + /* if the extent is allocated-but-not-recorded + * there is no real data to be moved in this extent, + */ + if (xad->flag & XAD_NOTRECORDED) + goto out; + else + /* release xtpage for cmRead()/xtLookup() */ + XT_PUTPAGE(pmp); + + /* + * cmRelocate() + * + * copy target data pages to be relocated; + * + * data extent must start at page boundary and + * multiple of page size (except the last data extent); + * read in each page of the source data extent into cbuf, + * update the cbuf extent descriptor of the page to be + * homeward bound to new dst data extent + * copy the data from the old extent to new extent. + * copy is essential for compressed files to avoid problems + * that can arise if there was a change in compression + * algorithms. + * it is a good strategy because it may disrupt cache + * policy to keep the pages in memory afterwards. + */ + offset = xoff << JFS_SBI(ip->i_sb)->l2bsize; + assert((offset & CM_OFFSET) == 0); + nbytes = xlen << JFS_SBI(ip->i_sb)->l2bsize; + pno = offset >> CM_L2BSIZE; + npages = (nbytes + (CM_BSIZE - 1)) >> CM_L2BSIZE; +/* + npages = ((offset + nbytes - 1) >> CM_L2BSIZE) - + (offset >> CM_L2BSIZE) + 1; +*/ + sxaddr = oxaddr; + dxaddr = nxaddr; + + /* process the request one cache buffer at a time */ + for (nbrd = 0; nbrd < nbytes; nbrd += nb, + offset += nb, pno++, npages--) { + /* compute page size */ + nb = min(nbytes - nbrd, CM_BSIZE); + + /* get the cache buffer of the page */ + if (rc = cmRead(ip, offset, npages, &cp)) + break; + + assert(addressPXD(&cp->cm_pxd) == sxaddr); + assert(!cp->cm_modified); + + /* bind buffer with the new extent address */ + nblks = nb >> JFS_IP(ip->i_sb)->l2bsize; + cmSetXD(ip, cp, pno, dxaddr, nblks); + + /* release the cbuf, mark it as modified */ + cmPut(cp, TRUE); + + dxaddr += nblks; + sxaddr += nblks; + } + + /* get back parent page */ + rc = xtSearch(ip, xoff, &cmp, &btstack, 0); + XT_GETSEARCH(ip, btstack.top, bn, pmp, pp, index); + jEVENT(0, ("xtRelocate: target data extent relocated.\n")); + } else { /* (xtype == XTPAGE) */ + + /* + * read in the target xtpage from the source extent; + */ + XT_GETPAGE(ip, oxaddr, mp, PSIZE, p, rc); + if (rc) { + XT_PUTPAGE(pmp); + return rc; + } + + /* + * read in sibling pages if any to update sibling pointers; + */ + rmp = NULL; + if (p->header.next) { + nextbn = le64_to_cpu(p->header.next); + XT_GETPAGE(ip, nextbn, rmp, PSIZE, rp, rc); + if (rc) { + XT_PUTPAGE(pmp); + XT_PUTPAGE(mp); + return (rc); + } + } + + lmp = NULL; + if (p->header.prev) { + prevbn = le64_to_cpu(p->header.prev); + XT_GETPAGE(ip, prevbn, lmp, PSIZE, lp, rc); + if (rc) { + XT_PUTPAGE(pmp); + XT_PUTPAGE(mp); + if (rmp) + XT_PUTPAGE(rmp); + return (rc); + } + } + + /* at this point, all xtpages to be updated are in memory */ + + /* + * update sibling pointers of sibling xtpages if any; + */ + if (lmp) { + BT_MARK_DIRTY(lmp, ip); + tlck = + txLock(tid, ip, lmp, tlckXTREE | tlckRELINK); + lp->header.next = cpu_to_le64(nxaddr); + XT_PUTPAGE(lmp); + } + + if (rmp) { + BT_MARK_DIRTY(rmp, ip); + tlck = + txLock(tid, ip, rmp, tlckXTREE | tlckRELINK); + rp->header.prev = cpu_to_le64(nxaddr); + XT_PUTPAGE(rmp); + } + + /* + * update the target xtpage to be relocated + * + * update the self address of the target page + * and write to destination extent; + * redo image covers the whole xtpage since it is new page + * to the destination extent; + * update of bmap for the free of source extent + * of the target xtpage itself: + * update of bmap for the allocation of destination extent + * of the target xtpage itself: + * update of bmap for the extents covered by xad entries in + * the target xtpage is not necessary since they are not + * updated; + * if not committed before this relocation, + * target page may contain XAD_NEW entries which must + * be scanned for bmap update (logredo() always + * scan xtpage REDOPAGE image for bmap update); + * if committed before this relocation (tlckRELOCATE), + * scan may be skipped by commit() and logredo(); + */ + BT_MARK_DIRTY(mp, ip); + /* tlckNEW init xtlck->lwm.offset = XTENTRYSTART; */ + tlck = txLock(tid, ip, mp, tlckXTREE | tlckNEW); + xtlck = (xtlock_t *) & tlck->lock; + + /* update the self address in the xtpage header */ + pxd = &p->header.self; + PXDaddress(pxd, nxaddr); + + /* linelock for the after image of the whole page */ + xtlck->lwm.length = + le16_to_cpu(p->header.nextindex) - xtlck->lwm.offset; + + /* update the buffer extent descriptor of target xtpage */ + xsize = xlen << JFS_SBI(ip->i_sb)->l2bsize; + bmSetXD(mp, nxaddr, xsize); + + /* unpin the target page to new homeward bound */ + XT_PUTPAGE(mp); + jEVENT(0, ("xtRelocate: target xtpage relocated.\n")); + } + + /* + * 3. acquire maplock for the source extent to be freed; + * + * acquire a maplock saving the src relocated extent address; + * to free of the extent at commit time; + */ + out: + /* if DATAEXT relocation, write a LOG_UPDATEMAP record for + * free PXD of the source data extent (logredo() will update + * bmap for free of source data extent), and update bmap for + * free of the source data extent; + */ + if (xtype == DATAEXT) + tlck = txMaplock(tid, ip, tlckMAP); + /* if XTPAGE relocation, write a LOG_NOREDOPAGE record + * for the source xtpage (logredo() will init NoRedoPage + * filter and will also update bmap for free of the source + * xtpage), and update bmap for free of the source xtpage; + * N.B. We use tlckMAP instead of tlkcXTREE because there + * is no buffer associated with this lock since the buffer + * has been redirected to the target location. + */ + else /* (xtype == XTPAGE) */ + tlck = txMaplock(tid, ip, tlckMAP | tlckRELOCATE); + + pxdlock = (pxdlock_t *) & tlck->lock; + pxdlock->flag = mlckFREEPXD; + PXDaddress(&pxdlock->pxd, oxaddr); + PXDlength(&pxdlock->pxd, xlen); + pxdlock->index = 1; + + /* + * 4. update the parent xad entry for relocation; + * + * acquire tlck for the parent entry with XAD_NEW as entry + * update which will write LOG_REDOPAGE and update bmap for + * allocation of XAD_NEW destination extent; + */ + jEVENT(0, ("xtRelocate: update parent xad entry.\n")); + BT_MARK_DIRTY(pmp, ip); + tlck = txLock(tid, ip, pmp, tlckXTREE | tlckGROW); + xtlck = (xtlock_t *) & tlck->lock; + + /* update the XAD with the new destination extent; */ + xad = &pp->xad[index]; + xad->flag |= XAD_NEW; + XADaddress(xad, nxaddr); + + xtlck->lwm.offset = min(index, xtlck->lwm.offset); + xtlck->lwm.length = le16_to_cpu(pp->header.nextindex) - + xtlck->lwm.offset; + + /* unpin the parent xtpage */ + XT_PUTPAGE(pmp); + + return rc; +} + + +/* + * xtSearchNode() + * + * function: search for the internal xad entry covering specified extent. + * This function is mainly used by defragfs utility. + * + * parameters: + * ip - file object; + * xad - extent to find; + * cmpp - comparison result: + * btstack - traverse stack; + * flag - search process flag; + * + * returns: + * btstack contains (bn, index) of search path traversed to the entry. + * *cmpp is set to result of comparison with the entry returned. + * the page containing the entry is pinned at exit. + */ +static int xtSearchNode(struct inode *ip, xad_t * xad, /* required XAD entry */ + int *cmpp, btstack_t * btstack, int flag) +{ + int rc = 0; + s64 xoff, xaddr; + int xlen; + int cmp = 1; /* init for empty page */ + s64 bn; /* block number */ + metapage_t *mp; /* meta-page buffer */ + xtpage_t *p; /* page */ + int base, index, lim; + btframe_t *btsp; + s64 t64; + + BT_CLR(btstack); + + xoff = offsetXAD(xad); + xlen = lengthXAD(xad); + xaddr = addressXAD(xad); + + /* + * search down tree from root: + * + * between two consecutive entries of and of + * internal page, child page Pi contains entry with k, Ki <= K < Kj. + * + * if entry with search key K is not found + * internal page search find the entry with largest key Ki + * less than K which point to the child page to search; + * leaf page search find the entry with smallest key Kj + * greater than K so that the returned index is the position of + * the entry to be shifted right for insertion of new entry. + * for empty tree, search key is greater than any key of the tree. + * + * by convention, root bn = 0. + */ + for (bn = 0;;) { + /* get/pin the page to search */ + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + if (p->header.flag & BT_LEAF) + return ESTALE; + + lim = le16_to_cpu(p->header.nextindex) - XTENTRYSTART; + + /* + * binary search with search key K on the current page + */ + for (base = XTENTRYSTART; lim; lim >>= 1) { + index = base + (lim >> 1); + + XT_CMP(cmp, xoff, &p->xad[index], t64); + if (cmp == 0) { + /* + * search hit + * + * verify for exact match; + */ + if (xaddr == addressXAD(&p->xad[index]) && + xoff == offsetXAD(&p->xad[index])) { + *cmpp = cmp; + + /* save search result */ + btsp = btstack->top; + btsp->bn = bn; + btsp->index = index; + btsp->mp = mp; + + return 0; + } + + /* descend/search its child page */ + goto next; + } + + if (cmp > 0) { + base = index + 1; + --lim; + } + } + + /* + * search miss - non-leaf page: + * + * base is the smallest index with key (Kj) greater than + * search key (K) and may be zero or maxentry index. + * if base is non-zero, decrement base by one to get the parent + * entry of the child page to search. + */ + index = base ? base - 1 : base; + + /* + * go down to child page + */ + next: + /* get the child page block number */ + bn = addressXAD(&p->xad[index]); + + /* unpin the parent page */ + XT_PUTPAGE(mp); + } +} + + +/* + * xtRelink() + * + * function: + * link around a freed page. + * + * Parameter: + * int tid, + * struct inode *ip, + * xtpage_t *p) + * + * returns: + */ +static int xtRelink(tid_t tid, struct inode *ip, xtpage_t * p) +{ + int rc = 0; + metapage_t *mp; + s64 nextbn, prevbn; + tlock_t *tlck; + + nextbn = le64_to_cpu(p->header.next); + prevbn = le64_to_cpu(p->header.prev); + + /* update prev pointer of the next page */ + if (nextbn != 0) { + XT_GETPAGE(ip, nextbn, mp, PSIZE, p, rc); + if (rc) + return rc; + + /* + * acquire a transaction lock on the page; + * + * action: update prev pointer; + */ + BT_MARK_DIRTY(mp, ip); + tlck = txLock(tid, ip, mp, tlckXTREE | tlckRELINK); + + /* the page may already have been tlock'd */ + + p->header.prev = cpu_to_le64(prevbn); + + XT_PUTPAGE(mp); + } + + /* update next pointer of the previous page */ + if (prevbn != 0) { + XT_GETPAGE(ip, prevbn, mp, PSIZE, p, rc); + if (rc) + return rc; + + /* + * acquire a transaction lock on the page; + * + * action: update next pointer; + */ + BT_MARK_DIRTY(mp, ip); + tlck = txLock(tid, ip, mp, tlckXTREE | tlckRELINK); + + /* the page may already have been tlock'd */ + + p->header.next = le64_to_cpu(nextbn); + + XT_PUTPAGE(mp); + } + + return 0; +} +#endif /* _STILL_TO_PORT */ + + +/* + * xtInitRoot() + * + * initialize file root (inline in inode) + */ +void xtInitRoot(tid_t tid, struct inode *ip) +{ + xtpage_t *p; + tlock_t *tlck; + + /* + * acquire a transaction lock on the root + * + * action: + */ + tlck = txLock(tid, ip, (metapage_t *) &JFS_IP(ip)->bxflag, + tlckXTREE | tlckNEW); + p = &JFS_IP(ip)->i_xtroot; + + p->header.flag = DXD_INDEX | BT_ROOT | BT_LEAF; + p->header.nextindex = cpu_to_le16(XTENTRYSTART); + + if (S_ISDIR(ip->i_mode)) + p->header.maxentry = cpu_to_le16(XTROOTINITSLOT_DIR); + else { + p->header.maxentry = cpu_to_le16(XTROOTINITSLOT); + ip->i_size = 0; + } + + + return; +} + + +/* + * We can run into a deadlock truncating a file with a large number of + * xtree pages (large fragmented file). A robust fix would entail a + * reservation system where we would reserve a number of metadata pages + * and tlocks which we would be guaranteed without a deadlock. Without + * this, a partial fix is to limit number of metadata pages we will lock + * in a single transaction. Currently we will truncate the file so that + * no more than 50 leaf pages will be locked. The caller of xtTruncate + * will be responsible for ensuring that the current transaction gets + * committed, and that subsequent transactions are created to truncate + * the file further if needed. + */ +#define MAX_TRUNCATE_LEAVES 50 + +/* + * xtTruncate() + * + * function: + * traverse for truncation logging backward bottom up; + * terminate at the last extent entry at the current subtree + * root page covering new down size. + * truncation may occur within the last extent entry. + * + * parameter: + * int tid, + * struct inode *ip, + * s64 newsize, + * int type) {PWMAP, PMAP, WMAP; DELETE, TRUNCATE} + * + * return: + * + * note: + * PWMAP: + * 1. truncate (non-COMMIT_NOLINK file) + * by jfs_truncate() or jfs_open(O_TRUNC): + * xtree is updated; + * 2. truncate index table of directory when last entry removed + * map update via tlock at commit time; + * PMAP: + * Call xtTruncate_pmap instead + * WMAP: + * 1. remove (free zero link count) on last reference release + * (pmap has been freed at commit zero link count); + * 2. truncate (COMMIT_NOLINK file, i.e., tmp file): + * xtree is updated; + * map update directly at truncation time; + * + * if (DELETE) + * no LOG_NOREDOPAGE is required (NOREDOFILE is sufficient); + * else if (TRUNCATE) + * must write LOG_NOREDOPAGE for deleted index page; + * + * pages may already have been tlocked by anonymous transactions + * during file growth (i.e., write) before truncation; + * + * except last truncated entry, deleted entries remains as is + * in the page (nextindex is updated) for other use + * (e.g., log/update allocation map): this avoid copying the page + * info but delay free of pages; + * + */ +s64 xtTruncate(tid_t tid, struct inode *ip, s64 newsize, int flag) +{ + int rc = 0; + s64 teof; + metapage_t *mp; + xtpage_t *p; + s64 bn; + int index, nextindex; + xad_t *xad; + s64 xoff, xaddr; + int xlen, len, freexlen; + btstack_t btstack; + btframe_t *parent; + tblock_t *tblk = 0; + tlock_t *tlck = 0; + xtlock_t *xtlck = 0; + xdlistlock_t xadlock; /* maplock for COMMIT_WMAP */ + pxdlock_t *pxdlock; /* maplock for COMMIT_WMAP */ + s64 nfreed; + int freed, log; + int locked_leaves = 0; + + /* save object truncation type */ + if (tid) { + tblk = tid_to_tblock(tid); + tblk->xflag |= flag; + } + + nfreed = 0; + + flag &= COMMIT_MAP; + assert(flag != COMMIT_PMAP); + + if (flag == COMMIT_PWMAP) + log = 1; + else { + log = 0; + xadlock.flag = mlckFREEXADLIST; + xadlock.index = 1; + } + + /* + * if the newsize is not an integral number of pages, + * the file between newsize and next page boundary will + * be cleared. + * if truncating into a file hole, it will cause + * a full block to be allocated for the logical block. + */ + + /* + * release page blocks of truncated region + * + * free the data blocks from the leaf index blocks. + * delete the parent index entries corresponding to + * the freed child data/index blocks. + * free the index blocks themselves which aren't needed + * in new sized file. + * + * index blocks are updated only if the blocks are to be + * retained in the new sized file. + * if type is PMAP, the data and index pages are NOT + * freed, and the data and index blocks are NOT freed + * from working map. + * (this will allow continued access of data/index of + * temporary file (zerolink count file truncated to zero-length)). + */ + teof = (newsize + (JFS_SBI(ip->i_sb)->bsize - 1)) >> + JFS_SBI(ip->i_sb)->l2bsize; + + /* clear stack */ + BT_CLR(&btstack); + + /* + * start with root + * + * root resides in the inode + */ + bn = 0; + + /* + * first access of each page: + */ + getPage: + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return -rc; + + /* process entries backward from last index */ + index = le16_to_cpu(p->header.nextindex) - 1; + + if (p->header.flag & BT_INTERNAL) + goto getChild; + + /* + * leaf page + */ + + /* Since this is the rightmost leaf, and we may have already freed + * a page that was formerly to the right, let's make sure that the + * next pointer is zero. + */ + p->header.next = 0; + + freed = 0; + + /* does region covered by leaf page precede Teof ? */ + xad = &p->xad[index]; + xoff = offsetXAD(xad); + xlen = lengthXAD(xad); + if (teof >= xoff + xlen) { + XT_PUTPAGE(mp); + goto getParent; + } + + /* (re)acquire tlock of the leaf page */ + if (log) { + if (++locked_leaves > MAX_TRUNCATE_LEAVES) { + /* + * We need to limit the size of the transaction + * to avoid exhausting pagecache & tlocks + */ + XT_PUTPAGE(mp); + newsize = (xoff + xlen) << JFS_SBI(ip->i_sb)->l2bsize; + goto getParent; + } + tlck = txLock(tid, ip, mp, tlckXTREE); + tlck->type = tlckXTREE | tlckTRUNCATE; + xtlck = (xtlock_t *) & tlck->lock; + xtlck->hwm.offset = le16_to_cpu(p->header.nextindex) - 1; + } + BT_MARK_DIRTY(mp, ip); + + /* + * scan backward leaf page entries + */ + for (; index >= XTENTRYSTART; index--) { + xad = &p->xad[index]; + xoff = offsetXAD(xad); + xlen = lengthXAD(xad); + xaddr = addressXAD(xad); + + /* + * entry beyond eof: continue scan of current page + * xad + * ---|---=======-------> + * eof + */ + if (teof < xoff) { + nfreed += xlen; + continue; + } + + /* + * (xoff <= teof): last entry to be deleted from page; + * If other entries remain in page: keep and update the page. + */ + + /* + * eof == entry_start: delete the entry + * xad + * -------|=======-------> + * eof + * + */ + if (teof == xoff) { + nfreed += xlen; + + if (index == XTENTRYSTART) + break; + + nextindex = index; + } + /* + * eof within the entry: truncate the entry. + * xad + * -------===|===-------> + * eof + */ + else if (teof < xoff + xlen) { + /* update truncated entry */ + len = teof - xoff; + freexlen = xlen - len; + XADlength(xad, len); + + /* save pxd of truncated extent in tlck */ + xaddr += len; + if (log) { /* COMMIT_PWMAP */ + xtlck->lwm.offset = (xtlck->lwm.offset) ? + min(index, (int)xtlck->lwm.offset) : index; + xtlck->lwm.length = index + 1 - + xtlck->lwm.offset; + pxdlock = (pxdlock_t *) & xtlck->pxdlock; + pxdlock->flag = mlckFREEPXD; + PXDaddress(&pxdlock->pxd, xaddr); + PXDlength(&pxdlock->pxd, freexlen); + } + /* free truncated extent */ + else { /* COMMIT_WMAP */ + + pxdlock = (pxdlock_t *) & xadlock; + pxdlock->flag = mlckFREEPXD; + PXDaddress(&pxdlock->pxd, xaddr); + PXDlength(&pxdlock->pxd, freexlen); + txFreeMap(ip, pxdlock, 0, COMMIT_WMAP); + + /* reset map lock */ + xadlock.flag = mlckFREEXADLIST; + } + + /* current entry is new last entry; */ + nextindex = index + 1; + + nfreed += freexlen; + } + /* + * eof beyond the entry: + * xad + * -------=======---|---> + * eof + */ + else { /* (xoff + xlen < teof) */ + + nextindex = index + 1; + } + + if (nextindex < le16_to_cpu(p->header.nextindex)) { + if (!log) { /* COMMIT_WAMP */ + xadlock.xdlist = &p->xad[nextindex]; + xadlock.count = + le16_to_cpu(p->header.nextindex) - + nextindex; + txFreeMap(ip, (maplock_t *) & xadlock, 0, + COMMIT_WMAP); + } + p->header.nextindex = cpu_to_le16(nextindex); + } + + XT_PUTPAGE(mp); + + /* assert(freed == 0); */ + goto getParent; + } /* end scan of leaf page entries */ + + freed = 1; + + /* + * leaf page become empty: free the page if type != PMAP + */ + if (log) { /* COMMIT_PWMAP */ + /* txCommit() with tlckFREE: + * free data extents covered by leaf [XTENTRYSTART:hwm); + * invalidate leaf if COMMIT_PWMAP; + * if (TRUNCATE), will write LOG_NOREDOPAGE; + */ + tlck->type = tlckXTREE | tlckFREE; + } else { /* COMMIT_WAMP */ + + /* free data extents covered by leaf */ + xadlock.xdlist = &p->xad[XTENTRYSTART]; + xadlock.count = + le16_to_cpu(p->header.nextindex) - XTENTRYSTART; + txFreeMap(ip, (maplock_t *) & xadlock, 0, COMMIT_WMAP); + } + + if (p->header.flag & BT_ROOT) { + p->header.flag &= ~BT_INTERNAL; + p->header.flag |= BT_LEAF; + p->header.nextindex = cpu_to_le16(XTENTRYSTART); + + XT_PUTPAGE(mp); /* debug */ + goto out; + } else { + if (log) { /* COMMIT_PWMAP */ + /* page will be invalidated at tx completion + */ + XT_PUTPAGE(mp); + } else { /* COMMIT_WMAP */ + + if (mp->lid) + lid_to_tlock(mp->lid)->flag |= tlckFREELOCK; + + /* invalidate empty leaf page */ + discard_metapage(mp); + } + } + + /* + * the leaf page become empty: delete the parent entry + * for the leaf page if the parent page is to be kept + * in the new sized file. + */ + + /* + * go back up to the parent page + */ + getParent: + /* pop/restore parent entry for the current child page */ + if ((parent = BT_POP(&btstack)) == NULL) + /* current page must have been root */ + goto out; + + /* get back the parent page */ + bn = parent->bn; + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return -rc; + + index = parent->index; + + /* + * child page was not empty: + */ + if (freed == 0) { + /* has any entry deleted from parent ? */ + if (index < le16_to_cpu(p->header.nextindex) - 1) { + /* (re)acquire tlock on the parent page */ + if (log) { /* COMMIT_PWMAP */ + /* txCommit() with tlckTRUNCATE: + * free child extents covered by parent [); + */ + tlck = txLock(tid, ip, mp, tlckXTREE); + xtlck = (xtlock_t *) & tlck->lock; + xtlck->twm.offset = index; + if (!(tlck->type & tlckTRUNCATE)) { + xtlck->hwm.offset = + le16_to_cpu(p->header. + nextindex) - 1; + tlck->type = + tlckXTREE | tlckTRUNCATE; + } + } else { /* COMMIT_WMAP */ + + /* free child extents covered by parent */ + xadlock.xdlist = &p->xad[index + 1]; + xadlock.count = + le16_to_cpu(p->header.nextindex) - + index - 1; + txFreeMap(ip, (maplock_t *) & xadlock, 0, + COMMIT_WMAP); + } + BT_MARK_DIRTY(mp, ip); + + p->header.nextindex = cpu_to_le16(index + 1); + } + XT_PUTPAGE(mp); + goto getParent; + } + + /* + * child page was empty: + */ + nfreed += lengthXAD(&p->xad[index]); + + /* + * During working map update, child page's tlock must be handled + * before parent's. This is because the parent's tlock will cause + * the child's disk space to be marked available in the wmap, so + * it's important that the child page be released by that time. + * + * ToDo: tlocks should be on doubly-linked list, so we can + * quickly remove it and add it to the end. + */ + + /* + * Move parent page's tlock to the end of the tid's tlock list + */ + if (log && mp->lid && (tblk->last != mp->lid) && + lid_to_tlock(mp->lid)->tid) { + lid_t lid = mp->lid; + tlock_t *prev; + + tlck = lid_to_tlock(lid); + + if (tblk->next == lid) + tblk->next = tlck->next; + else { + for (prev = lid_to_tlock(tblk->next); + prev->next != lid; + prev = lid_to_tlock(prev->next)) { + assert(prev->next); + } + prev->next = tlck->next; + } + lid_to_tlock(tblk->last)->next = lid; + tlck->next = 0; + tblk->last = lid; + } + + /* + * parent page become empty: free the page + */ + if (index == XTENTRYSTART) { + if (log) { /* COMMIT_PWMAP */ + /* txCommit() with tlckFREE: + * free child extents covered by parent; + * invalidate parent if COMMIT_PWMAP; + */ + tlck = txLock(tid, ip, mp, tlckXTREE); + xtlck = (xtlock_t *) & tlck->lock; + xtlck->twm.offset = index; + xtlck->hwm.offset = + le16_to_cpu(p->header.nextindex) - 1; + tlck->type = tlckXTREE | tlckFREE; + } else { /* COMMIT_WMAP */ + + /* free child extents covered by parent */ + xadlock.xdlist = &p->xad[XTENTRYSTART]; + xadlock.count = + le16_to_cpu(p->header.nextindex) - + XTENTRYSTART; + txFreeMap(ip, (maplock_t *) & xadlock, 0, + COMMIT_WMAP); + } + BT_MARK_DIRTY(mp, ip); + + if (p->header.flag & BT_ROOT) { + p->header.flag &= ~BT_INTERNAL; + p->header.flag |= BT_LEAF; + p->header.nextindex = cpu_to_le16(XTENTRYSTART); + if (le16_to_cpu(p->header.maxentry) == XTROOTMAXSLOT) { + /* + * Shrink root down to allow inline + * EA (otherwise fsck complains) + */ + p->header.maxentry = + cpu_to_le16(XTROOTINITSLOT); + JFS_IP(ip)->mode2 |= INLINEEA; + } + + XT_PUTPAGE(mp); /* debug */ + goto out; + } else { + if (log) { /* COMMIT_PWMAP */ + /* page will be invalidated at tx completion + */ + XT_PUTPAGE(mp); + } else { /* COMMIT_WMAP */ + + if (mp->lid) + lid_to_tlock(mp->lid)->flag |= + tlckFREELOCK; + + /* invalidate parent page */ + discard_metapage(mp); + } + + /* parent has become empty and freed: + * go back up to its parent page + */ + /* freed = 1; */ + goto getParent; + } + } + /* + * parent page still has entries for front region; + */ + else { + /* try truncate region covered by preceding entry + * (process backward) + */ + index--; + + /* go back down to the child page corresponding + * to the entry + */ + goto getChild; + } + + /* + * internal page: go down to child page of current entry + */ + getChild: + /* save current parent entry for the child page */ + BT_PUSH(&btstack, bn, index); + + /* get child page */ + xad = &p->xad[index]; + bn = addressXAD(xad); + + /* + * first access of each internal entry: + */ + /* release parent page */ + XT_PUTPAGE(mp); + + /* process the child page */ + goto getPage; + + out: + /* + * update file resource stat + */ + /* set size + */ + if (S_ISDIR(ip->i_mode) && !newsize) + ip->i_size = 1; /* fsck hates zero-length directories */ + else + ip->i_size = newsize; + + /* update nblocks to reflect freed blocks */ + ip->i_blocks -= LBLK2PBLK(ip->i_sb, nfreed); + + /* + * free tlock of invalidated pages + */ + if (flag == COMMIT_WMAP) + txFreelock(ip); + + return newsize; +} + + +/* + * xtTruncate_pmap() + * + * function: + * Perform truncate to zero lenghth for deleted file, leaving the + * the xtree and working map untouched. This allows the file to + * be accessed via open file handles, while the delete of the file + * is committed to disk. + * + * parameter: + * tid_t tid, + * struct inode *ip, + * s64 committed_size) + * + * return: new committed size + * + * note: + * + * To avoid deadlock by holding too many transaction locks, the + * truncation may be broken up into multiple transactions. + * The committed_size keeps track of part of the file has been + * freed from the pmaps. + */ +s64 xtTruncate_pmap(tid_t tid, struct inode *ip, s64 committed_size) +{ + s64 bn; + btstack_t btstack; + int cmp; + int index; + int locked_leaves = 0; + metapage_t *mp; + xtpage_t *p; + btframe_t *parent; + int rc; + tblock_t *tblk; + tlock_t *tlck = 0; + xad_t *xad; + int xlen; + s64 xoff; + xtlock_t *xtlck = 0; + + /* save object truncation type */ + tblk = tid_to_tblock(tid); + tblk->xflag |= COMMIT_PMAP; + + /* clear stack */ + BT_CLR(&btstack); + + if (committed_size) { + xoff = (committed_size >> JFS_SBI(ip->i_sb)->l2bsize) - 1; + rc = xtSearch(ip, xoff, &cmp, &btstack, 0); + if (rc) + return -rc; + assert(cmp == 0); + XT_GETSEARCH(ip, btstack.top, bn, mp, p, index); + } else { + /* + * start with root + * + * root resides in the inode + */ + bn = 0; + + /* + * first access of each page: + */ + getPage: + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return -rc; + + /* process entries backward from last index */ + index = le16_to_cpu(p->header.nextindex) - 1; + + if (p->header.flag & BT_INTERNAL) + goto getChild; + } + + /* + * leaf page + */ + + if (++locked_leaves > MAX_TRUNCATE_LEAVES) { + /* + * We need to limit the size of the transaction + * to avoid exhausting pagecache & tlocks + */ + xad = &p->xad[index]; + xoff = offsetXAD(xad); + xlen = lengthXAD(xad); + XT_PUTPAGE(mp); + return (xoff + xlen) << JFS_SBI(ip->i_sb)->l2bsize; + } + tlck = txLock(tid, ip, mp, tlckXTREE); + tlck->type = tlckXTREE | tlckTRUNCATE; + xtlck = (xtlock_t *) & tlck->lock; + xtlck->hwm.offset = index; + + tlck->type = tlckXTREE | tlckFREE; + + XT_PUTPAGE(mp); + + /* + * go back up to the parent page + */ + getParent: + /* pop/restore parent entry for the current child page */ + if ((parent = BT_POP(&btstack)) == NULL) + /* current page must have been root */ + goto out; + + /* get back the parent page */ + bn = parent->bn; + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return -rc; + + index = parent->index; + + /* + * parent page become empty: free the page + */ + if (index == XTENTRYSTART) { + /* txCommit() with tlckFREE: + * free child extents covered by parent; + * invalidate parent if COMMIT_PWMAP; + */ + tlck = txLock(tid, ip, mp, tlckXTREE); + xtlck = (xtlock_t *) & tlck->lock; + xtlck->twm.offset = index; + xtlck->hwm.offset = + le16_to_cpu(p->header.nextindex) - 1; + tlck->type = tlckXTREE | tlckFREE; + + XT_PUTPAGE(mp); + + if (p->header.flag & BT_ROOT) { + + goto out; + } else { + goto getParent; + } + } + /* + * parent page still has entries for front region; + */ + else + index--; + /* + * internal page: go down to child page of current entry + */ + getChild: + /* save current parent entry for the child page */ + BT_PUSH(&btstack, bn, index); + + /* get child page */ + xad = &p->xad[index]; + bn = addressXAD(xad); + + /* + * first access of each internal entry: + */ + /* release parent page */ + XT_PUTPAGE(mp); + + /* process the child page */ + goto getPage; + + out: + + return 0; +} + + +#ifdef _JFS_DEBUG_XTREE +/* + * xtDisplayTree() + * + * function: traverse forward + */ +int xtDisplayTree(struct inode *ip) +{ + int rc = 0; + metapage_t *mp; + xtpage_t *p; + s64 bn, pbn; + int index, lastindex, v, h; + xad_t *xad; + btstack_t btstack; + btframe_t *btsp; + btframe_t *parent; + + printk("display B+-tree.\n"); + + /* clear stack */ + btsp = btstack.stack; + + /* + * start with root + * + * root resides in the inode + */ + bn = 0; + v = h = 0; + + /* + * first access of each page: + */ + getPage: + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + /* process entries forward from first index */ + index = XTENTRYSTART; + lastindex = le16_to_cpu(p->header.nextindex) - 1; + + if (p->header.flag & BT_INTERNAL) { + /* + * first access of each internal page + */ + goto getChild; + } else { /* (p->header.flag & BT_LEAF) */ + + /* + * first access of each leaf page + */ + printf("leaf page "); + xtDisplayPage(ip, bn, p); + + /* unpin the leaf page */ + XT_PUTPAGE(mp); + } + + /* + * go back up to the parent page + */ + getParent: + /* pop/restore parent entry for the current child page */ + if ((parent = (btsp == btstack.stack ? NULL : --btsp)) == NULL) + /* current page must have been root */ + return; + + /* + * parent page scan completed + */ + if ((index = parent->index) == (lastindex = parent->lastindex)) { + /* go back up to the parent page */ + goto getParent; + } + + /* + * parent page has entries remaining + */ + /* get back the parent page */ + bn = parent->bn; + /* v = parent->level; */ + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + /* get next parent entry */ + index++; + + /* + * internal page: go down to child page of current entry + */ + getChild: + /* push/save current parent entry for the child page */ + btsp->bn = pbn = bn; + btsp->index = index; + btsp->lastindex = lastindex; + /* btsp->level = v; */ + /* btsp->node = h; */ + ++btsp; + + /* get child page */ + xad = &p->xad[index]; + bn = addressXAD(xad); + + /* + * first access of each internal entry: + */ + /* release parent page */ + XT_PUTPAGE(mp); + + printk("traverse down 0x%lx[%d]->0x%lx\n", (ulong) pbn, index, + (ulong) bn); + v++; + h = index; + + /* process the child page */ + goto getPage; +} + + +/* + * xtDisplayPage() + * + * function: display page + */ +int xtDisplayPage(struct inode *ip, s64 bn, xtpage_t * p) +{ + int rc = 0; + metapage_t *mp; + xad_t *xad; + s64 xaddr, xoff; + int xlen, i, j; + + if (p == NULL) { + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + } + + /* display page control */ + printf("bn:0x%lx flag:0x%x nextindex:%d\n", + (ulong) bn, p->header.flag, + le16_to_cpu(p->header.nextindex)); + + /* display entries */ + xad = &p->xad[XTENTRYSTART]; + for (i = XTENTRYSTART, j = 1; i < le16_to_cpu(p->header.nextindex); + i++, xad++, j++) { + xoff = offsetXAD(xad); + xaddr = addressXAD(xad); + xlen = lengthXAD(xad); + printf("\t[%d] 0x%lx:0x%lx(0x%x)", i, (ulong) xoff, + (ulong) xaddr, xlen); + + if (j == 4) { + printf("\n"); + j = 0; + } + } + + printf("\n"); +} +#endif /* _JFS_DEBUG_XTREE */ + + +#ifdef _JFS_WIP +/* + * xtGather() + * + * function: + * traverse for allocation acquiring tlock at commit time + * (vs at the time of update) logging backward top down + * + * note: + * problem - establishing that all new allocation have been + * processed both for append and random write in sparse file + * at the current entry at the current subtree root page + * + */ +int xtGather(t) +btree_t *t; +{ + int rc = 0; + xtpage_t *p; + u64 bn; + int index; + btentry_t *e; + btstack_t btstack; + struct btsf *parent; + + /* clear stack */ + BT_CLR(&btstack); + + /* + * start with root + * + * root resides in the inode + */ + bn = 0; + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + /* new root is NOT pointed by a new entry + if (p->header.flag & NEW) + allocate new page lock; + write a NEWPAGE log; + */ + + dopage: + /* + * first access of each page: + */ + /* process entries backward from last index */ + index = le16_to_cpu(p->header.nextindex) - 1; + + if (p->header.flag & BT_LEAF) { + /* + * first access of each leaf page + */ + /* process leaf page entries backward */ + for (; index >= XTENTRYSTART; index--) { + e = &p->xad[index]; + /* + * if newpage, log NEWPAGE. + * + if (e->flag & XAD_NEW) { + nfound =+ entry->length; + update current page lock for the entry; + newpage(entry); + * + * if moved, log move. + * + } else if (e->flag & XAD_MOVED) { + reset flag; + update current page lock for the entry; + } + */ + } + + /* unpin the leaf page */ + XT_PUTPAGE(mp); + + /* + * go back up to the parent page + */ + getParent: + /* restore parent entry for the current child page */ + if ((parent = BT_POP(&btstack)) == NULL) + /* current page must have been root */ + return 0; + + if ((index = parent->index) == XTENTRYSTART) { + /* + * parent page scan completed + */ + /* go back up to the parent page */ + goto getParent; + } else { + /* + * parent page has entries remaining + */ + /* get back the parent page */ + bn = parent->bn; + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return EIO; + + /* first subroot page which + * covers all new allocated blocks + * itself not new/modified. + * (if modified from split of descendent, + * go down path of split page) + + if (nfound == nnew && + !(p->header.flag & (NEW | MOD))) + exit scan; + */ + + /* process parent page entries backward */ + index--; + } + } else { + /* + * first access of each internal page + */ + } + + /* + * internal page: go down to child page of current entry + */ + + /* save current parent entry for the child page */ + BT_PUSH(&btstack, bn, index); + + /* get current entry for the child page */ + e = &p->xad[index]; + + /* + * first access of each internal entry: + */ + /* + * if new entry, log btree_tnewentry. + * + if (e->flag & XAD_NEW) + update parent page lock for the entry; + */ + + /* release parent page */ + XT_PUTPAGE(mp); + + /* get child page */ + bn = e->bn; + XT_GETPAGE(ip, bn, mp, PSIZE, p, rc); + if (rc) + return rc; + + /* + * first access of each non-root page: + */ + /* + * if new, log btree_newpage. + * + if (p->header.flag & NEW) + allocate new page lock; + write a NEWPAGE log (next, prev); + */ + + /* process the child page */ + goto dopage; + + out: + return 0; +} +#endif /* _JFS_WIP */ + + +#ifdef CONFIG_JFS_STATISTICS +int jfs_xtstat_read(char *buffer, char **start, off_t offset, int length, + int *eof, void *data) +{ + int len = 0; + off_t begin; + + len += sprintf(buffer, + "JFS Xtree statistics\n" + "====================\n" + "searches = %d\n" + "fast searches = %d\n" + "splits = %d\n", + xtStat.search, + xtStat.fastSearch, + xtStat.split); + + begin = offset; + *start = buffer + begin; + len -= begin; + + if (len > length) + len = length; + else + *eof = 1; + + if (len < 0) + len = 0; + + return len; +} +#endif diff -Nru a/fs/jfs/jfs_xtree.h b/fs/jfs/jfs_xtree.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/jfs_xtree.h Thu Mar 7 18:17:47 2002 @@ -0,0 +1,143 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 +*/ +/* + * Change History : + * +*/ + +#ifndef _H_JFS_XTREE +#define _H_JFS_XTREE + +/* + * jfs_xtree.h: extent allocation descriptor B+-tree manager + */ + +#include "jfs_btree.h" + + +/* + * extent allocation descriptor (xad) + */ +typedef struct xad { + unsigned flag:8; /* 1: flag */ + unsigned rsvrd:16; /* 2: reserved */ + unsigned off1:8; /* 1: offset in unit of fsblksize */ + u32 off2; /* 4: offset in unit of fsblksize */ + unsigned len:24; /* 3: length in unit of fsblksize */ + unsigned addr1:8; /* 1: address in unit of fsblksize */ + u32 addr2; /* 4: address in unit of fsblksize */ +} xad_t; /* (16) */ + +#define MAXXLEN ((1 << 24) - 1) + +#define XTSLOTSIZE 16 +#define L2XTSLOTSIZE 4 + +/* xad_t field construction */ +#define XADoffset(xad, offset64)\ +{\ + (xad)->off1 = ((u64)offset64) >> 32;\ + (xad)->off2 = __cpu_to_le32((offset64) & 0xffffffff);\ +} +#define XADaddress(xad, address64)\ +{\ + (xad)->addr1 = ((u64)address64) >> 32;\ + (xad)->addr2 = __cpu_to_le32((address64) & 0xffffffff);\ +} +#define XADlength(xad, length32) (xad)->len = __cpu_to_le24(length32) + +/* xad_t field extraction */ +#define offsetXAD(xad)\ + ( ((s64)((xad)->off1)) << 32 | __le32_to_cpu((xad)->off2)) +#define addressXAD(xad)\ + ( ((s64)((xad)->addr1)) << 32 | __le32_to_cpu((xad)->addr2)) +#define lengthXAD(xad) __le24_to_cpu((xad)->len) + +/* xad list */ +typedef struct { + s16 maxnxad; + s16 nxad; + xad_t *xad; +} xadlist_t; + +/* xad_t flags */ +#define XAD_NEW 0x01 /* new */ +#define XAD_EXTENDED 0x02 /* extended */ +#define XAD_COMPRESSED 0x04 /* compressed with recorded length */ +#define XAD_NOTRECORDED 0x08 /* allocated but not recorded */ +#define XAD_COW 0x10 /* copy-on-write */ + + +/* possible values for maxentry */ +#define XTROOTINITSLOT_DIR 6 +#define XTROOTINITSLOT 10 +#define XTROOTMAXSLOT 18 +#define XTPAGEMAXSLOT 256 +#define XTENTRYSTART 2 + +/* + * xtree page: + */ +typedef union { + struct xtheader { + s64 next; /* 8: */ + s64 prev; /* 8: */ + + u8 flag; /* 1: */ + u8 rsrvd1; /* 1: */ + s16 nextindex; /* 2: next index = number of entries */ + s16 maxentry; /* 2: max number of entries */ + s16 rsrvd2; /* 2: */ + + pxd_t self; /* 8: self */ + } header; /* (32) */ + + xad_t xad[XTROOTMAXSLOT]; /* 16 * maxentry: xad array */ +} xtpage_t; + +/* + * external declaration + */ +extern int xtLookup(struct inode *ip, s64 lstart, s64 llen, + int *pflag, s64 * paddr, int *plen, int flag); +extern int xtLookupList(struct inode *ip, lxdlist_t * lxdlist, + xadlist_t * xadlist, int flag); +extern void xtInitRoot(tid_t tid, struct inode *ip); +extern int xtInsert(tid_t tid, struct inode *ip, + int xflag, s64 xoff, int xlen, s64 * xaddrp, int flag); +extern int xtExtend(tid_t tid, struct inode *ip, s64 xoff, int xlen, + int flag); +extern int xtTailgate(tid_t tid, struct inode *ip, + s64 xoff, int xlen, s64 xaddr, int flag); +extern int xtUpdate(tid_t tid, struct inode *ip, struct xad *nxad); +extern int xtDelete(tid_t tid, struct inode *ip, s64 xoff, int xlen, + int flag); +extern s64 xtTruncate(tid_t tid, struct inode *ip, s64 newsize, int type); +extern s64 xtTruncate_pmap(tid_t tid, struct inode *ip, s64 committed_size); +extern int xtRelocate(tid_t tid, struct inode *ip, + xad_t * oxad, s64 nxaddr, int xtype); +extern int xtAppend(tid_t tid, + struct inode *ip, int xflag, s64 xoff, int maxblocks, + int *xlenp, s64 * xaddrp, int flag); + +#ifdef _JFS_DEBUG_XTREE +extern int xtDisplayTree(struct inode *ip); +extern int xtDisplayPage(struct inode *ip, s64 bn, xtpage_t * p); +#endif /* _JFS_DEBUG_XTREE */ + +#endif /* !_H_JFS_XTREE */ diff -Nru a/fs/jfs/namei.c b/fs/jfs/namei.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/namei.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,1499 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * + * Module: jfs/namei.c + * + */ + +/* + * Change History : + * + */ + +#include +#include "jfs_incore.h" +#include "jfs_inode.h" +#include "jfs_dinode.h" +#include "jfs_dmap.h" +#include "jfs_unicode.h" +#include "jfs_metapage.h" +#include "jfs_debug.h" +#include +#include + +extern struct inode_operations jfs_file_inode_operations; +extern struct inode_operations jfs_symlink_inode_operations; +extern struct file_operations jfs_file_operations; +extern struct address_space_operations jfs_aops; + +extern int jfs_fsync(struct file *, struct dentry *, int); +extern void jfs_truncate_nolock(struct inode *, loff_t); + +/* + * forward references + */ +struct inode_operations jfs_dir_inode_operations; +struct file_operations jfs_dir_operations; + +s64 commitZeroLink(tid_t, struct inode *); + +/* + * NAME: jfs_create(dip, dentry, mode) + * + * FUNCTION: create a regular file in the parent directory + * with name = and mode = + * + * PARAMETER: dip - parent directory vnode + * dentry - dentry of new file + * mode - create mode (rwxrwxrwx). + * + * RETURN: Errors from subroutines + * + */ +int jfs_create(struct inode *dip, struct dentry *dentry, int mode) +{ + int rc = 0; + tid_t tid; /* transaction id */ + struct inode *ip = NULL; /* child directory inode */ + ino_t ino; + component_t dname; /* child directory name */ + btstack_t btstack; + struct inode *iplist[2]; + tblock_t *tblk; + + jFYI(1, ("jfs_create: dip:0x%p name:%s\n", dip, dentry->d_name.name)); + + IWRITE_LOCK(dip); + + /* + * search parent directory for entry/freespace + * (dtSearch() returns parent directory page pinned) + */ + if ((rc = get_UCSname(&dname, dentry, JFS_SBI(dip->i_sb)->nls_tab))) + goto out1; + + /* + * Either iAlloc() or txBegin() may block. Deadlock can occur if we + * block there while holding dtree page, so we allocate the inode & + * begin the transaction before we search the directory. + */ + ip = ialloc(dip, mode); + if (ip == NULL) { + rc = ENOSPC; + goto out2; + } + + tid = txBegin(dip->i_sb, 0); + + if ((rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE))) { + jERROR(1, ("jfs_create: dtSearch returned %d\n", rc)); + ip->i_nlink = 0; + iput(ip); + txEnd(tid); + goto out2; + } + + tblk = tid_to_tblock(tid); + tblk->xflag |= COMMIT_CREATE; + tblk->ip = ip; + + iplist[0] = dip; + iplist[1] = ip; + + /* + * initialize the child XAD tree root in-line in inode + */ + xtInitRoot(tid, ip); + + /* + * create entry in parent directory for child directory + * (dtInsert() releases parent directory page) + */ + ino = ip->i_ino; + if ((rc = dtInsert(tid, dip, &dname, &ino, &btstack))) { + jERROR(1, ("jfs_create: dtInsert returned %d\n", rc)); + /* discard new inode */ + ip->i_nlink = 0; + iput(ip); + + if (rc == EIO) + txAbort(tid, 1); /* Marks Filesystem dirty */ + else + txAbort(tid, 0); /* Filesystem full */ + txEnd(tid); + goto out2; + } + + ip->i_op = &jfs_file_inode_operations; + ip->i_fop = &jfs_file_operations; + ip->i_mapping->a_ops = &jfs_aops; + + insert_inode_hash(ip); + mark_inode_dirty(ip); + d_instantiate(dentry, ip); + + dip->i_version = ++event; + dip->i_ctime = dip->i_mtime = CURRENT_TIME; + + mark_inode_dirty(dip); + + rc = txCommit(tid, 2, &iplist[0], 0); + txEnd(tid); + + out2: + free_UCSname(&dname); + + out1: + + IWRITE_UNLOCK(dip); + jFYI(1, ("jfs_create: rc:%d\n", -rc)); + return -rc; +} + + +/* + * NAME: jfs_mkdir(dip, dentry, mode) + * + * FUNCTION: create a child directory in the parent directory + * with name = and mode = + * + * PARAMETER: dip - parent directory vnode + * dentry - dentry of child directory + * mode - create mode (rwxrwxrwx). + * + * RETURN: Errors from subroutines + * + * note: + * EACCESS: user needs search+write permission on the parent directory + */ +int jfs_mkdir(struct inode *dip, struct dentry *dentry, int mode) +{ + int rc = 0; + tid_t tid; /* transaction id */ + struct inode *ip = NULL; /* child directory inode */ + ino_t ino; + component_t dname; /* child directory name */ + btstack_t btstack; + struct inode *iplist[2]; + tblock_t *tblk; + + jFYI(1, ("jfs_mkdir: dip:0x%p name:%s\n", dip, dentry->d_name.name)); + + IWRITE_LOCK(dip); + + /* link count overflow on parent directory ? */ + if (dip->i_nlink == JFS_LINK_MAX) { + rc = EMLINK; + goto out1; + } + + /* + * search parent directory for entry/freespace + * (dtSearch() returns parent directory page pinned) + */ + if ((rc = get_UCSname(&dname, dentry, JFS_SBI(dip->i_sb)->nls_tab))) + goto out1; + + /* + * Either iAlloc() or txBegin() may block. Deadlock can occur if we + * block there while holding dtree page, so we allocate the inode & + * begin the transaction before we search the directory. + */ + ip = ialloc(dip, S_IFDIR | mode); + if (ip == NULL) { + rc = ENOSPC; + goto out2; + } + + tid = txBegin(dip->i_sb, 0); + + if ((rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE))) { + jERROR(1, ("jfs_mkdir: dtSearch returned %d\n", rc)); + ip->i_nlink = 0; + iput(ip); + txEnd(tid); + goto out2; + } + + tblk = tid_to_tblock(tid); + tblk->xflag |= COMMIT_CREATE; + tblk->ip = ip; + + iplist[0] = dip; + iplist[1] = ip; + + /* + * initialize the child directory in-line in inode + */ + dtInitRoot(tid, ip, dip->i_ino); + + /* + * create entry in parent directory for child directory + * (dtInsert() releases parent directory page) + */ + ino = ip->i_ino; + if ((rc = dtInsert(tid, dip, &dname, &ino, &btstack))) { + jERROR(1, ("jfs_mkdir: dtInsert returned %d\n", rc)); + /* discard new directory inode */ + ip->i_nlink = 0; + iput(ip); + + if (rc == EIO) + txAbort(tid, 1); /* Marks Filesystem dirty */ + else + txAbort(tid, 0); /* Filesystem full */ + txEnd(tid); + goto out2; + } + + ip->i_nlink = 2; /* for '.' */ + ip->i_op = &jfs_dir_inode_operations; + ip->i_fop = &jfs_dir_operations; + ip->i_mapping->a_ops = &jfs_aops; + ip->i_mapping->gfp_mask = GFP_NOFS; + + insert_inode_hash(ip); + mark_inode_dirty(ip); + d_instantiate(dentry, ip); + + /* update parent directory inode */ + dip->i_nlink++; /* for '..' from child directory */ + dip->i_version = ++event; + dip->i_ctime = dip->i_mtime = CURRENT_TIME; + mark_inode_dirty(dip); + + rc = txCommit(tid, 2, &iplist[0], 0); + txEnd(tid); + + out2: + free_UCSname(&dname); + + out1: + + IWRITE_UNLOCK(dip); + + jFYI(1, ("jfs_mkdir: rc:%d\n", -rc)); + return -rc; +} + +/* + * NAME: jfs_rmdir(dip, dentry) + * + * FUNCTION: remove a link to child directory + * + * PARAMETER: dip - parent inode + * dentry - child directory dentry + * + * RETURN: EINVAL - if name is . or .. + * EINVAL - if . or .. exist but are invalid. + * errors from subroutines + * + * note: + * if other threads have the directory open when the last link + * is removed, the "." and ".." entries, if present, are removed before + * rmdir() returns and no new entries may be created in the directory, + * but the directory is not removed until the last reference to + * the directory is released (cf.unlink() of regular file). + */ +int jfs_rmdir(struct inode *dip, struct dentry *dentry) +{ + int rc; + tid_t tid; /* transaction id */ + struct inode *ip = dentry->d_inode; + ino_t ino; + component_t dname; + struct inode *iplist[2]; + tblock_t *tblk; + + jFYI(1, ("jfs_rmdir: dip:0x%p name:%s\n", dip, dentry->d_name.name)); + + IWRITE_LOCK_LIST(2, dip, ip); + + /* directory must be empty to be removed */ + if (!dtEmpty(ip)) { + IWRITE_UNLOCK(ip); + IWRITE_UNLOCK(dip); + rc = ENOTEMPTY; + goto out; + } + + if ((rc = get_UCSname(&dname, dentry, JFS_SBI(dip->i_sb)->nls_tab))) { + IWRITE_UNLOCK(ip); + IWRITE_UNLOCK(dip); + goto out; + } + + tid = txBegin(dip->i_sb, 0); + + iplist[0] = dip; + iplist[1] = ip; + + tblk = tid_to_tblock(tid); + tblk->xflag |= COMMIT_DELETE; + tblk->ip = ip; + + /* + * delete the entry of target directory from parent directory + */ + ino = ip->i_ino; + if ((rc = dtDelete(tid, dip, &dname, &ino, JFS_REMOVE))) { + jERROR(1, ("jfs_rmdir: dtDelete returned %d\n", rc)); + if (rc == EIO) + txAbort(tid, 1); + txEnd(tid); + + IWRITE_UNLOCK(ip); + IWRITE_UNLOCK(dip); + + goto out2; + } + + /* update parent directory's link count corresponding + * to ".." entry of the target directory deleted + */ + dip->i_nlink--; + dip->i_ctime = dip->i_mtime = CURRENT_TIME; + dip->i_version = ++event; + mark_inode_dirty(dip); + + /* + * OS/2 could have created EA and/or ACL + */ + /* free EA from both persistent and working map */ + if (JFS_IP(ip)->ea.flag & DXD_EXTENT) { + /* free EA pages */ + txEA(tid, ip, &JFS_IP(ip)->ea, NULL); + } + JFS_IP(ip)->ea.flag = 0; + + /* free ACL from both persistent and working map */ + if (JFS_IP(ip)->acl.flag & DXD_EXTENT) { + /* free ACL pages */ + txEA(tid, ip, &JFS_IP(ip)->acl, NULL); + } + JFS_IP(ip)->acl.flag = 0; + + /* mark the target directory as deleted */ + ip->i_nlink = 0; + mark_inode_dirty(ip); + + rc = txCommit(tid, 2, &iplist[0], 0); + + txEnd(tid); + + IWRITE_UNLOCK(ip); + + /* + * Truncating the directory index table is not guaranteed. It + * may need to be done iteratively + */ + if (test_cflag(COMMIT_Stale, dip)) { + if (dip->i_size > 1) + jfs_truncate_nolock(dip, 0); + + clear_cflag(COMMIT_Stale, dip); + } + + IWRITE_UNLOCK(dip); + + d_delete(dentry); + + out2: + free_UCSname(&dname); + + out: + jFYI(1, ("jfs_rmdir: rc:%d\n", rc)); + return -rc; +} + +/* + * NAME: jfs_unlink(dip, dentry) + * + * FUNCTION: remove a link to object named by + * from parent directory + * + * PARAMETER: dip - inode of parent directory + * dentry - dentry of object to be removed + * + * RETURN: errors from subroutines + * + * note: + * temporary file: if one or more processes have the file open + * when the last link is removed, the link will be removed before + * unlink() returns, but the removal of the file contents will be + * postponed until all references to the files are closed. + * + * JFS does NOT support unlink() on directories. + * + */ +int jfs_unlink(struct inode *dip, struct dentry *dentry) +{ + int rc; + tid_t tid; /* transaction id */ + struct inode *ip = dentry->d_inode; + ino_t ino; + component_t dname; /* object name */ + struct inode *iplist[2]; + tblock_t *tblk; + s64 new_size = 0; + int commit_flag; + + jFYI(1, ("jfs_unlink: dip:0x%p name:%s\n", dip, dentry->d_name.name)); + + if ((rc = get_UCSname(&dname, dentry, JFS_SBI(dip->i_sb)->nls_tab))) + goto out; + + IWRITE_LOCK_LIST(2, ip, dip); + + tid = txBegin(dip->i_sb, 0); + + iplist[0] = dip; + iplist[1] = ip; + + /* + * delete the entry of target file from parent directory + */ + ino = ip->i_ino; + if ((rc = dtDelete(tid, dip, &dname, &ino, JFS_REMOVE))) { + jERROR(1, ("jfs_unlink: dtDelete returned %d\n", rc)); + if (rc == EIO) + txAbort(tid, 1); /* Marks FS Dirty */ + txEnd(tid); + IWRITE_UNLOCK(ip); + IWRITE_UNLOCK(dip); + goto out1; + } + + ASSERT(ip->i_nlink); + + ip->i_ctime = dip->i_ctime = dip->i_mtime = CURRENT_TIME; + dip->i_version = ++event; + mark_inode_dirty(dip); + + /* update target's inode */ + ip->i_nlink--; + mark_inode_dirty(ip); + + /* + * commit zero link count object + */ + if (ip->i_nlink == 0) { + assert(!test_cflag(COMMIT_Nolink, ip)); + /* free block resources */ + if ((new_size = commitZeroLink(tid, ip)) < 0) { + txAbort(tid, 1); /* Marks FS Dirty */ + txEnd(tid); + IWRITE_UNLOCK(ip); + IWRITE_UNLOCK(dip); + rc = -new_size; /* We return -rc */ + goto out1; + } + tblk = tid_to_tblock(tid); + tblk->xflag |= COMMIT_DELETE; + tblk->ip = ip; + } + + /* + * Incomplete truncate of file data can + * result in timing problems unless we synchronously commit the + * transaction. + */ + if (new_size) + commit_flag = COMMIT_SYNC; + else + commit_flag = 0; + + /* + * If xtTruncate was incomplete, commit synchronously to avoid + * timing complications + */ + rc = txCommit(tid, 2, &iplist[0], commit_flag); + + txEnd(tid); + + while (new_size && (rc == 0)) { + tid = txBegin(dip->i_sb, 0); + new_size = xtTruncate_pmap(tid, ip, new_size); + if (new_size < 0) { + txAbort(tid, 1); /* Marks FS Dirty */ + rc = -new_size; /* We return -rc */ + } else + rc = txCommit(tid, 2, &iplist[0], COMMIT_SYNC); + txEnd(tid); + } + + if (!test_cflag(COMMIT_Holdlock, ip)) + IWRITE_UNLOCK(ip); + + /* + * Truncating the directory index table is not guaranteed. It + * may need to be done iteratively + */ + if (test_cflag(COMMIT_Stale, dip)) { + if (dip->i_size > 1) + jfs_truncate_nolock(dip, 0); + + clear_cflag(COMMIT_Stale, dip); + } + + IWRITE_UNLOCK(dip); + + d_delete(dentry); + + out1: + free_UCSname(&dname); + out: + jFYI(1, ("jfs_unlink: rc:%d\n", -rc)); + return -rc; +} + +/* + * NAME: commitZeroLink() + * + * FUNCTION: for non-directory, called by jfs_remove(), + * truncate a regular file, directory or symbolic + * link to zero length. return 0 if type is not + * one of these. + * + * if the file is currently associated with a VM segment + * only permanent disk and inode map resources are freed, + * and neither the inode nor indirect blocks are modified + * so that the resources can be later freed in the work + * map by ctrunc1. + * if there is no VM segment on entry, the resources are + * freed in both work and permanent map. + * (? for temporary file - memory object is cached even + * after no reference: + * reference count > 0 - ) + * + * PARAMETERS: cd - pointer to commit data structure. + * current inode is the one to truncate. + * + * RETURN : Errors from subroutines + */ +s64 commitZeroLink(tid_t tid, struct inode *ip) +{ + int filetype, committype; + tblock_t *tblk; + + jFYI(1, ("commitZeroLink: tid = %d, ip = 0x%p\n", tid, ip)); + + filetype = ip->i_mode & S_IFMT; + switch (filetype) { + case S_IFREG: + break; + case S_IFLNK: + /* fast symbolic link */ + if (ip->i_size <= 256) { + ip->i_size = 0; + return 0; + } + break; + default: + assert(filetype != S_IFDIR); + return 0; + } + +#ifdef _STILL_TO_PORT + /* + * free from block allocation map: + * + * if there is no cache control element associated with + * the file, free resources in both persistent and work map; + * otherwise just persistent map. + */ + if (ip->i_cacheid) { + committype = COMMIT_PMAP; + + /* mark for iClose() to free from working map */ + set_cflag(COMMIT_Freewmap, ip); + } else + committype = COMMIT_PWMAP; +#else /* _STILL_TO_PORT */ + + set_cflag(COMMIT_Freewmap, ip); + committype = COMMIT_PMAP; +#endif /* _STILL_TO_PORT */ + + /* mark transaction of block map update type */ + tblk = tid_to_tblock(tid); + tblk->xflag |= committype; + + /* + * free EA + */ + if (JFS_IP(ip)->ea.flag & DXD_EXTENT) { +#ifdef _STILL_TO_PORT + /* free EA pages from cache */ + if (committype == COMMIT_PWMAP) + bmExtentInvalidate(ip, addressDXD(&ip->i_ea), + lengthDXD(&ip->i_ea)); +#endif /* _STILL_TO_PORT */ + + /* acquire maplock on EA to be freed from block map */ + txEA(tid, ip, &JFS_IP(ip)->ea, NULL); + + if (committype == COMMIT_PWMAP) + JFS_IP(ip)->ea.flag = 0; + } + + /* + * free ACL + */ + if (JFS_IP(ip)->acl.flag & DXD_EXTENT) { +#ifdef _STILL_TO_PORT + /* free ACL pages from cache */ + if (committype == COMMIT_PWMAP) + bmExtentInvalidate(ip, addressDXD(&ip->i_acl), + lengthDXD(&ip->i_acl)); +#endif /* _STILL_TO_PORT */ + + /* acquire maplock on EA to be freed from block map */ + txEA(tid, ip, &JFS_IP(ip)->acl, NULL); + + if (committype == COMMIT_PWMAP) + JFS_IP(ip)->acl.flag = 0; + } + + /* + * free xtree/data (truncate to zero length): + * free xtree/data pages from cache if COMMIT_PWMAP, + * free xtree/data blocks from persistent block map, and + * free xtree/data blocks from working block map if COMMIT_PWMAP; + */ + if (ip->i_size) + return xtTruncate_pmap(tid, ip, 0); + + return 0; +} + + +/* + * NAME: freeZeroLink() + * + * FUNCTION: for non-directory, called by iClose(), + * free resources of a file from cache and WORKING map + * for a file previously committed with zero link count + * while associated with a pager object, + * + * PARAMETER: ip - pointer to inode of file. + * + * RETURN: 0 -ok + */ +int freeZeroLink(struct inode *ip) +{ + int rc = 0; + int type; + + jFYI(1, ("freeZeroLink: ip = 0x%p\n", ip)); + + /* return if not reg or symbolic link or if size is + * already ok. + */ + type = ip->i_mode & S_IFMT; + + switch (type) { + case S_IFREG: + break; + case S_IFLNK: + /* if its contained in inode nothing to do */ + if (ip->i_size <= 256) + return 0; + break; + default: + return 0; + } + + /* + * free EA + */ + if (JFS_IP(ip)->ea.flag & DXD_EXTENT) { + s64 xaddr; + int xlen; + maplock_t maplock; /* maplock for COMMIT_WMAP */ + pxdlock_t *pxdlock; /* maplock for COMMIT_WMAP */ + + /* free EA pages from cache */ + xaddr = addressDXD(&JFS_IP(ip)->ea); + xlen = lengthDXD(&JFS_IP(ip)->ea); +#ifdef _STILL_TO_PORT + bmExtentInvalidate(ip, xaddr, xlen); +#endif + + /* free EA extent from working block map */ + maplock.index = 1; + pxdlock = (pxdlock_t *) & maplock; + pxdlock->flag = mlckFREEPXD; + PXDaddress(&pxdlock->pxd, xaddr); + PXDlength(&pxdlock->pxd, xlen); + txFreeMap(ip, pxdlock, 0, COMMIT_WMAP); + } + + /* + * free ACL + */ + if (JFS_IP(ip)->acl.flag & DXD_EXTENT) { + s64 xaddr; + int xlen; + maplock_t maplock; /* maplock for COMMIT_WMAP */ + pxdlock_t *pxdlock; /* maplock for COMMIT_WMAP */ + + /* free ACL pages from cache */ + xaddr = addressDXD(&JFS_IP(ip)->acl); + xlen = lengthDXD(&JFS_IP(ip)->acl); +#ifdef _STILL_TO_PORT + bmExtentInvalidate(ip, xaddr, xlen); +#endif + + /* free ACL extent from working block map */ + maplock.index = 1; + pxdlock = (pxdlock_t *) & maplock; + pxdlock->flag = mlckFREEPXD; + PXDaddress(&pxdlock->pxd, xaddr); + PXDlength(&pxdlock->pxd, xlen); + txFreeMap(ip, pxdlock, 0, COMMIT_WMAP); + } + + /* + * free xtree/data (truncate to zero length): + * free xtree/data pages from cache, and + * free xtree/data blocks from working block map; + */ + if (ip->i_size) + rc = xtTruncate(0, ip, 0, COMMIT_WMAP); + + return rc; +} + +/* + * NAME: jfs_link(vp, dvp, name, crp) + * + * FUNCTION: create a link to by the name = + * in the parent directory + * + * PARAMETER: vp - target object + * dvp - parent directory of new link + * name - name of new link to target object + * crp - credential + * + * RETURN: Errors from subroutines + * + * note: + * JFS does NOT support link() on directories (to prevent circular + * path in the directory hierarchy); + * EPERM: the target object is a directory, and either the caller + * does not have appropriate privileges or the implementation prohibits + * using link() on directories [XPG4.2]. + * + * JFS does NOT support links between file systems: + * EXDEV: target object and new link are on different file systems and + * implementation does not support links between file systems [XPG4.2]. + */ +int jfs_link(struct dentry *old_dentry, + struct inode *dir, struct dentry *dentry) +{ + int rc; + tid_t tid; + struct inode *ip = old_dentry->d_inode; + ino_t ino; + component_t dname; + btstack_t btstack; + struct inode *iplist[2]; + + jFYI(1, + ("jfs_link: %s %s\n", old_dentry->d_name.name, + dentry->d_name.name)); + + IWRITE_LOCK_LIST(2, dir, ip); + + tid = txBegin(ip->i_sb, 0); + + if (ip->i_nlink == JFS_LINK_MAX) { + rc = EMLINK; + goto out; + } + + /* + * scan parent directory for entry/freespace + */ + if ((rc = get_UCSname(&dname, dentry, JFS_SBI(ip->i_sb)->nls_tab))) + goto out; + + if ((rc = dtSearch(dir, &dname, &ino, &btstack, JFS_CREATE))) + goto out; + + /* + * create entry for new link in parent directory + */ + ino = ip->i_ino; + if ((rc = dtInsert(tid, dir, &dname, &ino, &btstack))) + goto out; + + dir->i_version = ++event; + + /* update object inode */ + ip->i_nlink++; /* for new link */ + ip->i_ctime = CURRENT_TIME; + mark_inode_dirty(dir); + atomic_inc(&ip->i_count); + d_instantiate(dentry, ip); + + iplist[0] = ip; + iplist[1] = dir; + rc = txCommit(tid, 2, &iplist[0], 0); + + out: + IWRITE_UNLOCK(dir); + IWRITE_UNLOCK(ip); + + txEnd(tid); + + jFYI(1, ("jfs_link: rc:%d\n", rc)); + return -rc; +} + +/* + * NAME: jfs_symlink(dip, dentry, name) + * + * FUNCTION: creates a symbolic link to by name + * in directory + * + * PARAMETER: dip - parent directory vnode + * dentry - dentry of symbolic link + * name - the path name of the existing object + * that will be the source of the link + * + * RETURN: errors from subroutines + * + * note: + * ENAMETOOLONG: pathname resolution of a symbolic link produced + * an intermediate result whose length exceeds PATH_MAX [XPG4.2] +*/ + +int jfs_symlink(struct inode *dip, struct dentry *dentry, const char *name) +{ + int rc; + tid_t tid; + ino_t ino = 0; + component_t dname; + int ssize; /* source pathname size */ + btstack_t btstack; + struct inode *ip = dentry->d_inode; + unchar *i_fastsymlink; + s64 xlen = 0; + int bmask = 0, xsize; + s64 xaddr; + metapage_t *mp; + struct super_block *sb; + tblock_t *tblk; + + struct inode *iplist[2]; + + jFYI(1, ("jfs_symlink: dip:0x%p name:%s\n", dip, name)); + + IWRITE_LOCK(dip); + + ssize = strlen(name) + 1; + + tid = txBegin(dip->i_sb, 0); + + /* + * search parent directory for entry/freespace + * (dtSearch() returns parent directory page pinned) + */ + + if ((rc = get_UCSname(&dname, dentry, JFS_SBI(dip->i_sb)->nls_tab))) + goto out1; + + if ((rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE))) + goto out2; + + + + /* + * allocate on-disk/in-memory inode for symbolic link: + * (iAlloc() returns new, locked inode) + */ + + ip = ialloc(dip, S_IFLNK | 0777); + if (ip == NULL) { + BT_PUTSEARCH(&btstack); + rc = ENOSPC; + goto out2; + } + + tblk = tid_to_tblock(tid); + tblk->xflag |= COMMIT_CREATE; + tblk->ip = ip; + + /* + * create entry for symbolic link in parent directory + */ + + ino = ip->i_ino; + + + + if ((rc = dtInsert(tid, dip, &dname, &ino, &btstack))) { + jERROR(1, ("jfs_symlink: dtInsert returned %d\n", rc)); + /* discard ne inode */ + ip->i_nlink = 0; + iput(ip); + goto out2; + + } + + /* fix symlink access permission + * (dir_create() ANDs in the u.u_cmask, + * but symlinks really need to be 777 access) + */ + ip->i_mode |= 0777; + + /* + * write symbolic link target path name + */ + xtInitRoot(tid, ip); + + /* + * write source path name inline in on-disk inode (fast symbolic link) + */ + + if (ssize <= IDATASIZE) { + ip->i_op = &jfs_symlink_inode_operations; + + i_fastsymlink = JFS_IP(ip)->i_inline; + memcpy(i_fastsymlink, name, ssize); + ip->i_size = ssize - 1; + jFYI(1, + ("jfs_symlink: fast symlink added ssize:%d name:%s \n", + ssize, name)); + } + /* + * write source path name in a single extent + */ + else { + jFYI(1, ("jfs_symlink: allocate extent ip:0x%p\n", ip)); + + ip->i_op = &page_symlink_inode_operations; + ip->i_mapping->a_ops = &jfs_aops; + + /* + * even though the data of symlink object (source + * path name) is treated as non-journaled user data, + * it is read/written thru buffer cache for performance. + */ + sb = ip->i_sb; + bmask = JFS_SBI(sb)->bsize - 1; + xsize = (ssize + bmask) & ~bmask; + xaddr = 0; + xlen = xsize >> JFS_SBI(sb)->l2bsize; + if ((rc = xtInsert(tid, ip, 0, 0, xlen, &xaddr, 0)) == 0) { + ip->i_size = ssize - 1; + while (ssize) { + int copy_size = min(ssize, PSIZE); + + mp = get_metapage(ip, xaddr, PSIZE, 1); + + if (mp == NULL) { + dtDelete(tid, dip, &dname, &ino, + JFS_REMOVE); + ip->i_nlink = 0; + iput(ip); + rc = EIO; + goto out2; + } + memcpy(mp->data, name, copy_size); + flush_metapage(mp); +#if 0 + mark_buffer_uptodate(bp, 1); + mark_buffer_dirty(bp, 1); + if (IS_SYNC(dip)) { + ll_rw_block(WRITE, 1, &bp); + wait_on_buffer(bp); + } + brelse(bp); +#endif /* 0 */ + ssize -= copy_size; + xaddr += JFS_SBI(sb)->nbperpage; + } + ip->i_blocks = LBLK2PBLK(sb, xlen); + } else { + dtDelete(tid, dip, &dname, &ino, JFS_REMOVE); + ip->i_nlink = 0; + iput(ip); + rc = ENOSPC; + goto out2; + } + } + dip->i_version = ++event; + + insert_inode_hash(ip); + mark_inode_dirty(ip); + d_instantiate(dentry, ip); + + /* + * commit update of parent directory and link object + * + * if extent allocation failed (ENOSPC), + * the parent inode is committed regardless to avoid + * backing out parent directory update (by dtInsert()) + * and subsequent dtDelete() which is harmless wrt + * integrity concern. + * the symlink inode will be freed by iput() at exit + * as it has a zero link count (by dtDelete()) and + * no permanant resources. + */ + + iplist[0] = dip; + if (rc == 0) { + iplist[1] = ip; + rc = txCommit(tid, 2, &iplist[0], 0); + } else + rc = txCommit(tid, 1, &iplist[0], 0); + + out2: + + free_UCSname(&dname); + out1: + IWRITE_UNLOCK(dip); + + txEnd(tid); + + jFYI(1, ("jfs_symlink: rc:%d\n", -rc)); + return -rc; +} + + +/* + * NAME: jfs_rename + * + * FUNCTION: rename a file or directory + */ +int jfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + btstack_t btstack; + ino_t ino; + component_t new_dname; + struct inode *new_ip; + component_t old_dname; + struct inode *old_ip; + int rc; + tid_t tid; + tlock_t *tlck; + dtlock_t *dtlck; + lv_t *lv; + int ipcount; + struct inode *iplist[4]; + tblock_t *tblk; + s64 new_size = 0; + int commit_flag; + + + jFYI(1, + ("jfs_rename: %s %s\n", old_dentry->d_name.name, + new_dentry->d_name.name)); + + old_ip = old_dentry->d_inode; + new_ip = new_dentry->d_inode; + + if (old_dir == new_dir) { + if (new_ip) + IWRITE_LOCK_LIST(3, old_dir, old_ip, new_ip); + else + IWRITE_LOCK_LIST(2, old_dir, old_ip); + } else { + if (new_ip) + IWRITE_LOCK_LIST(4, old_dir, new_dir, old_ip, + new_ip); + else + IWRITE_LOCK_LIST(3, old_dir, new_dir, old_ip); + } + + if ((rc = get_UCSname(&old_dname, old_dentry, + JFS_SBI(old_dir->i_sb)->nls_tab))) + goto out1; + + if ((rc = get_UCSname(&new_dname, new_dentry, + JFS_SBI(old_dir->i_sb)->nls_tab))) + goto out2; + + /* + * Make sure source inode number is what we think it is + */ + rc = dtSearch(old_dir, &old_dname, &ino, &btstack, JFS_LOOKUP); + if (rc || (ino != old_ip->i_ino)) { + rc = ENOENT; + goto out3; + } + + /* + * Make sure dest inode number (if any) is what we think it is + */ + rc = dtSearch(new_dir, &new_dname, &ino, &btstack, JFS_LOOKUP); + if (rc == 0) { + if ((new_ip == 0) || (ino != new_ip->i_ino)) { + rc = ESTALE; + goto out3; + } + } else if (rc != ENOENT) + goto out3; + else if (new_ip) { + /* no entry exists, but one was expected */ + rc = ESTALE; + goto out3; + } + + if (S_ISDIR(old_ip->i_mode)) { + if (new_ip) { + if (!dtEmpty(new_ip)) { + rc = ENOTEMPTY; + goto out3; + } + } else if ((new_dir != old_dir) && + (new_dir->i_nlink == JFS_LINK_MAX)) { + rc = EMLINK; + goto out3; + } + } + + /* + * The real work starts here + */ + tid = txBegin(new_dir->i_sb, 0); + + if (new_ip) { + /* + * Change existing directory entry to new inode number + */ + ino = new_ip->i_ino; + rc = dtModify(tid, new_dir, &new_dname, &ino, + old_ip->i_ino, JFS_RENAME); + if (rc) + goto out4; + new_ip->i_nlink--; + if (S_ISDIR(new_ip->i_mode)) { + new_ip->i_nlink--; + assert(new_ip->i_nlink == 0); + tblk = tid_to_tblock(tid); + tblk->xflag |= COMMIT_DELETE; + tblk->ip = new_ip; + } else if (new_ip->i_nlink == 0) { + assert(!test_cflag(COMMIT_Nolink, new_ip)); + /* free block resources */ + if ((new_size = commitZeroLink(tid, new_ip)) < 0) { + txAbort(tid, 1); /* Marks FS Dirty */ + rc = -new_size; /* We return -rc */ + goto out4; + } + tblk = tid_to_tblock(tid); + tblk->xflag |= COMMIT_DELETE; + tblk->ip = new_ip; + } else { + new_ip->i_ctime = CURRENT_TIME; + mark_inode_dirty(new_ip); + } + } else { + /* + * Add new directory entry + */ + rc = dtSearch(new_dir, &new_dname, &ino, &btstack, + JFS_CREATE); + if (rc) { + jERROR(1, + ("jfs_rename didn't expect dtSearch to fail w/rc = %d\n", + rc)); + goto out4; + } + + ino = old_ip->i_ino; + rc = dtInsert(tid, new_dir, &new_dname, &ino, &btstack); + if (rc) { + jERROR(1, + ("jfs_rename: dtInsert failed w/rc = %d\n", + rc)); + goto out4; + } + if (S_ISDIR(old_ip->i_mode)) + new_dir->i_nlink++; + } + /* + * Remove old directory entry + */ + + ino = old_ip->i_ino; + rc = dtDelete(tid, old_dir, &old_dname, &ino, JFS_REMOVE); + if (rc) { + jERROR(1, + ("jfs_rename did not expect dtDelete to return rc = %d\n", + rc)); + txAbort(tid, 1); /* Marks Filesystem dirty */ + goto out4; + } + if (S_ISDIR(old_ip->i_mode)) { + old_dir->i_nlink--; + if (old_dir != new_dir) { + /* + * Change inode number of parent for moved directory + */ + + JFS_IP(old_ip)->i_dtroot.header.idotdot = + cpu_to_le32(new_dir->i_ino); + + /* Linelock header of dtree */ + tlck = txLock(tid, old_ip, + (metapage_t *) & JFS_IP(old_ip)->bxflag, + tlckDTREE | tlckBTROOT); + dtlck = (dtlock_t *) & tlck->lock; + ASSERT(dtlck->index == 0); + lv = (lv_t *) & dtlck->lv[0]; + lv->offset = 0; + lv->length = 1; + dtlck->index++; + } + } + + /* + * Update ctime on changed/moved inodes & mark dirty + */ + old_ip->i_ctime = CURRENT_TIME; + mark_inode_dirty(old_ip); + + new_dir->i_version = ++event; + new_dir->i_ctime = CURRENT_TIME; + mark_inode_dirty(new_dir); + + /* Build list of inodes modified by this transaction */ + ipcount = 0; + iplist[ipcount++] = old_ip; + if (new_ip) + iplist[ipcount++] = new_ip; + iplist[ipcount++] = old_dir; + + if (old_dir != new_dir) { + iplist[ipcount++] = new_dir; + old_dir->i_version = ++event; + old_dir->i_ctime = CURRENT_TIME; + mark_inode_dirty(old_dir); + } + + /* + * Incomplete truncate of file data can + * result in timing problems unless we synchronously commit the + * transaction. + */ + if (new_size) + commit_flag = COMMIT_SYNC; + else + commit_flag = 0; + + rc = txCommit(tid, ipcount, iplist, commit_flag); + + /* + * Don't unlock new_ip if COMMIT_HOLDLOCK is set + */ + if (new_ip && test_cflag(COMMIT_Holdlock, new_ip)) + new_ip = 0; + + out4: + txEnd(tid); + + while (new_size && (rc == 0)) { + tid = txBegin(new_ip->i_sb, 0); + new_size = xtTruncate_pmap(tid, new_ip, new_size); + if (new_size < 0) { + txAbort(tid, 1); + rc = -new_size; /* We return -rc */ + } else + rc = txCommit(tid, 1, &new_ip, COMMIT_SYNC); + txEnd(tid); + } + out3: + free_UCSname(&new_dname); + out2: + free_UCSname(&old_dname); + out1: + IWRITE_UNLOCK(old_ip); + if (old_dir != new_dir) + IWRITE_UNLOCK(new_dir); + if (new_ip) + IWRITE_UNLOCK(new_ip); + + /* + * Truncating the directory index table is not guaranteed. It + * may need to be done iteratively + */ + if (test_cflag(COMMIT_Stale, old_dir)) { + if (old_dir->i_size > 1) + jfs_truncate_nolock(old_dir, 0); + + clear_cflag(COMMIT_Stale, old_dir); + } + + IWRITE_UNLOCK(old_dir); + + jFYI(1, ("jfs_rename: returning %d\n", rc)); + return -rc; +} + + +/* + * NAME: jfs_mknod + * + * FUNCTION: Create a special file (device) + */ +int jfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) +{ + btstack_t btstack; + component_t dname; + ino_t ino; + struct inode *ip; + struct inode *iplist[2]; + int rc; + tid_t tid; + tblock_t *tblk; + + jFYI(1, ("jfs_mknod: %s\n", dentry->d_name.name)); + + if ((rc = get_UCSname(&dname, dentry, JFS_SBI(dir->i_sb)->nls_tab))) + goto out; + + IWRITE_LOCK(dir); + + ip = ialloc(dir, mode); + if (ip == NULL) { + rc = ENOSPC; + goto out1; + } + + tid = txBegin(dir->i_sb, 0); + + if ((rc = dtSearch(dir, &dname, &ino, &btstack, JFS_CREATE))) { + ip->i_nlink = 0; + iput(ip); + txEnd(tid); + goto out1; + } + + tblk = tid_to_tblock(tid); + tblk->xflag |= COMMIT_CREATE; + tblk->ip = ip; + + ino = ip->i_ino; + if ((rc = dtInsert(tid, dir, &dname, &ino, &btstack))) { + ip->i_nlink = 0; + iput(ip); + txEnd(tid); + goto out1; + } + + if (S_ISREG(ip->i_mode)) { + ip->i_op = &jfs_file_inode_operations; + ip->i_fop = &jfs_file_operations; + ip->i_mapping->a_ops = &jfs_aops; + } else + init_special_inode(ip, ip->i_mode, rdev); + + insert_inode_hash(ip); + mark_inode_dirty(ip); + d_instantiate(dentry, ip); + + dir->i_version = ++event; + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + + mark_inode_dirty(dir); + + iplist[0] = dir; + iplist[1] = ip; + rc = txCommit(tid, 2, iplist, 0); + txEnd(tid); + + out1: + IWRITE_UNLOCK(dir); + free_UCSname(&dname); + + out: + jFYI(1, ("jfs_mknod: returning %d\n", rc)); + return -rc; +} + +static struct dentry *jfs_lookup(struct inode *dip, struct dentry *dentry) +{ + btstack_t btstack; + ino_t inum; + struct inode *ip; + component_t key; + const char *name = dentry->d_name.name; + int len = dentry->d_name.len; + int rc; + + jFYI(1, ("jfs_lookup: name = %s\n", name)); + + + if ((name[0] == '.') && (len == 1)) + inum = dip->i_ino; + else if (strcmp(name, "..") == 0) + inum = PARENT(dip); + else { + if ((rc = + get_UCSname(&key, dentry, JFS_SBI(dip->i_sb)->nls_tab))) + return ERR_PTR(-rc); + IREAD_LOCK(dip); + rc = dtSearch(dip, &key, &inum, &btstack, JFS_LOOKUP); + IREAD_UNLOCK(dip); + free_UCSname(&key); + if (rc == ENOENT) { + d_add(dentry, NULL); + return ERR_PTR(0); + } else if (rc) { + jERROR(1, + ("jfs_lookup: dtSearch returned %d\n", rc)); + return ERR_PTR(-rc); + } + } + + ip = iget(dip->i_sb, inum); + if (ip == NULL) { + jERROR(1, + ("jfs_lookup: iget failed on inum %d\n", + (uint) inum)); + return ERR_PTR(-EACCES); + } + + d_add(dentry, ip); + + return ERR_PTR(0); +} + +struct inode_operations jfs_dir_inode_operations = { + create: jfs_create, + lookup: jfs_lookup, + link: jfs_link, + unlink: jfs_unlink, + symlink: jfs_symlink, + mkdir: jfs_mkdir, + rmdir: jfs_rmdir, + mknod: jfs_mknod, + rename: jfs_rename, +}; + +struct file_operations jfs_dir_operations = { + read: generic_read_dir, + readdir: jfs_readdir, + fsync: jfs_fsync, +}; diff -Nru a/fs/jfs/super.c b/fs/jfs/super.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/super.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,499 @@ +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 "jfs_incore.h" +#include "jfs_filsys.h" +#include "jfs_metapage.h" +#include "jfs_superblock.h" +#include "jfs_dmap.h" +#include "jfs_imap.h" +#include "jfs_debug.h" + +MODULE_DESCRIPTION("The Journaled Filesystem (JFS)"); +MODULE_AUTHOR("Steve Best/Dave Kleikamp/Barry Arndt, IBM"); +MODULE_LICENSE("GPL"); + +static kmem_cache_t * jfs_inode_cachep; + +static int in_shutdown; +static pid_t jfsIOthread; +static pid_t jfsCommitThread; +static pid_t jfsSyncThread; +struct task_struct *jfsIOtask; +struct task_struct *jfsCommitTask; +struct task_struct *jfsSyncTask; +DECLARE_COMPLETION(jfsIOwait); + +#ifdef CONFIG_JFS_DEBUG +int jfsloglevel = 1; +MODULE_PARM(jfsloglevel, "i"); +MODULE_PARM_DESC(jfsloglevel, "Specify JFS loglevel (0, 1 or 2)"); +#endif + +/* + * External declarations + */ +extern int jfs_mount(struct super_block *); +extern int jfs_mount_rw(struct super_block *, int); +extern int jfs_umount(struct super_block *); +extern int jfs_umount_rw(struct super_block *); + +extern int jfsIOWait(void *); +extern int jfs_lazycommit(void *); +extern int jfs_sync(void *); +extern void jfs_put_inode(struct inode *inode); +extern void jfs_read_inode(struct inode *inode); +extern void jfs_dirty_inode(struct inode *inode); +extern void jfs_delete_inode(struct inode *inode); +extern void jfs_write_inode(struct inode *inode, int wait); + +#if defined(CONFIG_JFS_DEBUG) && defined(CONFIG_PROC_FS) +extern void jfs_proc_init(void); +extern void jfs_proc_clean(void); +#endif + +int jfs_thread_stopped(void) +{ + unsigned long signr; + siginfo_t info; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (signr == SIGKILL && in_shutdown) + return 1; + return 0; +} + +static struct inode *jfs_alloc_inode(struct super_block *sb) +{ + struct jfs_inode_info *jfs_inode; + + jfs_inode = kmem_cache_alloc(jfs_inode_cachep, GFP_NOFS); + if (!jfs_inode) + return NULL; + return &jfs_inode->vfs_inode; +} + +static void jfs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(jfs_inode_cachep, JFS_IP(inode)); +} + +static int jfs_statfs(struct super_block *sb, struct statfs *buf) +{ + struct jfs_sb_info *sbi = JFS_SBI(sb); + s64 maxinodes; + imap_t *imap = JFS_IP(sbi->ipimap)->i_imap; + + jFYI(1, ("In jfs_statfs\n")); + buf->f_type = JFS_SUPER_MAGIC; + buf->f_bsize = sbi->bsize; + buf->f_blocks = sbi->bmap->db_mapsize; + buf->f_bfree = sbi->bmap->db_nfree; + buf->f_bavail = sbi->bmap->db_nfree; + /* + * If we really return the number of allocated & free inodes, some + * applications will fail because they won't see enough free inodes. + * We'll try to calculate some guess as to how may inodes we can + * really allocate + * + * buf->f_files = atomic_read(&imap->im_numinos); + * buf->f_ffree = atomic_read(&imap->im_numfree); + */ + maxinodes = min((s64) atomic_read(&imap->im_numinos) + + ((sbi->bmap->db_nfree >> imap->im_l2nbperiext) + << L2INOSPEREXT), (s64)0xffffffffLL); + buf->f_files = maxinodes; + buf->f_ffree = maxinodes - (atomic_read(&imap->im_numinos) - + atomic_read(&imap->im_numfree)); + + buf->f_namelen = JFS_NAME_MAX; + return 0; +} + +static void jfs_put_super(struct super_block *sb) +{ + struct jfs_sb_info *sbi = JFS_SBI(sb); + int rc; + + jFYI(1, ("In jfs_put_super\n")); + rc = jfs_umount(sb); + if (rc) { + jERROR(1, ("jfs_umount failed with return code %d\n", rc)); + } + unload_nls(sbi->nls_tab); + sbi->nls_tab = NULL; + + /* + * We need to clean out the direct_inode pages since this inode + * is not in the inode hash. + */ + fsync_inode_data_buffers(sbi->direct_inode); + truncate_inode_pages(sbi->direct_mapping, 0); + iput(sbi->direct_inode); + sbi->direct_inode = NULL; + sbi->direct_mapping = NULL; + + JFS_SBI(sb) = 0; + kfree(sbi); +} + +static int parse_options (char * options, struct jfs_sb_info *sbi) +{ + void *nls_map = NULL; + char * this_char; + char * value; + + if (!options) + return 1; + for (this_char = strtok (options, ","); + this_char != NULL; + this_char = strtok (NULL, ",")) { + if ((value = strchr (this_char, '=')) != NULL) + *value++ = 0; + if (!strcmp (this_char, "iocharset")) { + if (!value || !*value) + goto needs_arg; + if (nls_map) /* specified iocharset twice! */ + unload_nls(nls_map); + nls_map = load_nls(value); + if (!nls_map) { + printk(KERN_ERR "JFS: charset not found\n"); + goto cleanup; + } + /* Silently ignore the quota options */ + } else if (!strcmp (this_char, "grpquota") + || !strcmp (this_char, "noquota") + || !strcmp (this_char, "quota") + || !strcmp (this_char, "usrquota")) + /* Don't do anything ;-) */ ; + else { + printk ("jfs: Unrecognized mount option %s\n", this_char); + goto cleanup; + } + } + if (nls_map) { + /* Discard old (if remount) */ + if (sbi->nls_tab) + unload_nls(sbi->nls_tab); + sbi->nls_tab = nls_map; + } + return 1; +needs_arg: + printk(KERN_ERR "JFS: %s needs an argument\n", this_char); +cleanup: + if (nls_map) + unload_nls(nls_map); + return 0; +} + +int jfs_remount(struct super_block *sb, int *flags, char *data) +{ + struct jfs_sb_info *sbi = JFS_SBI(sb); + + if (!parse_options(data, sbi)) { + return -EINVAL; + } + + if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) { + /* + * Invalidate any previously read metadata. fsck may + * have changed the on-disk data since we mounted r/o + */ + truncate_inode_pages(sbi->direct_mapping, 0); + + return jfs_mount_rw(sb, 1); + } else if ((!(sb->s_flags & MS_RDONLY)) && (*flags & MS_RDONLY)) + return jfs_umount_rw(sb); + + return 0; +} + +static struct super_operations jfs_sops = { + alloc_inode: jfs_alloc_inode, + destroy_inode: jfs_destroy_inode, + read_inode: jfs_read_inode, + dirty_inode: jfs_dirty_inode, + write_inode: jfs_write_inode, + put_inode: jfs_put_inode, + delete_inode: jfs_delete_inode, + put_super: jfs_put_super, + statfs: jfs_statfs, + remount_fs: jfs_remount, +}; + +static int jfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct jfs_sb_info *sbi; + struct inode *inode; + int rc; + + jFYI(1, ("In jfs_read_super: s_flags=0x%lx\n", sb->s_flags)); + + sbi = kmalloc(sizeof(struct jfs_sb_info), GFP_KERNEL); + JFS_SBI(sb) = sbi; + if (!sbi) + return -ENOSPC; + memset(sbi, 0, sizeof(struct jfs_sb_info)); + + if (!parse_options((char *)data, sbi)) { + kfree(sbi); + return -EINVAL; + } + + /* + * Initialize blocksize to 4K. + */ + sb->s_blocksize = PSIZE; + sb->s_blocksize_bits = L2PSIZE; + set_blocksize(sb->s_dev, PSIZE); + + sb->s_op = &jfs_sops; + /* + * Initialize direct-mapping inode/address-space + */ + inode = new_inode(sb); + if (inode == NULL) + goto out_kfree; + inode->i_ino = 0; + inode->i_nlink = 1; + inode->i_size = 0x0000010000000000LL; + inode->i_mapping->a_ops = &direct_aops; + inode->i_mapping->gfp_mask = GFP_NOFS; + + sbi->direct_inode = inode; + sbi->direct_mapping = inode->i_mapping; + + rc = jfs_mount(sb); + if (rc) { + if (!silent) { + jERROR(1, + ("jfs_mount failed w/return code = %d\n", + rc)); + } + goto out_mount_failed; + } + if (sb->s_flags & MS_RDONLY) + sbi->log = 0; + else { + rc = jfs_mount_rw(sb, 0); + if (rc) { + if (!silent) { + jERROR(1, + ("jfs_mount_rw failed w/return code = %d\n", + rc)); + } + goto out_no_rw; + } + } + + sb->s_magic = JFS_SUPER_MAGIC; + + inode = iget(sb, ROOT_I); + if (!inode || is_bad_inode(inode)) + goto out_no_root; + sb->s_root = d_alloc_root(inode); + if (!sb->s_root) + goto out_no_root; + + if (!sbi->nls_tab) + sbi->nls_tab = load_nls_default(); + + sb->s_maxbytes = ((u64) sb->s_blocksize) << 40; +#if BITS_PER_LONG == 32 + sb->s_maxbytes = min((u64)PAGE_CACHE_SIZE << 32, sb->s_maxbytes); +#endif + + return 0; + +out_no_root: + jEVENT(1, ("jfs_read_super: get root inode failed\n")); + if (inode) + iput(inode); + +out_no_rw: + rc = jfs_umount(sb); + if (rc) { + jERROR(1, ("jfs_umount failed with return code %d\n", rc)); + } +out_mount_failed: + fsync_inode_data_buffers(sbi->direct_inode); + truncate_inode_pages(sbi->direct_mapping, 0); + make_bad_inode(sbi->direct_inode); + iput(sbi->direct_inode); + sbi->direct_inode = NULL; + sbi->direct_mapping = NULL; +out_kfree: + if (sbi->nls_tab) + unload_nls(sbi->nls_tab); + kfree(sbi); + return -EINVAL; +} + +static struct super_block *jfs_get_sb(struct file_system_type *fs_type, + int flags, char *dev_name, void *data) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, jfs_fill_super); +} + +static struct file_system_type jfs_fs_type = { + owner: THIS_MODULE, + name: "jfs", + get_sb: jfs_get_sb, + fs_flags: FS_REQUIRES_DEV, +}; + +extern int metapage_init(void); +extern int txInit(void); +extern void txExit(void); +extern void metapage_exit(void); + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct jfs_inode_info *jfs_ip = (struct jfs_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) { + INIT_LIST_HEAD(&jfs_ip->anon_inode_list); + INIT_LIST_HEAD(&jfs_ip->mp_list); + RDWRLOCK_INIT(&jfs_ip->rdwrlock); + inode_init_once(&jfs_ip->vfs_inode); + } +} + +static int __init init_jfs_fs(void) +{ + int rc; + + jfs_inode_cachep = + kmem_cache_create("jfs_ip", + sizeof(struct jfs_inode_info), + 0, 0, init_once, NULL); + if (jfs_inode_cachep == NULL) + return -ENOMEM; + + /* + * Metapage initialization + */ + rc = metapage_init(); + if (rc) { + jERROR(1, ("metapage_init failed w/rc = %d\n", rc)); + goto free_slab; + } + + /* + * Transaction Manager initialization + */ + rc = txInit(); + if (rc) { + jERROR(1, ("txInit failed w/rc = %d\n", rc)); + goto free_metapage; + } + + /* + * I/O completion thread (endio) + */ + jfsIOthread = kernel_thread(jfsIOWait, 0, + CLONE_FS | CLONE_FILES | + CLONE_SIGHAND); + if (jfsIOthread < 0) { + jERROR(1, + ("init_jfs_fs: fork failed w/rc = %d\n", + jfsIOthread)); + goto end_txmngr; + } + wait_for_completion(&jfsIOwait); /* Wait until IO thread starts */ + + jfsCommitThread = kernel_thread(jfs_lazycommit, 0, + CLONE_FS | CLONE_FILES | + CLONE_SIGHAND); + if (jfsCommitThread < 0) { + jERROR(1, + ("init_jfs_fs: fork failed w/rc = %d\n", + jfsCommitThread)); + goto kill_iotask; + } + wait_for_completion(&jfsIOwait); /* Wait until IO thread starts */ + + jfsSyncThread = kernel_thread(jfs_sync, 0, + CLONE_FS | CLONE_FILES | + CLONE_SIGHAND); + if (jfsSyncThread < 0) { + jERROR(1, + ("init_jfs_fs: fork failed w/rc = %d\n", + jfsSyncThread)); + goto kill_committask; + } + wait_for_completion(&jfsIOwait); /* Wait until IO thread starts */ + +#if defined(CONFIG_JFS_DEBUG) && defined(CONFIG_PROC_FS) + jfs_proc_init(); +#endif + + return register_filesystem(&jfs_fs_type); + + +kill_committask: + send_sig(SIGKILL, jfsCommitTask, 1); + wait_for_completion(&jfsIOwait); /* Wait until Commit thread exits */ +kill_iotask: + send_sig(SIGKILL, jfsIOtask, 1); + wait_for_completion(&jfsIOwait); /* Wait until IO thread exits */ +end_txmngr: + txExit(); +free_metapage: + metapage_exit(); +free_slab: + kmem_cache_destroy(jfs_inode_cachep); + return -rc; +} + +static void __exit exit_jfs_fs(void) +{ + jFYI(1, ("exit_jfs_fs called\n")); + + in_shutdown = 1; + txExit(); + metapage_exit(); + send_sig(SIGKILL, jfsIOtask, 1); + wait_for_completion(&jfsIOwait); /* Wait until IO thread exits */ + send_sig(SIGKILL, jfsCommitTask, 1); + wait_for_completion(&jfsIOwait); /* Wait until Commit thread exits */ + send_sig(SIGKILL, jfsSyncTask, 1); + wait_for_completion(&jfsIOwait); /* Wait until Sync thread exits */ +#if defined(CONFIG_JFS_DEBUG) && defined(CONFIG_PROC_FS) + jfs_proc_clean(); +#endif + unregister_filesystem(&jfs_fs_type); + kmem_cache_destroy(jfs_inode_cachep); +} + + +EXPORT_NO_SYMBOLS; + +module_init(init_jfs_fs) +module_exit(exit_jfs_fs) diff -Nru a/fs/jfs/symlink.c b/fs/jfs/symlink.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/jfs/symlink.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,47 @@ + +/* + * + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 + * + * JFS fast symlink handling code + */ + +#include +#include "jfs_incore.h" + +static int jfs_readlink(struct dentry *, char *buffer, int buflen); +static int jfs_follow_link(struct dentry *dentry, struct nameidata *nd); + +/* + * symlinks can't do much... + */ +struct inode_operations jfs_symlink_inode_operations = { + readlink: jfs_readlink, + follow_link: jfs_follow_link, +}; + +static int jfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char *s = JFS_IP(dentry->d_inode)->i_inline; + return vfs_follow_link(nd, s); +} + +static int jfs_readlink(struct dentry *dentry, char *buffer, int buflen) +{ + char *s = JFS_IP(dentry->d_inode)->i_inline; + return vfs_readlink(dentry, buffer, buflen, s); +} diff -Nru a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c --- a/fs/lockd/svc4proc.c Thu Mar 7 18:17:42 2002 +++ b/fs/lockd/svc4proc.c Thu Mar 7 18:17:42 2002 @@ -524,7 +524,7 @@ struct nlm_void { int dummy; }; -#define PROC(name, xargt, xrest, argt, rest) \ +#define PROC(name, xargt, xrest, argt, rest, respsize) \ { (svc_procfunc) nlm4svc_proc_##name, \ (kxdrproc_t) nlm4svc_decode_##xargt, \ (kxdrproc_t) nlm4svc_encode_##xrest, \ @@ -532,33 +532,34 @@ sizeof(struct nlm_##argt), \ sizeof(struct nlm_##rest), \ 0, \ - 0 \ + 0, \ + respsize, \ } struct svc_procedure nlmsvc_procedures4[] = { - PROC(null, void, void, void, void), - PROC(test, testargs, testres, args, res), - PROC(lock, lockargs, res, args, res), - PROC(cancel, cancargs, res, args, res), - PROC(unlock, unlockargs, res, args, res), - PROC(granted, testargs, res, args, res), - PROC(test_msg, testargs, norep, args, void), - PROC(lock_msg, lockargs, norep, args, void), - PROC(cancel_msg, cancargs, norep, args, void), - PROC(unlock_msg, unlockargs, norep, args, void), - PROC(granted_msg, testargs, norep, args, void), - PROC(test_res, testres, norep, res, void), - PROC(lock_res, lockres, norep, res, void), - PROC(cancel_res, cancelres, norep, res, void), - PROC(unlock_res, unlockres, norep, res, void), - PROC(granted_res, grantedres, norep, res, void), + PROC(null, void, void, void, void, 0), + PROC(test, testargs, testres, args, res, 0), + PROC(lock, lockargs, res, args, res, 0), + PROC(cancel, cancargs, res, args, res, 0), + PROC(unlock, unlockargs, res, args, res, 0), + PROC(granted, testargs, res, args, res, 0), + PROC(test_msg, testargs, norep, args, void, 0), + PROC(lock_msg, lockargs, norep, args, void, 0), + PROC(cancel_msg, cancargs, norep, args, void, 0), + PROC(unlock_msg, unlockargs, norep, args, void, 0), + PROC(granted_msg, testargs, norep, args, void, 0), + PROC(test_res, testres, norep, res, void, 0), + PROC(lock_res, lockres, norep, res, void, 0), + PROC(cancel_res, cancelres, norep, res, void, 0), + PROC(unlock_res, unlockres, norep, res, void, 0), + PROC(granted_res, grantedres, norep, res, void, 0), /* statd callback */ - PROC(sm_notify, reboot, void, reboot, void), - PROC(none, void, void, void, void), - PROC(none, void, void, void, void), - PROC(none, void, void, void, void), - PROC(share, shareargs, shareres, args, res), - PROC(unshare, shareargs, shareres, args, res), - PROC(nm_lock, lockargs, res, args, res), - PROC(free_all, notify, void, args, void), + PROC(sm_notify, reboot, void, reboot, void, 0), + PROC(none, void, void, void, void, 0), + PROC(none, void, void, void, void, 0), + PROC(none, void, void, void, void, 0), + PROC(share, shareargs, shareres, args, res, 0), + PROC(unshare, shareargs, shareres, args, res, 0), + PROC(nm_lock, lockargs, res, args, res, 0), + PROC(free_all, notify, void, args, void, 0), }; diff -Nru a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c --- a/fs/lockd/svcproc.c Thu Mar 7 18:17:45 2002 +++ b/fs/lockd/svcproc.c Thu Mar 7 18:17:45 2002 @@ -552,7 +552,7 @@ struct nlm_void { int dummy; }; -#define PROC(name, xargt, xrest, argt, rest) \ +#define PROC(name, xargt, xrest, argt, rest, respsize) \ { (svc_procfunc) nlmsvc_proc_##name, \ (kxdrproc_t) nlmsvc_decode_##xargt, \ (kxdrproc_t) nlmsvc_encode_##xrest, \ @@ -560,33 +560,34 @@ sizeof(struct nlm_##argt), \ sizeof(struct nlm_##rest), \ 0, \ - 0 \ + 0, \ + respsize, \ } struct svc_procedure nlmsvc_procedures[] = { - PROC(null, void, void, void, void), - PROC(test, testargs, testres, args, res), - PROC(lock, lockargs, res, args, res), - PROC(cancel, cancargs, res, args, res), - PROC(unlock, unlockargs, res, args, res), - PROC(granted, testargs, res, args, res), - PROC(test_msg, testargs, norep, args, void), - PROC(lock_msg, lockargs, norep, args, void), - PROC(cancel_msg, cancargs, norep, args, void), - PROC(unlock_msg, unlockargs, norep, args, void), - PROC(granted_msg, testargs, norep, args, void), - PROC(test_res, testres, norep, res, void), - PROC(lock_res, lockres, norep, res, void), - PROC(cancel_res, cancelres, norep, res, void), - PROC(unlock_res, unlockres, norep, res, void), - PROC(granted_res, grantedres, norep, res, void), + PROC(null, void, void, void, void, 0), + PROC(test, testargs, testres, args, res, 0), + PROC(lock, lockargs, res, args, res, 0), + PROC(cancel, cancargs, res, args, res, 0), + PROC(unlock, unlockargs, res, args, res, 0), + PROC(granted, testargs, res, args, res, 0), + PROC(test_msg, testargs, norep, args, void, 0), + PROC(lock_msg, lockargs, norep, args, void, 0), + PROC(cancel_msg, cancargs, norep, args, void, 0), + PROC(unlock_msg, unlockargs, norep, args, void, 0), + PROC(granted_msg, testargs, norep, args, void, 0), + PROC(test_res, testres, norep, res, void, 0), + PROC(lock_res, lockres, norep, res, void, 0), + PROC(cancel_res, cancelres, norep, res, void, 0), + PROC(unlock_res, unlockres, norep, res, void, 0), + PROC(granted_res, grantedres, norep, res, void, 0), /* statd callback */ - PROC(sm_notify, reboot, void, reboot, void), - PROC(none, void, void, void, void), - PROC(none, void, void, void, void), - PROC(none, void, void, void, void), - PROC(share, shareargs, shareres, args, res), - PROC(unshare, shareargs, shareres, args, res), - PROC(nm_lock, lockargs, res, args, res), - PROC(free_all, notify, void, args, void), + PROC(sm_notify, reboot, void, reboot, void, 0), + PROC(none, void, void, void, void, 0), + PROC(none, void, void, void, void, 0), + PROC(none, void, void, void, void, 0), + PROC(share, shareargs, shareres, args, res, 0), + PROC(unshare, shareargs, shareres, args, res, 0), + PROC(nm_lock, lockargs, res, args, res, 0), + PROC(free_all, notify, void, args, void, 0), }; diff -Nru a/fs/locks.c b/fs/locks.c --- a/fs/locks.c Thu Mar 7 18:17:42 2002 +++ b/fs/locks.c Thu Mar 7 18:17:42 2002 @@ -1459,7 +1459,7 @@ (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) { struct address_space *mapping = inode->i_mapping; - if (mapping->i_mmap_shared != NULL) { + if (!list_empty(&mapping->i_mmap_shared)) { error = -EAGAIN; goto out_putf; } @@ -1615,7 +1615,7 @@ (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) { struct address_space *mapping = inode->i_mapping; - if (mapping->i_mmap_shared != NULL) { + if (!list_empty(&mapping->i_mmap_shared)) { error = -EAGAIN; goto out_putf; } diff -Nru a/fs/namei.c b/fs/namei.c --- a/fs/namei.c Thu Mar 7 18:17:42 2002 +++ b/fs/namei.c Thu Mar 7 18:17:42 2002 @@ -609,18 +609,13 @@ } err = -ENOENT; if (!inode) - goto no_inode; + break; if (lookup_flags & LOOKUP_DIRECTORY) { err = -ENOTDIR; if (!inode->i_op || !inode->i_op->lookup) break; } goto return_base; -no_inode: - err = -ENOENT; - if (lookup_flags & (LOOKUP_POSITIVE|LOOKUP_DIRECTORY)) - break; - goto return_base; lookup_parent: nd->last = this; nd->last_type = LAST_NORM; @@ -686,17 +681,16 @@ struct nameidata nd; struct vfsmount *mnt = NULL, *oldmnt; struct dentry *dentry = NULL, *olddentry; - if (emul) { - read_lock(¤t->fs->lock); - nd.mnt = mntget(current->fs->rootmnt); - nd.dentry = dget(current->fs->root); - read_unlock(¤t->fs->lock); - nd.flags = LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_POSITIVE; - if (path_walk(emul,&nd) == 0) { - mnt = nd.mnt; - dentry = nd.dentry; - } + int err; + + if (!emul) + goto set_it; + err = path_lookup(emul, LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_NOALT, &nd); + if (err) { + mnt = nd.mnt; + dentry = nd.dentry; } +set_it: write_lock(¤t->fs->lock); oldmnt = current->fs->altrootmnt; olddentry = current->fs->altroot; @@ -825,15 +819,11 @@ */ int __user_walk(const char *name, unsigned flags, struct nameidata *nd) { - char *tmp; - int err; + char *tmp = getname(name); + int err = PTR_ERR(tmp); - tmp = getname(name); - err = PTR_ERR(tmp); if (!IS_ERR(tmp)) { - err = 0; - if (path_init(tmp, flags, nd)) - err = path_walk(tmp, nd); + err = path_lookup(tmp, flags, nd); putname(tmp); } return err; @@ -1024,8 +1014,7 @@ * The simplest case - just a plain lookup. */ if (!(flag & O_CREAT)) { - if (path_init(pathname, lookup_flags(flag), nd)) - error = path_walk(pathname, nd); + error = path_lookup(pathname, lookup_flags(flag), nd); if (error) return error; dentry = nd->dentry; @@ -1035,8 +1024,7 @@ /* * Create - we need to know the parent. */ - if (path_init(pathname, LOOKUP_PARENT, nd)) - error = path_walk(pathname, nd); + error = path_lookup(pathname, LOOKUP_PARENT, nd); if (error) return error; @@ -1281,8 +1269,7 @@ if (IS_ERR(tmp)) return PTR_ERR(tmp); - if (path_init(tmp, LOOKUP_PARENT, &nd)) - error = path_walk(tmp, &nd); + error = path_lookup(tmp, LOOKUP_PARENT, &nd); if (error) goto out; dentry = lookup_create(&nd, 0); @@ -1342,8 +1329,7 @@ struct dentry *dentry; struct nameidata nd; - if (path_init(tmp, LOOKUP_PARENT, &nd)) - error = path_walk(tmp, &nd); + error = path_lookup(tmp, LOOKUP_PARENT, &nd); if (error) goto out; dentry = lookup_create(&nd, 1); @@ -1434,8 +1420,7 @@ if(IS_ERR(name)) return PTR_ERR(name); - if (path_init(name, LOOKUP_PARENT, &nd)) - error = path_walk(name, &nd); + error = path_lookup(name, LOOKUP_PARENT, &nd); if (error) goto exit; @@ -1506,8 +1491,7 @@ if(IS_ERR(name)) return PTR_ERR(name); - if (path_init(name, LOOKUP_PARENT, &nd)) - error = path_walk(name, &nd); + error = path_lookup(name, LOOKUP_PARENT, &nd); if (error) goto exit; error = -EISDIR; @@ -1570,8 +1554,7 @@ struct dentry *dentry; struct nameidata nd; - if (path_init(to, LOOKUP_PARENT, &nd)) - error = path_walk(to, &nd); + error = path_lookup(to, LOOKUP_PARENT, &nd); if (error) goto out; dentry = lookup_create(&nd, 0); @@ -1634,46 +1617,37 @@ */ asmlinkage long sys_link(const char * oldname, const char * newname) { + struct dentry *new_dentry; + struct nameidata nd, old_nd; int error; - char * from; char * to; - from = getname(oldname); - if(IS_ERR(from)) - return PTR_ERR(from); to = getname(newname); - error = PTR_ERR(to); - if (!IS_ERR(to)) { - struct dentry *new_dentry; - struct nameidata nd, old_nd; + if (IS_ERR(to)) + return PTR_ERR(to); - error = 0; - if (path_init(from, LOOKUP_POSITIVE, &old_nd)) - error = path_walk(from, &old_nd); - if (error) - goto exit; - if (path_init(to, LOOKUP_PARENT, &nd)) - error = path_walk(to, &nd); - if (error) - goto out; - error = -EXDEV; - if (old_nd.mnt != nd.mnt) - goto out_release; - new_dentry = lookup_create(&nd, 0); - error = PTR_ERR(new_dentry); - if (!IS_ERR(new_dentry)) { - error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry); - dput(new_dentry); - } - up(&nd.dentry->d_inode->i_sem); + error = __user_walk(oldname, 0, &old_nd); + if (error) + goto exit; + error = path_lookup(to, LOOKUP_PARENT, &nd); + if (error) + goto out; + error = -EXDEV; + if (old_nd.mnt != nd.mnt) + goto out_release; + new_dentry = lookup_create(&nd, 0); + error = PTR_ERR(new_dentry); + if (!IS_ERR(new_dentry)) { + error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry); + dput(new_dentry); + } + up(&nd.dentry->d_inode->i_sem); out_release: - path_release(&nd); + path_release(&nd); out: - path_release(&old_nd); + path_release(&old_nd); exit: - putname(to); - } - putname(from); + putname(to); return error; } @@ -1830,14 +1804,11 @@ struct dentry * trap; struct nameidata oldnd, newnd; - if (path_init(oldname, LOOKUP_PARENT, &oldnd)) - error = path_walk(oldname, &oldnd); - + error = path_lookup(oldname, LOOKUP_PARENT, &oldnd); if (error) goto exit; - if (path_init(newname, LOOKUP_PARENT, &newnd)) - error = path_walk(newname, &newnd); + error = path_lookup(newname, LOOKUP_PARENT, &newnd); if (error) goto exit1; diff -Nru a/fs/namespace.c b/fs/namespace.c --- a/fs/namespace.c Thu Mar 7 18:17:38 2002 +++ b/fs/namespace.c Thu Mar 7 18:17:38 2002 @@ -360,17 +360,9 @@ asmlinkage long sys_umount(char * name, int flags) { struct nameidata nd; - char *kname; int retval; - kname = getname(name); - retval = PTR_ERR(kname); - if (IS_ERR(kname)) - goto out; - retval = 0; - if (path_init(kname, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &nd)) - retval = path_walk(kname, &nd); - putname(kname); + retval = __user_walk(name, LOOKUP_FOLLOW, &nd); if (retval) goto out; retval = -EINVAL; @@ -497,8 +489,7 @@ return err; if (!old_name || !*old_name) return -EINVAL; - if (path_init(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd)) - err = path_walk(old_name, &old_nd); + err = path_lookup(old_name, LOOKUP_FOLLOW, &old_nd); if (err) return err; @@ -564,8 +555,7 @@ return -EPERM; if (!old_name || !*old_name) return -EINVAL; - if (path_init(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd)) - err = path_walk(old_name, &old_nd); + err = path_lookup(old_name, LOOKUP_FOLLOW, &old_nd); if (err) return err; @@ -731,8 +721,7 @@ flags &= ~(MS_NOSUID|MS_NOEXEC|MS_NODEV); /* ... and get the mountpoint */ - if (path_init(dir_name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd)) - retval = path_walk(dir_name, &nd); + retval = path_lookup(dir_name, LOOKUP_FOLLOW, &nd); if (retval) return retval; @@ -911,7 +900,6 @@ { struct vfsmount *tmp; struct nameidata new_nd, old_nd, parent_nd, root_parent, user_nd; - char *name; int error; if (!capable(CAP_SYS_ADMIN)) @@ -919,28 +907,14 @@ lock_kernel(); - name = getname(new_root); - error = PTR_ERR(name); - if (IS_ERR(name)) - goto out0; - error = 0; - if (path_init(name, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &new_nd)) - error = path_walk(name, &new_nd); - putname(name); + error = __user_walk(new_root, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &new_nd); if (error) goto out0; error = -EINVAL; if (!check_mnt(new_nd.mnt)) goto out1; - name = getname(put_old); - error = PTR_ERR(name); - if (IS_ERR(name)) - goto out1; - error = 0; - if (path_init(name, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &old_nd)) - error = path_walk(name, &old_nd); - putname(name); + error = __user_walk(put_old, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &old_nd); if (error) goto out1; diff -Nru a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c --- a/fs/ncpfs/dir.c Thu Mar 7 18:17:36 2002 +++ b/fs/ncpfs/dir.c Thu Mar 7 18:17:36 2002 @@ -257,12 +257,18 @@ __ncp_lookup_validate(struct dentry * dentry, int flags) { struct ncp_server *server; - struct inode *dir = dentry->d_parent->d_inode; + struct dentry *parent; + struct inode *dir; struct ncp_entry_info finfo; int res, val = 0, len = dentry->d_name.len + 1; __u8 __name[len]; - if (!dentry->d_inode || !dir) + read_lock(&dparent_lock); + parent = dget(dentry->d_parent); + read_unlock(&dparent_lock); + dir = parent->d_inode; + + if (!dentry->d_inode) goto finished; server = NCP_SERVER(dir); @@ -313,6 +319,7 @@ finished: DDPRINTK("ncp_lookup_validate: result=%d\n", val); + dput(parent); return val; } diff -Nru a/fs/nfs/dir.c b/fs/nfs/dir.c --- a/fs/nfs/dir.c Thu Mar 7 18:17:44 2002 +++ b/fs/nfs/dir.c Thu Mar 7 18:17:44 2002 @@ -467,12 +467,16 @@ { struct inode *dir; struct inode *inode; + struct dentry *parent; int error; struct nfs_fh fhandle; struct nfs_fattr fattr; + read_lock(&dparent_lock); + parent = dget(dentry->d_parent); + read_unlock(&dparent_lock); lock_kernel(); - dir = dentry->d_parent->d_inode; + dir = parent->d_inode; inode = dentry->d_inode; if (!inode) { @@ -508,6 +512,7 @@ nfs_renew_times(dentry); out_valid: unlock_kernel(); + dput(parent); return 1; out_bad: NFS_CACHEINV(dir); @@ -521,6 +526,7 @@ } d_drop(dentry); unlock_kernel(); + dput(parent); return 0; } diff -Nru a/fs/nfs/flushd.c b/fs/nfs/flushd.c --- a/fs/nfs/flushd.c Thu Mar 7 18:17:38 2002 +++ b/fs/nfs/flushd.c Thu Mar 7 18:17:38 2002 @@ -50,7 +50,7 @@ /* * This is the wait queue all cluster daemons sleep on */ -static struct rpc_wait_queue flushd_queue = RPC_INIT_WAITQ("nfs_flushd"); +static RPC_WAITQ(flushd_queue, "nfs_flushd"); /* * Local function declarations. diff -Nru a/fs/nfs/unlink.c b/fs/nfs/unlink.c --- a/fs/nfs/unlink.c Thu Mar 7 18:17:46 2002 +++ b/fs/nfs/unlink.c Thu Mar 7 18:17:46 2002 @@ -24,7 +24,7 @@ }; static struct nfs_unlinkdata *nfs_deletes; -static struct rpc_wait_queue nfs_delete_queue = RPC_INIT_WAITQ("nfs_delete_queue"); +static RPC_WAITQ(nfs_delete_queue, "nfs_delete_queue"); /** * nfs_detach_unlinkdata - Remove asynchronous unlink from global list diff -Nru a/fs/nfsd/export.c b/fs/nfsd/export.c --- a/fs/nfsd/export.c Thu Mar 7 18:17:45 2002 +++ b/fs/nfsd/export.c Thu Mar 7 18:17:45 2002 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -60,10 +61,6 @@ static struct svc_clnthash * clnt_hash[CLIENT_HASHMAX]; static svc_client * clients; -static int hash_lock; -static int want_lock; -static int hash_count; -static DECLARE_WAIT_QUEUE_HEAD( hash_wait ); /* * Find the client's export entry matching xdev/xino. @@ -112,11 +109,15 @@ struct list_head *head = &clp->cl_export[EXPORT_HASH(sb->s_dev)]; struct list_head *p; + spin_lock(&dcache_lock); list_for_each(p, head) { svc_export *exp = list_entry(p, svc_export, ex_hash); - if (is_subdir(dentry, exp->ex_dentry)) + if (is_subdir(dentry, exp->ex_dentry)) { + spin_unlock(&dcache_lock); return exp; + } } + spin_unlock(&dcache_lock); return NULL; } @@ -132,12 +133,16 @@ struct list_head *p; struct dentry *ndentry; + spin_lock(&dcache_lock); list_for_each(p, head) { svc_export *exp = list_entry(p, svc_export, ex_hash); ndentry = exp->ex_dentry; - if (ndentry && is_subdir(ndentry->d_parent, dentry)) + if (ndentry && is_subdir(ndentry->d_parent, dentry)) { + spin_unlock(&dcache_lock); return exp; + } } + spin_unlock(&dcache_lock); return NULL; } @@ -155,6 +160,39 @@ } /* + * Hashtable locking. Write locks are placed only by user processes + * wanting to modify export information. + * Write locking only done in this file. Read locking + * needed externally. + */ + +static DECLARE_RWSEM(hash_sem); + +void +exp_readlock(void) +{ + down_read(&hash_sem); +} + +static inline void +exp_writelock(void) +{ + down_write(&hash_sem); +} + +void +exp_readunlock(void) +{ + up_read(&hash_sem); +} + +static inline void +exp_writeunlock(void) +{ + up_write(&hash_sem); +} + +/* * Export a file system. */ int @@ -181,11 +219,9 @@ ino = nxp->ex_ino; /* Try to lock the export table for update */ - if ((err = exp_writelock()) < 0) - goto out; + exp_writelock(); /* Look up client info */ - err = -EINVAL; if (!(clp = exp_getclientbyname(nxp->ex_client))) goto out_unlock; @@ -202,9 +238,7 @@ } /* Look up the dentry */ - err = 0; - if (path_init(nxp->ex_path, LOOKUP_POSITIVE, &nd)) - err = path_walk(nxp->ex_path, &nd); + err = path_lookup(nxp->ex_path, 0, &nd); if (err) goto out_unlock; @@ -270,7 +304,7 @@ /* Unlock hashtable */ out_unlock: - exp_unlock(); + exp_writeunlock(); out: return err; @@ -337,8 +371,7 @@ if (!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX)) return -EINVAL; - if ((err = exp_writelock()) < 0) - goto out; + exp_writelock(); err = -EINVAL; clp = exp_getclientbyname(nxp->ex_client); @@ -353,8 +386,7 @@ } } - exp_unlock(); -out: + exp_writeunlock(); return err; } @@ -374,8 +406,7 @@ err = -EPERM; /* NB: we probably ought to check that it's NUL-terminated */ - if (path_init(path, LOOKUP_POSITIVE, &nd) && - path_walk(path, &nd)) { + if (path_lookup(path, 0, &nd)) { printk("nfsd: exp_rootfh path not found %s", path); return err; } @@ -407,58 +438,6 @@ return err; } -/* - * Hashtable locking. Write locks are placed only by user processes - * wanting to modify export information. - */ -void -exp_readlock(void) -{ - while (hash_lock || want_lock) - sleep_on(&hash_wait); - hash_count++; -} - -int -exp_writelock(void) -{ - /* fast track */ - if (!hash_count && !hash_lock) { - lock_it: - hash_lock = 1; - return 0; - } - - clear_thread_flag(TIF_SIGPENDING); - want_lock++; - while (hash_count || hash_lock) { - interruptible_sleep_on(&hash_wait); - if (signal_pending(current)) - break; - } - want_lock--; - - /* restore the task's signals */ - spin_lock_irq(¤t->sigmask_lock); - recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); - - if (!hash_count && !hash_lock) - goto lock_it; - return -EINTR; -} - -void -exp_unlock(void) -{ - if (!hash_count && !hash_lock) - printk(KERN_WARNING "exp_unlock: not locked!\n"); - if (hash_count) - hash_count--; - else - hash_lock = 0; - wake_up(&hash_wait); -} /* * Find a valid client given an inet address. We always move the most @@ -564,7 +543,7 @@ static void e_stop(struct seq_file *m, void *p) { - exp_unlock(); + exp_readunlock(); } struct flags { @@ -680,8 +659,7 @@ goto out; /* Lock the hashtable */ - if ((err = exp_writelock()) < 0) - goto out; + exp_writelock(); /* First check if this is a change request for a client. */ for (clp = clients; clp; clp = clp->cl_next) @@ -746,7 +724,7 @@ err = 0; out_unlock: - exp_unlock(); + exp_writeunlock(); out: return err; } @@ -765,10 +743,8 @@ goto out; /* Lock the hashtable */ - if ((err = exp_writelock()) < 0) - goto out; + exp_writelock(); - err = -EINVAL; for (clpp = &clients; (clp = *clpp); clpp = &(clp->cl_next)) if (!strcmp(ncp->cl_ident, clp->cl_ident)) break; @@ -779,7 +755,7 @@ err = 0; } - exp_unlock(); + exp_writeunlock(); out: return err; } @@ -885,16 +861,14 @@ dprintk("nfsd: shutting down export module.\n"); - if (exp_writelock() < 0) { - printk(KERN_WARNING "Weird: hashtable locked in exp_shutdown"); - return; - } + exp_writelock(); + for (i = 0; i < CLIENT_HASHMAX; i++) { while (clnt_hash[i]) exp_freeclient(clnt_hash[i]->h_client); } clients = NULL; /* we may be restarted before the module unloads */ - exp_unlock(); + exp_writeunlock(); dprintk("nfsd: export shutdown complete.\n"); } diff -Nru a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c --- a/fs/nfsd/lockd.c Thu Mar 7 18:17:40 2002 +++ b/fs/nfsd/lockd.c Thu Mar 7 18:17:40 2002 @@ -62,7 +62,7 @@ struct nlmsvc_binding nfsd_nlm_ops = { exp_readlock, /* lock export table for reading */ - exp_unlock, /* unlock export table */ + exp_readunlock, /* unlock export table */ exp_getclient, /* look up NFS client */ nlm_fopen, /* open file for locking */ nlm_fclose, /* close file */ diff -Nru a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c --- a/fs/nfsd/nfs3proc.c Thu Mar 7 18:17:42 2002 +++ b/fs/nfsd/nfs3proc.c Thu Mar 7 18:17:42 2002 @@ -183,11 +183,12 @@ */ svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &avail, 1 + NFS3_POST_OP_ATTR_WORDS + 3); - resp->count = argp->count; if ((avail << 2) < resp->count) resp->count = avail << 2; + svc_reserve(rqstp, ((1 + NFS3_POST_OP_ATTR_WORDS + 3)<<2) + argp->count +4); + fh_copy(&resp->fh, &argp->fh); nfserr = nfsd_read(rqstp, &resp->fh, argp->offset, @@ -646,7 +647,7 @@ #define nfsd3_voidres nfsd3_voidargs struct nfsd3_voidargs { int dummy; }; -#define PROC(name, argt, rest, relt, cache) \ +#define PROC(name, argt, rest, relt, cache, respsize) \ { (svc_procfunc) nfsd3_proc_##name, \ (kxdrproc_t) nfs3svc_decode_##argt##args, \ (kxdrproc_t) nfs3svc_encode_##rest##res, \ @@ -654,29 +655,30 @@ sizeof(struct nfsd3_##argt##args), \ sizeof(struct nfsd3_##rest##res), \ 0, \ - cache \ + cache, \ + respsize, \ } struct svc_procedure nfsd_procedures3[22] = { - PROC(null, void, void, void, RC_NOCACHE), - PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE), - PROC(setattr, sattr, wccstat, fhandle, RC_REPLBUFF), - PROC(lookup, dirop, dirop, fhandle2, RC_NOCACHE), - PROC(access, access, access, fhandle, RC_NOCACHE), - PROC(readlink, fhandle, readlink, fhandle, RC_NOCACHE), - PROC(read, read, read, fhandle, RC_NOCACHE), - PROC(write, write, write, fhandle, RC_REPLBUFF), - PROC(create, create, create, fhandle2, RC_REPLBUFF), - PROC(mkdir, mkdir, create, fhandle2, RC_REPLBUFF), - PROC(symlink, symlink, create, fhandle2, RC_REPLBUFF), - PROC(mknod, mknod, create, fhandle2, RC_REPLBUFF), - PROC(remove, dirop, wccstat, fhandle, RC_REPLBUFF), - PROC(rmdir, dirop, wccstat, fhandle, RC_REPLBUFF), - PROC(rename, rename, rename, fhandle2, RC_REPLBUFF), - PROC(link, link, link, fhandle2, RC_REPLBUFF), - PROC(readdir, readdir, readdir, fhandle, RC_NOCACHE), - PROC(readdirplus,readdirplus, readdir, fhandle, RC_NOCACHE), - PROC(fsstat, fhandle, fsstat, void, RC_NOCACHE), - PROC(fsinfo, fhandle, fsinfo, void, RC_NOCACHE), - PROC(pathconf, fhandle, pathconf, void, RC_NOCACHE), - PROC(commit, commit, commit, fhandle, RC_NOCACHE) + PROC(null, void, void, void, RC_NOCACHE, 1), + PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE, 1+21), + PROC(setattr, sattr, wccstat, fhandle, RC_REPLBUFF, 1+7+22), + PROC(lookup, dirop, dirop, fhandle2, RC_NOCACHE, 1+9+22+22), + PROC(access, access, access, fhandle, RC_NOCACHE, 1+22+1), + PROC(readlink, fhandle, readlink, fhandle, RC_NOCACHE, 1+22+1+256), + PROC(read, read, read, fhandle, RC_NOCACHE, 1+22+4+NFSSVC_MAXBLKSIZE), + PROC(write, write, write, fhandle, RC_REPLBUFF, 1+7+22+4), + PROC(create, create, create, fhandle2, RC_REPLBUFF, 1+(1+9+22)+7+22), + PROC(mkdir, mkdir, create, fhandle2, RC_REPLBUFF, 1+(1+9+22)+7+22), + PROC(symlink, symlink, create, fhandle2, RC_REPLBUFF, 1+(1+9+22)+7+22), + PROC(mknod, mknod, create, fhandle2, RC_REPLBUFF, 1+(1+9+22)+7+22), + PROC(remove, dirop, wccstat, fhandle, RC_REPLBUFF, 1+7+22), + PROC(rmdir, dirop, wccstat, fhandle, RC_REPLBUFF, 1+7+22), + PROC(rename, rename, rename, fhandle2, RC_REPLBUFF, 1+7+22+7+22), + PROC(link, link, link, fhandle2, RC_REPLBUFF, 1+22+7+22), + PROC(readdir, readdir, readdir, fhandle, RC_NOCACHE, 0), + PROC(readdirplus,readdirplus, readdir, fhandle, RC_NOCACHE, 0), + PROC(fsstat, fhandle, fsstat, void, RC_NOCACHE, 1+14), + PROC(fsinfo, fhandle, fsinfo, void, RC_NOCACHE, 1+13), + PROC(pathconf, fhandle, pathconf, void, RC_NOCACHE, 1+6), + PROC(commit, commit, commit, fhandle, RC_NOCACHE, 1+7+22+2), }; diff -Nru a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c --- a/fs/nfsd/nfs3xdr.c Thu Mar 7 18:17:36 2002 +++ b/fs/nfsd/nfs3xdr.c Thu Mar 7 18:17:36 2002 @@ -699,10 +699,12 @@ fh_init(&fh, NFS3_FHSIZE); if (isdotent(name, namlen)) { - dchild = dparent; - if (namlen == 2) - dchild = dchild->d_parent; - dchild = dget(dchild); + if (namlen == 2) { + read_lock(&dparent_lock); + dchild = dget(dparent->d_parent); + read_unlock(&dparent_lock); + } else + dchild = dget(dparent); } else dchild = lookup_one_len(name, dparent,namlen); if (IS_ERR(dchild)) diff -Nru a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c --- a/fs/nfsd/nfscache.c Thu Mar 7 18:17:44 2002 +++ b/fs/nfsd/nfscache.c Thu Mar 7 18:17:44 2002 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -38,11 +39,17 @@ static struct svc_cacherep * lru_head; static struct svc_cacherep * lru_tail; static struct svc_cacherep * nfscache; -static int cache_initialized; static int cache_disabled = 1; static int nfsd_cache_append(struct svc_rqst *rqstp, struct svc_buf *data); +/* + * locking for the reply cache: + * A cache entry is "single use" if c_state == RC_INPROG + * Otherwise, it when accessing _prev or _next, the lock must be held. + */ +static spinlock_t cache_lock = SPIN_LOCK_UNLOCKED; + void nfsd_cache_init(void) { @@ -51,8 +58,6 @@ size_t i; unsigned long order; - if (cache_initialized) - return; i = CACHESIZE * sizeof (struct svc_cacherep); for (order = 0; (PAGE_SIZE << order) < i; order++) @@ -90,7 +95,6 @@ lru_head->c_lru_prev = NULL; lru_tail->c_lru_next = NULL; - cache_initialized = 1; cache_disabled = 0; } @@ -101,15 +105,11 @@ size_t i; unsigned long order; - if (!cache_initialized) - return; - for (rp = lru_head; rp; rp = rp->c_lru_next) { if (rp->c_state == RC_DONE && rp->c_type == RC_REPLBUFF) kfree(rp->c_replbuf.buf); } - cache_initialized = 0; cache_disabled = 1; i = CACHESIZE * sizeof (struct svc_cacherep); @@ -179,6 +179,7 @@ vers = rqstp->rq_vers, proc = rqstp->rq_proc; unsigned long age; + int rtn; rqstp->rq_cacherep = NULL; if (cache_disabled || type == RC_NOCACHE) { @@ -186,6 +187,9 @@ return RC_DOIT; } + spin_lock(&cache_lock); + rtn = RC_DOIT; + rp = rh = (struct svc_cacherep *) &hash_list[REQHASH(xid)]; while ((rp = rp->c_hash_next) != rh) { if (rp->c_state != RC_UNUSED && @@ -208,7 +212,7 @@ if (safe++ > CACHESIZE) { printk("nfsd: loop in repcache LRU list\n"); cache_disabled = 1; - return RC_DOIT; + goto out; } } } @@ -222,7 +226,7 @@ printk(KERN_WARNING "nfsd: disabling repcache.\n"); cache_disabled = 1; } - return RC_DOIT; + goto out; } rqstp->rq_cacherep = rp; @@ -242,8 +246,9 @@ rp->c_replbuf.buf = NULL; } rp->c_type = RC_NOCACHE; - - return RC_DOIT; + out: + spin_unlock(&cache_lock); + return rtn; found_entry: /* We found a matching entry which is either in progress or done. */ @@ -251,33 +256,36 @@ rp->c_timestamp = jiffies; lru_put_front(rp); + rtn = RC_DROPIT; /* Request being processed or excessive rexmits */ if (rp->c_state == RC_INPROG || age < RC_DELAY) - return RC_DROPIT; + goto out; /* From the hall of fame of impractical attacks: * Is this a user who tries to snoop on the cache? */ + rtn = RC_DOIT; if (!rqstp->rq_secure && rp->c_secure) - return RC_DOIT; + goto out; /* Compose RPC reply header */ switch (rp->c_type) { case RC_NOCACHE: - return RC_DOIT; + break; case RC_REPLSTAT: svc_putlong(&rqstp->rq_resbuf, rp->c_replstat); + rtn = RC_REPLY; break; case RC_REPLBUFF: if (!nfsd_cache_append(rqstp, &rp->c_replbuf)) - return RC_DOIT; /* should not happen */ + goto out; /* should not happen */ + rtn = RC_REPLY; break; default: printk(KERN_WARNING "nfsd: bad repcache type %d\n", rp->c_type); rp->c_state = RC_UNUSED; - return RC_DOIT; } - return RC_REPLY; + goto out; } /* @@ -324,20 +332,22 @@ cachp = &rp->c_replbuf; cachp->buf = (u32 *) kmalloc(len << 2, GFP_KERNEL); if (!cachp->buf) { + spin_lock(&cache_lock); rp->c_state = RC_UNUSED; + spin_unlock(&cache_lock); return; } cachp->len = len; memcpy(cachp->buf, statp, len << 2); break; } - + spin_lock(&cache_lock); lru_put_front(rp); rp->c_secure = rqstp->rq_secure; rp->c_type = cachetype; rp->c_state = RC_DONE; rp->c_timestamp = jiffies; - + spin_unlock(&cache_lock); return; } diff -Nru a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c --- a/fs/nfsd/nfsctl.c Thu Mar 7 18:17:39 2002 +++ b/fs/nfsd/nfsctl.c Thu Mar 7 18:17:39 2002 @@ -32,6 +32,7 @@ #include #include #include +#include static int nfsctl_svc(struct nfsctl_svc *data); static int nfsctl_addclient(struct nfsctl_client *data); @@ -121,7 +122,7 @@ err = -EPERM; else err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen); - exp_unlock(); + exp_readunlock(); return err; } @@ -144,7 +145,7 @@ err = -EPERM; else err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE); - exp_unlock(); + exp_readunlock(); if (err == 0) { if (fh.fh_size > NFS_FHSIZE) @@ -187,7 +188,6 @@ int err; int argsize, respsize; - lock_kernel (); err = -EPERM; if (!capable(CAP_SYS_ADMIN)) { @@ -257,7 +257,6 @@ if (res) kfree(res); - unlock_kernel (); return err; } diff -Nru a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c --- a/fs/nfsd/nfsfh.c Thu Mar 7 18:17:42 2002 +++ b/fs/nfsd/nfsfh.c Thu Mar 7 18:17:42 2002 @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -273,7 +274,9 @@ /* I'm going to assume that if the returned dentry is different, then * it is well connected. But nobody returns different dentrys do they? */ + down(&child->d_inode->i_sem); pdentry = child->d_inode->i_op->lookup(child->d_inode, tdentry); + up(&child->d_inode->i_sem); d_drop(tdentry); /* we never want ".." hashed */ if (!pdentry && tdentry->d_inode == NULL) { /* File system cannot find ".." ... sad but possible */ @@ -426,6 +429,7 @@ */ dprintk("nfs_fh: need to look harder for %s/%d\n", sb->s_id, datap[0]); + lock_kernel(); if (!S_ISDIR(result->d_inode->i_mode)) { nfsdstats.fh_nocache_nondir++; /* need to iget dirino and make sure this inode is in that directory */ @@ -477,6 +481,11 @@ /* Something wrong. We need to drop the whole dentry->result path * whatever it was */ + /* + * FIXME: the loop below will do Bad Things(tm) if + * dentry (or one of its ancestors) become attached + * to the tree (e.g. due to VFAT-style alias handling) + */ struct dentry *d; for (d=result ; d ; d=(d->d_parent == d)?NULL:d->d_parent) d_drop(d); @@ -494,6 +503,7 @@ dput(dentry); dput(result); /* this will discard the whole free path, so we can up the semaphore */ up(&sb->s_nfsd_free_path_sem); + unlock_kernel(); goto retry; } dput(dentry); @@ -501,6 +511,7 @@ } dput(dentry); up(&sb->s_nfsd_free_path_sem); + unlock_kernel(); return result; err_dentry: @@ -508,6 +519,7 @@ err_result: dput(result); up(&sb->s_nfsd_free_path_sem); + unlock_kernel(); err_out: if (err == -ESTALE) nfsdstats.fh_stale++; @@ -610,7 +622,7 @@ dentry = dget(exp->ex_dentry); break; default: - dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb, + dentry = find_fh_dentry(exp->ex_dentry->d_sb, datap, data_left, fh->fh_fileid_type, !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)); } @@ -619,7 +631,7 @@ tfh[0] = fh->ofh_ino; tfh[1] = fh->ofh_generation; tfh[2] = fh->ofh_dirino; - dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb, + dentry = find_fh_dentry(exp->ex_dentry->d_sb, tfh, 3, fh->ofh_dirino?2:1, !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)); } @@ -676,11 +688,15 @@ if (exp->ex_dentry != dentry) { struct dentry *tdentry = dentry; + spin_lock(&dcache_lock); do { tdentry = tdentry->d_parent; if (exp->ex_dentry == tdentry) break; /* executable only by root and we can't be root */ + /* + * FIXME: permissions check is not that simple + */ if (current->fsuid && (exp->ex_flags & NFSEXP_ROOTSQUASH) && !(tdentry->d_inode->i_uid @@ -700,8 +716,10 @@ printk("nfsd Security: %s/%s bad export.\n", dentry->d_parent->d_name.name, dentry->d_name.name); + spin_unlock(&dcache_lock); goto out; } + spin_unlock(&dcache_lock); } } @@ -729,7 +747,7 @@ inline int _fh_update(struct dentry *dentry, struct svc_export *exp, __u32 *datap, int *maxsize) { - struct super_block *sb = dentry->d_inode->i_sb; + struct super_block *sb = dentry->d_sb; if (dentry == exp->ex_dentry) { *maxsize = 0; @@ -806,7 +824,7 @@ if (ref_fh && ref_fh->fh_handle.fh_version == 0xca && - parent->d_inode->i_sb->s_op->dentry_to_fh == NULL) { + dentry->d_sb->s_op->dentry_to_fh == NULL) { /* old style filehandle please */ memset(&fhp->fh_handle.fh_base, 0, NFS_FHSIZE); fhp->fh_handle.fh_size = NFS_FHSIZE; diff -Nru a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c --- a/fs/nfsd/nfsproc.c Thu Mar 7 18:17:41 2002 +++ b/fs/nfsd/nfsproc.c Thu Mar 7 18:17:41 2002 @@ -148,6 +148,7 @@ argp->count); argp->count = avail << 2; } + svc_reserve(rqstp, (19<<2) + argp->count + 4); resp->count = argp->count; nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh), @@ -522,7 +523,7 @@ #define nfssvc_release_none NULL struct nfsd_void { int dummy; }; -#define PROC(name, argt, rest, relt, cache) \ +#define PROC(name, argt, rest, relt, cache, respsize) \ { (svc_procfunc) nfsd_proc_##name, \ (kxdrproc_t) nfssvc_decode_##argt, \ (kxdrproc_t) nfssvc_encode_##rest, \ @@ -530,27 +531,28 @@ sizeof(struct nfsd_##argt), \ sizeof(struct nfsd_##rest), \ 0, \ - cache \ + cache, \ + respsize, \ } struct svc_procedure nfsd_procedures2[18] = { - PROC(null, void, void, none, RC_NOCACHE), - PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE), - PROC(setattr, sattrargs, attrstat, fhandle, RC_REPLBUFF), - PROC(none, void, void, none, RC_NOCACHE), - PROC(lookup, diropargs, diropres, fhandle, RC_NOCACHE), - PROC(readlink, fhandle, readlinkres, none, RC_NOCACHE), - PROC(read, readargs, readres, fhandle, RC_NOCACHE), - PROC(none, void, void, none, RC_NOCACHE), - PROC(write, writeargs, attrstat, fhandle, RC_REPLBUFF), - PROC(create, createargs, diropres, fhandle, RC_REPLBUFF), - PROC(remove, diropargs, void, none, RC_REPLSTAT), - PROC(rename, renameargs, void, none, RC_REPLSTAT), - PROC(link, linkargs, void, none, RC_REPLSTAT), - PROC(symlink, symlinkargs, void, none, RC_REPLSTAT), - PROC(mkdir, createargs, diropres, fhandle, RC_REPLBUFF), - PROC(rmdir, diropargs, void, none, RC_REPLSTAT), - PROC(readdir, readdirargs, readdirres, none, RC_REPLBUFF), - PROC(statfs, fhandle, statfsres, none, RC_NOCACHE), + PROC(null, void, void, none, RC_NOCACHE, 1), + PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE, 1+18), + PROC(setattr, sattrargs, attrstat, fhandle, RC_REPLBUFF, 1+18), + PROC(none, void, void, none, RC_NOCACHE, 1), + PROC(lookup, diropargs, diropres, fhandle, RC_NOCACHE, 1+8+18), + PROC(readlink, fhandle, readlinkres, none, RC_NOCACHE, 1+1+256), + PROC(read, readargs, readres, fhandle, RC_NOCACHE, 1+18+1+NFSSVC_MAXBLKSIZE), + PROC(none, void, void, none, RC_NOCACHE, 1), + PROC(write, writeargs, attrstat, fhandle, RC_REPLBUFF, 1+18), + PROC(create, createargs, diropres, fhandle, RC_REPLBUFF, 1+8+18), + PROC(remove, diropargs, void, none, RC_REPLSTAT, 1), + PROC(rename, renameargs, void, none, RC_REPLSTAT, 1), + PROC(link, linkargs, void, none, RC_REPLSTAT, 1), + PROC(symlink, symlinkargs, void, none, RC_REPLSTAT, 1), + PROC(mkdir, createargs, diropres, fhandle, RC_REPLBUFF, 1+8+18), + PROC(rmdir, diropargs, void, none, RC_REPLSTAT, 1), + PROC(readdir, readdirargs, readdirres, none, RC_REPLBUFF, 0), + PROC(statfs, fhandle, statfsres, none, RC_NOCACHE, 1+5), }; diff -Nru a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c --- a/fs/nfsd/nfssvc.c Thu Mar 7 18:17:36 2002 +++ b/fs/nfsd/nfssvc.c Thu Mar 7 18:17:36 2002 @@ -54,8 +54,9 @@ static void nfsd(struct svc_rqst *rqstp); struct timeval nfssvc_boot; static struct svc_serv *nfsd_serv; -static int nfsd_busy; +static atomic_t nfsd_busy; static unsigned long nfsd_last_call; +static spinlock_t nfsd_call_lock = SPIN_LOCK_UNLOCKED; struct nfsd_list { struct list_head list; @@ -74,7 +75,8 @@ int error; int none_left; struct list_head *victim; - + + lock_kernel(); dprintk("nfsd: creating service\n"); error = -EINVAL; if (nrservs <= 0) @@ -87,6 +89,7 @@ if (error<0) goto out; if (!nfsd_serv) { + atomic_set(&nfsd_busy, 0); nfsd_serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE); if (nfsd_serv == NULL) goto out; @@ -94,7 +97,7 @@ if (error < 0) goto failure; -#if 0 /* Don't even pretend that TCP works. It doesn't. */ +#if CONFIG_NFSD_TCP error = svc_makesock(nfsd_serv, IPPROTO_TCP, port); if (error < 0) goto failure; @@ -125,6 +128,7 @@ nfsd_racache_shutdown(); } out: + unlock_kernel(); return error; } @@ -135,6 +139,7 @@ unsigned long diff; int decile; + spin_lock(&nfsd_call_lock); prev_call = nfsd_last_call; nfsd_last_call = jiffies; decile = busy_threads*10/nfsdstats.th_cnt; @@ -145,6 +150,7 @@ if (decile == 10) nfsdstats.th_fullcnt++; } + spin_unlock(&nfsd_call_lock); } /* @@ -173,6 +179,7 @@ me.task = current; list_add(&me.list, &nfsd_list); + unlock_kernel(); /* * The main request loop */ @@ -188,12 +195,12 @@ * recvfrom routine. */ while ((err = svc_recv(serv, rqstp, - MAX_SCHEDULE_TIMEOUT)) == -EAGAIN) + 5*60*HZ)) == -EAGAIN) ; if (err < 0) break; - update_thread_usage(nfsd_busy); - nfsd_busy++; + update_thread_usage(atomic_read(&nfsd_busy)); + atomic_inc(&nfsd_busy); /* Lock the export hash tables for reading. */ exp_readlock(); @@ -211,9 +218,9 @@ svc_process(serv, rqstp); /* Unlock export hash tables */ - exp_unlock(); - update_thread_usage(nfsd_busy); - nfsd_busy--; + exp_readunlock(); + update_thread_usage(atomic_read(&nfsd_busy)); + atomic_dec(&nfsd_busy); } if (err != -EINTR) { @@ -227,6 +234,8 @@ break; err = signo; } + + lock_kernel(); /* Release lockd */ lockd_down(); diff -Nru a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c --- a/fs/nfsd/vfs.c Thu Mar 7 18:17:42 2002 +++ b/fs/nfsd/vfs.c Thu Mar 7 18:17:42 2002 @@ -114,29 +114,33 @@ if (isdotent(name, len)) { if (len==1) dentry = dget(dparent); - else if (dparent != exp->ex_dentry) + else if (dparent != exp->ex_dentry) { + read_lock(&dparent_lock); dentry = dget(dparent->d_parent); - else if (!EX_CROSSMNT(exp)) + read_unlock(&dparent_lock); + } else if (!EX_CROSSMNT(exp)) dentry = dget(dparent); /* .. == . just like at / */ else { /* checking mountpoint crossing is very different when stepping up */ struct svc_export *exp2 = NULL; - struct dentry *dp; + struct dentry *dp, *old; struct vfsmount *mnt = mntget(exp->ex_mnt); dentry = dget(dparent); while(follow_up(&mnt, &dentry)) ; - dp = dget(dentry->d_parent); - dput(dentry); - dentry = dp; + old = dentry; + read_lock(&dparent_lock); + dp = dentry->d_parent; for ( ; !exp2 && dp->d_parent != dp; dp=dp->d_parent) exp2 = exp_get_by_name(exp->ex_client, mnt, dp); if (!exp2) { - dput(dentry); dentry = dget(dparent); } else { + dget(dentry->d_parent); exp = exp2; } + read_unlock(&dparent_lock); + dput(old); mntput(mnt); } } else { @@ -545,12 +549,15 @@ * Obtain the readahead parameters for the file * specified by (dev, ino). */ +static spinlock_t ra_lock = SPIN_LOCK_UNLOCKED; + static inline struct raparms * nfsd_get_raparms(kdev_t dev, ino_t ino) { struct raparms *ra, **rap, **frap = NULL; int depth = 0; - + + spin_lock(&ra_lock); for (rap = &raparm_cache; (ra = *rap); rap = &ra->p_next) { if (ra->p_ino == ino && kdev_same(ra->p_dev, dev)) goto found; @@ -559,8 +566,10 @@ frap = rap; } depth = nfsdstats.ra_size*11/10; - if (!frap) + if (!frap) { + spin_unlock(&ra_lock); return NULL; + } rap = frap; ra = *frap; ra->p_dev = dev; @@ -578,6 +587,7 @@ } ra->p_count++; nfsdstats.ra_depth[depth*10/nfsdstats.ra_size]++; + spin_unlock(&ra_lock); return ra; } diff -Nru a/fs/nls/Config.in b/fs/nls/Config.in --- a/fs/nls/Config.in Thu Mar 7 18:17:42 2002 +++ b/fs/nls/Config.in Thu Mar 7 18:17:42 2002 @@ -12,7 +12,7 @@ # msdos and Joliet want NLS if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" \ -o "$CONFIG_NTFS_FS" != "n" -o "$CONFIG_NCPFS_NLS" = "y" \ - -o "$CONFIG_SMB_NLS" = "y" ]; then + -o "$CONFIG_SMB_NLS" = "y" -o "$CONFIG_JFS_FS" != "n" ]; then define_bool CONFIG_NLS y else define_bool CONFIG_NLS n diff -Nru a/fs/open.c b/fs/open.c --- a/fs/open.c Thu Mar 7 18:17:37 2002 +++ b/fs/open.c Thu Mar 7 18:17:37 2002 @@ -358,19 +358,10 @@ asmlinkage long sys_chdir(const char * filename) { - int error; struct nameidata nd; - char *name; - - name = getname(filename); - error = PTR_ERR(name); - if (IS_ERR(name)) - goto out; + int error; - error = 0; - if (path_init(name,LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY,&nd)) - error = path_walk(name, &nd); - putname(name); + error = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &nd); if (error) goto out; @@ -418,19 +409,10 @@ asmlinkage long sys_chroot(const char * filename) { - int error; struct nameidata nd; - char *name; - - name = getname(filename); - error = PTR_ERR(name); - if (IS_ERR(name)) - goto out; + int error; - path_init(name, LOOKUP_POSITIVE | LOOKUP_FOLLOW | - LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd); - error = path_walk(name, &nd); - putname(name); + error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd); if (error) goto out; diff -Nru a/fs/partitions/Config.in b/fs/partitions/Config.in --- a/fs/partitions/Config.in Thu Mar 7 18:17:41 2002 +++ b/fs/partitions/Config.in Thu Mar 7 18:17:41 2002 @@ -38,7 +38,7 @@ fi if [ "$CONFIG_AMIGA" != "y" -a "$CONFIG_ATARI" != "y" -a \ "$CONFIG_MAC" != "y" -a "$CONFIG_SGI_IP22" != "y" -a \ - "$CONFIG_SGI_IP27" != "y" ]; then + "$CONFIG_ARM" != "y" -a "$CONFIG_SGI_IP27" != "y" ]; then define_bool CONFIG_MSDOS_PARTITION y fi if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_AFFS_FS" = "y" ]; then diff -Nru a/fs/partitions/check.c b/fs/partitions/check.c --- a/fs/partitions/check.c Thu Mar 7 18:17:42 2002 +++ b/fs/partitions/check.c Thu Mar 7 18:17:42 2002 @@ -480,3 +480,28 @@ return 0; } + +/* + * Make sure that a proposed subpartition is strictly contained inside + * the parent partition. If all is well, call add_gd_partition(). + */ +int +check_and_add_subpartition(struct gendisk *hd, int super_minor, int minor, + int sub_start, int sub_size) +{ + int start = hd->part[super_minor].start_sect; + int size = hd->part[super_minor].nr_sects; + + if (start == sub_start && size == sub_size) { + /* full parent partition, we have it already */ + return 0; + } + + if (start <= sub_start && start+size >= sub_start+sub_size) { + add_gd_partition(hd, minor, sub_start, sub_size); + return 1; + } + + printk("bad subpartition - ignored\n"); + return 0; +} diff -Nru a/fs/partitions/check.h b/fs/partitions/check.h --- a/fs/partitions/check.h Thu Mar 7 18:17:42 2002 +++ b/fs/partitions/check.h Thu Mar 7 18:17:42 2002 @@ -7,4 +7,10 @@ */ void add_gd_partition(struct gendisk *hd, int minor, int start, int size); +/* + * check_and_add_subpartition does the same for subpartitions + */ +int check_and_add_subpartition(struct gendisk *hd, int super_minor, + int minor, int sub_start, int sub_size); + extern int warn_no_part; diff -Nru a/fs/partitions/msdos.c b/fs/partitions/msdos.c --- a/fs/partitions/msdos.c Thu Mar 7 18:17:38 2002 +++ b/fs/partitions/msdos.c Thu Mar 7 18:17:38 2002 @@ -261,50 +261,6 @@ } #ifdef CONFIG_BSD_DISKLABEL -static void -check_and_add_bsd_partition(struct gendisk *hd, struct bsd_partition *bsd_p, - int minor, int *current_minor) -{ - struct hd_struct *lin_p; - /* check relative position of partitions. */ - for (lin_p = hd->part + 1 + minor; - lin_p - hd->part - minor < *current_minor; lin_p++) { - /* no relationship -> try again */ - if (lin_p->start_sect + lin_p->nr_sects <= le32_to_cpu(bsd_p->p_offset) || - lin_p->start_sect >= le32_to_cpu(bsd_p->p_offset) + le32_to_cpu(bsd_p->p_size)) - continue; - /* equal -> no need to add */ - if (lin_p->start_sect == le32_to_cpu(bsd_p->p_offset) && - lin_p->nr_sects == le32_to_cpu(bsd_p->p_size)) - return; - /* bsd living within dos partition */ - if (lin_p->start_sect <= le32_to_cpu(bsd_p->p_offset) && lin_p->start_sect - + lin_p->nr_sects >= le32_to_cpu(bsd_p->p_offset) + le32_to_cpu(bsd_p->p_size)) { -#ifdef DEBUG_BSD_DISKLABEL - printk("w: %d %ld+%ld,%d+%d", - lin_p - hd->part, - lin_p->start_sect, lin_p->nr_sects, - le32_to_cpu(bsd_p->p_offset), - le32_to_cpu(bsd_p->p_size)); -#endif - break; - } - /* ouch: bsd and linux overlap. Don't even try for that partition */ -#ifdef DEBUG_BSD_DISKLABEL - printk("???: %d %ld+%ld,%d+%d", - lin_p - hd->part, lin_p->start_sect, lin_p->nr_sects, - le32_to_cpu(bsd_p->p_offset), le32_to_cpu(bsd_p->p_size)); -#endif - printk("???"); - return; - } /* if the bsd partition is not currently known to linux, we end - * up here - */ - add_gd_partition(hd, *current_minor, le32_to_cpu(bsd_p->p_offset), - le32_to_cpu(bsd_p->p_size)); - (*current_minor)++; -} - /* * Create devices for BSD partitions listed in a disklabel, under a * dos-like partition. See extended_partition() for more information. @@ -326,16 +282,22 @@ put_dev_sector(sect); return; } - printk(" %s: <%s", partition_name(hd, minor, buf), name); + printk(" %s: <%s:", partition_name(hd, minor, buf), name); if (le16_to_cpu(l->d_npartitions) < max_partitions) max_partitions = le16_to_cpu(l->d_npartitions); - for (p = l->d_partitions; p - l->d_partitions < max_partitions; p++) { + for (p = l->d_partitions; p - l->d_partitions < max_partitions; p++) { + int bsd_start, bsd_size; + if ((*current_minor & mask) == 0) break; if (p->p_fstype == BSD_FS_UNUSED) continue; - check_and_add_bsd_partition(hd, p, minor, current_minor); + bsd_start = le32_to_cpu(p->p_offset); + bsd_size = le32_to_cpu(p->p_size); + if (check_and_add_subpartition(hd, minor, *current_minor, + bsd_start, bsd_size)) + (*current_minor)++; } put_dev_sector(sect); printk(" >\n"); diff -Nru a/fs/pipe.c b/fs/pipe.c --- a/fs/pipe.c Thu Mar 7 18:17:36 2002 +++ b/fs/pipe.c Thu Mar 7 18:17:36 2002 @@ -116,7 +116,7 @@ * writers synchronously that there is more * room. */ - wake_up_interruptible_sync(PIPE_WAIT(*inode)); + wake_up_interruptible(PIPE_WAIT(*inode)); if (!PIPE_EMPTY(*inode)) BUG(); goto do_more_read; @@ -214,7 +214,7 @@ * is going to give up this CPU, so it doesnt have * to do idle reschedules. */ - wake_up_interruptible_sync(PIPE_WAIT(*inode)); + wake_up_interruptible(PIPE_WAIT(*inode)); PIPE_WAITING_WRITERS(*inode)++; pipe_wait(inode); PIPE_WAITING_WRITERS(*inode)--; diff -Nru a/fs/reiserfs/bitmap.c b/fs/reiserfs/bitmap.c --- a/fs/reiserfs/bitmap.c Thu Mar 7 18:17:45 2002 +++ b/fs/reiserfs/bitmap.c Thu Mar 7 18:17:45 2002 @@ -103,7 +103,7 @@ if (nr >= sb_bmap_nr (rs)) { reiserfs_warning ("vs-4075: reiserfs_free_block: " "block %lu is out of range on %s\n", - block, bdevname(s->s_dev)); + block, s->s_id); return; } diff -Nru a/fs/reiserfs/fix_node.c b/fs/reiserfs/fix_node.c --- a/fs/reiserfs/fix_node.c Thu Mar 7 18:17:41 2002 +++ b/fs/reiserfs/fix_node.c Thu Mar 7 18:17:41 2002 @@ -2099,7 +2099,7 @@ reiserfs_panic (p_s_sb, "tb_buffer_sanity_check(): buffer is not in tree %s[%d] (%b)\n", descr, level, p_s_bh); } - if (!kdev_same(p_s_bh->b_dev, p_s_sb->s_dev) || + if (p_s_bh->b_bdev != p_s_sb->s_bdev || p_s_bh->b_size != p_s_sb->s_blocksize || p_s_bh->b_blocknr > SB_BLOCK_COUNT(p_s_sb)) { reiserfs_panic (p_s_sb, "tb_buffer_sanity_check(): check failed for buffer %s[%d] (%b)\n", descr, level, p_s_bh); diff -Nru a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c --- a/fs/reiserfs/inode.c Thu Mar 7 18:17:41 2002 +++ b/fs/reiserfs/inode.c Thu Mar 7 18:17:41 2002 @@ -1325,15 +1325,17 @@ if (maxlen < 5 || ! need_parent) return 3 ; + read_lock(&dparent_lock); inode = dentry->d_parent->d_inode ; data[3] = inode->i_ino ; data[4] = le32_to_cpu(INODE_PKEY (inode)->k_dir_id) ; *lenp = 5 ; - if (maxlen < 6) - return 5 ; - data[5] = inode->i_generation ; - *lenp = 6 ; - return 6 ; + if (maxlen >= 6) { + data[5] = inode->i_generation ; + *lenp = 6 ; + } + read_unlock(&dparent_lock); + return *lenp ; } @@ -1700,7 +1702,7 @@ ** call prepare_write */ reiserfs_warning("clm-6000: error reading block %lu on dev %s\n", - bh->b_blocknr, kdevname(bh->b_dev)) ; + bh->b_blocknr, p_s_inode->i_sb->s_id) ; error = -EIO ; goto unlock ; } diff -Nru a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c --- a/fs/reiserfs/journal.c Thu Mar 7 18:17:37 2002 +++ b/fs/reiserfs/journal.c Thu Mar 7 18:17:37 2002 @@ -98,6 +98,21 @@ static int release_journal_dev( struct super_block *super, struct reiserfs_journal *journal ); +static inline struct buffer_head *journ_get_hash_table(struct super_block *s, int block) +{ + return __get_hash_table(SB_JOURNAL(s)->j_dev_bd, block, s->s_blocksize); +} + +static inline struct buffer_head *journ_getblk(struct super_block *s, int block) +{ + return __getblk(SB_JOURNAL(s)->j_dev_bd, block, s->s_blocksize); +} + +static inline struct buffer_head *journ_bread(struct super_block *s, int block) +{ + return __bread(SB_JOURNAL(s)->j_dev_bd, block, s->s_blocksize); +} + static void init_journal_hash(struct super_block *p_s_sb) { memset(SB_JOURNAL(p_s_sb)->j_hash_table, 0, JOURNAL_HASH_SIZE * sizeof(struct reiserfs_journal_cnode *)) ; } @@ -690,7 +705,7 @@ count = 0 ; for (i = 0 ; atomic_read(&(jl->j_commit_left)) > 1 && i < (jl->j_len + 1) ; i++) { /* everything but commit_bh */ bn = SB_ONDISK_JOURNAL_1st_BLOCK(s) + (jl->j_start+i) % SB_ONDISK_JOURNAL_SIZE(s); - tbh = get_hash_table(SB_JOURNAL_DEV(s), bn, s->s_blocksize) ; + tbh = journ_get_hash_table(s, bn) ; /* kill this sanity check */ if (count > (orig_commit_left + 2)) { @@ -719,7 +734,7 @@ for (i = 0 ; atomic_read(&(jl->j_commit_left)) > 1 && i < (jl->j_len + 1) ; i++) { /* everything but commit_bh */ bn = SB_ONDISK_JOURNAL_1st_BLOCK(s) + (jl->j_start + i) % SB_ONDISK_JOURNAL_SIZE(s) ; - tbh = get_hash_table(SB_JOURNAL_DEV(s), bn, s->s_blocksize) ; + tbh = journ_get_hash_table(s, bn) ; wait_on_buffer(tbh) ; if (!buffer_uptodate(tbh)) { @@ -1410,9 +1425,8 @@ offset = d_bh->b_blocknr - SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) ; /* ok, we have a journal description block, lets see if the transaction was valid */ - c_bh = bread(SB_JOURNAL_DEV(p_s_sb), SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + - ((offset + le32_to_cpu(desc->j_len) + 1) % SB_ONDISK_JOURNAL_SIZE(p_s_sb)), - p_s_sb->s_blocksize) ; + c_bh = journ_bread(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + + ((offset + le32_to_cpu(desc->j_len) + 1) % SB_ONDISK_JOURNAL_SIZE(p_s_sb))) ; if (!c_bh) return 0 ; commit = (struct reiserfs_journal_commit *)c_bh->b_data ; @@ -1466,7 +1480,7 @@ unsigned long trans_offset ; int i; - d_bh = bread(SB_JOURNAL_DEV(p_s_sb), cur_dblock, p_s_sb->s_blocksize) ; + d_bh = journ_bread(p_s_sb, cur_dblock) ; if (!d_bh) return 1 ; desc = (struct reiserfs_journal_desc *)d_bh->b_data ; @@ -1490,9 +1504,9 @@ brelse(d_bh) ; return 1 ; } - c_bh = bread(SB_JOURNAL_DEV(p_s_sb), SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + + c_bh = journ_bread(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + ((trans_offset + le32_to_cpu(desc->j_len) + 1) % - SB_ONDISK_JOURNAL_SIZE(p_s_sb)), p_s_sb->s_blocksize) ; + SB_ONDISK_JOURNAL_SIZE(p_s_sb))) ; if (!c_bh) { brelse(d_bh) ; return 1 ; @@ -1521,7 +1535,7 @@ } /* get all the buffer heads */ for(i = 0 ; i < le32_to_cpu(desc->j_len) ; i++) { - log_blocks[i] = getblk(SB_JOURNAL_DEV(p_s_sb), SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + (trans_offset + 1 + i) % SB_ONDISK_JOURNAL_SIZE(p_s_sb), p_s_sb->s_blocksize); + log_blocks[i] = journ_getblk(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + (trans_offset + 1 + i) % SB_ONDISK_JOURNAL_SIZE(p_s_sb)); if (i < JOURNAL_TRANS_HALF) { real_blocks[i] = sb_getblk(p_s_sb, le32_to_cpu(desc->j_realblock[i])) ; } else { @@ -1600,7 +1614,7 @@ ** ** On exit, it sets things up so the first transaction will work correctly. */ -struct buffer_head * reiserfs_breada (kdev_t dev, int block, int bufsize, +struct buffer_head * reiserfs_breada (struct super_block *sb, int block, unsigned int max_block) { struct buffer_head * bhlist[BUFNR]; @@ -1608,7 +1622,7 @@ struct buffer_head * bh; int i, j; - bh = getblk (dev, block, bufsize); + bh = sb_getblk (sb, block); if (buffer_uptodate (bh)) return (bh); @@ -1618,7 +1632,7 @@ bhlist[0] = bh; j = 1; for (i = 1; i < blocks; i++) { - bh = getblk (dev, block + i, bufsize); + bh = sb_getblk (sb, block + i); if (buffer_uptodate (bh)) { brelse (bh); break; @@ -1661,10 +1675,9 @@ ** is the first unflushed, and if that transaction is not valid, ** replay is done */ - SB_JOURNAL(p_s_sb)->j_header_bh = bread (SB_JOURNAL_DEV(p_s_sb), + SB_JOURNAL(p_s_sb)->j_header_bh = journ_bread(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + - SB_ONDISK_JOURNAL_SIZE(p_s_sb), - p_s_sb->s_blocksize) ; + SB_ONDISK_JOURNAL_SIZE(p_s_sb)); if (!SB_JOURNAL(p_s_sb)->j_header_bh) { return 1 ; } @@ -1685,7 +1698,7 @@ ** there is nothing more we can do, and it makes no sense to read ** through the whole log. */ - d_bh = bread(SB_JOURNAL_DEV(p_s_sb), SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + le32_to_cpu(jh->j_first_unflushed_offset), p_s_sb->s_blocksize) ; + d_bh = journ_bread(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + le32_to_cpu(jh->j_first_unflushed_offset)) ; ret = journal_transaction_is_valid(p_s_sb, d_bh, NULL, NULL) ; if (!ret) { continue_replay = 0 ; @@ -1705,7 +1718,7 @@ ** all the valid transactions, and pick out the oldest. */ while(continue_replay && cur_dblock < (SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + SB_ONDISK_JOURNAL_SIZE(p_s_sb))) { - d_bh = reiserfs_breada(p_s_sb->s_dev, cur_dblock, p_s_sb->s_blocksize, + d_bh = reiserfs_breada(p_s_sb, cur_dblock, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + SB_ONDISK_JOURNAL_SIZE(p_s_sb)) ; ret = journal_transaction_is_valid(p_s_sb, d_bh, &oldest_invalid_trans_id, &newest_mount_id) ; if (ret == 1) { @@ -1973,9 +1986,6 @@ if( !S_ISBLK( jdev_inode -> i_mode ) ) { printk( "journal_init_dev: '%s' is not a block device", jdev_name ); result = -ENOTBLK; - } else if( journal -> j_dev_file -> f_vfsmnt -> mnt_flags & MNT_NODEV) { - printk( "journal_init_dev: Cannot use devices on '%s'", jdev_name ); - result = -EACCES; } else if( jdev_inode -> i_bdev == NULL ) { printk( "journal_init_dev: bdev unintialized for '%s'", jdev_name ); result = -ENOMEM; @@ -2039,9 +2049,8 @@ rs = SB_DISK_SUPER_BLOCK(p_s_sb); /* read journal header */ - bhjh = bread (SB_JOURNAL_DEV(p_s_sb), - SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + SB_ONDISK_JOURNAL_SIZE(p_s_sb), - SB_BLOCKSIZE(p_s_sb)); + bhjh = journ_bread(p_s_sb, + SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + SB_ONDISK_JOURNAL_SIZE(p_s_sb)); if (!bhjh) { printk("sh-459: unable to read journal header\n") ; return 1 ; @@ -2054,7 +2063,7 @@ char fname[ 32 ]; strcpy( jname, kdevname( SB_JOURNAL_DEV(p_s_sb) ) ); - strcpy( fname, kdevname( p_s_sb->s_dev ) ); + strcpy( fname, p_s_sb->s_id); printk("sh-460: journal header magic %x (device %s) does not match " "to magic found in super block %x (device %s)\n", jh->jh_journal.jp_journal_magic, jname, @@ -2979,7 +2988,7 @@ rs = SB_DISK_SUPER_BLOCK(p_s_sb) ; /* setup description block */ - d_bh = getblk(SB_JOURNAL_DEV(p_s_sb), SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + SB_JOURNAL(p_s_sb)->j_start, p_s_sb->s_blocksize) ; + d_bh = journ_getblk(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + SB_JOURNAL(p_s_sb)->j_start) ; mark_buffer_uptodate(d_bh, 1) ; desc = (struct reiserfs_journal_desc *)(d_bh)->b_data ; memset(desc, 0, sizeof(struct reiserfs_journal_desc)) ; @@ -2987,9 +2996,8 @@ desc->j_trans_id = cpu_to_le32(SB_JOURNAL(p_s_sb)->j_trans_id) ; /* setup commit block. Don't write (keep it clean too) this one until after everyone else is written */ - c_bh = getblk(SB_JOURNAL_DEV(p_s_sb), SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + - ((SB_JOURNAL(p_s_sb)->j_start + SB_JOURNAL(p_s_sb)->j_len + 1) % SB_ONDISK_JOURNAL_SIZE(p_s_sb)), - p_s_sb->s_blocksize) ; + c_bh = journ_getblk(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + + ((SB_JOURNAL(p_s_sb)->j_start + SB_JOURNAL(p_s_sb)->j_len + 1) % SB_ONDISK_JOURNAL_SIZE(p_s_sb))) ; commit = (struct reiserfs_journal_commit *)c_bh->b_data ; memset(commit, 0, sizeof(struct reiserfs_journal_commit)) ; commit->j_trans_id = cpu_to_le32(SB_JOURNAL(p_s_sb)->j_trans_id) ; @@ -3079,9 +3087,8 @@ /* copy all the real blocks into log area. dirty log blocks */ if (test_bit(BH_JDirty, &cn->bh->b_state)) { struct buffer_head *tmp_bh ; - tmp_bh = getblk(SB_JOURNAL_DEV(p_s_sb), SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + - ((cur_write_start + jindex) % SB_ONDISK_JOURNAL_SIZE(p_s_sb)), - p_s_sb->s_blocksize) ; + tmp_bh = journ_getblk(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + + ((cur_write_start + jindex) % SB_ONDISK_JOURNAL_SIZE(p_s_sb))) ; mark_buffer_uptodate(tmp_bh, 1) ; memcpy(tmp_bh->b_data, cn->bh->b_data, cn->bh->b_size) ; jindex++ ; diff -Nru a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c --- a/fs/reiserfs/namei.c Thu Mar 7 18:17:37 2002 +++ b/fs/reiserfs/namei.c Thu Mar 7 18:17:37 2002 @@ -344,8 +344,6 @@ struct reiserfs_dir_entry de; INITIALIZE_PATH (path_to_entry); - reiserfs_check_lock_depth("lookup") ; - if (dentry->d_name.len > REISERFS_MAX_NAME_LEN (dir->i_sb->s_blocksize)) return ERR_PTR(-ENAMETOOLONG); diff -Nru a/fs/reiserfs/super.c b/fs/reiserfs/super.c --- a/fs/reiserfs/super.c Thu Mar 7 18:17:44 2002 +++ b/fs/reiserfs/super.c Thu Mar 7 18:17:44 2002 @@ -581,11 +581,6 @@ } -int reiserfs_is_super(struct super_block *s) { - return (!kdev_same(s->s_dev, NODEV) && s->s_op == &reiserfs_sops) ; -} - - // // a portion of this function, particularly the VFS interface portion, // was derived from minix or ext2's analog and evolved as the @@ -662,7 +657,7 @@ return 1; for (i = 0, bmp = REISERFS_DISK_OFFSET_IN_BYTES / s->s_blocksize + 1; i < SB_BMAP_NR(s); i++, bmp = s->s_blocksize * 8 * i) { - SB_AP_BITMAP (s)[i] = getblk (s->s_dev, bmp, s->s_blocksize); + SB_AP_BITMAP (s)[i] = sb_getblk(s, bmp); if (!buffer_uptodate(SB_AP_BITMAP(s)[i])) ll_rw_block(READ, 1, SB_AP_BITMAP(s) + i); } @@ -770,7 +765,7 @@ brelse(bh) ; printk("dev %s: Unfinished reiserfsck --rebuild-tree run detected. Please run\n" "reiserfsck --rebuild-tree and wait for a completion. If that fais\n" - "get newer reiserfsprogs package\n", kdevname (s->s_dev)); + "get newer reiserfsprogs package\n", s->s_id); return 1; } @@ -881,7 +876,7 @@ ( (r5hash == yurahash) && (yurahash == GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])))) ) ) { reiserfs_warning("reiserfs: Unable to automatically detect hash" "function for device %s\n" - "please mount with -o hash={tea,rupasov,r5}\n", kdevname (s->s_dev)); + "please mount with -o hash={tea,rupasov,r5}\n", s->s_id); hash = UNSET_HASH; break; } @@ -893,7 +888,7 @@ hash = R5_HASH; else { reiserfs_warning("reiserfs: Unrecognised hash function for " - "device %s\n", kdevname (s->s_dev)); + "device %s\n", s->s_id); hash = UNSET_HASH; } } while (0); @@ -1200,6 +1195,11 @@ get_sb: reiserfs_get_sb, fs_flags: FS_REQUIRES_DEV, }; + +int reiserfs_is_super(struct super_block *s) +{ + return s->s_type == &reiserfs_fs_type; +} // // this is exactly what 2.3.99-pre9's init_ext2_fs is diff -Nru a/fs/smbfs/ChangeLog b/fs/smbfs/ChangeLog --- a/fs/smbfs/ChangeLog Thu Mar 7 18:17:46 2002 +++ b/fs/smbfs/ChangeLog Thu Mar 7 18:17:46 2002 @@ -1,5 +1,18 @@ ChangeLog for smbfs. +2001-08-03 Urban Widmark + + * *.c: Unicode support + +2001-08-23 Jochen Dolze + + * proc.c: Correct rsize/wsize computation for readX/writeX + +2001-0?-?? Urban Widmark + + * *.c: Add LFS + * *.c: Move to a "driver" style handling of different servertypes. + (Not all operations are done this way. yet.) 2001-12-31 René Scharfe * inode.c: added smb_show_options to show mount options in /proc/mounts diff -Nru a/fs/smbfs/cache.c b/fs/smbfs/cache.c --- a/fs/smbfs/cache.c Thu Mar 7 18:17:45 2002 +++ b/fs/smbfs/cache.c Thu Mar 7 18:17:45 2002 @@ -84,7 +84,7 @@ struct list_head *next; if (d_validate(dent, parent)) { - if (dent->d_name.len <= SMB_MAXPATHLEN && + if (dent->d_name.len <= SMB_MAXNAMELEN && (unsigned long)dent->d_fsdata == fpos) { if (!dent->d_inode) { dput(dent); diff -Nru a/fs/smbfs/dir.c b/fs/smbfs/dir.c --- a/fs/smbfs/dir.c Thu Mar 7 18:17:43 2002 +++ b/fs/smbfs/dir.c Thu Mar 7 18:17:43 2002 @@ -187,7 +187,7 @@ ctl.filled = 0; ctl.valid = 1; read_really: - result = smb_proc_readdir(filp, dirent, filldir, &ctl); + result = server->ops->readdir(filp, dirent, filldir, &ctl); if (ctl.idx == -1) goto invalid_cache; /* retry */ ctl.head.end = ctl.fpos - 1; @@ -378,12 +378,14 @@ void smb_renew_times(struct dentry * dentry) { + read_lock(&dparent_lock); for (;;) { dentry->d_time = jiffies; if (IS_ROOT(dentry)) break; dentry = dentry->d_parent; } + read_unlock(&dparent_lock); } static struct dentry * diff -Nru a/fs/smbfs/file.c b/fs/smbfs/file.c --- a/fs/smbfs/file.c Thu Mar 7 18:17:46 2002 +++ b/fs/smbfs/file.c Thu Mar 7 18:17:46 2002 @@ -55,12 +55,13 @@ smb_readpage_sync(struct dentry *dentry, struct page *page) { char *buffer = kmap(page); - unsigned long offset = page->index << PAGE_CACHE_SHIFT; - int rsize = smb_get_rsize(server_from_dentry(dentry)); + loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT; + struct smb_sb_info *server = server_from_dentry(dentry); + unsigned int rsize = smb_get_rsize(server); int count = PAGE_SIZE; int result; - VERBOSE("file %s/%s, count=%d@%ld, rsize=%d\n", + VERBOSE("file %s/%s, count=%d@%Ld, rsize=%d\n", DENTRY_PATH(dentry), count, offset, rsize); result = smb_open(dentry, SMB_O_RDONLY); @@ -74,7 +75,7 @@ if (count < rsize) rsize = count; - result = smb_proc_read(dentry->d_inode, offset, rsize, buffer); + result = server->ops->read(dentry->d_inode,offset,rsize,buffer); if (result < 0) goto io_error; @@ -118,21 +119,23 @@ */ static int smb_writepage_sync(struct inode *inode, struct page *page, - unsigned long offset, unsigned int count) + unsigned long pageoffset, unsigned int count) { - char *buffer = kmap(page) + offset; - int wsize = smb_get_wsize(server_from_inode(inode)); + loff_t offset; + char *buffer = kmap(page) + pageoffset; + struct smb_sb_info *server = server_from_inode(inode); + unsigned int wsize = smb_get_wsize(server); int result, written = 0; - offset += page->index << PAGE_CACHE_SHIFT; - VERBOSE("file ino=%ld, fileid=%d, count=%d@%ld, wsize=%d\n", - inode->i_ino, SMB_I(inode)->fileid, count, offset, wsize); + offset = ((loff_t)page->index << PAGE_CACHE_SHIFT) + pageoffset; + VERBOSE("file ino=%ld, fileid=%d, count=%d@%Ld, wsize=%d\n", + inode->i_ino, inode->u.smbfs_i.fileid, count, offset, wsize); do { if (count < wsize) wsize = count; - result = smb_proc_write(inode, offset, wsize, buffer); + result = server->ops->write(inode, offset, wsize, buffer); if (result < 0) { PARANOIA("failed write, wsize=%d, result=%d\n", wsize, result); diff -Nru a/fs/smbfs/inode.c b/fs/smbfs/inode.c --- a/fs/smbfs/inode.c Thu Mar 7 18:17:38 2002 +++ b/fs/smbfs/inode.c Thu Mar 7 18:17:38 2002 @@ -445,8 +445,7 @@ if (server->conn_pid) kill_proc(server->conn_pid, SIGTERM, 1); - smb_kfree(server->mnt); - smb_kfree(server->temp_buf); + smb_kfree(server->ops); if (server->packet) smb_vfree(server->packet); @@ -468,6 +467,7 @@ struct inode *root_inode; struct smb_fattr root; int ver; + void *mem; if (!raw_data) goto out_no_data; @@ -494,22 +494,29 @@ if (!server->packet) goto out_no_mem; - /* Allocate the global temp buffer */ - server->temp_buf = smb_kmalloc(2*SMB_MAXPATHLEN+20, GFP_KERNEL); - if (!server->temp_buf) + /* Allocate the global temp buffer and some superblock helper structs */ + VERBOSE("alloc chunk = %d\n", sizeof(struct smb_ops) + + sizeof(struct smb_mount_data_kernel) + + 2*SMB_MAXPATHLEN + 20); + mem = smb_kmalloc(sizeof(struct smb_ops) + + sizeof(struct smb_mount_data_kernel) + + 2*SMB_MAXPATHLEN + 20, GFP_KERNEL); + if (!mem) goto out_no_temp; + server->ops = mem; + server->mnt = mem + sizeof(struct smb_ops); + server->name_buf = mem + sizeof(struct smb_ops) + + sizeof(struct smb_mount_data_kernel); + server->temp_buf = mem + sizeof(struct smb_ops) + + sizeof(struct smb_mount_data_kernel) + + SMB_MAXPATHLEN + 1; + /* Setup NLS stuff */ server->remote_nls = NULL; server->local_nls = NULL; - server->name_buf = server->temp_buf + SMB_MAXPATHLEN + 20; - /* Allocate the mount data structure */ - /* FIXME: merge this with the other malloc and get a whole page? */ - mnt = smb_kmalloc(sizeof(struct smb_mount_data_kernel), GFP_KERNEL); - if (!mnt) - goto out_no_mount; - server->mnt = mnt; + mnt = server->mnt; memset(mnt, 0, sizeof(struct smb_mount_data_kernel)); strncpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT, @@ -565,9 +572,7 @@ out_no_root: iput(root_inode); out_bad_option: - smb_kfree(server->mnt); -out_no_mount: - smb_kfree(server->temp_buf); + smb_kfree(mem); out_no_temp: smb_vfree(server->packet); out_no_mem: @@ -630,8 +635,7 @@ error = smb_open(dentry, O_WRONLY); if (error) goto out; - error = smb_proc_trunc(server, SMB_I(inode)->fileid, - attr->ia_size); + error = server->ops->truncate(inode, attr->ia_size); if (error) goto out; error = vmtruncate(inode, attr->ia_size); diff -Nru a/fs/smbfs/proc.c b/fs/smbfs/proc.c --- a/fs/smbfs/proc.c Thu Mar 7 18:17:38 2002 +++ b/fs/smbfs/proc.c Thu Mar 7 18:17:38 2002 @@ -48,16 +48,29 @@ #define SMB_ST_BLKSIZE (PAGE_SIZE) #define SMB_ST_BLKSHIFT (PAGE_SHIFT) +static struct smb_ops smb_ops_core; +static struct smb_ops smb_ops_os2; +static struct smb_ops smb_ops_win95; +static struct smb_ops smb_ops_winNT; + +static void +smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); +static void +smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); +static int +smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *fattr); static int -smb_proc_setattr_ext(struct smb_sb_info *, struct inode *, - struct smb_fattr *); +smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, + struct smb_fattr *fattr); static int smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, - __u16 attr); + u16 attr); static int -smb_proc_do_getattr(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr); - +smb_proc_setattr_ext(struct smb_sb_info *server, + struct inode *inode, struct smb_fattr *fattr); +static void +install_ops(struct smb_ops *dst, struct smb_ops *src); static void @@ -98,8 +111,8 @@ } /* no conversion, just a wrapper for memcpy. */ -static int convert_memcpy(char *output, int olen, - const char *input, int ilen, +static int convert_memcpy(unsigned char *output, int olen, + const unsigned char *input, int ilen, struct nls_table *nls_from, struct nls_table *nls_to) { @@ -109,9 +122,25 @@ return ilen; } +static inline int write_char(unsigned char ch, char *output, int olen) +{ + if (olen < 4) + return -ENAMETOOLONG; + sprintf(output, ":x%02x", ch); + return 4; +} + +static inline int write_unichar(wchar_t ch, char *output, int olen) +{ + if (olen < 5) + return -ENAMETOOLONG; + sprintf(output, ":%04x", ch); + return 5; +} + /* convert from one "codepage" to another (possibly being utf8). */ -static int convert_cp(char *output, int olen, - const char *input, int ilen, +static int convert_cp(unsigned char *output, int olen, + const unsigned char *input, int ilen, struct nls_table *nls_from, struct nls_table *nls_to) { @@ -119,20 +148,26 @@ int n; wchar_t ch; - if (!nls_from || !nls_to) { - PARANOIA("nls_from=%p, nls_to=%p\n", nls_from, nls_to); - return convert_memcpy(output, olen, input, ilen, NULL, NULL); - } - while (ilen > 0) { /* convert by changing to unicode and back to the new cp */ - n = nls_from->char2uni((unsigned char *)input, ilen, &ch); - if (n < 0) + n = nls_from->char2uni(input, ilen, &ch); + if (n == -EINVAL) { + ilen--; + n = write_char(*input++, output, olen); + if (n < 0) + goto fail; + output += n; + olen -= n; + len += n; + continue; + } else if (n < 0) goto fail; input += n; ilen -= n; n = nls_to->uni2char(ch, output, olen); + if (n == -EINVAL) + n = write_unichar(ch, output, olen); if (n < 0) goto fail; output += n; @@ -145,6 +180,42 @@ return n; } +/* ----------------------------------------------------------- */ + +/* + * nls_unicode + * + * This encodes/decodes little endian unicode format + */ + +static int uni2char(wchar_t uni, unsigned char *out, int boundlen) +{ + if (boundlen < 2) + return -EINVAL; + *out++ = uni & 0xff; + *out++ = uni >> 8; + return 2; +} + +static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni) +{ + if (boundlen < 2) + return -EINVAL; + *uni = (rawstring[1] << 8) | rawstring[0]; + return 2; +} + +static struct nls_table unicode_table = { + "unicode", + uni2char, + char2uni, + NULL, /* not used by smbfs */ + NULL, + NULL, /* not a module */ +}; + +/* ----------------------------------------------------------- */ + static int setcodepage(struct nls_table **p, char *name) { struct nls_table *nls; @@ -157,7 +228,7 @@ } /* if already set, unload the previous one. */ - if (*p) + if (*p && *p != &unicode_table) unload_nls(*p); *p = nls; @@ -175,18 +246,25 @@ if (!*cp->remote_name) goto out; + /* local */ n = setcodepage(&server->local_nls, cp->local_name); if (n != 0) goto out; - n = setcodepage(&server->remote_nls, cp->remote_name); - if (n != 0) - setcodepage(&server->local_nls, NULL); + + /* remote */ + if (!strcmp(cp->remote_name, "unicode")) { + server->remote_nls = &unicode_table; + } else { + n = setcodepage(&server->remote_nls, cp->remote_name); + if (n != 0) + setcodepage(&server->local_nls, NULL); + } out: if (server->local_nls != NULL && server->remote_nls != NULL) - server->convert = convert_cp; + server->ops->convert = convert_cp; else - server->convert = convert_memcpy; + server->ops->convert = convert_memcpy; smb_unlock_server(server); return n; @@ -217,17 +295,19 @@ * smb_build_path: build the path to entry and name storing it in buf. * The path returned will have the trailing '\0'. */ -static int smb_build_path(struct smb_sb_info *server, char * buf, int maxlen, - struct dentry * entry, struct qstr * name) +static int smb_build_path(struct smb_sb_info *server, unsigned char *buf, + int maxlen, + struct dentry *entry, struct qstr *name) { - char *path = buf; + unsigned char *path = buf; int len; + int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE) != 0; - if (maxlen < 2) + if (maxlen < (2< SMB_MAXNAMELEN + 1) - maxlen = SMB_MAXNAMELEN + 1; + if (maxlen > SMB_MAXPATHLEN + 1) + maxlen = SMB_MAXPATHLEN + 1; if (entry == NULL) goto test_name_and_out; @@ -237,41 +317,56 @@ */ if (IS_ROOT(entry) && !name) { *path++ = '\\'; + if (unicode) *path++ = '\0'; *path++ = '\0'; - return 2; + if (unicode) *path++ = '\0'; + return path-buf; } /* * Build the path string walking the tree backward from end to ROOT * and store it in reversed order [see reverse_string()] */ + read_lock(&dparent_lock); while (!IS_ROOT(entry)) { - if (maxlen < 3) + if (maxlen < (3<convert(path, maxlen-2, + len = server->ops->convert(path, maxlen-2, entry->d_name.name, entry->d_name.len, server->local_nls, server->remote_nls); - if (len < 0) + if (len < 0) { + read_unlock(&dparent_lock); return len; + } reverse_string(path, len); path += len; + if (unicode) { + /* Note: reverse order */ + *path++ = '\0'; + maxlen--; + } *path++ = '\\'; maxlen -= len+1; entry = entry->d_parent; - if (IS_ROOT(entry)) - break; } + read_unlock(&dparent_lock); reverse_string(buf, path-buf); - /* maxlen is at least 1 */ + /* maxlen has space for at least one char */ test_name_and_out: if (name) { - if (maxlen < 3) + if (maxlen < (3<convert(path, maxlen-2, + if (unicode) { + *path++ = '\0'; + maxlen--; + } + len = server->ops->convert(path, maxlen-2, name->name, name->len, server->local_nls, server->remote_nls); if (len < 0) @@ -279,8 +374,9 @@ path += len; maxlen -= len+1; } - /* maxlen is at least 1 */ + /* maxlen has space for at least one char */ *path++ = '\0'; + if (unicode) *path++ = '\0'; return path-buf; } @@ -298,16 +394,31 @@ return result; } +/* encode_path for non-trans2 request SMBs */ static int smb_simple_encode_path(struct smb_sb_info *server, char **p, struct dentry * entry, struct qstr * name) { char *s = *p; int res; int maxlen = ((char *)server->packet + server->packet_size) - s; + int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE); if (!maxlen) return -ENAMETOOLONG; - *s++ = 4; + *s++ = 4; /* ASCII data format */ + + /* + * SMB Unicode strings must be 16bit aligned relative the start of the + * packet. If they are not they must be padded with 0. + */ + if (unicode) { + int align = s - (char *)server->packet; + if (align & 1) { + *s++ = '\0'; + maxlen--; + } + } + res = smb_encode_path(server, s, maxlen-1, entry, name); if (res < 0) return res; @@ -499,7 +610,8 @@ int smb_get_rsize(struct smb_sb_info *server) { - int overhead = SMB_HEADER_LEN + 5 * sizeof(__u16) + 2 + 1 + 2; + /* readX has 12 parameters, read has 5 */ + int overhead = SMB_HEADER_LEN + 12 * sizeof(__u16) + 2 + 1 + 2; int size = smb_get_xmitsize(server, overhead); VERBOSE("packet=%d, xmit=%d, size=%d\n", @@ -514,7 +626,8 @@ int smb_get_wsize(struct smb_sb_info *server) { - int overhead = SMB_HEADER_LEN + 5 * sizeof(__u16) + 2 + 1 + 2; + /* writeX has 14 parameters, write has 5 */ + int overhead = SMB_HEADER_LEN + 14 * sizeof(__u16) + 2 + 1 + 2; int size = smb_get_xmitsize(server, overhead); VERBOSE("packet=%d, xmit=%d, size=%d\n", @@ -830,12 +943,56 @@ /* now that we have an established connection we can detect the server type and enable bug workarounds */ - if (server->opt.protocol == SMB_PROTOCOL_NT1 && - (server->opt.max_xmit < 0x1000) && - !(server->opt.capabilities & SMB_CAP_NT_SMBS)) { + if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) + install_ops(server->ops, &smb_ops_core); + else if (server->opt.protocol == SMB_PROTOCOL_LANMAN2) + install_ops(server->ops, &smb_ops_os2); + else if (server->opt.protocol == SMB_PROTOCOL_NT1 && + (server->opt.max_xmit < 0x1000) && + !(server->opt.capabilities & SMB_CAP_NT_SMBS)) { + /* FIXME: can we kill the WIN95 flag now? */ server->mnt->flags |= SMB_MOUNT_WIN95; - VERBOSE("smb_newconn: detected WIN95 server\n"); + VERBOSE("detected WIN95 server\n"); + install_ops(server->ops, &smb_ops_win95); + } else { + /* + * Samba has max_xmit 65535 + * NT4spX has max_xmit 4536 (or something like that) + * win2k has ... + */ + VERBOSE("detected NT1 (Samba, NT4/5) server\n"); + install_ops(server->ops, &smb_ops_winNT); + } + + /* FIXME: the win9x code wants to modify these ... (seek/trunc bug) */ + if (server->mnt->flags & SMB_MOUNT_OLDATTR) { + server->ops->getattr = smb_proc_getattr_core; + } else if (server->mnt->flags & SMB_MOUNT_DIRATTR) { + server->ops->getattr = smb_proc_getattr_ff; + } + + /* Decode server capabilities */ + if (server->opt.capabilities & SMB_CAP_LARGE_FILES) { + /* Should be ok to set this now, as no one can access the + mount until the connection has been established. */ + SB_of(server)->s_maxbytes = ~0ULL >> 1; + VERBOSE("LFS enabled\n"); + } + if (server->opt.capabilities & SMB_CAP_UNICODE) { + server->mnt->flags |= SMB_MOUNT_UNICODE; + VERBOSE("Unicode enabled\n"); + } else { + server->mnt->flags &= ~SMB_MOUNT_UNICODE; } +#if 0 + /* flags we may test for other patches ... */ + if (server->opt.capabilities & SMB_CAP_LARGE_READX) { + VERBOSE("Large reads enabled\n"); + } + if (server->opt.capabilities & SMB_CAP_LARGE_WRITEX) { + VERBOSE("Large writes enabled\n"); + } +#endif VERBOSE("protocol=%d, max_xmit=%d, pid=%d capabilities=0x%x\n", server->opt.protocol, server->opt.max_xmit, server->conn_pid, @@ -913,10 +1070,15 @@ WSET(buf, smb_uid, server->opt.server_uid); WSET(buf, smb_mid, 1); - if (server->opt.protocol > SMB_PROTOCOL_CORE) - { - *(buf+smb_flg) = 0x8; - WSET(buf, smb_flg2, 0x3); + if (server->opt.protocol > SMB_PROTOCOL_CORE) { + int flags = SMB_FLAGS_CASELESS_PATHNAMES; + int flags2 = SMB_FLAGS2_LONG_PATH_COMPONENTS | + SMB_FLAGS2_EXTENDED_ATTRIBUTES; /* EA? not really ... */ + + *(buf+smb_flg) = flags; + if (server->mnt->flags & SMB_MOUNT_UNICODE) + flags2 |= SMB_FLAGS2_UNICODE_STRINGS; + WSET(buf, smb_flg2, flags2); } *p++ = wct; /* wct */ p += 2 * wct; @@ -1175,8 +1337,8 @@ /* In smb_proc_read and smb_proc_write we do not retry, because the file-id would not be valid after a reconnection. */ -int -smb_proc_read(struct inode *inode, off_t offset, int count, char *data) +static int +smb_proc_read(struct inode *inode, loff_t offset, int count, char *data) { struct smb_sb_info *server = server_from_inode(inode); __u16 returned_count, data_len; @@ -1224,15 +1386,15 @@ return result; } -int -smb_proc_write(struct inode *inode, off_t offset, int count, const char *data) +static int +smb_proc_write(struct inode *inode, loff_t offset, int count, const char *data) { struct smb_sb_info *server = server_from_inode(inode); int result; __u8 *p; __u16 fileid = SMB_I(inode)->fileid; - VERBOSE("ino=%ld, fileid=%d, count=%d@%ld, packet_size=%d\n", + VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld, packet_size=%d\n", inode->i_ino, SMB_I(inode)->fileid, count, offset, server->packet_size); @@ -1255,6 +1417,94 @@ return result; } +/* + * In smb_proc_readX and smb_proc_writeX we do not retry, because the + * file-id would not be valid after a reconnection. + */ +static int +smb_proc_readX(struct inode *inode, loff_t offset, int count, char *data) +{ + struct smb_sb_info *server = server_from_inode(inode); + u16 data_len, data_off; + unsigned char *buf; + int result; + + smb_lock_server(server); + smb_setup_header(server, SMBreadX, 12, 0); + buf = server->packet; + WSET(buf, smb_vwv0, 0x00ff); + WSET(buf, smb_vwv1, 0); + WSET(buf, smb_vwv2, SMB_I(inode)->fileid); + DSET(buf, smb_vwv3, (u32)offset); /* low 32 bits */ + WSET(buf, smb_vwv5, count); + WSET(buf, smb_vwv6, 0); + DSET(buf, smb_vwv7, 0); + WSET(buf, smb_vwv9, 0); + DSET(buf, smb_vwv10, (u32)(offset >> 32)); /* high 32 bits */ + WSET(buf, smb_vwv11, 0); + + result = smb_request_ok(server, SMBreadX, 12, -1); + if (result < 0) + goto out; + data_len = WVAL(server->packet, smb_vwv5); + data_off = WVAL(server->packet, smb_vwv6); + buf = smb_base(server->packet) + data_off; + + /* we can NOT simply trust the info given by the server ... */ + if (data_len > server->packet_size - (buf - server->packet)) { + printk(KERN_ERR "smb_proc_read: invalid data length!! " + "%d > %d - (%p - %p)\n", + data_len, server->packet_size, buf, server->packet); + result = -EIO; + goto out; + } + + memcpy(data, buf, data_len); + result = data_len; + +out: + smb_unlock_server(server); + VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n", + inode->i_ino, SMB_I(inode)->fileid, count, result); + return result; +} + +static int +smb_proc_writeX(struct inode *inode, loff_t offset, int count, const char *data) +{ + struct smb_sb_info *server = server_from_inode(inode); + int result; + u8 *p; + + VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld, packet_size=%d\n", + inode->i_ino, SMB_I(inode)->fileid, count, offset, + server->packet_size); + + smb_lock_server(server); + p = smb_setup_header(server, SMBwriteX, 14, count + 2); + WSET(server->packet, smb_vwv0, 0x00ff); + WSET(server->packet, smb_vwv1, 0); + WSET(server->packet, smb_vwv2, SMB_I(inode)->fileid); + DSET(server->packet, smb_vwv3, (u32)offset); /* low 32 bits */ + DSET(server->packet, smb_vwv5, 0); + WSET(server->packet, smb_vwv7, 0); /* write mode */ + WSET(server->packet, smb_vwv8, 0); + WSET(server->packet, smb_vwv9, 0); + WSET(server->packet, smb_vwv10, count); /* data length */ + WSET(server->packet, smb_vwv11, p + 2 - smb_base(server->packet)); + DSET(server->packet, smb_vwv12, (u32)(offset >> 32)); + *p++ = 0; /* FIXME: pad to short or long ... change +2 above also */ + *p++ = 0; + memcpy(p, data, count); + + result = smb_request_ok(server, SMBwriteX, 6, 0); + if (result >= 0) + result = WVAL(server->packet, smb_vwv2); + + smb_unlock_server(server); + return result; +} + int smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid) { @@ -1374,7 +1624,9 @@ struct smb_fattr fattr; /* first get current attribute */ - result = smb_proc_do_getattr(server, dentry, &fattr); + smb_init_dirent(server, &fattr); + result = server->ops->getattr(server, dentry, &fattr); + smb_finish_dirent(server, &fattr); if (result < 0) return result; @@ -1449,38 +1701,72 @@ return smb_request_ok(server, SMBflush, 0, 0); } -int -smb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length) +static int +smb_proc_trunc32(struct inode *inode, loff_t length) { - char *p; + /* + * Writing 0bytes is old-SMB magic for truncating files. + * MAX_NON_LFS should prevent this from being called with a too + * large offset. + */ + return smb_proc_write(inode, length, 0, NULL); +} + +static int +smb_proc_trunc64(struct inode *inode, loff_t length) +{ + struct smb_sb_info *server = server_from_inode(inode); + int command; int result; + char *param = server->temp_buf; + char data[8]; + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; smb_lock_server(server); - + command = TRANSACT2_SETFILEINFO; + + /* FIXME: must we also set allocation size? winNT seems to do that */ retry: - p = smb_setup_header(server, SMBwrite, 5, 3); - WSET(server->packet, smb_vwv0, fid); - WSET(server->packet, smb_vwv1, 0); - DSET(server->packet, smb_vwv2, length); - WSET(server->packet, smb_vwv4, 0); - *p++ = 1; - WSET(p, 0, 0); - - if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0) { + WSET(param, 0, SMB_I(inode)->fileid); + WSET(param, 2, SMB_SET_FILE_END_OF_FILE_INFO); + WSET(param, 4, 0); + LSET(data, 0, length); + result = smb_trans2_request(server, command, + 8, data, 6, param, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param); + if (result < 0) { if (smb_retry(server)) goto retry; goto out; } + result = 0; + if (server->rcls != 0) + result = smb_errno(server); +out: + smb_unlock_server(server); + return result; +} + +static int +smb_proc_trunc95(struct inode *inode, loff_t length) +{ + struct smb_sb_info *server = server_from_inode(inode); + int result = smb_proc_trunc32(inode, length); + /* * win9x doesn't appear to update the size immediately. * It will return the old file size after the truncate, - * confusing smbfs. - * NT and Samba return the new value immediately. + * confusing smbfs. So we force an update. + * + * FIXME: is this still necessary? */ - if (server->mnt->flags & SMB_MOUNT_WIN95) - smb_proc_flush(server, fid); -out: + smb_lock_server(server); + smb_proc_flush(server, SMB_I(inode)->fileid); smb_unlock_server(server); return result; } @@ -1561,7 +1847,6 @@ */ while (len > 2 && qname->name[len-1] == ' ') len--; - qname->len = len; smb_finish_dirent(server, fattr); @@ -1580,12 +1865,16 @@ } #endif - qname->len = server->convert(server->name_buf, SMB_MAXNAMELEN, - qname->name, len, - server->remote_nls, server->local_nls); - qname->name = server->name_buf; + qname->len = 0; + len = server->ops->convert(server->name_buf, SMB_MAXNAMELEN, + qname->name, len, + server->remote_nls, server->local_nls); + if (len > 0) { + qname->len = len; + qname->name = server->name_buf; + DEBUG1("len=%d, name=%.*s\n",qname->len,qname->len,qname->name); + } - DEBUG1("len=%d, name=%.*s\n", qname->len, qname->len, qname->name); return p + 22; } @@ -1701,6 +1990,8 @@ for (i = 0; i < count; i++) { p = smb_decode_short_dirent(server, p, &qname, &fattr); + if (qname.len == 0) + continue; if (entries_seen == 2 && qname.name[0] == '.') { if (qname.len == 1) @@ -1738,7 +2029,9 @@ { char *result; unsigned int len = 0; + int n; __u16 date, time; + int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE); /* * SMB doesn't have a concept of inode numbers ... @@ -1774,16 +2067,16 @@ result = p + WVAL(p, 0); len = DVAL(p, 60); if (len > 255) len = 255; - /* NT4 null terminates */ + /* NT4 null terminates, unless we are using unicode ... */ qname->name = p + 94; - if (len && qname->name[len-1] == '\0') + if (!unicode && len && qname->name[len-1] == '\0') len--; fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 8)); fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 16)); fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 24)); /* change time (32) */ - fattr->f_size = DVAL(p, 40); + fattr->f_size = LVAL(p, 40); /* alloc size (48) */ fattr->attr = DVAL(p, 56); @@ -1813,10 +2106,14 @@ } #endif - qname->len = server->convert(server->name_buf, SMB_MAXNAMELEN, - qname->name, len, - server->remote_nls, server->local_nls); - qname->name = server->name_buf; + qname->len = 0; + n = server->ops->convert(server->name_buf, SMB_MAXNAMELEN, + qname->name, len, + server->remote_nls, server->local_nls); + if (n > 0) { + qname->len = n; + qname->name = server->name_buf; + } out: return result; @@ -1882,7 +2179,7 @@ */ mask = param + 12; - mask_len = smb_encode_path(server, mask, SMB_MAXNAMELEN+1, dir, &star); + mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dir, &star); if (mask_len < 0) { result = mask_len; goto unlock_return; @@ -2057,18 +2354,6 @@ return result; } -int -smb_proc_readdir(struct file *filp, void *dirent, filldir_t filldir, - struct smb_cache_control *ctl) -{ - struct smb_sb_info *server = server_from_dentry(filp->f_dentry); - - if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) - return smb_proc_readdir_long(filp, dirent, filldir, ctl); - else - return smb_proc_readdir_short(filp, dirent, filldir, ctl); -} - /* * This version uses the trans2 TRANSACT2_FINDFIRST message * to get the attribute data. @@ -2089,7 +2374,7 @@ int mask_len, result; retry: - mask_len = smb_encode_path(server, mask, SMB_MAXNAMELEN+1, dentry, NULL); + mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dentry,NULL); if (mask_len < 0) { result = mask_len; goto out; @@ -2201,29 +2486,25 @@ */ static int smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *attr) + int *lrdata, unsigned char **rdata, + int *lrparam, unsigned char **rparam, + int infolevel) { char *p, *param = server->temp_buf; - __u16 date, time; - int off_date = 0, off_time = 2; - unsigned char *resp_data = NULL; - unsigned char *resp_param = NULL; - int resp_data_len = 0; - int resp_param_len = 0; int result; - retry: - WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ +retry: + WSET(param, 0, infolevel); DSET(param, 2, 0); - result = smb_encode_path(server, param+6, SMB_MAXNAMELEN+1, dir, NULL); + result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL); if (result < 0) goto out; p = param + 6 + result; result = smb_trans2_request(server, TRANSACT2_QPATHINFO, 0, NULL, p - param, param, - &resp_data_len, &resp_data, - &resp_param_len, &resp_param); + lrdata, rdata, + lrparam, rparam); if (result < 0) { if (smb_retry(server)) @@ -2238,13 +2519,36 @@ goto out; } result = -ENOENT; - if (resp_data_len < 22) + if (*lrdata < 22) { PARANOIA("not enough data for %s, len=%d\n", - ¶m[6], resp_data_len); + ¶m[6], *lrdata); goto out; } + result = 0; +out: + return result; +} + +static int +smb_proc_getattr_trans2_std(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *attr) +{ + u16 date, time; + int off_date = 0, off_time = 2; + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; + + int result = smb_proc_getattr_trans2(server, dir, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param, + SMB_INFO_STANDARD); + if (result < 0) + goto out; + /* * Kludge alert: Win 95 swaps the date and time field, * contrary to the CIFS docs and Win NT practice. @@ -2270,37 +2574,51 @@ #endif attr->f_size = DVAL(resp_data, 12); attr->attr = WVAL(resp_data, 20); - result = 0; out: return result; } -/* - * Note: called with the server locked - */ static int -smb_proc_do_getattr(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr) +smb_proc_getattr_trans2_all(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *attr) { - int result; - struct inode *inode = dir->d_inode; + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; + + int result = smb_proc_getattr_trans2(server, dir, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param, + SMB_QUERY_FILE_ALL_INFO); + if (result < 0) + goto out; + + attr->f_ctime = smb_ntutc2unixutc(LVAL(resp_data, 0)); + attr->f_atime = smb_ntutc2unixutc(LVAL(resp_data, 8)); + attr->f_mtime = smb_ntutc2unixutc(LVAL(resp_data, 16)); + /* change (24) */ + attr->attr = WVAL(resp_data, 32); + /* pad? (34) */ + /* allocated size (40) */ + attr->f_size = LVAL(resp_data, 48); - smb_init_dirent(server, fattr); +out: + return result; +} - /* - * Select whether to use core or trans2 getattr. - * Win 95 appears to break with the trans2 getattr. - */ - if (server->opt.protocol < SMB_PROTOCOL_LANMAN2 || - (server->mnt->flags & (SMB_MOUNT_OLDATTR|SMB_MOUNT_WIN95)) ) { - result = smb_proc_getattr_core(server, dir, fattr); - } else { - if (server->mnt->flags & SMB_MOUNT_DIRATTR) - result = smb_proc_getattr_ff(server, dir, fattr); - else - result = smb_proc_getattr_trans2(server, dir, fattr); - } +static int +smb_proc_getattr_95(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *attr) +{ + struct inode *inode = dir->d_inode; + int result; + + /* FIXME: why not use the "all" version? */ + result = smb_proc_getattr_trans2_std(server, dir, attr); + if (result < 0) + goto out; /* * None of the getattr versions here can make win9x return the right @@ -2308,16 +2626,14 @@ * A seek-to-end does return the right size, but we only need to do * that on files we have written. */ - if (server->mnt->flags & SMB_MOUNT_WIN95 && - inode && - SMB_I(inode)->flags & SMB_F_LOCALWRITE && + if (inode && SMB_I(inode)->flags & SMB_F_LOCALWRITE && smb_is_open(inode)) { __u16 fileid = SMB_I(inode)->fileid; - fattr->f_size = smb_proc_seek(server, fileid, 2, 0); + attr->f_size = smb_proc_seek(server, fileid, 2, 0); } - smb_finish_dirent(server, fattr); +out: return result; } @@ -2328,7 +2644,11 @@ int result; smb_lock_server(server); - result = smb_proc_do_getattr(server, dir, fattr); + + smb_init_dirent(server, fattr); + result = server->ops->getattr(server, dir, fattr); + smb_finish_dirent(server, fattr); + smb_unlock_server(server); return result; } @@ -2465,7 +2785,7 @@ retry: WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ DSET(param, 2, 0); - result = smb_encode_path(server, param+6, SMB_MAXNAMELEN+1, dir, NULL); + result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL); if (result < 0) goto out; p = param + 6 + result; @@ -2590,3 +2910,50 @@ smb_unlock_server(server); return result; } + + +static void +install_ops(struct smb_ops *dst, struct smb_ops *src) +{ + memcpy(dst, src, sizeof(void *) * SMB_OPS_NUM_STATIC); +} + +/* < LANMAN2 */ +static struct smb_ops smb_ops_core = +{ + read: smb_proc_read, + write: smb_proc_write, + readdir: smb_proc_readdir_short, + getattr: smb_proc_getattr_core, + truncate: smb_proc_trunc32, +}; + +/* LANMAN2, OS/2, others? */ +static struct smb_ops smb_ops_os2 = +{ + read: smb_proc_read, + write: smb_proc_write, + readdir: smb_proc_readdir_long, + getattr: smb_proc_getattr_trans2_std, + truncate: smb_proc_trunc32, +}; + +/* Win95, and possibly some NetApp versions too */ +static struct smb_ops smb_ops_win95 = +{ + read: smb_proc_read, /* does not support 12word readX */ + write: smb_proc_write, + readdir: smb_proc_readdir_long, + getattr: smb_proc_getattr_95, + truncate: smb_proc_trunc95, +}; + +/* Samba, NT4 and NT5 */ +static struct smb_ops smb_ops_winNT = +{ + read: smb_proc_readX, + write: smb_proc_writeX, + readdir: smb_proc_readdir_long, + getattr: smb_proc_getattr_trans2_all, + truncate: smb_proc_trunc64, +}; diff -Nru a/fs/smbfs/proto.h b/fs/smbfs/proto.h --- a/fs/smbfs/proto.h Thu Mar 7 18:17:37 2002 +++ b/fs/smbfs/proto.h Thu Mar 7 18:17:37 2002 @@ -1,5 +1,5 @@ /* - * Autogenerated with cproto on: Tue Oct 2 20:40:54 CEST 2001 + * Autogenerated with cproto on: Thu Nov 22 21:18:04 CET 2001 */ /* proc.c */ @@ -14,17 +14,13 @@ extern int smb_open(struct dentry *dentry, int wish); extern int smb_close(struct inode *ino); extern int smb_close_fileid(struct dentry *dentry, __u16 fileid); -extern int smb_proc_read(struct inode *inode, off_t offset, int count, char *data); -extern int smb_proc_write(struct inode *inode, off_t offset, int count, const char *data); extern int smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid); extern int smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry); extern int smb_proc_mkdir(struct dentry *dentry); extern int smb_proc_rmdir(struct dentry *dentry); extern int smb_proc_unlink(struct dentry *dentry); extern int smb_proc_flush(struct smb_sb_info *server, __u16 fileid); -extern int smb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length); extern void smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); -extern int smb_proc_readdir(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctl); extern int smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr); extern int smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr); extern int smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr); diff -Nru a/fs/super.c b/fs/super.c --- a/fs/super.c Thu Mar 7 18:17:44 2002 +++ b/fs/super.c Thu Mar 7 18:17:44 2002 @@ -355,6 +355,7 @@ } up_write(&s->s_umount); put_super(s); + yield(); return 0; } @@ -381,33 +382,87 @@ * remove_super - makes superblock unreachable * @s: superblock in question * - * Removes superblock from the lists, unlocks it, drop the reference - * and releases the hosting device. @s should have no active - * references by that time and after remove_super() it's essentially - * in rundown mode - all remaining references are temporary, no new - * reference of any sort are going to appear and all holders of - * temporary ones will eventually drop them. At that point superblock - * itself will be destroyed; all its contents is already gone. + * Removes superblock from the lists, unlocks it and drop the reference + * @s should have no active references by that time and after + * remove_super() it's essentially in rundown mode - all remaining + * references are temporary, no new reference of any sort are going + * to appear and all holders of temporary ones will eventually drop them. + * At that point superblock itself will be destroyed; all its contents + * is already gone. */ static void remove_super(struct super_block *s) { - kdev_t dev = s->s_dev; - struct block_device *bdev = s->s_bdev; - struct file_system_type *fs = s->s_type; - spin_lock(&sb_lock); list_del(&s->s_list); list_del(&s->s_instances); spin_unlock(&sb_lock); up_write(&s->s_umount); put_super(s); - put_filesystem(fs); - if (bdev) +} + +static void generic_shutdown_super(struct super_block *sb) +{ + struct dentry *root = sb->s_root; + struct super_operations *sop = sb->s_op; + + if (root) { + sb->s_root = NULL; + shrink_dcache_parent(root); + dput(root); + fsync_super(sb); + lock_super(sb); + lock_kernel(); + sb->s_flags &= ~MS_ACTIVE; + /* bad name - it should be evict_inodes() */ + invalidate_inodes(sb); + if (sop) { + if (sop->write_super && sb->s_dirt) + sop->write_super(sb); + if (sop->put_super) + sop->put_super(sb); + } + + /* Forget any remaining inodes */ + if (invalidate_inodes(sb)) { + printk("VFS: Busy inodes after unmount. " + "Self-destruct in 5 seconds. Have a nice day...\n"); + } + + unlock_kernel(); + unlock_super(sb); + } + remove_super(sb); +} + +static void shutdown_super(struct super_block *sb) +{ + struct file_system_type *fs = sb->s_type; + kdev_t dev = sb->s_dev; + struct block_device *bdev = sb->s_bdev; + + /* Need to clean after the sucker */ + if (fs->fs_flags & FS_LITTER && sb->s_root) + d_genocide(sb->s_root); + generic_shutdown_super(sb); + if (bdev) { + bd_release(bdev); blkdev_put(bdev, BDEV_FS); - else + } else put_anon_dev(dev); } +void kill_super(struct super_block *sb) +{ + struct file_system_type *fs = sb->s_type; + + if (!deactivate_super(sb)) + return; + + down_write(&sb->s_umount); + shutdown_super(sb); + put_filesystem(fs); +} + struct vfsmount *alloc_vfsmnt(char *name); void free_vfsmnt(struct vfsmount *mnt); @@ -445,19 +500,9 @@ * hold up the sync while mounting a device. (The newly * mounted device won't need syncing.) */ -void sync_supers(kdev_t dev) +void sync_supers(void) { struct super_block * sb; - - if (!kdev_none(dev)) { - sb = get_super(dev); - if (sb) { - if (sb->s_dirt) - write_super(sb); - drop_super(sb); - } - return; - } restart: spin_lock(&sb_lock); sb = sb_entry(super_blocks.next); @@ -656,8 +701,7 @@ /* What device it is? */ if (!dev_name || !*dev_name) return ERR_PTR(-EINVAL); - if (path_init(dev_name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd)) - error = path_walk(dev_name, &nd); + error = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); if (error) return ERR_PTR(error); inode = nd.dentry->d_inode; @@ -684,37 +728,40 @@ error = -EACCES; if (!(flags & MS_RDONLY) && is_read_only(dev)) goto out1; + error = bd_claim(bdev, fs_type); + if (error) + goto out1; error = -ENOMEM; s = alloc_super(); if (!s) - goto out1; + goto out2; error = -EBUSY; restart: spin_lock(&sb_lock); - list_for_each(p, &super_blocks) { + list_for_each(p, &fs_type->fs_supers) { struct super_block *old = sb_entry(p); if (old->s_bdev != bdev) continue; - if (old->s_type != fs_type || - ((flags ^ old->s_flags) & MS_RDONLY)) { - spin_unlock(&sb_lock); - destroy_super(s); - goto out1; - } if (!grab_super(old)) goto restart; destroy_super(s); + if ((flags ^ old->s_flags) & MS_RDONLY) { + up_write(&old->s_umount); + kill_super(old); + old = ERR_PTR(-EBUSY); + } + bd_release(bdev); blkdev_put(bdev, BDEV_FS); path_release(&nd); return old; } - s->s_dev = dev; s->s_bdev = bdev; - s->s_flags = flags; + s->s_dev = dev; insert_super(s, fs_type); + s->s_flags = flags; strncpy(s->s_id, bdevname(dev), sizeof(s->s_id)); error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); if (error) @@ -724,9 +771,11 @@ return s; failed: - deactivate_super(s); - remove_super(s); + up_write(&s->s_umount); + kill_super(s); goto out; +out2: + bd_release(bdev); out1: blkdev_put(bdev, BDEV_FS); out: @@ -748,8 +797,8 @@ error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); if (error) { - deactivate_super(s); - remove_super(s); + up_write(&s->s_umount); + kill_super(s); return ERR_PTR(error); } s->s_flags |= MS_ACTIVE; @@ -774,8 +823,8 @@ s->s_flags = flags; error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); if (error) { - deactivate_super(s); - remove_super(s); + up_write(&s->s_umount); + kill_super(s); return ERR_PTR(error); } s->s_flags |= MS_ACTIVE; @@ -814,45 +863,6 @@ out: put_filesystem(type); return (struct vfsmount *)sb; -} - -void kill_super(struct super_block *sb) -{ - struct dentry *root = sb->s_root; - struct file_system_type *fs = sb->s_type; - struct super_operations *sop = sb->s_op; - - if (!deactivate_super(sb)) - return; - - down_write(&sb->s_umount); - sb->s_root = NULL; - /* Need to clean after the sucker */ - if (fs->fs_flags & FS_LITTER) - d_genocide(root); - shrink_dcache_parent(root); - dput(root); - fsync_super(sb); - lock_super(sb); - lock_kernel(); - sb->s_flags &= ~MS_ACTIVE; - invalidate_inodes(sb); /* bad name - it should be evict_inodes() */ - if (sop) { - if (sop->write_super && sb->s_dirt) - sop->write_super(sb); - if (sop->put_super) - sop->put_super(sb); - } - - /* Forget any remaining inodes */ - if (invalidate_inodes(sb)) { - printk("VFS: Busy inodes after unmount. " - "Self-destruct in 5 seconds. Have a nice day...\n"); - } - - unlock_kernel(); - unlock_super(sb); - remove_super(sb); } struct vfsmount *kern_mount(struct file_system_type *type) diff -Nru a/fs/sysv/ChangeLog b/fs/sysv/ChangeLog --- a/fs/sysv/ChangeLog Thu Mar 7 18:17:36 2002 +++ b/fs/sysv/ChangeLog Thu Mar 7 18:17:36 2002 @@ -81,7 +81,7 @@ * super.c (sysv_read_super): Likewise. (v7_read_super): Likewise. -Sat Dec 15 2001 Christoph Hellwig +Sat Dec 15 2001 Christoph Hellwig * inode.c (sysv_read_inode): Mark inode as bad in case of failure. * super.c (complete_read_super): Check for bad root inode. @@ -90,13 +90,13 @@ * file.c (sysv_sync_file): Call fsync_inode_data_buffers. -Fri Oct 26 2001 Christoph Hellwig +Fri Oct 26 2001 Christoph Hellwig * dir.c, ialloc.c, namei.c, include/linux/sysv_fs_i.h: Implement per-Inode lookup offset cache. Modelled after Ted's ext2 patch. -Fri Oct 26 2001 Christoph Hellwig +Fri Oct 26 2001 Christoph Hellwig * inode.c, super.c, include/linux/sysv_fs.h, include/linux/sysv_fs_sb.h: diff -Nru a/fs/sysv/symlink.c b/fs/sysv/symlink.c --- a/fs/sysv/symlink.c Thu Mar 7 18:17:46 2002 +++ b/fs/sysv/symlink.c Thu Mar 7 18:17:46 2002 @@ -2,7 +2,7 @@ * linux/fs/sysv/symlink.c * * Handling of System V filesystem fast symlinks extensions. - * Aug 2001, Christoph Hellwig (hch@caldera.de) + * Aug 2001, Christoph Hellwig (hch@infradead.org) */ #include diff -Nru a/fs/udf/dir.c b/fs/udf/dir.c --- a/fs/udf/dir.c Thu Mar 7 18:17:41 2002 +++ b/fs/udf/dir.c Thu Mar 7 18:17:41 2002 @@ -224,7 +224,7 @@ if ( cfi.fileCharacteristics & FILE_PARENT ) { - iblock = udf_get_lb_pblock(dir->i_sb, UDF_I_LOCATION(filp->f_dentry->d_parent->d_inode), 0); + iblock = parent_ino(filp->f_dentry); flen = 2; memcpy(fname, "..", flen); dt_type = DT_DIR; diff -Nru a/fs/ufs/swab.h b/fs/ufs/swab.h --- a/fs/ufs/swab.h Thu Mar 7 18:17:46 2002 +++ b/fs/ufs/swab.h Thu Mar 7 18:17:46 2002 @@ -3,7 +3,7 @@ * * Copyright (C) 1997, 1998 Francois-Rene Rideau * Copyright (C) 1998 Jakub Jelinek - * Copyright (C) 2001 Christoph Hellwig + * Copyright (C) 2001 Christoph Hellwig */ #ifndef _UFS_SWAB_H diff -Nru a/include/asm-alpha/bitops.h b/include/asm-alpha/bitops.h --- a/include/asm-alpha/bitops.h Thu Mar 7 18:17:44 2002 +++ b/include/asm-alpha/bitops.h Thu Mar 7 18:17:44 2002 @@ -460,6 +460,27 @@ #ifdef __KERNEL__ +/* + * Every architecture must define this function. It's the fastest + * way of searching a 140-bit bitmap where the first 100 bits are + * unlikely to be set. It's guaranteed that at least one of the 140 + * bits is set. + */ +static inline unsigned long +sched_find_first_bit(unsigned long b[3]) +{ + unsigned long b0 = b[0], b1 = b[1], b2 = b[2]; + unsigned long ofs; + + ofs = (b1 ? 64 : 128); + b1 = (b1 ? b1 : b2); + ofs = (b0 ? 0 : ofs); + b0 = (b0 ? b0 : b1); + + return __ffs(b0) + ofs; +} + + #define ext2_set_bit __test_and_set_bit #define ext2_clear_bit __test_and_clear_bit #define ext2_test_bit test_bit diff -Nru a/include/asm-alpha/jensen.h b/include/asm-alpha/jensen.h --- a/include/asm-alpha/jensen.h Thu Mar 7 18:17:45 2002 +++ b/include/asm-alpha/jensen.h Thu Mar 7 18:17:45 2002 @@ -7,9 +7,6 @@ * Defines for the AlphaPC EISA IO and memory address space. */ -/* The Jensen is strange */ -#define AUX_IRQ (9) - /* * NOTE! The memory operations do not set any memory barriers, as it's * not needed for cases like a frame buffer that is essentially memory-like. diff -Nru a/include/asm-alpha/keyboard.h b/include/asm-alpha/keyboard.h --- a/include/asm-alpha/keyboard.h Thu Mar 7 18:17:36 2002 +++ b/include/asm-alpha/keyboard.h Thu Mar 7 18:17:36 2002 @@ -60,7 +60,8 @@ * Machine specific bits for the PS/2 driver */ -#define AUX_IRQ 12 +/* Jensen puts this at 9, everyone else at the standard 12. */ +#define AUX_IRQ (RTC_PORT(0) == 0x170 ? 9 : 12) #define aux_request_irq(hand, dev_id) \ request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id) diff -Nru a/include/asm-alpha/mmu_context.h b/include/asm-alpha/mmu_context.h --- a/include/asm-alpha/mmu_context.h Thu Mar 7 18:17:45 2002 +++ b/include/asm-alpha/mmu_context.h Thu Mar 7 18:17:45 2002 @@ -21,31 +21,6 @@ #include #endif -/* ??? This does not belong here. */ -/* - * Every architecture must define this function. It's the fastest - * way of searching a 168-bit bitmap where the first 128 bits are - * unlikely to be set. It's guaranteed that at least one of the 168 - * bits is set. - */ -#if MAX_RT_PRIO != 128 || MAX_PRIO > 192 -# error update this function. -#endif - -static inline int -sched_find_first_bit(unsigned long *b) -{ - unsigned long b0 = b[0], b1 = b[1], b2 = b[2]; - unsigned long offset = 128; - - if (unlikely(b0 | b1)) { - b2 = (b0 ? b0 : b1); - offset = (b0 ? 0 : 64); - } - - return __ffs(b2) + offset; -} - extern inline unsigned long __reload_thread(struct pcb_struct *pcb) diff -Nru a/include/asm-alpha/pgalloc.h b/include/asm-alpha/pgalloc.h --- a/include/asm-alpha/pgalloc.h Thu Mar 7 18:17:36 2002 +++ b/include/asm-alpha/pgalloc.h Thu Mar 7 18:17:36 2002 @@ -230,121 +230,66 @@ * used to allocate a kernel page table - this turns on ASN bits * if any. */ -#ifndef CONFIG_SMP -extern struct pgtable_cache_struct { - unsigned long *pgd_cache; - unsigned long *pmd_cache; - unsigned long *pte_cache; - unsigned long pgtable_cache_sz; -} quicklists; -#else -#include -#define quicklists cpu_data[smp_processor_id()] -#endif -#define pgd_quicklist (quicklists.pgd_cache) -#define pmd_quicklist (quicklists.pmd_cache) -#define pte_quicklist (quicklists.pte_cache) -#define pgtable_cache_size (quicklists.pgtable_cache_sz) - -#define pmd_populate(mm, pmd, pte) pmd_set(pmd, pte) -#define pgd_populate(mm, pgd, pmd) pgd_set(pgd, pmd) - -extern pgd_t *get_pgd_slow(void); - -static inline pgd_t *get_pgd_fast(void) -{ - unsigned long *ret; - - if ((ret = pgd_quicklist) != NULL) { - pgd_quicklist = (unsigned long *)(*ret); - ret[0] = 0; - pgtable_cache_size--; - } else - ret = (unsigned long *)get_pgd_slow(); - return (pgd_t *)ret; -} - -static inline void free_pgd_fast(pgd_t *pgd) -{ - *(unsigned long *)pgd = (unsigned long) pgd_quicklist; - pgd_quicklist = (unsigned long *) pgd; - pgtable_cache_size++; -} -static inline void free_pgd_slow(pgd_t *pgd) +static inline void +pmd_populate(struct mm_struct *mm, pmd_t *pmd, struct page *pte) { - free_page((unsigned long)pgd); + pmd_set(pmd, (pte_t *)((pte - mem_map) << PAGE_SHIFT)); } -static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address) +static inline void +pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte) { - pmd_t *ret = (pmd_t *)__get_free_page(GFP_KERNEL); - if (ret) - clear_page(ret); - return ret; + pmd_set(pmd, pte); } -static inline pmd_t *pmd_alloc_one_fast(struct mm_struct *mm, unsigned long address) +static inline void +pgd_populate(struct mm_struct *mm, pgd_t *pgd, pmd_t *pmd) { - unsigned long *ret; - - if ((ret = (unsigned long *)pte_quicklist) != NULL) { - pte_quicklist = (unsigned long *)(*ret); - ret[0] = 0; - pgtable_cache_size--; - } - return (pmd_t *)ret; + pgd_set(pgd, pmd); } -static inline void pmd_free_fast(pmd_t *pmd) +extern pgd_t *pgd_alloc(struct mm_struct *mm); + +static inline void +pgd_free(pgd_t *pgd) { - *(unsigned long *)pmd = (unsigned long) pte_quicklist; - pte_quicklist = (unsigned long *) pmd; - pgtable_cache_size++; + free_page((unsigned long)pgd); } -static inline void pmd_free_slow(pmd_t *pmd) +static inline pmd_t * +pmd_alloc_one(struct mm_struct *mm, unsigned long address) { - free_page((unsigned long)pmd); + pmd_t *ret = (pmd_t *)__get_free_page(GFP_KERNEL); + if (ret) + clear_page(ret); + return ret; } -static inline pte_t *pte_alloc_one(struct mm_struct *mm, unsigned long address) +static inline void +pmd_free(pmd_t *pmd) { - pte_t *pte = (pte_t *)__get_free_page(GFP_KERNEL); - if (pte) - clear_page(pte); - return pte; + free_page((unsigned long)pmd); } -static inline pte_t *pte_alloc_one_fast(struct mm_struct *mm, unsigned long address) -{ - unsigned long *ret; +extern pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr); - if ((ret = (unsigned long *)pte_quicklist) != NULL) { - pte_quicklist = (unsigned long *)(*ret); - ret[0] = 0; - pgtable_cache_size--; - } - return (pte_t *)ret; +static inline void +pte_free_kernel(pte_t *pte) +{ + free_page((unsigned long)pte); } -static inline void pte_free_fast(pte_t *pte) +static inline struct page * +pte_alloc_one(struct mm_struct *mm, unsigned long addr) { - *(unsigned long *)pte = (unsigned long) pte_quicklist; - pte_quicklist = (unsigned long *) pte; - pgtable_cache_size++; + return virt_to_page(pte_alloc_one_kernel(mm, addr)); } -static inline void pte_free_slow(pte_t *pte) +static inline void +pte_free(struct page *page) { - free_page((unsigned long)pte); + __free_page(page); } - -#define pte_free(pte) pte_free_fast(pte) -#define pmd_free(pmd) pmd_free_fast(pmd) -#define pgd_free(pgd) free_pgd_fast(pgd) -#define pgd_alloc(mm) get_pgd_fast() - -extern int do_check_pgt_cache(int, int); #endif /* _ALPHA_PGALLOC_H */ diff -Nru a/include/asm-alpha/pgtable.h b/include/asm-alpha/pgtable.h --- a/include/asm-alpha/pgtable.h Thu Mar 7 18:17:41 2002 +++ b/include/asm-alpha/pgtable.h Thu Mar 7 18:17:41 2002 @@ -248,8 +248,13 @@ }) #endif -extern inline unsigned long pmd_page(pmd_t pmd) -{ return PAGE_OFFSET + ((pmd_val(pmd) & _PFN_MASK) >> (32-PAGE_SHIFT)); } +extern inline unsigned long +pmd_page_kernel(pmd_t pmd) +{ + return ((pmd_val(pmd) & _PFN_MASK) >> (32-PAGE_SHIFT)) + PAGE_OFFSET; +} + +#define pmd_page(pmd) (mem_map + ((pmd_val(pmd) & _PFN_MASK) >> 32)) extern inline unsigned long pgd_page(pgd_t pgd) { return PAGE_OFFSET + ((pgd_val(pgd) & _PFN_MASK) >> (32-PAGE_SHIFT)); } @@ -306,10 +311,16 @@ } /* Find an entry in the third-level page table.. */ -extern inline pte_t * pte_offset(pmd_t * dir, unsigned long address) +extern inline pte_t * pte_offset_kernel(pmd_t * dir, unsigned long address) { - return (pte_t *) pmd_page(*dir) + ((address >> PAGE_SHIFT) & (PTRS_PER_PAGE - 1)); + return (pte_t *) pmd_page_kernel(*dir) + + ((address >> PAGE_SHIFT) & (PTRS_PER_PAGE - 1)); } + +#define pte_offset_map(dir,addr) pte_offset_kernel((dir),(addr)) +#define pte_offset_map_nested(dir,addr) pte_offset_kernel((dir),(addr)) +#define pte_unmap(pte) do { } while (0) +#define pte_unmap_nested(pte) do { } while (0) extern pgd_t swapper_pg_dir[1024]; diff -Nru a/include/asm-alpha/smp.h b/include/asm-alpha/smp.h --- a/include/asm-alpha/smp.h Thu Mar 7 18:17:42 2002 +++ b/include/asm-alpha/smp.h Thu Mar 7 18:17:42 2002 @@ -29,10 +29,6 @@ unsigned long last_asn; int need_new_asn; int asn_lock; - unsigned long *pgd_cache; - unsigned long *pmd_cache; - unsigned long *pte_cache; - unsigned long pgtable_cache_sz; unsigned long ipi_count; unsigned long irq_attempt[NR_IRQS]; unsigned long prof_multiplier; diff -Nru a/include/asm-alpha/spinlock.h b/include/asm-alpha/spinlock.h --- a/include/asm-alpha/spinlock.h Thu Mar 7 18:17:45 2002 +++ b/include/asm-alpha/spinlock.h Thu Mar 7 18:17:45 2002 @@ -121,8 +121,8 @@ " bne %1,6b\n" " br 1b\n" ".previous" - : "=m" (*(volatile int *)lock), "=&r" (regx) - : "0" (*(volatile int *)lock) : "memory"); + : "=m" (*lock), "=&r" (regx) + : "0" (*lock) : "memory"); } static inline void _raw_read_lock(rwlock_t * lock) @@ -141,8 +141,8 @@ " blbs %1,6b\n" " br 1b\n" ".previous" - : "=m" (*(volatile int *)lock), "=&r" (regx) - : "m" (*(volatile int *)lock) : "memory"); + : "=m" (*lock), "=&r" (regx) + : "m" (*lock) : "memory"); } #endif /* CONFIG_DEBUG_RWLOCK */ @@ -164,8 +164,8 @@ ".subsection 2\n" "6: br 1b\n" ".previous" - : "=m" (*(volatile int *)lock), "=&r" (regx) - : "m" (*(volatile int *)lock) : "memory"); + : "=m" (*lock), "=&r" (regx) + : "m" (*lock) : "memory"); } #endif /* _ALPHA_SPINLOCK_H */ diff -Nru a/include/asm-alpha/system.h b/include/asm-alpha/system.h --- a/include/asm-alpha/system.h Thu Mar 7 18:17:37 2002 +++ b/include/asm-alpha/system.h Thu Mar 7 18:17:37 2002 @@ -131,15 +131,14 @@ #define __halt() __asm__ __volatile__ ("call_pal %0 #halt" : : "i" (PAL_halt)) #define prepare_to_switch() do { } while(0) -#define switch_to(prev,next,last) \ -do { \ - unsigned long pcbb; \ - pcbb = virt_to_phys(&(next)->thread_info->pcb); \ - (last) = alpha_switch_to(pcbb, (prev)); \ - check_mmu_context(); \ +#define switch_to(prev,next) \ +do { \ + alpha_switch_to(virt_to_phys(&(next)->thread_info->pcb), (prev)); \ + check_mmu_context(); \ } while (0) -extern struct task_struct* alpha_switch_to(unsigned long, struct task_struct*); +struct task_struct; +extern void alpha_switch_to(unsigned long, struct task_struct*); #define mb() \ __asm__ __volatile__("mb": : :"memory") @@ -368,7 +367,59 @@ * it must clobber "memory" (also for interrupts in UP). */ -extern __inline__ unsigned long +static inline unsigned long +__xchg_u8(volatile char *m, unsigned long val) +{ + unsigned long ret, tmp, addr64; + + __asm__ __volatile__( + " andnot %4,7,%3\n" + " insbl %1,%4,%1\n" + "1: ldq_l %2,0(%3)\n" + " extbl %2,%4,%0\n" + " mskbl %2,%4,%2\n" + " or %1,%2,%2\n" + " stq_c %2,0(%3)\n" + " beq %2,2f\n" +#ifdef CONFIG_SMP + " mb\n" +#endif + ".subsection 2\n" + "2: br 1b\n" + ".previous" + : "=&r" (ret), "=&r" (val), "=&r" (tmp), "=&r" (addr64) + : "r" ((long)m), "1" (val) : "memory"); + + return ret; +} + +static inline unsigned long +__xchg_u16(volatile short *m, unsigned long val) +{ + unsigned long ret, tmp, addr64; + + __asm__ __volatile__( + " andnot %4,7,%3\n" + " inswl %1,%4,%1\n" + "1: ldq_l %2,0(%3)\n" + " extwl %2,%4,%0\n" + " mskwl %2,%4,%2\n" + " or %1,%2,%2\n" + " stq_c %2,0(%3)\n" + " beq %2,2f\n" +#ifdef CONFIG_SMP + " mb\n" +#endif + ".subsection 2\n" + "2: br 1b\n" + ".previous" + : "=&r" (ret), "=&r" (val), "=&r" (tmp), "=&r" (addr64) + : "r" ((long)m), "1" (val) : "memory"); + + return ret; +} + +static inline unsigned long __xchg_u32(volatile int *m, unsigned long val) { unsigned long dummy; @@ -390,7 +441,7 @@ return val; } -extern __inline__ unsigned long +static inline unsigned long __xchg_u64(volatile long *m, unsigned long val) { unsigned long dummy; @@ -416,10 +467,14 @@ if something tries to do an invalid xchg(). */ extern void __xchg_called_with_bad_pointer(void); -static __inline__ unsigned long +static inline unsigned long __xchg(volatile void *ptr, unsigned long x, int size) { switch (size) { + case 1: + return __xchg_u8(ptr, x); + case 2: + return __xchg_u16(ptr, x); case 4: return __xchg_u32(ptr, x); case 8: @@ -451,7 +506,65 @@ #define __HAVE_ARCH_CMPXCHG 1 -extern __inline__ unsigned long +static inline unsigned long +__cmpxchg_u8(volatile char *m, long old, long new) +{ + unsigned long prev, tmp, cmp, addr64; + + __asm__ __volatile__( + " andnot %5,7,%4\n" + " insbl %1,%5,%1\n" + "1: ldq_l %2,0(%4)\n" + " extbl %2,%5,%0\n" + " cmpeq %0,%6,%3\n" + " beq %3,2f\n" + " mskbl %2,%5,%2\n" + " or %1,%2,%2\n" + " stq_c %2,0(%4)\n" + " beq %2,3f\n" +#ifdef CONFIG_SMP + " mb\n" +#endif + "2:\n" + ".subsection 2\n" + "3: br 1b\n" + ".previous" + : "=&r" (prev), "=&r" (new), "=&r" (tmp), "=&r" (cmp), "=&r" (addr64) + : "r" ((long)m), "Ir" (old), "1" (new) : "memory"); + + return prev; +} + +static inline unsigned long +__cmpxchg_u16(volatile short *m, long old, long new) +{ + unsigned long prev, tmp, cmp, addr64; + + __asm__ __volatile__( + " andnot %5,7,%4\n" + " inswl %1,%5,%1\n" + "1: ldq_l %2,0(%4)\n" + " extwl %2,%5,%0\n" + " cmpeq %0,%6,%3\n" + " beq %3,2f\n" + " mskwl %2,%5,%2\n" + " or %1,%2,%2\n" + " stq_c %2,0(%4)\n" + " beq %2,3f\n" +#ifdef CONFIG_SMP + " mb\n" +#endif + "2:\n" + ".subsection 2\n" + "3: br 1b\n" + ".previous" + : "=&r" (prev), "=&r" (new), "=&r" (tmp), "=&r" (cmp), "=&r" (addr64) + : "r" ((long)m), "Ir" (old), "1" (new) : "memory"); + + return prev; +} + +static inline unsigned long __cmpxchg_u32(volatile int *m, int old, int new) { unsigned long prev, cmp; @@ -476,7 +589,7 @@ return prev; } -extern __inline__ unsigned long +static inline unsigned long __cmpxchg_u64(volatile long *m, unsigned long old, unsigned long new) { unsigned long prev, cmp; @@ -505,10 +618,14 @@ if something tries to do an invalid cmpxchg(). */ extern void __cmpxchg_called_with_bad_pointer(void); -static __inline__ unsigned long +static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) { switch (size) { + case 1: + return __cmpxchg_u8(ptr, old, new); + case 2: + return __cmpxchg_u16(ptr, old, new); case 4: return __cmpxchg_u32(ptr, old, new); case 8: diff -Nru a/include/asm-arm/arch-adifcc/irq.h b/include/asm-arm/arch-adifcc/irq.h --- a/include/asm-arm/arch-adifcc/irq.h Thu Mar 7 18:17:39 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,13 +0,0 @@ -/* - * linux/include/asm-arm/arch-adifcc/irq.h - * - * Copyright (C) 2001 MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#define fixup_irq(irq) (irq) - - diff -Nru a/include/asm-arm/arch-adifcc/irqs.h b/include/asm-arm/arch-adifcc/irqs.h --- a/include/asm-arm/arch-adifcc/irqs.h Thu Mar 7 18:17:44 2002 +++ b/include/asm-arm/arch-adifcc/irqs.h Thu Mar 7 18:17:44 2002 @@ -18,11 +18,3 @@ #define NR_IRQS NR_XS80200_IRQS #define IRQ_XSCALE_PMU IRQ_XS80200_PMU - -#ifdef CONFIG_XSCALE_ADI_EVB - -/* Interrupts available on the ADI Eval Board */ - -#endif - - diff -Nru a/include/asm-arm/arch-adifcc/serial.h b/include/asm-arm/arch-adifcc/serial.h --- a/include/asm-arm/arch-adifcc/serial.h Thu Mar 7 18:17:45 2002 +++ b/include/asm-arm/arch-adifcc/serial.h Thu Mar 7 18:17:45 2002 @@ -5,7 +5,7 @@ * * Copyright (c) 2001 MontaVista Software, Inc. */ - +#include /* * This assumes you have a 1.8432 MHz clock for your UART. diff -Nru a/include/asm-arm/arch-anakin/ide.h b/include/asm-arm/arch-anakin/ide.h --- a/include/asm-arm/arch-anakin/ide.h Thu Mar 7 18:17:36 2002 +++ b/include/asm-arm/arch-anakin/ide.h Thu Mar 7 18:17:36 2002 @@ -7,7 +7,6 @@ * (jonm@bluemug.com). */ -#include #include #include diff -Nru a/include/asm-arm/arch-anakin/irq.h b/include/asm-arm/arch-anakin/irq.h --- a/include/asm-arm/arch-anakin/irq.h Thu Mar 7 18:17:45 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,19 +0,0 @@ -/* - * linux/include/asm-arm/arch-anakin/irq.h - * - * Copyright (C) 2001 Aleph One Ltd. for Acunia N.V. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Changelog: - * 10-Apr-2001 TTC Created - */ - -#ifndef __ASM_ARCH_IRQ_H -#define __ASM_ARCH_IRQ_H - -#define fixup_irq(i) i - -#endif diff -Nru a/include/asm-arm/arch-anakin/time.h b/include/asm-arm/arch-anakin/time.h --- a/include/asm-arm/arch-anakin/time.h Thu Mar 7 18:17:45 2002 +++ b/include/asm-arm/arch-anakin/time.h Thu Mar 7 18:17:45 2002 @@ -24,7 +24,7 @@ { timer_irq.handler = anakin_timer_interrupt; timer_irq.flags = SA_INTERRUPT; - setup_arm_irq(IRQ_TICK, &timer_irq); + setup_irq(IRQ_TICK, &timer_irq); } #endif diff -Nru a/include/asm-arm/arch-anakin/uncompress.h b/include/asm-arm/arch-anakin/uncompress.h --- a/include/asm-arm/arch-anakin/uncompress.h Thu Mar 7 18:17:46 2002 +++ b/include/asm-arm/arch-anakin/uncompress.h Thu Mar 7 18:17:46 2002 @@ -14,6 +14,7 @@ #ifndef __ASM_ARCH_UNCOMPRESS_H #define __ASM_ARCH_UNCOMPRESS_H +#include #include #include diff -Nru a/include/asm-arm/arch-arc/irq.h b/include/asm-arm/arch-arc/irq.h --- a/include/asm-arm/arch-arc/irq.h Thu Mar 7 18:17:39 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,10 +0,0 @@ -/* - * linux/include/asm-arm/arch-arc/irq.h - * - * Copyright (C) 1996 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#define fixup_irq(x) (x) diff -Nru a/include/asm-arm/arch-arc/time.h b/include/asm-arm/arch-arc/time.h --- a/include/asm-arm/arch-arc/time.h Thu Mar 7 18:17:37 2002 +++ b/include/asm-arm/arch-arc/time.h Thu Mar 7 18:17:37 2002 @@ -30,5 +30,5 @@ timer_irq.handler = timer_interrupt; - setup_arm_irq(IRQ_TIMER, &timer_irq); + setup_irq(IRQ_TIMER, &timer_irq); } diff -Nru a/include/asm-arm/arch-cl7500/time.h b/include/asm-arm/arch-cl7500/time.h --- a/include/asm-arm/arch-cl7500/time.h Thu Mar 7 18:17:38 2002 +++ b/include/asm-arm/arch-cl7500/time.h Thu Mar 7 18:17:38 2002 @@ -37,5 +37,5 @@ timer_irq.handler = timer_interrupt; - setup_arm_irq(IRQ_TIMER, &timer_irq); + setup_irq(IRQ_TIMER, &timer_irq); } diff -Nru a/include/asm-arm/arch-clps711x/irq.h b/include/asm-arm/arch-clps711x/irq.h --- a/include/asm-arm/arch-clps711x/irq.h Thu Mar 7 18:17:41 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,20 +0,0 @@ -/* - * linux/include/asm-arm/arch-clps711x/irq.h - * - * Copyright (C) 2000 Deep Blue Solutions Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#define fixup_irq(i) (i) diff -Nru a/include/asm-arm/arch-clps711x/memory.h b/include/asm-arm/arch-clps711x/memory.h --- a/include/asm-arm/arch-clps711x/memory.h Thu Mar 7 18:17:37 2002 +++ b/include/asm-arm/arch-clps711x/memory.h Thu Mar 7 18:17:37 2002 @@ -20,6 +20,8 @@ #ifndef __ASM_ARCH_MMU_H #define __ASM_ARCH_MMU_H +#include + /* * Task size: 3GB */ diff -Nru a/include/asm-arm/arch-clps711x/time.h b/include/asm-arm/arch-clps711x/time.h --- a/include/asm-arm/arch-clps711x/time.h Thu Mar 7 18:17:46 2002 +++ b/include/asm-arm/arch-clps711x/time.h Thu Mar 7 18:17:46 2002 @@ -39,5 +39,5 @@ { clps711x_setup_timer(); timer_irq.handler = p720t_timer_interrupt; - setup_arm_irq(IRQ_TC2OI, &timer_irq); + setup_irq(IRQ_TC2OI, &timer_irq); } diff -Nru a/include/asm-arm/arch-ebsa110/irq.h b/include/asm-arm/arch-ebsa110/irq.h --- a/include/asm-arm/arch-ebsa110/irq.h Thu Mar 7 18:17:40 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,13 +0,0 @@ -/* - * linux/include/asm-arm/arch-ebsa110/irq.h - * - * Copyright (C) 1996-1998 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Changelog: - * 22-08-1998 RMK Restructured IRQ routines - */ -#define fixup_irq(i) (i) diff -Nru a/include/asm-arm/arch-ebsa110/time.h b/include/asm-arm/arch-ebsa110/time.h --- a/include/asm-arm/arch-ebsa110/time.h Thu Mar 7 18:17:40 2002 +++ b/include/asm-arm/arch-ebsa110/time.h Thu Mar 7 18:17:40 2002 @@ -39,7 +39,7 @@ timer_irq.handler = timer_interrupt; - setup_arm_irq(IRQ_EBSA110_TIMER0, &timer_irq); + setup_irq(IRQ_EBSA110_TIMER0, &timer_irq); } diff -Nru a/include/asm-arm/arch-ebsa285/irq.h b/include/asm-arm/arch-ebsa285/irq.h --- a/include/asm-arm/arch-ebsa285/irq.h Thu Mar 7 18:17:46 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,33 +0,0 @@ -/* - * linux/include/asm-arm/arch-ebsa285/irq.h - * - * Copyright (C) 1996-1998 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Changelog: - * 22-Aug-1998 RMK Restructured IRQ routines - * 03-Sep-1998 PJB Merged CATS support - * 20-Jan-1998 RMK Started merge of EBSA286, CATS and NetWinder - * 26-Jan-1999 PJB Don't use IACK on CATS - * 16-Mar-1999 RMK Added autodetect of ISA PICs - */ -#include -#include -#include -#include - -int isa_irq = -1; - -static inline int fixup_irq(unsigned int irq) -{ -#ifdef PCIIACK_BASE - if (irq == isa_irq) - irq = *(unsigned char *)PCIIACK_BASE; -#endif - - return irq; -} - diff -Nru a/include/asm-arm/arch-ebsa285/keyboard.h b/include/asm-arm/arch-ebsa285/keyboard.h --- a/include/asm-arm/arch-ebsa285/keyboard.h Thu Mar 7 18:17:41 2002 +++ b/include/asm-arm/arch-ebsa285/keyboard.h Thu Mar 7 18:17:41 2002 @@ -6,6 +6,7 @@ * Copyright (C) 1998-2001 Russell King * (C) 1998 Phil Blundell */ +#include #include #include #include diff -Nru a/include/asm-arm/arch-ebsa285/time.h b/include/asm-arm/arch-ebsa285/time.h --- a/include/asm-arm/arch-ebsa285/time.h Thu Mar 7 18:17:45 2002 +++ b/include/asm-arm/arch-ebsa285/time.h Thu Mar 7 18:17:45 2002 @@ -270,5 +270,5 @@ timer_irq.handler = isa_timer_interrupt; irq = IRQ_ISA_TIMER; } - setup_arm_irq(irq, &timer_irq); + setup_irq(irq, &timer_irq); } diff -Nru a/include/asm-arm/arch-epxa10db/irq.h b/include/asm-arm/arch-epxa10db/irq.h --- a/include/asm-arm/arch-epxa10db/irq.h Thu Mar 7 18:17:36 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,20 +0,0 @@ -/* - * linux/include/asm-arm/arch-epxa10/irq.h - * - * Copyright (C) 1999 ARM Limited - * - * 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 fixup_irq(i) (i) diff -Nru a/include/asm-arm/arch-epxa10db/time.h b/include/asm-arm/arch-epxa10db/time.h --- a/include/asm-arm/arch-epxa10db/time.h Thu Mar 7 18:17:44 2002 +++ b/include/asm-arm/arch-epxa10db/time.h Thu Mar 7 18:17:44 2002 @@ -17,7 +17,6 @@ * 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 @@ -49,7 +48,7 @@ /* * Make irqs happen for the system timer */ - setup_arm_irq(IRQ_TIMER0, &timer_irq); + setup_irq(IRQ_TIMER0, &timer_irq); /* Start the timer */ *TIMER0_LIMIT(IO_ADDRESS(EXC_TIMER00_BASE))=(unsigned int)(EXC_AHB2_CLK_FREQUENCY/200); diff -Nru a/include/asm-arm/arch-epxa10db/uncompress.h b/include/asm-arm/arch-epxa10db/uncompress.h --- a/include/asm-arm/arch-epxa10db/uncompress.h Thu Mar 7 18:17:42 2002 +++ b/include/asm-arm/arch-epxa10db/uncompress.h Thu Mar 7 18:17:42 2002 @@ -29,14 +29,18 @@ static void puts(const char *s) { while (*s) { - while ((*UART_TSR(IO_ADDRESS(EXC_UART00_BASE)) & UART_TSR_TX_LEVEL_MSK)==15); + while ((*UART_TSR(EXC_UART00_BASE) & + UART_TSR_TX_LEVEL_MSK)==15) + barrier(); - *UART_TD(IO_ADDRESS(EXC_UART00_BASE)) = *s; + *UART_TD(EXC_UART00_BASE) = *s; if (*s == '\n') { - while ((*UART_TSR(IO_ADDRESS(EXC_UART00_BASE)) & UART_TSR_TX_LEVEL_MSK)==15); + while ((*UART_TSR(EXC_UART00_BASE) & + UART_TSR_TX_LEVEL_MSK)==15) + barrier(); - *UART_TD(IO_ADDRESS(EXC_UART00_BASE)) = '\r'; + *UART_TD(EXC_UART00_BASE) = '\r'; } s++; } diff -Nru a/include/asm-arm/arch-integrator/irq.h b/include/asm-arm/arch-integrator/irq.h --- a/include/asm-arm/arch-integrator/irq.h Thu Mar 7 18:17:39 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,20 +0,0 @@ -/* - * linux/include/asm-arm/arch-integrator/irq.h - * - * Copyright (C) 1999 ARM Limited - * - * 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 fixup_irq(i) (i) diff -Nru a/include/asm-arm/arch-integrator/time.h b/include/asm-arm/arch-integrator/time.h --- a/include/asm-arm/arch-integrator/time.h Thu Mar 7 18:17:40 2002 +++ b/include/asm-arm/arch-integrator/time.h Thu Mar 7 18:17:40 2002 @@ -135,6 +135,6 @@ /* * Make irqs happen for the system timer */ - setup_arm_irq(IRQ_TIMERINT1, &timer_irq); + setup_irq(IRQ_TIMERINT1, &timer_irq); gettimeoffset = integrator_gettimeoffset; } diff -Nru a/include/asm-arm/arch-iop310/irq.h b/include/asm-arm/arch-iop310/irq.h --- a/include/asm-arm/arch-iop310/irq.h Thu Mar 7 18:17:45 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,13 +0,0 @@ -/* - * linux/include/asm-arm/arch-iop80310/irq.h - * - * Copyright (C) 2001 MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#define fixup_irq(irq) (irq) - - diff -Nru a/include/asm-arm/arch-iop310/irqs.h b/include/asm-arm/arch-iop310/irqs.h --- a/include/asm-arm/arch-iop310/irqs.h Thu Mar 7 18:17:42 2002 +++ b/include/asm-arm/arch-iop310/irqs.h Thu Mar 7 18:17:42 2002 @@ -11,7 +11,7 @@ * 06/13/01: Added 80310 on-chip interrupt sources * */ - +#include /* * XS80200 specific IRQs diff -Nru a/include/asm-arm/arch-iop310/memory.h b/include/asm-arm/arch-iop310/memory.h --- a/include/asm-arm/arch-iop310/memory.h Thu Mar 7 18:17:41 2002 +++ b/include/asm-arm/arch-iop310/memory.h Thu Mar 7 18:17:41 2002 @@ -5,6 +5,7 @@ #ifndef __ASM_ARCH_MEMORY_H #define __ASM_ARCH_MEMORY_H +#include /* * Task size: 3GB diff -Nru a/include/asm-arm/arch-iop310/serial.h b/include/asm-arm/arch-iop310/serial.h --- a/include/asm-arm/arch-iop310/serial.h Thu Mar 7 18:17:46 2002 +++ b/include/asm-arm/arch-iop310/serial.h Thu Mar 7 18:17:46 2002 @@ -1,7 +1,7 @@ /* * include/asm-arm/arch-iop310/serial.h */ - +#include /* * This assumes you have a 1.8432 MHz clock for your UART. diff -Nru a/include/asm-arm/arch-iop310/timex.h b/include/asm-arm/arch-iop310/timex.h --- a/include/asm-arm/arch-iop310/timex.h Thu Mar 7 18:17:36 2002 +++ b/include/asm-arm/arch-iop310/timex.h Thu Mar 7 18:17:36 2002 @@ -3,7 +3,7 @@ * * IOP310 architecture timex specifications */ - +#include #ifdef CONFIG_ARCH_IQ80310 diff -Nru a/include/asm-arm/arch-iop310/uncompress.h b/include/asm-arm/arch-iop310/uncompress.h --- a/include/asm-arm/arch-iop310/uncompress.h Thu Mar 7 18:17:45 2002 +++ b/include/asm-arm/arch-iop310/uncompress.h Thu Mar 7 18:17:45 2002 @@ -1,6 +1,7 @@ /* * linux/include/asm-arm/arch-iop80310/uncompress.h */ +#include #ifdef CONFIG_ARCH_IQ80310 #define UART1_BASE ((volatile unsigned char *)0xfe800000) diff -Nru a/include/asm-arm/arch-l7200/irq.h b/include/asm-arm/arch-l7200/irq.h --- a/include/asm-arm/arch-l7200/irq.h Thu Mar 7 18:17:45 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -/* - * include/asm-arm/arch-l7200/irq.h - * - * Copyright (C) 2000 Rob Scott (rscott@mtrob.fdns.ne - * Steve Hill (sjhill@cotw.com) - * - * Changelog: - * 01-02-2000 RS Created l7200 version, derived from ebsa110 code - * 04-15-2000 RS Made dependent on hardware.h - * 05-05-2000 SJH Complete rewrite - */ -#define fixup_irq(x) (x) diff -Nru a/include/asm-arm/arch-l7200/time.h b/include/asm-arm/arch-l7200/time.h --- a/include/asm-arm/arch-l7200/time.h Thu Mar 7 18:17:37 2002 +++ b/include/asm-arm/arch-l7200/time.h Thu Mar 7 18:17:37 2002 @@ -58,7 +58,7 @@ timer_irq.handler = timer_interrupt; - setup_arm_irq(IRQ_RTC_TICK, &timer_irq); + setup_irq(IRQ_RTC_TICK, &timer_irq); RTC_RTCCR = RTC_RATE_128 | RTC_EN_TIC; /* Set rate and enable timer */ } diff -Nru a/include/asm-arm/arch-nexuspci/irq.h b/include/asm-arm/arch-nexuspci/irq.h --- a/include/asm-arm/arch-nexuspci/irq.h Thu Mar 7 18:17:36 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,14 +0,0 @@ -/* - * include/asm-arm/arch-nexuspci/irq.h - * - * Copyright (C) 1998, 1999, 2000 Philip Blundell - */ - -/* - * 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. - */ -#define fixup_irq(x) (x) - diff -Nru a/include/asm-arm/arch-nexuspci/time.h b/include/asm-arm/arch-nexuspci/time.h --- a/include/asm-arm/arch-nexuspci/time.h Thu Mar 7 18:17:41 2002 +++ b/include/asm-arm/arch-nexuspci/time.h Thu Mar 7 18:17:41 2002 @@ -55,5 +55,5 @@ timer_irq.handler = timer_interrupt; timer_irq.flags = SA_SHIRQ; - setup_arm_irq(IRQ_TIMER, &timer_irq); + setup_irq(IRQ_TIMER, &timer_irq); } diff -Nru a/include/asm-arm/arch-rpc/irq.h b/include/asm-arm/arch-rpc/irq.h --- a/include/asm-arm/arch-rpc/irq.h Thu Mar 7 18:17:46 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,14 +0,0 @@ -/* - * linux/include/asm-arm/arch-rpc/irq.h - * - * Copyright (C) 1996 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Changelog: - * 10-10-1996 RMK Brought up to date with arch-sa110eval - * 22-08-1998 RMK Restructured IRQ routines - */ -#define fixup_irq(x) (x) diff -Nru a/include/asm-arm/arch-rpc/time.h b/include/asm-arm/arch-rpc/time.h --- a/include/asm-arm/arch-rpc/time.h Thu Mar 7 18:17:38 2002 +++ b/include/asm-arm/arch-rpc/time.h Thu Mar 7 18:17:38 2002 @@ -30,5 +30,5 @@ timer_irq.handler = timer_interrupt; - setup_arm_irq(IRQ_TIMER, &timer_irq); + setup_irq(IRQ_TIMER, &timer_irq); } diff -Nru a/include/asm-arm/arch-sa1100/assabet.h b/include/asm-arm/arch-sa1100/assabet.h --- a/include/asm-arm/arch-sa1100/assabet.h Thu Mar 7 18:17:36 2002 +++ b/include/asm-arm/arch-sa1100/assabet.h Thu Mar 7 18:17:36 2002 @@ -12,6 +12,8 @@ #ifndef __ASM_ARCH_ASSABET_H #define __ASM_ARCH_ASSABET_H +#include + /* System Configuration Register flags */ #define ASSABET_SCR_SDRAM_LOW (1<<2) /* SDRAM size (low bit) */ @@ -60,7 +62,7 @@ #ifdef CONFIG_SA1100_ASSABET extern void ASSABET_BCR_frob(unsigned int mask, unsigned int set); #else -#define ASSABET_BCR_frob(x) do { } while (0) +#define ASSABET_BCR_frob(x,y) do { } while (0) #endif #define ASSABET_BCR_set(x) ASSABET_BCR_frob((x), (x)) diff -Nru a/include/asm-arm/arch-sa1100/badge4.h b/include/asm-arm/arch-sa1100/badge4.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-arm/arch-sa1100/badge4.h Thu Mar 7 18:17:47 2002 @@ -0,0 +1,74 @@ +/* + * linux/include/asm-arm/arch-sa1100/badge4.h + * + * Tim Connors + * Christopher Hoover + * + * Copyright (C) 2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __ASM_ARCH_HARDWARE_H +#error "include instead" +#endif + +#define BADGE4_SA1111_BASE (0x48000000) + +/* GPIOs on the BadgePAD 4 */ +#define BADGE4_GPIO_INT_1111 GPIO_GPIO0 /* SA-1111 IRQ */ + +#define BADGE4_GPIO_INT_VID GPIO_GPIO1 /* Video expansion */ +#define BADGE4_GPIO_LGP2 GPIO_GPIO2 /* GPIO_LDD8 */ +#define BADGE4_GPIO_LGP3 GPIO_GPIO3 /* GPIO_LDD9 */ +#define BADGE4_GPIO_LGP4 GPIO_GPIO4 /* GPIO_LDD10 */ +#define BADGE4_GPIO_LGP5 GPIO_GPIO5 /* GPIO_LDD11 */ +#define BADGE4_GPIO_LGP6 GPIO_GPIO6 /* GPIO_LDD12 */ +#define BADGE4_GPIO_LGP7 GPIO_GPIO7 /* GPIO_LDD13 */ +#define BADGE4_GPIO_LGP8 GPIO_GPIO8 /* GPIO_LDD14 */ +#define BADGE4_GPIO_LGP9 GPIO_GPIO9 /* GPIO_LDD15 */ +#define BADGE4_GPIO_GPA_VID GPIO_GPIO10 /* Video expansion */ +#define BADGE4_GPIO_GPB_VID GPIO_GPIO11 /* Video expansion */ +#define BADGE4_GPIO_GPC_VID GPIO_GPIO12 /* Video expansion */ + +#define BADGE4_GPIO_UART_HS1 GPIO_GPIO13 +#define BADGE4_GPIO_UART_HS2 GPIO_GPIO14 + +#define BADGE4_GPIO_MUXSEL0 GPIO_GPIO15 +#define BADGE4_GPIO_TESTPT_J7 GPIO_GPIO16 + +#define BADGE4_GPIO_SDSDA GPIO_GPIO17 /* SDRAM SPD Data */ +#define BADGE4_GPIO_SDSCL GPIO_GPIO18 /* SDRAM SPD Clock */ +#define BADGE4_GPIO_SDTYP0 GPIO_GPIO19 /* SDRAM Type Control */ +#define BADGE4_GPIO_SDTYP1 GPIO_GPIO20 /* SDRAM Type Control */ + +#define BADGE4_GPIO_BGNT_1111 GPIO_GPIO21 /* GPIO_MBGNT */ +#define BADGE4_GPIO_BREQ_1111 GPIO_GPIO22 /* GPIO_TREQA */ + +#define BADGE4_GPIO_TESTPT_J6 GPIO_GPIO23 + +#define BADGE4_GPIO_PCMEN5V GPIO_GPIO24 /* 5V power */ + +#define BADGE4_GPIO_SA1111_NRST GPIO_GPIO25 /* SA-1111 nRESET */ + +#define BADGE4_GPIO_TESTPT_J5 GPIO_GPIO26 + +#define BADGE4_GPIO_CLK_1111 GPIO_GPIO27 /* GPIO_32_768kHz */ + +/* Interrupts on the BadgePAD 4 */ +#define BADGE4_IRQ_GPIO_SA1111 IRQ_GPIO0 /* SA-1111 interrupt */ + + +/* PCM5ENV Usage tracking */ + +#define BADGE4_5V_PCMCIA_SOCK0 (1<<0) +#define BADGE4_5V_PCMCIA_SOCK1 (1<<1) +#define BADGE4_5V_PCMCIA_SOCK(n) (1<<(n)) +#define BADGE4_5V_USB (1<<2) + +#ifndef __ASSEMBLY__ +extern void badge4_set_5V(unsigned subsystem, int on); +#endif diff -Nru a/include/asm-arm/arch-sa1100/cerf.h b/include/asm-arm/arch-sa1100/cerf.h --- a/include/asm-arm/arch-sa1100/cerf.h Thu Mar 7 18:17:44 2002 +++ b/include/asm-arm/arch-sa1100/cerf.h Thu Mar 7 18:17:44 2002 @@ -1,6 +1,8 @@ #ifndef _INCLUDE_CERF_H_ #define _INCLUDE_CERF_H_ +#include + #ifdef CONFIG_SA1100_CERF_CPLD diff -Nru a/include/asm-arm/arch-sa1100/graphicsclient.h b/include/asm-arm/arch-sa1100/graphicsclient.h --- a/include/asm-arm/arch-sa1100/graphicsclient.h Thu Mar 7 18:17:44 2002 +++ b/include/asm-arm/arch-sa1100/graphicsclient.h Thu Mar 7 18:17:44 2002 @@ -76,15 +76,6 @@ #define IRQ_GC_UART1_CTS IRQ_GPIO16 #define IRQ_GC_UART2_CTS IRQ_GPIO17 -#ifndef __ASSEMBLY__ -struct gc_uart_ctrl_data_t { - int cts_gpio; - int cts_prev_state; - struct uart_info *info; - struct uart_port *port; -}; -#endif /* __ASSEMBLY__ */ - /* LEDs */ #define ADS_LED0 GPIO_GPIO20 /* on-board D22 */ diff -Nru a/include/asm-arm/arch-sa1100/hardware.h b/include/asm-arm/arch-sa1100/hardware.h --- a/include/asm-arm/arch-sa1100/hardware.h Thu Mar 7 18:17:42 2002 +++ b/include/asm-arm/arch-sa1100/hardware.h Thu Mar 7 18:17:42 2002 @@ -87,22 +87,6 @@ #include "SA-1100.h" - -/* - * SA1100 GPIO edge detection for IRQs: - * IRQs are generated on Falling-Edge, Rising-Edge, or both. - * This must be called *before* the corresponding IRQ is registered. - * Use this instead of directly setting GRER/GFER. - */ -#define GPIO_NO_EDGES 0 -#define GPIO_FALLING_EDGE 1 -#define GPIO_RISING_EDGE 2 -#define GPIO_BOTH_EDGES 3 -#ifndef __ASSEMBLY__ -extern void set_GPIO_IRQ_edge( int gpio_mask, int edge_mask ); -#endif - - /* * Implementation specifics. * @@ -112,6 +96,8 @@ * ifdefs, and lots of other preprocessor gunk elsewhere. */ +#include "badge4.h" + #ifdef CONFIG_SA1100_PANGOLIN #include "pangolin.h" #endif @@ -178,6 +164,8 @@ #if defined(CONFIG_SA1100_ADSBITSY) #include "adsbitsy.h" #endif + +#include "stork.h" #include "system3.h" diff -Nru a/include/asm-arm/arch-sa1100/ide.h b/include/asm-arm/arch-sa1100/ide.h --- a/include/asm-arm/arch-sa1100/ide.h Thu Mar 7 18:17:37 2002 +++ b/include/asm-arm/arch-sa1100/ide.h Thu Mar 7 18:17:37 2002 @@ -113,7 +113,7 @@ /* Enable GPIO as interrupt line */ GPDR &= ~LART_GPIO_IDE; - set_GPIO_IRQ_edge(LART_GPIO_IDE, GPIO_RISING_EDGE); + set_irq_type(LART_IRQ_IDE, IRQT_RISING); /* set PCMCIA interface timing */ MECR = 0x00060006; diff -Nru a/include/asm-arm/arch-sa1100/irq.h b/include/asm-arm/arch-sa1100/irq.h --- a/include/asm-arm/arch-sa1100/irq.h Thu Mar 7 18:17:39 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,13 +0,0 @@ -/* - * linux/include/asm-arm/arch-sa1100/irq.h - * - * Author: Nicolas Pitre - */ - -#define fixup_irq(x) (x) - -/* - * This prototype is required for cascading of multiplexed interrupts. - * Since it doesn't exist elsewhere, we'll put it here for now. - */ -extern void do_IRQ(int irq, struct pt_regs *regs); diff -Nru a/include/asm-arm/arch-sa1100/irqs.h b/include/asm-arm/arch-sa1100/irqs.h --- a/include/asm-arm/arch-sa1100/irqs.h Thu Mar 7 18:17:36 2002 +++ b/include/asm-arm/arch-sa1100/irqs.h Thu Mar 7 18:17:36 2002 @@ -148,6 +148,8 @@ */ #define IRQ_NEPONSET_SMC9196 (IRQ_BOARD_START + 0) #define IRQ_NEPONSET_USAR (IRQ_BOARD_START + 1) +#define IRQ_NEPONSET_SA1111 (IRQ_BOARD_START + 2) /* PT Digital Board Interrupts (CONFIG_SA1100_PT_SYSTEM3) */ -#define IRQ_SYSTEM3_SMC9196 (IRQ_BOARD_START + 0) +#define IRQ_SYSTEM3_SA1111 (IRQ_BOARD_START + 0) +#define IRQ_SYSTEM3_SMC9196 (IRQ_BOARD_START + 1) diff -Nru a/include/asm-arm/arch-sa1100/pangolin.h b/include/asm-arm/arch-sa1100/pangolin.h --- a/include/asm-arm/arch-sa1100/pangolin.h Thu Mar 7 18:17:44 2002 +++ b/include/asm-arm/arch-sa1100/pangolin.h Thu Mar 7 18:17:44 2002 @@ -6,10 +6,11 @@ * This file contains the hardware specific definitions for Pangolin * */ - #ifndef __ASM_ARCH_HARDWARE_H #error "include instead" #endif + +#include #ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE diff -Nru a/include/asm-arm/arch-sa1100/stork.h b/include/asm-arm/arch-sa1100/stork.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-arm/arch-sa1100/stork.h Thu Mar 7 18:17:47 2002 @@ -0,0 +1,164 @@ +/* + stork.h + +*/ + +#ifndef STORK_SA1100_H +#define STORK_SA1100_H + +/* ugly - this will make sure we build sa1100fb for the Nec display not the Kyocera */ +#define STORK_TFT 1 + + +#define GPIO_STORK_SWITCH_1 (1 << 0) /* Switch 1 - input */ +#define GPIO_STORK_SWITCH_2 (1 << 1) /* Switch 2 - input */ +#define GPIO_STORK_TOUCH_SCREEN_BUSY (1 << 10) /* TOUCH_SCREEN_BUSY - input */ +#define GPIO_STORK_TOUCH_SCREEN_DATA (1 << 11) /* TOUCH_SCREEN_DATA - input */ +#define GPIO_STORK_CODEC_AGCSTAT (1 << 12) /* CODEC_AGCSTAT -input */ +#define GPIO_STORK_RS232_ON (1 << 13) /* enable RS232 (UART1) */ +#define GPIO_STORK_TEST_POINT (1 << 14) /* to test point */ +#define GPIO_STORK_L3_I2C_SDA (1 << 15) /* L3_I2C_SDA - bidirectional */ +#define GPIO_STORK_PSU_SYNC_MODE (1 << 16) /* PSU_SYNC_MODE - output */ +#define GPIO_STORK_L3_MODE (1 << 17) /* L3 mode - output (??) */ +#define GPIO_STORK_L3_I2C_SCL (1 << 18) /* L3_I2C_SCL - bidirectional */ +#define GPIO_STORK_AUDIO_CLK (1 << 19) /* SSP external clock (Audio clock) - input */ +#define GPIO_STORK_PCMCIA_A_CARD_DETECT (1 << 20) /* PCMCIA_A_CARD_DETECT - input */ +#define GPIO_STORK_PCMCIA_B_CARD_DETECT (1 << 21) /* PCMCIA_B_CARD_DETECT - input */ +#define GPIO_STORK_PCMCIA_A_RDY (1 << 22) /* PCMCIA_A_RDY - input */ +#define GPIO_STORK_PCMCIA_B_RDY (1 << 23) /* PCMCIA_B_RDY - input */ +#define GPIO_STORK_SWITCH_EXTRA_IRQ (1 << 24) /* Extra IRQ from switch detect logic - input */ +#define GPIO_STORK_SWITCH_IRQ (1 << 25) /* Sitch irq - input */ +#define GPIO_STORK_BATTERY_LOW_IRQ (1 << 26) /* BATTERY_LOW_IRQ - input */ +#define GPIO_STORK_TOUCH_SCREEN_PEN_IRQ (1 << 27) /* TOUCH_SCREEN_PEN_IRQ -input */ + +#define IRQ_GPIO_STORK_PCMCIA_A_CARD_DETECT IRQ_GPIO20 /* PCMCIA_A_CARD_DETECT - input */ +#define IRQ_GPIO_STORK_PCMCIA_B_CARD_DETECT IRQ_GPIO21 /* PCMCIA_B_CARD_DETECT - input */ + +#define IRQ_GPIO_STORK_SWITCH_1 IRQ_GPIO0 /* Switch 1 - input - active low */ +#define IRQ_GPIO_STORK_SWITCH_2 IRQ_GPIO1 /* Switch 2 - input - active low */ +#define IRQ_GPIO_STORK_SWITCH_EXTRA_IRQ IRQ_GPIO24 /* Extra IRQ from switch detect logic - input - active low */ +#define IRQ_GPIO_STORK_SWITCH_IRQ IRQ_GPIO25 /* Switch irq - input- active low */ +#define IRQ_GPIO_STORK_BATTERY_LOW_IRQ IRQ_GPIO26 /* BATTERY_LOW_IRQ - input - active low */ +#define IRQ_GPIO_STORK_TOUCH_SCREEN_PEN_IRQ IRQ_GPIO27 /* TOUCH_SCREEN_PEN_IRQ -input - goes low when it happens */ + +/* this may be bogus - no it's not the RDY line becomes the IRQ line when we're up as an IO device */ +#define IRQ_GPIO_STORK_PCMCIA_A_RDY IRQ_GPIO22 /* PCMCIA_A_RDY - input */ +#define IRQ_GPIO_STORK_PCMCIA_B_RDY IRQ_GPIO23 /* PCMCIA_B_RDY - input */ + +/* the default outputs, others are optional - I'll set these in the bootldr for now */ +#define GPIO_STORK_OUTPUT_BITS (GPIO_STORK_RS232_ON | GPIO_STORK_PSU_SYNC_MODE | GPIO_STORK_L3_MODE) + +#define STORK_LATCH_A_ADDR 0x08000000 /* cs1 A5 = 0 */ +#define STORK_LATCH_B_ADDR 0x08000020 /* cs1 A5 = 1 */ + +#define STORK_LCDCPLD_BASE_ADDR 0x10000000 /* cs2 A5 = 0 */ + +/* bit defs for latch A - these are write only and will need to be mirrored! */ + +#define STORK_TEMP_IC_POWER_ON (1 << 0) +#define STORK_SED1386_POWER_ON (1 << 1) +#define STORK_LCD_3V3_POWER_ON (1 << 2) +#define STORK_LCD_5V_POWER_ON (1 << 3) +#define STORK_LCD_BACKLIGHT_INVERTER_ON (1 << 4) +#define STORK_PCMCIA_PULL_UPS_POWER_ON (1 << 5) +#define STORK_PCMCIA_A_POWER_ON (1 << 6) +#define STORK_PCMCIA_B_POWER_ON (1 << 7) +#define STORK_AUDIO_POWER_ON (1 << 8) +#define STORK_AUDIO_AMP_ON (1 << 9) +#define STORK_BAR_CODE_POWER_ON (1 << 10) +#define STORK_BATTERY_CHARGER_ON (1 << 11) +#define STORK_SED1386_RESET (1 << 12) +#define STORK_IRDA_FREQUENCY_SELECT (1 << 13) +#define STORK_IRDA_MODE_0 (1 << 14) +#define STORK_IRDA_MODE_1 (1 << 15) + +/* and for B */ + +#define STORK_AUX_AD_SEL_0 (1 << 0) +#define STORK_AUX_AD_SEL_1 (1 << 1) +#define STORK_TOUCH_SCREEN_DCLK (1 << 2) +#define STORK_TOUCH_SCREEN_DIN (1 << 3) +#define STORK_TOUCH_SCREEN_CS (1 << 4) +#define STORK_DA_CS (1 << 5) +#define STORK_DA_LD (1 << 6) +#define STORK_RED_LED (1 << 7) /* active LOW */ +#define STORK_GREEN_LED (1 << 8) /* active LOW */ +#define STORK_YELLOW_LED (1 << 9) /* active LOW */ +#define STORK_PCMCIA_B_RESET (1 << 10) +#define STORK_PCMCIA_A_RESET (1 << 11) +#define STORK_AUDIO_CODEC_RESET (1 << 12) +#define STORK_CODEC_QMUTE (1 << 13) +#define STORK_AUDIO_CLOCK_SEL0 (1 << 14) +#define STORK_AUDIO_CLOCK_SEL1 (1 << 15) + + +/* + + There are 8 control bits in the touch screen controller (AD7873) + + S A2 A1 A0 MODE SER/DFR# PD1 PD0 + + S Start bit, always one. + A2 - A0 Channel select bits + MODE 0 => 12 bit resolution, 1 => 8 bit + SER/DFR# Single ender/Differential Reference Select bit + PD1, PD0 Power management bits (usually 10) + + +From Table 1. + + A2-A0 + + 0 Temp0 (SER must be 1) + 1 X+ (is this a typo? - is this X- really?) + 2 VBAT, + 3 read X+ (Z1), + 4 read Y- (Z2), 5 => read Y+, + +*/ + +#define AD7873_START 0x80 /* all commands need this to be set */ +#define AD7873_ADDR_BITS 4 /* ie shift by this */ +#define AD7873_8BITMODE 0x08 /* 0 => 12 bit convertions */ +#define AD7873_SER_DFR 0x04 +#define AD7873_PD1 0x02 +#define AD7873_PD0 0x01 + +#define AD7873_TEMP0 AD7873_SER_DFR +#define AD7873_X (1 << AD7873_ADDR_BITS) +#define AD7873_VBAT ((2 << AD7873_ADDR_BITS) | AD7873_SER_DFR) +#define AD7873_X_Z1 (3 << AD7873_ADDR_BITS) +#define AD7873_Y_Z2 (4 << AD7873_ADDR_BITS) +#define AD7873_Y (5 << AD7873_ADDR_BITS) +#define AD7873_AUX ((6 << AD7873_ADDR_BITS) | AD7873_SER_DFR) +#define AD7873_TEMP1 ((7 << AD7873_ADDR_BITS) | AD7873_SER_DFR) + +#ifndef __ASSEMBLY__ + +extern int storkSetLatchA(int bits); +extern int storkClearLatchA(int bits); + +extern int storkSetLatchB(int bits); +extern int storkClearLatchB(int bits); + +extern int storkSetLCDCPLD(int which, int bits); +extern int storkClearLCDCPLD(int which, int bits); + +extern void storkSetGPIO(int bits); +extern void storkClearGPIO(int bits); + +extern int storkGetGPIO(void); + +extern void storkClockShortToDtoA(int word); +extern int storkClockByteToTS(int byte); + + +/* this will return the current state of the hardware ANDED with the given bits + so NE => at least one bit was set, but maybe not all of them! */ + +extern int storkTestGPIO(int bits); + + +#endif + +#endif diff -Nru a/include/asm-arm/arch-sa1100/system3.h b/include/asm-arm/arch-sa1100/system3.h --- a/include/asm-arm/arch-sa1100/system3.h Thu Mar 7 18:17:36 2002 +++ b/include/asm-arm/arch-sa1100/system3.h Thu Mar 7 18:17:36 2002 @@ -67,17 +67,17 @@ /* System ID register */ /* IRQ Source Register */ -#define PT_IRQ_LAN ( 1<<0 ) -#define PT_IRQ_X ( 1<<1 ) -#define PT_IRQ_SA1111 ( 1<<2 ) -#define PT_IRQ_RS1 ( 1<<3 ) -#define PT_IRQ_RS1_RING ( 1<<4 ) -#define PT_IRQ_RS1_DCD ( 1<<5 ) -#define PT_IRQ_RS1_DSR ( 1<<6 ) -#define PT_IRQ_RS2 ( 1<<7 ) +#define PT_IRR_LAN ( 1<<0 ) +#define PT_IRR_X ( 1<<1 ) +#define PT_IRR_SA1111 ( 1<<2 ) +#define PT_IRR_RS1 ( 1<<3 ) +#define PT_IRR_RS1_RING ( 1<<4 ) +#define PT_IRR_RS1_DCD ( 1<<5 ) +#define PT_IRR_RS1_DSR ( 1<<6 ) +#define PT_IRR_RS2 ( 1<<7 ) /* FIXME */ -#define PT_IRQ_USAR ( 1<<1 ) +#define PT_IRR_USAR ( 1<<1 ) /* CTRL 0 */ #define PT_CTRL0_USBSLAVE ( 1<<0 ) diff -Nru a/include/asm-arm/arch-sa1100/time.h b/include/asm-arm/arch-sa1100/time.h --- a/include/asm-arm/arch-sa1100/time.h Thu Mar 7 18:17:44 2002 +++ b/include/asm-arm/arch-sa1100/time.h Thu Mar 7 18:17:44 2002 @@ -98,7 +98,7 @@ timer_irq.handler = sa1100_timer_interrupt; OSMR0 = 0; /* set initial match at 0 */ OSSR = 0xf; /* clear status on all timers */ - setup_arm_irq(IRQ_OST0, &timer_irq); + setup_irq(IRQ_OST0, &timer_irq); OIER |= OIER_E0; /* enable match on timer 0 to cause interrupts */ OSCR = 0; /* initialize free-running timer, force first match */ } diff -Nru a/include/asm-arm/arch-shark/hardware.h b/include/asm-arm/arch-shark/hardware.h --- a/include/asm-arm/arch-shark/hardware.h Thu Mar 7 18:17:39 2002 +++ b/include/asm-arm/arch-shark/hardware.h Thu Mar 7 18:17:39 2002 @@ -38,7 +38,8 @@ /* defines for the Framebuffer */ -#define FB_START 0x06000000 +#define FB_START 0x06000000 +#define FB_SIZE 0x01000000 #define UNCACHEABLE_ADDR 0xdf010000 diff -Nru a/include/asm-arm/arch-shark/irq.h b/include/asm-arm/arch-shark/irq.h --- a/include/asm-arm/arch-shark/irq.h Thu Mar 7 18:17:37 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,10 +0,0 @@ -/* - * linux/include/asm-arm/arch-shark/irq.h - * - * by Alexander Schulz - * - * derived from linux/arch/ppc/kernel/i8259.c and: - * include/asm-arm/arch-ebsa110/irq.h - * Copyright (C) 1996-1998 Russell King - */ -#define fixup_irq(x) (x) diff -Nru a/include/asm-arm/arch-shark/keyboard.h b/include/asm-arm/arch-shark/keyboard.h --- a/include/asm-arm/arch-shark/keyboard.h Thu Mar 7 18:17:38 2002 +++ b/include/asm-arm/arch-shark/keyboard.h Thu Mar 7 18:17:38 2002 @@ -6,6 +6,7 @@ * (C) 1998 Russell King * (C) 1998 Phil Blundell */ +#include #include #include #include @@ -28,7 +29,6 @@ static inline void kbd_init_hw(void) { - if (have_isa_bridge) { k_setkeycode = pckbd_setkeycode; k_getkeycode = pckbd_getkeycode; k_translate = pckbd_translate; @@ -39,7 +39,6 @@ k_sysrq_xlate = pckbd_sysrq_xlate; #endif pckbd_init_hw(); - } } /* diff -Nru a/include/asm-arm/arch-shark/param.h b/include/asm-arm/arch-shark/param.h --- a/include/asm-arm/arch-shark/param.h Thu Mar 7 18:17:46 2002 +++ b/include/asm-arm/arch-shark/param.h Thu Mar 7 18:17:46 2002 @@ -4,9 +4,5 @@ * by Alexander Schulz */ -/* This must be a power of 2 because the RTC - * can't use anything else. - */ -#define HZ 64 +#define HZ 100 -#define hz_to_std(a) ((a * HZ)/100) diff -Nru a/include/asm-arm/arch-shark/time.h b/include/asm-arm/arch-shark/time.h --- a/include/asm-arm/arch-shark/time.h Thu Mar 7 18:17:45 2002 +++ b/include/asm-arm/arch-shark/time.h Thu Mar 7 18:17:45 2002 @@ -3,44 +3,21 @@ * * by Alexander Schulz * - * Uses the real time clock because you can't run - * the timer with level triggered interrupts and - * you can't run the shark with edge triggered - * inetrrupts (loses ints and hangs). - * - * derived from linux/drivers/char/rtc.c and: - * linux/include/asm-arm/arch-ebsa110/time.h + * derived from include/asm-arm/arch-ebsa110/time.h * Copyright (c) 1996,1997,1998 Russell King. */ #include -#include +#include -#define IRQ_TIMER 8 - -extern void get_rtc_time(struct rtc_time *rtc_tm); -extern void set_rtc_irq_bit(unsigned char bit); -extern unsigned long epoch; +#define IRQ_TIMER 0 +#define HZ_TIME ((1193180 + HZ/2) / HZ) static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - - CMOS_READ(RTC_INTR_FLAGS); - do_leds(); - - { -#ifdef DIVISOR - static unsigned int divisor; - - if (divisor-- == 0) { - divisor = DIVISOR - 1; -#else - { -#endif - do_timer(regs); - } - } + do_timer(regs); + do_profile(regs); } /* @@ -48,38 +25,15 @@ */ void __init time_init(void) { - struct rtc_time r_time; unsigned long flags; - int tmp = 0; - unsigned char val; - /* - * Set the clock to 128 Hz, we already have a valid - * vector now: - */ - - while (HZ > (1<> 8, 0x40); + + xtime.tv_sec = 0; timer_irq.handler = timer_interrupt; timer_irq.flags = SA_INTERRUPT; /* FIXME: really? */ - setup_arm_irq(IRQ_TIMER, &timer_irq); + setup_irq(IRQ_TIMER, &timer_irq); } diff -Nru a/include/asm-arm/arch-tbox/irq.h b/include/asm-arm/arch-tbox/irq.h --- a/include/asm-arm/arch-tbox/irq.h Thu Mar 7 18:17:40 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,13 +0,0 @@ -/* - * include/asm-arm/arch-tbox/irq.h - * - * Copyright (C) 1998, 1999, 2000 Philip Blundell - */ - -/* - * 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. - */ -#define fixup_irq(x) (x) diff -Nru a/include/asm-arm/arch-tbox/time.h b/include/asm-arm/arch-tbox/time.h --- a/include/asm-arm/arch-tbox/time.h Thu Mar 7 18:17:36 2002 +++ b/include/asm-arm/arch-tbox/time.h Thu Mar 7 18:17:36 2002 @@ -32,5 +32,5 @@ void __init time_init(void) { timer_irq.handler = timer_interrupt; - setup_arm_irq(IRQ_TIMER, &timer_irq); + setup_irq(IRQ_TIMER, &timer_irq); } diff -Nru a/include/asm-arm/bitops.h b/include/asm-arm/bitops.h --- a/include/asm-arm/bitops.h Thu Mar 7 18:17:37 2002 +++ b/include/asm-arm/bitops.h Thu Mar 7 18:17:37 2002 @@ -302,12 +302,46 @@ } /* + * ffz = Find First Zero in word. Undefined if no zero exists, + * so code should check against ~0UL first.. + */ +static inline unsigned long __ffs(unsigned long word) +{ + int k; + + k = 31; + if (word & 0x0000ffff) { k -= 16; word <<= 16; } + if (word & 0x00ff0000) { k -= 8; word <<= 8; } + if (word & 0x0f000000) { k -= 4; word <<= 4; } + if (word & 0x30000000) { k -= 2; word <<= 2; } + if (word & 0x40000000) { k -= 1; } + return k; +} + +/* * ffs: find first bit set. This is defined the same way as * the libc and compiler builtin ffs routines, therefore * differs in spirit from the above ffz (man ffs). */ #define ffs(x) generic_ffs(x) + +/* + * Find first bit set in a 168-bit bitmap, where the first + * 128 bits are unlikely to be set. + */ +static inline int sched_find_first_bit(unsigned long *b) +{ + if (unlikely(b[0])) + return __ffs(b[0]); + if (unlikely(b[1])) + return __ffs(b[1]) + 32; + if (unlikely(b[2])) + return __ffs(b[2]) + 64; + if (b[3]) + return __ffs(b[3]) + 96; + return __ffs(b[4]) + 128; +} /* * hweightN: returns the hamming weight (i.e. the number diff -Nru a/include/asm-arm/cpu-multi32.h b/include/asm-arm/cpu-multi32.h --- a/include/asm-arm/cpu-multi32.h Thu Mar 7 18:17:38 2002 +++ b/include/asm-arm/cpu-multi32.h Thu Mar 7 18:17:38 2002 @@ -93,21 +93,6 @@ void (*invalidate_page)(void *virt_page); } icache; - struct { /* TLB */ - /* - * flush all TLBs - */ - void (*invalidate_all)(void); - /* - * flush a specific TLB - */ - void (*invalidate_range)(unsigned long address, unsigned long end); - /* - * flush a specific TLB - */ - void (*invalidate_page)(unsigned long address, int flags); - } tlb; - struct { /* PageTable */ /* * Set the page table @@ -151,10 +136,6 @@ #define cpu_icache_invalidate_range(s,e) processor.icache.invalidate_range(s,e) #define cpu_icache_invalidate_page(vp) processor.icache.invalidate_page(vp) - -#define cpu_tlb_invalidate_all() processor.tlb.invalidate_all() -#define cpu_tlb_invalidate_range(s,e) processor.tlb.invalidate_range(s,e) -#define cpu_tlb_invalidate_page(vp,f) processor.tlb.invalidate_page(vp,f) #define cpu_set_pgd(pgd) processor.pgtable.set_pgd(pgd) #define cpu_set_pmd(pmdp, pmd) processor.pgtable.set_pmd(pmdp, pmd) diff -Nru a/include/asm-arm/cpu-single.h b/include/asm-arm/cpu-single.h --- a/include/asm-arm/cpu-single.h Thu Mar 7 18:17:37 2002 +++ b/include/asm-arm/cpu-single.h Thu Mar 7 18:17:37 2002 @@ -37,9 +37,6 @@ #define cpu_dcache_clean_entry __cpu_fn(CPU_NAME,_dcache_clean_entry) #define cpu_icache_invalidate_range __cpu_fn(CPU_NAME,_icache_invalidate_range) #define cpu_icache_invalidate_page __cpu_fn(CPU_NAME,_icache_invalidate_page) -#define cpu_tlb_invalidate_all __cpu_fn(CPU_NAME,_tlb_invalidate_all) -#define cpu_tlb_invalidate_range __cpu_fn(CPU_NAME,_tlb_invalidate_range) -#define cpu_tlb_invalidate_page __cpu_fn(CPU_NAME,_tlb_invalidate_page) #define cpu_set_pgd __cpu_fn(CPU_NAME,_set_pgd) #define cpu_set_pmd __cpu_fn(CPU_NAME,_set_pmd) #define cpu_set_pte __cpu_fn(CPU_NAME,_set_pte) @@ -72,10 +69,6 @@ extern void cpu_icache_invalidate_range(unsigned long start, unsigned long end); extern void cpu_icache_invalidate_page(void *virt_page); - -extern void cpu_tlb_invalidate_all(void); -extern void cpu_tlb_invalidate_range(unsigned long address, unsigned long end); -extern void cpu_tlb_invalidate_page(unsigned long address, int flags); extern void cpu_set_pgd(unsigned long pgd_phys); extern void cpu_set_pmd(pmd_t *pmdp, pmd_t pmd); diff -Nru a/include/asm-arm/current.h b/include/asm-arm/current.h --- a/include/asm-arm/current.h Thu Mar 7 18:17:38 2002 +++ b/include/asm-arm/current.h Thu Mar 7 18:17:38 2002 @@ -1,12 +1,13 @@ #ifndef _ASMARM_CURRENT_H #define _ASMARM_CURRENT_H +#include + static inline struct task_struct *get_current(void) __attribute__ (( __const__ )); static inline struct task_struct *get_current(void) { - register unsigned long sp asm ("sp"); - return (struct task_struct *)(sp & ~0x1fff); + return current_thread_info()->task; } #define current (get_current()) diff -Nru a/include/asm-arm/fpstate.h b/include/asm-arm/fpstate.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-arm/fpstate.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,29 @@ +/* + * linux/include/asm-arm/fpstate.h + * + * Copyright (C) 1995 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ASM_ARM_FPSTATE_H +#define __ASM_ARM_FPSTATE_H + +#define FP_SIZE 35 + +struct fp_hard_struct { + unsigned int save[FP_SIZE]; /* as yet undefined */ +}; + +struct fp_soft_struct { + unsigned int save[FP_SIZE]; /* undefined information */ +}; + +union fp_state { + struct fp_hard_struct hard; + struct fp_soft_struct soft; +}; + +#endif diff -Nru a/include/asm-arm/glue.h b/include/asm-arm/glue.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-arm/glue.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,78 @@ +/* + * linux/include/asm-arm/glue.h + * + * Copyright (C) 1997-1999 Russell King + * Copyright (C) 2000-2002 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This file provides the glue to stick the processor-specific bits + * into the kernel in an efficient manner. The idea is to use branches + * when we're only targetting one class of TLB, or indirect calls + * when we're targetting multiple classes of TLBs. + */ +#ifdef __KERNEL__ + +#include + +#ifdef __STDC__ +#define ____glue(name,fn) name##fn +#else +#define ____glue(name,fn) name/**/fn +#endif +#define __glue(name,fn) ____glue(name,fn) + +/* + * Select MMU TLB handling. + */ + +/* + * ARMv3 MMU + */ +#undef _TLB +#if defined(CONFIG_CPU_ARM610) || defined(CONFIG_CPU_ARM710) +# ifdef _TLB +# define MULTI_TLB 1 +# else +# define _TLB v3 +# endif +#endif + +/* + * ARMv4 MMU without write buffer + */ +#if defined(CONFIG_CPU_ARM720T) +# ifdef _TLB +# define MULTI_TLB 1 +# else +# define _TLB v4 +# endif +#endif + +/* + * ARMv4 MMU with write buffer, with invalidate I TLB entry instruction + */ +#if defined(CONFIG_CPU_ARM920T) || defined(CONFIG_CPU_ARM922T) || \ + defined(CONFIG_CPU_ARM926T) || defined(CONFIG_CPU_ARM1020) || \ + defined(CONFIG_CPU_XSCALE) +# ifdef _TLB +# define MULTI_TLB 1 +# else +# define _TLB v4wbi +# endif +#endif + +/* + * ARMv4 MMU with write buffer, without invalidate I TLB entry instruction + */ +#if defined(CONFIG_CPU_SA110) || defined(CONFIG_CPU_SA1100) +# ifdef _TLB +# define MULTI_TLB 1 +# else +# define _TLB v4wb +# endif +#endif + +#endif diff -Nru a/include/asm-arm/hardirq.h b/include/asm-arm/hardirq.h --- a/include/asm-arm/hardirq.h Thu Mar 7 18:17:37 2002 +++ b/include/asm-arm/hardirq.h Thu Mar 7 18:17:37 2002 @@ -34,6 +34,7 @@ #define irq_exit(cpu,irq) (local_irq_count(cpu)--) #define synchronize_irq() do { } while (0) +#define release_irqlock(cpu) do { } while (0) #else #error SMP not supported diff -Nru a/include/asm-arm/io.h b/include/asm-arm/io.h --- a/include/asm-arm/io.h Thu Mar 7 18:17:41 2002 +++ b/include/asm-arm/io.h Thu Mar 7 18:17:41 2002 @@ -31,9 +31,9 @@ * Generic IO read/write. These perform native-endian accesses. Note * that some architectures will want to re-define __raw_{read,write}w. */ -extern void __raw_writesb(unsigned int addr, void *data, int bytelen); -extern void __raw_writesw(unsigned int addr, void *data, int wordlen); -extern void __raw_writesl(unsigned int addr, void *data, int longlen); +extern void __raw_writesb(unsigned int addr, const void *data, int bytelen); +extern void __raw_writesw(unsigned int addr, const void *data, int wordlen); +extern void __raw_writesl(unsigned int addr, const void *data, int longlen); extern void __raw_readsb(unsigned int addr, void *data, int bytelen); extern void __raw_readsw(unsigned int addr, void *data, int wordlen); diff -Nru a/include/asm-arm/irq.h b/include/asm-arm/irq.h --- a/include/asm-arm/irq.h Thu Mar 7 18:17:45 2002 +++ b/include/asm-arm/irq.h Thu Mar 7 18:17:45 2002 @@ -24,5 +24,19 @@ extern void disable_irq(unsigned int); extern void enable_irq(unsigned int); +#define __IRQT_FALEDGE (1 << 0) +#define __IRQT_RISEDGE (1 << 1) +#define __IRQT_LOWLVL (1 << 2) +#define __IRQT_HIGHLVL (1 << 3) + +#define IRQT_NOEDGE (0) +#define IRQT_RISING (__IRQT_RISEDGE) +#define IRQT_FALLING (__IRQT_FALEDGE) +#define IRQT_BOTHEDGE (__IRQT_RISEDGE|__IRQT_FALEDGE) +#define IRQT_LOW (__IRQT_LOWLVL) +#define IRQT_HIGH (__IRQT_HIGHLVL) + +int set_irq_type(unsigned int irq, unsigned int type); + #endif diff -Nru a/include/asm-arm/mach/irq.h b/include/asm-arm/mach/irq.h --- a/include/asm-arm/mach/irq.h Thu Mar 7 18:17:41 2002 +++ b/include/asm-arm/mach/irq.h Thu Mar 7 18:17:41 2002 @@ -10,32 +10,110 @@ #ifndef __ASM_ARM_MACH_IRQ_H #define __ASM_ARM_MACH_IRQ_H +struct irqdesc; +struct pt_regs; +struct seq_file; + +typedef void (*irq_handler_t)(unsigned int, struct irqdesc *, struct pt_regs *); +typedef void (*irq_control_t)(unsigned int); + +struct irqchip { + /* + * Acknowledge the IRQ. + * If this is a level-based IRQ, then it is expected to mask the IRQ + * as well. + */ + void (*ack)(unsigned int); + /* + * Mask the IRQ in hardware. + */ + void (*mask)(unsigned int); + /* + * Unmask the IRQ in hardware. + */ + void (*unmask)(unsigned int); + /* + * Re-run the IRQ + */ + void (*rerun)(unsigned int); + /* + * Set the type of the IRQ. + */ + int (*type)(unsigned int, unsigned int); +}; + struct irqdesc { - unsigned int nomask : 1; /* IRQ does not mask in IRQ */ - unsigned int enabled : 1; /* IRQ is currently enabled */ - unsigned int triggered: 1; /* IRQ has occurred */ - unsigned int probing : 1; /* IRQ in use for a probe */ - unsigned int probe_ok : 1; /* IRQ can be used for probe */ - unsigned int valid : 1; /* IRQ claimable */ - unsigned int noautoenable : 1; /* don't automatically enable IRQ */ - unsigned int unused :25; - void (*mask_ack)(unsigned int irq); /* Mask and acknowledge IRQ */ - void (*mask)(unsigned int irq); /* Mask IRQ */ - void (*unmask)(unsigned int irq); /* Unmask IRQ */ + irq_handler_t handle; + struct irqchip *chip; struct irqaction *action; + + unsigned int enabled : 1; /* IRQ is currently enabled */ + unsigned int triggered: 1; /* IRQ has occurred */ + unsigned int running : 1; /* IRQ is running */ + unsigned int pending : 1; /* IRQ is pending */ + unsigned int probing : 1; /* IRQ in use for a probe */ + unsigned int probe_ok : 1; /* IRQ can be used for probe */ + unsigned int valid : 1; /* IRQ claimable */ + unsigned int noautoenable : 1; /* don't automatically enable IRQ */ + unsigned int unused :23; + unsigned int depth; /* disable depth */ + /* * IRQ lock detection */ - unsigned int lck_cnt; - unsigned int lck_pc; - unsigned int lck_jif; + unsigned int lck_cnt; + unsigned int lck_pc; + unsigned int lck_jif; }; extern struct irqdesc irq_desc[]; +/* + * This is internal. Do not use it. + */ extern void (*init_arch_irq)(void); -extern int setup_arm_irq(int, struct irqaction *); -extern int show_fiq_list(struct seq_file *, void *); extern void init_FIQ(void); +extern int show_fiq_list(struct seq_file *, void *); +void __set_irq_handler(unsigned int irq, irq_handler_t, int); +int setup_irq(unsigned int, struct irqaction *); + +/* + * External stuff. + */ +#define set_irq_handler(irq,handler) __set_irq_handler(irq,handler,0) +#define set_irq_chained_handler(irq,handler) __set_irq_handler(irq,handler,1) + +void set_irq_chip(unsigned int irq, struct irqchip *); +void set_irq_flags(unsigned int irq, unsigned int flags); + +#ifdef not_yet +/* + * This is to be used by the top-level machine IRQ decoder only. + */ +static inline void call_irq(struct pt_regs *regs, unsigned int irq) +{ + struct irqdesc *desc = irq_desc + irq; + + spin_lock(&irq_controller_lock); + desc->handle(irq, desc, regs); + spin_unlock(&irq_controller_lock); + + if (softirq_pending(smp_processor_id())) + do_softirq(); +} +#endif + +#define IRQF_VALID (1 << 0) +#define IRQF_PROBE (1 << 1) +#define IRQF_NOAUTOEN (1 << 2) + +/* + * Built-in IRQ handlers. + */ +void do_level_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs); +void do_edge_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs); +void do_simple_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs); +void do_bad_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs); +void dummy_mask_unmask_irq(unsigned int irq); #endif diff -Nru a/include/asm-arm/mmu.h b/include/asm-arm/mmu.h --- a/include/asm-arm/mmu.h Thu Mar 7 18:17:39 2002 +++ b/include/asm-arm/mmu.h Thu Mar 7 18:17:39 2002 @@ -1,7 +1,9 @@ #ifndef __ARM_MMU_H #define __ARM_MMU_H -/* The ARM doesn't have a mmu context */ +/* + * The ARM doesn't have a mmu context + */ typedef struct { } mm_context_t; #endif diff -Nru a/include/asm-arm/mmu_context.h b/include/asm-arm/mmu_context.h --- a/include/asm-arm/mmu_context.h Thu Mar 7 18:17:43 2002 +++ b/include/asm-arm/mmu_context.h Thu Mar 7 18:17:43 2002 @@ -42,11 +42,8 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk, unsigned int cpu) { - if (prev != next) { + if (prev != next) cpu_switch_mm(next->pgd, tsk); - clear_bit(cpu, &prev->cpu_vm_mask); - } - set_bit(cpu, &next->cpu_vm_mask); } #define activate_mm(prev, next) \ diff -Nru a/include/asm-arm/page.h b/include/asm-arm/page.h --- a/include/asm-arm/page.h Thu Mar 7 18:17:39 2002 +++ b/include/asm-arm/page.h Thu Mar 7 18:17:39 2002 @@ -14,8 +14,19 @@ #define clear_page(page) memzero((void *)(page), PAGE_SIZE) extern void copy_page(void *to, void *from); -#define clear_user_page(page, vaddr) cpu_clear_user_page(page,vaddr) -#define copy_user_page(to, from, vaddr) cpu_copy_user_page(to,from,vaddr) +#define clear_user_page(addr,vaddr) \ + do { \ + preempt_disable(); \ + cpu_clear_user_page(addr, vaddr); \ + preempt_enable(); \ + } while (0) + +#define copy_user_page(to,from,vaddr) \ + do { \ + preempt_disable(); \ + cpu_copy_user_page(to, from, vaddr); \ + preempt_enable(); \ + } while (0) #ifdef STRICT_MM_TYPECHECKS /* diff -Nru a/include/asm-arm/pci.h b/include/asm-arm/pci.h --- a/include/asm-arm/pci.h Thu Mar 7 18:17:41 2002 +++ b/include/asm-arm/pci.h Thu Mar 7 18:17:41 2002 @@ -2,7 +2,7 @@ #define ASMARM_PCI_H #ifdef __KERNEL__ - +#include #include /* bah! */ #include @@ -142,18 +142,9 @@ for (i = 0; i < nents; i++, sg++) { char *virt; - if (sg->address && sg->page) - BUG(); - else if (!sg->address && !sg->page) - BUG(); - - if (sg->address) { - sg->dma_address = virt_to_bus(sg->address); - virt = sg->address; - } else { - sg->dma_address = page_to_bus(sg->page) + sg->offset; - virt = page_address(sg->page) + sg->offset; - } + + sg->dma_address = page_to_bus(sg->page) + sg->offset; + virt = page_address(sg->page) + sg->offset; consistent_sync(virt, sg->length, direction); } @@ -197,12 +188,7 @@ int i; for (i = 0; i < nelems; i++, sg++) { - char *virt; - - if (sg->address) - virt = sg->address; - else - virt = page_address(sg->page) + sg->offset; + char *virt = page_address(sg->page) + sg->offset; consistent_sync(virt, sg->length, direction); } } diff -Nru a/include/asm-arm/pgalloc.h b/include/asm-arm/pgalloc.h --- a/include/asm-arm/pgalloc.h Thu Mar 7 18:17:46 2002 +++ b/include/asm-arm/pgalloc.h Thu Mar 7 18:17:46 2002 @@ -14,128 +14,20 @@ #include -/* - * Get the cache handling stuff now. - */ #include - -/* - * ARM processors do not cache TLB tables in RAM. - */ -#define flush_tlb_pgtables(mm,start,end) do { } while (0) - -/* - * Processor specific parts... - */ #include /* - * Page table cache stuff - */ -#ifndef CONFIG_NO_PGT_CACHE - -#ifdef CONFIG_SMP -#error Pgtable caches have to be per-CPU, so that no locking is needed. -#endif /* CONFIG_SMP */ - -extern struct pgtable_cache_struct { - unsigned long *pgd_cache; - unsigned long *pte_cache; - unsigned long pgtable_cache_sz; -} quicklists; - -#define pgd_quicklist (quicklists.pgd_cache) -#define pmd_quicklist ((unsigned long *)0) -#define pte_quicklist (quicklists.pte_cache) -#define pgtable_cache_size (quicklists.pgtable_cache_sz) - -/* used for quicklists */ -#define __pgd_next(pgd) (((unsigned long *)pgd)[1]) -#define __pte_next(pte) (((unsigned long *)pte)[0]) - -static inline pgd_t *get_pgd_fast(void) -{ - unsigned long *ret; - - if ((ret = pgd_quicklist) != NULL) { - pgd_quicklist = (unsigned long *)__pgd_next(ret); - ret[1] = ret[2]; - clean_dcache_entry(ret + 1); - pgtable_cache_size--; - } - return (pgd_t *)ret; -} - -static inline void free_pgd_fast(pgd_t *pgd) -{ - __pgd_next(pgd) = (unsigned long) pgd_quicklist; - pgd_quicklist = (unsigned long *) pgd; - pgtable_cache_size++; -} - -static inline pte_t *pte_alloc_one_fast(struct mm_struct *mm, unsigned long address) -{ - unsigned long *ret; - - if((ret = pte_quicklist) != NULL) { - pte_quicklist = (unsigned long *)__pte_next(ret); - ret[0] = 0; - clean_dcache_entry(ret); - pgtable_cache_size--; - } - return (pte_t *)ret; -} - -static inline void free_pte_fast(pte_t *pte) -{ - __pte_next(pte) = (unsigned long) pte_quicklist; - pte_quicklist = (unsigned long *) pte; - pgtable_cache_size++; -} - -#else /* CONFIG_NO_PGT_CACHE */ - -#define pgd_quicklist ((unsigned long *)0) -#define pmd_quicklist ((unsigned long *)0) -#define pte_quicklist ((unsigned long *)0) - -#define get_pgd_fast() ((pgd_t *)0) -#define pte_alloc_one_fast(mm,addr) ((pte_t *)0) - -#define free_pgd_fast(pgd) free_pgd_slow(pgd) -#define free_pte_fast(pte) pte_free_slow(pte) - -#endif /* CONFIG_NO_PGT_CACHE */ - -#define pte_free(pte) free_pte_fast(pte) - - -/* * Since we have only two-level page tables, these are trivial */ -#define pmd_alloc_one_fast(mm,addr) ({ BUG(); ((pmd_t *)1); }) #define pmd_alloc_one(mm,addr) ({ BUG(); ((pmd_t *)2); }) -#define pmd_free_slow(pmd) do { } while (0) -#define pmd_free_fast(pmd) do { } while (0) #define pmd_free(pmd) do { } while (0) #define pgd_populate(mm,pmd,pte) BUG() extern pgd_t *get_pgd_slow(struct mm_struct *mm); extern void free_pgd_slow(pgd_t *pgd); -static inline pgd_t *pgd_alloc(struct mm_struct *mm) -{ - pgd_t *pgd; - - pgd = get_pgd_fast(); - if (!pgd) - pgd = get_pgd_slow(mm); - - return pgd; -} - -#define pgd_free(pgd) free_pgd_fast(pgd) - -extern int do_check_pgt_cache(int, int); +#define pgd_alloc(mm) get_pgd_slow(mm) +#define pgd_free(pgd) free_pgd_slow(pgd) #endif diff -Nru a/include/asm-arm/pgtable.h b/include/asm-arm/pgtable.h --- a/include/asm-arm/pgtable.h Thu Mar 7 18:17:40 2002 +++ b/include/asm-arm/pgtable.h Thu Mar 7 18:17:40 2002 @@ -13,7 +13,6 @@ #include #include #include -#include /* * PMD_SHIFT determines the size of the area a second-level page table can map @@ -146,8 +145,16 @@ #define pmd_offset(dir, addr) ((pmd_t *)(dir)) /* Find an entry in the third-level page table.. */ -#define __pte_offset(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) -#define pte_offset(dir, addr) ((pte_t *)pmd_page(*(dir)) + __pte_offset(addr)) +#define __pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) + +#define pmd_page(dir) ((struct page *)__pmd_page(dir)) + +#define __pte_offset(dir, addr) ((pte_t *)__pmd_page(*(dir)) + __pte_index(addr)) +#define pte_offset_kernel __pte_offset +#define pte_offset_map __pte_offset +#define pte_offset_map_nested __pte_offset +#define pte_unmap(pte) do { } while (0) +#define pte_unmap_nested(pte) do { } while (0) #include diff -Nru a/include/asm-arm/proc-armo/processor.h b/include/asm-arm/proc-armo/processor.h --- a/include/asm-arm/proc-armo/processor.h Thu Mar 7 18:17:40 2002 +++ b/include/asm-arm/proc-armo/processor.h Thu Mar 7 18:17:40 2002 @@ -23,7 +23,7 @@ #define KERNEL_STACK_SIZE 4096 -struct context_save_struct { +struct cpu_context_save { unsigned long r4; unsigned long r5; unsigned long r6; @@ -35,7 +35,7 @@ unsigned long pc; }; -#define INIT_CSS (struct context_save_struct){ 0, 0, 0, 0, 0, 0, 0, 0, SVC26_MODE } +#define INIT_CSS (struct cpu_context_save){ 0, 0, 0, 0, 0, 0, 0, 0, SVC26_MODE } typedef struct { void (*put_byte)(void); /* Special calling convention */ @@ -73,15 +73,5 @@ #define KSTK_EIP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1022]) #define KSTK_ESP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1020]) - -/* Allocation and freeing of basic task resources. */ -/* - * NOTE! The task struct and the stack go together - */ -extern unsigned long get_page_8k(int priority); -extern void free_page_8k(unsigned long page); - -#define ll_alloc_task_struct() ((struct task_struct *)get_page_8k(GFP_KERNEL)) -#define ll_free_task_struct(p) free_page_8k((unsigned long)(p)) #endif diff -Nru a/include/asm-arm/proc-armv/cache.h b/include/asm-arm/proc-armv/cache.h --- a/include/asm-arm/proc-armv/cache.h Thu Mar 7 18:17:44 2002 +++ b/include/asm-arm/proc-armv/cache.h Thu Mar 7 18:17:44 2002 @@ -8,6 +8,7 @@ * published by the Free Software Foundation. */ #include +#include /* * This flag is used to indicate that the page pointed to by a pte @@ -200,69 +201,93 @@ } while (0) /* - * Old ARM MEMC stuff. This supports the reversed mapping handling that - * we have on the older 26-bit machines. We don't have a MEMC chip, so... - */ -#define memc_update_all() do { } while (0) -#define memc_update_mm(mm) do { } while (0) -#define memc_update_addr(mm,pte,log) do { } while (0) -#define memc_clear(mm,physaddr) do { } while (0) - -/* - * TLB flushing. + * TLB Management + * ============== + * + * The arch/arm/mm/tlb-*.S files implement this methods. + * + * The TLB specific code is expected to perform whatever tests it + * needs to determine if it should invalidate the TLB for each + * call. Start addresses are inclusive and end addresses are + * exclusive; it is safe to round these addresses down. + * + * flush_tlb_all() + * + * Invalidate the entire TLB. * - * - flush_tlb_all() flushes all processes TLBs - * - flush_tlb_mm(mm) flushes the specified mm context TLB's - * - flush_tlb_page(vma, vmaddr) flushes TLB for specified page - * - flush_tlb_range(vma, start, end) flushes TLB for specified range of pages + * flush_tlb_mm(mm) * - * We drain the write buffer in here to ensure that the page tables in ram - * are really up to date. It is more efficient to do this here... + * Invalidate all TLB entries in a particular address + * space. + * - mm - mm_struct describing address space + * + * flush_tlb_range(mm,start,end) + * + * Invalidate a range of TLB entries in the specified + * address space. + * - mm - mm_struct describing address space + * - start - start address (may not be aligned) + * - end - end address (exclusive, may not be aligned) + * + * flush_tlb_page(vaddr,vma) + * + * Invalidate the specified page in the specified address range. + * - vaddr - virtual address (may not be aligned) + * - vma - vma_struct describing address range + * + * flush_kern_tlb_page(kaddr) + * + * Invalidate the TLB entry for the specified page. The address + * will be in the kernels virtual memory space. Current uses + * only require the D-TLB to be invalidated. + * - kaddr - Kernel virtual memory address */ -/* - * Notes: - * current->active_mm is the currently active memory description. - * current->mm == NULL iff we are lazy. - */ -#define flush_tlb_all() \ - do { \ - cpu_tlb_invalidate_all(); \ - } while (0) +struct cpu_tlb_fns { + void (*flush_kern_all)(void); + void (*flush_user_mm)(struct mm_struct *); + void (*flush_user_range)(unsigned long, unsigned long, struct vm_area_struct *); + void (*flush_user_page)(unsigned long, struct vm_area_struct *); + void (*flush_kern_page)(unsigned long); +}; /* - * Flush all user virtual address space translations described by `_mm'. - * - * Currently, this is always called for current->mm, which should be - * the same as current->active_mm. This is currently not be called for - * the lazy TLB case. + * Convert calls to our calling convention. */ -#define flush_tlb_mm(_mm) \ - do { \ - if ((_mm) == current->active_mm) \ - cpu_tlb_invalidate_all(); \ - } while (0) +#define flush_tlb_all() __cpu_flush_kern_tlb_all() +#define flush_tlb_mm(mm) __cpu_flush_user_tlb_mm(mm) +#define flush_tlb_range(vma,start,end) __cpu_flush_user_tlb_range(start,end,vma) +#define flush_tlb_page(vma,vaddr) __cpu_flush_user_tlb_page(vaddr,vma) +#define flush_kern_tlb_page(kaddr) __cpu_flush_kern_tlb_page(kaddr) /* - * Flush the specified range of user virtual address space translations. - * - * _mm may not be current->active_mm, but may not be NULL. + * Now select the calling method */ -#define flush_tlb_range(_vma,_start,_end) \ - do { \ - if ((_mm)->vm_mm == current->active_mm) \ - cpu_tlb_invalidate_range((_start), (_end)); \ - } while (0) +#ifdef MULTI_TLB -/* - * Flush the specified user virtual address space translation. - */ -#define flush_tlb_page(_vma,_page) \ - do { \ - if ((_vma)->vm_mm == current->active_mm) \ - cpu_tlb_invalidate_page((_page), \ - ((_vma)->vm_flags & VM_EXEC)); \ - } while (0) +extern struct cpu_tlb_fns cpu_tlb; + +#define __cpu_flush_kern_tlb_all cpu_tlb.flush_kern_all +#define __cpu_flush_user_tlb_mm cpu_tlb.flush_user_mm +#define __cpu_flush_user_tlb_range cpu_tlb.flush_user_range +#define __cpu_flush_user_tlb_page cpu_tlb.flush_user_page +#define __cpu_flush_kern_tlb_page cpu_tlb.flush_kern_page + +#else + +#define __cpu_flush_kern_tlb_all __glue(_TLB,_flush_kern_tlb_all) +#define __cpu_flush_user_tlb_mm __glue(_TLB,_flush_user_tlb_mm) +#define __cpu_flush_user_tlb_range __glue(_TLB,_flush_user_tlb_range) +#define __cpu_flush_user_tlb_page __glue(_TLB,_flush_user_tlb_page) +#define __cpu_flush_kern_tlb_page __glue(_TLB,_flush_kern_tlb_page) + +extern void __cpu_flush_kern_tlb_all(void); +extern void __cpu_flush_user_tlb_mm(struct mm_struct *); +extern void __cpu_flush_user_tlb_range(unsigned long, unsigned long, struct vm_area_struct *); +extern void __cpu_flush_user_tlb_page(unsigned long, struct vm_area_struct *); +extern void __cpu_flush_kern_tlb_page(unsigned long); + +#endif /* * if PG_dcache_dirty is set for the page, we need to ensure that any @@ -270,3 +295,12 @@ * back to the page. */ extern void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte); + +/* + * Old ARM MEMC stuff. This supports the reversed mapping handling that + * we have on the older 26-bit machines. We don't have a MEMC chip, so... + */ +#define memc_update_all() do { } while (0) +#define memc_update_mm(mm) do { } while (0) +#define memc_update_addr(mm,pte,log) do { } while (0) +#define memc_clear(mm,physaddr) do { } while (0) diff -Nru a/include/asm-arm/proc-armv/pgalloc.h b/include/asm-arm/proc-armv/pgalloc.h --- a/include/asm-arm/proc-armv/pgalloc.h Thu Mar 7 18:17:36 2002 +++ b/include/asm-arm/proc-armv/pgalloc.h Thu Mar 7 18:17:36 2002 @@ -18,7 +18,8 @@ * from the Linux copy. The processor copies are offset by -PTRS_PER_PTE * words from the Linux copy. */ -static inline pte_t *pte_alloc_one(struct mm_struct *mm, unsigned long address) +static inline pte_t * +pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr) { pte_t *pte; @@ -28,10 +29,21 @@ return pte; } +static inline struct page * +pte_alloc_one(struct mm_struct *mm, unsigned long addr) +{ + pte_t *pte; + + pte = kmem_cache_alloc(pte_cache, GFP_KERNEL); + if (pte) + pte += PTRS_PER_PTE; + return (struct page *)pte; +} + /* * Free one PTE table. */ -static inline void pte_free_slow(pte_t *pte) +static inline void pte_free_kernel(pte_t *pte) { if (pte) { pte -= PTRS_PER_PTE; @@ -39,6 +51,15 @@ } } +static inline void pte_free(struct page *pte) +{ + pte_t *_pte = (pte_t *)pte; + if (pte) { + _pte -= PTRS_PER_PTE; + kmem_cache_free(pte_cache, _pte); + } +} + /* * Populate the pmdp entry with a pointer to the pte. This pmd is part * of the mm address space. @@ -46,12 +67,14 @@ * If 'mm' is the init tasks mm, then we are doing a vmalloc, and we * need to set stuff up correctly for it. */ +#define pmd_populate_kernel(mm,pmdp,pte) \ + do { \ + BUG_ON(mm != &init_mm); \ + set_pmd(pmdp, __mk_pmd(pte, _PAGE_KERNEL_TABLE));\ + } while (0) + #define pmd_populate(mm,pmdp,pte) \ do { \ - unsigned long __prot; \ - if (mm == &init_mm) \ - __prot = _PAGE_KERNEL_TABLE; \ - else \ - __prot = _PAGE_USER_TABLE; \ - set_pmd(pmdp, __mk_pmd(pte, __prot)); \ + BUG_ON(mm == &init_mm); \ + set_pmd(pmdp, __mk_pmd(pte, _PAGE_USER_TABLE)); \ } while (0) diff -Nru a/include/asm-arm/proc-armv/pgtable.h b/include/asm-arm/proc-armv/pgtable.h --- a/include/asm-arm/proc-armv/pgtable.h Thu Mar 7 18:17:38 2002 +++ b/include/asm-arm/proc-armv/pgtable.h Thu Mar 7 18:17:38 2002 @@ -125,7 +125,7 @@ return pmd; } -static inline unsigned long pmd_page(pmd_t pmd) +static inline unsigned long __pmd_page(pmd_t pmd) { unsigned long ptr; diff -Nru a/include/asm-arm/proc-armv/processor.h b/include/asm-arm/proc-armv/processor.h --- a/include/asm-arm/proc-armv/processor.h Thu Mar 7 18:17:43 2002 +++ b/include/asm-arm/proc-armv/processor.h Thu Mar 7 18:17:43 2002 @@ -22,7 +22,7 @@ #define KERNEL_STACK_SIZE PAGE_SIZE -struct context_save_struct { +struct cpu_context_save { unsigned long cpsr; unsigned long r4; unsigned long r5; @@ -35,15 +35,12 @@ unsigned long pc; }; -#define INIT_CSS (struct context_save_struct){ SVC_MODE, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +#define INIT_CSS (struct cpu_context_save){ SVC_MODE, 0, 0, 0, 0, 0, 0, 0, 0, 0 } -#define EXTRA_THREAD_STRUCT \ - unsigned int domain; - -#define EXTRA_THREAD_STRUCT_INIT \ - domain: domain_val(DOMAIN_USER, DOMAIN_CLIENT) | \ - domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \ - domain_val(DOMAIN_IO, DOMAIN_CLIENT) +#define INIT_EXTRA_THREAD_INFO \ + cpu_domain: domain_val(DOMAIN_USER, DOMAIN_CLIENT) | \ + domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \ + domain_val(DOMAIN_IO, DOMAIN_CLIENT) #define start_thread(regs,pc,sp) \ ({ \ @@ -63,12 +60,5 @@ #define KSTK_EIP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1021]) #define KSTK_ESP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1019]) - -/* Allocation and freeing of basic task resources. */ -/* - * NOTE! The task struct and the stack go together - */ -#define ll_alloc_task_struct() ((struct task_struct *) __get_free_pages(GFP_KERNEL,1)) -#define ll_free_task_struct(p) free_pages((unsigned long)(p),1) #endif diff -Nru a/include/asm-arm/proc-armv/uaccess.h b/include/asm-arm/proc-armv/uaccess.h --- a/include/asm-arm/proc-armv/uaccess.h Thu Mar 7 18:17:36 2002 +++ b/include/asm-arm/proc-armv/uaccess.h Thu Mar 7 18:17:36 2002 @@ -16,8 +16,7 @@ static inline void set_fs (mm_segment_t fs) { - current->addr_limit = fs; - + current_thread_info()->addr_limit = fs; modify_domain(DOMAIN_KERNEL, fs ? DOMAIN_CLIENT : DOMAIN_MANAGER); } @@ -26,7 +25,7 @@ unsigned long flag, sum; \ __asm__("adds %1, %2, %3; sbcccs %1, %1, %0; movcc %0, #0" \ : "=&r" (flag), "=&r" (sum) \ - : "r" (addr), "Ir" (size), "0" (current->addr_limit) \ + : "r" (addr), "Ir" (size), "0" (current_thread_info()->addr_limit) \ : "cc"); \ flag; }) @@ -34,7 +33,7 @@ unsigned long flag; \ __asm__("cmp %2, %0; movlo %0, #0" \ : "=&r" (flag) \ - : "0" (current->addr_limit), "r" (addr) \ + : "0" (current_thread_info()->addr_limit), "r" (addr) \ : "cc"); \ (flag == 0); }) diff -Nru a/include/asm-arm/processor.h b/include/asm-arm/processor.h --- a/include/asm-arm/processor.h Thu Mar 7 18:17:44 2002 +++ b/include/asm-arm/processor.h Thu Mar 7 18:17:44 2002 @@ -17,23 +17,6 @@ */ #define current_text_addr() ({ __label__ _l; _l: &&_l;}) -#define FP_SIZE 35 - -struct fp_hard_struct { - unsigned int save[FP_SIZE]; /* as yet undefined */ -}; - -struct fp_soft_struct { - unsigned int save[FP_SIZE]; /* undefined information */ -}; - -union fp_state { - struct fp_hard_struct hard; - struct fp_soft_struct soft; -}; - -typedef unsigned long mm_segment_t; /* domain register */ - #ifdef __KERNEL__ #define EISA_bus 0 @@ -54,37 +37,15 @@ }; struct thread_struct { - atomic_t refcount; /* fault info */ unsigned long address; unsigned long trap_no; unsigned long error_code; - /* floating point */ - union fp_state fpstate; /* debugging */ struct debug_info debug; - /* context info */ - struct context_save_struct *save; - EXTRA_THREAD_STRUCT }; -#define INIT_THREAD { \ - refcount: ATOMIC_INIT(1), \ - EXTRA_THREAD_STRUCT_INIT \ -} - -/* - * Return saved PC of a blocked thread. - */ -static inline unsigned long thread_saved_pc(struct thread_struct *t) -{ - return t->save ? pc_pointer(t->save->pc) : 0; -} - -static inline unsigned long get_css_fp(struct thread_struct *t) -{ - return t->save ? t->save->fp : 0; -} +#define INIT_THREAD { } /* Forward declaration, a strange C thing */ struct task_struct; @@ -98,21 +59,7 @@ unsigned long get_wchan(struct task_struct *p); -#define THREAD_SIZE (8192) - -extern struct task_struct *alloc_task_struct(void); -extern void __free_task_struct(struct task_struct *); -#define get_task_struct(p) atomic_inc(&(p)->thread.refcount) -#define free_task_struct(p) \ - do { \ - if (atomic_dec_and_test(&(p)->thread.refcount)) \ - __free_task_struct((p)); \ - } while (0) - -#define init_task (init_task_union.task) -#define init_stack (init_task_union.stack) - -#define cpu_relax() do { } while (0) +#define cpu_relax() do { } while (0) /* * Create a new kernel thread diff -Nru a/include/asm-arm/procinfo.h b/include/asm-arm/procinfo.h --- a/include/asm-arm/procinfo.h Thu Mar 7 18:17:39 2002 +++ b/include/asm-arm/procinfo.h Thu Mar 7 18:17:39 2002 @@ -14,6 +14,9 @@ #include +struct cpu_tlb_fns; +struct processor; + struct proc_info_item { const char *manufacturer; const char *cpu_name; @@ -37,14 +40,13 @@ const char *elf_name; unsigned int elf_hwcap; struct proc_info_item *info; -#ifdef MULTI_CPU struct processor *proc; -#else - void *unused; -#endif + struct cpu_tlb_fns *tlb; }; #endif /* __ASSEMBLY__ */ + +#define PROC_INFO_SZ 40 #define HWCAP_SWP 1 #define HWCAP_HALF 2 diff -Nru a/include/asm-arm/smplock.h b/include/asm-arm/smplock.h --- a/include/asm-arm/smplock.h Thu Mar 7 18:17:37 2002 +++ b/include/asm-arm/smplock.h Thu Mar 7 18:17:37 2002 @@ -3,31 +3,34 @@ * * Default SMP lock implementation */ +#include #include #include extern spinlock_t kernel_flag; +#ifdef CONFIG_PREEMPT +#define kernel_locked() preempt_get_count() +#else #define kernel_locked() spin_is_locked(&kernel_flag) +#endif /* * Release global kernel lock and global interrupt lock */ -#define release_kernel_lock(task, cpu) \ -do { \ - if (task->lock_depth >= 0) \ - spin_unlock(&kernel_flag); \ - release_irqlock(cpu); \ - __sti(); \ +#define release_kernel_lock(task, cpu) \ +do { \ + if (unlikely(task->lock_depth >= 0)) \ + spin_unlock(&kernel_flag); \ } while (0) /* * Re-acquire the kernel lock */ -#define reacquire_kernel_lock(task) \ -do { \ - if (task->lock_depth >= 0) \ - spin_lock(&kernel_flag); \ +#define reacquire_kernel_lock(task) \ +do { \ + if (unlikely(task->lock_depth >= 0)) \ + spin_lock(&kernel_flag); \ } while (0) @@ -40,8 +43,14 @@ */ static inline void lock_kernel(void) { +#ifdef CONFIG_PREEMPT + if (current->lock_depth == -1) + spin_lock(&kernel_flag); + ++current->lock_depth; +#else if (!++current->lock_depth) spin_lock(&kernel_flag); +#endif } static inline void unlock_kernel(void) diff -Nru a/include/asm-arm/softirq.h b/include/asm-arm/softirq.h --- a/include/asm-arm/softirq.h Thu Mar 7 18:17:42 2002 +++ b/include/asm-arm/softirq.h Thu Mar 7 18:17:42 2002 @@ -5,20 +5,22 @@ #include #define __cpu_bh_enable(cpu) \ - do { barrier(); local_bh_count(cpu)--; } while (0) + do { barrier(); local_bh_count(cpu)--; preempt_enable(); } while (0) #define cpu_bh_disable(cpu) \ - do { local_bh_count(cpu)++; barrier(); } while (0) + do { preempt_disable(); local_bh_count(cpu)++; barrier(); } while (0) #define local_bh_disable() cpu_bh_disable(smp_processor_id()) #define __local_bh_enable() __cpu_bh_enable(smp_processor_id()) #define in_softirq() (local_bh_count(smp_processor_id()) != 0) -#define local_bh_enable() \ +#define _local_bh_enable() \ do { \ unsigned int *ptr = &local_bh_count(smp_processor_id()); \ if (!--*ptr && ptr[-2]) \ __asm__("bl%? __do_softirq": : : "lr");/* out of line */\ } while (0) + +#define local_bh_enable() do { _local_bh_enable(); preempt_enable(); } while (0) #endif /* __ASM_SOFTIRQ_H */ diff -Nru a/include/asm-arm/stat.h b/include/asm-arm/stat.h --- a/include/asm-arm/stat.h Thu Mar 7 18:17:38 2002 +++ b/include/asm-arm/stat.h Thu Mar 7 18:17:38 2002 @@ -42,8 +42,14 @@ * insane amounts of padding around dev_t's. */ struct stat64 { - unsigned short st_dev; - unsigned char __pad0[10]; +#if defined(__ARMEB__) + unsigned char __pad0b[6]; + unsigned short st_dev; +#else + unsigned short st_dev; + unsigned char __pad0b[6]; +#endif + unsigned char __pad0[4]; #define STAT64_HAS_BROKEN_ST_INO 1 unsigned long __st_ino; @@ -53,14 +59,25 @@ unsigned long st_uid; unsigned long st_gid; - unsigned short st_rdev; - unsigned char __pad3[10]; +#if defined(__ARMEB__) + unsigned char __pad3b[6]; + unsigned short st_rdev; +#else /* Must be little */ + unsigned short st_rdev; + unsigned char __pad3b[6]; +#endif + unsigned char __pad3[4]; long long st_size; unsigned long st_blksize; - unsigned long st_blocks; /* Number 512-byte blocks allocated. */ - unsigned long __pad4; /* future possible st_blocks high bits */ +#if defined(__ARMEB__) + unsigned long __pad4; /* Future possible st_blocks hi bits */ + unsigned long st_blocks; /* Number 512-byte blocks allocated. */ +#else /* Must be little */ + unsigned long st_blocks; /* Number 512-byte blocks allocated. */ + unsigned long __pad4; /* Future possible st_blocks hi bits */ +#endif unsigned long st_atime; unsigned long __pad5; diff -Nru a/include/asm-arm/system.h b/include/asm-arm/system.h --- a/include/asm-arm/system.h Thu Mar 7 18:17:38 2002 +++ b/include/asm-arm/system.h Thu Mar 7 18:17:38 2002 @@ -6,6 +6,8 @@ #include #include +struct thread_info; + /* information about the system we're running on */ extern unsigned int system_rev; extern unsigned int system_serial_low; @@ -48,12 +50,13 @@ * `prev' will never be the same as `next'. * The `mb' is to tell GCC not to cache `current' across this call. */ -extern struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *next); +struct thread_info; +extern struct task_struct *__switch_to(struct thread_info *, struct thread_info *); -#define switch_to(prev,next,last) \ - do { \ - last = __switch_to(prev,next); \ - mb(); \ +#define switch_to(prev,next) \ + do { \ + __switch_to(prev->thread_info,next->thread_info); \ + mb(); \ } while (0) /* For spinlocks etc */ diff -Nru a/include/asm-arm/thread_info.h b/include/asm-arm/thread_info.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-arm/thread_info.h Thu Mar 7 18:17:47 2002 @@ -0,0 +1,134 @@ +/* + * linux/include/asm-arm/thread_info.h + * + * Copyright (C) 2002 Russell King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_THREAD_INFO_H +#define __ASM_ARM_THREAD_INFO_H + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ + +struct task_struct; +struct exec_domain; + +#include +#include +#include +#include + +typedef unsigned long mm_segment_t; /* domain register */ + +/* + * low level task data that entry.S needs immediate access to. + */ +struct thread_info { + __u32 flags; /* low level flags */ + __s32 preempt_count; /* 0 => preemptable, <0 => bug */ + mm_segment_t addr_limit; /* address limit */ + __u32 cpu; /* cpu */ + struct cpu_context_save *cpu_context; /* cpu context */ + __u32 cpu_domain; /* cpu domain */ + struct task_struct *task; /* main task structure */ + struct exec_domain *exec_domain; /* execution domain */ + union fp_state fpstate; +}; + +#define INIT_THREAD_INFO(tsk) \ +{ \ + task: &tsk, \ + exec_domain: &default_exec_domain, \ + flags: 0, \ + preempt_count: 0, \ + addr_limit: KERNEL_DS, \ + INIT_EXTRA_THREAD_INFO, \ +} + +#define init_thread_info (init_thread_union.thread_info) +#define init_stack (init_thread_union.stack) + +/* + * how to get the thread information struct from C + */ +static inline struct thread_info *current_thread_info(void) __attribute__ (( __const__ )); + +static inline struct thread_info *current_thread_info(void) +{ + register unsigned long sp asm ("sp"); + return (struct thread_info *)(sp & ~0x1fff); +} + +#define THREAD_SIZE (8192) + +extern struct thread_info *alloc_thread_info(void); +extern void free_thread_info(struct thread_info *); + +#define get_thread_info(ti) get_task_struct((ti)->task) +#define put_thread_info(ti) put_task_struct((ti)->task) + +static inline unsigned long __thread_saved_pc(struct thread_info *thread) +{ + struct cpu_context_save *context = thread->cpu_context; + + return context ? pc_pointer(context->pc) : 0; +} + +static inline unsigned long __thread_saved_fp(struct thread_info *thread) +{ + struct cpu_context_save *context = thread->cpu_context; + + return context ? context->fp : 0; +} + +#define thread_saved_pc(tsk) __thread_saved_pc((tsk)->thread_info) +#define thread_saved_fp(tsk) __thread_saved_fp((tsk)->thread_info) + +#else /* !__ASSEMBLY__ */ + +#define TI_FLAGS 0 +#define TI_PREEMPT 4 +#define TI_ADDR_LIMIT 8 +#define TI_CPU 12 +#define TI_CPU_SAVE 16 +#define TI_CPU_DOMAIN 20 +#define TI_TASK 24 +#define TI_EXEC_DOMAIN 28 +#define TI_FPSTATE 32 + +#endif + +/* + * thread information flags: + * TIF_SYSCALL_TRACE - syscall trace active + * TIF_NOTIFY_RESUME - resumption notification requested + * TIF_SIGPENDING - signal pending + * TIF_NEED_RESCHED - rescheduling necessary + * TIF_USEDFPU - FPU was used by this task this quantum (SMP) + * TIF_POLLING_NRFLAG - true if poll_idle() is polling TIF_NEED_RESCHED + */ +#define TIF_NOTIFY_RESUME 0 +#define TIF_SIGPENDING 1 +#define TIF_NEED_RESCHED 2 +#define TIF_SYSCALL_TRACE 8 +#define TIF_USED_FPU 16 +#define TIF_POLLING_NRFLAG 17 + +#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) +#define _TIF_SIGPENDING (1 << TIF_SIGPENDING) +#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) +#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) +#define _TIF_USED_FPU (1 << TIF_USED_FPU) +#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) + +/* + * Change these and you break ASM code in entry-common.S + */ +#define _TIF_WORK_MASK 0x000000ff + +#endif /* __KERNEL__ */ +#endif /* __ASM_ARM_THREAD_INFO_H */ diff -Nru a/include/asm-arm/uaccess.h b/include/asm-arm/uaccess.h --- a/include/asm-arm/uaccess.h Thu Mar 7 18:17:38 2002 +++ b/include/asm-arm/uaccess.h Thu Mar 7 18:17:38 2002 @@ -32,7 +32,7 @@ extern unsigned long search_exception_table(unsigned long); #define get_ds() (KERNEL_DS) -#define get_fs() (current->addr_limit) +#define get_fs() (current_thread_info()->addr_limit) #define segment_eq(a,b) ((a) == (b)) #include diff -Nru a/include/asm-arm/unistd.h b/include/asm-arm/unistd.h --- a/include/asm-arm/unistd.h Thu Mar 7 18:17:42 2002 +++ b/include/asm-arm/unistd.h Thu Mar 7 18:17:42 2002 @@ -244,6 +244,19 @@ #define __NR_security (__NR_SYSCALL_BASE+223) #define __NR_gettid (__NR_SYSCALL_BASE+224) #define __NR_readahead (__NR_SYSCALL_BASE+225) +#define __NR_setxattr (__NR_SYSCALL_BASE+226) +#define __NR_lsetxattr (__NR_SYSCALL_BASE+227) +#define __NR_fsetxattr (__NR_SYSCALL_BASE+228) +#define __NR_getxattr (__NR_SYSCALL_BASE+229) +#define __NR_lgetxattr (__NR_SYSCALL_BASE+230) +#define __NR_fgetxattr (__NR_SYSCALL_BASE+231) +#define __NR_listxattr (__NR_SYSCALL_BASE+232) +#define __NR_llistxattr (__NR_SYSCALL_BASE+233) +#define __NR_flistxattr (__NR_SYSCALL_BASE+234) +#define __NR_removexattr (__NR_SYSCALL_BASE+235) +#define __NR_lremovexattr (__NR_SYSCALL_BASE+236) +#define __NR_fremovexattr (__NR_SYSCALL_BASE+237) +#define __NR_tkill (__NR_SYSCALL_BASE+238) /* * The following SWIs are ARM private. diff -Nru a/include/asm-cris/ide.h b/include/asm-cris/ide.h --- a/include/asm-cris/ide.h Thu Mar 7 18:17:39 2002 +++ b/include/asm-cris/ide.h Thu Mar 7 18:17:39 2002 @@ -146,8 +146,7 @@ unsigned char IN_BYTE(ide_ioreg_t reg); /* this tells ide.h not to define the standard macros */ -#define HAVE_ARCH_OUT_BYTE -#define HAVE_ARCH_IN_BYTE +#define HAVE_ARCH_IN_OUT 1 #endif /* __KERNEL__ */ diff -Nru a/include/asm-i386/fixmap.h b/include/asm-i386/fixmap.h --- a/include/asm-i386/fixmap.h Thu Mar 7 18:17:37 2002 +++ b/include/asm-i386/fixmap.h Thu Mar 7 18:17:37 2002 @@ -65,6 +65,11 @@ FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, #endif + __end_of_permanent_fixed_addresses, + /* temporary boot-time mappings, used before ioremap() is functional */ +#define NR_FIX_BTMAPS 16 + FIX_BTMAP_END = __end_of_permanent_fixed_addresses, + FIX_BTMAP_BEGIN = FIX_BTMAP_END + NR_FIX_BTMAPS - 1, __end_of_fixed_addresses }; @@ -86,8 +91,8 @@ * at the top of mem.. */ #define FIXADDR_TOP (0xffffe000UL) -#define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT) -#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE) +#define __FIXADDR_SIZE (__end_of_permanent_fixed_addresses << PAGE_SHIFT) +#define FIXADDR_START (FIXADDR_TOP - __FIXADDR_SIZE) #define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT)) diff -Nru a/include/asm-i386/hw_irq.h b/include/asm-i386/hw_irq.h --- a/include/asm-i386/hw_irq.h Thu Mar 7 18:17:44 2002 +++ b/include/asm-i386/hw_irq.h Thu Mar 7 18:17:44 2002 @@ -35,14 +35,13 @@ * into a single vector (CALL_FUNCTION_VECTOR) to save vector space. * TLB, reschedule and local APIC vectors are performance-critical. * - * Vectors 0xf0-0xf9 are free (reserved for future Linux use). + * Vectors 0xf0-0xfa are free (reserved for future Linux use). */ #define SPURIOUS_APIC_VECTOR 0xff #define ERROR_APIC_VECTOR 0xfe #define INVALIDATE_TLB_VECTOR 0xfd #define RESCHEDULE_VECTOR 0xfc -#define TASK_MIGRATION_VECTOR 0xfb -#define CALL_FUNCTION_VECTOR 0xfa +#define CALL_FUNCTION_VECTOR 0xfb /* * Local APIC timer IRQ vector is on a different priority level, diff -Nru a/include/asm-i386/io.h b/include/asm-i386/io.h --- a/include/asm-i386/io.h Thu Mar 7 18:17:44 2002 +++ b/include/asm-i386/io.h Thu Mar 7 18:17:44 2002 @@ -39,7 +39,8 @@ #define IO_SPACE_LIMIT 0xffff #define XQUAD_PORTIO_BASE 0xfe400000 -#define XQUAD_PORTIO_LEN 0x40000 /* 256k per quad. Only remapping 1st */ +#define XQUAD_PORTIO_QUAD 0x40000 /* 256k per quad. */ +#define XQUAD_PORTIO_LEN 0x80000 /* Only remapping first 2 quads */ #ifdef __KERNEL__ @@ -95,6 +96,14 @@ extern void iounmap(void *addr); /* + * bt_ioremap() and bt_iounmap() are for temporary early boot-time + * mappings, before the real ioremap() is functional. + * A boot-time mapping is currently limited to at most 16 pages. + */ +extern void *bt_ioremap(unsigned long offset, unsigned long size); +extern void bt_iounmap(void *addr, unsigned long size); + +/* * ISA I/O bus memory addresses are 1:1 with the physical address. */ #define isa_virt_to_bus virt_to_phys @@ -253,52 +262,65 @@ __asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1" #ifdef CONFIG_MULTIQUAD -/* Make the default portio routines operate on quad 0 for now */ -#define __OUT(s,s1,x) \ -__OUT1(s##_local,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \ -__OUT1(s##_p_local,x) __OUT2(s,s1,"w") __FULL_SLOW_DOWN_IO : : "a" (value), "Nd" (port));} \ -__OUTQ0(s,s,x) \ -__OUTQ0(s,s##_p,x) -#else -#define __OUT(s,s1,x) \ -__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \ -__OUT1(s##_p,x) __OUT2(s,s1,"w") __FULL_SLOW_DOWN_IO : : "a" (value), "Nd" (port));} -#endif /* CONFIG_MULTIQUAD */ - -#ifdef CONFIG_MULTIQUAD -#define __OUTQ0(s,ss,x) /* Do the equivalent of the portio op on quad 0 */ \ +#define __OUTQ(s,ss,x) /* Do the equivalent of the portio op on quads */ \ static inline void out##ss(unsigned x value, unsigned short port) { \ if (xquad_portio) \ write##s(value, (unsigned long) xquad_portio + port); \ else /* We're still in early boot, running on quad 0 */ \ out##ss##_local(value, port); \ -} +} \ +static inline void out##ss##_quad(unsigned x value, unsigned short port, int quad) { \ + if (xquad_portio) \ + write##s(value, (unsigned long) xquad_portio + (XQUAD_PORTIO_QUAD*quad)\ + + port); \ +} -#define __INQ0(s,ss) /* Do the equivalent of the portio op on quad 0 */ \ +#define __INQ(s,ss) /* Do the equivalent of the portio op on quads */ \ static inline RETURN_TYPE in##ss(unsigned short port) { \ if (xquad_portio) \ return read##s((unsigned long) xquad_portio + port); \ else /* We're still in early boot, running on quad 0 */ \ return in##ss##_local(port); \ +} \ +static inline RETURN_TYPE in##ss##_quad(unsigned short port, int quad) { \ + if (xquad_portio) \ + return read##s((unsigned long) xquad_portio + (XQUAD_PORTIO_QUAD*quad)\ + + port); \ + else\ + return 0;\ } #endif /* CONFIG_MULTIQUAD */ +#ifndef CONFIG_MULTIQUAD +#define __OUT(s,s1,x) \ +__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \ +__OUT1(s##_p,x) __OUT2(s,s1,"w") __FULL_SLOW_DOWN_IO : : "a" (value), "Nd" (port));} +#else +/* Make the default portio routines operate on quad 0 */ +#define __OUT(s,s1,x) \ +__OUT1(s##_local,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \ +__OUT1(s##_p_local,x) __OUT2(s,s1,"w") __FULL_SLOW_DOWN_IO : : "a" (value), "Nd" (port));} \ +__OUTQ(s,s,x) \ +__OUTQ(s,s##_p,x) +#endif /* CONFIG_MULTIQUAD */ + #define __IN1(s) \ static inline RETURN_TYPE in##s(unsigned short port) { RETURN_TYPE _v; #define __IN2(s,s1,s2) \ __asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0" -#ifdef CONFIG_MULTIQUAD -#define __IN(s,s1,i...) \ -__IN1(s##_local) __IN2(s,s1,"w") : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ -__IN1(s##_p_local) __IN2(s,s1,"w") __FULL_SLOW_DOWN_IO : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ -__INQ0(s,s) \ -__INQ0(s,s##_p) -#else +#ifndef CONFIG_MULTIQUAD #define __IN(s,s1,i...) \ __IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ __IN1(s##_p) __IN2(s,s1,"w") __FULL_SLOW_DOWN_IO : "=a" (_v) : "Nd" (port) ,##i ); return _v; } +#else +/* Make the default portio routines operate on quad 0 */ +#define __IN(s,s1,i...) \ +__IN1(s##_local) __IN2(s,s1,"w") : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ +__IN1(s##_p_local) __IN2(s,s1,"w") __FULL_SLOW_DOWN_IO : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ +__INQ(s,s) \ +__INQ(s,s##_p) #endif /* CONFIG_MULTIQUAD */ #define __INS(s) \ diff -Nru a/include/asm-i386/mpspec.h b/include/asm-i386/mpspec.h --- a/include/asm-i386/mpspec.h Thu Mar 7 18:17:36 2002 +++ b/include/asm-i386/mpspec.h Thu Mar 7 18:17:36 2002 @@ -198,6 +198,9 @@ MP_BUS_MCA }; extern int mp_bus_id_to_type [MAX_MP_BUSSES]; +extern int mp_bus_id_to_node [MAX_MP_BUSSES]; +extern int mp_bus_id_to_local [MAX_MP_BUSSES]; +extern int quad_local_to_mp_bus_id [NR_CPUS/4][4]; extern int mp_bus_id_to_pci_bus [MAX_MP_BUSSES]; extern unsigned int boot_cpu_physical_apicid; diff -Nru a/include/asm-i386/siginfo.h b/include/asm-i386/siginfo.h --- a/include/asm-i386/siginfo.h Thu Mar 7 18:17:40 2002 +++ b/include/asm-i386/siginfo.h Thu Mar 7 18:17:40 2002 @@ -108,6 +108,7 @@ #define SI_ASYNCIO -4 /* sent by AIO completion */ #define SI_SIGIO -5 /* sent by queued SIGIO */ #define SI_TKILL -6 /* sent by tkill system call */ +#define SI_DETHREAD -7 /* sent by execve() killing subsidiary threads */ #define SI_FROMUSER(siptr) ((siptr)->si_code <= 0) #define SI_FROMKERNEL(siptr) ((siptr)->si_code > 0) diff -Nru a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h --- a/include/asm-i386/unistd.h Thu Mar 7 18:17:45 2002 +++ b/include/asm-i386/unistd.h Thu Mar 7 18:17:45 2002 @@ -243,6 +243,7 @@ #define __NR_lremovexattr 236 #define __NR_fremovexattr 237 #define __NR_tkill 238 +#define __NR_sendfile64 239 /* user-visible error numbers are in the range -1 - -124: see */ diff -Nru a/include/asm-ia64/sn/pci/pcibr.h b/include/asm-ia64/sn/pci/pcibr.h --- a/include/asm-ia64/sn/pci/pcibr.h Thu Mar 7 18:17:41 2002 +++ b/include/asm-ia64/sn/pci/pcibr.h Thu Mar 7 18:17:41 2002 @@ -47,7 +47,7 @@ * These functions are normal device driver entry points * and are called along with the similar entry points from * other device drivers. They are included here as documentation - * of their existance and purpose. + * of their existence and purpose. * * pcibr_init() is called to inform us that there is a pcibr driver * configured into the kernel; it is responsible for registering diff -Nru a/include/asm-ppc/highmem.h b/include/asm-ppc/highmem.h --- a/include/asm-ppc/highmem.h Thu Mar 7 18:17:43 2002 +++ b/include/asm-ppc/highmem.h Thu Mar 7 18:17:43 2002 @@ -106,7 +106,7 @@ static inline void kunmap_atomic(void *kvaddr, enum km_type type) { #if HIGHMEM_DEBUG - unsigned long vaddr = (unsigned long) kvaddr; + unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK; unsigned int idx = type + KM_TYPE_NR*smp_processor_id(); if (vaddr < KMAP_FIX_BEGIN) // FIXME diff -Nru a/include/asm-ppc/kmap_types.h b/include/asm-ppc/kmap_types.h --- a/include/asm-ppc/kmap_types.h Thu Mar 7 18:17:39 2002 +++ b/include/asm-ppc/kmap_types.h Thu Mar 7 18:17:39 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.kmap_types.h 1.9 08/29/01 14:03:05 paulus + * BK Id: %F% %I% %G% %U% %#% */ #ifdef __KERNEL__ #ifndef _ASM_KMAP_TYPES_H @@ -12,6 +12,8 @@ KM_USER0, KM_USER1, KM_BIO_IRQ, + KM_PTE0, + KM_PTE1, KM_TYPE_NR }; diff -Nru a/include/asm-ppc/pgalloc.h b/include/asm-ppc/pgalloc.h --- a/include/asm-ppc/pgalloc.h Thu Mar 7 18:17:36 2002 +++ b/include/asm-ppc/pgalloc.h Thu Mar 7 18:17:36 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.pgalloc.h 1.9 05/17/01 18:14:25 cort + * BK Id: %F% %I% %G% %U% %#% */ #ifdef __KERNEL__ #ifndef _PPC_PGALLOC_H @@ -7,55 +7,12 @@ #include #include +#include #include -/* - * This is handled very differently on the PPC since out page tables - * are all 0's and I want to be able to use these zero'd pages elsewhere - * as well - it gives us quite a speedup. - * - * Note that the SMP/UP versions are the same but we don't need a - * per cpu list of zero pages because we do the zero-ing with the cache - * off and the access routines are lock-free but the pgt cache stuff - * is per-cpu since it isn't done with any lock-free access routines - * (although I think we need arch-specific routines so I can do lock-free). - * - * I need to generalize this so we can use it for other arch's as well. - * -- Cort - */ -#ifdef CONFIG_SMP -#define quicklists cpu_data[smp_processor_id()] -#else -extern struct pgtable_cache_struct { - unsigned long *pgd_cache; - unsigned long *pte_cache; - unsigned long pgtable_cache_sz; -} quicklists; -#endif - -#define pgd_quicklist (quicklists.pgd_cache) -#define pmd_quicklist ((unsigned long *)0) -#define pte_quicklist (quicklists.pte_cache) -#define pgtable_cache_size (quicklists.pgtable_cache_sz) - -extern unsigned long *zero_cache; /* head linked list of pre-zero'd pages */ -extern atomic_t zero_sz; /* # currently pre-zero'd pages */ -extern atomic_t zeropage_hits; /* # zero'd pages request that we've done */ -extern atomic_t zeropage_calls; /* # zero'd pages request that've been made */ -extern atomic_t zerototal; /* # pages zero'd over time */ - -#define zero_quicklist (zero_cache) -#define zero_cache_sz (zero_sz) -#define zero_cache_calls (zeropage_calls) -#define zero_cache_hits (zeropage_hits) -#define zero_cache_total (zerototal) - -/* return a pre-zero'd page from the list, return NULL if none available -- Cort */ -extern unsigned long get_zero_page_fast(void); - extern void __bad_pte(pmd_t *pmd); -extern __inline__ pgd_t *get_pgd_slow(void) +static inline pgd_t *pgd_alloc(struct mm_struct *mm) { pgd_t *ret; @@ -64,85 +21,75 @@ return ret; } -extern __inline__ pgd_t *get_pgd_fast(void) -{ - unsigned long *ret; - - if ((ret = pgd_quicklist) != NULL) { - pgd_quicklist = (unsigned long *)(*ret); - ret[0] = 0; - pgtable_cache_size--; - } else - ret = (unsigned long *)get_pgd_slow(); - return (pgd_t *)ret; -} - -extern __inline__ void free_pgd_fast(pgd_t *pgd) -{ - *(unsigned long **)pgd = pgd_quicklist; - pgd_quicklist = (unsigned long *) pgd; - pgtable_cache_size++; -} - -extern __inline__ void free_pgd_slow(pgd_t *pgd) +extern __inline__ void pgd_free(pgd_t *pgd) { free_page((unsigned long)pgd); } -#define pgd_free(pgd) free_pgd_fast(pgd) -#define pgd_alloc(mm) get_pgd_fast() - /* * We don't have any real pmd's, and this code never triggers because * the pgd will always be present.. */ -#define pmd_alloc_one_fast(mm, address) ({ BUG(); ((pmd_t *)1); }) #define pmd_alloc_one(mm,address) ({ BUG(); ((pmd_t *)2); }) #define pmd_free(x) do { } while (0) #define pgd_populate(mm, pmd, pte) BUG() -static inline pte_t *pte_alloc_one(struct mm_struct *mm, unsigned long address) +static inline pte_t * +pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) { pte_t *pte; extern int mem_init_done; extern void *early_get_page(void); + int timeout = 0; - if (mem_init_done) - pte = (pte_t *) __get_free_page(GFP_KERNEL); - else + if (mem_init_done) { + while ((pte = (pte_t *) __get_free_page(GFP_KERNEL)) == NULL + && ++timeout < 10) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ); + } + } else pte = (pte_t *) early_get_page(); if (pte != NULL) clear_page(pte); return pte; } -static inline pte_t *pte_alloc_one_fast(struct mm_struct *mm, unsigned long address) +static inline struct page * +pte_alloc_one(struct mm_struct *mm, unsigned long address) { - unsigned long *ret; + struct page *pte; + int timeout = 0; +#ifdef CONFIG_HIGHPTE + int flags = GFP_KERNEL | __GFP_HIGHMEM; +#else + int flags = GFP_KERNEL; +#endif - if ((ret = pte_quicklist) != NULL) { - pte_quicklist = (unsigned long *)(*ret); - ret[0] = 0; - pgtable_cache_size--; + while ((pte = alloc_pages(flags, 0)) == NULL) { + if (++timeout >= 10) + return NULL; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ); } - return (pte_t *)ret; + clear_highpage(pte); + return pte; } -extern __inline__ void pte_free_fast(pte_t *pte) +static inline void pte_free_kernel(pte_t *pte) { - *(unsigned long **)pte = pte_quicklist; - pte_quicklist = (unsigned long *) pte; - pgtable_cache_size++; + free_page((unsigned long)pte); } -extern __inline__ void pte_free_slow(pte_t *pte) +static inline void pte_free(struct page *pte) { - free_page((unsigned long)pte); + __free_page(pte); } -#define pte_free(pte) pte_free_slow(pte) - -#define pmd_populate(mm, pmd, pte) (pmd_val(*(pmd)) = (unsigned long) (pte)) +#define pmd_populate_kernel(mm, pmd, pte) \ + (pmd_val(*(pmd)) = __pa(pte)) +#define pmd_populate(mm, pmd, pte) \ + (pmd_val(*(pmd)) = ((pte) - mem_map) << PAGE_SHIFT) extern int do_check_pgt_cache(int, int); diff -Nru a/include/asm-ppc/pgtable.h b/include/asm-ppc/pgtable.h --- a/include/asm-ppc/pgtable.h Thu Mar 7 18:17:40 2002 +++ b/include/asm-ppc/pgtable.h Thu Mar 7 18:17:40 2002 @@ -13,6 +13,7 @@ #include /* For TASK_SIZE */ #include #include +#include extern void _tlbie(unsigned long address); extern void _tlbia(void); @@ -98,6 +99,7 @@ struct page *page, unsigned long addr, int len); extern void flush_icache_range(unsigned long, unsigned long); extern void __flush_dcache_icache(void *page_va); +extern void __flush_dcache_icache_phys(unsigned long physaddr); extern void flush_dcache_page(struct page *page); extern void flush_icache_page(struct vm_area_struct *vma, struct page *page); @@ -274,7 +276,6 @@ #define _PAGE_HWWRITE 0x0100 /* h/w write enable: never set in Linux PTE */ #define _PAGE_USER 0x0800 /* One of the PP bits, the other is USER&~RW */ -#define _PMD_PRESENT 0x0001 #define _PMD_PAGE_MASK 0x000c #define _PMD_PAGE_8M 0x000c @@ -385,8 +386,8 @@ #define pte_clear(ptep) do { set_pte((ptep), __pte(0)); } while (0) #define pmd_none(pmd) (!pmd_val(pmd)) -#define pmd_bad(pmd) ((pmd_val(pmd) & ~PAGE_MASK) != 0) -#define pmd_present(pmd) ((pmd_val(pmd) & PAGE_MASK) != 0) +#define pmd_bad(pmd) (0) +#define pmd_present(pmd) (pmd_val(pmd) != 0) #define pmd_clear(pmdp) do { pmd_val(*(pmdp)) = 0; } while (0) #define pte_page(x) (mem_map+(unsigned long)((pte_val(x)-PPC_MEMSTART) >> PAGE_SHIFT)) @@ -530,7 +531,10 @@ #define pte_same(A,B) (((pte_val(A) ^ pte_val(B)) & ~_PAGE_HASHPTE) == 0) -#define pmd_page(pmd) (pmd_val(pmd) & PAGE_MASK) +#define pmd_page_kernel(pmd) \ + ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK)) +#define pmd_page(pmd) \ + (mem_map + (pmd_val(pmd) >> PAGE_SHIFT)) /* to find an entry in a kernel page-table-directory */ #define pgd_offset_k(address) pgd_offset(&init_mm, address) @@ -545,11 +549,18 @@ return (pmd_t *) dir; } -/* Find an entry in the third-level page table.. */ -static inline pte_t * pte_offset(pmd_t * dir, unsigned long address) -{ - return (pte_t *) pmd_page(*dir) + ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)); -} +/* Find an entry in the third-level page table.. */ +#define __pte_offset(address) \ + ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) +#define pte_offset_kernel(dir, addr) \ + ((pte_t *) pmd_page_kernel(*(dir)) + __pte_offset(addr)) +#define pte_offset_map(dir, addr) \ + ((pte_t *) kmap_atomic(pmd_page(*(dir)), KM_PTE0) + __pte_offset(addr)) +#define pte_offset_map_nested(dir, addr) \ + ((pte_t *) kmap_atomic(pmd_page(*(dir)), KM_PTE1) + __pte_offset(addr)) + +#define pte_unmap(pte) kunmap_atomic(pte, KM_PTE0) +#define pte_unmap_nested(pte) kunmap_atomic(pte, KM_PTE1) extern pgd_t swapper_pg_dir[1024]; extern void paging_init(void); @@ -558,10 +569,12 @@ * When flushing the tlb entry for a page, we also need to flush the hash * table entry. flush_hash_page is assembler (for speed) in hashtable.S. */ -extern int flush_hash_page(unsigned context, unsigned long va, pte_t *ptep); +extern int flush_hash_pages(unsigned context, unsigned long va, + unsigned long pmdval, int count); /* Add an HPTE to the hash table */ -extern void add_hash_page(unsigned context, unsigned long va, pte_t *ptep); +extern void add_hash_page(unsigned context, unsigned long va, + unsigned long pmdval); /* * Encode and decode a swap entry. diff -Nru a/include/asm-x86_64/bitops.h b/include/asm-x86_64/bitops.h --- a/include/asm-x86_64/bitops.h Thu Mar 7 18:17:42 2002 +++ b/include/asm-x86_64/bitops.h Thu Mar 7 18:17:42 2002 @@ -413,6 +413,16 @@ #ifdef __KERNEL__ +static inline int sched_find_first_bit(unsigned long *b) +{ + if (b[0]) + return __ffs(b[0]); + if (b[1]) + return __ffs(b[1]) + 64; + if (b[2]) + return __ffs(b[2]) + 128; +} + /** * ffs - find first bit set * @x: the word to search diff -Nru a/include/asm-x86_64/mmu_context.h b/include/asm-x86_64/mmu_context.h --- a/include/asm-x86_64/mmu_context.h Thu Mar 7 18:17:41 2002 +++ b/include/asm-x86_64/mmu_context.h Thu Mar 7 18:17:41 2002 @@ -7,33 +7,6 @@ #include /* - * Every architecture must define this function. It's the fastest - * way of searching a 168-bit bitmap where the first 128 bits are - * unlikely to be set. It's guaranteed that at least one of the 168 - * bits is cleared. - */ -#if MAX_RT_PRIO != 128 || MAX_PRIO != 168 -# error update this function. -#endif - -static inline int __sched_find_first_bit(unsigned long *b) -{ - if (b[0]) - return __ffs(b[0]); - if (b[1]) - return __ffs(b[1]) + 64; - if (b[2]) - return __ffs(b[2]) + 128; -} - -static inline int sched_find_first_bit(unsigned long *b) -{ - int n = __sched_find_first_bit(b); - BUG_ON((unsigned)n > 167); - return n; -} - -/* * possibly do the LDT unload here? */ #define destroy_context(mm) do { } while(0) diff -Nru a/include/asm-x86_64/page.h b/include/asm-x86_64/page.h --- a/include/asm-x86_64/page.h Thu Mar 7 18:17:37 2002 +++ b/include/asm-x86_64/page.h Thu Mar 7 18:17:37 2002 @@ -112,6 +112,8 @@ #define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT)) #define VALID_PAGE(page) ((page - mem_map) < max_mapnr) +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) #endif /* __KERNEL__ */ diff -Nru a/include/asm-x86_64/pda.h b/include/asm-x86_64/pda.h --- a/include/asm-x86_64/pda.h Thu Mar 7 18:17:42 2002 +++ b/include/asm-x86_64/pda.h Thu Mar 7 18:17:42 2002 @@ -19,11 +19,6 @@ struct task_struct *pcurrent; /* Current process */ int irqcount; /* Irq nesting counter. Starts with -1 */ int cpunumber; /* Logical CPU number */ - /* XXX: could be a single list */ - unsigned long *pgd_quick; - unsigned long *pmd_quick; - unsigned long *pte_quick; - unsigned long pgtable_cache_sz; char *irqstackptr; unsigned int __softirq_pending; unsigned int __local_irq_count; diff -Nru a/include/asm-x86_64/pgalloc.h b/include/asm-x86_64/pgalloc.h --- a/include/asm-x86_64/pgalloc.h Thu Mar 7 18:17:45 2002 +++ b/include/asm-x86_64/pgalloc.h Thu Mar 7 18:17:45 2002 @@ -8,173 +8,74 @@ #include #include -#define inc_pgcache_size() add_pda(pgtable_cache_sz,1UL) -#define dec_pgcache_size() sub_pda(pgtable_cache_sz,1UL) - -#define pmd_populate(mm, pmd, pte) \ +#define pmd_populate_kernel(mm, pmd, pte) \ set_pmd(pmd, __pmd(_PAGE_TABLE | __pa(pte))) #define pgd_populate(mm, pgd, pmd) \ set_pgd(pgd, __pgd(_PAGE_TABLE | __pa(pmd))) -extern __inline__ pmd_t *get_pmd_slow(void) +static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, struct page *pte) { - pmd_t *ret = (pmd_t *)__get_free_page(GFP_KERNEL); - - if (ret) - memset(ret, 0, PAGE_SIZE); - return ret; -} - -extern __inline__ pmd_t *get_pmd_fast(void) -{ - unsigned long *ret; - - preempt_disable(); - ret = read_pda(pmd_quick); - if (ret) { - write_pda(pmd_quick, (unsigned long *)(*ret)); - ret[0] = 0; - dec_pgcache_size(); - } - preempt_enable(); - if (!ret) - ret = (unsigned long *)get_pmd_slow(); - return (pmd_t *)ret; + set_pmd(pmd, __pmd(_PAGE_TABLE | + ((u64)(pte - mem_map) << PAGE_SHIFT))); } -extern __inline__ void pmd_free(pmd_t *pmd) +extern __inline__ pmd_t *get_pmd(void) { - preempt_disable(); - *(unsigned long *)pmd = (unsigned long) read_pda(pmd_quick); - write_pda(pmd_quick,(unsigned long *) pmd); - inc_pgcache_size(); - preempt_enable(); + return (pmd_t *)get_zeroed_page(GFP_KERNEL); } -extern __inline__ void pmd_free_slow(pmd_t *pmd) +extern __inline__ void pmd_free(pmd_t *pmd) { if ((unsigned long)pmd & (PAGE_SIZE-1)) BUG(); free_page((unsigned long)pmd); } -static inline pmd_t *pmd_alloc_one_fast (struct mm_struct *mm, unsigned long addr) -{ - unsigned long *ret; - - preempt_disable(); - ret = (unsigned long *)read_pda(pmd_quick); - - if (__builtin_expect(ret != NULL, 1)) { - write_pda(pmd_quick, (unsigned long *)(*ret)); - ret[0] = 0; - dec_pgcache_size(); - } - preempt_enable(); - return (pmd_t *)ret; -} - static inline pmd_t *pmd_alloc_one (struct mm_struct *mm, unsigned long addr) { - pmd_t *pmd = (pmd_t *) __get_free_page(GFP_KERNEL); - - if (__builtin_expect(pmd != NULL, 1)) - clear_page(pmd); - return pmd; -} - - -static inline pgd_t *pgd_alloc_one_fast (void) -{ - unsigned long *ret; - - preempt_disable(); - ret = read_pda(pgd_quick); - if (likely(ret != NULL)) { - write_pda(pgd_quick,(unsigned long *)(*ret)); - ret[0] = 0; - dec_pgcache_size(); - } - preempt_enable(); - return (pgd_t *) ret; + return (pmd_t *) get_zeroed_page(GFP_KERNEL); } static inline pgd_t *pgd_alloc (struct mm_struct *mm) { - /* the VM system never calls pgd_alloc_one_fast(), so we do it here. */ - pgd_t *pgd = pgd_alloc_one_fast(); - - if (pgd == NULL) { - pgd = (pgd_t *)__get_free_page(GFP_KERNEL); - if (__builtin_expect(pgd != NULL, 1)) - clear_page(pgd); - } - return pgd; + return (pgd_t *)get_zeroed_page(GFP_KERNEL); } static inline void pgd_free (pgd_t *pgd) { - preempt_disable(); - *(unsigned long *)pgd = (unsigned long) read_pda(pgd_quick); - write_pda(pgd_quick,(unsigned long *) pgd); - inc_pgcache_size(); - preempt_enable(); -} - - -static inline void pgd_free_slow (pgd_t *pgd) -{ if ((unsigned long)pgd & (PAGE_SIZE-1)) BUG(); free_page((unsigned long)pgd); } - -static inline pte_t *pte_alloc_one(struct mm_struct *mm, unsigned long address) +static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) { - pte_t *pte; - - pte = (pte_t *) __get_free_page(GFP_KERNEL); - if (pte) - clear_page(pte); - return pte; + return (pte_t *) get_zeroed_page(GFP_KERNEL); } -extern __inline__ pte_t *pte_alloc_one_fast(struct mm_struct *mm, unsigned long address) +static inline struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) { - unsigned long *ret; - - preempt_disable(); - if(__builtin_expect((ret = read_pda(pte_quick)) != NULL, !0)) { - write_pda(pte_quick, (unsigned long *)(*ret)); - ret[0] = ret[1]; - dec_pgcache_size(); - } - preempt_enable(); - return (pte_t *)ret; + void *p = (void *)get_zeroed_page(GFP_KERNEL); + if (!p) + return NULL; + return virt_to_page(p); } -/* Should really implement gc for free page table pages. This could be done with - a reference count in struct page. */ +/* Should really implement gc for free page table pages. This could be + done with a reference count in struct page. */ -extern __inline__ void pte_free(pte_t *pte) -{ - preempt_disable(); - *(unsigned long *)pte = (unsigned long) read_pda(pte_quick); - write_pda(pte_quick, (unsigned long *) pte); - inc_pgcache_size(); - preempt_enable(); -} - -extern __inline__ void pte_free_slow(pte_t *pte) +extern __inline__ void pte_free_kernel(pte_t *pte) { if ((unsigned long)pte & (PAGE_SIZE-1)) BUG(); free_page((unsigned long)pte); } +extern inline void pte_free(struct page *pte) +{ + __free_page(pte); +} -extern int do_check_pgt_cache(int, int); /* * TLB flushing: diff -Nru a/include/asm-x86_64/pgtable.h b/include/asm-x86_64/pgtable.h --- a/include/asm-x86_64/pgtable.h Thu Mar 7 18:17:39 2002 +++ b/include/asm-x86_64/pgtable.h Thu Mar 7 18:17:39 2002 @@ -26,7 +26,7 @@ extern pmd_t level2_kernel_pgt[512]; extern void paging_init(void); -/* Caches aren't brain-dead on the intel. */ +/* Caches aren't brain-dead. */ #define flush_cache_all() do { } while (0) #define flush_cache_mm(mm) do { } while (0) #define flush_cache_range(vma, start, end) do { } while (0) @@ -35,6 +35,7 @@ #define flush_dcache_page(page) do { } while (0) #define flush_icache_range(start, end) do { } while (0) #define flush_icache_page(vma,pg) do { } while (0) +#define flush_icache_user_range(vma,pg,adr,len) do { } while (0) #define __flush_tlb() \ do { \ @@ -341,8 +342,10 @@ #define page_pte(page) page_pte_prot(page, __pgprot(0)) -#define pmd_page(pmd) \ +#define pmd_page_kernel(pmd) \ ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK)) +#define pmd_page(pmd) \ + (mem_map + (pmd_val(pmd) >> PAGE_SHIFT)) /* to find an entry in a page-table-directory. */ #define pgd_index(address) ((address >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) @@ -360,8 +363,14 @@ /* Find an entry in the third-level page table.. */ #define __pte_offset(address) \ ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) -#define pte_offset(dir, address) ((pte_t *) pmd_page(*(dir)) + \ +#define pte_offset_kernel(dir, address) ((pte_t *) pmd_page_kernel(*(dir)) + \ __pte_offset(address)) + +#define pte_offset_map(dir,address) pte_offset_kernel(dir,address) +#define pte_offset_map_nested(dir,address) pte_offset_kernel(dir,address) +#define pte_unmap(pte) /* NOP */ +#define pte_unmap_nested(pte) /* NOP */ + /* never use these in the common code */ #define level4_page(level4) ((unsigned long) __va(level4_val(level4) & PAGE_MASK)) diff -Nru a/include/asm-x86_64/rwsem.h b/include/asm-x86_64/rwsem.h --- a/include/asm-x86_64/rwsem.h Thu Mar 7 18:17:46 2002 +++ b/include/asm-x86_64/rwsem.h Thu Mar 7 18:17:46 2002 @@ -157,9 +157,9 @@ " jmp 1b\n" ".previous\n" "# ending __up_read\n" - : "+m"(sem->count), "+d"(tmp) + : "+d"(tmp) : "a"(sem) - : "memory", "cc"); + : "memory"); } /* @@ -169,7 +169,7 @@ { __asm__ __volatile__( "# beginning __up_write\n\t" - " movl %2,%%edx\n\t" + " movl %1,%%edx\n\t" LOCK_PREFIX " xaddl %%edx,(%%rax)\n\t" /* tries to transition 0xffff0001 -> 0x00000000 */ " jnz 2f\n\t" /* jump if the lock is being waited upon */ "1:\n\t" @@ -181,7 +181,7 @@ " jmp 1b\n" ".previous\n" "# ending __up_write\n" - : "+m"(sem->count) + : : "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS) : "memory", "cc", "edx"); } diff -Nru a/include/asm-x86_64/system.h b/include/asm-x86_64/system.h --- a/include/asm-x86_64/system.h Thu Mar 7 18:17:41 2002 +++ b/include/asm-x86_64/system.h Thu Mar 7 18:17:41 2002 @@ -18,7 +18,7 @@ #define prepare_to_switch() do { } while(0) -#define switch_to(prev,next,last) do { \ +#define switch_to(prev,next) do { \ asm volatile("pushq %%rbp\n\t" \ "pushq %%rbx\n\t" \ "pushq %%r8\n\t" \ @@ -30,10 +30,10 @@ "pushq %%r14\n\t" \ "pushq %%r15\n\t" \ "movq %%rsp,%0\n\t" /* save RSP */ \ - "movq %3,%%rsp\n\t" /* restore RSP */ \ + "movq %2,%%rsp\n\t" /* restore RSP */ \ "leaq 1f(%%rip),%%rbp\n\t" \ "movq %%rbp,%1\n\t" /* save RIP */ \ - "pushq %4\n\t" /* setup new RIP */ \ + "pushq %3\n\t" /* setup new RIP */ \ "jmp __switch_to\n\t" \ "1:\t" \ "popq %%r15\n\t" \ @@ -46,8 +46,7 @@ "popq %%r8\n\t" \ "popq %%rbx\n\t" \ "popq %%rbp\n\t" \ - :"=m" (prev->thread.rsp),"=m" (prev->thread.rip), \ - "=b" (last) \ + :"=m" (prev->thread.rsp),"=m" (prev->thread.rip) \ :"m" (next->thread.rsp),"m" (next->thread.rip), \ "b" (prev), "S" (next), "D" (prev)); \ } while (0) diff -Nru a/include/linux/blk.h b/include/linux/blk.h --- a/include/linux/blk.h Thu Mar 7 18:17:41 2002 +++ b/include/linux/blk.h Thu Mar 7 18:17:41 2002 @@ -8,26 +8,27 @@ #include #include -/* - * get rid of this next... - */ -extern int ide_init(void); - extern void set_device_ro(kdev_t dev,int flag); extern void add_blkdev_randomness(int major); +#ifdef CONFIG_BLK_DEV_RAM + +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ + #ifdef CONFIG_BLK_DEV_INITRD #define INITRD_MINOR 250 /* shouldn't collide with /dev/ram* too soon ... */ extern unsigned long initrd_start,initrd_end; extern int initrd_below_start_ok; /* 1 if it is not an error if initrd_start < memory_start */ -extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ -extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ -extern int rd_image_start; /* starting block # of image */ void initrd_init(void); +#endif /* CONFIG_BLK_DEV_INITRD */ + #endif + /* * end_request() and friends. Must be called with the request queue spinlock * acquired. All functions called within end_request() _must_be_ atomic. diff -Nru a/include/linux/blkpg.h b/include/linux/blkpg.h --- a/include/linux/blkpg.h Thu Mar 7 18:17:44 2002 +++ b/include/linux/blkpg.h Thu Mar 7 18:17:44 2002 @@ -57,7 +57,7 @@ #ifdef __KERNEL__ extern char * partition_name(kdev_t dev); -extern int blk_ioctl(kdev_t dev, unsigned int cmd, unsigned long arg); +extern int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg); #endif /* __KERNEL__ */ diff -Nru a/include/linux/cache.h b/include/linux/cache.h --- a/include/linux/cache.h Thu Mar 7 18:17:45 2002 +++ b/include/linux/cache.h Thu Mar 7 18:17:45 2002 @@ -4,8 +4,10 @@ #include #include +#define ALIGN(x,a) (((x)+(a)-1)&~((a)-1)) + #ifndef L1_CACHE_ALIGN -#define L1_CACHE_ALIGN(x) (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1)) +#define L1_CACHE_ALIGN(x) ALIGN(x, L1_CACHE_BYTES) #endif #ifndef SMP_CACHE_BYTES diff -Nru a/include/linux/coda_fs_i.h b/include/linux/coda_fs_i.h --- a/include/linux/coda_fs_i.h Thu Mar 7 18:17:41 2002 +++ b/include/linux/coda_fs_i.h Thu Mar 7 18:17:41 2002 @@ -34,6 +34,7 @@ #define C_PURGE 0x8 int coda_cnode_make(struct inode **, struct ViceFid *, struct super_block *); +struct inode *coda_iget(struct super_block *sb, struct ViceFid *fid, struct coda_vattr *attr); int coda_cnode_makectl(struct inode **inode, struct super_block *sb); struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb); void coda_replace_fid(struct inode *, ViceFid *, ViceFid *); diff -Nru a/include/linux/coda_linux.h b/include/linux/coda_linux.h --- a/include/linux/coda_linux.h Thu Mar 7 18:17:39 2002 +++ b/include/linux/coda_linux.h Thu Mar 7 18:17:39 2002 @@ -44,8 +44,6 @@ int coda_isnullfid(ViceFid *fid); /* global variables */ -extern int coda_debug; -extern int coda_print_entry; extern int coda_access_cache; extern int coda_fake_statfs; @@ -70,44 +68,19 @@ void coda_sysctl_init(void); void coda_sysctl_clean(void); - -/* debugging masks */ -#define D_SUPER 1 /* print results returned by Venus */ -#define D_INODE 2 /* print entry and exit into procedure */ -#define D_FILE 4 -#define D_CACHE 8 /* cache debugging */ -#define D_MALLOC 16 /* print malloc, de-alloc information */ -#define D_CNODE 32 -#define D_UPCALL 64 /* up and downcall debugging */ -#define D_PSDEV 128 -#define D_PIOCTL 256 -#define D_SPECIAL 512 -#define D_TIMING 1024 -#define D_DOWNCALL 2048 - -#define CDEBUG(mask, format, a...) \ - do { \ - if (coda_debug & mask) { \ - printk("(%s,l. %d): ", __FUNCTION__, __LINE__); \ - printk(format, ## a); } \ -} while (0) - -#define CODA_ALLOC(ptr, cast, size) \ -do { \ - if (size < PAGE_SIZE) { \ - ptr = (cast)kmalloc((unsigned long) size, GFP_KERNEL); \ - CDEBUG(D_MALLOC, "kmalloced: %lx at %p.\n", (long)size, ptr); \ - } else { \ - ptr = (cast)vmalloc((unsigned long) size); \ - CDEBUG(D_MALLOC, "vmalloced: %lx at %p .\n", (long)size, ptr);} \ - if (ptr == 0) { \ +#define CODA_ALLOC(ptr, cast, size) do { \ + if (size < PAGE_SIZE) \ + ptr = (cast)kmalloc((unsigned long) size, GFP_KERNEL); \ + else \ + ptr = (cast)vmalloc((unsigned long) size); \ + if (!ptr) \ printk("kernel malloc returns 0 at %s:%d\n", __FILE__, __LINE__); \ - } \ - else memset( ptr, 0, size ); \ + else memset( ptr, 0, size ); \ } while (0) -#define CODA_FREE(ptr,size) do {if (size < PAGE_SIZE) { kfree((ptr)); CDEBUG(D_MALLOC, "kfreed: %lx at %p.\n", (long) size, ptr); } else { vfree((ptr)); CDEBUG(D_MALLOC, "vfreed: %lx at %p.\n", (long) size, ptr);} } while (0) +#define CODA_FREE(ptr,size) \ + do { if (size < PAGE_SIZE) kfree((ptr)); else vfree((ptr)); } while (0) /* inode to cnode access functions */ diff -Nru a/include/linux/coda_proc.h b/include/linux/coda_proc.h --- a/include/linux/coda_proc.h Thu Mar 7 18:17:37 2002 +++ b/include/linux/coda_proc.h Thu Mar 7 18:17:37 2002 @@ -24,14 +24,12 @@ * * /proc/fs/coda/vfs_stats * upcall_stats - * permission_stats * cache_inv_stats * * these four files are presented to reset the statistics to 0: * * /proc/sys/coda/vfs_stats * upcall_stats - * permission_stats * cache_inv_stats */ @@ -70,15 +68,6 @@ unsigned long time_squared_sum; }; - - -/* cache hits for permissions statistics */ -struct coda_permission_stats -{ - int count; - int hit_count; -}; - /* cache invalidation statistics */ struct coda_cache_inv_stats { @@ -93,14 +82,12 @@ /* these global variables hold the actual statistics data */ extern struct coda_vfs_stats coda_vfs_stat; -extern struct coda_permission_stats coda_permission_stat; extern struct coda_cache_inv_stats coda_cache_inv_stat; extern int coda_upcall_timestamping; /* reset statistics to 0 */ void reset_coda_vfs_stats( void ); void reset_coda_upcall_stats( void ); -void reset_coda_permission_stats( void ); void reset_coda_cache_inv_stats( void ); /* some utitlities to make it easier for you to do statistics for time */ @@ -121,9 +108,6 @@ int do_reset_coda_upcall_stats( ctl_table * table, int write, struct file * filp, void * buffer, size_t * lenp ); -int do_reset_coda_permission_stats( ctl_table * table, int write, - struct file * filp, void * buffer, - size_t * lenp ); int do_reset_coda_cache_inv_stats( ctl_table * table, int write, struct file * filp, void * buffer, size_t * lenp ); @@ -133,8 +117,6 @@ int length); int coda_upcall_stats_get_info( char * buffer, char ** start, off_t offset, int length); -int coda_permission_stats_get_info( char * buffer, char ** start, off_t offset, - int length); int coda_cache_inv_stats_get_info( char * buffer, char ** start, off_t offset, int length); diff -Nru a/include/linux/compiler.h b/include/linux/compiler.h --- a/include/linux/compiler.h Thu Mar 7 18:17:38 2002 +++ b/include/linux/compiler.h Thu Mar 7 18:17:38 2002 @@ -13,4 +13,10 @@ #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) +/* This macro obfuscates arithmetic on a variable address so that gcc + shouldn't recognize the original var, and make assumptions about it */ +#define RELOC_HIDE(var, off) \ + ({ __typeof__(&(var)) __ptr; \ + __asm__ ("" : "=g"(__ptr) : "0"((void *)&(var) + (off))); \ + *__ptr; }) #endif /* __LINUX_COMPILER_H */ diff -Nru a/include/linux/cramfs_fs.h b/include/linux/cramfs_fs.h --- a/include/linux/cramfs_fs.h Thu Mar 7 18:17:41 2002 +++ b/include/linux/cramfs_fs.h Thu Mar 7 18:17:41 2002 @@ -24,6 +24,12 @@ #define CRAMFS_OFFSET_WIDTH 26 /* + * Since inode.namelen is a unsigned 6-bit number, the maximum cramfs + * path length is 63 << 2 = 252. + */ +#define CRAMFS_MAXPATHLEN (((1 << CRAMFS_NAMELEN_WIDTH) - 1) << 2) + +/* * Reasonably terse representation of the inode data. */ struct cramfs_inode { @@ -52,14 +58,14 @@ * Superblock information at the beginning of the FS. */ struct cramfs_super { - u32 magic; /* 0x28cd3d45 - random number */ - u32 size; /* length in bytes */ - u32 flags; /* 0 */ - u32 future; /* 0 */ - u8 signature[16]; /* "Compressed ROMFS" */ + u32 magic; /* 0x28cd3d45 - random number */ + u32 size; /* length in bytes */ + u32 flags; /* feature flags */ + u32 future; /* reserved for future use */ + u8 signature[16]; /* "Compressed ROMFS" */ struct cramfs_info fsid; /* unique filesystem info */ - u8 name[16]; /* user-defined name */ - struct cramfs_inode root; /* Root inode data */ + u8 name[16]; /* user-defined name */ + struct cramfs_inode root; /* root inode data */ }; /* @@ -79,7 +85,10 @@ * if (flags & ~CRAMFS_SUPPORTED_FLAGS). Maybe that should be * changed to test super.future instead. */ -#define CRAMFS_SUPPORTED_FLAGS (0x7ff) +#define CRAMFS_SUPPORTED_FLAGS ( 0x000000ff \ + | CRAMFS_FLAG_HOLES \ + | CRAMFS_FLAG_WRONG_SIGNATURE \ + | CRAMFS_FLAG_SHIFTED_ROOT_OFFSET ) /* Uncompression interfaces to the underlying zlib */ int cramfs_uncompress_block(void *dst, int dstlen, void *src, int srclen); diff -Nru a/include/linux/dcache.h b/include/linux/dcache.h --- a/include/linux/dcache.h Thu Mar 7 18:17:44 2002 +++ b/include/linux/dcache.h Thu Mar 7 18:17:44 2002 @@ -126,6 +126,7 @@ #define DCACHE_REFERENCED 0x0008 /* Recently used, don't discard. */ extern spinlock_t dcache_lock; +extern rwlock_t dparent_lock; /** * d_drop - drop a dentry diff -Nru a/include/linux/dnotify.h b/include/linux/dnotify.h --- a/include/linux/dnotify.h Thu Mar 7 18:17:37 2002 +++ b/include/linux/dnotify.h Thu Mar 7 18:17:37 2002 @@ -33,13 +33,13 @@ static inline void dnotify_parent(struct dentry *dentry, unsigned long event) { struct dentry *parent; - spin_lock(&dcache_lock); + read_lock(&dparent_lock); parent = dentry->d_parent; if (parent->d_inode->i_dnotify_mask & event) { dget(parent); - spin_unlock(&dcache_lock); + read_unlock(&dparent_lock); __inode_dir_notify(parent->d_inode, event); dput(parent); } else - spin_unlock(&dcache_lock); + read_unlock(&dparent_lock); } diff -Nru a/include/linux/fs.h b/include/linux/fs.h --- a/include/linux/fs.h Thu Mar 7 18:17:40 2002 +++ b/include/linux/fs.h Thu Mar 7 18:17:40 2002 @@ -377,8 +377,8 @@ unsigned long nrpages; /* number of total pages */ struct address_space_operations *a_ops; /* methods */ struct inode *host; /* owner: inode, block_device */ - struct vm_area_struct *i_mmap; /* list of private mappings */ - struct vm_area_struct *i_mmap_shared; /* list of shared mappings */ + list_t i_mmap; /* list of private mappings */ + list_t i_mmap_shared; /* list of private mappings */ spinlock_t i_shared_lock; /* and spinlock protecting it */ int gfp_mask; /* how to allocate the pages */ }; @@ -400,6 +400,8 @@ const struct block_device_operations *bd_op; struct semaphore bd_sem; /* open/close mutex */ struct list_head bd_inodes; + void * bd_holder; + int bd_holders; }; struct inode { @@ -1069,6 +1071,8 @@ extern int ioctl_by_bdev(struct block_device *, unsigned, unsigned long); extern int blkdev_get(struct block_device *, mode_t, unsigned, int); extern int blkdev_put(struct block_device *, int); +extern int bd_claim(struct block_device *, void *); +extern void bd_release(struct block_device *); /* fs/devices.c */ extern const struct block_device_operations *get_blkfops(unsigned int); @@ -1212,12 +1216,12 @@ #define destroy_buffers(dev) __invalidate_buffers((dev), 1) extern void invalidate_bdev(struct block_device *, int); extern void __invalidate_buffers(kdev_t dev, int); -extern void sync_inodes(kdev_t); +extern void sync_inodes(void); extern void sync_unlocked_inodes(void); extern void write_inode_now(struct inode *, int); -extern int sync_buffers(kdev_t, int); -extern void sync_dev(kdev_t); +extern int sync_buffers(struct block_device *, int); extern int fsync_dev(kdev_t); +extern int fsync_bdev(struct block_device *); extern int fsync_super(struct super_block *); extern int fsync_no_super(struct block_device *); extern void sync_inodes_sb(struct super_block *); @@ -1234,7 +1238,7 @@ extern int inode_has_buffers(struct inode *); extern int filemap_fdatasync(struct address_space *); extern int filemap_fdatawait(struct address_space *); -extern void sync_supers(kdev_t); +extern void sync_supers(void); extern int bmap(struct inode *, int); extern int notify_change(struct dentry *, struct iattr *); extern int permission(struct inode *, int); @@ -1273,7 +1277,6 @@ #define LOOKUP_FOLLOW (1) #define LOOKUP_DIRECTORY (2) #define LOOKUP_CONTINUE (4) -#define LOOKUP_POSITIVE (8) #define LOOKUP_PARENT (16) #define LOOKUP_NOALT (32) /* @@ -1306,13 +1309,20 @@ extern int FASTCALL(path_init(const char *, unsigned, struct nameidata *)); extern int FASTCALL(path_walk(const char *, struct nameidata *)); extern int FASTCALL(link_path_walk(const char *, struct nameidata *)); +static inline int path_lookup(const char *path, unsigned flags, struct nameidata *nd) +{ + int error = 0; + if (path_init(path, flags, nd)) + error = path_walk(path, nd); + return error; +} extern void path_release(struct nameidata *); extern int follow_down(struct vfsmount **, struct dentry **); extern int follow_up(struct vfsmount **, struct dentry **); extern struct dentry * lookup_one_len(const char *, struct dentry *, int); extern struct dentry * lookup_hash(struct qstr *, struct dentry *); -#define user_path_walk(name,nd) __user_walk(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, nd) -#define user_path_walk_link(name,nd) __user_walk(name, LOOKUP_POSITIVE, nd) +#define user_path_walk(name,nd) __user_walk(name, LOOKUP_FOLLOW, nd) +#define user_path_walk_link(name,nd) __user_walk(name, 0, nd) extern void inode_init_once(struct inode *); extern void iput(struct inode *); @@ -1474,15 +1484,6 @@ extern struct file_system_type *get_fs_type(const char *name); extern struct super_block *get_super(kdev_t); extern void drop_super(struct super_block *sb); -static inline int is_mounted(kdev_t dev) -{ - struct super_block *sb = get_super(dev); - if (sb) { - drop_super(sb); - return 1; - } - return 0; -} extern kdev_t ROOT_DEV; extern char root_device_name[]; @@ -1511,9 +1512,9 @@ static inline ino_t parent_ino(struct dentry *dentry) { ino_t res; - spin_lock(&dcache_lock); + read_lock(&dparent_lock); res = dentry->d_parent->d_inode->i_ino; - spin_unlock(&dcache_lock); + read_unlock(&dparent_lock); return res; } diff -Nru a/include/linux/hdlc/ioctl.h b/include/linux/hdlc/ioctl.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/linux/hdlc/ioctl.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,55 @@ +#ifndef __HDLC_IOCTL_H__ +#define __HDLC_IOCTL_H__ + +typedef struct { + unsigned int clock_rate; /* bits per second */ + unsigned int clock_type; /* internal, external, TX-internal etc. */ + unsigned short loopback; +} sync_serial_settings; /* V.35, V.24, X.21 */ + +typedef struct { + unsigned int clock_rate; /* bits per second */ + unsigned int clock_type; /* internal, external, TX-internal etc. */ + unsigned short loopback; + unsigned int slot_map; +} te1_settings; /* T1, E1 */ + +typedef struct { + unsigned short encoding; + unsigned short parity; +} raw_hdlc_proto; + +typedef struct { + unsigned int t391; + unsigned int t392; + unsigned int n391; + unsigned int n392; + unsigned int n393; + unsigned short lmi; + unsigned short dce; /* 1 for DCE (network side) operation */ +} fr_proto; + +typedef struct { + unsigned int dlci; +} fr_proto_pvc; /* for creating/deleting FR PVCs */ + +typedef struct { + unsigned int interval; + unsigned int timeout; +} cisco_proto; + +/* PPP doesn't need any info now - supply length = 0 to ioctl */ + +union hdlc_settings { + raw_hdlc_proto raw_hdlc; + cisco_proto cisco; + fr_proto fr; + fr_proto_pvc fr_pvc; +}; + +union line_settings { + sync_serial_settings sync; + te1_settings te1; +}; + +#endif /* __HDLC_IOCTL_H__ */ diff -Nru a/include/linux/hdlc.h b/include/linux/hdlc.h --- a/include/linux/hdlc.h Thu Mar 7 18:17:46 2002 +++ b/include/linux/hdlc.h Thu Mar 7 18:17:46 2002 @@ -1,7 +1,7 @@ /* * Generic HDLC support routines for Linux * - * Copyright (C) 1999, 2000 Krzysztof Halasa + * Copyright (C) 1999-2002 Krzysztof Halasa * * 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 @@ -12,64 +12,49 @@ #ifndef __HDLC_H #define __HDLC_H -/* Ioctls - to be changed */ -#define HDLCGSLOTMAP (0x89F4) /* E1/T1 slot bitmap */ -#define HDLCGCLOCK (0x89F5) /* clock sources */ -#define HDLCGCLOCKRATE (0x89F6) /* clock rate */ -#define HDLCGMODE (0x89F7) /* internal to hdlc.c - protocol used */ -#define HDLCGLINE (0x89F8) /* physical interface */ -#define HDLCSSLOTMAP (0x89F9) -#define HDLCSCLOCK (0x89FA) -#define HDLCSCLOCKRATE (0x89FB) -#define HDLCSMODE (0x89FC) /* internal to hdlc.c - select protocol */ -#define HDLCPVC (0x89FD) /* internal to hdlc.c - create/delete PVC */ -#define HDLCSLINE (0x89FE) -#define HDLCRUN (0x89FF) /* Download firmware and run board */ - -/* Modes */ -#define MODE_NONE 0x00000000 /* Not initialized */ -#define MODE_DCE 0x00000080 /* DCE */ -#define MODE_HDLC 0x00000100 /* Raw HDLC frames */ -#define MODE_CISCO 0x00000200 -#define MODE_PPP 0x00000400 -#define MODE_FR 0x00000800 /* Any LMI */ -#define MODE_FR_ANSI 0x00000801 -#define MODE_FR_CCITT 0x00000802 -#define MODE_X25 0x00001000 -#define MODE_MASK 0x0000FF00 -#define MODE_SOFT 0x80000000 /* Driver modes, using hardware HDLC */ - -/* Lines */ -#define LINE_DEFAULT 0x00000000 -#define LINE_V35 0x00000001 -#define LINE_RS232 0x00000002 -#define LINE_X21 0x00000003 -#define LINE_T1 0x00000004 -#define LINE_E1 0x00000005 -#define LINE_MASK 0x000000FF -#define LINE_LOOPBACK 0x80000000 /* On-card loopback */ - -#define CLOCK_EXT 0 /* External TX and RX clock - DTE */ -#define CLOCK_INT 1 /* Internal TX and RX clock - DCE */ -#define CLOCK_TXINT 2 /* Internal TX and external RX clock */ -#define CLOCK_TXFROMRX 3 /* TX clock derived from external RX clock */ +#define CLOCK_DEFAULT 0 /* Default (current) setting */ +#define CLOCK_EXT 1 /* External TX and RX clock - DTE */ +#define CLOCK_INT 2 /* Internal TX and RX clock - DCE */ +#define CLOCK_TXINT 3 /* Internal TX and external RX clock */ +#define CLOCK_TXFROMRX 4 /* TX clock derived from external RX clock */ + + +#define ENCODING_DEFAULT 0 /* Default (current) setting */ +#define ENCODING_NRZ 1 +#define ENCODING_NRZI 2 +#define ENCODING_FM_MARK 3 +#define ENCODING_FM_SPACE 4 +#define ENCODING_MANCHESTER 5 + + +#define PARITY_DEFAULT 0 /* Default (current) setting */ +#define PARITY_NONE 1 /* No parity */ +#define PARITY_CRC16_PR0 2 /* CRC16, initial value 0x0000 */ +#define PARITY_CRC16_PR1 3 /* CRC16, initial value 0xFFFF */ +#define PARITY_CRC16_PR0_CCITT 4 /* CRC16, initial 0x0000, ITU-T version */ +#define PARITY_CRC16_PR1_CCITT 5 /* CRC16, initial 0xFFFF, ITU-T version */ +#define PARITY_CRC32_PR0_CCITT 6 /* CRC32, initial value 0x00000000 */ +#define PARITY_CRC32_PR1_CCITT 7 /* CRC32, initial value 0xFFFFFFFF */ + +#define LMI_DEFAULT 0 /* Default (current) setting */ +#define LMI_NONE 1 /* No LMI, all PVCs are static */ +#define LMI_ANSI 2 /* ANSI Annex D */ +#define LMI_CCITT 3 /* ITU-T Annex A */ +/* PPP doesn't need any info now - supply length = 0 to ioctl */ -#define HDLC_MAX_MTU 1500 /* Ethernet 1500 bytes */ -#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10) /* max 10 bytes for FR */ #ifdef __KERNEL__ #include #include #include +#include -#define MAXLEN_LMISTAT 20 /* max size of status enquiry frame */ +#define HDLC_MAX_MTU 1500 /* Ethernet 1500 bytes */ +#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10) /* max 10 bytes for FR */ -#define LINK_STATE_RELIABLE 0x01 -#define LINK_STATE_REQUEST 0x02 /* full stat sent (DCE) / req pending (DTE) */ -#define LINK_STATE_CHANGED 0x04 /* change in PVCs state, send full report */ -#define LINK_STATE_FULLREP_SENT 0x08 /* full report sent */ +#define MAXLEN_LMISTAT 20 /* max size of status enquiry frame */ #define PVC_STATE_NEW 0x01 #define PVC_STATE_ACTIVE 0x02 @@ -112,6 +97,7 @@ typedef struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) unsigned ea1 : 1; unsigned cr : 1; unsigned dlcih: 6; @@ -121,6 +107,19 @@ unsigned becn : 1; unsigned fecn : 1; unsigned dlcil: 4; +#elif defined (__BIG_ENDIAN_BITFIELD) + unsigned dlcih: 6; + unsigned cr : 1; + unsigned ea1 : 1; + + unsigned dlcil: 4; + unsigned fecn : 1; + unsigned becn : 1; + unsigned de : 1; + unsigned ea2 : 1; +#else +#error "Please fix " +#endif }__attribute__ ((packed)) fr_hdr; @@ -151,63 +150,96 @@ struct hdlc_device_struct *master; struct pvc_device_struct *next; - u8 state; - u8 newstate; + struct { + int active; + int new; + int deleted; + int fecn; + int becn; + }state; }pvc_device; -typedef struct { - u32 last_errors; /* last errors bit list */ - int last_poll; /* ! */ - u8 T391; /* ! link integrity verification polling timer */ - u8 T392; /* ! polling verification timer */ - u8 N391; /* full status polling counter */ - u8 N392; /* error threshold */ - u8 N393; /* monitored events count */ - u8 N391cnt; - - u8 state; /* ! */ - u32 txseq; /* ! TX sequence number - Cisco uses 4 bytes */ - u32 rxseq; /* ! RX sequence number */ -}fr_lmi; /* ! means used in Cisco HDLC as well */ - - typedef struct hdlc_device_struct { - /* to be initialized by hardware driver: */ + /* To be initialized by hardware driver */ struct net_device netdev; /* master net device - must be first */ struct net_device_stats stats; - struct ppp_device pppdev; - struct ppp_device *syncppp_ptr; + /* used by HDLC layer to take control over HDLC device from hw driver*/ + int (*attach)(struct hdlc_device_struct *hdlc, + unsigned short encoding, unsigned short parity); - /* set_mode may be NULL if HDLC-only board */ - int (*set_mode)(struct hdlc_device_struct *hdlc, int mode); - int (*open)(struct hdlc_device_struct *hdlc); - void (*close)(struct hdlc_device_struct *hdlc); - int (*xmit)(struct hdlc_device_struct *hdlc, struct sk_buff *skb); - int (*ioctl)(struct hdlc_device_struct *hdlc, struct ifreq *ifr, - int cmd); - - /* Only in "hardware" FR modes etc. - may be NULL */ - int (*create_pvc)(pvc_device *pvc); - void (*destroy_pvc)(pvc_device *pvc); - int (*open_pvc)(pvc_device *pvc); - void (*close_pvc)(pvc_device *pvc); - - /* for hdlc.c internal use only */ - pvc_device *first_pvc; - u16 pvc_count; - int mode; + /* hardware driver must handle this instead of dev->hard_start_xmit */ + int (*xmit)(struct sk_buff *skb, struct net_device *dev); - struct timer_list timer; - fr_lmi lmi; + + /* Things below are for HDLC layer internal use only */ + int (*ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); + int (*open)(struct hdlc_device_struct *hdlc); + void (*stop)(struct hdlc_device_struct *hdlc); + void (*detach)(struct hdlc_device_struct *hdlc); + void (*netif_rx)(struct sk_buff *skb); + int proto; /* IF_PROTO_HDLC/CISCO/FR/etc. */ + + union { + struct { + fr_proto settings; + pvc_device *first_pvc; + int pvc_count; + + struct timer_list timer; + int last_poll; + int reliable; + int changed; + int request; + int fullrep_sent; + u32 last_errors; /* last errors bit list */ + u8 n391cnt; + u8 txseq; /* TX sequence number */ + u8 rxseq; /* RX sequence number */ + }fr; + + struct { + cisco_proto settings; + + struct timer_list timer; + int last_poll; + int up; + u32 txseq; /* TX sequence number */ + u32 rxseq; /* RX sequence number */ + }cisco; + + struct { + raw_hdlc_proto settings; + }raw_hdlc; + + struct { + struct ppp_device pppdev; + struct ppp_device *syncppp_ptr; + int (*old_change_mtu)(struct net_device *dev, + int new_mtu); + }ppp; + }state; }hdlc_device; + +int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr); +int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr); +int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr); +int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr); +int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr); + + +/* Exported from hdlc.o */ + +/* Called by hardware driver when a user requests HDLC service */ +int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); + +/* Must be used by hardware driver on module startup/exit */ int register_hdlc_device(hdlc_device *hdlc); void unregister_hdlc_device(hdlc_device *hdlc); -void hdlc_netif_rx(hdlc_device *hdlc, struct sk_buff *skb); static __inline__ struct net_device* hdlc_to_dev(hdlc_device *hdlc) @@ -246,33 +278,6 @@ } -static __inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status, u8 *state) -{ - *state &= ~(PVC_STATE_ACTIVE | PVC_STATE_NEW); - if (status[2] & 0x08) - *state |= PVC_STATE_NEW; - else if (status[2] & 0x02) - *state |= PVC_STATE_ACTIVE; - - return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3); -} - - -static __inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status, - u8 state) -{ - status[0] = (dlci>>4) & 0x3F; - status[1] = ((dlci<<3) & 0x78) | 0x80; - status[2] = 0x80; - - if (state & PVC_STATE_NEW) - status[2] |= 0x08; - else if (state & PVC_STATE_ACTIVE) - status[2] |= 0x02; -} - - - static __inline__ u16 netdev_dlci(struct net_device *dev) { return ntohs(*(u16*)dev->dev_addr); @@ -282,37 +287,15 @@ static __inline__ u16 q922_to_dlci(u8 *hdr) { - return ((hdr[0] & 0xFC)<<2) | ((hdr[1] & 0xF0)>>4); + return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4); } static __inline__ void dlci_to_q922(u8 *hdr, u16 dlci) { - hdr[0] = (dlci>>2) & 0xFC; - hdr[1] = ((dlci<<4) & 0xF0) | 0x01; -} - - - -static __inline__ int mode_is(hdlc_device *hdlc, int mask) -{ - return (hdlc->mode & mask) == mask; -} - - - -static __inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci) -{ - pvc_device *pvc=hdlc->first_pvc; - - while (pvc) { - if (netdev_dlci(&pvc->netdev) == dlci) - return pvc; - pvc=pvc->next; - } - - return NULL; + hdr[0] = (dlci >> 2) & 0xFC; + hdr[1] = ((dlci << 4) & 0xF0) | 0x01; } @@ -329,6 +312,36 @@ printk(" %02X", skb->data[i]); } printk("\n"); +} + + + +/* Must be called by hardware driver when HDLC device is being opened */ +static __inline__ int hdlc_open(hdlc_device *hdlc) +{ + if (hdlc->proto == -1) + return -ENOSYS; /* no protocol attached */ + + if (hdlc->open) + return hdlc->open(hdlc); + return 0; +} + + +/* Must be called by hardware driver when HDLC device is being closed */ +static __inline__ void hdlc_close(hdlc_device *hdlc) +{ + if (hdlc->stop) + hdlc->stop(hdlc); +} + + +/* May be used by hardware driver to gain control over HDLC device */ +static __inline__ void hdlc_detach(hdlc_device *hdlc) +{ + if (hdlc->detach) + hdlc->detach(hdlc); + hdlc->detach = NULL; } diff -Nru a/include/linux/hdreg.h b/include/linux/hdreg.h --- a/include/linux/hdreg.h Thu Mar 7 18:17:44 2002 +++ b/include/linux/hdreg.h Thu Mar 7 18:17:44 2002 @@ -51,7 +51,7 @@ /* * Command Header sizes for IOCTL commands - * HDIO_DRIVE_CMD, HDIO_DRIVE_TASK, and HDIO_DRIVE_TASKFILE + * HDIO_DRIVE_CMD and HDIO_DRIVE_TASK */ #if 0 @@ -355,7 +355,6 @@ #define HDIO_GET_BUSSTATE 0x031a /* get the bus state of the hwif */ #define HDIO_TRISTATE_HWIF 0x031b /* execute a channel tristate */ #define HDIO_DRIVE_RESET 0x031c /* execute a device reset */ -#define HDIO_DRIVE_TASKFILE 0x031d /* execute raw taskfile */ #define HDIO_DRIVE_TASK 0x031e /* execute task and special drive command */ #define HDIO_DRIVE_CMD 0x031f /* execute a special drive command */ diff -Nru a/include/linux/ide.h b/include/linux/ide.h --- a/include/linux/ide.h Thu Mar 7 18:17:39 2002 +++ b/include/linux/ide.h Thu Mar 7 18:17:39 2002 @@ -1,9 +1,7 @@ #ifndef _IDE_H #define _IDE_H /* - * linux/include/linux/ide.h - * - * Copyright (C) 1994-1998 Linus Torvalds & authors + * Copyright (C) 1994-2002 Linus Torvalds & authors */ #include @@ -13,6 +11,7 @@ #include #include #include +#include #include #include @@ -29,10 +28,7 @@ /****************************************************************************** * IDE driver configuration options (play with these as desired): - * - * REALLY_SLOW_IO can be defined in ide.c and ide-cd.c, if necessary */ -#undef REALLY_FAST_IO /* define if ide ports are perfect */ #define INITIAL_MULT_COUNT 0 /* off=0; on=2,4,8,16,32, etc.. */ #ifndef SUPPORT_SLOW_DATA_PORTS /* 1 to support slow data ports */ @@ -50,32 +46,12 @@ #ifndef FANCY_STATUS_DUMPS /* 1 for human-readable drive errors */ #define FANCY_STATUS_DUMPS 1 /* 0 to reduce kernel size */ #endif - -#ifdef CONFIG_BLK_DEV_CMD640 -#if 0 /* change to 1 when debugging cmd640 problems */ -void cmd640_dump_regs (void); -#define CMD640_DUMP_REGS cmd640_dump_regs() /* for debugging cmd640 chipset */ -#endif -#endif /* CONFIG_BLK_DEV_CMD640 */ - #ifndef DISABLE_IRQ_NOSYNC #define DISABLE_IRQ_NOSYNC 0 #endif /* - * IDE_DRIVE_CMD is used to implement many features of the hdparm utility - */ -#define IDE_DRIVE_CMD 99 /* (magic) undef to reduce kernel size*/ - -#define IDE_DRIVE_TASK 98 - -/* - * IDE_DRIVE_TASKFILE is used to implement many features needed for raw tasks - */ -#define IDE_DRIVE_TASKFILE 97 - -/* - * "No user-serviceable parts" beyond this point :) + * "No user-serviceable parts" beyond this point *****************************************************************************/ typedef unsigned char byte; /* used everywhere */ @@ -92,13 +68,6 @@ */ #define DMA_PIO_RETRY 1 /* retrying in PIO */ -/* - * Ensure that various configuration flags have compatible settings - */ -#ifdef REALLY_SLOW_IO -#undef REALLY_FAST_IO -#endif - #define HWIF(drive) ((drive)->hwif) #define HWGROUP(drive) (HWIF(drive)->hwgroup) @@ -193,33 +162,17 @@ #define PARTN_BITS 6 /* number of minor dev bits for partitions */ #define PARTN_MASK ((1< (b2) + (t)) || ((b2) > (b1) + (t))) -#define IDE_MIN(a,b) ((a)<(b) ? (a):(b)) -#define IDE_MAX(a,b) ((a)>(b) ? (a):(b)) - -#ifndef SPLIT_WORD -# define SPLIT_WORD(W,HB,LB) ((HB)=(W>>8), (LB)=(W-((W>>8)<<8))) -#endif -#ifndef MAKE_WORD -# define MAKE_WORD(W,HB,LB) ((W)=((HB<<8)+LB)) -#endif - /* * Timeouts for various operations: */ #define WAIT_DRQ (5*HZ/100) /* 50msec - spec allows up to 20ms */ -#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) -#define WAIT_READY (5*HZ) /* 5sec - some laptops are very slow */ -#else -#define WAIT_READY (3*HZ/100) /* 30msec - should be instantaneous */ -#endif /* CONFIG_APM || CONFIG_APM_MODULE */ -#define WAIT_PIDENTIFY (10*HZ) /* 10sec - should be less than 3ms (?), if all ATAPI CD is closed at boot */ -#define WAIT_WORSTCASE (30*HZ) /* 30sec - worst case when spinning up */ -#define WAIT_CMD (10*HZ) /* 10sec - maximum wait for an IRQ to happen */ +#define WAIT_READY (5*HZ) /* 5sec - some laptops are very slow */ +#define WAIT_PIDENTIFY (10*HZ) /* 10sec - should be less than 3ms (?), if all ATAPI CD is closed at boot */ +#define WAIT_WORSTCASE (30*HZ) /* 30sec - worst case when spinning up */ +#define WAIT_CMD (10*HZ) /* 10sec - maximum wait for an IRQ to happen */ #define WAIT_MIN_SLEEP (2*HZ/100) /* 20msec - minimum sleep time */ #define SELECT_DRIVE(hwif,drive) \ @@ -229,40 +182,12 @@ OUT_BYTE((drive)->select.all, hwif->io_ports[IDE_SELECT_OFFSET]); \ } -#define SELECT_INTERRUPT(hwif,drive) \ -{ \ - if (hwif->intrproc) \ - hwif->intrproc(drive); \ - else \ - OUT_BYTE((drive)->ctl|2, hwif->io_ports[IDE_CONTROL_OFFSET]); \ -} - #define SELECT_MASK(hwif,drive,mask) \ { \ if (hwif->maskproc) \ hwif->maskproc(drive,mask); \ } -#define SELECT_READ_WRITE(hwif,drive,func) \ -{ \ - if (hwif->rwproc) \ - hwif->rwproc(drive,func); \ -} - -#define QUIRK_LIST(hwif,drive) \ -{ \ - if (hwif->quirkproc) \ - (drive)->quirk_list = hwif->quirkproc(drive); \ -} - -#define HOST(hwif,chipset) \ -{ \ - return ((hwif)->chipset == chipset) ? 1 : 0; \ -} - -#define IDE_DEBUG(lineno) \ - printk("%s,%s,line=%d\n", __FILE__, __FUNCTION__, (lineno)) - /* * Check for an interrupt and acknowledge the interrupt status */ @@ -270,19 +195,31 @@ typedef int (ide_ack_intr_t)(struct hwif_s *); #ifndef NO_DMA -#define NO_DMA 255 +# define NO_DMA 255 #endif /* - * hwif_chipset_t is used to keep track of the specific hardware - * chipset used by each IDE interface, if known. + * This is used to keep track of the specific hardware chipset used by each IDE + * interface, if known. Please note that we don't discriminate between + * different PCI host chips here. */ -typedef enum { ide_unknown, ide_generic, ide_pci, - ide_cmd640, ide_dtc2278, ide_ali14xx, - ide_qd65xx, ide_umc8672, ide_ht6560b, - ide_pdc4030, ide_rz1000, ide_trm290, - ide_cmd646, ide_cy82c693, ide_4drives, - ide_pmac, ide_etrax100 +typedef enum { + ide_unknown, + ide_generic, + ide_pci, + ide_cmd640, + ide_dtc2278, + ide_ali14xx, + ide_qd65xx, + ide_umc8672, + ide_ht6560b, + ide_pdc4030, + ide_rz1000, + ide_trm290, + ide_cmd646, + ide_cy82c693, + ide_pmac, + ide_etrax100 } hwif_chipset_t; @@ -310,7 +247,7 @@ /* * Set up hw_regs_t structure before calling ide_register_hw (optional) */ -void ide_setup_ports( hw_regs_t *hw, +void ide_setup_ports(hw_regs_t *hw, ide_ioreg_t base, int *offsets, ide_ioreg_t ctrl, @@ -321,40 +258,29 @@ #include /* - * If the arch-dependant ide.h did not declare/define any OUT_BYTE - * or IN_BYTE functions, we make some defaults here. + * If the arch-dependant ide.h did not declare/define any OUT_BYTE or IN_BYTE + * functions, we make some defaults here. The only architecture currently + * needing this is Cris. */ -#ifndef HAVE_ARCH_OUT_BYTE -#ifdef REALLY_FAST_IO -#define OUT_BYTE(b,p) outb((b),(p)) -#define OUT_WORD(w,p) outw((w),(p)) -#else -#define OUT_BYTE(b,p) outb_p((b),(p)) -#define OUT_WORD(w,p) outw_p((w),(p)) -#endif -#endif - -#ifndef HAVE_ARCH_IN_BYTE -#ifdef REALLY_FAST_IO -#define IN_BYTE(p) (byte)inb(p) -#define IN_WORD(p) (short)inw(p) -#else -#define IN_BYTE(p) (byte)inb_p(p) -#define IN_WORD(p) (short)inw_p(p) -#endif +#ifndef HAVE_ARCH_IN_OUT +# define OUT_BYTE(b,p) outb((b),(p)) +# define OUT_WORD(w,p) outw((w),(p)) +# define IN_BYTE(p) (u8)inb(p) +# define IN_WORD(p) (u16)inw(p) #endif /* * Now for the data we need to maintain per-drive: ide_drive_t */ -#define ide_scsi 0x21 -#define ide_disk 0x20 -#define ide_optical 0x7 -#define ide_cdrom 0x5 -#define ide_tape 0x1 -#define ide_floppy 0x0 +#define ATA_DISK 0x20 +#define ATA_TAPE 0x01 +#define ATA_ROM 0x05 /* CD-ROM */ +#define ATA_MOD 0x07 /* optical */ +#define ATA_FLOPPY 0x00 +#define ATA_SCSI 0x21 +#define ATA_NO_LUN 0x7f typedef union { unsigned all : 8; /* all of the bits together */ @@ -370,7 +296,15 @@ struct ide_settings_s; typedef struct ide_drive_s { - request_queue_t queue; /* request queue */ + unsigned int usage; /* current "open()" count for drive */ + char type; /* distingiush different devices: disk, cdrom, tape, floppy, ... */ + + /* NOTE: If we had proper separation between channel and host chip, we + * could move this to the chanell and many sync problems would + * magically just go away. + */ + request_queue_t queue; /* per device request queue */ + struct ide_drive_s *next; /* circular list of hwgroup drives */ unsigned long sleep; /* sleep until this time */ unsigned long service_start; /* time we started last request */ @@ -381,14 +315,14 @@ byte using_dma; /* disk is using dma for read/write */ byte retry_pio; /* retrying dma capable host in pio */ byte state; /* retry state */ - byte waiting_for_dma; /* dma currently in progress */ byte unmask; /* flag: okay to unmask other irqs */ byte slow; /* flag: slow data port */ byte bswap; /* flag: byte swap data */ byte dsc_overlap; /* flag: DSC overlap */ byte nice1; /* flag: give potential excess bandwidth */ + unsigned waiting_for_dma: 1; /* dma currently in progress */ unsigned present : 1; /* drive is physically present */ - unsigned noprobe : 1; /* from: hdx=noprobe */ + unsigned noprobe : 1; /* from: hdx=noprobe */ unsigned busy : 1; /* currently doing revalidate_disk() */ unsigned removable : 1; /* 1 if need to do check_media_change */ unsigned forced_geom : 1; /* 1 if hdx=c,h,s was given at boot */ @@ -405,7 +339,6 @@ unsigned ata_flash : 1; /* 1=present, 0=default */ unsigned addressing; /* : 2; 0=28-bit, 1=48-bit, 2=64-bit */ byte scsi; /* 0=default, 1=skip current ide-subdriver for ide-scsi emulation */ - byte media; /* disk, cdrom, tape, floppy, ... */ select_t select; /* basic drive/head select reg value */ byte ctl; /* "normal" value for IDE_CONTROL_REG */ byte ready_stat; /* min status value for drive ready */ @@ -416,7 +349,6 @@ byte bad_wstat; /* used for ignoring WRERR_STAT */ byte nowerr; /* used for ignoring WRERR_STAT */ byte sect0; /* offset of first sector for DM6:DDO */ - unsigned int usage; /* current "open()" count for drive */ byte head; /* "real" number of heads */ byte sect; /* "real" sectors per track */ byte bios_head; /* BIOS/fdisk/LILO number of heads */ @@ -426,12 +358,12 @@ unsigned long capacity; /* total number of sectors */ unsigned long long capacity48; /* total number of sectors */ unsigned int drive_data; /* for use by tuneproc/selectproc as needed */ - struct hwif_s *hwif; /* actually (ide_hwif_t *) */ + struct hwif_s *hwif; /* parent pointer to the interface we are attached to */ wait_queue_head_t wqueue; /* used to wait for drive in open() */ struct hd_driveid *id; /* drive model identification info */ struct hd_struct *part; /* drive partition table */ char name[4]; /* drive name, such as "hda" */ - struct ide_driver_s *driver; /* (ide_driver_t *) */ + struct ata_operations *driver; void *driver_data; /* extra driver data */ devfs_handle_t de; /* directory for device */ struct proc_dir_entry *proc; /* /proc/ide/ directory entry */ @@ -450,6 +382,7 @@ byte acoustic; /* acoustic management */ unsigned int failures; /* current failure count */ unsigned int max_failures; /* maximum allowed failure count */ + struct device device; /* global device tree handle */ } ide_drive_t; /* @@ -519,16 +452,6 @@ */ typedef int (ide_busproc_t) (ide_drive_t *, int); -#ifdef CONFIG_BLK_DEV_IDEPCI -typedef struct ide_pci_devid_s { - unsigned short vid; - unsigned short did; -} ide_pci_devid_t; - -#define IDE_PCI_DEVID_NULL ((ide_pci_devid_t){0,0}) -#define IDE_PCI_DEVID_EQ(a,b) (a.vid == b.vid && a.did == b.did) -#endif /* CONFIG_BLK_DEV_IDEPCI */ - typedef struct hwif_s { struct hwif_s *next; /* for linked-list in ide_hwgroup_t */ struct hwgroup_s *hwgroup; /* actually (ide_hwgroup_t *) */ @@ -558,12 +481,12 @@ unsigned long select_data; /* for use by chipset-specific code */ struct proc_dir_entry *proc; /* /proc/ide/ directory entry */ int irq; /* our irq number */ - byte major; /* our major number */ - char name[6]; /* name of interface, eg. "ide0" */ - byte index; /* 0 for ide0; 1 for ide1; ... */ + int major; /* our major number */ + char name[80]; /* name of interface */ + int index; /* 0 for ide0; 1 for ide1; ... */ hwif_chipset_t chipset; /* sub-module for tuning.. */ unsigned noprobe : 1; /* don't probe for this interface */ - unsigned present : 1; /* this interface exists */ + unsigned present : 1; /* there is a device on this interface */ unsigned serialized : 1; /* serialized operation with mate hwif */ unsigned sharing_irq: 1; /* 1 = sharing irq with another hwif */ unsigned reset : 1; /* reset after probe */ @@ -573,15 +496,14 @@ byte channel; /* for dual-port chips: 0=primary, 1=secondary */ #ifdef CONFIG_BLK_DEV_IDEPCI struct pci_dev *pci_dev; /* for pci chipsets */ - ide_pci_devid_t pci_devid; /* for pci chipsets: {VID,DID} */ -#endif /* CONFIG_BLK_DEV_IDEPCI */ +#endif #if (DISK_RECOVERY_TIME > 0) unsigned long last_time; /* time when previous rq was done */ #endif byte straight8; /* Alan's straight 8 check */ - void *hwif_data; /* extra hwif data */ ide_busproc_t *busproc; /* driver soft-power interface */ byte bus_state; /* power state of the IDE bus */ + struct device device; /* global device tree handle */ } ide_hwif_t; /* @@ -671,8 +593,6 @@ #ifdef CONFIG_PROC_FS void proc_ide_create(void); void proc_ide_destroy(void); -void recreate_proc_ide_device(ide_hwif_t *, ide_drive_t *); -void destroy_proc_ide_device(ide_hwif_t *, ide_drive_t *); void destroy_proc_ide_drives(ide_hwif_t *); void create_proc_ide_interfaces(void); void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data); @@ -700,37 +620,53 @@ #endif /* - * Subdrivers support. + * This structure describes the operations possible on a particular device type + * (CD-ROM, tape, DISK and so on). + * + * This is the main hook for device type support submodules. */ -typedef struct ide_driver_s { - const char *name; - byte media; - unsigned busy : 1; - unsigned supports_dma : 1; - unsigned supports_dsc_overlap : 1; + +struct ata_operations { + struct module *owner; + unsigned busy: 1; /* FIXME: this will go soon away... */ int (*cleanup)(ide_drive_t *); int (*standby)(ide_drive_t *); - int (*flushcache)(ide_drive_t *); ide_startstop_t (*do_request)(ide_drive_t *, struct request *, unsigned long); int (*end_request)(ide_drive_t *drive, int uptodate); + int (*ioctl)(ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long); int (*open)(struct inode *, struct file *, ide_drive_t *); void (*release)(struct inode *, struct file *, ide_drive_t *); - int (*media_change)(ide_drive_t *); + int (*check_media_change)(ide_drive_t *); void (*revalidate)(ide_drive_t *); + void (*pre_reset)(ide_drive_t *); unsigned long (*capacity)(ide_drive_t *); ide_startstop_t (*special)(ide_drive_t *); - ide_proc_entry_t *proc; - int (*driver_init)(void); - int (*driver_reinit)(ide_drive_t *); + ide_proc_entry_t *proc; +}; - /* FIXME: Single linked list of drivers for iteration. - */ - struct ide_driver_s *next; -} ide_driver_t; +/* Alas, no aliases. Too much hassle with bringing module.h everywhere */ +#define ata_get(ata) \ + (((ata) && (ata)->owner) \ + ? ( try_inc_mod_count((ata)->owner) ? (ata) : NULL ) \ + : (ata)) + +#define ata_put(ata) \ +do { \ + if ((ata) && (ata)->owner) \ + __MOD_DEC_USE_COUNT((ata)->owner); \ +} while(0) + +extern unsigned long ata_capacity(ide_drive_t *drive); + +/* FIXME: Actually implement and use them as soon as possible! to make the + * ide_scan_devices() go away! */ -#define DRIVER(drive) ((drive)->driver) +extern int unregister_ata_driver(unsigned int type, struct ata_operations *driver); +extern int register_ata_driver(unsigned int type, struct ata_operations *driver); + +#define ata_ops(drive) ((drive)->driver) /* * ide_hwifs[] is the master data structure used to keep track @@ -740,10 +676,7 @@ * structure directly (the allocation/layout may change!). * */ -#ifndef _IDE_C extern struct hwif_s ide_hwifs[]; /* master data repository */ -extern struct ide_driver_s *ide_drivers; -#endif extern int noautodma; /* @@ -811,16 +744,6 @@ ide_drive_t *get_info_ptr (kdev_t i_rdev); /* - * Return the current idea about the total capacity of this drive. - */ -unsigned long current_capacity (ide_drive_t *drive); - -/* - * Revalidate (read partition tables) - */ -void ide_revalidate_drive (ide_drive_t *drive); - -/* * Start a reset operation for an IDE interface. * The caller should return immediately after invoking this. */ @@ -855,34 +778,6 @@ #define task_rq_offset(rq) \ (((rq)->nr_sectors - (rq)->current_nr_sectors) * SECTOR_SIZE) -extern inline void *ide_map_buffer(struct request *rq, unsigned long *flags) -{ - return bio_kmap_irq(rq->bio, flags) + ide_rq_offset(rq); -} - -extern inline void ide_unmap_buffer(char *buffer, unsigned long *flags) -{ - bio_kunmap_irq(buffer, flags); -} - -/* - * for now, taskfile requests are special :/ - */ -extern inline char *ide_map_rq(struct request *rq, unsigned long *flags) -{ - if (rq->bio) - return ide_map_buffer(rq, flags); - else - return rq->buffer + task_rq_offset(rq); -} - -extern inline void ide_unmap_rq(struct request *rq, char *buf, - unsigned long *flags) -{ - if (rq->bio) - ide_unmap_buffer(buf, flags); -} - /* * This function issues a special IDE device request * onto the request queue. @@ -912,18 +807,9 @@ /* * Clean up after success/failure of an explicit drive cmd. - * stat/err are used only when (HWGROUP(drive)->rq->cmd == IDE_DRIVE_CMD). - * stat/err are used only when (HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASK_MASK). */ void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err); -/* - * Issue ATA command and wait for completion. use for implementing commands in kernel - */ -int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf); - -int ide_wait_cmd_task (ide_drive_t *drive, byte *buf); - typedef struct ide_task_s { task_ioreg_t tfRegister[8]; task_ioreg_t hobRegister[8]; @@ -959,18 +845,9 @@ /* * Special Flagged Register Validation Caller */ -// ide_startstop_t flagged_taskfile (ide_drive_t *drive, ide_task_t *task); ide_startstop_t set_multmode_intr (ide_drive_t *drive); -ide_startstop_t set_geometry_intr (ide_drive_t *drive); -ide_startstop_t recal_intr (ide_drive_t *drive); ide_startstop_t task_no_data_intr (ide_drive_t *drive); -ide_startstop_t task_in_intr (ide_drive_t *drive); -ide_startstop_t task_mulin_intr (ide_drive_t *drive); -ide_startstop_t pre_task_out_intr (ide_drive_t *drive, struct request *rq); -ide_startstop_t task_out_intr (ide_drive_t *drive); -ide_startstop_t task_mulout_intr (ide_drive_t *drive); -void ide_init_drive_taskfile (struct request *rq); int ide_wait_taskfile (ide_drive_t *drive, struct hd_drive_task_hdr *taskfile, struct hd_drive_hob_hdr *hobfile, byte *buf); @@ -981,16 +858,10 @@ /* Expects args is a full set of TF registers and parses the command type */ int ide_cmd_type_parser (ide_task_t *args); -int ide_taskfile_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); int ide_cmd_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); int ide_task_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); -#ifdef CONFIG_PKT_TASK_IOCTL -int pkt_taskfile_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); -#endif /* CONFIG_PKT_TASK_IOCTL */ - void ide_delay_50ms (void); -int system_bus_clock(void); byte ide_auto_reduce_xfer (ide_drive_t *drive); int ide_driveid_update (ide_drive_t *drive); @@ -999,13 +870,7 @@ byte eighty_ninty_three (ide_drive_t *drive); int set_transfer (ide_drive_t *drive, ide_task_t *args); -/* - * ide_system_bus_speed() returns what we think is the system VESA/PCI - * bus speed (in MHz). This is used for calculating interface PIO timings. - * The default is 40 for known PCI systems, 50 otherwise. - * The "idebus=xx" parameter can be used to override this value. - */ -int ide_system_bus_speed (void); +extern int system_bus_speed; /* * idedisk_input_data() is a wrapper around ide_input_data() which copes @@ -1022,14 +887,16 @@ /* * ide_get_queue() returns the queue which corresponds to a given device. */ -request_queue_t *ide_get_queue (kdev_t dev); +request_queue_t *ide_get_queue(kdev_t dev); /* * CompactFlash cards and their brethern pretend to be removable hard disks, * but they never have a slave unit, and they don't have doorlock mechanisms. - * This test catches them, and is invoked elsewhere when setting appropriate config bits. + * This test catches them, and is invoked elsewhere when setting appropriate + * config bits. */ -int drive_is_flashcard (ide_drive_t *drive); + +extern int drive_is_flashcard(ide_drive_t *drive); int ide_spin_wait_hwgroup (ide_drive_t *drive); void ide_timer_expiry (unsigned long data); @@ -1037,76 +904,60 @@ void do_ide_request (request_queue_t * q); void ide_init_subdrivers (void); -#ifndef _IDE_C extern struct block_device_operations ide_fops[]; extern ide_proc_entry_t generic_subdriver_entries[]; -#endif - -int ide_reinit_drive (ide_drive_t *drive); -#ifdef _IDE_C -# ifdef CONFIG_BLK_DEV_IDE +#ifdef CONFIG_BLK_DEV_IDE /* Probe for devices attached to the systems host controllers. */ extern int ideprobe_init (void); -# endif +#endif #ifdef CONFIG_BLK_DEV_IDEDISK -int idedisk_reinit (ide_drive_t *drive); -int idedisk_init (void); -#endif /* CONFIG_BLK_DEV_IDEDISK */ +extern int idedisk_init (void); +#endif #ifdef CONFIG_BLK_DEV_IDECD -int ide_cdrom_reinit (ide_drive_t *drive); -int ide_cdrom_init (void); -#endif /* CONFIG_BLK_DEV_IDECD */ +extern int ide_cdrom_init (void); +#endif #ifdef CONFIG_BLK_DEV_IDETAPE -int idetape_reinit (ide_drive_t *drive); -int idetape_init (void); -#endif /* CONFIG_BLK_DEV_IDETAPE */ +extern int idetape_init (void); +#endif #ifdef CONFIG_BLK_DEV_IDEFLOPPY -int idefloppy_reinit (ide_drive_t *drive); -int idefloppy_init (void); -#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ +extern int idefloppy_init (void); +#endif #ifdef CONFIG_BLK_DEV_IDESCSI -int idescsi_reinit (ide_drive_t *drive); -int idescsi_init (void); -#endif /* CONFIG_BLK_DEV_IDESCSI */ -#endif /* _IDE_C */ - -extern int ide_register_module (struct ide_driver_s *d); -extern void ide_unregister_module (struct ide_driver_s *d); -ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n); -extern int ide_register_subdriver(ide_drive_t *drive, ide_driver_t *driver); +extern int idescsi_init (void); +#endif + +ide_drive_t *ide_scan_devices (byte media, const char *name, struct ata_operations *driver, int n); +extern int ide_register_subdriver(ide_drive_t *drive, struct ata_operations *driver); extern int ide_unregister_subdriver(ide_drive_t *drive); -extern int ide_replace_subdriver(ide_drive_t *drive, const char *driver); #ifdef CONFIG_BLK_DEV_IDEPCI #define ON_BOARD 1 #define NEVER_BOARD 0 #ifdef CONFIG_BLK_DEV_OFFBOARD -# define OFF_BOARD ON_BOARD -#else /* CONFIG_BLK_DEV_OFFBOARD */ -# define OFF_BOARD NEVER_BOARD -#endif /* CONFIG_BLK_DEV_OFFBOARD */ +# define OFF_BOARD ON_BOARD +#else +# define OFF_BOARD NEVER_BOARD +#endif -void ide_scan_pcibus (int scan_direction) __init; +void __init ide_scan_pcibus(int scan_direction); #endif #ifdef CONFIG_BLK_DEV_IDEDMA -#define BAD_DMA_DRIVE 0 -#define GOOD_DMA_DRIVE 1 +# define BAD_DMA_DRIVE 0 +# define GOOD_DMA_DRIVE 1 int ide_build_dmatable (ide_drive_t *drive, ide_dma_action_t func); void ide_destroy_dmatable (ide_drive_t *drive); ide_startstop_t ide_dma_intr (ide_drive_t *drive); int check_drive_lists (ide_drive_t *drive, int good_bad); -int report_drive_dmaing (ide_drive_t *drive); int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive); int ide_release_dma (ide_hwif_t *hwif); void ide_setup_dma (ide_hwif_t *hwif, unsigned long dmabase, unsigned int num_ports) __init; -/* FIXME spilt this up into a get and set function */ -extern unsigned long ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) __init; #endif extern spinlock_t ide_lock; extern int drive_is_ready(ide_drive_t *drive); +extern void revalidate_drives(void); #endif /* _IDE_H */ diff -Nru a/include/linux/if.h b/include/linux/if.h --- a/include/linux/if.h Thu Mar 7 18:17:44 2002 +++ b/include/linux/if.h Thu Mar 7 18:17:44 2002 @@ -21,6 +21,7 @@ #include /* for "__kernel_caddr_t" et al */ #include /* for "struct sockaddr" et al */ +#include /* Standard interface flags (netdevice->flags). */ #define IFF_UP 0x1 /* interface is up */ @@ -48,6 +49,29 @@ /* Private (from user) interface flags (netdevice->priv_flags). */ #define IFF_802_1Q_VLAN 0x1 /* 802.1Q VLAN device. */ + +#define IF_GET_IFACE 0x0001 /* for querying only */ +#define IF_GET_PROTO 0x0002 + +/* For definitions see hdlc.h */ +#define IF_IFACE_V35 0x1000 /* V.35 serial interface */ +#define IF_IFACE_V24 0x1001 /* V.24 serial interface */ +#define IF_IFACE_X21 0x1002 /* X.21 serial interface */ +#define IF_IFACE_T1 0x1003 /* T1 telco serial interface */ +#define IF_IFACE_E1 0x1004 /* E1 telco serial interface */ +#define IF_IFACE_SYNC_SERIAL 0x1005 /* cant'b be set by software */ + +/* For definitions see hdlc.h */ +#define IF_PROTO_HDLC 0x2000 /* raw HDLC protocol */ +#define IF_PROTO_PPP 0x2001 /* PPP protocol */ +#define IF_PROTO_CISCO 0x2002 /* Cisco HDLC protocol */ +#define IF_PROTO_FR 0x2003 /* Frame Relay protocol */ +#define IF_PROTO_FR_ADD_PVC 0x2004 /* Create FR PVC */ +#define IF_PROTO_FR_DEL_PVC 0x2005 /* Delete FR PVC */ +#define IF_PROTO_X25 0x2006 /* X.25 */ + + + /* * Device mapping structure. I'd just gone off and designed a * beautiful scheme using only loadable modules with arguments @@ -69,6 +93,19 @@ /* 3 bytes spare */ }; +struct if_settings +{ + unsigned int type; /* Type of physical device or protocol */ + union { + /* {atm/eth/dsl}_settings anyone ? */ + union hdlc_settings ifsu_hdlc; + union line_settings ifsu_line; + } ifs_ifsu; +}; + +#define ifs_hdlc ifs_ifsu.ifsu_hdlc +#define ifs_line ifs_ifsu.ifsu_line + /* * Interface request structure used for socket * ioctl's. All interface ioctl's must have parameter @@ -98,6 +135,7 @@ char ifru_slave[IFNAMSIZ]; /* Just fits the size */ char ifru_newname[IFNAMSIZ]; char * ifru_data; + struct if_settings *ifru_settings; } ifr_ifru; }; @@ -117,6 +155,7 @@ #define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */ #define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */ #define ifr_newname ifr_ifru.ifru_newname /* New name */ +#define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/ /* * Structure used in SIOCGIFCONF request. diff -Nru a/include/linux/if_ether.h b/include/linux/if_ether.h --- a/include/linux/if_ether.h Thu Mar 7 18:17:37 2002 +++ b/include/linux/if_ether.h Thu Mar 7 18:17:37 2002 @@ -85,6 +85,7 @@ #define ETH_P_CONTROL 0x0016 /* Card specific control frames */ #define ETH_P_IRDA 0x0017 /* Linux-IrDA */ #define ETH_P_ECONET 0x0018 /* Acorn Econet */ +#define ETH_P_HDLC 0x0019 /* HDLC frames */ /* * This is an Ethernet frame header. diff -Nru a/include/linux/intermezzo_fs.h b/include/linux/intermezzo_fs.h --- a/include/linux/intermezzo_fs.h Thu Mar 7 18:17:43 2002 +++ b/include/linux/intermezzo_fs.h Thu Mar 7 18:17:43 2002 @@ -68,7 +68,7 @@ }; /* super.c */ -struct presto_cache *presto_find_cache(kdev_t dev) ; +struct presto_cache *presto_find_cache(struct super_block *sb) ; extern struct file_system_type presto_fs_type; extern int init_intermezzo_fs(void); @@ -89,7 +89,6 @@ int cache_flags; char *cache_root_fileset; /* fileset mounted on cache "/" */ - kdev_t cache_dev; /* underlying block device */ struct super_block *cache_sb; struct dentry *cache_mtde; /* unix mtpt of cache XXX NOT VALID XXX */ char *cache_mtpt; /* again */ diff -Nru a/include/linux/irq_cpustat.h b/include/linux/irq_cpustat.h --- a/include/linux/irq_cpustat.h Thu Mar 7 18:17:42 2002 +++ b/include/linux/irq_cpustat.h Thu Mar 7 18:17:42 2002 @@ -19,11 +19,13 @@ extern irq_cpustat_t irq_stat[]; /* defined in asm/hardirq.h */ +#ifndef __ARCH_IRQ_STAT /* Some architectures can do this more efficiently */ #ifdef CONFIG_SMP #define __IRQ_STAT(cpu, member) (irq_stat[cpu].member) #else #define __IRQ_STAT(cpu, member) ((void)(cpu), irq_stat[0].member) #endif +#endif /* arch independent irq_stat fields */ #define softirq_pending(cpu) __IRQ_STAT((cpu), __softirq_pending) diff -Nru a/include/linux/jbd.h b/include/linux/jbd.h --- a/include/linux/jbd.h Thu Mar 7 18:17:46 2002 +++ b/include/linux/jbd.h Thu Mar 7 18:17:46 2002 @@ -352,7 +352,7 @@ */ struct journal_head * t_async_datalist; - /* Doubly-linked circular list of all forget buffers (superceded + /* Doubly-linked circular list of all forget buffers (superseded buffers which we can un-checkpoint once this transaction commits) */ struct journal_head * t_forget; @@ -793,7 +793,7 @@ #define BJ_SyncData 1 /* Normal data: flush before commit */ #define BJ_AsyncData 2 /* writepage data: wait on it before commit */ #define BJ_Metadata 3 /* Normal journaled metadata */ -#define BJ_Forget 4 /* Buffer superceded by this transaction */ +#define BJ_Forget 4 /* Buffer superseded by this transaction */ #define BJ_IO 5 /* Buffer is for temporary IO use */ #define BJ_Shadow 6 /* Buffer contents being shadowed to the log */ #define BJ_LogCtl 7 /* Buffer contains log descriptors */ diff -Nru a/include/linux/jffs2.h b/include/linux/jffs2.h --- a/include/linux/jffs2.h Thu Mar 7 18:17:41 2002 +++ b/include/linux/jffs2.h Thu Mar 7 18:17:41 2002 @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: jffs2.h,v 1.18 2001/03/25 22:36:12 dwmw2 Exp $ + * $Id: jffs2.h,v 1.19 2001/10/09 13:20:23 dwmw2 Exp $ * */ @@ -103,7 +103,7 @@ __u16 nodetype; __u32 totlen; /* So we can skip over nodes we don't grok */ __u32 hdr_crc; -}; +} __attribute__((packed)); struct jffs2_raw_dirent { @@ -121,7 +121,7 @@ __u32 node_crc; __u32 name_crc; __u8 name[0]; -}; +} __attribute__((packed)); /* The JFFS2 raw inode structure: Used for storage on physical media. */ /* The uid, gid, atime, mtime and ctime members could be longer, but @@ -153,7 +153,7 @@ __u32 data_crc; /* CRC for the (compressed) data. */ __u32 node_crc; /* CRC for the raw inode (excluding data) */ // __u8 data[dsize]; -}; +} __attribute__((packed)); union jffs2_node_union { struct jffs2_raw_inode i; diff -Nru a/include/linux/jffs2_fs_sb.h b/include/linux/jffs2_fs_sb.h --- a/include/linux/jffs2_fs_sb.h Thu Mar 7 18:17:37 2002 +++ b/include/linux/jffs2_fs_sb.h Thu Mar 7 18:17:37 2002 @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_sb.h,v 1.16 2001/09/18 20:15:18 dwmw2 Exp $ */ +/* $Id: jffs2_fs_sb.h,v 1.16.2.1 2002/02/23 14:13:34 dwmw2 Exp $ */ #ifndef _JFFS2_FS_SB #define _JFFS2_FS_SB @@ -12,6 +12,7 @@ #define INOCACHE_HASHSIZE 1 #define JFFS2_SB_FLAG_RO 1 +#define JFFS2_SB_FLAG_MOUNTING 2 /* A struct for the overall file system control. Pointers to jffs2_sb_info structs are named `c' in the source code. diff -Nru a/include/linux/lvm.h b/include/linux/lvm.h --- a/include/linux/lvm.h Thu Mar 7 18:17:37 2002 +++ b/include/linux/lvm.h Thu Mar 7 18:17:37 2002 @@ -477,8 +477,16 @@ * Structure Logical Volume (LV) Version 3 */ -/* core */ -typedef struct lv_v5 { +struct kern_lv_v5; +struct user_lv_v5; +typedef struct user_lv_v5 userlv_t; +#ifdef __KERNEL__ +typedef struct kern_lv_v5 lv_t; +#else +typedef struct user_lv_v5 lv_t; +#endif + +struct user_lv_v5 { char lv_name[NAME_LEN]; char vg_name[NAME_LEN]; uint lv_access; @@ -501,15 +509,18 @@ uint lv_read_ahead; /* delta to version 1 starts here */ - struct lv_v5 *lv_snapshot_org; - struct lv_v5 *lv_snapshot_prev; - struct lv_v5 *lv_snapshot_next; + lv_t *lv_snapshot_org; + lv_t *lv_snapshot_prev; + lv_t *lv_snapshot_next; lv_block_exception_t *lv_block_exception; uint lv_remap_ptr; uint lv_remap_end; uint lv_chunk_size; uint lv_snapshot_minor; -#ifdef __KERNEL__ +}; + +struct kern_lv_v5{ + struct user_lv_v5 u; struct kiobuf *lv_iobuf; sector_t blocks[LVM_MAX_SECTORS]; struct kiobuf *lv_COW_table_iobuf; @@ -520,12 +531,8 @@ wait_queue_head_t lv_snapshot_wait; int lv_snapshot_use_rate; struct vg_v3 *vg; - uint lv_allocated_snapshot_le; -#else - char dummy[200]; -#endif -} lv_t; +}; /* disk */ typedef struct lv_disk_v3 { @@ -679,13 +686,13 @@ } static int inline LVM_GET_COW_TABLE_CHUNKS_PER_PE(vg_t *vg, lv_t *lv) { - return vg->pe_size / lv->lv_chunk_size; + return vg->pe_size / lv->u.lv_chunk_size; } static int inline LVM_GET_COW_TABLE_ENTRIES_PER_PE(vg_t *vg, lv_t *lv) { - ulong chunks = vg->pe_size / lv->lv_chunk_size; + ulong chunks = vg->pe_size / lv->u.lv_chunk_size; ulong entry_size = sizeof(lv_COW_table_disk_t); - ulong chunk_size = lv->lv_chunk_size * SECTOR_SIZE; + ulong chunk_size = lv->u.lv_chunk_size * SECTOR_SIZE; ulong entries = (vg->pe_size * SECTOR_SIZE) / (entry_size + chunk_size); diff -Nru a/include/linux/mm.h b/include/linux/mm.h --- a/include/linux/mm.h Thu Mar 7 18:17:37 2002 +++ b/include/linux/mm.h Thu Mar 7 18:17:37 2002 @@ -61,8 +61,7 @@ * one of the address_space->i_mmap{,shared} lists, * for shm areas, the list of attaches, otherwise unused. */ - struct vm_area_struct *vm_next_share; - struct vm_area_struct **vm_pprev_share; + list_t shared; /* Function pointers to deal with this struct. */ struct vm_operations_struct * vm_ops; @@ -194,11 +193,6 @@ #define page_count(p) atomic_read(&(p)->count) #define set_page_count(p,v) atomic_set(&(p)->count, v) -static inline void init_page_count(struct page *page) -{ - page->count.counter = 0; -} - /* * Various page->flags bits: * @@ -585,7 +579,7 @@ extern struct vm_area_struct *find_extend_vma(struct mm_struct *mm, unsigned long addr); -extern struct page * vmalloc_to_page(unsigned long adr); +extern struct page * vmalloc_to_page(void *addr); #endif /* __KERNEL__ */ diff -Nru a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h --- a/include/linux/nfsd/export.h Thu Mar 7 18:17:36 2002 +++ b/include/linux/nfsd/export.h Thu Mar 7 18:17:36 2002 @@ -87,8 +87,7 @@ void nfsd_export_init(void); void nfsd_export_shutdown(void); void exp_readlock(void); -int exp_writelock(void); -void exp_unlock(void); +void exp_readunlock(void); struct svc_client * exp_getclient(struct sockaddr_in *sin); void exp_putclient(struct svc_client *clp); struct svc_export * exp_get(struct svc_client *clp, kdev_t dev, ino_t ino); diff -Nru a/include/linux/nfsd/interface.h b/include/linux/nfsd/interface.h --- a/include/linux/nfsd/interface.h Thu Mar 7 18:17:43 2002 +++ b/include/linux/nfsd/interface.h Thu Mar 7 18:17:43 2002 @@ -12,13 +12,15 @@ #include -#ifdef CONFIG_NFSD_MODULE +#ifndef CONFIG_NFSD +#ifdef CONFIG_MODULES extern struct nfsd_linkage { long (*do_nfsservctl)(int cmd, void *argp, void *resp); struct module *owner; } * nfsd_linkage; +#endif #endif #endif /* LINUX_NFSD_INTERFACE_H */ diff -Nru a/include/linux/parport.h b/include/linux/parport.h --- a/include/linux/parport.h Thu Mar 7 18:17:42 2002 +++ b/include/linux/parport.h Thu Mar 7 18:17:42 2002 @@ -459,7 +459,10 @@ extern int parport_negotiate (struct parport *, int mode); extern ssize_t parport_write (struct parport *, const void *buf, size_t len); extern ssize_t parport_read (struct parport *, void *buf, size_t len); + +#define PARPORT_INACTIVITY_O_NONBLOCK 1 extern long parport_set_timeout (struct pardevice *, long inactivity); + extern int parport_wait_event (struct parport *, long timeout); extern int parport_wait_peripheral (struct parport *port, unsigned char mask, diff -Nru a/include/linux/pci.h b/include/linux/pci.h --- a/include/linux/pci.h Thu Mar 7 18:17:44 2002 +++ b/include/linux/pci.h Thu Mar 7 18:17:44 2002 @@ -483,7 +483,7 @@ int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */ void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */ int (*save_state) (struct pci_dev *dev, u32 state); /* Save Device Context */ - int (*suspend)(struct pci_dev *dev, u32 state); /* Device suspended */ + int (*suspend) (struct pci_dev *dev, u32 state); /* Device suspended */ int (*resume) (struct pci_dev *dev); /* Device woken up */ int (*enable_wake) (struct pci_dev *dev, u32 state, int enable); /* Enable wake event */ }; @@ -564,6 +564,10 @@ int pci_enable_device(struct pci_dev *dev); void pci_disable_device(struct pci_dev *dev); void pci_set_master(struct pci_dev *dev); +#define HAVE_PCI_SET_MWI +int pci_set_mwi(struct pci_dev *dev); +void pci_clear_mwi(struct pci_dev *dev); +int pdev_set_mwi(struct pci_dev *dev); int pci_set_dma_mask(struct pci_dev *dev, u64 mask); int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask); int pci_assign_resource(struct pci_dev *dev, int i); diff -Nru a/include/linux/pci_ids.h b/include/linux/pci_ids.h --- a/include/linux/pci_ids.h Thu Mar 7 18:17:43 2002 +++ b/include/linux/pci_ids.h Thu Mar 7 18:17:43 2002 @@ -772,6 +772,7 @@ #define PCI_VENDOR_ID_3COM 0x10b7 #define PCI_DEVICE_ID_3COM_3C985 0x0001 #define PCI_DEVICE_ID_3COM_3C339 0x3390 +#define PCI_DEVICE_ID_3COM_3C359 0x3590 #define PCI_DEVICE_ID_3COM_3C590 0x5900 #define PCI_DEVICE_ID_3COM_3C595TX 0x5950 #define PCI_DEVICE_ID_3COM_3C595T4 0x5951 @@ -1461,9 +1462,9 @@ #define PCI_VENDOR_ID_OXSEMI 0x1415 #define PCI_DEVICE_ID_OXSEMI_12PCI840 0x8403 #define PCI_DEVICE_ID_OXSEMI_16PCI954 0x9501 -#define PCI_DEVICE_ID_OXSEMI_16PCI952 0x950A #define PCI_DEVICE_ID_OXSEMI_16PCI95N 0x9511 #define PCI_DEVICE_ID_OXSEMI_16PCI954PP 0x9513 +#define PCI_DEVICE_ID_OXSEMI_16PCI952 0x9521 #define PCI_VENDOR_ID_AIRONET 0x14b9 #define PCI_DEVICE_ID_AIRONET_4800_1 0x0001 @@ -1493,7 +1494,11 @@ #define PCI_VENDOR_ID_BROADCOM 0x14e4 #define PCI_DEVICE_ID_TIGON3_5700 0x1644 #define PCI_DEVICE_ID_TIGON3_5701 0x1645 +#define PCI_DEVICE_ID_TIGON3_5702 0x1646 #define PCI_DEVICE_ID_TIGON3_5703 0x1647 +#define PCI_DEVICE_ID_TIGON3_5702FE 0x164d +#define PCI_DEVICE_ID_TIGON3_5702X 0x16a6 +#define PCI_DEVICE_ID_TIGON3_5703X 0x16a7 #define PCI_VENDOR_ID_SYBA 0x1592 #define PCI_DEVICE_ID_SYBA_2P_EPP 0x0782 @@ -1515,6 +1520,9 @@ #define PCI_DEVICE_ID_MACROLINK_MCCSH 0x1003 #define PCI_DEVICE_ID_MACROLINK_MCCR8 0x2000 #define PCI_DEVICE_ID_MACROLINK_MCCR 0x2001 + +#define PCI_VENDOR_ID_ALTIMA 0x173b +#define PCI_DEVICE_ID_ALTIMA_AC1000 0x03e8 #define PCI_VENDOR_ID_SYMPHONY 0x1c1c #define PCI_DEVICE_ID_SYMPHONY_101 0x0001 diff -Nru a/include/linux/pnpbios.h b/include/linux/pnpbios.h --- a/include/linux/pnpbios.h Thu Mar 7 18:17:42 2002 +++ b/include/linux/pnpbios.h Thu Mar 7 18:17:42 2002 @@ -142,7 +142,7 @@ extern int pnpbios_dont_use_current_config; extern void *pnpbios_kmalloc(size_t size, int f); -extern void pnpbios_init (void); +extern int pnpbios_init (void); extern void pnpbios_proc_init (void); extern int pnp_bios_dev_node_info (struct pnp_dev_node_info *data); diff -Nru a/include/linux/reiserfs_fs_sb.h b/include/linux/reiserfs_fs_sb.h --- a/include/linux/reiserfs_fs_sb.h Thu Mar 7 18:17:45 2002 +++ b/include/linux/reiserfs_fs_sb.h Thu Mar 7 18:17:45 2002 @@ -454,6 +454,5 @@ #define SB_JOURNAL_MAX_COMMIT_AGE(s) (SB_JOURNAL(s)->s_journal_max_commit_age) #define SB_JOURNAL_MAX_TRANS_AGE(s) (SB_JOURNAL(s)->s_journal_max_trans_age) #define SB_JOURNAL_DEV(s) (SB_JOURNAL(s)->j_dev) - #endif /* _LINUX_REISER_FS_SB */ diff -Nru a/include/linux/rtc.h b/include/linux/rtc.h --- a/include/linux/rtc.h Thu Mar 7 18:17:36 2002 +++ b/include/linux/rtc.h Thu Mar 7 18:17:36 2002 @@ -66,4 +66,17 @@ #define RTC_WKALM_SET _IOW('p', 0x0f, struct rtc_wkalrm)/* Set wakeup alarm*/ #define RTC_WKALM_RD _IOR('p', 0x10, struct rtc_wkalrm)/* Get wakeup alarm*/ +#ifdef __KERNEL__ + +typedef struct rtc_task { + void (*func)(void *private_data); + void *private_data; +} rtc_task_t; + +int rtc_register(rtc_task_t *task); +int rtc_unregister(rtc_task_t *task); +int rtc_control(rtc_task_t *t, unsigned int cmd, unsigned long arg); + +#endif /* __KERNEL__ */ + #endif /* _LINUX_RTC_H_ */ diff -Nru a/include/linux/sched.h b/include/linux/sched.h --- a/include/linux/sched.h Thu Mar 7 18:17:38 2002 +++ b/include/linux/sched.h Thu Mar 7 18:17:38 2002 @@ -150,8 +150,7 @@ extern void update_one_process(struct task_struct *p, unsigned long user, unsigned long system, int cpu); extern void scheduler_tick(int user_tick, int system); -extern void sched_task_migrated(struct task_struct *p); -extern void smp_migrate_task(int cpu, task_t *task); +extern void migration_init(void); extern unsigned long cache_decay_ticks; @@ -286,6 +285,7 @@ wait_queue_head_t wait_chldexit; /* for wait4() */ struct completion *vfork_done; /* for vfork() */ + unsigned long rt_priority; unsigned long it_real_value, it_prof_value, it_virt_value; unsigned long it_real_incr, it_prof_incr, it_virt_incr; @@ -382,7 +382,12 @@ */ #define _STK_LIM (8*1024*1024) +#if CONFIG_SMP extern void set_cpus_allowed(task_t *p, unsigned long new_mask); +#else +# define set_cpus_allowed(p, new_mask) do { } while (0) +#endif + extern void set_user_nice(task_t *p, long nice); extern int task_prio(task_t *p); extern int task_nice(task_t *p); @@ -460,7 +465,6 @@ extern unsigned long prof_shift; extern void FASTCALL(__wake_up(wait_queue_head_t *q, unsigned int mode, int nr)); -extern void FASTCALL(__wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr)); extern void FASTCALL(sleep_on(wait_queue_head_t *q)); extern long FASTCALL(sleep_on_timeout(wait_queue_head_t *q, signed long timeout)); @@ -474,13 +478,9 @@ #define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1) #define wake_up_nr(x, nr) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, nr) #define wake_up_all(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 0) -#define wake_up_sync(x) __wake_up_sync((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1) -#define wake_up_sync_nr(x, nr) __wake_up_sync((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, nr) #define wake_up_interruptible(x) __wake_up((x),TASK_INTERRUPTIBLE, 1) #define wake_up_interruptible_nr(x, nr) __wake_up((x),TASK_INTERRUPTIBLE, nr) #define wake_up_interruptible_all(x) __wake_up((x),TASK_INTERRUPTIBLE, 0) -#define wake_up_interruptible_sync(x) __wake_up_sync((x),TASK_INTERRUPTIBLE, 1) -#define wake_up_interruptible_sync_nr(x) __wake_up_sync((x),TASK_INTERRUPTIBLE, nr) asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru); extern int in_group_p(gid_t); diff -Nru a/include/linux/smb.h b/include/linux/smb.h --- a/include/linux/smb.h Thu Mar 7 18:17:45 2002 +++ b/include/linux/smb.h Thu Mar 7 18:17:45 2002 @@ -86,7 +86,7 @@ uid_t f_uid; gid_t f_gid; kdev_t f_rdev; - off_t f_size; + loff_t f_size; time_t f_atime; time_t f_mtime; time_t f_ctime; diff -Nru a/include/linux/smb_fs.h b/include/linux/smb_fs.h --- a/include/linux/smb_fs.h Thu Mar 7 18:17:45 2002 +++ b/include/linux/smb_fs.h Thu Mar 7 18:17:45 2002 @@ -117,6 +117,7 @@ #define SMB_CAP_NT_FIND 0x0200 #define SMB_CAP_DFS 0x1000 #define SMB_CAP_LARGE_READX 0x4000 +#define SMB_CAP_LARGE_WRITEX 0x8000 /* @@ -157,6 +158,30 @@ union smb_dir_cache *cache; unsigned long fpos, ofs; int filled, valid, idx; +}; + +#define SMB_OPS_NUM_STATIC 5 +struct smb_ops { + int (*read)(struct inode *inode, loff_t offset, int count, + char *data); + int (*write)(struct inode *inode, loff_t offset, int count, const + char *data); + int (*readdir)(struct file *filp, void *dirent, filldir_t filldir, + struct smb_cache_control *ctl); + + int (*getattr)(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *fattr); + /* int (*setattr)(...); */ /* setattr is really icky! */ + + int (*truncate)(struct inode *inode, loff_t length); + + + /* --- --- --- end of "static" entries --- --- --- */ + + int (*convert)(unsigned char *output, int olen, + const unsigned char *input, int ilen, + struct nls_table *nls_from, + struct nls_table *nls_to); }; static inline int diff -Nru a/include/linux/smb_fs_sb.h b/include/linux/smb_fs_sb.h --- a/include/linux/smb_fs_sb.h Thu Mar 7 18:17:42 2002 +++ b/include/linux/smb_fs_sb.h Thu Mar 7 18:17:42 2002 @@ -54,8 +54,7 @@ to put it on the stack. This points to temp_buf space. */ char *name_buf; - int (*convert)(char *, int, const char *, int, - struct nls_table *, struct nls_table *); + struct smb_ops *ops; }; diff -Nru a/include/linux/smb_mount.h b/include/linux/smb_mount.h --- a/include/linux/smb_mount.h Thu Mar 7 18:17:36 2002 +++ b/include/linux/smb_mount.h Thu Mar 7 18:17:36 2002 @@ -37,6 +37,7 @@ #define SMB_MOUNT_OLDATTR 0x0002 /* Use core getattr (Win 95 speedup) */ #define SMB_MOUNT_DIRATTR 0x0004 /* Use find_first for getattr */ #define SMB_MOUNT_CASE 0x0008 /* Be case sensitive */ +#define SMB_MOUNT_UNICODE 0x0010 /* Server talks unicode */ struct smb_mount_data_kernel { diff -Nru a/include/linux/smbno.h b/include/linux/smbno.h --- a/include/linux/smbno.h Thu Mar 7 18:17:45 2002 +++ b/include/linux/smbno.h Thu Mar 7 18:17:45 2002 @@ -281,4 +281,51 @@ #define TRANSACT2_FINDNOTIFYNEXT 12 #define TRANSACT2_MKDIR 13 +/* Information Levels - Shared? */ +#define SMB_INFO_STANDARD 1 +#define SMB_INFO_QUERY_EA_SIZE 2 +#define SMB_INFO_QUERY_EAS_FROM_LIST 3 +#define SMB_INFO_QUERY_ALL_EAS 4 +#define SMB_INFO_IS_NAME_VALID 6 + +/* Information Levels - TRANSACT2_FINDFIRST */ +#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 +#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_FILE_NAMES_INFO 0x103 +#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 + +/* Information Levels - TRANSACT2_QPATHINFO */ +#define SMB_QUERY_FILE_BASIC_INFO 0x101 +#define SMB_QUERY_FILE_STANDARD_INFO 0x102 +#define SMB_QUERY_FILE_EA_INFO 0x103 +#define SMB_QUERY_FILE_NAME_INFO 0x104 +#define SMB_QUERY_FILE_ALL_INFO 0x107 +#define SMB_QUERY_FILE_ALT_NAME_INFO 0x108 +#define SMB_QUERY_FILE_STREAM_INFO 0x109 +#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10b + +/* Information Levels - TRANSACT2_SETFILEINFO */ +#define SMB_SET_FILE_BASIC_INFO 0x101 +#define SMB_SET_FILE_DISPOSITION_INFO 0x102 +#define SMB_SET_FILE_ALLOCATION_INFO 0x103 +#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 + +/* smb_flg field flags */ +#define SMB_FLAGS_SUPPORT_LOCKREAD 0x01 +#define SMB_FLAGS_CLIENT_BUF_AVAIL 0x02 +#define SMB_FLAGS_RESERVED 0x04 +#define SMB_FLAGS_CASELESS_PATHNAMES 0x08 +#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10 +#define SMB_FLAGS_REQUEST_OPLOCK 0x20 +#define SMB_FLAGS_REQUEST_BATCH_OPLOCK 0x40 +#define SMB_FLAGS_REPLY 0x80 + +/* smb_flg2 field flags (samba-2.2.0/source/include/smb.h) */ +#define SMB_FLAGS2_LONG_PATH_COMPONENTS 0x0001 +#define SMB_FLAGS2_EXTENDED_ATTRIBUTES 0x0002 +#define SMB_FLAGS2_DFS_PATHNAMES 0x1000 +#define SMB_FLAGS2_READ_PERMIT_NO_EXECUTE 0x2000 +#define SMB_FLAGS2_32_BIT_ERROR_CODES 0x4000 +#define SMB_FLAGS2_UNICODE_STRINGS 0x8000 + #endif /* _SMBNO_H_ */ diff -Nru a/include/linux/smp.h b/include/linux/smp.h --- a/include/linux/smp.h Thu Mar 7 18:17:36 2002 +++ b/include/linux/smp.h Thu Mar 7 18:17:36 2002 @@ -11,6 +11,7 @@ #ifdef CONFIG_SMP #include +#include #include /* @@ -71,7 +72,17 @@ #define MSG_RESCHEDULE 0x0003 /* Reschedule request from master CPU*/ #define MSG_CALL_FUNCTION 0x0004 /* Call function on all other CPUs */ -#else +#define __per_cpu_data __attribute__((section(".data.percpu"))) + +#ifndef __HAVE_ARCH_PER_CPU +extern unsigned long __per_cpu_offset[NR_CPUS]; + +/* var is in discarded region: offset to particular copy we want */ +#define per_cpu(var, cpu) RELOC_HIDE(var, per_cpu_offset(cpu)) + +#define this_cpu(var) per_cpu(var, smp_processor_id()) +#endif /* !__HAVE_ARCH_PER_CPU */ +#else /* !SMP */ /* * These macros fold the SMP functionality into a single CPU system @@ -90,6 +101,9 @@ #define cpu_online_map 1 static inline void smp_send_reschedule(int cpu) { } static inline void smp_send_reschedule_all(void) { } +#define __per_cpu_data +#define per_cpu(var, cpu) var +#define this_cpu(var) var #endif #endif diff -Nru a/include/linux/sockios.h b/include/linux/sockios.h --- a/include/linux/sockios.h Thu Mar 7 18:17:37 2002 +++ b/include/linux/sockios.h Thu Mar 7 18:17:37 2002 @@ -81,6 +81,8 @@ #define SIOCGMIIREG 0x8948 /* Read MII PHY register. */ #define SIOCSMIIREG 0x8949 /* Write MII PHY register. */ +#define SIOCWANDEV 0x894A /* get/set netdev parameters */ + /* ARP cache control calls. */ /* 0x8950 - 0x8952 * obsolete calls, don't re-use */ #define SIOCDARP 0x8953 /* delete ARP table entry */ diff -Nru a/include/linux/spinlock.h b/include/linux/spinlock.h --- a/include/linux/spinlock.h Thu Mar 7 18:17:44 2002 +++ b/include/linux/spinlock.h Thu Mar 7 18:17:44 2002 @@ -84,12 +84,12 @@ #define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 } #endif -#define spin_lock_init(lock) do { } while(0) +#define spin_lock_init(lock) do { (void)(lock); } while(0) #define _raw_spin_lock(lock) (void)(lock) /* Not "unused variable". */ -#define spin_is_locked(lock) (0) -#define _raw_spin_trylock(lock) ({1; }) -#define spin_unlock_wait(lock) do { } while(0) -#define _raw_spin_unlock(lock) do { } while(0) +#define spin_is_locked(lock) ((void)(lock), 0) +#define _raw_spin_trylock(lock) ((void)(lock), 1) +#define spin_unlock_wait(lock) do { (void)(lock); } while(0) +#define _raw_spin_unlock(lock) do { (void)(lock); } while(0) #elif (DEBUG_SPINLOCKS < 2) diff -Nru a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h --- a/include/linux/sunrpc/sched.h Thu Mar 7 18:17:38 2002 +++ b/include/linux/sunrpc/sched.h Thu Mar 7 18:17:38 2002 @@ -34,13 +34,11 @@ * This is the RPC task struct */ struct rpc_task { - struct rpc_task * tk_prev; /* wait queue links */ - struct rpc_task * tk_next; + struct list_head tk_list; /* wait queue links */ #ifdef RPC_DEBUG unsigned long tk_magic; /* 0xf00baa */ #endif - struct rpc_task * tk_next_task; /* global list of tasks */ - struct rpc_task * tk_prev_task; /* global list of tasks */ + struct list_head tk_task; /* global list of tasks */ struct rpc_clnt * tk_client; /* RPC client */ struct rpc_rqst * tk_rqstp; /* RPC request */ int tk_status; /* result of last operation */ @@ -88,6 +86,20 @@ #define tk_auth tk_client->cl_auth #define tk_xprt tk_client->cl_xprt +/* support walking a list of tasks on a wait queue */ +#define task_for_each(task, pos, head) \ + list_for_each(pos, head) \ + if ((task=list_entry(pos, struct rpc_task, tk_list)),1) + +#define task_for_first(task, head) \ + if (!list_empty(head) && \ + ((task=list_entry((head)->next, struct rpc_task, tk_list)),1)) + +/* .. and walking list of all tasks */ +#define alltask_for_each(task, pos, head) \ + list_for_each(pos, head) \ + if ((task=list_entry(pos, struct rpc_task, tk_task)),1) + typedef void (*rpc_action)(struct rpc_task *); /* @@ -133,16 +145,24 @@ * RPC synchronization objects */ struct rpc_wait_queue { - struct rpc_task * task; + struct list_head tasks; #ifdef RPC_DEBUG char * name; #endif }; #ifndef RPC_DEBUG -# define RPC_INIT_WAITQ(name) ((struct rpc_wait_queue) { NULL }) +# define RPC_WAITQ_INIT(var,qname) ((struct rpc_wait_queue) {LIST_HEAD_INIT(var)}) +# define RPC_WAITQ(var,qname) struct rpc_wait_queue var = RPC_WAITQ_INIT(var.tasks,qname) +# define INIT_RPC_WAITQ(ptr,qname) do { \ + INIT_LIST_HEAD(&(ptr)->tasks); \ + } while(0) #else -# define RPC_INIT_WAITQ(name) ((struct rpc_wait_queue) { NULL, name }) +# define RPC_WAITQ_INIT(var,qname) ((struct rpc_wait_queue) {LIST_HEAD_INIT(var.tasks), qname}) +# define RPC_WAITQ(var,qname) struct rpc_wait_queue var = RPC_WAITQ_INIT(var,qname) +# define INIT_RPC_WAITQ(ptr,qname) do { \ + INIT_LIST_HEAD(&(ptr)->tasks); (ptr)->name = qname; \ + } while(0) #endif /* diff -Nru a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h --- a/include/linux/sunrpc/svc.h Thu Mar 7 18:17:36 2002 +++ b/include/linux/sunrpc/svc.h Thu Mar 7 18:17:36 2002 @@ -27,8 +27,8 @@ * We currently do not support more than one RPC program per daemon. */ struct svc_serv { - struct svc_rqst * sv_threads; /* idle server threads */ - struct svc_sock * sv_sockets; /* pending sockets */ + struct list_head sv_threads; /* idle server threads */ + struct list_head sv_sockets; /* pending sockets */ struct svc_program * sv_program; /* RPC program */ struct svc_stat * sv_stats; /* RPC statistics */ spinlock_t sv_lock; @@ -36,7 +36,9 @@ unsigned int sv_bufsz; /* datagram buffer size */ unsigned int sv_xdrsize; /* XDR buffer size */ - struct svc_sock * sv_allsocks; /* all sockets */ + struct list_head sv_permsocks; /* all permanent sockets */ + struct list_head sv_tempsocks; /* all temporary sockets */ + int sv_tmpcnt; /* count of temporary sockets */ char * sv_name; /* service name */ }; @@ -89,8 +91,7 @@ * NOTE: First two items must be prev/next. */ struct svc_rqst { - struct svc_rqst * rq_prev; /* idle list */ - struct svc_rqst * rq_next; + struct list_head rq_list; /* idle list */ struct svc_sock * rq_sock; /* socket */ struct sockaddr_in rq_addr; /* peer address */ int rq_addrlen; @@ -115,6 +116,10 @@ void * rq_argp; /* decoded arguments */ void * rq_resp; /* xdr'd results */ + int rq_reserved; /* space on socket outq + * reserved for this request + */ + /* Catering to nfsd */ struct svc_client * rq_client; /* RPC peer info */ struct svc_cacherep * rq_cacherep; /* cache info */ @@ -163,6 +168,7 @@ unsigned int pc_ressize; /* result struct size */ unsigned int pc_count; /* call count */ unsigned int pc_cachetype; /* cache info (NFS) */ + unsigned int pc_xdrressize; /* maximum size of XDR reply */ }; /* @@ -180,5 +186,6 @@ int svc_process(struct svc_serv *, struct svc_rqst *); int svc_register(struct svc_serv *, int, unsigned short); void svc_wake_up(struct svc_serv *); +void svc_reserve(struct svc_rqst *rqstp, int space); #endif /* SUNRPC_SVC_H */ diff -Nru a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h --- a/include/linux/sunrpc/svcsock.h Thu Mar 7 18:17:40 2002 +++ b/include/linux/sunrpc/svcsock.h Thu Mar 7 18:17:40 2002 @@ -13,38 +13,38 @@ /* * RPC server socket. - * NOTE: First two items must be prev/next. */ struct svc_sock { - struct svc_sock * sk_prev; /* list of ready sockets */ - struct svc_sock * sk_next; - struct svc_sock * sk_list; /* list of all sockets */ + struct list_head sk_ready; /* list of ready sockets */ + struct list_head sk_list; /* list of all sockets */ struct socket * sk_sock; /* berkeley socket layer */ struct sock * sk_sk; /* INET layer */ - spinlock_t sk_lock; struct svc_serv * sk_server; /* service for this socket */ unsigned char sk_inuse; /* use count */ - unsigned char sk_busy; /* enqueued/receiving */ - unsigned char sk_conn; /* conn pending */ - unsigned char sk_close; /* dead or dying */ - int sk_data; /* data pending */ - unsigned int sk_temp : 1, /* temp socket */ - sk_qued : 1, /* on serv->sk_sockets */ - sk_dead : 1; /* socket closed */ + unsigned int sk_flags; +#define SK_BUSY 0 /* enqueued/receiving */ +#define SK_CONN 1 /* conn pending */ +#define SK_CLOSE 2 /* dead or dying */ +#define SK_DATA 3 /* data pending */ +#define SK_TEMP 4 /* temp (TCP) socket */ +#define SK_QUED 5 /* on serv->sk_sockets */ +#define SK_DEAD 6 /* socket closed */ + + int sk_reserved; /* space on outq that is reserved */ + int (*sk_recvfrom)(struct svc_rqst *rqstp); int (*sk_sendto)(struct svc_rqst *rqstp); /* We keep the old state_change and data_ready CB's here */ void (*sk_ostate)(struct sock *); void (*sk_odata)(struct sock *, int bytes); + void (*sk_owspace)(struct sock *); /* private TCP part */ int sk_reclen; /* length of record */ int sk_tcplen; /* current read length */ - - /* Debugging */ - struct svc_rqst * sk_rqstp; + time_t sk_lastrecv; /* time of last received request */ }; /* @@ -55,5 +55,6 @@ int svc_recv(struct svc_serv *, struct svc_rqst *, long); int svc_send(struct svc_rqst *); void svc_drop(struct svc_rqst *); +void svc_sock_update_bufs(struct svc_serv *serv); #endif /* SUNRPC_SVCSOCK_H */ diff -Nru a/include/linux/sunrpc/types.h b/include/linux/sunrpc/types.h --- a/include/linux/sunrpc/types.h Thu Mar 7 18:17:40 2002 +++ b/include/linux/sunrpc/types.h Thu Mar 7 18:17:40 2002 @@ -12,60 +12,7 @@ #include #include #include - -/* - * These are the RPC list manipulation primitives used everywhere. - */ -struct rpc_listitem { - struct rpc_listitem * prev; - struct rpc_listitem * next; -}; - -static __inline__ void -__rpc_append_list(struct rpc_listitem **q, struct rpc_listitem *item) -{ - struct rpc_listitem *next, *prev; - - if (!(next = *q)) { - *q = item->next = item->prev = item; - } else { - prev = next->prev; - prev->next = item; - next->prev = item; - item->next = next; - item->prev = prev; - } -} - -static __inline__ void -__rpc_insert_list(struct rpc_listitem **q, struct rpc_listitem *item) -{ - __rpc_append_list(q, item); - *q = item; -} - -static __inline__ void -__rpc_remove_list(struct rpc_listitem **q, struct rpc_listitem *item) -{ - struct rpc_listitem *prev = item->prev, - *next = item->next; - - if (item != prev) { - next->prev = prev; - prev->next = next; - } else { - next = NULL; - } - if (*q == item) - *q = next; -} - -#define rpc_insert_list(q, i) \ - __rpc_insert_list((struct rpc_listitem **) q, (struct rpc_listitem *) i) -#define rpc_append_list(q, i) \ - __rpc_append_list((struct rpc_listitem **) q, (struct rpc_listitem *) i) -#define rpc_remove_list(q, i) \ - __rpc_remove_list((struct rpc_listitem **) q, (struct rpc_listitem *) i) +#include /* * Shorthands diff -Nru a/include/linux/swap.h b/include/linux/swap.h --- a/include/linux/swap.h Thu Mar 7 18:17:37 2002 +++ b/include/linux/swap.h Thu Mar 7 18:17:37 2002 @@ -64,7 +64,6 @@ enum { SWP_USED = (1 << 0), /* is slot in swap_info[] used? */ SWP_WRITEOK = (1 << 1), /* ok to write to this swap? */ - SWP_BLOCKDEV = (1 << 2), /* is this swap a block device? */ SWP_ACTIVE = (SWP_USED | SWP_WRITEOK), }; @@ -151,7 +150,6 @@ extern int total_swap_pages; extern unsigned int nr_swapfiles; extern struct swap_info_struct swap_info[]; -extern int is_swap_partition(kdev_t); extern void si_swapinfo(struct sysinfo *); extern swp_entry_t get_swap_page(void); extern void get_swaphandle_info(swp_entry_t, unsigned long *, struct inode **); diff -Nru a/include/linux/telephony.h b/include/linux/telephony.h --- a/include/linux/telephony.h Thu Mar 7 18:17:45 2002 +++ b/include/linux/telephony.h Thu Mar 7 18:17:45 2002 @@ -228,8 +228,8 @@ * indicate the current state of the hookswitch. The pstn_ring bit * indicates that the DAA on a LineJACK card has detected ring voltage on * the PSTN port. The caller_id bit indicates that caller_id data has been -* recieved and is available. The pstn_wink bit indicates that the DAA on -* the LineJACK has recieved a wink from the telco switch. The f0, f1, f2 +* received and is available. The pstn_wink bit indicates that the DAA on +* the LineJACK has received a wink from the telco switch. The f0, f1, f2 * and f3 bits indicate that the filter has been triggered by detecting the * frequency programmed into that filter. * diff -Nru a/include/linux/usb.h b/include/linux/usb.h --- a/include/linux/usb.h Thu Mar 7 18:17:43 2002 +++ b/include/linux/usb.h Thu Mar 7 18:17:43 2002 @@ -44,8 +44,8 @@ /* * USB directions */ -#define USB_DIR_OUT 0 -#define USB_DIR_IN 0x80 +#define USB_DIR_OUT 0 /* to device */ +#define USB_DIR_IN 0x80 /* to host */ /* * Endpoints @@ -148,12 +148,6 @@ unsigned long devicemap[128 / (8*sizeof(unsigned long))]; }; -#define USB_MAXBUS 64 - -struct usb_busmap { - unsigned long busmap[USB_MAXBUS / (8*sizeof(unsigned long))]; -}; - struct usb_device; /*-------------------------------------------------------------------------*/ @@ -516,7 +510,8 @@ * work to connect to a device should be done when the device is opened, * and undone at the last close. The disconnect code needs to address * concurrency issues with respect to open() and close() methods, as - * well as cancel any I/O requests that are still pending. + * well as forcing all pending I/O requests to complete (by unlinking + * them as necessary, and blocking until the unlinks complete). */ struct usb_driver { struct module *owner; @@ -905,13 +900,7 @@ /* Host Controller Driver (HCD) support */ -struct usb_operations { - int (*allocate)(struct usb_device *); - int (*deallocate)(struct usb_device *); - int (*get_frame_number) (struct usb_device *usb_dev); - int (*submit_urb) (struct urb *urb, int mem_flags); - int (*unlink_urb) (struct urb *urb); -}; +struct usb_operations; #define DEVNUM_ROUND_ROBIN /***** OPTION *****/ @@ -944,41 +933,12 @@ atomic_t refcnt; }; -extern struct usb_bus *usb_alloc_bus(struct usb_operations *); -extern void usb_free_bus(struct usb_bus *); -extern void usb_register_bus(struct usb_bus *); -extern void usb_deregister_bus(struct usb_bus *); -extern int usb_register_root_hub(struct usb_device *usb_dev, struct device *parent_dev); - -extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb); -extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, - int bustime, int isoc); -extern void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, - int isoc); +// FIXME: root_hub_string vanishes when "usb_hcd" conversion is done, +// along with pre-hcd versions of the OHCI and UHCI drivers. extern int usb_root_hub_string(int id, int serial, char *type, __u8 *data, int len); /* - * Some USB 1.1 bandwidth allocation constants. - */ -#define BW_HOST_DELAY 1000L /* nanoseconds */ -#define BW_HUB_LS_SETUP 333L /* nanoseconds */ - /* 4 full-speed bit times (est.) */ - -#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */ -#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L) -#define FRAME_TIME_USECS 1000L -#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L) - -#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */ - /* Trying not to use worst-case bit-stuffing - of (7/6 * 8 * bytecount) = 9.33 * bytecount */ - /* bytecount = data payload byte count */ - -#define NS_TO_US(ns) ((ns + 500L) / 1000L) - /* convert & round nanoseconds to microseconds */ - -/* * As of USB 2.0, full/low speed devices are segregated into trees. * One type grows from USB 1.1 host controllers (OHCI, UHCI etc). * The other type grows from high speed hubs when they connect to @@ -1209,13 +1169,11 @@ /* -------------------------------------------------------------------------- */ /* - * bus and driver list + * driver list * exported only for usbfs (not visible outside usbcore) */ extern struct list_head usb_driver_list; -extern struct list_head usb_bus_list; -extern struct semaphore usb_bus_list_lock; /* * USB device fs stuff diff -Nru a/include/math-emu/op-4.h b/include/math-emu/op-4.h --- a/include/math-emu/op-4.h Thu Mar 7 18:17:41 2002 +++ b/include/math-emu/op-4.h Thu Mar 7 18:17:41 2002 @@ -645,7 +645,7 @@ X##_f[1] = (rsize <= _FP_W_TYPE_SIZE ? 0 : r >> _FP_W_TYPE_SIZE); \ X##_f[2] = (rsize <= 2*_FP_W_TYPE_SIZE ? 0 : r >> 2*_FP_W_TYPE_SIZE); \ X##_f[3] = (rsize <= 3*_FP_W_TYPE_SIZE ? 0 : r >> 3*_FP_W_TYPE_SIZE); \ - } while (0); + } while (0) #define _FP_FRAC_CONV_4_1(dfs, sfs, D, S) \ do { \ diff -Nru a/include/net/irda/irda-usb.h b/include/net/irda/irda-usb.h --- a/include/net/irda/irda-usb.h Thu Mar 7 18:17:41 2002 +++ b/include/net/irda/irda-usb.h Thu Mar 7 18:17:41 2002 @@ -139,10 +139,10 @@ wait_queue_head_t wait_q; /* for timeouts */ - struct urb rx_urb[IU_MAX_RX_URBS]; /* URBs used to receive data frames */ + struct urb *rx_urb[IU_MAX_RX_URBS]; /* URBs used to receive data frames */ struct urb *idle_rx_urb; /* Pointer to idle URB in Rx path */ - struct urb tx_urb; /* URB used to send data frames */ - struct urb speed_urb; /* URB used to send speed commands */ + struct urb *tx_urb; /* URB used to send data frames */ + struct urb *speed_urb; /* URB used to send speed commands */ struct net_device *netdev; /* Yes! we are some kind of netdev. */ struct net_device_stats stats; diff -Nru a/include/net/irda/irda.h b/include/net/irda/irda.h --- a/include/net/irda/irda.h Thu Mar 7 18:17:41 2002 +++ b/include/net/irda/irda.h Thu Mar 7 18:17:41 2002 @@ -54,8 +54,8 @@ #define IRDA_MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif -#ifndef ALIGN -# define ALIGN __attribute__((aligned)) +#ifndef IRDA_ALIGN +# define IRDA_ALIGN __attribute__((aligned)) #endif #ifndef PACK # define PACK __attribute__((packed)) diff -Nru a/include/net/irda/irlap.h b/include/net/irda/irlap.h --- a/include/net/irda/irlap.h Thu Mar 7 18:17:43 2002 +++ b/include/net/irda/irlap.h Thu Mar 7 18:17:43 2002 @@ -11,6 +11,7 @@ * * Copyright (c) 1998-1999 Dag Brattli , * All Rights Reserved. + * Copyright (c) 2000-2001 Jean Tourrilhes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -44,6 +45,7 @@ #define LAP_ADDR_HEADER 1 /* IrLAP Address Header */ #define LAP_CTRL_HEADER 1 /* IrLAP Control Header */ +/* May be different when we get VFIR */ #define LAP_MAX_HEADER (LAP_ADDR_HEADER + LAP_CTRL_HEADER) #define BROADCAST 0xffffffff /* Broadcast device address */ @@ -210,9 +212,8 @@ void irlap_init_qos_capabilities(struct irlap_cb *, struct qos_info *); void irlap_apply_default_connection_parameters(struct irlap_cb *self); void irlap_apply_connection_parameters(struct irlap_cb *self, int now); -void irlap_set_local_busy(struct irlap_cb *self, int status); -#define IRLAP_GET_HEADER_SIZE(self) 2 /* Will be different when we get VFIR */ +#define IRLAP_GET_HEADER_SIZE(self) (LAP_MAX_HEADER) #define IRLAP_GET_TX_QUEUE_LEN(self) skb_queue_len(&self->txq) #endif diff -Nru a/include/net/irda/irlap_event.h b/include/net/irda/irlap_event.h --- a/include/net/irda/irlap_event.h Thu Mar 7 18:17:36 2002 +++ b/include/net/irda/irlap_event.h Thu Mar 7 18:17:36 2002 @@ -134,7 +134,6 @@ void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info); -void irlap_next_state(struct irlap_cb *self, IRLAP_STATE state); void irlap_print_event(IRLAP_EVENT event); extern int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb); diff -Nru a/include/net/irda/irqueue.h b/include/net/irda/irqueue.h --- a/include/net/irda/irqueue.h Thu Mar 7 18:17:41 2002 +++ b/include/net/irda/irqueue.h Thu Mar 7 18:17:41 2002 @@ -49,8 +49,8 @@ #define HASHBIN_SIZE 8 #define HASHBIN_MASK 0x7 -#ifndef ALIGN -#define ALIGN __attribute__((aligned)) +#ifndef IRDA_ALIGN +#define IRDA_ALIGN __attribute__((aligned)) #endif #define Q_NULL { NULL, NULL, "", 0 } @@ -75,8 +75,8 @@ __u32 magic; int hb_type; int hb_size; - spinlock_t hb_mutex[HASHBIN_SIZE] ALIGN; - irda_queue_t *hb_queue[HASHBIN_SIZE] ALIGN; + spinlock_t hb_mutex[HASHBIN_SIZE] IRDA_ALIGN; + irda_queue_t *hb_queue[HASHBIN_SIZE] IRDA_ALIGN; irda_queue_t* hb_current; } hashbin_t; diff -Nru a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h --- a/include/sound/ac97_codec.h Thu Mar 7 18:17:36 2002 +++ b/include/sound/ac97_codec.h Thu Mar 7 18:17:36 2002 @@ -135,6 +135,7 @@ struct _snd_ac97 { void (*write) (ac97_t *ac97, unsigned short reg, unsigned short val); unsigned short (*read) (ac97_t *ac97, unsigned short reg); + void (*wait) (ac97_t *ac97); void (*init) (ac97_t *ac97); snd_info_entry_t *proc_entry; snd_info_entry_t *proc_regs_entry; diff -Nru a/include/sound/asound.h b/include/sound/asound.h --- a/include/sound/asound.h Thu Mar 7 18:17:45 2002 +++ b/include/sound/asound.h Thu Mar 7 18:17:45 2002 @@ -127,7 +127,7 @@ * * *****************************************************************************/ -#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 1) typedef unsigned long sndrv_pcm_uframes_t; typedef long sndrv_pcm_sframes_t; @@ -411,6 +411,7 @@ SNDRV_PCM_IOCTL_PAUSE = _IOW('A', 0x45, int), SNDRV_PCM_IOCTL_REWIND = _IOW('A', 0x46, sndrv_pcm_uframes_t), SNDRV_PCM_IOCTL_RESUME = _IO('A', 0x47), + SNDRV_PCM_IOCTL_XRUN = _IO('A', 0x48), SNDRV_PCM_IOCTL_WRITEI_FRAMES = _IOW('A', 0x50, struct sndrv_xferi), SNDRV_PCM_IOCTL_READI_FRAMES = _IOR('A', 0x51, struct sndrv_xferi), SNDRV_PCM_IOCTL_WRITEN_FRAMES = _IOW('A', 0x52, struct sndrv_xfern), @@ -578,7 +579,7 @@ * * ****************************************************************************/ -#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) +#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) struct sndrv_ctl_card_info { int card; /* card number */ diff -Nru a/include/sound/core.h b/include/sound/core.h --- a/include/sound/core.h Thu Mar 7 18:17:44 2002 +++ b/include/sound/core.h Thu Mar 7 18:17:44 2002 @@ -22,6 +22,10 @@ * */ +#ifdef CONFIG_PM +#include /* wake_up() and struct semaphore */ +#endif + /* Typedef's */ typedef struct timeval snd_timestamp_t; typedef struct sndrv_interval snd_interval_t; @@ -292,13 +296,21 @@ /* misc.c */ int snd_task_name(struct task_struct *task, char *name, size_t size); +#ifdef CONFIG_SND_VERBOSE_PRINTK +int snd_verbose_printk(const char *file, int line, const char *format); +#endif /* --- */ +#ifdef CONFIG_SND_VERBOSE_PRINTK +#define snd_printk(format, args...) do { \ + printk(snd_verbose_printk(__FILE__, __LINE__, format) ? format + 3 : format, ##args); \ +} while (0) +#else #define snd_printk(format, args...) do { \ - printk("ALSA %s:%d: ", __FILE__, __LINE__); \ printk(format, ##args); \ } while (0) +#endif #ifdef CONFIG_SND_DEBUG diff -Nru a/include/sound/driver.h b/include/sound/driver.h --- a/include/sound/driver.h Thu Mar 7 18:17:42 2002 +++ b/include/sound/driver.h Thu Mar 7 18:17:42 2002 @@ -78,12 +78,4 @@ #include "sndmagic.h" -/* - * Temporary hack, until linux/init.h is fixed. - */ -#include -#ifndef __devexit_p -#define __devexit_p(x) x -#endif - #endif /* __SOUND_DRIVER_H */ diff -Nru a/include/sound/emu10k1.h b/include/sound/emu10k1.h --- a/include/sound/emu10k1.h Thu Mar 7 18:17:40 2002 +++ b/include/sound/emu10k1.h Thu Mar 7 18:17:40 2002 @@ -588,7 +588,7 @@ #define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ #define A_DBG 0x53 -#define A_DBG_SINGLE_STEP_ADDR 0x00020000 /* Set to zero to start dsp */ +#define A_DBG_SINGLE_STEP 0x00020000 /* Set to zero to start dsp */ #define A_DBG_ZC 0x40000000 /* zero tram counter */ #define A_DBG_STEP_ADDR 0x000003ff #define A_DBG_SATURATION_OCCURED 0x20000000 @@ -657,6 +657,11 @@ #define SRCS_RATELOCKED 0x01000000 /* Sample rate locked */ #define SRCS_ESTSAMPLERATE 0x0007ffff /* Do not modify this field. */ +/* Note that these values can vary +/- by a small amount */ +#define SRCS_SPDIFRATE_44 0x0003acd9 +#define SRCS_SPDIFRATE_48 0x00040000 +#define SRCS_SPDIFRATE_96 0x00080000 + #define MICIDX 0x63 /* Microphone recording buffer index register */ #define MICIDX_MASK 0x0000ffff /* 16-bit value */ #define MICIDX_IDX 0x10000063 @@ -683,15 +688,15 @@ #define A_MUSTAT2 A_MUCMD2 #define A_SPDIF_SAMPLERATE 0x76 /* Set the sample rate of SPDIF output */ -#define A_SPDIF_48000 0x00000000 -#define A_SPDIF_44100 0x00000040 -#define A_SPDIF_96000 0x00000080 +#define A_SPDIF_48000 0x00000080 +#define A_SPDIF_44100 0x00000000 +#define A_SPDIF_96000 0x00000040 #define A_FXRT2 0x7c -#define A_FXRT_CHANNELE 0x0000003f /* Effects send bus number for channel's effects send A */ -#define A_FXRT_CHANNELF 0x00003f00 /* Effects send bus number for channel's effects send B */ -#define A_FXRT_CHANNELG 0x003f0000 /* Effects send bus number for channel's effects send C */ -#define A_FXRT_CHANNELH 0x3f000000 /* Effects send bus number for channel's effects send D */ +#define A_FXRT_CHANNELE 0x0000003f /* Effects send bus number for channel's effects send E */ +#define A_FXRT_CHANNELF 0x00003f00 /* Effects send bus number for channel's effects send F */ +#define A_FXRT_CHANNELG 0x003f0000 /* Effects send bus number for channel's effects send G */ +#define A_FXRT_CHANNELH 0x3f000000 /* Effects send bus number for channel's effects send H */ #define A_SENDAMOUNTS 0x7d #define A_FXSENDAMOUNT_E_MASK 0xFF000000 @@ -797,8 +802,8 @@ }; typedef struct { - unsigned long send_routing[3]; - unsigned char send_volume[3][4]; + unsigned char send_routing[3][8]; + unsigned char send_volume[3][8]; unsigned short attn[3]; snd_kcontrol_t *ctl_send_routing; snd_kcontrol_t *ctl_send_volume; @@ -806,6 +811,15 @@ emu10k1_pcm_t *epcm; } emu10k1_pcm_mixer_t; +#define snd_emu10k1_compose_send_routing(route) \ +((route[0] | (route[1] << 4) | (route[2] << 8) | (route[3] << 12)) << 16) + +#define snd_emu10k1_compose_audigy_fxrt1(route) \ +(((unsigned int)route[0] | ((unsigned int)route[1] << 8) | ((unsigned int)route[2] << 16) | ((unsigned int)route[3] << 12)) << 24) + +#define snd_emu10k1_compose_audigy_fxrt2(route) \ +(((unsigned int)route[4] | ((unsigned int)route[5] << 8) | ((unsigned int)route[6] << 16) | ((unsigned int)route[7] << 12)) << 24) + typedef struct snd_emu10k1_memblk { snd_util_memblk_t mem; /* private part */ @@ -1102,11 +1116,11 @@ #define GPR_NOISE1 0x59 /* noise source */ #define GPR_IRQ 0x5a /* IRQ register */ #define GPR_DBAC 0x5b /* TRAM Delay Base Address Counter */ -#define GPR(x) (FXGPREGBASE + (x)) /* free GPRs: x = 0x00 - 0xff */ -#define ITRAM_DATA(x) (TANKMEMDATAREGBASE + (x)) /* x = 0x00 - 0x7f */ -#define ETRAM_DATA(x) (TANKMEMDATAREGBASE + 80 + (x)) /* x = 0x00 - 0x1f */ -#define ITRAM_ADDR(x) (TANKMEMADDRREGBASE + (x)) /* x = 0x00 - 0x7f */ -#define ETRAM_ADDR(x) (TANKMEMADDRREGBASE + 80 + (x)) /* x = 0x00 - 0x1f */ +#define GPR(x) (FXGPREGBASE + (x)) /* free GPRs: x = 0x00 - 0xff */ +#define ITRAM_DATA(x) (TANKMEMDATAREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ +#define ETRAM_DATA(x) (TANKMEMDATAREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ +#define ITRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ +#define ETRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ #define A_FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x3f? */ #define A_EXTIN(x) (0x40 + (x)) /* x = 0x00 - 0x1f? */ @@ -1171,6 +1185,10 @@ #define A_EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ #define A_EXTIN_SPDIF_CD_L 0x02 /* digital CD left */ #define A_EXTIN_SPDIF_CD_R 0x03 /* digital CD left */ +#define A_EXTIN_LINE2_L 0x08 /* audigy drive line2/mic2 - left */ +#define A_EXTIN_LINE2_R 0x09 /* right */ +#define A_EXTIN_AUX2_L 0x0c /* audigy drive aux2 - left */ +#define A_EXTIN_AUX2_R 0x0d /* - right */ /* Audigiy Outputs */ #define A_EXTOUT_FRONT_L 0x00 /* digital front left */ @@ -1189,9 +1207,38 @@ /* 0x0d ?? */ #define A_EXTOUT_AREAR_L 0x0e /* analog rear left */ #define A_EXTOUT_AREAR_R 0x0f /* right */ -/* 0x10-0x15 ?? */ -#define A_EXTOUT_ADC_CAP_L 0x16 /* ADC capture buffer left */ -#define A_EXTOUT_ADC_CAP_R 0x17 /* right */ +#define A_EXTOUT_AC97_L 0x10 /* AC97 left (front) */ +#define A_EXTOUT_AC97_R 0x11 /* right */ +#define A_EXTOUT_ADC_CAP_L 0x12 /* ADC capture buffer left */ +#define A_EXTOUT_ADC_CAP_R 0x13 /* right */ + +/* Audigy constants */ +#define A_C_00000000 0xc0 +#define A_C_00000001 0xc1 +#define A_C_00000002 0xc2 +#define A_C_00000003 0xc3 +#define A_C_00000004 0xc4 +#define A_C_00000008 0xc5 +#define A_C_00000010 0xc6 +#define A_C_00000020 0xc7 +#define A_C_00000100 0xc8 +#define A_C_00010000 0xc9 +#define A_C_00000800 0xca +#define A_C_10000000 0xcb +#define A_C_20000000 0xcc +#define A_C_40000000 0xcd +#define A_C_80000000 0xce +#define A_C_7fffffff 0xcf +#define A_C_ffffffff 0xd0 +#define A_C_fffffffe 0xd1 +#define A_C_c0000000 0xd2 +#define A_C_4f1bbcdc 0xd3 +#define A_C_5a7ef9db 0xd4 +#define A_C_00100000 0xd5 +/* 0xd6 = 0x7fffffff (?) ACCUM? */ +/* 0xd7 = 0x0000000 CCR */ +/* 0xd8 = noise1 */ +/* 0xd9 = noise2 */ /* definitions for debug register */ #define EMU10K1_DBG_ZC 0x80000000 /* zero tram counter */ diff -Nru a/include/sound/info.h b/include/sound/info.h --- a/include/sound/info.h Thu Mar 7 18:17:39 2002 +++ b/include/sound/info.h Thu Mar 7 18:17:39 2002 @@ -104,6 +104,11 @@ #ifdef CONFIG_PROC_FS extern snd_info_entry_t *snd_seq_root; +#ifdef CONFIG_SND_OSSEMUL +extern snd_info_entry_t *snd_oss_root; +#else +#define snd_oss_root NULL +#endif int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) __attribute__ ((format (printf, 2, 3))); int snd_info_init(void); @@ -138,6 +143,7 @@ #else #define snd_seq_root NULL +#define snd_oss_root NULL static inline int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) { return 0; } static inline int snd_info_init(void) { return 0; } diff -Nru a/include/sound/pcm.h b/include/sound/pcm.h --- a/include/sound/pcm.h Thu Mar 7 18:17:44 2002 +++ b/include/sound/pcm.h Thu Mar 7 18:17:44 2002 @@ -25,6 +25,7 @@ #include #include +#include typedef sndrv_pcm_uframes_t snd_pcm_uframes_t; typedef sndrv_pcm_sframes_t snd_pcm_sframes_t; diff -Nru a/include/sound/pcm_params.h b/include/sound/pcm_params.h --- a/include/sound/pcm_params.h Thu Mar 7 18:17:39 2002 +++ b/include/sound/pcm_params.h Thu Mar 7 18:17:39 2002 @@ -22,8 +22,6 @@ * */ -#include - extern int snd_pcm_hw_param_mask(snd_pcm_substream_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, const snd_mask_t *val); extern unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params, diff -Nru a/include/sound/version.h b/include/sound/version.h --- a/include/sound/version.h Thu Mar 7 18:17:42 2002 +++ b/include/sound/version.h Thu Mar 7 18:17:42 2002 @@ -1,3 +1,3 @@ /* include/version.h. Generated automatically by configure. */ -#define CONFIG_SND_VERSION "0.9.0beta11" -#define CONFIG_SND_DATE " (Tue Feb 19 08:14:59 2002 UTC)" +#define CONFIG_SND_VERSION "0.9.0beta12" +#define CONFIG_SND_DATE " (Wed Mar 06 07:56:20 2002 UTC)" diff -Nru a/init/do_mounts.c b/init/do_mounts.c --- a/init/do_mounts.c Thu Mar 7 18:17:42 2002 +++ b/init/do_mounts.c Thu Mar 7 18:17:42 2002 @@ -840,7 +840,7 @@ mount_devfs_fs (); } -#ifdef BUILD_CRAMDISK +#if defined(BUILD_CRAMDISK) && defined(CONFIG_BLK_DEV_RAM) /* * gzip declarations @@ -985,4 +985,4 @@ return result; } -#endif /* BUILD_CRAMDISK */ +#endif /* BUILD_CRAMDISK && CONFIG_BLK_DEV_RAM */ diff -Nru a/init/main.c b/init/main.c --- a/init/main.c Thu Mar 7 18:17:39 2002 +++ b/init/main.c Thu Mar 7 18:17:39 2002 @@ -270,8 +270,35 @@ #define smp_init() do { } while (0) #endif +static inline void setup_per_cpu_areas(void) +{ +} #else +#ifndef __HAVE_ARCH_PER_CPU +unsigned long __per_cpu_offset[NR_CPUS]; + +static void __init setup_per_cpu_areas(void) +{ + unsigned long size, i; + char *ptr; + /* Created by linker magic */ + extern char __per_cpu_start[], __per_cpu_end[]; + + /* Copy section for each CPU (we discard the original) */ + size = ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES); + if (!size) + return; + + ptr = alloc_bootmem(size * NR_CPUS); + + for (i = 0; i < NR_CPUS; i++, ptr += size) { + __per_cpu_offset[i] = ptr - __per_cpu_start; + memcpy(ptr, __per_cpu_start, size); + } +} +#endif /* !__HAVE_ARCH_PER_CPU */ + /* Called by boot processor to activate the rest. */ static void __init smp_init(void) { @@ -314,6 +341,7 @@ lock_kernel(); printk(linux_banner); setup_arch(&command_line); + setup_per_cpu_areas(); printk("Kernel command line: %s\n", saved_command_line); parse_options(command_line); trap_init(); @@ -377,6 +405,7 @@ printk("POSIX conformance testing by UNIFIX\n"); init_idle(current, smp_processor_id()); + /* * We count on the initial thread going ok * Like idlers init is an unlocked kernel thread, which will @@ -413,7 +442,12 @@ */ static void __init do_basic_setup(void) { - + /* + * Let the per-CPU migration threads start up: + */ +#if CONFIG_SMP + migration_init(); +#endif /* * Tell the world that we're going to be the grim * reaper of innocent orphaned children. @@ -455,6 +489,8 @@ static int init(void * unused) { + static char * argv_sh[] = { "sh", NULL, }; + lock_kernel(); do_basic_setup(); @@ -486,6 +522,6 @@ execve("/sbin/init",argv_init,envp_init); execve("/etc/init",argv_init,envp_init); execve("/bin/init",argv_init,envp_init); - execve("/bin/sh",argv_init,envp_init); + execve("/bin/sh",argv_sh,envp_init); panic("No init found. Try passing init= option to kernel."); } diff -Nru a/kernel/exec_domain.c b/kernel/exec_domain.c --- a/kernel/exec_domain.c Thu Mar 7 18:17:45 2002 +++ b/kernel/exec_domain.c Thu Mar 7 18:17:45 2002 @@ -4,7 +4,7 @@ * We group personalities into execution domains which have their * own handlers for kernel entry points, signal mapping, etc... * - * 2001-05-06 Complete rewrite, Christoph Hellwig (hch@caldera.de) + * 2001-05-06 Complete rewrite, Christoph Hellwig (hch@infradead.org) */ #include diff -Nru a/kernel/fork.c b/kernel/fork.c --- a/kernel/fork.c Thu Mar 7 18:17:38 2002 +++ b/kernel/fork.c Thu Mar 7 18:17:38 2002 @@ -219,11 +219,7 @@ /* insert tmp into the share list, just after mpnt */ spin_lock(&inode->i_mapping->i_shared_lock); - if((tmp->vm_next_share = mpnt->vm_next_share) != NULL) - mpnt->vm_next_share->vm_pprev_share = - &tmp->vm_next_share; - mpnt->vm_next_share = tmp; - tmp->vm_pprev_share = &mpnt->vm_next_share; + list_add_tail(&tmp->shared, &mpnt->shared); spin_unlock(&inode->i_mapping->i_shared_lock); } diff -Nru a/kernel/info.c b/kernel/info.c --- a/kernel/info.c Thu Mar 7 18:17:38 2002 +++ b/kernel/info.c Thu Mar 7 18:17:38 2002 @@ -26,7 +26,7 @@ val.loads[1] = avenrun[1] << (SI_LOAD_SHIFT - FSHIFT); val.loads[2] = avenrun[2] << (SI_LOAD_SHIFT - FSHIFT); - val.procs = nr_threads-1; + val.procs = nr_threads; sti(); si_meminfo(&val); diff -Nru a/kernel/kmod.c b/kernel/kmod.c --- a/kernel/kmod.c Thu Mar 7 18:17:41 2002 +++ b/kernel/kmod.c Thu Mar 7 18:17:41 2002 @@ -159,9 +159,14 @@ ret = exec_usermodehelper(modprobe_path, argv, envp); if (ret) { - printk(KERN_ERR - "kmod: failed to exec %s -s -k %s, errno = %d\n", - modprobe_path, (char*) module_name, errno); + static unsigned long last; + unsigned long now = jiffies; + if (now - last > HZ) { + last = now; + printk(KERN_DEBUG + "kmod: failed to exec %s -s -k %s, errno = %d\n", + modprobe_path, (char*) module_name, errno); + } } return ret; } diff -Nru a/kernel/ksyms.c b/kernel/ksyms.c --- a/kernel/ksyms.c Thu Mar 7 18:17:36 2002 +++ b/kernel/ksyms.c Thu Mar 7 18:17:36 2002 @@ -107,6 +107,7 @@ EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(vfree); EXPORT_SYMBOL(__vmalloc); +EXPORT_SYMBOL_GPL(vmalloc_to_page); EXPORT_SYMBOL(mem_map); EXPORT_SYMBOL(remap_page_range); EXPORT_SYMBOL(max_mapnr); @@ -151,6 +152,7 @@ EXPORT_SYMBOL(lookup_hash); EXPORT_SYMBOL(sys_close); EXPORT_SYMBOL(dcache_lock); +EXPORT_SYMBOL(dparent_lock); EXPORT_SYMBOL(d_alloc_root); EXPORT_SYMBOL(d_delete); EXPORT_SYMBOL(dget_locked); @@ -180,6 +182,7 @@ EXPORT_SYMBOL(invalidate_inode_pages); EXPORT_SYMBOL(truncate_inode_pages); EXPORT_SYMBOL(fsync_dev); +EXPORT_SYMBOL(fsync_bdev); EXPORT_SYMBOL(fsync_no_super); EXPORT_SYMBOL(permission); EXPORT_SYMBOL(vfs_permission); @@ -195,6 +198,8 @@ EXPORT_SYMBOL(cdput); EXPORT_SYMBOL(bdget); EXPORT_SYMBOL(bdput); +EXPORT_SYMBOL(bd_claim); +EXPORT_SYMBOL(bd_release); EXPORT_SYMBOL(__bread); EXPORT_SYMBOL(__brelse); EXPORT_SYMBOL(__bforget); @@ -310,7 +315,6 @@ EXPORT_SYMBOL(is_read_only); EXPORT_SYMBOL(set_device_ro); EXPORT_SYMBOL(bmap); -EXPORT_SYMBOL(sync_dev); EXPORT_SYMBOL(devfs_register_partitions); EXPORT_SYMBOL(blkdev_open); EXPORT_SYMBOL(blkdev_get); @@ -442,7 +446,6 @@ /* process management */ EXPORT_SYMBOL(complete_and_exit); EXPORT_SYMBOL(__wake_up); -EXPORT_SYMBOL(__wake_up_sync); EXPORT_SYMBOL(wake_up_process); EXPORT_SYMBOL(sleep_on); EXPORT_SYMBOL(sleep_on_timeout); @@ -457,6 +460,9 @@ EXPORT_SYMBOL(set_user_nice); EXPORT_SYMBOL(task_nice); EXPORT_SYMBOL_GPL(idle_cpu); +#if CONFIG_SMP +EXPORT_SYMBOL_GPL(set_cpus_allowed); +#endif EXPORT_SYMBOL(jiffies); EXPORT_SYMBOL(xtime); EXPORT_SYMBOL(do_gettimeofday); diff -Nru a/kernel/sched.c b/kernel/sched.c --- a/kernel/sched.c Thu Mar 7 18:17:40 2002 +++ b/kernel/sched.c Thu Mar 7 18:17:40 2002 @@ -16,12 +16,12 @@ #include #include #include +#include #include +#include #include #include -#include #include -#include /* * Priority of a process goes from 0 to 139. The 0-99 @@ -127,8 +127,6 @@ struct prio_array { int nr_active; - spinlock_t *lock; - runqueue_t *rq; unsigned long bitmap[BITMAP_SIZE]; list_t queue[MAX_PRIO]; }; @@ -146,6 +144,8 @@ task_t *curr, *idle; prio_array_t *active, *expired, arrays[2]; int prev_nr_running[NR_CPUS]; + task_t *migration_thread; + list_t migration_queue; } ____cacheline_aligned; static struct runqueue runqueues[NR_CPUS] __cacheline_aligned; @@ -156,23 +156,23 @@ #define cpu_curr(cpu) (cpu_rq(cpu)->curr) #define rt_task(p) ((p)->prio < MAX_RT_PRIO) -static inline runqueue_t *lock_task_rq(task_t *p, unsigned long *flags) +static inline runqueue_t *task_rq_lock(task_t *p, unsigned long *flags) { - struct runqueue *__rq; + struct runqueue *rq; repeat_lock_task: preempt_disable(); - __rq = task_rq(p); - spin_lock_irqsave(&__rq->lock, *flags); - if (unlikely(__rq != task_rq(p))) { - spin_unlock_irqrestore(&__rq->lock, *flags); + rq = task_rq(p); + spin_lock_irqsave(&rq->lock, *flags); + if (unlikely(rq != task_rq(p))) { + spin_unlock_irqrestore(&rq->lock, *flags); preempt_enable(); goto repeat_lock_task; } - return __rq; + return rq; } -static inline void unlock_task_rq(runqueue_t *rq, unsigned long *flags) +static inline void task_rq_unlock(runqueue_t *rq, unsigned long *flags) { spin_unlock_irqrestore(&rq->lock, *flags); preempt_enable(); @@ -184,7 +184,7 @@ static inline void dequeue_task(struct task_struct *p, prio_array_t *array) { array->nr_active--; - list_del_init(&p->run_list); + list_del(&p->run_list); if (list_empty(array->queue + p->prio)) __clear_bit(p->prio, array->bitmap); } @@ -289,31 +289,17 @@ cpu_relax(); barrier(); } - rq = lock_task_rq(p, &flags); + rq = task_rq_lock(p, &flags); if (unlikely(rq->curr == p)) { - unlock_task_rq(rq, &flags); + task_rq_unlock(rq, &flags); preempt_enable(); goto repeat; } - unlock_task_rq(rq, &flags); + task_rq_unlock(rq, &flags); preempt_enable(); } /* - * The SMP message passing code calls this function whenever - * the new task has arrived at the target CPU. We move the - * new task into the local runqueue. - * - * This function must be called with interrupts disabled. - */ -void sched_task_migrated(task_t *new_task) -{ - wait_task_inactive(new_task); - new_task->thread_info->cpu = smp_processor_id(); - wake_up_process(new_task); -} - -/* * Kick the remote CPU if the task is running currently, * this code is used by the signal code to signal tasks * which are in user-mode as quickly as possible. @@ -337,27 +323,27 @@ * "current->state = TASK_RUNNING" to mark yourself runnable * without the overhead of this. */ -static int try_to_wake_up(task_t * p, int synchronous) +static int try_to_wake_up(task_t * p) { unsigned long flags; int success = 0; runqueue_t *rq; - rq = lock_task_rq(p, &flags); + rq = task_rq_lock(p, &flags); p->state = TASK_RUNNING; if (!p->array) { activate_task(p, rq); - if ((rq->curr == rq->idle) || (p->prio < rq->curr->prio)) + if (p->prio < rq->curr->prio) resched_task(rq->curr); success = 1; } - unlock_task_rq(rq, &flags); + task_rq_unlock(rq, &flags); return success; } int wake_up_process(task_t * p) { - return try_to_wake_up(p, 0); + return try_to_wake_up(p); } void wake_up_forked_process(task_t * p) @@ -366,6 +352,7 @@ preempt_disable(); rq = this_rq(); + spin_lock_irq(&rq->lock); p->state = TASK_RUNNING; if (!rt_task(p)) { @@ -378,9 +365,9 @@ p->sleep_avg = p->sleep_avg * CHILD_PENALTY / 100; p->prio = effective_prio(p); } - spin_lock_irq(&rq->lock); p->thread_info->cpu = smp_processor_id(); activate_task(p, rq); + spin_unlock_irq(&rq->lock); preempt_enable(); } @@ -410,7 +397,7 @@ p->sleep_avg) / (EXIT_WEIGHT + 1); } -#if CONFIG_SMP +#if CONFIG_SMP || CONFIG_PREEMPT asmlinkage void schedule_tail(void) { spin_unlock_irq(&this_rq()->lock); @@ -861,44 +848,33 @@ * started to run but is not in state TASK_RUNNING. try_to_wake_up() returns * zero in this (rare) case, and we handle it by continuing to scan the queue. */ -static inline void __wake_up_common (wait_queue_head_t *q, unsigned int mode, - int nr_exclusive, const int sync) +static inline void __wake_up_common(wait_queue_head_t *q, unsigned int mode, int nr_exclusive) { struct list_head *tmp; + unsigned int state; + wait_queue_t *curr; task_t *p; - list_for_each(tmp,&q->task_list) { - unsigned int state; - wait_queue_t *curr = list_entry(tmp, wait_queue_t, task_list); - + list_for_each(tmp, &q->task_list) { + curr = list_entry(tmp, wait_queue_t, task_list); p = curr->task; state = p->state; - if ((state & mode) && - try_to_wake_up(p, sync) && - ((curr->flags & WQ_FLAG_EXCLUSIVE) && - !--nr_exclusive)) - break; + if ((state & mode) && try_to_wake_up(p) && + ((curr->flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)) + break; } } -void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr) +void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr_exclusive) { - if (q) { - unsigned long flags; - wq_read_lock_irqsave(&q->lock, flags); - __wake_up_common(q, mode, nr, 0); - wq_read_unlock_irqrestore(&q->lock, flags); - } -} + unsigned long flags; -void __wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr) -{ - if (q) { - unsigned long flags; - wq_read_lock_irqsave(&q->lock, flags); - __wake_up_common(q, mode, nr, 1); - wq_read_unlock_irqrestore(&q->lock, flags); - } + if (unlikely(!q)) + return; + + wq_read_lock_irqsave(&q->lock, flags); + __wake_up_common(q, mode, nr_exclusive); + wq_read_unlock_irqrestore(&q->lock, flags); } void complete(struct completion *x) @@ -907,7 +883,7 @@ spin_lock_irqsave(&x->wait.lock, flags); x->done++; - __wake_up_common(&x->wait, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, 0); + __wake_up_common(&x->wait, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1); spin_unlock_irqrestore(&x->wait.lock, flags); } @@ -994,34 +970,6 @@ return timeout; } -/* - * Change the current task's CPU affinity. Migrate the process to a - * proper CPU and schedule away if the current CPU is removed from - * the allowed bitmask. - */ -void set_cpus_allowed(task_t *p, unsigned long new_mask) -{ - new_mask &= cpu_online_map; - if (!new_mask) - BUG(); - if (p != current) - BUG(); - - p->cpus_allowed = new_mask; - /* - * Can the task run on the current CPU? If not then - * migrate the process off to a proper CPU. - */ - if (new_mask & (1UL << smp_processor_id())) - return; -#if CONFIG_SMP - current->state = TASK_UNINTERRUPTIBLE; - smp_migrate_task(__ffs(new_mask), current); - - schedule(); -#endif -} - void scheduling_functions_end_here(void) { } void set_user_nice(task_t *p, long nice) @@ -1036,7 +984,7 @@ * We have to be careful, if called from sys_setpriority(), * the task might be in the middle of scheduling on another CPU. */ - rq = lock_task_rq(p, &flags); + rq = task_rq_lock(p, &flags); if (rt_task(p)) { p->static_prio = NICE_TO_PRIO(nice); goto out_unlock; @@ -1056,7 +1004,7 @@ resched_task(rq->curr); } out_unlock: - unlock_task_rq(rq, &flags); + task_rq_unlock(rq, &flags); } #ifndef __alpha__ @@ -1154,7 +1102,7 @@ * To be able to change p->policy safely, the apropriate * runqueue lock must be held. */ - rq = lock_task_rq(p, &flags); + rq = task_rq_lock(p, &flags); if (policy < 0) policy = p->policy; @@ -1189,7 +1137,7 @@ retval = 0; p->policy = policy; p->rt_priority = lp.sched_priority; - if (rt_task(p)) + if (policy != SCHED_OTHER) p->prio = 99 - p->rt_priority; else p->prio = p->static_prio; @@ -1197,7 +1145,7 @@ activate_task(p, task_rq(p)); out_unlock: - unlock_task_rq(rq, &flags); + task_rq_unlock(rq, &flags); out_unlock_tasklist: read_unlock_irq(&tasklist_lock); @@ -1477,7 +1425,7 @@ void __init init_idle(task_t *idle, int cpu) { - runqueue_t *idle_rq = cpu_rq(cpu), *rq = idle->array->rq; + runqueue_t *idle_rq = cpu_rq(cpu), *rq = cpu_rq(idle->thread_info->cpu); unsigned long flags; __save_flags(flags); @@ -1493,6 +1441,9 @@ double_rq_unlock(idle_rq, rq); set_tsk_need_resched(idle); __restore_flags(flags); + + /* Set the preempt count _outside_ the spinlocks! */ + idle->thread_info->preempt_count = (idle->lock_depth >= 0); } extern void init_timervecs(void); @@ -1509,14 +1460,13 @@ runqueue_t *rq = cpu_rq(i); prio_array_t *array; - rq->active = rq->arrays + 0; + rq->active = rq->arrays; rq->expired = rq->arrays + 1; spin_lock_init(&rq->lock); + INIT_LIST_HEAD(&rq->migration_queue); for (j = 0; j < 2; j++) { array = rq->arrays + j; - array->rq = rq; - array->lock = &rq->lock; for (k = 0; k < MAX_PRIO; k++) { INIT_LIST_HEAD(array->queue + k); __clear_bit(k, array->bitmap); @@ -1545,3 +1495,173 @@ atomic_inc(&init_mm.mm_count); enter_lazy_tlb(&init_mm, current, smp_processor_id()); } + +#if CONFIG_SMP + +/* + * This is how migration works: + * + * 1) we queue a migration_req_t structure in the source CPU's + * runqueue and wake up that CPU's migration thread. + * 2) we down() the locked semaphore => thread blocks. + * 3) migration thread wakes up (implicitly it forces the migrated + * thread off the CPU) + * 4) it gets the migration request and checks whether the migrated + * task is still in the wrong runqueue. + * 5) if it's in the wrong runqueue then the migration thread removes + * it and puts it into the right queue. + * 6) migration thread up()s the semaphore. + * 7) we wake up and the migration is done. + */ + +typedef struct { + list_t list; + task_t *task; + struct semaphore sem; +} migration_req_t; + +/* + * Change a given task's CPU affinity. Migrate the process to a + * proper CPU and schedule it away if the CPU it's executing on + * is removed from the allowed bitmask. + * + * NOTE: the caller must have a valid reference to the task, the + * task must not exit() & deallocate itself prematurely. + */ +void set_cpus_allowed(task_t *p, unsigned long new_mask) +{ + unsigned long flags; + migration_req_t req; + runqueue_t *rq; + + new_mask &= cpu_online_map; + if (!new_mask) + BUG(); + + rq = task_rq_lock(p, &flags); + p->cpus_allowed = new_mask; + /* + * Can the task run on the task's current CPU? If not then + * migrate the process off to a proper CPU. + */ + if (new_mask & (1UL << p->thread_info->cpu)) { + task_rq_unlock(rq, &flags); + return; + } + + init_MUTEX_LOCKED(&req.sem); + req.task = p; + list_add(&req.list, &rq->migration_queue); + task_rq_unlock(rq, &flags); + wake_up_process(rq->migration_thread); + + down(&req.sem); +} + +static volatile unsigned long migration_mask; + +static int migration_thread(void * unused) +{ + struct sched_param param = { sched_priority: 99 }; + runqueue_t *rq; + int ret; + + daemonize(); + sigfillset(¤t->blocked); + set_fs(KERNEL_DS); + ret = setscheduler(0, SCHED_FIFO, ¶m); + + /* + * We have to migrate manually - there is no migration thread + * to do this for us yet :-) + * + * We use the following property of the Linux scheduler. At + * this point no other task is running, so by keeping all + * migration threads running, the load-balancer will distribute + * them between all CPUs equally. At that point every migration + * task binds itself to the current CPU. + */ + + /* wait for all migration threads to start up. */ + while (!migration_mask) + yield(); + + for (;;) { + preempt_disable(); + if (test_and_clear_bit(smp_processor_id(), &migration_mask)) + current->cpus_allowed = 1 << smp_processor_id(); + if (test_thread_flag(TIF_NEED_RESCHED)) + schedule(); + if (!migration_mask) + break; + preempt_enable(); + } + rq = this_rq(); + rq->migration_thread = current; + preempt_enable(); + + sprintf(current->comm, "migration_CPU%d", smp_processor_id()); + + for (;;) { + runqueue_t *rq_src, *rq_dest; + struct list_head *head; + int cpu_src, cpu_dest; + migration_req_t *req; + unsigned long flags; + task_t *p; + + spin_lock_irqsave(&rq->lock, flags); + head = &rq->migration_queue; + current->state = TASK_INTERRUPTIBLE; + if (list_empty(head)) { + spin_unlock_irqrestore(&rq->lock, flags); + schedule(); + continue; + } + req = list_entry(head->next, migration_req_t, list); + list_del_init(head->next); + spin_unlock_irqrestore(&rq->lock, flags); + + p = req->task; + cpu_dest = __ffs(p->cpus_allowed); + rq_dest = cpu_rq(cpu_dest); +repeat: + cpu_src = p->thread_info->cpu; + rq_src = cpu_rq(cpu_src); + + double_rq_lock(rq_src, rq_dest); + if (p->thread_info->cpu != cpu_src) { + double_rq_unlock(rq_src, rq_dest); + goto repeat; + } + if (rq_src == rq) { + p->thread_info->cpu = cpu_dest; + if (p->array) { + deactivate_task(p, rq_src); + activate_task(p, rq_dest); + } + } + double_rq_unlock(rq_src, rq_dest); + + up(&req->sem); + } +} + +void __init migration_init(void) +{ + int cpu; + + for (cpu = 0; cpu < smp_num_cpus; cpu++) + if (kernel_thread(migration_thread, NULL, + CLONE_FS | CLONE_FILES | CLONE_SIGNAL) < 0) + BUG(); + + migration_mask = (1 << smp_num_cpus) - 1; + + for (cpu = 0; cpu < smp_num_cpus; cpu++) + while (!cpu_rq(cpu)->migration_thread) + schedule_timeout(2); + if (migration_mask) + BUG(); +} +#endif diff -Nru a/kernel/signal.c b/kernel/signal.c --- a/kernel/signal.c Thu Mar 7 18:17:42 2002 +++ b/kernel/signal.c Thu Mar 7 18:17:42 2002 @@ -546,7 +546,7 @@ if (bad_signal(sig, info, t)) goto out_nolock; - /* The null signal is a permissions and process existance probe. + /* The null signal is a permissions and process existence probe. No signal is actually delivered. Same goes for zombies. */ ret = 0; if (!sig || !t->sig) diff -Nru a/lib/zlib_inflate/inflate.c b/lib/zlib_inflate/inflate.c --- a/lib/zlib_inflate/inflate.c Thu Mar 7 18:17:45 2002 +++ b/lib/zlib_inflate/inflate.c Thu Mar 7 18:17:45 2002 @@ -117,12 +117,12 @@ z_streamp z; int f; { - int r; + int r, trv; uInt b; if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) return Z_STREAM_ERROR; - f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + trv = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; r = Z_BUF_ERROR; while (1) switch (z->state->mode) { @@ -193,10 +193,10 @@ break; } if (r == Z_OK) - r = f; + r = trv; if (r != Z_STREAM_END) return r; - r = f; + r = trv; zlib_inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); if (z->state->nowrap) { diff -Nru a/mm/filemap.c b/mm/filemap.c --- a/mm/filemap.c Thu Mar 7 18:17:41 2002 +++ b/mm/filemap.c Thu Mar 7 18:17:41 2002 @@ -1399,7 +1399,7 @@ * virtual addresses, take care about potential aliasing * before reading the page on the kernel side. */ - if (mapping->i_mmap_shared != NULL) + if (!list_empty(&mapping->i_mmap_shared)) flush_dcache_page(page); /* @@ -1715,7 +1715,7 @@ return written; } -asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count) +static ssize_t common_sendfile(int out_fd, int in_fd, loff_t *offset, size_t count, loff_t max) { ssize_t retval; struct file * in_file, * out_file; @@ -1760,27 +1760,34 @@ retval = 0; if (count) { read_descriptor_t desc; - loff_t pos = 0, *ppos; + loff_t pos; + + if (!offset) + offset = &in_file->f_pos; - retval = -EFAULT; - ppos = &in_file->f_pos; - if (offset) { - if (get_user(pos, offset)) + pos = *offset; + retval = -EINVAL; + if (unlikely(pos < 0)) + goto fput_out; + if (unlikely(pos + count > max)) { + retval = -EOVERFLOW; + if (pos >= max) goto fput_out; - ppos = &pos; + count = max - pos; } desc.written = 0; desc.count = count; desc.buf = (char *) out_file; desc.error = 0; - do_generic_file_read(in_file, ppos, &desc, file_send_actor); + do_generic_file_read(in_file, offset, &desc, file_send_actor); retval = desc.written; if (!retval) retval = desc.error; - if (offset) - put_user(pos, offset); + pos = *offset; + if (pos > max) + retval = -EOVERFLOW; } fput_out: @@ -1789,6 +1796,38 @@ fput(in_file); out: return retval; +} + +asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count) +{ + loff_t pos, *ppos = NULL; + ssize_t ret; + if (offset) { + off_t off; + if (unlikely(get_user(off, offset))) + return -EFAULT; + pos = off; + ppos = &pos; + } + ret = common_sendfile(out_fd, in_fd, ppos, count, MAX_NON_LFS); + if (offset && put_user(pos, offset)) + ret = -EFAULT; + return ret; +} + +asmlinkage ssize_t sys_sendfile64(int out_fd, int in_fd, loff_t *offset, size_t count) +{ + loff_t pos, *ppos = NULL; + ssize_t ret; + if (offset) { + if (unlikely(copy_from_user(&pos, offset, sizeof(loff_t)))) + return -EFAULT; + ppos = &pos; + } + ret = common_sendfile(out_fd, in_fd, ppos, count, MAX_LFS_FILESIZE); + if (offset && put_user(pos, offset)) + ret = -EFAULT; + return ret; } static ssize_t do_readahead(struct file *file, unsigned long index, unsigned long nr) diff -Nru a/mm/memory.c b/mm/memory.c --- a/mm/memory.c Thu Mar 7 18:17:44 2002 +++ b/mm/memory.c Thu Mar 7 18:17:44 2002 @@ -1032,31 +1032,35 @@ return -1; } -static void vmtruncate_list(struct vm_area_struct *mpnt, unsigned long pgoff) +static void vmtruncate_list(list_t *head, unsigned long pgoff) { - do { - unsigned long start = mpnt->vm_start; - unsigned long end = mpnt->vm_end; - unsigned long len = end - start; - unsigned long diff; + unsigned long start, end, len, diff; + struct vm_area_struct *vma; + list_t *curr; + + list_for_each(curr, head) { + vma = list_entry(curr, struct vm_area_struct, shared); + start = vma->vm_start; + end = vma->vm_end; + len = end - start; /* mapping wholly truncated? */ - if (mpnt->vm_pgoff >= pgoff) { - zap_page_range(mpnt, start, len); + if (vma->vm_pgoff >= pgoff) { + zap_page_range(vma, start, len); continue; } /* mapping wholly unaffected? */ len = len >> PAGE_SHIFT; - diff = pgoff - mpnt->vm_pgoff; + diff = pgoff - vma->vm_pgoff; if (diff >= len) continue; /* Ok, partially affected.. */ start += diff << PAGE_SHIFT; len = (len - diff) << PAGE_SHIFT; - zap_page_range(mpnt, start, len); - } while ((mpnt = mpnt->vm_next_share) != NULL); + zap_page_range(vma, start, len); + } } /* @@ -1077,14 +1081,14 @@ goto do_expand; inode->i_size = offset; spin_lock(&mapping->i_shared_lock); - if (!mapping->i_mmap && !mapping->i_mmap_shared) + if (list_empty(&mapping->i_mmap) && list_empty(&mapping->i_mmap_shared)) goto out_unlock; pgoff = (offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - if (mapping->i_mmap != NULL) - vmtruncate_list(mapping->i_mmap, pgoff); - if (mapping->i_mmap_shared != NULL) - vmtruncate_list(mapping->i_mmap_shared, pgoff); + if (!list_empty(&mapping->i_mmap)) + vmtruncate_list(&mapping->i_mmap, pgoff); + if (!list_empty(&mapping->i_mmap_shared)) + vmtruncate_list(&mapping->i_mmap_shared, pgoff); out_unlock: spin_unlock(&mapping->i_shared_lock); @@ -1480,8 +1484,9 @@ /* * Map a vmalloc()-space virtual address to the physical page. */ -struct page * vmalloc_to_page(unsigned long addr) +struct page * vmalloc_to_page(void * vmalloc_addr) { + unsigned long addr = (unsigned long) vmalloc_addr; struct page *page = NULL; pgd_t *pgd = pgd_offset_k(addr); pmd_t *pmd; diff -Nru a/mm/mmap.c b/mm/mmap.c --- a/mm/mmap.c Thu Mar 7 18:17:38 2002 +++ b/mm/mmap.c Thu Mar 7 18:17:38 2002 @@ -101,9 +101,7 @@ struct inode *inode = file->f_dentry->d_inode; if (vma->vm_flags & VM_DENYWRITE) atomic_inc(&inode->i_writecount); - if(vma->vm_next_share) - vma->vm_next_share->vm_pprev_share = vma->vm_pprev_share; - *vma->vm_pprev_share = vma->vm_next_share; + list_del_init(&vma->shared); } } @@ -308,20 +306,14 @@ if (file) { struct inode * inode = file->f_dentry->d_inode; struct address_space *mapping = inode->i_mapping; - struct vm_area_struct **head; if (vma->vm_flags & VM_DENYWRITE) atomic_dec(&inode->i_writecount); - head = &mapping->i_mmap; if (vma->vm_flags & VM_SHARED) - head = &mapping->i_mmap_shared; - - /* insert vma into inode's share list */ - if((vma->vm_next_share = *head) != NULL) - (*head)->vm_pprev_share = &vma->vm_next_share; - *head = vma; - vma->vm_pprev_share = head; + list_add_tail(&vma->shared, &mapping->i_mmap_shared); + else + list_add_tail(&vma->shared, &mapping->i_mmap); } } diff -Nru a/mm/page_alloc.c b/mm/page_alloc.c --- a/mm/page_alloc.c Thu Mar 7 18:17:38 2002 +++ b/mm/page_alloc.c Thu Mar 7 18:17:38 2002 @@ -811,7 +811,7 @@ for (i = 0; i < size; i++) { struct page *page = mem_map + offset + i; set_page_zone(page, nid * MAX_NR_ZONES + j); - init_page_count(page); + set_page_count(page, 0); __SetPageReserved(page); memlist_init(&page->list); if (j != ZONE_HIGHMEM) diff -Nru a/mm/shmem.c b/mm/shmem.c --- a/mm/shmem.c Thu Mar 7 18:17:43 2002 +++ b/mm/shmem.c Thu Mar 7 18:17:43 2002 @@ -904,7 +904,7 @@ if ((desc->error = shmem_getpage(inode, index, &page))) break; - if (mapping->i_mmap_shared != NULL) + if (!list_empty(&mapping->i_mmap_shared)) flush_dcache_page(page); /* diff -Nru a/mm/slab.c b/mm/slab.c --- a/mm/slab.c Thu Mar 7 18:17:46 2002 +++ b/mm/slab.c Thu Mar 7 18:17:46 2002 @@ -341,7 +341,9 @@ { 32, NULL, NULL}, #endif { 64, NULL, NULL}, + { 96, NULL, NULL}, { 128, NULL, NULL}, + { 192, NULL, NULL}, { 256, NULL, NULL}, { 512, NULL, NULL}, { 1024, NULL, NULL}, @@ -364,7 +366,9 @@ CN("size-32"), #endif CN("size-64"), + CN("size-96"), CN("size-128"), + CN("size-192"), CN("size-256"), CN("size-512"), CN("size-1024"), diff -Nru a/mm/swapfile.c b/mm/swapfile.c --- a/mm/swapfile.c Thu Mar 7 18:17:39 2002 +++ b/mm/swapfile.c Thu Mar 7 18:17:39 2002 @@ -786,6 +786,8 @@ swap_device_unlock(p); swap_list_unlock(); vfree(swap_map); + if (S_ISBLK(swap_file->f_dentry->d_inode->i_mode)) + bd_release(swap_file->f_dentry->d_inode->i_bdev); filp_close(swap_file, NULL); err = 0; @@ -807,43 +809,33 @@ len = sprintf(buf, "Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n"); for (i = 0 ; i < nr_swapfiles ; i++, ptr++) { - if ((ptr->flags & SWP_USED) && ptr->swap_map) { - char * path = d_path(ptr->swap_file->f_dentry, - ptr->swap_file->f_vfsmnt, - page, PAGE_SIZE); - int j, usedswap = 0; - for (j = 0; j < ptr->max; ++j) - switch (ptr->swap_map[j]) { - case SWAP_MAP_BAD: - case 0: - continue; - default: - usedswap++; - } - len += sprintf(buf + len, "%-39s %s\t%d\t%d\t%d\n", - path, - (ptr->flags & SWP_BLOCKDEV) ? - "partition" : "file\t", - ptr->pages << (PAGE_SHIFT - 10), - usedswap << (PAGE_SHIFT - 10), - ptr->prio); - } - } - free_page((unsigned long) page); - return len; -} + int j, usedswap; + struct file *file; + char *path; -int is_swap_partition(kdev_t dev) { - struct swap_info_struct *ptr = swap_info; - int i; + if (!(ptr->flags & SWP_USED) || !ptr->swap_map) + continue; - for (i = 0 ; i < nr_swapfiles ; i++, ptr++) { - if ((ptr->flags & SWP_USED) && - (ptr->flags & SWP_BLOCKDEV) && - (kdev_same(ptr->swap_file->f_dentry->d_inode->i_rdev, dev))) - return 1; + file = ptr->swap_file; + path = d_path(file->f_dentry, file->f_vfsmnt, page, PAGE_SIZE); + for (j = 0,usedswap = 0; j < ptr->max; ++j) + switch (ptr->swap_map[j]) { + case SWAP_MAP_BAD: + case 0: + continue; + default: + usedswap++; + } + len += sprintf(buf + len, "%-39s %s\t%d\t%d\t%d\n", + path, + S_ISBLK(file->f_dentry->d_inode->i_mode) ? + "partition" : "file\t", + ptr->pages << (PAGE_SHIFT - 10), + usedswap << (PAGE_SHIFT - 10), + ptr->prio); } - return 0; + free_page((unsigned long) page); + return len; } /* @@ -855,6 +847,7 @@ { struct swap_info_struct * p; char *name; + struct block_device *bdev = NULL; struct file *swap_file = NULL; struct address_space *mapping; unsigned int type; @@ -905,13 +898,21 @@ swap_file = filp_open(name, O_RDWR, 0); putname(name); error = PTR_ERR(swap_file); - if (IS_ERR(swap_file)) + if (IS_ERR(swap_file)) { + swap_file = NULL; goto bad_swap_2; + } p->swap_file = swap_file; error = -EINVAL; if (S_ISBLK(swap_file->f_dentry->d_inode->i_mode)) { + bdev = swap_file->f_dentry->d_inode->i_bdev; + error = bd_claim(bdev, sys_swapon); + if (error < 0) { + bdev = NULL; + goto bad_swap; + } error = set_blocksize(swap_file->f_dentry->d_inode->i_rdev, PAGE_SIZE); if (error < 0) @@ -1039,8 +1040,6 @@ swap_device_lock(p); p->max = maxpages; p->flags = SWP_ACTIVE; - if (S_ISBLK(swap_file->f_dentry->d_inode->i_mode)) - p->flags |= SWP_BLOCKDEV; p->pages = nr_good_pages; nr_swap_pages += nr_good_pages; total_swap_pages += nr_good_pages; @@ -1066,6 +1065,8 @@ error = 0; goto out; bad_swap: + if (bdev) + bd_release(bdev); bad_swap_2: swap_list_lock(); swap_map = p->swap_map; diff -Nru a/mm/vmalloc.c b/mm/vmalloc.c --- a/mm/vmalloc.c Thu Mar 7 18:17:37 2002 +++ b/mm/vmalloc.c Thu Mar 7 18:17:37 2002 @@ -6,6 +6,7 @@ * SMP-safe vmalloc/vfree/ioremap, Tigran Aivazian , May 2000 */ +#include #include #include #include @@ -163,6 +164,7 @@ ret = 0; } while (address && (address < end)); spin_unlock(&init_mm.page_table_lock); + flush_cache_all(); return ret; } @@ -273,6 +275,43 @@ if (count == 0) goto finished; *buf = *addr; + buf++; + addr++; + count--; + } while (--n > 0); + } +finished: + read_unlock(&vmlist_lock); + return buf - buf_start; +} + +long vwrite(char *buf, char *addr, unsigned long count) +{ + struct vm_struct *tmp; + char *vaddr, *buf_start = buf; + unsigned long n; + + /* Don't allow overflow */ + if ((unsigned long) addr + count < count) + count = -(unsigned long) addr; + + read_lock(&vmlist_lock); + for (tmp = vmlist; tmp; tmp = tmp->next) { + vaddr = (char *) tmp->addr; + if (addr >= vaddr + tmp->size - PAGE_SIZE) + continue; + while (addr < vaddr) { + if (count == 0) + goto finished; + buf++; + addr++; + count--; + } + n = vaddr + tmp->size - PAGE_SIZE - addr; + do { + if (count == 0) + goto finished; + *addr = *buf; buf++; addr++; count--; diff -Nru a/mm/vmscan.c b/mm/vmscan.c --- a/mm/vmscan.c Thu Mar 7 18:17:39 2002 +++ b/mm/vmscan.c Thu Mar 7 18:17:39 2002 @@ -578,7 +578,9 @@ return 0; shrink_dcache_memory(priority, gfp_mask); - shrink_icache_memory(priority, gfp_mask); + + /* After shrinking the dcache, get rid of unused inodes too .. */ + shrink_icache_memory(1, gfp_mask); #ifdef CONFIG_QUOTA shrink_dqcache_memory(DEF_PRIORITY, gfp_mask); #endif diff -Nru a/net/Makefile b/net/Makefile --- a/net/Makefile Thu Mar 7 18:17:43 2002 +++ b/net/Makefile Thu Mar 7 18:17:43 2002 @@ -1,7 +1,7 @@ # # Makefile for the linux networking. # -# 2 Sep 2000, Christoph Hellwig +# 2 Sep 2000, Christoph Hellwig # Rewritten to use lists instead of if-statements. # diff -Nru a/net/core/dev.c b/net/core/dev.c --- a/net/core/dev.c Thu Mar 7 18:17:45 2002 +++ b/net/core/dev.c Thu Mar 7 18:17:45 2002 @@ -445,7 +445,7 @@ /* Return value is changed to int to prevent illegal usage in future. - It is still legal to use to check for device existance. + It is still legal to use to check for device existence. User should understand, that the result returned by this function is meaningless, if it was not issued under rtnl semaphore. @@ -2110,7 +2110,8 @@ cmd == SIOCETHTOOL || cmd == SIOCGMIIPHY || cmd == SIOCGMIIREG || - cmd == SIOCSMIIREG) { + cmd == SIOCSMIIREG || + cmd == SIOCWANDEV) { if (dev->do_ioctl) { if (!netif_device_present(dev)) return -ENODEV; @@ -2276,8 +2277,9 @@ */ default: - if (cmd >= SIOCDEVPRIVATE && - cmd <= SIOCDEVPRIVATE + 15) { + if (cmd == SIOCWANDEV || + (cmd >= SIOCDEVPRIVATE && + cmd <= SIOCDEVPRIVATE + 15)) { dev_load(ifr.ifr_name); dev_probe_lock(); rtnl_lock(); diff -Nru a/net/irda/af_irda.c b/net/irda/af_irda.c --- a/net/irda/af_irda.c Thu Mar 7 18:17:41 2002 +++ b/net/irda/af_irda.c Thu Mar 7 18:17:41 2002 @@ -1022,28 +1022,26 @@ /* Now the loop */ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) return -EINPROGRESS; - - cli(); /* To avoid races on the sleep */ - - /* A Connect Ack with Choke or timeout or failed routing will go to - * closed. */ + + /* Here, there is a race condition : the state may change between + * our test and the sleep, via irda_connect_confirm(). + * The way to workaround that is to sleep with a timeout, so that + * we don't sleep forever and check the state when waking up. + * 50ms is plenty good enough, because the LAP is already connected. + * Jean II */ while (sk->state == TCP_SYN_SENT) { - interruptible_sleep_on(sk->sleep); + interruptible_sleep_on_timeout(sk->sleep, HZ/20); if (signal_pending(current)) { - sti(); return -ERESTARTSYS; } } if (sk->state != TCP_ESTABLISHED) { - sti(); sock->state = SS_UNCONNECTED; return sock_error(sk); /* Always set at this point */ } sock->state = SS_CONNECTED; - - sti(); /* At this point, IrLMP has assigned our source address */ self->saddr = irttp_get_saddr(self->tsap); diff -Nru a/net/irda/irda_device.c b/net/irda/irda_device.c --- a/net/irda/irda_device.c Thu Mar 7 18:17:44 2002 +++ b/net/irda/irda_device.c Thu Mar 7 18:17:44 2002 @@ -598,7 +598,7 @@ disable_dma(channel); clear_dma_ff(channel); set_dma_mode(channel, mode); - set_dma_addr(channel, virt_to_bus(buffer)); + set_dma_addr(channel, isa_virt_to_bus(buffer)); set_dma_count(channel, count); enable_dma(channel); diff -Nru a/net/irda/irlap.c b/net/irda/irlap.c --- a/net/irda/irlap.c Thu Mar 7 18:17:42 2002 +++ b/net/irda/irlap.c Thu Mar 7 18:17:42 2002 @@ -131,7 +131,7 @@ /* FIXME: should we get our own field? */ dev->atalk_ptr = self; - irlap_next_state(self, LAP_OFFLINE); + self->state = LAP_OFFLINE; /* Initialize transmit queue */ skb_queue_head_init(&self->txq); @@ -155,7 +155,7 @@ self->N3 = 3; /* # connections attemts to try before giving up */ - irlap_next_state(self, LAP_NDM); + self->state = LAP_NDM; hashbin_insert(irlap, (irda_queue_t *) self, self->saddr, NULL); @@ -346,25 +346,21 @@ else skb->data[1] = I_FRAME; + /* Add at the end of the queue (keep ordering) - Jean II */ + skb_queue_tail(&self->txq, skb); + /* * Send event if this frame only if we are in the right state * FIXME: udata should be sent first! (skb_queue_head?) */ if ((self->state == LAP_XMIT_P) || (self->state == LAP_XMIT_S)) { - /* - * Check if the transmit queue contains some unsent frames, - * and if so, make sure they are sent first - */ - if (!skb_queue_empty(&self->txq)) { - skb_queue_tail(&self->txq, skb); - skb = skb_dequeue(&self->txq); - - ASSERT(skb != NULL, return;); - } - irlap_do_event(self, SEND_I_CMD, skb, NULL); - kfree_skb(skb); - } else - skb_queue_tail(&self->txq, skb); + /* If we are not already processing the Tx queue, trigger + * transmission immediately - Jean II */ + if((skb_queue_len(&self->txq) <= 1) && (!self->local_busy)) + irlap_do_event(self, DATA_REQUEST, skb, NULL); + /* Otherwise, the packets will be sent normally at the + * next pf-poll - Jean II */ + } } /* @@ -1013,6 +1009,7 @@ self->window_size = self->qos_tx.window_size.value; self->window = self->qos_tx.window_size.value; +#ifdef CONFIG_IRDA_DYNAMIC_WINDOW /* * Calculate how many bytes it is possible to transmit before the * link must be turned around @@ -1020,6 +1017,8 @@ self->line_capacity = irlap_max_line_capacity(self->qos_tx.baud_rate.value, self->qos_tx.max_turn_time.value); + self->bytes_left = self->line_capacity; +#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ /* @@ -1080,24 +1079,6 @@ self->N2 = self->qos_tx.link_disc_time.value * 1000 / self->qos_rx.max_turn_time.value; IRDA_DEBUG(4, "Setting N2 = %d\n", self->N2); -} - -/* - * Function irlap_set_local_busy (self, status) - * - * - * - */ -void irlap_set_local_busy(struct irlap_cb *self, int status) -{ - IRDA_DEBUG(0, __FUNCTION__ "()\n"); - - self->local_busy = status; - - if (status) - IRDA_DEBUG(0, __FUNCTION__ "(), local busy ON\n"); - else - IRDA_DEBUG(0, __FUNCTION__ "(), local busy OFF\n"); } #ifdef CONFIG_PROC_FS diff -Nru a/net/irda/irlap_event.c b/net/irda/irlap_event.c --- a/net/irda/irlap_event.c Thu Mar 7 18:17:37 2002 +++ b/net/irda/irlap_event.c Thu Mar 7 18:17:37 2002 @@ -252,6 +252,9 @@ * that will change the state away form XMIT */ if (skb_queue_len(&self->txq)) { + /* Prevent race conditions with irlap_data_request() */ + self->local_busy = TRUE; + /* Try to send away all queued data frames */ while ((skb = skb_dequeue(&self->txq)) != NULL) { ret = (*state[self->state])(self, SEND_I_CMD, @@ -260,6 +263,8 @@ if (ret == -EPROTO) break; /* Try again later! */ } + /* Finished transmitting */ + self->local_busy = FALSE; } else if (self->disconnect_pending) { self->disconnect_pending = FALSE; @@ -282,25 +287,15 @@ * Switches state and provides debug information * */ -void irlap_next_state(struct irlap_cb *self, IRLAP_STATE state) +static inline void irlap_next_state(struct irlap_cb *self, IRLAP_STATE state) { + /* if (!self || self->magic != LAP_MAGIC) return; IRDA_DEBUG(4, "next LAP state = %s\n", irlap_state[state]); - + */ self->state = state; - -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - /* - * If we are swithing away from a XMIT state then we are allowed to - * transmit a maximum number of bytes again when we enter the XMIT - * state again. Since its possible to "switch" from XMIT to XMIT, - * we cannot do this when swithing into the XMIT state :-) - */ - if ((state != LAP_XMIT_P) && (state != LAP_XMIT_S)) - self->bytes_left = self->line_capacity; -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ } /* @@ -1017,6 +1012,12 @@ IRDA_DEBUG(3, __FUNCTION__ "(), POLL_TIMER_EXPIRED (%ld)\n", jiffies); irlap_send_rr_frame(self, CMD_FRAME); + /* Return to NRM properly - Jean II */ + self->window = self->window_size; +#ifdef CONFIG_IRDA_DYNAMIC_WINDOW + /* Allowed to transmit a maximum number of bytes again. */ + self->bytes_left = self->line_capacity; +#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ irlap_start_final_timer(self, self->final_timeout); irlap_next_state(self, LAP_NRM_P); break; @@ -1029,6 +1030,10 @@ self->retry_count = 0; irlap_next_state(self, LAP_PCLOSE); break; + case DATA_REQUEST: + /* Nothing to do, irlap_do_event() will send the packet + * when we return... - Jean II */ + break; default: IRDA_DEBUG(0, __FUNCTION__ "(), Unknown event %s\n", irlap_event[event]); @@ -1645,12 +1650,17 @@ */ if (skb->len > self->bytes_left) { skb_queue_head(&self->txq, skb_get(skb)); + /* * Switch to NRM_S, this is only possible * when we are in secondary mode, since we * must be sure that we don't miss any RR * frames */ + self->window = self->window_size; + self->bytes_left = self->line_capacity; + irlap_start_wd_timer(self, self->wd_timeout); + irlap_next_state(self, LAP_NRM_S); return -EPROTO; /* Try again later */ @@ -1687,6 +1697,10 @@ irlap_flush_all_queues(self); irlap_start_wd_timer(self, self->wd_timeout); irlap_next_state(self, LAP_SCLOSE); + break; + case DATA_REQUEST: + /* Nothing to do, irlap_do_event() will send the packet + * when we return... - Jean II */ break; default: IRDA_DEBUG(2, __FUNCTION__ "(), Unknown event %s\n", diff -Nru a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c --- a/net/irda/irlap_frame.c Thu Mar 7 18:17:40 2002 +++ b/net/irda/irlap_frame.c Thu Mar 7 18:17:40 2002 @@ -768,6 +768,9 @@ { struct sk_buff *tx_skb; + /* Stop P timer */ + del_timer(&self->poll_timer); + /* Is this reliable or unreliable data? */ if (skb->data[1] == I_FRAME) { @@ -793,23 +796,15 @@ * skb, since retransmitted need to set or clear the poll * bit depending on when they are sent. */ - /* Stop P timer */ - del_timer(&self->poll_timer); - tx_skb->data[1] |= PF_BIT; self->vs = (self->vs + 1) % 8; self->ack_required = FALSE; - self->window = self->window_size; - - irlap_start_final_timer(self, self->final_timeout); irlap_send_i_frame(self, tx_skb, CMD_FRAME); } else { IRDA_DEBUG(4, __FUNCTION__ "(), sending unreliable frame\n"); - del_timer(&self->poll_timer); - if (self->ack_required) { irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); irlap_send_rr_frame(self, CMD_FRAME); @@ -818,9 +813,15 @@ skb->data[1] |= PF_BIT; irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); } - self->window = self->window_size; - irlap_start_final_timer(self, self->final_timeout); } + + self->window = self->window_size; +#ifdef CONFIG_IRDA_DYNAMIC_WINDOW + /* We are allowed to transmit a maximum number of bytes again. */ + self->bytes_left = self->line_capacity; +#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ + + irlap_start_final_timer(self, self->final_timeout); } /* @@ -858,11 +859,8 @@ tx_skb->data[1] |= PF_BIT; self->vs = (self->vs + 1) % 8; - self->window = self->window_size; self->ack_required = FALSE; - irlap_start_wd_timer(self, self->wd_timeout); - irlap_send_i_frame(self, tx_skb, RSP_FRAME); } else { if (self->ack_required) { @@ -873,10 +871,15 @@ skb->data[1] |= PF_BIT; irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME); } - self->window = self->window_size; - - irlap_start_wd_timer(self, self->wd_timeout); } + + self->window = self->window_size; +#ifdef CONFIG_IRDA_DYNAMIC_WINDOW + /* We are allowed to transmit a maximum number of bytes again. */ + self->bytes_left = self->line_capacity; +#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ + + irlap_start_wd_timer(self, self->wd_timeout); } /* diff -Nru a/net/irda/irnet/irnet.h b/net/irda/irnet/irnet.h --- a/net/irda/irnet/irnet.h Thu Mar 7 18:17:45 2002 +++ b/net/irda/irnet/irnet.h Thu Mar 7 18:17:45 2002 @@ -206,6 +206,11 @@ * just after clearing it. *blush*. * o Use newly created irttp_listen() to fix potential crash when LAP * destroyed before irnet module removed. + * + * v10 - 4.3.2 - Jean II + * o When receiving a disconnect indication, don't reenable the + * PPP Tx queue, this will trigger a reconnect. Instead, close + * the channel, which will kill pppd... */ /***************************** INCLUDES *****************************/ diff -Nru a/net/irda/irnet/irnet_irda.c b/net/irda/irnet/irnet_irda.c --- a/net/irda/irnet/irnet_irda.c Thu Mar 7 18:17:37 2002 +++ b/net/irda/irnet/irnet_irda.c Thu Mar 7 18:17:37 2002 @@ -1122,9 +1122,10 @@ irttp_close_tsap(self->tsap); self->tsap = NULL; - /* Flush (drain) ppp_generic Tx queue (most often we have blocked it) */ + /* Cleanup & close the PPP channel, which will kill pppd and the rest */ if(self->ppp_open) - ppp_output_wakeup(&self->chan); + ppp_unregister_channel(&self->chan); + self->ppp_open = 0; } /* Cleanup the socket in case we want to reconnect */ self->stsap_sel = 0; diff -Nru a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c --- a/net/sunrpc/clnt.c Thu Mar 7 18:17:42 2002 +++ b/net/sunrpc/clnt.c Thu Mar 7 18:17:42 2002 @@ -78,10 +78,6 @@ dprintk("RPC: creating %s client for %s (xprt %p)\n", program->name, servname, xprt); -#ifdef RPC_DEBUG - rpc_register_sysctl(); -#endif - if (!xprt) goto out; if (vers >= program->nrvers || !(version = program->version[vers])) @@ -103,7 +99,7 @@ clnt->cl_vers = version->number; clnt->cl_prot = xprt->prot; clnt->cl_stats = program->stats; - clnt->cl_bindwait = RPC_INIT_WAITQ("bindwait"); + INIT_RPC_WAITQ(&clnt->cl_bindwait, "bindwait"); if (!clnt->cl_port) clnt->cl_autobind = 1; diff -Nru a/net/sunrpc/sched.c b/net/sunrpc/sched.c --- a/net/sunrpc/sched.c Thu Mar 7 18:17:36 2002 +++ b/net/sunrpc/sched.c Thu Mar 7 18:17:36 2002 @@ -41,23 +41,23 @@ * handler, or while executing another RPC task, it is put on * schedq, and rpciod is woken up. */ -static struct rpc_wait_queue schedq = RPC_INIT_WAITQ("schedq"); +static RPC_WAITQ(schedq, "schedq"); /* * RPC tasks that create another task (e.g. for contacting the portmapper) * will wait on this queue for their child's completion */ -static struct rpc_wait_queue childq = RPC_INIT_WAITQ("childq"); +static RPC_WAITQ(childq, "childq"); /* * RPC tasks sit here while waiting for conditions to improve. */ -static struct rpc_wait_queue delay_queue = RPC_INIT_WAITQ("delayq"); +static RPC_WAITQ(delay_queue, "delayq"); /* * All RPC tasks are linked into this list */ -static struct rpc_task * all_tasks; +static LIST_HEAD(all_tasks); /* * rpciod-related stuff @@ -194,9 +194,9 @@ return -EWOULDBLOCK; } if (RPC_IS_SWAPPER(task)) - rpc_insert_list(&queue->task, task); + list_add(&task->tk_list, &queue->tasks); else - rpc_append_list(&queue->task, task); + list_add_tail(&task->tk_list, &queue->tasks); task->tk_rpcwait = queue; dprintk("RPC: %4d added to queue %p \"%s\"\n", @@ -228,7 +228,7 @@ if (!queue) return; - rpc_remove_list(&queue->task, task); + list_del(&task->tk_list); task->tk_rpcwait = NULL; dprintk("RPC: %4d removed from queue %p \"%s\"\n", @@ -450,11 +450,11 @@ struct rpc_task * rpc_wake_up_next(struct rpc_wait_queue *queue) { - struct rpc_task *task; + struct rpc_task *task = NULL; dprintk("RPC: wake_up_next(%p \"%s\")\n", queue, rpc_qname(queue)); spin_lock_bh(&rpc_queue_lock); - if ((task = queue->task) != 0) + task_for_first(task, &queue->tasks) __rpc_wake_up_task(task); spin_unlock_bh(&rpc_queue_lock); @@ -470,9 +470,12 @@ void rpc_wake_up(struct rpc_wait_queue *queue) { + struct rpc_task *task; + spin_lock_bh(&rpc_queue_lock); - while (queue->task) - __rpc_wake_up_task(queue->task); + while (!list_empty(&queue->tasks)) + task_for_first(task, &queue->tasks) + __rpc_wake_up_task(task); spin_unlock_bh(&rpc_queue_lock); } @@ -486,12 +489,14 @@ void rpc_wake_up_status(struct rpc_wait_queue *queue, int status) { - struct rpc_task *task; + struct rpc_task *task; spin_lock_bh(&rpc_queue_lock); - while ((task = queue->task) != NULL) { - task->tk_status = status; - __rpc_wake_up_task(task); + while (!list_empty(&queue->tasks)) { + task_for_first(task, &queue->tasks) { + task->tk_status = status; + __rpc_wake_up_task(task); + } } spin_unlock_bh(&rpc_queue_lock); } @@ -703,23 +708,25 @@ rpciod_tcp_dispatcher(); spin_lock_bh(&rpc_queue_lock); - if (!(task = schedq.task)) { - spin_unlock_bh(&rpc_queue_lock); - break; - } - if (task->tk_lock) { - spin_unlock_bh(&rpc_queue_lock); - printk(KERN_ERR "RPC: Locked task was scheduled !!!!\n"); + + task_for_first(task, &schedq.tasks) { + if (task->tk_lock) { + spin_unlock_bh(&rpc_queue_lock); + printk(KERN_ERR "RPC: Locked task was scheduled !!!!\n"); #ifdef RPC_DEBUG - rpc_debug = ~0; - rpc_show_tasks(); + rpc_debug = ~0; + rpc_show_tasks(); #endif + break; + } + __rpc_remove_wait_queue(task); + spin_unlock_bh(&rpc_queue_lock); + + __rpc_execute(task); + } else { + spin_unlock_bh(&rpc_queue_lock); break; } - __rpc_remove_wait_queue(task); - spin_unlock_bh(&rpc_queue_lock); - - __rpc_execute(task); if (++count >= 200 || need_resched()) { count = 0; @@ -814,11 +821,7 @@ /* Add to global list of all tasks */ spin_lock(&rpc_sched_lock); - task->tk_next_task = all_tasks; - task->tk_prev_task = NULL; - if (all_tasks) - all_tasks->tk_prev_task = task; - all_tasks = task; + list_add(&task->tk_task, &all_tasks); spin_unlock(&rpc_sched_lock); if (clnt) @@ -877,8 +880,6 @@ void rpc_release_task(struct rpc_task *task) { - struct rpc_task *next, *prev; - dprintk("RPC: %4d release task\n", task->tk_pid); #ifdef RPC_DEBUG @@ -892,15 +893,7 @@ /* Remove from global task list */ spin_lock(&rpc_sched_lock); - prev = task->tk_prev_task; - next = task->tk_next_task; - if (next) - next->tk_prev_task = prev; - if (prev) - prev->tk_next_task = next; - else - all_tasks = next; - task->tk_next_task = task->tk_prev_task = NULL; + list_del(&task->tk_task); spin_unlock(&rpc_sched_lock); /* Protect the execution below. */ @@ -954,14 +947,13 @@ rpc_find_parent(struct rpc_task *child) { struct rpc_task *task, *parent; + struct list_head *le; parent = (struct rpc_task *) child->tk_calldata; - if ((task = childq.task) != NULL) { - do { - if (task == parent) - return parent; - } while ((task = task->tk_next) != childq.task); - } + task_for_each(task, le, &childq.tasks) + if (task == parent) + return parent; + return NULL; } @@ -1015,7 +1007,8 @@ void rpc_killall_tasks(struct rpc_clnt *clnt) { - struct rpc_task **q, *rovr; + struct rpc_task *rovr; + struct list_head *le; dprintk("RPC: killing all tasks for client %p\n", clnt); @@ -1023,13 +1016,12 @@ * Spin lock all_tasks to prevent changes... */ spin_lock(&rpc_sched_lock); - for (q = &all_tasks; (rovr = *q); q = &rovr->tk_next_task) { + alltask_for_each(rovr, le, &all_tasks) if (!clnt || rovr->tk_client == clnt) { rovr->tk_flags |= RPC_TASK_KILLED; rpc_exit(rovr, -EIO); rpc_wake_up_task(rovr); } - } spin_unlock(&rpc_sched_lock); } @@ -1038,7 +1030,7 @@ static inline int rpciod_task_pending(void) { - return schedq.task != NULL || xprt_tcp_pending(); + return !list_empty(&schedq.tasks) || xprt_tcp_pending(); } @@ -1090,7 +1082,7 @@ } dprintk("RPC: rpciod shutdown commences\n"); - if (all_tasks) { + if (!list_empty(&all_tasks)) { printk(KERN_ERR "rpciod: active tasks at shutdown?!\n"); rpciod_killall(); } @@ -1108,11 +1100,11 @@ { unsigned long flags; - while (all_tasks) { + while (!list_empty(&all_tasks)) { clear_thread_flag(TIF_SIGPENDING); rpc_killall_tasks(NULL); __rpc_schedule(); - if (all_tasks) { + if (!list_empty(&all_tasks)) { dprintk("rpciod_killall: waiting for tasks to exit\n"); yield(); } @@ -1207,25 +1199,23 @@ #ifdef RPC_DEBUG void rpc_show_tasks(void) { - struct rpc_task *t = all_tasks, *next; + struct list_head *le; + struct rpc_task *t; spin_lock(&rpc_sched_lock); - t = all_tasks; - if (!t) { + if (list_empty(&all_tasks)) { spin_unlock(&rpc_sched_lock); return; } printk("-pid- proc flgs status -client- -prog- --rqstp- -timeout " "-rpcwait -action- --exit--\n"); - for (; t; t = next) { - next = t->tk_next_task; + alltask_for_each(t, le, &all_tasks) printk("%05d %04d %04x %06d %8p %6d %8p %08ld %8s %8p %8p\n", t->tk_pid, t->tk_msg.rpc_proc, t->tk_flags, t->tk_status, t->tk_client, t->tk_client->cl_prog, t->tk_rqstp, t->tk_timeout, t->tk_rpcwait ? rpc_qname(t->tk_rpcwait) : " ", t->tk_action, t->tk_exit); - } spin_unlock(&rpc_sched_lock); } #endif diff -Nru a/net/sunrpc/stats.c b/net/sunrpc/stats.c --- a/net/sunrpc/stats.c Thu Mar 7 18:17:44 2002 +++ b/net/sunrpc/stats.c Thu Mar 7 18:17:44 2002 @@ -15,6 +15,7 @@ #define __NO_VERSION__ #include +#include #include #include #include @@ -181,10 +182,9 @@ } } -#ifdef MODULE -int -init_module(void) +static int __init +init_sunrpc(void) { #ifdef RPC_DEBUG rpc_register_sysctl(); @@ -193,13 +193,14 @@ return 0; } -void -cleanup_module(void) +static void __exit +cleanup_sunrpc(void) { #ifdef RPC_DEBUG rpc_unregister_sysctl(); #endif rpc_proc_exit(); } -#endif MODULE_LICENSE("GPL"); +module_init(init_sunrpc); +module_exit(cleanup_sunrpc); diff -Nru a/net/sunrpc/svc.c b/net/sunrpc/svc.c --- a/net/sunrpc/svc.c Thu Mar 7 18:17:46 2002 +++ b/net/sunrpc/svc.c Thu Mar 7 18:17:46 2002 @@ -31,10 +31,6 @@ { struct svc_serv *serv; -#ifdef RPC_DEBUG - rpc_register_sysctl(); -#endif - if (!(serv = (struct svc_serv *) kmalloc(sizeof(*serv), GFP_KERNEL))) return NULL; @@ -44,6 +40,10 @@ serv->sv_stats = prog->pg_stats; serv->sv_bufsz = bufsize? bufsize : 4096; serv->sv_xdrsize = xdrsize; + INIT_LIST_HEAD(&serv->sv_threads); + INIT_LIST_HEAD(&serv->sv_sockets); + INIT_LIST_HEAD(&serv->sv_tempsocks); + INIT_LIST_HEAD(&serv->sv_permsocks); spin_lock_init(&serv->sv_lock); serv->sv_name = prog->pg_name; @@ -67,13 +67,25 @@ serv->sv_nrthreads); if (serv->sv_nrthreads) { - if (--(serv->sv_nrthreads) != 0) + if (--(serv->sv_nrthreads) != 0) { + svc_sock_update_bufs(serv); return; + } } else printk("svc_destroy: no threads for serv=%p!\n", serv); - while ((svsk = serv->sv_allsocks) != NULL) + while (!list_empty(&serv->sv_tempsocks)) { + svsk = list_entry(serv->sv_tempsocks.next, + struct svc_sock, + sk_list); + svc_delete_socket(svsk); + } + while (!list_empty(&serv->sv_permsocks)) { + svsk = list_entry(serv->sv_permsocks.next, + struct svc_sock, + sk_list); svc_delete_socket(svsk); + } /* Unregister service with the portmapper */ svc_register(serv, 0, 0); @@ -138,6 +150,7 @@ error = kernel_thread((int (*)(void *)) func, rqstp, 0); if (error < 0) goto out_thread; + svc_sock_update_bufs(serv); error = 0; out: return error; @@ -295,6 +308,12 @@ /* Initialize storage for argp and resp */ memset(rqstp->rq_argp, 0, procp->pc_argsize); memset(rqstp->rq_resp, 0, procp->pc_ressize); + + /* un-reserve some of the out-queue now that we have a + * better idea of reply size + */ + if (procp->pc_xdrressize) + svc_reserve(rqstp, procp->pc_xdrressize<<2); /* Call the function that processes the request. */ if (!versp->vs_dispatch) { diff -Nru a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c --- a/net/sunrpc/svcsock.c Thu Mar 7 18:17:38 2002 +++ b/net/sunrpc/svcsock.c Thu Mar 7 18:17:38 2002 @@ -45,10 +45,19 @@ /* SMP locking strategy: * - * svc_sock->sk_lock and svc_serv->sv_lock protect their - * respective structures. + * svc_serv->sv_lock protects most stuff for that service. + * + * Some flags can be set to certain values at any time + * providing that certain rules are followed: + * + * SK_BUSY can be set to 0 at any time. + * svc_sock_enqueue must be called afterwards + * SK_CONN, SK_DATA, can be set or cleared at any time. + * after a set, svc_sock_enqueue must be called. + * after a clear, the socket must be read/accepted + * if this succeeds, it must be set again. + * SK_CLOSE can set at any time. It is never cleared. * - * Antideadlock ordering is sk_lock --> sv_lock. */ #define RPCDBG_FACILITY RPCDBG_SVCSOCK @@ -63,11 +72,14 @@ /* * Queue up an idle server thread. Must have serv->sv_lock held. + * Note: this is really a stack rather than a queue, so that we only + * use as many different threads as we need, and the rest don't polute + * the cache. */ static inline void svc_serv_enqueue(struct svc_serv *serv, struct svc_rqst *rqstp) { - rpc_append_list(&serv->sv_threads, rqstp); + list_add(&rqstp->rq_list, &serv->sv_threads); } /* @@ -76,7 +88,7 @@ static inline void svc_serv_dequeue(struct svc_serv *serv, struct svc_rqst *rqstp) { - rpc_remove_list(&serv->sv_threads, rqstp); + list_del(&rqstp->rq_list); } /* @@ -99,7 +111,6 @@ * Queue up a socket with data pending. If there are idle nfsd * processes, wake 'em up. * - * This must be called with svsk->sk_lock held. */ static void svc_sock_enqueue(struct svc_sock *svsk) @@ -107,26 +118,44 @@ struct svc_serv *serv = svsk->sk_server; struct svc_rqst *rqstp; - /* NOTE: Local BH is already disabled by our caller. */ - spin_lock(&serv->sv_lock); + if (!(svsk->sk_flags & + ( (1<sv_lock); - if (serv->sv_threads && serv->sv_sockets) + if (!list_empty(&serv->sv_threads) && + !list_empty(&serv->sv_sockets)) printk(KERN_ERR "svc_sock_enqueue: threads and sockets both waiting??\n"); - if (svsk->sk_busy) { + if (test_bit(SK_BUSY, &svsk->sk_flags)) { /* Don't enqueue socket while daemon is receiving */ dprintk("svc: socket %p busy, not enqueued\n", svsk->sk_sk); goto out_unlock; } + if (((svsk->sk_reserved + serv->sv_bufsz)*2 + > sock_wspace(svsk->sk_sk)) + && !test_bit(SK_CLOSE, &svsk->sk_flags) + && !test_bit(SK_CONN, &svsk->sk_flags)) { + /* Don't enqueue while not enough space for reply */ + dprintk("svc: socket %p no space, %d > %ld, not enqueued\n", + svsk->sk_sk, svsk->sk_reserved+serv->sv_bufsz, + sock_wspace(svsk->sk_sk)); + goto out_unlock; + } + /* Mark socket as busy. It will remain in this state until the * server has processed all pending data and put the socket back * on the idle list. */ - svsk->sk_busy = 1; + set_bit(SK_BUSY, &svsk->sk_flags); - if ((rqstp = serv->sv_threads) != NULL) { + if (!list_empty(&serv->sv_threads)) { + rqstp = list_entry(serv->sv_threads.next, + struct svc_rqst, + rq_list); dprintk("svc: socket %p served by daemon %p\n", svsk->sk_sk, rqstp); svc_serv_dequeue(serv, rqstp); @@ -136,15 +165,17 @@ rqstp, rqstp->rq_sock); rqstp->rq_sock = svsk; svsk->sk_inuse++; + rqstp->rq_reserved = serv->sv_bufsz; + svsk->sk_reserved += rqstp->rq_reserved; wake_up(&rqstp->rq_wait); } else { dprintk("svc: socket %p put into queue\n", svsk->sk_sk); - rpc_append_list(&serv->sv_sockets, svsk); - svsk->sk_qued = 1; + list_add_tail(&svsk->sk_ready, &serv->sv_sockets); + set_bit(SK_QUED, &svsk->sk_flags); } out_unlock: - spin_unlock(&serv->sv_lock); + spin_unlock_bh(&serv->sv_lock); } /* @@ -155,71 +186,69 @@ { struct svc_sock *svsk; - if ((svsk = serv->sv_sockets) != NULL) - rpc_remove_list(&serv->sv_sockets, svsk); + if (list_empty(&serv->sv_sockets)) + return NULL; - if (svsk) { - dprintk("svc: socket %p dequeued, inuse=%d\n", - svsk->sk_sk, svsk->sk_inuse); - svsk->sk_qued = 0; - } + svsk = list_entry(serv->sv_sockets.next, + struct svc_sock, sk_ready); + list_del(&svsk->sk_ready); + + dprintk("svc: socket %p dequeued, inuse=%d\n", + svsk->sk_sk, svsk->sk_inuse); + clear_bit(SK_QUED, &svsk->sk_flags); return svsk; } /* - * Having read count bytes from a socket, check whether it + * Having read something from a socket, check whether it * needs to be re-enqueued. + * Note: SK_DATA only gets cleared when a read-attempt finds + * no (or insufficient) data. */ static inline void -svc_sock_received(struct svc_sock *svsk, int count) +svc_sock_received(struct svc_sock *svsk) { - spin_lock_bh(&svsk->sk_lock); - if ((svsk->sk_data -= count) < 0) { - printk(KERN_NOTICE "svc: sk_data negative!\n"); - svsk->sk_data = 0; - } - svsk->sk_rqstp = NULL; /* XXX */ - svsk->sk_busy = 0; - if (svsk->sk_conn || svsk->sk_data || svsk->sk_close) { - dprintk("svc: socket %p re-enqueued after receive\n", - svsk->sk_sk); - svc_sock_enqueue(svsk); - } - spin_unlock_bh(&svsk->sk_lock); + clear_bit(SK_BUSY, &svsk->sk_flags); + svc_sock_enqueue(svsk); } -/* - * Dequeue a new connection. + +/** + * svc_reserve - change the space reserved for the reply to a request. + * @rqstp: The request in question + * @space: new max space to reserve + * + * Each request reserves some space on the output queue of the socket + * to make sure the reply fits. This function reduces that reserved + * space to be the amount of space used already, plus @space. + * */ -static inline void -svc_sock_accepted(struct svc_sock *svsk) +void svc_reserve(struct svc_rqst *rqstp, int space) { - spin_lock_bh(&svsk->sk_lock); - svsk->sk_busy = 0; - svsk->sk_conn--; - if (svsk->sk_conn || svsk->sk_data || svsk->sk_close) { - dprintk("svc: socket %p re-enqueued after accept\n", - svsk->sk_sk); - svc_sock_enqueue(svsk); - } - spin_unlock_bh(&svsk->sk_lock); + space += rqstp->rq_resbuf.len<<2; + + if (space < rqstp->rq_reserved) { + struct svc_sock *svsk = rqstp->rq_sock; + spin_lock_bh(&svsk->sk_server->sv_lock); + svsk->sk_reserved -= (rqstp->rq_reserved - space); + rqstp->rq_reserved = space; + spin_unlock_bh(&svsk->sk_server->sv_lock); + + svc_sock_enqueue(svsk); + } } /* * Release a socket after use. */ static inline void -svc_sock_release(struct svc_rqst *rqstp) +svc_sock_put(struct svc_sock *svsk) { - struct svc_sock *svsk = rqstp->rq_sock; - struct svc_serv *serv = svsk->sk_server; - - svc_release_skb(rqstp); - rqstp->rq_sock = NULL; + struct svc_serv *serv = svsk->sk_server; spin_lock_bh(&serv->sv_lock); - if (!--(svsk->sk_inuse) && svsk->sk_dead) { + if (!--(svsk->sk_inuse) && test_bit(SK_DEAD, &svsk->sk_flags)) { spin_unlock_bh(&serv->sv_lock); dprintk("svc: releasing dead socket\n"); sock_release(svsk->sk_sock); @@ -229,6 +258,31 @@ spin_unlock_bh(&serv->sv_lock); } +static void +svc_sock_release(struct svc_rqst *rqstp) +{ + struct svc_sock *svsk = rqstp->rq_sock; + + svc_release_skb(rqstp); + + /* Reset response buffer and release + * the reservation. + * But first, check that enough space was reserved + * for the reply, otherwise we have a bug! + */ + if ((rqstp->rq_resbuf.len<<2) > rqstp->rq_reserved) + printk(KERN_ERR "RPC request reserved %d but used %d\n", + rqstp->rq_reserved, + rqstp->rq_resbuf.len<<2); + + rqstp->rq_resbuf.buf = rqstp->rq_resbuf.base; + rqstp->rq_resbuf.len = 0; + svc_reserve(rqstp, 0); + rqstp->rq_sock = NULL; + + svc_sock_put(svsk); +} + /* * External function to wake up a server waiting for data */ @@ -238,7 +292,10 @@ struct svc_rqst *rqstp; spin_lock_bh(&serv->sv_lock); - if ((rqstp = serv->sv_threads) != NULL) { + if (!list_empty(&serv->sv_threads)) { + rqstp = list_entry(serv->sv_threads.next, + struct svc_rqst, + rq_list); dprintk("svc: daemon %p woken up.\n", rqstp); /* svc_serv_dequeue(serv, rqstp); @@ -271,7 +328,13 @@ msg.msg_control = NULL; msg.msg_controllen = 0; - msg.msg_flags = MSG_DONTWAIT; + /* This was MSG_DONTWAIT, but I now want it to wait. + * The only thing that it would wait for is memory and + * if we are fairly low on memory, then we aren't likely + * to make much progress anyway. + * sk->sndtimeo is set to 30seconds just in case. + */ + msg.msg_flags = 0; oldfs = get_fs(); set_fs(KERNEL_DS); len = sock_sendmsg(sock, &msg, buflen); @@ -341,6 +404,32 @@ } /* + * Set socket snd and rcv buffer lengths + */ +static inline void +svc_sock_setbufsize(struct socket *sock, unsigned int snd, unsigned int rcv) +{ +#if 0 + mm_segment_t oldfs; + oldfs = get_fs(); set_fs(KERNEL_DS); + sock_setsockopt(sock, SOL_SOCKET, SO_SNDBUF, + (char*)&snd, sizeof(snd)); + sock_setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + (char*)&rcv, sizeof(rcv)); +#else + /* sock_setsockopt limits use to sysctl_?mem_max, + * which isn't acceptable. Until that is made conditional + * on not having CAP_SYS_RESOURCE or similar, we go direct... + * DaveM said I could! + */ + lock_sock(sock->sk); + sock->sk->sndbuf = snd * 2; + sock->sk->rcvbuf = rcv * 2; + sock->sk->userlocks |= SOCK_SNDBUF_LOCK|SOCK_RCVBUF_LOCK; + release_sock(sock->sk); +#endif +} +/* * INET callback when data has been received on the socket. */ static void @@ -351,17 +440,36 @@ if (!svsk) goto out; dprintk("svc: socket %p(inet %p), count=%d, busy=%d\n", - svsk, sk, count, svsk->sk_busy); - spin_lock_bh(&svsk->sk_lock); - svsk->sk_data = 1; + svsk, sk, count, test_bit(SK_BUSY, &svsk->sk_flags)); + set_bit(SK_DATA, &svsk->sk_flags); svc_sock_enqueue(svsk); - spin_unlock_bh(&svsk->sk_lock); out: if (sk->sleep && waitqueue_active(sk->sleep)) wake_up_interruptible(sk->sleep); } /* + * INET callback when space is newly available on the socket. + */ +static void +svc_write_space(struct sock *sk) +{ + struct svc_sock *svsk = (struct svc_sock *)(sk->user_data); + + if (svsk) { + dprintk("svc: socket %p(inet %p), write_space busy=%d\n", + svsk, sk, test_bit(SK_BUSY, &svsk->sk_flags)); + svc_sock_enqueue(svsk); + } + + if (sk->sleep && waitqueue_active(sk->sleep)) { + printk(KERN_WARNING "RPC svc_write_space: some sleeping on %p\n", + svsk); + wake_up_interruptible(sk->sleep); + } +} + +/* * Receive a datagram from a UDP socket. */ static int @@ -373,20 +481,21 @@ u32 *data; int err, len; - svsk->sk_data = 0; + clear_bit(SK_DATA, &svsk->sk_flags); while ((skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err)) == NULL) { - svc_sock_received(svsk, 0); + svc_sock_received(svsk); if (err == -EAGAIN) return err; /* possibly an icmp error */ dprintk("svc: recvfrom returned error %d\n", -err); } + set_bit(SK_DATA, &svsk->sk_flags); /* there may be more data... */ /* Sorry. */ if (skb_is_nonlinear(skb)) { if (skb_linearize(skb, GFP_KERNEL) != 0) { kfree_skb(skb); - svc_sock_received(svsk, 0); + svc_sock_received(svsk); return 0; } } @@ -394,13 +503,11 @@ if (skb->ip_summed != CHECKSUM_UNNECESSARY) { if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) { skb_free_datagram(svsk->sk_sk, skb); - svc_sock_received(svsk, 0); + svc_sock_received(svsk); return 0; } } - /* There may be more data */ - svsk->sk_data = 1; len = skb->len - sizeof(struct udphdr); data = (u32 *) (skb->data + sizeof(struct udphdr)); @@ -422,7 +529,7 @@ /* One down, maybe more to go... */ svsk->sk_sk->stamp = skb->stamp; - svc_sock_received(svsk, 0); + svc_sock_received(svsk); return len; } @@ -445,9 +552,6 @@ if (error == -ECONNREFUSED) /* ICMP error on earlier request. */ error = svc_sendto(rqstp, bufp->iov, bufp->nriov); - else if (error == -EAGAIN) - /* Ignore and wait for re-xmit */ - error = 0; return error; } @@ -456,6 +560,7 @@ svc_udp_init(struct svc_sock *svsk) { svsk->sk_sk->data_ready = svc_udp_data_ready; + svsk->sk_sk->write_space = svc_write_space; svsk->sk_recvfrom = svc_udp_recvfrom; svsk->sk_sendto = svc_udp_sendto; @@ -482,10 +587,8 @@ printk("svc: socket %p: no user data\n", sk); goto out; } - spin_lock_bh(&svsk->sk_lock); - svsk->sk_conn++; + set_bit(SK_CONN, &svsk->sk_flags); svc_sock_enqueue(svsk); - spin_unlock_bh(&svsk->sk_lock); out: if (sk->sleep && waitqueue_active(sk->sleep)) wake_up_interruptible_all(sk->sleep); @@ -506,10 +609,8 @@ printk("svc: socket %p: no user data\n", sk); goto out; } - spin_lock_bh(&svsk->sk_lock); - svsk->sk_close = 1; + set_bit(SK_CLOSE, &svsk->sk_flags); svc_sock_enqueue(svsk); - spin_unlock_bh(&svsk->sk_lock); out: if (sk->sleep && waitqueue_active(sk->sleep)) wake_up_interruptible_all(sk->sleep); @@ -524,10 +625,8 @@ sk, sk->user_data); if (!(svsk = (struct svc_sock *)(sk->user_data))) goto out; - spin_lock_bh(&svsk->sk_lock); - svsk->sk_data++; + set_bit(SK_DATA, &svsk->sk_flags); svc_sock_enqueue(svsk); - spin_unlock_bh(&svsk->sk_lock); out: if (sk->sleep && waitqueue_active(sk->sleep)) wake_up_interruptible(sk->sleep); @@ -560,13 +659,14 @@ newsock->type = sock->type; newsock->ops = ops = sock->ops; + clear_bit(SK_CONN, &svsk->sk_flags); if ((err = ops->accept(sock, newsock, O_NONBLOCK)) < 0) { - if (net_ratelimit()) + if (err != -EAGAIN && net_ratelimit()) printk(KERN_WARNING "%s: accept failed (err %d)!\n", serv->sv_name, -err); goto failed; /* aborted connection or whatever */ } - + set_bit(SK_CONN, &svsk->sk_flags); slen = sizeof(sin); err = ops->getname(newsock, (struct sockaddr *) &sin, &slen, 1); if (err < 0) { @@ -594,14 +694,45 @@ if (!(newsvsk = svc_setup_socket(serv, newsock, &err, 0))) goto failed; + /* make sure that a write doesn't block forever when + * low on memory + */ + newsock->sk->sndtimeo = HZ*30; + /* Precharge. Data may have arrived on the socket before we * installed the data_ready callback. */ - spin_lock_bh(&newsvsk->sk_lock); - newsvsk->sk_data = 1; - newsvsk->sk_temp = 1; + set_bit(SK_DATA, &newsvsk->sk_flags); svc_sock_enqueue(newsvsk); - spin_unlock_bh(&newsvsk->sk_lock); + + /* make sure that we don't have too many active connections. + * If we have, something must be dropped. + * We randomly choose between newest and oldest (in terms + * of recent activity) and drop it. + */ + if (serv->sv_tmpcnt > serv->sv_nrthreads*5) { + struct svc_sock *svsk = NULL; + spin_lock_bh(&serv->sv_lock); + if (!list_empty(&serv->sv_tempsocks)) { + if (net_random()&1) + svsk = list_entry(serv->sv_tempsocks.prev, + struct svc_sock, + sk_list); + else + svsk = list_entry(serv->sv_tempsocks.next, + struct svc_sock, + sk_list); + set_bit(SK_CLOSE, &svsk->sk_flags); + svsk->sk_inuse ++; + } + spin_unlock_bh(&serv->sv_lock); + + if (svsk) { + svc_sock_enqueue(svsk); + svc_sock_put(svsk); + } + + } if (serv->sv_stats) serv->sv_stats->nettcpconn++; @@ -622,23 +753,25 @@ struct svc_sock *svsk = rqstp->rq_sock; struct svc_serv *serv = svsk->sk_server; struct svc_buf *bufp = &rqstp->rq_argbuf; - int len, ready, used; + int len; dprintk("svc: tcp_recv %p data %d conn %d close %d\n", - svsk, svsk->sk_data, svsk->sk_conn, svsk->sk_close); + svsk, test_bit(SK_DATA, &svsk->sk_flags), + test_bit(SK_CONN, &svsk->sk_flags), + test_bit(SK_CLOSE, &svsk->sk_flags)); - if (svsk->sk_close) { + if (test_bit(SK_CLOSE, &svsk->sk_flags)) { svc_delete_socket(svsk); return 0; } - if (svsk->sk_conn) { + if (test_bit(SK_CONN, &svsk->sk_flags)) { svc_tcp_accept(svsk); - svc_sock_accepted(svsk); + svc_sock_received(svsk); return 0; } - ready = svsk->sk_data; + clear_bit(SK_DATA, &svsk->sk_flags); /* Receive data. If we haven't got the record length yet, get * the next four bytes. Otherwise try to gobble up as much as @@ -661,13 +794,17 @@ * bit set in the fragment length header. * But apparently no known nfs clients send fragmented * records. */ - /* FIXME: shutdown socket */ - printk(KERN_NOTICE "RPC: bad TCP reclen %08lx", + printk(KERN_NOTICE "RPC: bad TCP reclen 0x%08lx (non-terminal)\n", (unsigned long) svsk->sk_reclen); - return -EIO; + goto err_delete; } svsk->sk_reclen &= 0x7fffffff; dprintk("svc: TCP record, %d bytes\n", svsk->sk_reclen); + if (svsk->sk_reclen > (bufp->buflen<<2)) { + printk(KERN_NOTICE "RPC: bad TCP reclen 0x%08lx (large)\n", + (unsigned long) svsk->sk_reclen); + goto err_delete; + } } /* Check whether enough data is available */ @@ -676,21 +813,12 @@ goto error; if (len < svsk->sk_reclen) { - /* FIXME: if sk_reclen > window-size, then we will - * never be able to receive the record, so should - * shutdown the connection - */ dprintk("svc: incomplete TCP record (%d of %d)\n", len, svsk->sk_reclen); - svc_sock_received(svsk, ready); + svc_sock_received(svsk); return -EAGAIN; /* record not complete */ } - /* if we think there is only one more record to read, but - * it is bigger than we expect, then two records must have arrived - * together, so pretend we aren't using the record.. */ - if (len > svsk->sk_reclen && ready == 1) - used = 0; - else used = 1; + set_bit(SK_DATA, &svsk->sk_flags); /* Frob argbuf */ bufp->iov[0].iov_base += 4; @@ -717,20 +845,24 @@ svsk->sk_reclen = 0; svsk->sk_tcplen = 0; - svc_sock_received(svsk, used); + svc_sock_received(svsk); if (serv->sv_stats) serv->sv_stats->nettcpcnt++; return len; -error: + err_delete: + svc_delete_socket(svsk); + return -EAGAIN; + + error: if (len == -EAGAIN) { dprintk("RPC: TCP recvfrom got EAGAIN\n"); - svc_sock_received(svsk, ready); /* Clear data ready */ + svc_sock_received(svsk); } else { printk(KERN_NOTICE "%s: recvfrom returned errno %d\n", svsk->sk_server->sv_name, -len); - svc_sock_received(svsk, 0); + svc_sock_received(svsk); } return len; @@ -738,8 +870,6 @@ /* * Send out data on TCP socket. - * FIXME: Make the sendto call non-blocking in order not to hang - * a daemon on a dead client. Requires write queue maintenance. */ static int svc_tcp_sendto(struct svc_rqst *rqstp) @@ -760,10 +890,8 @@ printk(KERN_NOTICE "rpc-srv/tcp: %s: sent only %d bytes of %d - should shutdown socket\n", rqstp->rq_sock->sk_server->sv_name, sent, bufp->len << 2); - /* FIXME: should shutdown the socket, or allocate more memort - * or wait and try again or something. Otherwise - * client will get confused - */ + svc_delete_socket(rqstp->rq_sock); + sent = -EAGAIN; } return sent; } @@ -783,21 +911,77 @@ dprintk("setting up TCP socket for reading\n"); sk->state_change = svc_tcp_state_change; sk->data_ready = svc_tcp_data_ready; + sk->write_space = svc_write_space; svsk->sk_reclen = 0; svsk->sk_tcplen = 0; + + /* sndbuf needs to have room for one request + * per thread, otherwise we can stall even when the + * network isn't a bottleneck. + * rcvbuf just needs to be able to hold a few requests. + * Normally they will be removed from the queue + * as soon a a complete request arrives. + */ + svc_sock_setbufsize(svsk->sk_sock, + svsk->sk_server->sv_nrthreads * + svsk->sk_server->sv_bufsz, + 3 * svsk->sk_server->sv_bufsz); } return 0; } +void +svc_sock_update_bufs(struct svc_serv *serv) +{ + /* + * The number of server threads has changed. Update + * rcvbuf and sndbuf accordingly on all sockets + */ + struct list_head *le; + + spin_lock_bh(&serv->sv_lock); + list_for_each(le, &serv->sv_permsocks) { + struct svc_sock *svsk = + list_entry(le, struct svc_sock, sk_list); + struct socket *sock = svsk->sk_sock; + if (sock->type == SOCK_DGRAM) { + /* udp sockets need large rcvbuf as all pending + * requests are still in that buffer. + * As outgoing requests do not wait for an + * ACK, only a moderate sndbuf is needed + */ + svc_sock_setbufsize(sock, + 5 * serv->sv_bufsz, + (serv->sv_nrthreads+2)* serv->sv_bufsz); + } else if (svsk->sk_sk->state != TCP_LISTEN) { + printk(KERN_ERR "RPC update_bufs: permanent sock neither UDP or TCP_LISTEN\n"); + } + } + list_for_each(le, &serv->sv_tempsocks) { + struct svc_sock *svsk = + list_entry(le, struct svc_sock, sk_list); + struct socket *sock = svsk->sk_sock; + if (sock->type == SOCK_STREAM) { + /* See svc_tcp_init above for rationale on buffer sizes */ + svc_sock_setbufsize(sock, + serv->sv_nrthreads * + serv->sv_bufsz, + 3 * serv->sv_bufsz); + } else + printk(KERN_ERR "RPC update_bufs: temp sock not TCP\n"); + } + spin_unlock_bh(&serv->sv_lock); +} + /* * Receive the next request on any socket. */ int svc_recv(struct svc_serv *serv, struct svc_rqst *rqstp, long timeout) { - struct svc_sock *svsk; + struct svc_sock *svsk =NULL; int len; DECLARE_WAITQUEUE(wait, current); @@ -821,9 +1005,28 @@ return -EINTR; spin_lock_bh(&serv->sv_lock); - if ((svsk = svc_sock_dequeue(serv)) != NULL) { + if (!list_empty(&serv->sv_tempsocks)) { + svsk = list_entry(serv->sv_tempsocks.next, + struct svc_sock, sk_list); + /* apparently the "standard" is that clients close + * idle connections after 5 minutes, servers after + * 6 minutes + * http://www.connectathon.org/talks96/nfstcp.pdf + */ + if (CURRENT_TIME - svsk->sk_lastrecv < 6*60 + || test_bit(SK_BUSY, &svsk->sk_flags)) + svsk = NULL; + } + if (svsk) { + set_bit(SK_BUSY, &svsk->sk_flags); + set_bit(SK_CLOSE, &svsk->sk_flags); + rqstp->rq_sock = svsk; + svsk->sk_inuse++; + } else if ((svsk = svc_sock_dequeue(serv)) != NULL) { rqstp->rq_sock = svsk; svsk->sk_inuse++; + rqstp->rq_reserved = serv->sv_bufsz; + svsk->sk_reserved += rqstp->rq_reserved; } else { /* No data pending. Go to sleep */ svc_serv_enqueue(serv, rqstp); @@ -860,6 +1063,14 @@ svc_sock_release(rqstp); return -EAGAIN; } + svsk->sk_lastrecv = CURRENT_TIME; + if (test_bit(SK_TEMP, &svsk->sk_flags)) { + /* push active sockets to end of list */ + spin_lock_bh(&serv->sv_lock); + list_del(&svsk->sk_list); + list_add_tail(&svsk->sk_list, &serv->sv_tempsocks); + spin_unlock_bh(&serv->sv_lock); + } rqstp->rq_secure = ntohs(rqstp->rq_addr.sin_port) < 1024; rqstp->rq_userset = 0; @@ -936,8 +1147,9 @@ svsk->sk_sk = inet; svsk->sk_ostate = inet->state_change; svsk->sk_odata = inet->data_ready; + svsk->sk_owspace = inet->write_space; svsk->sk_server = serv; - spin_lock_init(&svsk->sk_lock); + svsk->sk_lastrecv = CURRENT_TIME; /* Initialize the socket */ if (sock->type == SOCK_DGRAM) @@ -957,9 +1169,16 @@ return NULL; } + spin_lock_bh(&serv->sv_lock); - svsk->sk_list = serv->sv_allsocks; - serv->sv_allsocks = svsk; + if (!pmap_register) { + set_bit(SK_TEMP, &svsk->sk_flags); + list_add(&svsk->sk_list, &serv->sv_tempsocks); + serv->sv_tmpcnt++; + } else { + clear_bit(SK_TEMP, &svsk->sk_flags); + list_add(&svsk->sk_list, &serv->sv_permsocks); + } spin_unlock_bh(&serv->sv_lock); dprintk("svc: svc_setup_socket created %p (inet %p)\n", @@ -1020,7 +1239,6 @@ void svc_delete_socket(struct svc_sock *svsk) { - struct svc_sock **rsk; struct svc_serv *serv; struct sock *sk; @@ -1031,23 +1249,18 @@ sk->state_change = svsk->sk_ostate; sk->data_ready = svsk->sk_odata; + sk->write_space = svsk->sk_owspace; spin_lock_bh(&serv->sv_lock); - for (rsk = &serv->sv_allsocks; *rsk; rsk = &(*rsk)->sk_list) { - if (*rsk == svsk) - break; - } - if (!*rsk) { - spin_unlock_bh(&serv->sv_lock); - return; - } - *rsk = svsk->sk_list; - if (svsk->sk_qued) - rpc_remove_list(&serv->sv_sockets, svsk); + list_del(&svsk->sk_list); + if (test_bit(SK_TEMP, &svsk->sk_flags)) + serv->sv_tmpcnt--; + if (test_bit(SK_QUED, &svsk->sk_flags)) + list_del(&svsk->sk_ready); - svsk->sk_dead = 1; + set_bit(SK_DEAD, &svsk->sk_flags); if (!svsk->sk_inuse) { spin_unlock_bh(&serv->sv_lock); diff -Nru a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c --- a/net/sunrpc/xprt.c Thu Mar 7 18:17:38 2002 +++ b/net/sunrpc/xprt.c Thu Mar 7 18:17:38 2002 @@ -542,25 +542,15 @@ static inline struct rpc_rqst * xprt_lookup_rqst(struct rpc_xprt *xprt, u32 xid) { - struct rpc_task *head, *task; struct rpc_rqst *req; - int safe = 0; + struct list_head *le; + struct rpc_task *task; spin_lock_bh(&rpc_queue_lock); - if ((head = xprt->pending.task) != NULL) { - task = head; - do { - if ((req = task->tk_rqstp) && req->rq_xid == xid) - goto out; - task = task->tk_next; - if (++safe > 100) { - printk("xprt_lookup_rqst: loop in Q!\n"); - goto out_bad; - } - } while (task != head); - } + task_for_each(task, le, &xprt->pending.tasks) + if ((req = task->tk_rqstp) && req->rq_xid == xid) + goto out; dprintk("RPC: unknown XID %08x in reply.\n", xid); - out_bad: req = NULL; out: if (req && !__rpc_lock_task(req->rq_task)) @@ -1487,9 +1477,9 @@ } else xprt_default_timeout(&xprt->timeout, xprt->prot); - xprt->pending = RPC_INIT_WAITQ("xprt_pending"); - xprt->sending = RPC_INIT_WAITQ("xprt_sending"); - xprt->backlog = RPC_INIT_WAITQ("xprt_backlog"); + INIT_RPC_WAITQ(&xprt->pending, "xprt_pending"); + INIT_RPC_WAITQ(&xprt->sending, "xprt_sending"); + INIT_RPC_WAITQ(&xprt->backlog, "xprt_backlog"); /* initialize free list */ for (i = 0, req = xprt->slot; i < RPC_MAXREQS-1; i++, req++) diff -Nru a/net/unix/af_unix.c b/net/unix/af_unix.c --- a/net/unix/af_unix.c Thu Mar 7 18:17:45 2002 +++ b/net/unix/af_unix.c Thu Mar 7 18:17:45 2002 @@ -603,9 +603,7 @@ int err = 0; if (sunname->sun_path[0]) { - if (path_init(sunname->sun_path, - LOOKUP_POSITIVE|LOOKUP_FOLLOW, &nd)) - err = path_walk(sunname->sun_path, &nd); + err = path_lookup(sunname->sun_path, LOOKUP_FOLLOW, &nd); if (err) goto fail; err = permission(nd.dentry->d_inode,MAY_WRITE); @@ -691,8 +689,7 @@ * Get the parent directory, calculate the hash for last * component. */ - if (path_init(sunaddr->sun_path, LOOKUP_PARENT, &nd)) - err = path_walk(sunaddr->sun_path, &nd); + err = path_lookup(sunaddr->sun_path, LOOKUP_PARENT, &nd); if (err) goto out_mknod_parent; /* diff -Nru a/net/wanrouter/af_wanpipe.c b/net/wanrouter/af_wanpipe.c --- a/net/wanrouter/af_wanpipe.c Thu Mar 7 18:17:43 2002 +++ b/net/wanrouter/af_wanpipe.c Thu Mar 7 18:17:43 2002 @@ -110,7 +110,7 @@ * passes the packet to the driver. Before each send(), a poll * routine checks the sock resources The maximum value of * packet sent counter is 1, thus if one packet is queued, the - * application will block untill that packet is passed to the + * application will block until that packet is passed to the * driver. * * RECEIVE: @@ -121,7 +121,7 @@ * return code, the driver knows whether the packet was * sucessfully queued. If the socket queue is full, * protocol flow control is used by the driver, if any, - * to slow down the traffic untill the sock queue is free. + * to slow down the traffic until the sock queue is free. * * Every time a packet arrives into a socket queue the * socket wakes up processes which are waiting to receive @@ -2396,7 +2396,7 @@ /* Check if data buffers are pending for transmission, - * if so, check wheter user wants to wait untill data + * if so, check whether user wants to wait until data * is transmitted, or clear a call and drop packets */ if (atomic_read(&sk->wmem_alloc) || check_driver_busy(sk)){ @@ -2432,7 +2432,7 @@ /* Check if data buffers are pending for transmission, - * if so, check wheter user wants to wait untill data + * if so, check whether user wants to wait until data * is transmitted, or reset a call and drop packets */ if (atomic_read(&sk->wmem_alloc) || check_driver_busy(sk)){ diff -Nru a/net/wanrouter/wanmain.c b/net/wanrouter/wanmain.c --- a/net/wanrouter/wanmain.c Thu Mar 7 18:17:39 2002 +++ b/net/wanrouter/wanmain.c Thu Mar 7 18:17:39 2002 @@ -9,7 +9,7 @@ * o Logical connection management (switched virtual circuits) * o Protocol encapsulation/decapsulation * -* Author: Gideon Hack +* Author: Gideon Hack * * Copyright: (c) 1995-1999 Sangoma Technologies Inc. * @@ -18,11 +18,11 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ -* Nov 24, 2000 Nenad Corbic Updated for 2.4.X kernels +* Nov 24, 2000 Nenad Corbic Updated for 2.4.X kernels * Nov 07, 2000 Nenad Corbic Fixed the Mulit-Port PPP for kernels 2.2.16 and * greater. * Aug 2, 2000 Nenad Corbic Block the Multi-Port PPP from running on -* kernels 2.2.16 or greater. The SyncPPP +* kernels 2.2.16 or greater. The SyncPPP * has changed. * Jul 13, 2000 Nenad Corbic Added SyncPPP support * Added extra debugging in device_setup(). @@ -39,7 +39,7 @@ * Dec 22, 1998 Arnaldo Melo vmalloc/vfree used in device_setup to allocate * kernel memory and copy configuration data to * kernel space (for big firmwares) -* Jun 02, 1999 Gideon Hack Updates for Linux 2.0.X and 2.2.X kernels. +* Jun 02, 1999 Gideon Hack Updates for Linux 2.0.X and 2.2.X kernels. *****************************************************************************/ #include @@ -55,28 +55,10 @@ #include /* htons(), etc. */ #include /* WAN router API definitions */ - -#if defined(LINUX_2_4) - #include /* vmalloc, vfree */ - #include /* copy_to/from_user */ - #include /* __initfunc et al. */ - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3) - #include -#else - #include <../drivers/net/wan/syncppp.h> -#endif - -#elif defined(LINUX_2_1) - #define LINUX_2_1 - #include /* vmalloc, vfree */ - #include /* copy_to/from_user */ - #include /* __initfunc et al. */ - #include <../drivers/net/syncppp.h> - -#else - #include /* kernel <-> user copy */ -#endif +#include /* vmalloc, vfree */ +#include /* copy_to/from_user */ +#include /* __initfunc et al. */ +#include #define KMEM_SAFETYZONE 8 @@ -84,7 +66,7 @@ static void * dbg_kmalloc(unsigned int size, int prio, int line) { int i = 0; void * v = kmalloc(size+sizeof(unsigned int)+2*KMEM_SAFETYZONE*8,prio); - char * c1 = v; + char * c1 = v; c1 += sizeof(unsigned int); *((unsigned int *)v) = size; @@ -137,12 +119,11 @@ #define kfree(x) dbg_kfree(x,__LINE__) *****************************************************************************/ - /* - * Function Prototypes + * Function Prototypes */ -/* +/* * Kernel loadable module interface. */ #ifdef MODULE @@ -150,8 +131,8 @@ void cleanup_module (void); #endif -/* - * WAN device IOCTL handlers +/* + * WAN device IOCTL handlers */ static int device_setup(wan_device_t *wandev, wandev_conf_t *u_conf); @@ -159,9 +140,9 @@ static int device_shutdown(wan_device_t *wandev); static int device_new_if(wan_device_t *wandev, wanif_conf_t *u_conf); static int device_del_if(wan_device_t *wandev, char *u_name); - -/* - * Miscellaneous + +/* + * Miscellaneous */ static wan_device_t *find_device (char *name); @@ -181,8 +162,8 @@ wan_device_t* router_devlist = NULL; /* list of registered devices */ static int devcnt = 0; -/* - * Organize Unique Identifiers for encapsulation/decapsulation +/* + * Organize Unique Identifiers for encapsulation/decapsulation */ static unsigned char oui_ether[] = { 0x00, 0x00, 0x00 }; @@ -213,18 +194,15 @@ #ifdef CONFIG_VENDOR_SANGOMA sdladrv_init(); wanpipe_init(); -#endif - +#endif + return err; } - -#ifdef LINUX_2_4 static void __exit wanrouter_cleanup (void) { wanrouter_proc_cleanup(); } -#endif #else @@ -249,10 +227,10 @@ printk(KERN_INFO "%s v%u.%u %s\n", fullname, ROUTER_VERSION, ROUTER_RELEASE, copyright); - + err = wanrouter_proc_init(); - - if (err){ + + if (err){ printk(KERN_INFO "%s: can't create entry in proc filesystem!\n", modname); } @@ -299,21 +277,21 @@ if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC) || (wandev->name == NULL)) return -EINVAL; - + namelen = strlen(wandev->name); if (!namelen || (namelen > WAN_DRVNAME_SZ)) return -EINVAL; - + if (find_device(wandev->name) != NULL) return -EEXIST; -#ifdef WANDEBUG +#ifdef WANDEBUG printk(KERN_INFO "%s: registering WAN device %s\n", modname, wandev->name); #endif /* - * Register /proc directory entry + * Register /proc directory entry */ err = wanrouter_proc_add(wandev); if (err) { @@ -327,7 +305,7 @@ * Initialize fields of the wan_device structure maintained by the * router and update local data. */ - + wandev->ndev = 0; wandev->dev = NULL; wandev->next = router_devlist; @@ -364,20 +342,18 @@ if (wandev == NULL) return -ENODEV; -#ifdef WANDEBUG +#ifdef WANDEBUG printk(KERN_INFO "%s: unregistering WAN device %s\n", modname, name); #endif - if (wandev->state != WAN_UNCONFIGURED) { + if (wandev->state != WAN_UNCONFIGURED) device_shutdown(wandev); - } - - if (prev){ + + if (prev) prev->next = wandev->next; - }else{ + else router_devlist = wandev->next; - } - + --devcnt; wanrouter_proc_delete(wandev); MOD_DEC_USE_COUNT; @@ -457,7 +433,7 @@ skb->data[cnt+1], skb->data[cnt+2], skb->data[cnt+3], dev->name); return 0; - } + } ethertype = *((unsigned short*)&skb->data[cnt+4]); cnt += 6; break; @@ -491,23 +467,20 @@ struct proc_dir_entry *dent; wan_device_t *wandev; - #if defined (LINUX_2_1) || defined (LINUX_2_4) - if (!capable(CAP_NET_ADMIN)){ + if (!capable(CAP_NET_ADMIN)) return -EPERM; - } - #endif - + if ((cmd >> 8) != ROUTER_IOCTL) return -EINVAL; - + dent = PDE(inode); if ((dent == NULL) || (dent->data == NULL)) return -EINVAL; - + wandev = dent->data; if (wandev->magic != ROUTER_MAGIC) return -EINVAL; - + switch (cmd) { case ROUTER_SETUP: err = device_setup(wandev, (void*)arg); @@ -560,103 +533,62 @@ wandev_conf_t *conf; int err = -EINVAL; - if (wandev->setup == NULL){ /* Nothing to do ? */ + if (wandev->setup == NULL) { /* Nothing to do ? */ printk(KERN_INFO "%s: ERROR, No setup script: wandev->setup()\n", wandev->name); return 0; } - #ifdef LINUX_2_0 - err = verify_area (VERIFY_READ, u_conf, sizeof(wandev_conf_t)); - if(err){ - return err; - } - #endif - conf = kmalloc(sizeof(wandev_conf_t), GFP_KERNEL); if (conf == NULL){ printk(KERN_INFO "%s: ERROR, Failed to allocate kernel memory !\n", wandev->name); return -ENOBUFS; } - - #if defined (LINUX_2_1) || defined (LINUX_2_4) - if(copy_from_user(conf, u_conf, sizeof(wandev_conf_t))) { + + if (copy_from_user(conf, u_conf, sizeof(wandev_conf_t))) { printk(KERN_INFO "%s: Failed to copy user config data to kernel space!\n", wandev->name); kfree(conf); return -EFAULT; } - #else - memcpy_fromfs ((void *)conf, (void *)u_conf, sizeof(wandev_conf_t)); - #endif - - if (conf->magic != ROUTER_MAGIC){ + + if (conf->magic != ROUTER_MAGIC) { kfree(conf); printk(KERN_INFO "%s: ERROR, Invalid MAGIC Number\n", wandev->name); - return -EINVAL; + return -EINVAL; } - if (conf->data_size && conf->data){ - if(conf->data_size > 128000 || conf->data_size < 0) { - printk(KERN_INFO + if (conf->data_size && conf->data) { + if (conf->data_size > 128000 || conf->data_size < 0) { + printk(KERN_INFO "%s: ERROR, Invalid firmware data size %i !\n", wandev->name, conf->data_size); kfree(conf); - return -EINVAL;; + return -EINVAL; } -#if defined (LINUX_2_1) || defined (LINUX_2_4) data = vmalloc(conf->data_size); - if (data) { - if(!copy_from_user(data, conf->data, conf->data_size)){ - conf->data=data; - err = wandev->setup(wandev,conf); - }else{ - printk(KERN_INFO - "%s: ERROR, Faild to copy from user data !\n", - wandev->name); - err = -EFAULT; - } - }else{ - printk(KERN_INFO + if (!data) { + printk(KERN_INFO "%s: ERROR, Faild allocate kernel memory !\n", wandev->name); - err = -ENOBUFS; - } - - if (data){ - vfree(data); - } -#else - err = verify_area(VERIFY_READ, conf->data, conf->data_size); - if (!err) { - data = kmalloc(conf->data_size, GFP_KERNEL); - if (data) { - memcpy_fromfs(data, (void*)conf->data, - conf->data_size); - conf->data = data; - }else{ - printk(KERN_INFO - "%s: ERROR, Faild allocate kernel memory !\n",wandev->name); - err = -ENOMEM; - } - }else{ - printk(KERN_INFO - "%s: ERROR, Faild to copy from user data !\n",wandev->name); + kfree(conf); + return -ENOBUFS; } - - if (!err){ + if (!copy_from_user(data, conf->data, conf->data_size)) { + conf->data = data; err = wandev->setup(wandev, conf); + } else { + printk(KERN_INFO + "%s: ERROR, Faild to copy from user data !\n", + wandev->name); + err = -EFAULT; } - - if (data){ - kfree(data); - } -#endif - }else{ - printk(KERN_INFO + vfree(data); + } else { + printk(KERN_INFO "%s: ERROR, No firmware found ! Firmware size = %i !\n", wandev->name, conf->data_size); } @@ -670,37 +602,33 @@ * o delete all not opened logical channels for this device * o call driver's shutdown() entry point */ - + static int device_shutdown (wan_device_t *wandev) { netdevice_t *dev; int err=0; - - if (wandev->state == WAN_UNCONFIGURED){ + + if (wandev->state == WAN_UNCONFIGURED) return 0; - } printk(KERN_INFO "\n%s: Shutting Down!\n",wandev->name); - + for (dev = wandev->dev; dev;) { - if ((err=delete_interface(wandev, dev->name)) != 0){ + if ((err=delete_interface(wandev, dev->name)) != 0) return err; - } - /* The above function deallocates the current dev * structure. Therefore, we cannot use dev->priv * as the next element: wandev->dev points to the * next element */ dev = wandev->dev; } - - if (wandev->ndev){ + + if (wandev->ndev) return -EBUSY; /* there are opened interfaces */ - } - + if (wandev->shutdown) err=wandev->shutdown(wandev); - + return err; } @@ -712,13 +640,6 @@ { wandev_stat_t stat; - #ifdef LINUX_2_0 - int err; - err = verify_area(VERIFY_WRITE, u_stat, sizeof(wandev_stat_t)); - if (err) - return err; - #endif - memset(&stat, 0, sizeof(stat)); /* Ask device driver to update device statistics */ @@ -729,12 +650,8 @@ stat.ndev = wandev->ndev; stat.state = wandev->state; - #if defined (LINUX_2_1) || defined (LINUX_2_4) - if(copy_to_user(u_stat, &stat, sizeof(stat))) + if (copy_to_user(u_stat, &stat, sizeof(stat))) return -EFAULT; - #else - memcpy_tofs((void*)u_stat, (void*)&stat, sizeof(stat)); - #endif return 0; } @@ -760,103 +677,67 @@ if ((wandev->state == WAN_UNCONFIGURED) || (wandev->new_if == NULL)) return -ENODEV; - -#if defined (LINUX_2_1) || defined (LINUX_2_4) - if(copy_from_user(&conf, u_conf, sizeof(wanif_conf_t))) + + if (copy_from_user(&conf, u_conf, sizeof(wanif_conf_t))) return -EFAULT; -#else - err = verify_area(VERIFY_READ, u_conf, sizeof(wanif_conf_t)); - if (err) - return err; - memcpy_fromfs((void*)&conf, (void*)u_conf, sizeof(wanif_conf_t)); -#endif - + if (conf.magic != ROUTER_MAGIC) return -EINVAL; - err = -EPROTONOSUPPORT; - - + if (conf.config_id == WANCONFIG_MPPP) { #ifdef CONFIG_WANPIPE_MULTPPP - if (conf.config_id == WANCONFIG_MPPP){ - pppdev = kmalloc(sizeof(struct ppp_device), GFP_KERNEL); - if (pppdev == NULL){ + if (pppdev == NULL) return -ENOBUFS; - } memset(pppdev, 0, sizeof(struct ppp_device)); - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,16) pppdev->dev = kmalloc(sizeof(netdevice_t), GFP_KERNEL); - if (pppdev->dev == NULL){ + if (pppdev->dev == NULL) { kfree(pppdev); return -ENOBUFS; } memset(pppdev->dev, 0, sizeof(netdevice_t)); -#endif - err = wandev->new_if(wandev, (netdevice_t *)pppdev, &conf); - - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,16) - dev = pppdev->dev; - #else - dev = &pppdev->dev; - #endif - - }else{ - - dev = kmalloc(sizeof(netdevice_t), GFP_KERNEL); - if (dev == NULL){ - return -ENOBUFS; - } - memset(dev, 0, sizeof(netdevice_t)); - err = wandev->new_if(wandev, dev, &conf); - } - + dev = pppdev->dev; #else - /* Sync PPP is disabled */ - if (conf.config_id != WANCONFIG_MPPP){ - + printk(KERN_INFO "%s: Wanpipe Mulit-Port PPP support has not been compiled in!\n", + wandev->name); + return -EPROTONOSUPPORT; +#endif + } else { dev = kmalloc(sizeof(netdevice_t), GFP_KERNEL); - if (dev == NULL){ + if (dev == NULL) return -ENOBUFS; - } - memset(dev, 0, sizeof(netdevice_t)); + memset(dev, 0, sizeof(netdevice_t)); err = wandev->new_if(wandev, dev, &conf); - }else{ - printk(KERN_INFO "%s: Wanpipe Mulit-Port PPP support has not been compiled in!\n", - wandev->name); - return err; } -#endif - + if (!err) { /* Register network interface. This will invoke init() * function supplied by the driver. If device registered * successfully, add it to the interface list. */ - if (dev->name == NULL){ + if (dev->name == NULL) { err = -EINVAL; - }else if (dev_get(dev->name)){ + } else if (dev_get(dev->name)) { err = -EEXIST; /* name already exists */ - }else{ - - #ifdef WANDEBUG + } else { + + #ifdef WANDEBUG printk(KERN_INFO "%s: registering interface %s...\n", modname, dev->name); - #endif - + #endif + err = register_netdev(dev); if (!err) { netdevice_t *slave=NULL; unsigned long smp_flags=0; - + lock_adapter_irq(&wandev->lock, &smp_flags); - - if (wandev->dev == NULL){ + + if (wandev->dev == NULL) { wandev->dev = dev; - }else{ + } else { for (slave=wandev->dev; *((netdevice_t**)slave->priv); slave=*((netdevice_t**)slave->priv)); @@ -864,7 +745,7 @@ *((netdevice_t**)slave->priv) = dev; } ++wandev->ndev; - + unlock_adapter_irq(&wandev->lock, &smp_flags); return 0; /* done !!! */ } @@ -874,25 +755,23 @@ } /* This code has moved from del_if() function */ - if (dev->priv){ + if (dev->priv) { kfree(dev->priv); - dev->priv=NULL; + dev->priv = NULL; } - - #ifdef CONFIG_WANPIPE_MULTPPP - if (conf.config_id == WANCONFIG_MPPP){ + +#ifdef CONFIG_WANPIPE_MULTPPP + if (conf.config_id == WANCONFIG_MPPP) kfree(pppdev); - }else{ + else kfree(dev); - } - #else +#else /* Sync PPP is disabled */ - if (conf.config_id != WANCONFIG_MPPP){ + if (conf.config_id != WANCONFIG_MPPP) kfree(dev); - } - #endif - +#endif + return err; } @@ -910,42 +789,31 @@ if (wandev->state == WAN_UNCONFIGURED) return -ENODEV; - - #ifdef LINUX_2_0 - err = verify_area(VERIFY_READ, u_name, WAN_IFNAME_SZ); - if (err) - return err; - #endif memset(name, 0, sizeof(name)); - #if defined (LINUX_2_1) || defined (LINUX_2_4) - if(copy_from_user(name, u_name, WAN_IFNAME_SZ)) + if (copy_from_user(name, u_name, WAN_IFNAME_SZ)) return -EFAULT; - #else - memcpy_fromfs((void*)name, (void*)u_name, WAN_IFNAME_SZ); - #endif err = delete_interface(wandev, name); if (err) - return(err); + return err; /* If last interface being deleted, shutdown card * This helps with administration at leaf nodes - * (You can tell if the person at the other end of the phone + * (You can tell if the person at the other end of the phone * has an interface configured) and avoids DoS vulnerabilities * in binary driver files - this fixes a problem with the current * Sangoma driver going into strange states when all the network * interfaces are deleted and the link irrecoverably disconnected. - */ + */ - if (!wandev->ndev && wandev->shutdown){ + if (!wandev->ndev && wandev->shutdown) err = wandev->shutdown(wandev); - } + return err; } - /* * Miscellaneous Functions */ @@ -995,18 +863,12 @@ dev = *slave; } unlock_adapter_irq(&wandev->lock, &smp_flags); - - if (dev == NULL){ + + if (dev == NULL) return -ENODEV; /* interface not found */ - } - - #ifdef LINUX_2_4 - if (netif_running(dev)){ - #else - if (dev->start) { - #endif + + if (netif_running(dev)) return -EBUSY; /* interface in use */ - } if (wandev->del_if) wandev->del_if(wandev, dev); @@ -1023,9 +885,9 @@ } --wandev->ndev; unlock_adapter_irq(&wandev->lock, &smp_flags); - - printk(KERN_INFO "%s: unregistering '%s'\n", wandev->name, dev->name); - + + printk(KERN_INFO "%s: unregistering '%s'\n", wandev->name, dev->name); + /* Due to new interface linking method using dev->priv, * this code has moved from del_if() function.*/ if (dev->priv){ @@ -1035,48 +897,28 @@ unregister_netdev(dev); - #ifdef LINUX_2_4 - kfree(dev); - #else - if (dev->name){ - kfree(dev->name); - } kfree(dev); - #endif return 0; } void lock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags) { - #ifdef LINUX_2_0 - save_flags(*smp_flags); - cli(); - #else spin_lock_irqsave(lock, *smp_flags); - #endif } void unlock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags) { - #ifdef LINUX_2_0 - restore_flags(*smp_flags); - #else spin_unlock_irqrestore(lock, *smp_flags); - #endif } - - -#if defined (LINUX_2_1) || defined (LINUX_2_4) EXPORT_SYMBOL(register_wan_device); EXPORT_SYMBOL(unregister_wan_device); EXPORT_SYMBOL(wanrouter_encapsulate); EXPORT_SYMBOL(wanrouter_type_trans); EXPORT_SYMBOL(lock_adapter_irq); EXPORT_SYMBOL(unlock_adapter_irq); -#endif /* * End diff -Nru a/net/wanrouter/wanproc.c b/net/wanrouter/wanproc.c --- a/net/wanrouter/wanproc.c Thu Mar 7 18:17:44 2002 +++ b/net/wanrouter/wanproc.c Thu Mar 7 18:17:44 2002 @@ -31,23 +31,15 @@ #include /* htons(), etc. */ #include #include /* WAN router API definitions */ +#include +#include +#include /* __initfunc et al. */ +#include /* copy_to_user */ - - -#if defined(LINUX_2_1) || defined(LINUX_2_4) - #include /* __initfunc et al. */ - #include /* copy_to_user */ - #define PROC_STATS_FORMAT "%30s: %12lu\n" -#else - #define PROC_STATS_FORMAT "%30s: %12u\n" - #include /* kernel <-> user copy */ -#endif - +#define PROC_STATS_FORMAT "%30s: %12lu\n" /****** Defines and Macros **************************************************/ -#define PROC_BUFSZ 4000 /* buffer size for printing proc info */ - #define PROT_DECODE(prot) ((prot == WANCONFIG_FR) ? " FR" :\ (prot == WANCONFIG_X25) ? " X25" : \ (prot == WANCONFIG_PPP) ? " PPP" : \ @@ -69,962 +61,346 @@ #ifdef CONFIG_PROC_FS +/* Proc filesystem interface */ +static int router_proc_perms(struct inode *, int); -#ifdef LINUX_2_4 /* Start of LINUX 2.4.X code */ +/* Miscellaneous */ +/* + * Structures for interfacing with the /proc filesystem. + * Router creates its own directory /proc/net/router with the folowing + * entries: + * config device configuration + * status global device statistics + * entry for each WAN device + */ - /* Proc filesystem interface */ - static int router_proc_perms(struct inode *, int); - static ssize_t router_proc_read(struct file* file, char* buf, size_t count, loff_t *ppos); - - /* Methods for preparing data for reading proc entries */ - - static int config_get_info(char* buf, char** start, off_t offs, int len); - static int status_get_info(char* buf, char** start, off_t offs, int len); - static int wandev_get_info(char* buf, char** start, off_t offs, int len); - - /* Miscellaneous */ - - /* - * Structures for interfacing with the /proc filesystem. - * Router creates its own directory /proc/net/router with the folowing - * entries: - * config device configuration - * status global device statistics - * entry for each WAN device - */ - - /* - * Generic /proc/net/router/ file and inode operations - */ - static struct file_operations router_fops = - { - read: router_proc_read, - }; - - static struct inode_operations router_inode = - { - permission: router_proc_perms, - }; - - /* - * /proc/net/router/ file operations - */ - - static struct file_operations wandev_fops = - { - read: router_proc_read, - ioctl: wanrouter_ioctl, - }; - - /* - * /proc/net/router - */ - - static struct proc_dir_entry *proc_router; - - /* Strings */ - static char conf_hdr[] = - "Device name | port |IRQ|DMA| mem.addr |mem.size|" - "option1|option2|option3|option4\n"; - - static char stat_hdr[] = - "Device name |protocol|station|interface|clocking|baud rate" - "| MTU |ndev|link state\n"; - - - /* - * Interface functions - */ - - /* - * Initialize router proc interface. - */ - - int __init wanrouter_proc_init (void) - { - struct proc_dir_entry *p; - proc_router = proc_mkdir(ROUTER_NAME, proc_net); - if (!proc_router) - goto fail; - - p = create_proc_entry("config",0,proc_router); - if (!p) - goto fail_config; - p->proc_fops = &router_fops; - p->proc_iops = &router_inode; - p->get_info = config_get_info; - p = create_proc_entry("status",0,proc_router); - if (!p) - goto fail_stat; - p->proc_fops = &router_fops; - p->proc_iops = &router_inode; - p->get_info = status_get_info; - return 0; - fail_stat: - remove_proc_entry("config", proc_router); - fail_config: - remove_proc_entry(ROUTER_NAME, proc_net); - fail: - return -ENOMEM; - } +/* + * Generic /proc/net/router/ file and inode operations + */ - /* - * Clean up router proc interface. - */ - - void wanrouter_proc_cleanup (void) - { - remove_proc_entry("config", proc_router); - remove_proc_entry("status", proc_router); - remove_proc_entry(ROUTER_NAME,proc_net); - } +static struct inode_operations router_inode = +{ + permission: router_proc_perms, +}; - /* - * Add directory entry for WAN device. - */ - - int wanrouter_proc_add (wan_device_t* wandev) - { - if (wandev->magic != ROUTER_MAGIC) - return -EINVAL; - - wandev->dent = create_proc_entry(wandev->name, 0, proc_router); - if (!wandev->dent) - return -ENOMEM; - wandev->dent->proc_fops = &wandev_fops; - wandev->dent->proc_iops = &router_inode; - wandev->dent->get_info = wandev_get_info; - wandev->dent->data = wandev; - return 0; - } +/* + * /proc/net/router + */ - /* - * Delete directory entry for WAN device. - */ - - int wanrouter_proc_delete(wan_device_t* wandev) - { - if (wandev->magic != ROUTER_MAGIC) - return -EINVAL; - remove_proc_entry(wandev->name, proc_router); - return 0; - } +static struct proc_dir_entry *proc_router; - /****** Proc filesystem entry points ****************************************/ +/* Strings */ - /* - * Verify access rights. - */ +/* + * Interface functions + */ - static int router_proc_perms (struct inode* inode, int op) - { - return 0; - } +/****** Proc filesystem entry points ****************************************/ - /* - * Read router proc directory entry. - * This is universal routine for reading all entries in /proc/net/wanrouter - * directory. Each directory entry contains a pointer to the 'method' for - * preparing data for that entry. - * o verify arguments - * o allocate kernel buffer - * o call get_info() to prepare data - * o copy data to user space - * o release kernel buffer - * - * Return: number of bytes copied to user space (0, if no data) - * <0 error - */ - - static ssize_t router_proc_read(struct file* file, char* buf, size_t count, - loff_t *ppos) - { - struct inode *inode = file->f_dentry->d_inode; - struct proc_dir_entry* dent; - char* page; - int pos, offs, len; +/* + * Verify access rights. + */ - if (count <= 0) - return 0; - - dent = PDE(inode); - if ((dent == NULL) || (dent->get_info == NULL)) - return 0; - - page = kmalloc(PROC_BUFSZ, GFP_KERNEL); - if (page == NULL) - return -ENOBUFS; - - pos = dent->get_info(page, dent->data, 0, 0); - offs = file->f_pos; - if (offs < pos) { - len = min_t(unsigned int, pos - offs, count); - if (copy_to_user(buf, (page + offs), len)) { - kfree(page); - return -EFAULT; - } - file->f_pos += len; - } - else - len = 0; - kfree(page); - return len; - } +static int router_proc_perms (struct inode* inode, int op) +{ + return 0; +} - /* - * Prepare data for reading 'Config' entry. - * Return length of data. - */ - - static int config_get_info(char* buf, char** start, off_t offs, int len) - { - int cnt = sizeof(conf_hdr) - 1; - wan_device_t* wandev; - strcpy(buf, conf_hdr); - for (wandev = router_devlist; - wandev && (cnt < (PROC_BUFSZ - 120)); - wandev = wandev->next) { - if (wandev->state) cnt += sprintf(&buf[cnt], - "%-15s|0x%-4X|%3u|%3u| 0x%-8lX |0x%-6X|%7u|%7u|%7u|%7u\n", - wandev->name, - wandev->ioport, - wandev->irq, - wandev->dma, - wandev->maddr, - wandev->msize, - wandev->hw_opt[0], - wandev->hw_opt[1], - wandev->hw_opt[2], - wandev->hw_opt[3]); - } +/* + * Iterator + */ +static void *r_start(struct seq_file *m, loff_t *pos) +{ + wan_device_t *wandev; + loff_t l = *pos; - return cnt; - } + lock_kernel(); + if (!l--) + return (void *)1; + for (wandev = router_devlist; l-- && wandev; wandev = wandev->next) + ; + return wandev; +} +static void *r_next(struct seq_file *m, void *v, loff_t *pos) +{ + wan_device_t *wandev = v; + (*pos)++; + return (v == (void *)1) ? router_devlist : wandev->next; +} +static void r_stop(struct seq_file *m, void *v) +{ + unlock_kernel(); +} - /* - * Prepare data for reading 'Status' entry. - * Return length of data. - */ - - static int status_get_info(char* buf, char** start, off_t offs, int len) - { - int cnt = 0; - wan_device_t* wandev; - - //cnt += sprintf(&buf[cnt], "\nSTATUS:\n\n"); - strcpy(&buf[cnt], stat_hdr); - cnt += sizeof(stat_hdr) - 1; - - for (wandev = router_devlist; - wandev && (cnt < (PROC_BUFSZ - 80)); - wandev = wandev->next) { - if (!wandev->state) continue; - cnt += sprintf(&buf[cnt], - "%-15s|%-8s|%-7s|%-9s|%-8s|%9u|%5u|%3u |", - wandev->name, - PROT_DECODE(wandev->config_id), - wandev->config_id == WANCONFIG_FR ? - (wandev->station ? " Node" : " CPE") : - (wandev->config_id == WANCONFIG_X25 ? - (wandev->station ? " DCE" : " DTE") : - (" N/A")), - wandev->interface ? " V.35" : " RS-232", - wandev->clocking ? "internal" : "external", - wandev->bps, - wandev->mtu, - wandev->ndev); - - switch (wandev->state) { - - case WAN_UNCONFIGURED: - cnt += sprintf(&buf[cnt], "%-12s\n", "unconfigured"); - break; - - case WAN_DISCONNECTED: - cnt += sprintf(&buf[cnt], "%-12s\n", "disconnected"); - break; - - case WAN_CONNECTING: - cnt += sprintf(&buf[cnt], "%-12s\n", "connecting"); - break; - - case WAN_CONNECTED: - cnt += sprintf(&buf[cnt], "%-12s\n", "connected"); - break; - - default: - cnt += sprintf(&buf[cnt], "%-12s\n", "invalid"); - break; - } - } - return cnt; +static int config_show(struct seq_file *m, void *v) +{ + wan_device_t *p = v; + if (v == (void *)1) { + seq_puts(m, "Device name | port |IRQ|DMA| mem.addr |"); + seq_puts(m, "mem.size|option1|option2|option3|option4\n"); + return 0; } + if (!p->state) + return 0; + seq_printf(m, "%-15s|0x%-4X|%3u|%3u| 0x%-8lX |0x%-6X|%7u|%7u|%7u|%7u\n", + p->name, p->ioport, p->irq, p->dma, p->maddr, p->msize, + p->hw_opt[0], p->hw_opt[1], p->hw_opt[2], p->hw_opt[3]); + return 0; +} - /* - * Prepare data for reading entry. - * Return length of data. - * - * On entry, the 'start' argument will contain a pointer to WAN device - * data space. - */ - - static int wandev_get_info(char* buf, char** start, off_t offs, int len) - { - wan_device_t* wandev = (void*)start; - int cnt = 0; - int rslt = 0; - - if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC)) - return 0; - if (!wandev->state) - return sprintf(&buf[cnt], "device is not configured!\n"); - - /* Update device statistics */ - if (wandev->update) { - - rslt = wandev->update(wandev); - if(rslt) { - switch (rslt) { - case -EAGAIN: - return sprintf(&buf[cnt], "Device is busy!\n"); - - default: - return sprintf(&buf[cnt], - "Device is not configured!\n"); - } - } - } - - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "total packets received", wandev->stats.rx_packets); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "total packets transmitted", wandev->stats.tx_packets); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "total bytes received", wandev->stats.rx_bytes); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "total bytes transmitted", wandev->stats.tx_bytes); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "bad packets received", wandev->stats.rx_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "packet transmit problems", wandev->stats.tx_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "received frames dropped", wandev->stats.rx_dropped); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "transmit frames dropped", wandev->stats.tx_dropped); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "multicast packets received", wandev->stats.multicast); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "transmit collisions", wandev->stats.collisions); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "receive length errors", wandev->stats.rx_length_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "receiver overrun errors", wandev->stats.rx_over_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "CRC errors", wandev->stats.rx_crc_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "frame format errors (aborts)", wandev->stats.rx_frame_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "receiver fifo overrun", wandev->stats.rx_fifo_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "receiver missed packet", wandev->stats.rx_missed_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "aborted frames transmitted", wandev->stats.tx_aborted_errors); - return cnt; +static int status_show(struct seq_file *m, void *v) +{ + wan_device_t *p = v; + if (v == (void *)1) { + seq_puts(m, "Device name |protocol|station|interface|"); + seq_puts(m, "clocking|baud rate| MTU |ndev|link state\n"); + return 0; } + if (!p->state) + return 0; + seq_printf(m, "%-15s|%-8s|%-7s|%-9s|%-8s|%9u|%5u|%3u |", + p->name, + PROT_DECODE(p->config_id), + p->config_id == WANCONFIG_FR ? + (p->station ? " Node" : " CPE") : + (p->config_id == WANCONFIG_X25 ? + (p->station ? " DCE" : " DTE") : + (" N/A")), + p->interface ? " V.35" : " RS-232", + p->clocking ? "internal" : "external", + p->bps, + p->mtu, + p->ndev); + + switch (p->state) { + case WAN_UNCONFIGURED: + seq_printf(m, "%-12s\n", "unconfigured"); + break; + case WAN_DISCONNECTED: + seq_printf(m, "%-12s\n", "disconnected"); + break; + case WAN_CONNECTING: + seq_printf(m, "%-12s\n", "connecting"); + break; + case WAN_CONNECTED: + seq_printf(m, "%-12s\n", "connected"); + break; + default: + seq_printf(m, "%-12s\n", "invalid"); + break; + } + return 0; +} +static struct seq_operations config_op = { + start: r_start, + next: r_next, + stop: r_stop, + show: config_show +}; + +static struct seq_operations status_op = { + start: r_start, + next: r_next, + stop: r_stop, + show: status_show +}; -#else /* ------------------- END OF LINUX 2.4.X VERSION -------------*/ - - - - /* Proc filesystem interface */ - static int router_proc_perms(struct inode *, int); -#ifdef LINUX_2_1 - static ssize_t router_proc_read(struct file *file, char *buf, size_t count, loff_t *ppos); -#else - static int router_proc_read( - struct inode* inode, struct file* file, char* buf, int count); - static int device_write( - struct inode* inode, struct file* file, const char* buf, int count); -#endif - - /* Methods for preparing data for reading proc entries */ - static int config_get_info(char* buf, char** start, off_t offs, int len, - int dummy); - static int status_get_info(char* buf, char** start, off_t offs, int len, - int dummy); - static int wandev_get_info(char* buf, char** start, off_t offs, int len, - int dummy); - - /* Miscellaneous */ - - /* - * Global Data - */ - - /* - * Names of the proc directory entries - */ - - static char name_root[] = ROUTER_NAME; - static char name_conf[] = "config"; - static char name_stat[] = "status"; - - /* - * Structures for interfacing with the /proc filesystem. - * Router creates its own directory /proc/net/router with the folowing - * entries: - * config device configuration - * status global device statistics - * entry for each WAN device - */ - - /* - * Generic /proc/net/router/ file and inode operations - */ -#ifdef LINUX_2_1 - static struct file_operations router_fops = - { - NULL, /* lseek */ - router_proc_read, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* select */ - NULL, /* ioctl */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ - }; -#else - static struct file_operations router_fops = - { - NULL, /* lseek */ - router_proc_read, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* select */ - NULL, /* ioctl */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* no special release code */ - NULL /* can't fsync */ - }; -#endif +static int config_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &config_op); +} - static struct inode_operations router_inode = - { - &router_fops, - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* follow link */ - NULL, /* readlink */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* bmap */ - NULL, /* truncate */ - router_proc_perms - }; - - /* - * /proc/net/router/ file and inode operations - */ - -#ifdef LINUX_2_1 - static struct file_operations wandev_fops = - { - NULL, /* lseek */ - router_proc_read, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* select */ - wanrouter_ioctl, /* ioctl */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ - }; -#else - static struct file_operations wandev_fops = - { - NULL, /* lseek */ - router_proc_read, /* read */ - device_write, /* write */ - NULL, /* readdir */ - NULL, /* select */ - wanrouter_ioctl, /* ioctl */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* no special release code */ - NULL /* can't fsync */ - }; -#endif +static int status_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &status_op); +} - static struct inode_operations wandev_inode = - { - &wandev_fops, - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* bmap */ - NULL, /* truncate */ - router_proc_perms - }; - - /* - * Proc filesystem derectory entries. - */ - - /* - * /proc/net/router - */ - - static struct proc_dir_entry proc_router = - { - 0, /* .low_ino */ - sizeof(name_root) - 1, /* .namelen */ - name_root, /* .name */ - 0555 | S_IFDIR, /* .mode */ - 2, /* .nlink */ - 0, /* .uid */ - 0, /* .gid */ - 0, /* .size */ - &proc_dir_inode_operations, /* .ops */ - NULL, /* .get_info */ - NULL, /* .fill_node */ - NULL, /* .next */ - NULL, /* .parent */ - NULL, /* .subdir */ - NULL, /* .data */ - }; - - /* - * /proc/net/router/config - */ - - static struct proc_dir_entry proc_router_conf = - { - 0, /* .low_ino */ - sizeof(name_conf) - 1, /* .namelen */ - name_conf, /* .name */ - 0444 | S_IFREG, /* .mode */ - 1, /* .nlink */ - 0, /* .uid */ - 0, /* .gid */ - 0, /* .size */ - &router_inode, /* .ops */ - &config_get_info, /* .get_info */ - NULL, /* .fill_node */ - NULL, /* .next */ - NULL, /* .parent */ - NULL, /* .subdir */ - NULL, /* .data */ - }; - - /* - * /proc/net/router/status - */ - - static struct proc_dir_entry proc_router_stat = - { - 0, /* .low_ino */ - sizeof(name_stat) - 1, /* .namelen */ - name_stat, /* .name */ - 0444 | S_IFREG, /* .mode */ - 1, /* .nlink */ - 0, /* .uid */ - 0, /* .gid */ - 0, /* .size */ - &router_inode, /* .ops */ - status_get_info, /* .get_info */ - NULL, /* .fill_node */ - NULL, /* .next */ - NULL, /* .parent */ - NULL, /* .subdir */ - NULL, /* .data */ - }; - - /* Strings */ - static char conf_hdr[] = - "Device name | port |IRQ|DMA| mem.addr |mem.size|" - "option1|option2|option3|option4\n"; - - static char stat_hdr[] = - "Device name |protocol|station|interface|clocking|baud rate| MTU |ndev" - "|link state\n"; - - - /* - * Interface functions - */ - - /* - * Initialize router proc interface. - */ - -#ifdef LINUX_2_1 - __initfunc(int wanrouter_proc_init (void)) - { - int err = proc_register(proc_net, &proc_router); - - if (!err) { - proc_register(&proc_router, &proc_router_conf); - proc_register(&proc_router, &proc_router_stat); - } - return err; - } -#else - int wanrouter_proc_init (void) - { - int err = proc_register_dynamic(&proc_net, &proc_router); - - if (!err) { - proc_register_dynamic(&proc_router, &proc_router_conf); - proc_register_dynamic(&proc_router, &proc_router_stat); - } - return err; - } -#endif +static struct file_operations config_fops = +{ + open: config_open, + read: seq_read, + llseek: seq_lseek, + release: seq_release, +}; - /* - * Clean up router proc interface. - */ - - void wanrouter_proc_cleanup (void) - { - proc_unregister(&proc_router, proc_router_conf.low_ino); - proc_unregister(&proc_router, proc_router_stat.low_ino); -#ifdef LINUX_2_1 - proc_unregister(proc_net, proc_router.low_ino); -#else - proc_unregister(&proc_net, proc_router.low_ino); -#endif - } +static struct file_operations status_fops = +{ + open: status_open, + read: seq_read, + llseek: seq_lseek, + release: seq_release, +}; - /* - * Add directory entry for WAN device. - */ - - int wanrouter_proc_add (wan_device_t* wandev) - { - if (wandev->magic != ROUTER_MAGIC) - return -EINVAL; - - memset(&wandev->dent, 0, sizeof(wandev->dent)); - wandev->dent.namelen = strlen(wandev->name); - wandev->dent.name = wandev->name; - wandev->dent.mode = 0444 | S_IFREG; - wandev->dent.nlink = 1; - wandev->dent.ops = &wandev_inode; - wandev->dent.get_info = &wandev_get_info; - wandev->dent.data = wandev; -#ifdef LINUX_2_1 - return proc_register(&proc_router, &wandev->dent); -#else - return proc_register_dynamic(&proc_router, &wandev->dent); -#endif - } +static void *single_start(struct seq_file *p, loff_t *pos) +{ + return *pos == 0 ? p->private : NULL; +} +static void *single_next(struct seq_file *p, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} +static void single_stop(struct seq_file *p, void *v) +{ +} +static int wandev_show(struct seq_file *m, void *v) +{ + wan_device_t *wandev = v; - /* - * Delete directory entry for WAN device. - */ - - int wanrouter_proc_delete(wan_device_t* wandev) - { - if (wandev->magic != ROUTER_MAGIC) - return -EINVAL; - proc_unregister(&proc_router, wandev->dent.low_ino); + if (wandev->magic != ROUTER_MAGIC) return 0; - } - - /****** Proc filesystem entry points ****************************************/ - - /* - * Verify access rights. - */ - static int router_proc_perms (struct inode* inode, int op) - { + if (!wandev->state) { + seq_puts(m, "device is not configured!\n"); return 0; } - /* - * Read router proc directory entry. - * This is universal routine for reading all entries in /proc/net/wanrouter - * directory. Each directory entry contains a pointer to the 'method' for - * preparing data for that entry. - * o verify arguments - * o allocate kernel buffer - * o call get_info() to prepare data - * o copy data to user space - * o release kernel buffer - * - * Return: number of bytes copied to user space (0, if no data) - * <0 error - */ -#ifdef LINUX_2_1 - static ssize_t router_proc_read(struct file* file, char* buf, size_t count, - loff_t *ppos) - { - struct inode *inode = file->f_dentry->d_inode; - struct proc_dir_entry* dent; - char* page; - int pos, offs, len; - - if (count <= 0) - return 0; - - dent = PDE(inode); - if ((dent == NULL) || (dent->get_info == NULL)) + /* Update device statistics */ + if (wandev->update) { + int err = wandev->update(wandev); + if (err == -EAGAIN) { + seq_printf(m, "Device is busy!\n"); return 0; - - page = kmalloc(PROC_BUFSZ, GFP_KERNEL); - if (page == NULL) - return -ENOBUFS; - - pos = dent->get_info(page, dent->data, 0, 0, 0); - offs = file->f_pos; - if (offs < pos) { - len = min_t(unsigned int, pos - offs, count); - if (copy_to_user(buf, (page + offs), len)) { - kfree(page); - return -EFAULT; - } - file->f_pos += len; } - else - len = 0; - kfree(page); - return len; - } - -#else - static int router_proc_read( - struct inode* inode, struct file* file, char* buf, int count) - { - struct proc_dir_entry* dent; - char* page; - int err, pos, offs, len; - - if (count <= 0) + if (err) { + seq_printf(m, "Device is not configured!\n"); return 0; - dent = PDE(inode); - if ((dent == NULL) || (dent->get_info == NULL)) - return -ENODATA; - err = verify_area(VERIFY_WRITE, buf, count); - if (err) return err; - - page = kmalloc(PROC_BUFSZ, GFP_KERNEL); - if (page == NULL) - return -ENOMEM; - - pos = dent->get_info(page, dent->data, 0, 0, 0); - offs = file->f_pos; - if (offs < pos) { - len = min_t(unsigned int, pos - offs, count); - memcpy_tofs((void*)buf, (void*)(page + offs), len); - file->f_pos += len; - } - else len = 0; - kfree(page); - return len; - } -#endif - - - /* - * Prepare data for reading 'Config' entry. - * Return length of data. - */ - - static int config_get_info(char* buf, char** start, off_t offs, int len, - int dummy) - { - int cnt = sizeof(conf_hdr) - 1; - wan_device_t* wandev; - strcpy(buf, conf_hdr); - for (wandev = router_devlist; - wandev && (cnt < (PROC_BUFSZ - 120)); - wandev = wandev->next) { - if (wandev->state) cnt += sprintf(&buf[cnt], - "%-15s|0x%-4X|%3u|%3u| 0x%-8lX |0x%-6X|%7u|%7u|%7u|%7u\n", - wandev->name, - wandev->ioport, - wandev->irq, - wandev->dma, - wandev->maddr, - wandev->msize, - wandev->hw_opt[0], - wandev->hw_opt[1], - wandev->hw_opt[2], - wandev->hw_opt[3]); } - - return cnt; } - /* - * Prepare data for reading 'Status' entry. - * Return length of data. - */ - - static int status_get_info(char* buf, char** start, off_t offs, int len, - int dummy) - { - int cnt = 0; - wan_device_t* wandev; - - //cnt += sprintf(&buf[cnt], "\nSTATUS:\n\n"); - strcpy(&buf[cnt], stat_hdr); - cnt += sizeof(stat_hdr) - 1; - - for (wandev = router_devlist; - wandev && (cnt < (PROC_BUFSZ - 80)); - wandev = wandev->next) { - if (!wandev->state) continue; - cnt += sprintf(&buf[cnt], - "%-15s|%-8s|%-7s|%-9s|%-8s|%9u|%5u|%3u |", - wandev->name, - PROT_DECODE(wandev->config_id), - wandev->config_id == WANCONFIG_FR ? - (wandev->station ? " Node" : " CPE") : - (wandev->config_id == WANCONFIG_X25 ? - (wandev->station ? " DCE" : " DTE") : - (" N/A")), - wandev->interface ? " V.35" : " RS-232", - wandev->clocking ? "internal" : "external", - wandev->bps, - wandev->mtu, - wandev->ndev); - - switch (wandev->state) { - - case WAN_UNCONFIGURED: - cnt += sprintf(&buf[cnt], "%-12s\n", "unconfigured"); - break; - - case WAN_DISCONNECTED: - cnt += sprintf(&buf[cnt], "%-12s\n", "disconnected"); - break; - - case WAN_CONNECTING: - cnt += sprintf(&buf[cnt], "%-12s\n", "connecting"); - break; - - case WAN_CONNECTED: - cnt += sprintf(&buf[cnt], "%-12s\n", "connected"); - break; - - case WAN_FT1_READY: - cnt += sprintf(&buf[cnt], "%-12s\n", "ft1 ready"); - break; - - default: - cnt += sprintf(&buf[cnt], "%-12s\n", "invalid"); - break; - } - } - return cnt; + seq_printf(m, PROC_STATS_FORMAT, + "total packets received", wandev->stats.rx_packets); + seq_printf(m, PROC_STATS_FORMAT, + "total packets transmitted", wandev->stats.tx_packets); + seq_printf(m, PROC_STATS_FORMAT, + "total bytes received", wandev->stats.rx_bytes); + seq_printf(m, PROC_STATS_FORMAT, + "total bytes transmitted", wandev->stats.tx_bytes); + seq_printf(m, PROC_STATS_FORMAT, + "bad packets received", wandev->stats.rx_errors); + seq_printf(m, PROC_STATS_FORMAT, + "packet transmit problems", wandev->stats.tx_errors); + seq_printf(m, PROC_STATS_FORMAT, + "received frames dropped", wandev->stats.rx_dropped); + seq_printf(m, PROC_STATS_FORMAT, + "transmit frames dropped", wandev->stats.tx_dropped); + seq_printf(m, PROC_STATS_FORMAT, + "multicast packets received", wandev->stats.multicast); + seq_printf(m, PROC_STATS_FORMAT, + "transmit collisions", wandev->stats.collisions); + seq_printf(m, PROC_STATS_FORMAT, + "receive length errors", wandev->stats.rx_length_errors); + seq_printf(m, PROC_STATS_FORMAT, + "receiver overrun errors", wandev->stats.rx_over_errors); + seq_printf(m, PROC_STATS_FORMAT, + "CRC errors", wandev->stats.rx_crc_errors); + seq_printf(m, PROC_STATS_FORMAT, + "frame format errors (aborts)", wandev->stats.rx_frame_errors); + seq_printf(m, PROC_STATS_FORMAT, + "receiver fifo overrun", wandev->stats.rx_fifo_errors); + seq_printf(m, PROC_STATS_FORMAT, + "receiver missed packet", wandev->stats.rx_missed_errors); + seq_printf(m, PROC_STATS_FORMAT, + "aborted frames transmitted", wandev->stats.tx_aborted_errors); + return 0; +} +static struct seq_operations wandev_op = { + start: single_start, + next: single_next, + stop: single_stop, + show: wandev_show, +}; +static int wandev_open(struct inode *inode, struct file *file) +{ + int ret = seq_open(file, &wandev_op); + if (!ret) { + struct seq_file *m = file->private_data; + m->private = PDE(inode)->data; } + return ret; +} - /* - * Prepare data for reading entry. - * Return length of data. - * - * On entry, the 'start' argument will contain a pointer to WAN device - * data space. - */ - - static int wandev_get_info(char* buf, char** start, off_t offs, int len, - int dummy) - { - wan_device_t* wandev = (void*)start; - int cnt = 0; - int rslt = 0; +static struct file_operations wandev_fops = +{ + open: wandev_open, + read: seq_read, + llseek: seq_lseek, + release: seq_release, + ioctl: wanrouter_ioctl, +}; - if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC)) - return 0; - if (!wandev->state) - return sprintf(&buf[cnt], "Device is not configured!\n"); +/* + * Initialize router proc interface. + */ - /* Update device statistics */ - if (wandev->update) { +int __init wanrouter_proc_init (void) +{ + struct proc_dir_entry *p; + proc_router = proc_mkdir(ROUTER_NAME, proc_net); + if (!proc_router) + goto fail; + + p = create_proc_entry("config",0,proc_router); + if (!p) + goto fail_config; + p->proc_fops = &config_fops; + p->proc_iops = &router_inode; + p = create_proc_entry("status",0,proc_router); + if (!p) + goto fail_stat; + p->proc_fops = &status_fops; + p->proc_iops = &router_inode; + return 0; +fail_stat: + remove_proc_entry("config", proc_router); +fail_config: + remove_proc_entry(ROUTER_NAME, proc_net); +fail: + return -ENOMEM; +} - rslt = wandev->update(wandev); - if(rslt) { - switch (rslt) { - case -EAGAIN: - return sprintf(&buf[cnt], "Device is busy!\n"); - - default: - return sprintf(&buf[cnt], - "Device is not configured!\n"); - } - } - } +/* + * Clean up router proc interface. + */ - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "total packets received", wandev->stats.rx_packets); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "total packets transmitted", wandev->stats.tx_packets); -#ifdef LINUX_2_1 - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "total bytes received", wandev->stats.rx_bytes); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "total bytes transmitted", wandev->stats.tx_bytes); -#endif - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "bad packets received", wandev->stats.rx_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "packet transmit problems", wandev->stats.tx_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "received frames dropped", wandev->stats.rx_dropped); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "transmit frames dropped", wandev->stats.tx_dropped); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "multicast packets received", wandev->stats.multicast); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "transmit collisions", wandev->stats.collisions); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "receive length errors", wandev->stats.rx_length_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "receiver overrun errors", wandev->stats.rx_over_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "CRC errors", wandev->stats.rx_crc_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "frame format errors (aborts)", wandev->stats.rx_frame_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "receiver fifo overrun", wandev->stats.rx_fifo_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "receiver missed packet", wandev->stats.rx_missed_errors); - cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT, - "aborted frames transmitted", wandev->stats.tx_aborted_errors); +void wanrouter_proc_cleanup (void) +{ + remove_proc_entry("config", proc_router); + remove_proc_entry("status", proc_router); + remove_proc_entry(ROUTER_NAME,proc_net); +} - return cnt; - } +/* + * Add directory entry for WAN device. + */ -#endif /* End of ifdef LINUX_2_4 */ +int wanrouter_proc_add (wan_device_t* wandev) +{ + if (wandev->magic != ROUTER_MAGIC) + return -EINVAL; + + wandev->dent = create_proc_entry(wandev->name, 0, proc_router); + if (!wandev->dent) + return -ENOMEM; + wandev->dent->proc_fops = &wandev_fops; + wandev->dent->proc_iops = &router_inode; + wandev->dent->data = wandev; + return 0; +} +/* + * Delete directory entry for WAN device. + */ + +int wanrouter_proc_delete(wan_device_t* wandev) +{ + if (wandev->magic != ROUTER_MAGIC) + return -EINVAL; + remove_proc_entry(wandev->name, proc_router); + return 0; +} #else @@ -1039,7 +415,6 @@ void wanrouter_proc_cleanup(void) { - return; } int wanrouter_proc_add(wan_device_t *wandev) @@ -1054,34 +429,7 @@ #endif -/*============================================================================ - * Write WAN device ???. - * o Find WAN device associated with this node - */ -#ifdef LINUX_2_0 -static int device_write( - struct inode* inode, struct file* file, const char* buf, int count) -{ - int err = verify_area(VERIFY_READ, buf, count); - struct proc_dir_entry* dent; - wan_device_t* wandev; - - if (err) return err; - - dent = PDE(inode); - if ((dent == NULL) || (dent->data == NULL)) - return -ENODATA; - - wandev = dent->data; - - printk(KERN_ERR "%s: writing %d bytes to %s...\n", - name_root, count, dent->name); - - return 0; -} -#endif - /* * End */ - + diff -Nru a/scripts/cramfs/GNUmakefile b/scripts/cramfs/GNUmakefile --- a/scripts/cramfs/GNUmakefile Thu Mar 7 18:17:43 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -CC = gcc -CFLAGS = -W -Wall -O2 -g -CPPFLAGS = -I../../include -LDLIBS = -lz -PROGS = mkcramfs cramfsck - -all: $(PROGS) - -distclean clean: - rm -f $(PROGS) - -.PHONY: all clean diff -Nru a/scripts/cramfs/cramfsck.c b/scripts/cramfs/cramfsck.c --- a/scripts/cramfs/cramfsck.c Thu Mar 7 18:17:41 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,588 +0,0 @@ -/* - * cramfsck - check a cramfs file system - * - * Copyright (C) 2000-2001 Transmeta 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 - * - * 1999/12/03: Linus Torvalds (cramfs tester and unarchive program) - * 2000/06/03: Daniel Quinlan (CRC and length checking program) - * 2000/06/04: Daniel Quinlan (merged programs, added options, support - * for special files, preserve permissions and - * ownership, cramfs superblock v2, bogus mode - * test, pathname length test, etc.) - * 2000/06/06: Daniel Quinlan (support for holes, pretty-printing, - * symlink size test) - * 2000/07/11: Daniel Quinlan (file length tests, start at offset 0 or 512, - * fsck-compatible exit codes) - * 2000/07/15: Daniel Quinlan (initial support for block devices) - */ - -/* compile-time options */ -#define INCLUDE_FS_TESTS /* include cramfs checking and extraction */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#define _LINUX_STRING_H_ -#include -#include -#include - -static const char *progname = "cramfsck"; - -static int fd; /* ROM image file descriptor */ -static char *filename; /* ROM image filename */ -struct cramfs_super *super; /* just find the cramfs superblock once */ -static int opt_verbose = 0; /* 1 = verbose (-v), 2+ = very verbose (-vv) */ -#ifdef INCLUDE_FS_TESTS -static int opt_extract = 0; /* extract cramfs (-x) */ -char *extract_dir = NULL; /* extraction directory (-x) */ - -unsigned long start_inode = 1 << 28; /* start of first non-root inode */ -unsigned long end_inode = 0; /* end of the directory structure */ -unsigned long start_data = 1 << 28; /* start of the data (256 MB = max) */ -unsigned long end_data = 0; /* end of the data */ -/* true? cramfs_super < start_inode < end_inode <= start_data <= end_data */ -static uid_t euid; /* effective UID */ - -#define PAD_SIZE 512 -#define PAGE_CACHE_SIZE (4096) - -/* Guarantee access to at least 8kB at a time */ -#define ROMBUFFER_BITS 13 -#define ROMBUFFERSIZE (1 << ROMBUFFER_BITS) -#define ROMBUFFERMASK (ROMBUFFERSIZE-1) -static char read_buffer[ROMBUFFERSIZE * 2]; -static unsigned long read_buffer_block = ~0UL; - -/* Uncompressing data structures... */ -static char outbuffer[PAGE_CACHE_SIZE*2]; -z_stream stream; - -#endif /* INCLUDE_FS_TESTS */ - -/* Input status of 0 to print help and exit without an error. */ -static void usage(int status) -{ - FILE *stream = status ? stderr : stdout; - - fprintf(stream, "usage: %s [-hv] [-x dir] file\n" - " -h print this help\n" - " -x dir extract into dir\n" - " -v be more verbose\n" - " file file to test\n", progname); - - exit(status); -} - -#ifdef INCLUDE_FS_TESTS -void print_node(char type, struct cramfs_inode *i, char *name) -{ - char info[10]; - - if (S_ISCHR(i->mode) || (S_ISBLK(i->mode))) { - /* major/minor numbers can be as high as 2^12 or 4096 */ - snprintf(info, 10, "%4d,%4d", major(i->size), minor(i->size)); - } - else { - /* size be as high as 2^24 or 16777216 */ - snprintf(info, 10, "%9d", i->size); - } - - printf("%c %04o %s %5d:%-3d %s\n", - type, i->mode & ~S_IFMT, info, i->uid, i->gid, name); -} - -/* - * Create a fake "blocked" access - */ -static void *romfs_read(unsigned long offset) -{ - unsigned int block = offset >> ROMBUFFER_BITS; - if (block != read_buffer_block) { - read_buffer_block = block; - lseek(fd, block << ROMBUFFER_BITS, SEEK_SET); - read(fd, read_buffer, ROMBUFFERSIZE * 2); - } - return read_buffer + (offset & ROMBUFFERMASK); -} - -static struct cramfs_inode *cramfs_iget(struct cramfs_inode * i) -{ - struct cramfs_inode *inode = malloc(sizeof(struct cramfs_inode)); - *inode = *i; - return inode; -} - -static struct cramfs_inode *iget(unsigned int ino) -{ - return cramfs_iget(romfs_read(ino)); -} - -void iput(struct cramfs_inode *inode) -{ - free(inode); -} - -/* - * Return the offset of the root directory, - * or 0 if none. - */ -static struct cramfs_inode *read_super(void) -{ - unsigned long offset; - - offset = super->root.offset << 2; - if (super->magic != CRAMFS_MAGIC) - return NULL; - if (memcmp(super->signature, CRAMFS_SIGNATURE, sizeof(super->signature)) != 0) - return NULL; - if (offset < sizeof(super)) - return NULL; - return cramfs_iget(&super->root); -} - -static int uncompress_block(void *src, int len) -{ - int err; - - stream.next_in = src; - stream.avail_in = len; - - stream.next_out = (unsigned char *) outbuffer; - stream.avail_out = PAGE_CACHE_SIZE*2; - - inflateReset(&stream); - - err = inflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - fprintf(stderr, "%s: error %d while decompressing! %p(%d)\n", - filename, err, src, len); - exit(4); - } - return stream.total_out; -} - -static void change_file_status(char *path, struct cramfs_inode *i) -{ - struct utimbuf epoch = { 0, 0 }; - - if (euid == 0) { - if (lchown(path, i->uid, i->gid) < 0) { - perror(path); - exit(8); - } - if (S_ISLNK(i->mode)) - return; - if ((S_ISUID | S_ISGID) & i->mode) { - if (chmod(path, i->mode) < 0) { - perror(path); - exit(8); - } - } - } - if (S_ISLNK(i->mode)) - return; - if (utime(path, &epoch) < 0) { - perror(path); - exit(8); - } -} - -static void do_symlink(char *path, struct cramfs_inode *i) -{ - unsigned long offset = i->offset << 2; - unsigned long curr = offset + 4; - unsigned long next = *(u32 *) romfs_read(offset); - unsigned long size; - - if (next > end_data) { - end_data = next; - } - - size = uncompress_block(romfs_read(curr), next - curr); - if (size != i->size) { - fprintf(stderr, "%s: size error in symlink `%s'\n", - filename, path); - exit(4); - } - outbuffer[size] = 0; - if (opt_verbose) { - char *str; - - str = malloc(strlen(outbuffer) + strlen(path) + 5); - strcpy(str, path); - strncat(str, " -> ", 4); - strncat(str, outbuffer, size); - - print_node('l', i, str); - if (opt_verbose > 1) { - printf(" uncompressing block at %ld to %ld (%ld)\n", curr, next, next - curr); - } - } - if (opt_extract) { - symlink(outbuffer, path); - change_file_status(path, i); - } -} - -static void do_special_inode(char *path, struct cramfs_inode *i) -{ - dev_t devtype = 0; - char type; - - if (S_ISCHR(i->mode)) { - devtype = i->size; - type = 'c'; - } - else if (S_ISBLK(i->mode)) { - devtype = i->size; - type = 'b'; - } - else if (S_ISFIFO(i->mode)) - type = 'p'; - else if (S_ISSOCK(i->mode)) - type = 's'; - else { - fprintf(stderr, "%s: bogus mode on `%s' (%o)\n", filename, path, i->mode); - exit(4); - } - - if (opt_verbose) { - print_node(type, i, path); - } - - if (opt_extract) { - if (mknod(path, i->mode, devtype) < 0) { - perror(path); - exit(8); - } - change_file_status(path, i); - } -} - -static void do_uncompress(int fd, unsigned long offset, unsigned long size) -{ - unsigned long curr = offset + 4 * ((size + PAGE_CACHE_SIZE - 1) / PAGE_CACHE_SIZE); - - do { - unsigned long out = PAGE_CACHE_SIZE; - unsigned long next = *(u32 *) romfs_read(offset); - - if (next > end_data) { - end_data = next; - } - - offset += 4; - if (curr == next) { - if (opt_verbose > 1) { - printf(" hole at %ld (%d)\n", curr, PAGE_CACHE_SIZE); - } - if (size < PAGE_CACHE_SIZE) - out = size; - memset(outbuffer, 0x00, out); - } - else { - if (opt_verbose > 1) { - printf(" uncompressing block at %ld to %ld (%ld)\n", curr, next, next - curr); - } - out = uncompress_block(romfs_read(curr), next - curr); - } - if (size >= PAGE_CACHE_SIZE) { - if (out != PAGE_CACHE_SIZE) { - fprintf(stderr, "%s: Non-block (%ld) bytes\n", filename, out); - exit(4); - } - } else { - if (out != size) { - fprintf(stderr, "%s: Non-size (%ld vs %ld) bytes\n", filename, out, size); - exit(4); - } - } - size -= out; - if (opt_extract) { - write(fd, outbuffer, out); - } - curr = next; - } while (size); -} - -static void expand_fs(int pathlen, char *path, struct cramfs_inode *inode) -{ - if (S_ISDIR(inode->mode)) { - int count = inode->size; - unsigned long offset = inode->offset << 2; - char *newpath = malloc(pathlen + 256); - - if (count > 0 && offset < start_inode) { - start_inode = offset; - } - /* XXX - need to check end_inode for empty case? */ - memcpy(newpath, path, pathlen); - newpath[pathlen] = '/'; - pathlen++; - if (opt_verbose) { - print_node('d', inode, path); - } - if (opt_extract) { - mkdir(path, inode->mode); - change_file_status(path, inode); - } - while (count > 0) { - struct cramfs_inode *child = iget(offset); - int size; - int newlen = child->namelen << 2; - - size = sizeof(struct cramfs_inode) + newlen; - count -= size; - - offset += sizeof(struct cramfs_inode); - - memcpy(newpath + pathlen, romfs_read(offset), newlen); - newpath[pathlen + newlen] = 0; - if ((pathlen + newlen) - strlen(newpath) > 3) { - fprintf(stderr, "%s: invalid cramfs--bad path length\n", filename); - exit(4); - } - expand_fs(strlen(newpath), newpath, child); - - offset += newlen; - - if (offset > end_inode) { - end_inode = offset; - } - } - return; - } - if (S_ISREG(inode->mode)) { - int fd = 0; - unsigned long offset = inode->offset << 2; - - if (offset > 0 && offset < start_data) { - start_data = offset; - } - if (opt_verbose) { - print_node('f', inode, path); - } - if (opt_extract) { - fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, inode->mode); - } - if (inode->size) { - do_uncompress(fd, offset, inode->size); - } - if (opt_extract) { - close(fd); - change_file_status(path, inode); - } - return; - } - if (S_ISLNK(inode->mode)) { - unsigned long offset = inode->offset << 2; - - if (offset < start_data) { - start_data = offset; - } - do_symlink(path, inode); - return; - } - else { - do_special_inode(path, inode); - return; - } -} -#endif /* INCLUDE_FS_TESTS */ - -int main(int argc, char **argv) -{ - void *buf; - size_t length; - struct stat st; - u32 crc_old, crc_new; -#ifdef INCLUDE_FS_TESTS - struct cramfs_inode *root; -#endif /* INCLUDE_FS_TESTS */ - int c; /* for getopt */ - int start = 0; - - if (argc) - progname = argv[0]; - - /* command line options */ - while ((c = getopt(argc, argv, "hx:v")) != EOF) { - switch (c) { - case 'h': - usage(0); - case 'x': -#ifdef INCLUDE_FS_TESTS - opt_extract = 1; - extract_dir = malloc(strlen(optarg) + 1); - strcpy(extract_dir, optarg); - break; -#else /* not INCLUDE_FS_TESTS */ - fprintf(stderr, "%s: compiled without -x support\n", - progname); - exit(16); -#endif /* not INCLUDE_FS_TESTS */ - case 'v': - opt_verbose++; - break; - } - } - - if ((argc - optind) != 1) - usage(16); - filename = argv[optind]; - - /* find the physical size of the file or block device */ - if (lstat(filename, &st) < 0) { - perror(filename); - exit(8); - } - fd = open(filename, O_RDONLY); - if (fd < 0) { - perror(filename); - exit(8); - } - if (S_ISBLK(st.st_mode)) { - if (ioctl(fd, BLKGETSIZE, &length) < 0) { - fprintf(stderr, "%s: warning--unable to determine filesystem size \n", filename); - exit(4); - } - length = length * 512; - } - else if (S_ISREG(st.st_mode)) { - length = st.st_size; - } - else { - fprintf(stderr, "%s is not a block device or file\n", filename); - exit(8); - } - - if (length < sizeof(struct cramfs_super)) { - fprintf(stderr, "%s: invalid cramfs--file length too short\n", filename); - exit(4); - } - - if (S_ISBLK(st.st_mode)) { - /* nasty because mmap of block devices fails */ - buf = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - read(fd, buf, length); - } - else { - /* nice and easy */ - buf = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - } - - /* XXX - this could be cleaner... */ - if (((struct cramfs_super *) buf)->magic == CRAMFS_MAGIC) { - start = 0; - super = (struct cramfs_super *) buf; - } - else if (length >= (PAD_SIZE + sizeof(struct cramfs_super)) && - ((((struct cramfs_super *) (buf + PAD_SIZE))->magic == CRAMFS_MAGIC))) - { - start = PAD_SIZE; - super = (struct cramfs_super *) (buf + PAD_SIZE); - } - else { - fprintf(stderr, "%s: invalid cramfs--wrong magic\n", filename); - exit(4); - } - - if (super->flags & CRAMFS_FLAG_FSID_VERSION_2) { - /* length test */ - if (length < super->size) { - fprintf(stderr, "%s: invalid cramfs--file length too short\n", filename); - exit(4); - } - else if (length > super->size) { - fprintf(stderr, "%s: warning--file length too long, padded image?\n", filename); - } - - /* CRC test */ - crc_old = super->fsid.crc; - super->fsid.crc = crc32(0L, Z_NULL, 0); - crc_new = crc32(0L, Z_NULL, 0); - crc_new = crc32(crc_new, (unsigned char *) buf+start, super->size - start); - if (crc_new != crc_old) { - fprintf(stderr, "%s: invalid cramfs--crc error\n", filename); - exit(4); - } - } - else { - fprintf(stderr, "%s: warning--old cramfs image, no CRC\n", - filename); - } - -#ifdef INCLUDE_FS_TESTS - super = (struct cramfs_super *) malloc(sizeof(struct cramfs_super)); - if (((struct cramfs_super *) buf)->magic == CRAMFS_MAGIC) { - memcpy(super, buf, sizeof(struct cramfs_super)); - } - else if (length >= (PAD_SIZE + sizeof(struct cramfs_super)) && - ((((struct cramfs_super *) (buf + PAD_SIZE))->magic == CRAMFS_MAGIC))) - { - memcpy(super, (buf + PAD_SIZE), sizeof(struct cramfs_super)); - } - - munmap(buf, length); - - /* file format test, uses fake "blocked" accesses */ - root = read_super(); - umask(0); - euid = geteuid(); - if (!root) { - fprintf(stderr, "%s: invalid cramfs--bad superblock\n", - filename); - exit(4); - } - stream.next_in = NULL; - stream.avail_in = 0; - inflateInit(&stream); - - if (!extract_dir) { - extract_dir = "root"; - } - - expand_fs(strlen(extract_dir), extract_dir, root); - inflateEnd(&stream); - - if (start_data != 1 << 28 && end_inode != start_data) { - fprintf(stderr, "%s: invalid cramfs--directory data end (%ld) != file data start (%ld)\n", filename, end_inode, start_data); - exit(4); - } - if (super->flags & CRAMFS_FLAG_FSID_VERSION_2) { - if (end_data > super->size) { - fprintf(stderr, "%s: invalid cramfs--invalid file data offset\n", filename); - exit(4); - } - } -#endif /* INCLUDE_FS_TESTS */ - - exit(0); -} diff -Nru a/scripts/cramfs/mkcramfs.c b/scripts/cramfs/mkcramfs.c --- a/scripts/cramfs/mkcramfs.c Thu Mar 7 18:17:41 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,776 +0,0 @@ -/* - * mkcramfs - make a cramfs file system - * - * Copyright (C) 1999-2001 Transmeta 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 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PAD_SIZE 512 /* only 0 and 512 supported by kernel */ - -static const char *progname = "mkcramfs"; - -/* N.B. If you change the disk format of cramfs, please update fs/cramfs/README. */ - -/* Input status of 0 to print help and exit without an error. */ -static void usage(int status) -{ - FILE *stream = status ? stderr : stdout; - - fprintf(stream, "usage: %s [-h] [-e edition] [-i file] [-n name] dirname outfile\n" - " -h print this help\n" - " -E make all warnings errors (non-zero exit status)\n" - " -e edition set edition number (part of fsid)\n" - " -i file insert a file image into the filesystem (requires >= 2.4.0)\n" - " -n name set name of cramfs filesystem\n" - " -p pad by %d bytes for boot code\n" - " -s sort directory entries (old option, ignored)\n" - " -z make explicit holes (requires >= 2.3.39)\n" - " dirname root of the filesystem to be compressed\n" - " outfile output file\n", progname, PAD_SIZE); - - exit(status); -} - -#define PAGE_CACHE_SIZE (4096) -/* The kernel assumes PAGE_CACHE_SIZE as block size. */ -static unsigned int blksize = PAGE_CACHE_SIZE; -static long total_blocks = 0, total_nodes = 1; /* pre-count the root node */ -static int image_length = 0; - -/* - * If opt_holes is set, then mkcramfs can create explicit holes in the - * data, which saves 26 bytes per hole (which is a lot smaller a - * saving than most most filesystems). - * - * Note that kernels up to at least 2.3.39 don't support cramfs holes, - * which is why this is turned off by default. - */ -static int opt_edition = 0; -static int opt_errors = 0; -static int opt_holes = 0; -static int opt_pad = 0; -static char *opt_image = NULL; -static char *opt_name = NULL; - -static int warn_dev, warn_gid, warn_namelen, warn_skip, warn_size, warn_uid; - -#ifndef MIN -# define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b)) -#endif - -/* In-core version of inode / directory entry. */ -struct entry { - /* stats */ - char *name; - unsigned int mode, size, uid, gid; - - /* FS data */ - void *uncompressed; - /* points to other identical file */ - struct entry *same; - unsigned int offset; /* pointer to compressed data in archive */ - unsigned int dir_offset; /* Where in the archive is the directory entry? */ - - /* organization */ - struct entry *child; /* null for non-directories and empty directories */ - struct entry *next; -}; - -/* - * The longest file name component to allow for in the input directory tree. - * Ext2fs (and many others) allow up to 255 bytes. A couple of filesystems - * allow longer (e.g. smbfs 1024), but there isn't much use in supporting - * >255-byte names in the input directory tree given that such names get - * truncated to 255 bytes when written to cramfs. - */ -#define MAX_INPUT_NAMELEN 255 - -static int find_identical_file(struct entry *orig,struct entry *newfile) -{ - if(orig==newfile) return 1; - if(!orig) return 0; - if(orig->size==newfile->size && orig->uncompressed && !memcmp(orig->uncompressed,newfile->uncompressed,orig->size)) { - newfile->same=orig; - return 1; - } - return find_identical_file(orig->child,newfile) || - find_identical_file(orig->next,newfile); -} - -static void eliminate_doubles(struct entry *root,struct entry *orig) { - if(orig) { - if(orig->size && orig->uncompressed) - find_identical_file(root,orig); - eliminate_doubles(root,orig->child); - eliminate_doubles(root,orig->next); - } -} - -/* - * We define our own sorting function instead of using alphasort which - * uses strcoll and changes ordering based on locale information. - */ -static int cramsort (const void *a, const void *b) -{ - return strcmp ((*(const struct dirent **) a)->d_name, - (*(const struct dirent **) b)->d_name); -} - -static unsigned int parse_directory(struct entry *root_entry, const char *name, struct entry **prev, loff_t *fslen_ub) -{ - struct dirent **dirlist; - int totalsize = 0, dircount, dirindex; - char *path, *endpath; - size_t len = strlen(name); - - /* Set up the path. */ - /* TODO: Reuse the parent's buffer to save memcpy'ing and duplication. */ - path = malloc(len + 1 + MAX_INPUT_NAMELEN + 1); - if (!path) { - perror(NULL); - exit(8); - } - memcpy(path, name, len); - endpath = path + len; - *endpath = '/'; - endpath++; - - /* read in the directory and sort */ - dircount = scandir(name, &dirlist, 0, cramsort); - - if (dircount < 0) { - perror(name); - exit(8); - } - - /* process directory */ - for (dirindex = 0; dirindex < dircount; dirindex++) { - struct dirent *dirent; - struct entry *entry; - struct stat st; - int size; - size_t namelen; - - dirent = dirlist[dirindex]; - - /* Ignore "." and ".." - we won't be adding them to the archive */ - if (dirent->d_name[0] == '.') { - if (dirent->d_name[1] == '\0') - continue; - if (dirent->d_name[1] == '.') { - if (dirent->d_name[2] == '\0') - continue; - } - } - namelen = strlen(dirent->d_name); - if (namelen > MAX_INPUT_NAMELEN) { - fprintf(stderr, - "Very long (%u bytes) filename `%s' found.\n" - " Please increase MAX_INPUT_NAMELEN in mkcramfs.c and recompile. Exiting.\n", - namelen, dirent->d_name); - exit(8); - } - memcpy(endpath, dirent->d_name, namelen + 1); - - if (lstat(path, &st) < 0) { - perror(endpath); - warn_skip = 1; - continue; - } - entry = calloc(1, sizeof(struct entry)); - if (!entry) { - perror(NULL); - exit(8); - } - entry->name = strdup(dirent->d_name); - if (!entry->name) { - perror(NULL); - exit(8); - } - if (namelen > 255) { - /* Can't happen when reading from ext2fs. */ - - /* TODO: we ought to avoid chopping in half - multi-byte UTF8 characters. */ - entry->name[namelen = 255] = '\0'; - warn_namelen = 1; - } - entry->mode = st.st_mode; - entry->size = st.st_size; - entry->uid = st.st_uid; - if (entry->uid >= 1 << CRAMFS_UID_WIDTH) - warn_uid = 1; - entry->gid = st.st_gid; - if (entry->gid >= 1 << CRAMFS_GID_WIDTH) - /* TODO: We ought to replace with a default - gid instead of truncating; otherwise there - are security problems. Maybe mode should - be &= ~070. Same goes for uid once Linux - supports >16-bit uids. */ - warn_gid = 1; - size = sizeof(struct cramfs_inode) + ((namelen + 3) & ~3); - *fslen_ub += size; - if (S_ISDIR(st.st_mode)) { - entry->size = parse_directory(root_entry, path, &entry->child, fslen_ub); - } else if (S_ISREG(st.st_mode)) { - /* TODO: We ought to open files in do_compress, one - at a time, instead of amassing all these memory - maps during parse_directory (which don't get used - until do_compress anyway). As it is, we tend to - get EMFILE errors (especially if mkcramfs is run - by non-root). - - While we're at it, do analagously for symlinks - (which would just save a little memory). */ - int fd = open(path, O_RDONLY); - if (fd < 0) { - perror(path); - warn_skip = 1; - continue; - } - if (entry->size) { - if ((entry->size >= 1 << CRAMFS_SIZE_WIDTH)) { - warn_size = 1; - entry->size = (1 << CRAMFS_SIZE_WIDTH) - 1; - } - - entry->uncompressed = mmap(NULL, entry->size, PROT_READ, MAP_PRIVATE, fd, 0); - if (-1 == (int) (long) entry->uncompressed) { - perror("mmap"); - exit(8); - } - } - close(fd); - } else if (S_ISLNK(st.st_mode)) { - entry->uncompressed = malloc(entry->size); - if (!entry->uncompressed) { - perror(NULL); - exit(8); - } - if (readlink(path, entry->uncompressed, entry->size) < 0) { - perror(path); - warn_skip = 1; - continue; - } - } else if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) { - /* maybe we should skip sockets */ - entry->size = 0; - } else { - entry->size = st.st_rdev; - if (entry->size & -(1<size - 1) / blksize + 1); - - /* block pointers & data expansion allowance + data */ - if(entry->size) - *fslen_ub += (4+26)*blocks + entry->size + 3; - } - - /* Link it into the list */ - *prev = entry; - prev = &entry->next; - totalsize += size; - } - free(path); - free(dirlist); /* allocated by scandir() with malloc() */ - return totalsize; -} - -/* Returns sizeof(struct cramfs_super), which includes the root inode. */ -static unsigned int write_superblock(struct entry *root, char *base, int size) -{ - struct cramfs_super *super = (struct cramfs_super *) base; - unsigned int offset = sizeof(struct cramfs_super) + image_length; - - if (opt_pad) { - offset += opt_pad; - } - - super->magic = CRAMFS_MAGIC; - super->flags = CRAMFS_FLAG_FSID_VERSION_2 | CRAMFS_FLAG_SORTED_DIRS; - if (opt_holes) - super->flags |= CRAMFS_FLAG_HOLES; - if (image_length > 0) - super->flags |= CRAMFS_FLAG_SHIFTED_ROOT_OFFSET; - super->size = size; - memcpy(super->signature, CRAMFS_SIGNATURE, sizeof(super->signature)); - - super->fsid.crc = crc32(0L, Z_NULL, 0); - super->fsid.edition = opt_edition; - super->fsid.blocks = total_blocks; - super->fsid.files = total_nodes; - - memset(super->name, 0x00, sizeof(super->name)); - if (opt_name) - strncpy(super->name, opt_name, sizeof(super->name)); - else - strncpy(super->name, "Compressed", sizeof(super->name)); - - super->root.mode = root->mode; - super->root.uid = root->uid; - super->root.gid = root->gid; - super->root.size = root->size; - super->root.offset = offset >> 2; - - return offset; -} - -static void set_data_offset(struct entry *entry, char *base, unsigned long offset) -{ - struct cramfs_inode *inode = (struct cramfs_inode *) (base + entry->dir_offset); -#ifdef DEBUG - assert ((offset & 3) == 0); -#endif /* DEBUG */ - if (offset >= (1 << (2 + CRAMFS_OFFSET_WIDTH))) { - fprintf(stderr, "filesystem too big. Exiting.\n"); - exit(8); - } - inode->offset = (offset >> 2); -} - - -/* - * We do a width-first printout of the directory - * entries, using a stack to remember the directories - * we've seen. - */ -#define MAXENTRIES (100) -static unsigned int write_directory_structure(struct entry *entry, char *base, unsigned int offset) -{ - int stack_entries = 0; - struct entry *entry_stack[MAXENTRIES]; - - for (;;) { - int dir_start = stack_entries; - while (entry) { - struct cramfs_inode *inode = (struct cramfs_inode *) (base + offset); - size_t len = strlen(entry->name); - - entry->dir_offset = offset; - - inode->mode = entry->mode; - inode->uid = entry->uid; - inode->gid = entry->gid; - inode->size = entry->size; - inode->offset = 0; - /* Non-empty directories, regfiles and symlinks will - write over inode->offset later. */ - - offset += sizeof(struct cramfs_inode); - total_nodes++; /* another node */ - memcpy(base + offset, entry->name, len); - /* Pad up the name to a 4-byte boundary */ - while (len & 3) { - *(base + offset + len) = '\0'; - len++; - } - inode->namelen = len >> 2; - offset += len; - - /* TODO: this may get it wrong for chars >= 0x80. - Most filesystems use UTF8 encoding for filenames, - whereas the console is a single-byte character - set like iso-latin-1. */ - printf(" %s\n", entry->name); - if (entry->child) { - if (stack_entries >= MAXENTRIES) { - fprintf(stderr, "Exceeded MAXENTRIES. Raise this value in mkcramfs.c and recompile. Exiting.\n"); - exit(8); - } - entry_stack[stack_entries] = entry; - stack_entries++; - } - entry = entry->next; - } - - /* - * Reverse the order the stack entries pushed during - * this directory, for a small optimization of disk - * access in the created fs. This change makes things - * `ls -UR' order. - */ - { - struct entry **lo = entry_stack + dir_start; - struct entry **hi = entry_stack + stack_entries; - struct entry *tmp; - - while (lo < --hi) { - tmp = *lo; - *lo++ = *hi; - *hi = tmp; - } - } - - /* Pop a subdirectory entry from the stack, and recurse. */ - if (!stack_entries) - break; - stack_entries--; - entry = entry_stack[stack_entries]; - - set_data_offset(entry, base, offset); - printf("'%s':\n", entry->name); - entry = entry->child; - } - return offset; -} - -static int is_zero(char const *begin, unsigned len) -{ - if (opt_holes) - /* Returns non-zero iff the first LEN bytes from BEGIN are - all NULs. */ - return (len-- == 0 || - (begin[0] == '\0' && - (len-- == 0 || - (begin[1] == '\0' && - (len-- == 0 || - (begin[2] == '\0' && - (len-- == 0 || - (begin[3] == '\0' && - memcmp(begin, begin + 4, len) == 0)))))))); - else - /* Never create holes. */ - return 0; -} - -/* - * One 4-byte pointer per block and then the actual blocked - * output. The first block does not need an offset pointer, - * as it will start immediately after the pointer block; - * so the i'th pointer points to the end of the i'th block - * (i.e. the start of the (i+1)'th block or past EOF). - * - * Note that size > 0, as a zero-sized file wouldn't ever - * have gotten here in the first place. - */ -static unsigned int do_compress(char *base, unsigned int offset, char const *name, char *uncompressed, unsigned int size) -{ - unsigned long original_size = size; - unsigned long original_offset = offset; - unsigned long new_size; - unsigned long blocks = (size - 1) / blksize + 1; - unsigned long curr = offset + 4 * blocks; - int change; - - total_blocks += blocks; - - do { - unsigned long len = 2 * blksize; - unsigned int input = size; - if (input > blksize) - input = blksize; - size -= input; - if (!is_zero (uncompressed, input)) { - compress(base + curr, &len, uncompressed, input); - curr += len; - } - uncompressed += input; - - if (len > blksize*2) { - /* (I don't think this can happen with zlib.) */ - printf("AIEEE: block \"compressed\" to > 2*blocklength (%ld)\n", len); - exit(8); - } - - *(u32 *) (base + offset) = curr; - offset += 4; - } while (size); - - curr = (curr + 3) & ~3; - new_size = curr - original_offset; - /* TODO: Arguably, original_size in these 2 lines should be - st_blocks * 512. But if you say that then perhaps - administrative data should also be included in both. */ - change = new_size - original_size; - printf("%6.2f%% (%+d bytes)\t%s\n", - (change * 100) / (double) original_size, change, name); - - return curr; -} - - -/* - * Traverse the entry tree, writing data for every item that has - * non-null entry->compressed (i.e. every symlink and non-empty - * regfile). - */ -static unsigned int write_data(struct entry *entry, char *base, unsigned int offset) -{ - do { - if (entry->uncompressed) { - if(entry->same) { - set_data_offset(entry, base, entry->same->offset); - entry->offset=entry->same->offset; - } else { - set_data_offset(entry, base, offset); - entry->offset=offset; - offset = do_compress(base, offset, entry->name, entry->uncompressed, entry->size); - } - } - else if (entry->child) - offset = write_data(entry->child, base, offset); - entry=entry->next; - } while (entry); - return offset; -} - -static unsigned int write_file(char *file, char *base, unsigned int offset) -{ - int fd; - char *buf; - - fd = open(file, O_RDONLY); - if (fd < 0) { - perror(file); - exit(8); - } - buf = mmap(NULL, image_length, PROT_READ, MAP_PRIVATE, fd, 0); - memcpy(base + offset, buf, image_length); - munmap(buf, image_length); - close (fd); - /* Pad up the image_length to a 4-byte boundary */ - while (image_length & 3) { - *(base + offset + image_length) = '\0'; - image_length++; - } - return (offset + image_length); -} - -/* - * Maximum size fs you can create is roughly 256MB. (The last file's - * data must begin within 256MB boundary but can extend beyond that.) - * - * Note that if you want it to fit in a ROM then you're limited to what the - * hardware and kernel can support (64MB?). - */ -#define MAXFSLEN ((((1 << CRAMFS_OFFSET_WIDTH) - 1) << 2) /* offset */ \ - + (1 << CRAMFS_SIZE_WIDTH) - 1 /* filesize */ \ - + (1 << CRAMFS_SIZE_WIDTH) * 4 / PAGE_CACHE_SIZE /* block pointers */ ) - - -/* - * Usage: - * - * mkcramfs directory-name outfile - * - * where "directory-name" is simply the root of the directory - * tree that we want to generate a compressed filesystem out - * of. - */ -int main(int argc, char **argv) -{ - struct stat st; /* used twice... */ - struct entry *root_entry; - char *rom_image; - ssize_t offset, written; - int fd; - /* initial guess (upper-bound) of required filesystem size */ - loff_t fslen_ub = sizeof(struct cramfs_super); - char const *dirname, *outfile; - u32 crc = crc32(0L, Z_NULL, 0); - int c; /* for getopt */ - - total_blocks = 0; - - if (argc) - progname = argv[0]; - - /* command line options */ - while ((c = getopt(argc, argv, "hEe:i:n:psz")) != EOF) { - switch (c) { - case 'h': - usage(0); - case 'E': - opt_errors = 1; - break; - case 'e': - opt_edition = atoi(optarg); - break; - case 'i': - opt_image = optarg; - if (lstat(opt_image, &st) < 0) { - perror(opt_image); - exit(16); - } - image_length = st.st_size; /* may be padded later */ - fslen_ub += (image_length + 3); /* 3 is for padding */ - break; - case 'n': - opt_name = optarg; - break; - case 'p': - opt_pad = PAD_SIZE; - fslen_ub += PAD_SIZE; - break; - case 's': - /* old option, ignored */ - break; - case 'z': - opt_holes = 1; - break; - } - } - - if ((argc - optind) != 2) - usage(16); - dirname = argv[optind]; - outfile = argv[optind + 1]; - - if (stat(dirname, &st) < 0) { - perror(dirname); - exit(16); - } - fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); - - root_entry = calloc(1, sizeof(struct entry)); - if (!root_entry) { - perror(NULL); - exit(8); - } - root_entry->mode = st.st_mode; - root_entry->uid = st.st_uid; - root_entry->gid = st.st_gid; - - root_entry->size = parse_directory(root_entry, dirname, &root_entry->child, &fslen_ub); - - /* always allocate a multiple of blksize bytes because that's - what we're going to write later on */ - fslen_ub = ((fslen_ub - 1) | (blksize - 1)) + 1; - - if (fslen_ub > MAXFSLEN) { - fprintf(stderr, - "warning: guestimate of required size (upper bound) is %LdMB, but maximum image size is %uMB. We might die prematurely.\n", - fslen_ub >> 20, - MAXFSLEN >> 20); - fslen_ub = MAXFSLEN; - } - - /* find duplicate files. TODO: uses the most inefficient algorithm - possible. */ - eliminate_doubles(root_entry,root_entry); - - /* TODO: Why do we use a private/anonymous mapping here - followed by a write below, instead of just a shared mapping - and a couple of ftruncate calls? Is it just to save us - having to deal with removing the file afterwards? If we - really need this huge anonymous mapping, we ought to mmap - in smaller chunks, so that the user doesn't need nn MB of - RAM free. If the reason is to be able to write to - un-mmappable block devices, then we could try shared mmap - and revert to anonymous mmap if the shared mmap fails. */ - rom_image = mmap(NULL, fslen_ub?fslen_ub:1, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - if (-1 == (int) (long) rom_image) { - perror("ROM image map"); - exit(8); - } - - /* Skip the first opt_pad bytes for boot loader code */ - offset = opt_pad; - memset(rom_image, 0x00, opt_pad); - - /* Skip the superblock and come back to write it later. */ - offset += sizeof(struct cramfs_super); - - /* Insert a file image. */ - if (opt_image) { - printf("Including: %s\n", opt_image); - offset = write_file(opt_image, rom_image, offset); - } - - offset = write_directory_structure(root_entry->child, rom_image, offset); - printf("Directory data: %d bytes\n", offset); - - offset = write_data(root_entry, rom_image, offset); - - /* We always write a multiple of blksize bytes, so that - losetup works. */ - offset = ((offset - 1) | (blksize - 1)) + 1; - printf("Everything: %d kilobytes\n", offset >> 10); - - /* Write the superblock now that we can fill in all of the fields. */ - write_superblock(root_entry, rom_image+opt_pad, offset); - printf("Super block: %d bytes\n", sizeof(struct cramfs_super)); - - /* Put the checksum in. */ - crc = crc32(crc, (rom_image+opt_pad), (offset-opt_pad)); - ((struct cramfs_super *) (rom_image+opt_pad))->fsid.crc = crc; - printf("CRC: %x\n", crc); - - /* Check to make sure we allocated enough space. */ - if (fslen_ub < offset) { - fprintf(stderr, "not enough space allocated for ROM image (%Ld allocated, %d used)\n", - fslen_ub, offset); - exit(8); - } - - written = write(fd, rom_image, offset); - if (written < 0) { - perror("ROM image"); - exit(8); - } - if (offset != written) { - fprintf(stderr, "ROM image write failed (%d %d)\n", written, offset); - exit(8); - } - - /* (These warnings used to come at the start, but they scroll off the - screen too quickly.) */ - if (warn_namelen) /* (can't happen when reading from ext2fs) */ - fprintf(stderr, /* bytes, not chars: think UTF8. */ - "warning: filenames truncated to 255 bytes.\n"); - if (warn_skip) - fprintf(stderr, "warning: files were skipped due to errors.\n"); - if (warn_size) - fprintf(stderr, - "warning: file sizes truncated to %luMB (minus 1 byte).\n", - 1L << (CRAMFS_SIZE_WIDTH - 20)); - if (warn_uid) /* (not possible with current Linux versions) */ - fprintf(stderr, - "warning: uids truncated to %u bits. (This may be a security concern.)\n", - CRAMFS_UID_WIDTH); - if (warn_gid) - fprintf(stderr, - "warning: gids truncated to %u bits. (This may be a security concern.)\n", - CRAMFS_GID_WIDTH); - if (warn_dev) - fprintf(stderr, - "WARNING: device numbers truncated to %u bits. This almost certainly means\n" - "that some device files will be wrong.\n", - CRAMFS_OFFSET_WIDTH); - if (opt_errors && - (warn_namelen||warn_skip||warn_size||warn_uid||warn_gid||warn_dev)) - exit(8); - return 0; -} diff -Nru a/scripts/tkgen.c b/scripts/tkgen.c --- a/scripts/tkgen.c Thu Mar 7 18:17:46 2002 +++ b/scripts/tkgen.c Thu Mar 7 18:17:46 2002 @@ -620,11 +620,11 @@ case token_int: if ( cfg->value && *cfg->value == '$' ) { - int index = get_varnum( cfg->value+1 ); + int i = get_varnum( cfg->value+1 ); printf( "\n" ); - if ( ! vartable[index].global_written ) + if ( ! vartable[i].global_written ) { - global( vartable[index].name ); + global( vartable[i].name ); } printf( "\t" ); } @@ -989,7 +989,6 @@ static void end_proc( struct kconfig * scfg, int menu_num ) { struct kconfig * cfg; - int i; printf( "\n\n\n" ); printf( "\tfocus $w\n" ); @@ -1084,6 +1083,7 @@ { if ( cfg->token == token_tristate ) { + int i; if ( ! vartable[cfg->nameindex].global_written ) { vartable[cfg->nameindex].global_written = 1; diff -Nru a/sound/Config.help b/sound/Config.help --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/Config.help Thu Mar 7 18:17:46 2002 @@ -0,0 +1,7 @@ +CONFIG_SOUND_PRIME + Say 'Y' or 'M' to enable Open Sound System drivers. + +CONFIG_SOUND_SND + Say 'Y' or 'M' to enable Advanced Linux Sound Architecture (ALSA) drivers. + You need to install also alsa-lib and alsa-utils packages available on + the ALSA website at . diff -Nru a/sound/Makefile b/sound/Makefile --- a/sound/Makefile Thu Mar 7 18:17:41 2002 +++ b/sound/Makefile Thu Mar 7 18:17:41 2002 @@ -10,6 +10,7 @@ subdir-$(CONFIG_SOUND_PRIME) += oss ifeq ($(CONFIG_SOUND_PRIME),y) + subdir-m += oss obj-y += oss/sounddrivers.o endif diff -Nru a/sound/core/Config.help b/sound/core/Config.help --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/Config.help Thu Mar 7 18:17:46 2002 @@ -0,0 +1,40 @@ +CONFIG_SND_SEQUENCER + Say 'Y' or 'M' to enable MIDI sequencer and router support. This feature + allows routing and enqueing MIDI events. Events can be processed at given + time. + +CONFIG_SND_SEQ_DUMMY + Say 'Y' or 'M' to enable dummy sequencer client. This client is a simple + midi-through client. All normal input events are redirected to output port + immediately. + +CONFIG_SND_OSSEMUL + Say 'Y' to enable OSS (Open Sound System) API emulation code. + +CONFIG_SND_MIXER_OSS + Say 'Y' or 'M' to enable mixer OSS API emulation (/dev/mixer*). + +CONFIG_SND_PCM_OSS + Say 'Y' or 'M' to enable digital audio (PCM) OSS API emulation (/dev/dsp*). + +CONFIG_SND_SEQUENCER_OSS + Say 'Y' or 'M' to enable OSS sequencer emulation (both /dev/sequencer and + /dev/music interfaces). + +CONFIG_SND_RTCTIMER + Say 'Y' or 'M' to enable RTC timer support for ALSA. ALSA code uses RTC + timer as precise timing source and maps the RTC timer to the ALSA's timer + interface. ALSA sequencer code can also use this timing source. + +CONFIG_SND_VERBOSE_PRINTK + Say 'Y' to enable verbose log messages. These messages will help to + identify source file and position containing printed messages. + +CONFIG_SND_DEBUG + Say 'Y' to enable ALSA debug code. + +CONFIG_SND_DEBUG_MEMORY + Say 'Y' to enable debugging of memory allocation. + +CONFIG_SND_DEBUG_DETECTION + Say 'Y' to enable debugging of hardware detection. diff -Nru a/sound/core/Config.in b/sound/core/Config.in --- a/sound/core/Config.in Thu Mar 7 18:17:42 2002 +++ b/sound/core/Config.in Thu Mar 7 18:17:42 2002 @@ -1,6 +1,14 @@ # ALSA soundcard-configuration -dep_tristate ' RTC Timer support' CONFIG_SND_RTCTIMER $CONFIG_SND +if [ "$CONFIG_X86_64" = "y" -a "$CONFIG_IA32_EMULATION" = "y" ]; then + dep_tristate ' Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL +fi +if [ "$CONFIG_PPC64" = "y" ]; then + dep_tristate ' Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL +fi +if [ "$CONFIG_SPARC64" = "y" ]; then + dep_tristate ' Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL +fi dep_tristate ' Sequencer support' CONFIG_SND_SEQUENCER $CONFIG_SND if [ "$CONFIG_SND_SEQUENCER" != "n" ]; then dep_tristate ' Sequencer dummy client' CONFIG_SND_SEQ_DUMMY $CONFIG_SND_SEQUENCER @@ -13,9 +21,10 @@ dep_tristate ' OSS Sequencer API' CONFIG_SND_SEQUENCER_OSS $CONFIG_SND_SEQUENCER fi fi +dep_tristate ' RTC Timer support' CONFIG_SND_RTCTIMER $CONFIG_SND +bool ' Verbose printk' CONFIG_SND_VERBOSE_PRINTK bool ' Debug' CONFIG_SND_DEBUG if [ "$CONFIG_SND_DEBUG" = "y" ]; then bool ' Debug memory' CONFIG_SND_DEBUG_MEMORY - bool ' Debug full' CONFIG_SND_DEBUG_FULL bool ' Debug detection' CONFIG_SND_DEBUG_DETECT fi diff -Nru a/sound/core/Makefile b/sound/core/Makefile --- a/sound/core/Makefile Thu Mar 7 18:17:39 2002 +++ b/sound/core/Makefile Thu Mar 7 18:17:39 2002 @@ -33,14 +33,21 @@ subdir-$(CONFIG_SND_MIXER_OSS) += oss subdir-$(CONFIG_SND_PCM_OSS) += oss ifeq ($(filter $(subdir-y),oss),oss) + subdir-m += oss obj-y += oss/oss.o endif subdir-$(CONFIG_SND_SEQUENCER) += seq ifeq ($(CONFIG_SND_SEQUENCER),y) - ifeq ($(CONFIG_SND),y) - obj-y += seq/sq.o - endif + subdir-m += seq + obj-y += seq/sq.o +endif + +obj-$(CONFIG_SND_SEQUENCER) += snd-timer.o + +subdir-$(CONFIG_SND_BIT32_EMUL) += ioctl32 +ifeq ($(CONFIG_SND_BIT32_EMUL),y) + obj-y += ioctl32/_ioctl32.o endif # Toplevel Module Dependency @@ -69,9 +76,9 @@ obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_OPTI93X) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_SB) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_SB16) += snd-pcm.o snd-timer.o snd.o snd-hwdep.o snd-rawmidi.o -obj-$(CONFIG_SND_SBAWE) += snd-pcm.o snd-timer.o snd.o snd-hwdep.o snd-rawmidi.o +obj-$(CONFIG_SND_SB8) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_SB16) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_SBAWE) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_ES968) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_WAVEFRONT) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_ALS4000) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o @@ -79,11 +86,11 @@ obj-$(CONFIG_SND_CS4281) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_ENS1370) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_ENS1371) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_ES1938) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ES1938) += snd-pcm.o snd-timer.o snd.o snd-hwdep.o snd-rawmidi.o obj-$(CONFIG_SND_ES1968) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_FM801) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_ICE1712) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_INTEL8X0) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_INTEL8X0) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_MAESTRO3) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_RME96) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_SONICVIBES) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o @@ -98,6 +105,10 @@ obj-$(CONFIG_SND_TRIDENT) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_YMFPCI) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_POWERMAC) += snd-pcm.o snd-timer.o snd.o +ifeq ($(CONFIG_SND_SB16_CSP),y) + obj-$(CONFIG_SND_SB16) += snd-hwdep.o + obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o +endif include $(TOPDIR)/Rules.make diff -Nru a/sound/core/control.c b/sound/core/control.c --- a/sound/core/control.c Thu Mar 7 18:17:39 2002 +++ b/sound/core/control.c Thu Mar 7 18:17:39 2002 @@ -166,7 +166,7 @@ ev->mask = mask; list_add_tail(&ev->list, &ctl->events); } else { - snd_printk("No memory available to allocate event\n"); + snd_printk(KERN_ERR "No memory available to allocate event\n"); } _found: wake_up(&ctl->change_sleep); diff -Nru a/sound/core/device.c b/sound/core/device.c --- a/sound/core/device.c Thu Mar 7 18:17:37 2002 +++ b/sound/core/device.c Thu Mar 7 18:17:37 2002 @@ -58,11 +58,11 @@ list_del(&dev->list); if (dev->state == SNDRV_DEV_REGISTERED && dev->ops->dev_unregister) { if (dev->ops->dev_unregister(dev)) - snd_printk("device unregister failure\n"); + snd_printk(KERN_ERR "device unregister failure\n"); } else { if (dev->ops->dev_free) { if (dev->ops->dev_free(dev)) - snd_printk("device free failure\n"); + snd_printk(KERN_ERR "device free failure\n"); } } snd_magic_kfree(dev); diff -Nru a/sound/core/hwdep.c b/sound/core/hwdep.c --- a/sound/core/hwdep.c Thu Mar 7 18:17:37 2002 +++ b/sound/core/hwdep.c Thu Mar 7 18:17:37 2002 @@ -321,7 +321,7 @@ if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device, &snd_hwdep_reg, name)) < 0) { - snd_printk("unable to register hardware dependant device %i:%i\n", + snd_printk(KERN_ERR "unable to register hardware dependant device %i:%i\n", hwdep->card->number, hwdep->device); snd_hwdep_devices[idx] = NULL; up(®ister_mutex); @@ -331,12 +331,12 @@ hwdep->ossreg = 0; if (hwdep->oss_type >= 0) { if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) { - snd_printk ("only hwdep device 0 can be registered as OSS direct FM device!\n"); + snd_printk (KERN_WARNING "only hwdep device 0 can be registered as OSS direct FM device!\n"); } else { if (snd_register_oss_device(hwdep->oss_type, hwdep->card, hwdep->device, &snd_hwdep_reg, hwdep->oss_dev) < 0) { - snd_printk("unable to register OSS compatibility device %i:%i\n", + snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n", hwdep->card->number, hwdep->device); } else { snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, hwdep->card->number, hwdep->name); diff -Nru a/sound/core/info.c b/sound/core/info.c --- a/sound/core/info.c Thu Mar 7 18:17:43 2002 +++ b/sound/core/info.c Thu Mar 7 18:17:43 2002 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -54,7 +55,7 @@ "memdebug", "detect", "devices", - "oss-devices", + "oss", "cards", "timers", "synth", @@ -123,6 +124,9 @@ struct proc_dir_entry *snd_proc_root = NULL; struct proc_dir_entry *snd_proc_dev = NULL; snd_info_entry_t *snd_seq_root = NULL; +#ifdef CONFIG_SND_OSSEMUL +snd_info_entry_t *snd_oss_root = NULL; +#endif #ifdef LINUX_2_2 static void snd_info_fill_inode(struct inode *inode, int fill) @@ -162,31 +166,45 @@ { snd_info_private_data_t *data; struct snd_info_entry *entry; + loff_t ret; data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); entry = data->entry; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3) + lock_kernel(); +#endif switch (entry->content) { case SNDRV_INFO_CONTENT_TEXT: switch (orig) { case 0: /* SEEK_SET */ file->f_pos = offset; - return file->f_pos; + ret = file->f_pos; + goto out; case 1: /* SEEK_CUR */ file->f_pos += offset; - return file->f_pos; + ret = file->f_pos; + goto out; case 2: /* SEEK_END */ default: - return -EINVAL; + ret = -EINVAL; + goto out; } break; case SNDRV_INFO_CONTENT_DATA: - if (entry->c.ops->llseek) - return entry->c.ops->llseek(entry, + if (entry->c.ops->llseek) { + ret = entry->c.ops->llseek(entry, data->file_private_data, file, offset, orig); + goto out; + } break; } - return -ENXIO; + ret = -ENXIO; +out: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3) + unlock_kernel(); +#endif + return ret; } static ssize_t snd_info_entry_read(struct file *file, char *buffer, @@ -420,7 +438,7 @@ if (entry->c.text.write) { entry->c.text.write(entry, data->wbuffer); if (data->wbuffer->error) { - snd_printk("data write error to %s (%i)\n", + snd_printk(KERN_WARNING "data write error to %s (%i)\n", entry->name, data->wbuffer->error); } @@ -613,6 +631,19 @@ if (p == NULL) return -ENOMEM; snd_proc_dev = p; +#ifdef CONFIG_SND_OSSEMUL + { + snd_info_entry_t *entry; + if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + snd_oss_root = entry; + } +#endif #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) { snd_info_entry_t *entry; @@ -653,6 +684,10 @@ #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) if (snd_seq_root) snd_info_unregister(snd_seq_root); +#endif +#ifdef CONFIG_SND_OSSEMUL + if (snd_oss_root) + snd_info_unregister(snd_oss_root); #endif snd_remove_proc_entry(snd_proc_root, snd_proc_dev); snd_remove_proc_entry(&proc_root, snd_proc_root); diff -Nru a/sound/core/info_oss.c b/sound/core/info_oss.c --- a/sound/core/info_oss.c Thu Mar 7 18:17:39 2002 +++ b/sound/core/info_oss.c Thu Mar 7 18:17:39 2002 @@ -114,7 +114,7 @@ snd_info_entry_t *entry; memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings)); - if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", NULL)) != NULL) { + if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->c.text.read_size = 2048; entry->c.text.read = snd_sndstat_proc_read; diff -Nru a/sound/core/init.c b/sound/core/init.c --- a/sound/core/init.c Thu Mar 7 18:17:39 2002 +++ b/sound/core/init.c Thu Mar 7 18:17:39 2002 @@ -74,7 +74,7 @@ if (idx < 0 || idx >= snd_ecards_limit) { write_unlock(&snd_card_rwlock); if (idx >= snd_ecards_limit) - snd_printk("card %i is out of range (0-%i)\n", idx, snd_ecards_limit-1); + snd_printk(KERN_ERR "card %i is out of range (0-%i)\n", idx, snd_ecards_limit-1); goto __error; } snd_cards_lock |= 1 << idx; /* lock it */ @@ -140,26 +140,26 @@ snd_mixer_oss_notify_callback(card, 1); #endif if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) { - snd_printk("unable to free all devices (pre)\n"); + snd_printk(KERN_ERR "unable to free all devices (pre)\n"); /* Fatal, but this situation should never occur */ } if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) { - snd_printk("unable to free all devices (normal)\n"); + snd_printk(KERN_ERR "unable to free all devices (normal)\n"); /* Fatal, but this situation should never occur */ } if (snd_ctl_unregister(card) < 0) { - snd_printk("unable to unregister control minors\n"); + snd_printk(KERN_ERR "unable to unregister control minors\n"); /* Not fatal error */ } if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) { - snd_printk("unable to free all devices (post)\n"); + snd_printk(KERN_ERR "unable to free all devices (post)\n"); /* Fatal, but this situation should never occur */ } if (card->private_free) card->private_free(card); snd_info_free_entry(card->proc_id); if (snd_info_card_unregister(card) < 0) { - snd_printk("unable to unregister card info\n"); + snd_printk(KERN_WARNING "unable to unregister card info\n"); /* Not fatal error */ } write_lock(&snd_card_rwlock); diff -Nru a/sound/core/ioctl32/Makefile b/sound/core/ioctl32/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/ioctl32/Makefile Thu Mar 7 18:17:46 2002 @@ -0,0 +1,17 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := _ioctl32.o + +list-multi := snd-ioctl32.o + +snd-ioctl32-objs := ioctl32.o pcm32.o rawmidi32.o timer32.o hwdep32.o + +obj-$(CONFIG_SND_BIT32_EMUL) += snd-ioctl32.o + +include $(TOPDIR)/Rules.make + +snd-ioctl32.o: $(snd-ioctl32-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ioctl32-objs) diff -Nru a/sound/core/ioctl32/hwdep32.c b/sound/core/ioctl32/hwdep32.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/ioctl32/hwdep32.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,37 @@ +/* + * 32bit -> 64bit ioctl wrapper for timer API + * Copyright (c) by Takashi Iwai + * + * 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 __NO_VERSION__ +#include +#include +#include +#include +#include +#include "ioctl32.h" + +#define AP(x) snd_ioctl32_##x + +struct ioctl32_mapper hwdep_mappers[] = { + { SNDRV_HWDEP_IOCTL_PVERSION, NULL }, + { SNDRV_HWDEP_IOCTL_INFO, NULL }, + { SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_HWDEP_INFO, NULL }, + { 0 }, +}; diff -Nru a/sound/core/ioctl32/ioctl32.c b/sound/core/ioctl32/ioctl32.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/ioctl32/ioctl32.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,357 @@ +/* + * 32bit -> 64bit ioctl wrapper for control API + * Copyright (c) by Takashi Iwai + * + * 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 __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include "ioctl32.h" + +/* + * register/unregister mappers + * exported for other modules + */ + +int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)); +int unregister_ioctl32_conversion(unsigned int cmd); + + +int snd_ioctl32_register(struct ioctl32_mapper *mappers) +{ + int err; + struct ioctl32_mapper *m; + + lock_kernel(); + for (m = mappers; m->cmd; m++) { + err = register_ioctl32_conversion(m->cmd, m->handler); + if (err < 0) { + unlock_kernel(); + return err; + } + m->registered++; + } + return 0; +} + +void snd_ioctl32_unregister(struct ioctl32_mapper *mappers) +{ + struct ioctl32_mapper *m; + + lock_kernel(); + for (m = mappers; m->cmd; m++) { + if (m->registered) { + unregister_ioctl32_conversion(m->cmd); + m->registered = 0; + } + } + unlock_kernel(); +} + + +/* + * Controls + */ + +struct sndrv_ctl_elem_list32 { + u32 offset; + u32 space; + u32 used; + u32 count; + u32 pids; + unsigned char reserved[50]; +}; + +#define CVT_sndrv_ctl_elem_list()\ +{\ + COPY(offset);\ + COPY(space);\ + COPY(used);\ + COPY(count);\ + CPTR(pids);\ +} + +DEFINE_ALSA_IOCTL(ctl_elem_list); + + +/* + * control element info + * it uses union, so the things are not easy.. + */ + +struct sndrv_ctl_elem_info32 { + struct sndrv_ctl_elem_id id; // the size of struct is same + s32 type; + u32 access; + u32 count; + s32 owner; + union { + struct { + s32 min; + s32 max; + s32 step; + } integer; + struct { + u32 items; + u32 item; + char name[64]; + } enumerated; + unsigned char reserved[128]; + } value; + unsigned char reserved[64]; +}; + +static int snd_ioctl32_ctl_elem_info(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file) +{ + struct sndrv_ctl_elem_info data; + struct sndrv_ctl_elem_info32 data32; + int err; + + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; + memset(&data, 0, sizeof(data)); + data.id = data32.id; + err = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, (unsigned long)&data); + if (err < 0) + return err; + /* restore info to 32bit */ + data32.type = data.type; + data32.access = data.access; + data32.count = data.count; + data32.owner = data.owner; + switch (data.type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + data32.value.integer.min = data.value.integer.min; + data32.value.integer.max = data.value.integer.min; + data32.value.integer.step = data.value.integer.step; + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + data32.value.enumerated.items = data.value.enumerated.items; + data32.value.enumerated.item = data.value.enumerated.item; + memcpy(data32.value.enumerated.name, data.value.enumerated.name, + sizeof(data.value.enumerated.name)); + break; + default: + break; + } + if (copy_to_user((void*)arg, &data32, sizeof(data32))) + return -EFAULT; + return err; +} + + +struct sndrv_ctl_elem_value32 { + struct sndrv_ctl_elem_id id; + unsigned int indirect: 1; + union { + union { + s32 value[128]; + u32 value_ptr; + } integer; + union { + u32 item[128]; + u32 item_ptr; + } enumerated; + union { + unsigned char data[512]; + u32 data_ptr; + } bytes; + struct sndrv_aes_iec958 iec958; + } value; + unsigned char reserved[128]; +}; + + +/* hmm, it's so hard to retrieve the value type from the control id.. */ +static int get_ctl_type(struct file *file, snd_ctl_elem_id_t *id) +{ + snd_ctl_file_t *ctl; + snd_kcontrol_t *kctl; + snd_ctl_elem_info_t info; + int err; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + + read_lock(&ctl->card->control_rwlock); + kctl = snd_ctl_find_id(ctl->card, id); + if (! kctl) { + read_unlock(&ctl->card->control_rwlock); + return -ENXIO; + } + info.id = *id; + err = kctl->info(kctl, &info); + if (err >= 0) + err = info.type; + read_unlock(&ctl->card->control_rwlock); + return err; +} + + +static int snd_ioctl32_ctl_elem_value(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file) +{ + // too big? + struct sndrv_ctl_elem_value data; + struct sndrv_ctl_elem_value32 data32; + int err, i; + int type; + /* FIXME: check the sane ioctl.. */ + + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; + memset(&data, 0, sizeof(data)); + data.id = data32.id; + data.indirect = data32.indirect; + if (data.indirect) /* FIXME: this is not correct for long arrays */ + data.value.integer.value_ptr = (void*)TO_PTR(data32.value.integer.value_ptr); + type = get_ctl_type(file, &data.id); + if (type < 0) + return type; + if (! data.indirect) { + switch (type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + for (i = 0; i < 128; i++) + data.value.integer.value[i] = data32.value.integer.value[i]; + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + for (i = 0; i < 128; i++) + data.value.enumerated.item[i] = data32.value.enumerated.item[i]; + break; + case SNDRV_CTL_ELEM_TYPE_BYTES: + memcpy(data.value.bytes.data, data32.value.bytes.data, + sizeof(data.value.bytes.data)); + break; + case SNDRV_CTL_ELEM_TYPE_IEC958: + data.value.iec958 = data32.value.iec958; + break; + default: + break; + } + } + + err = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, (unsigned long)&data); + if (err < 0) + return err; + /* restore info to 32bit */ + if (! data.indirect) { + switch (type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + for (i = 0; i < 128; i++) + data.value.integer.value[i] = data32.value.integer.value[i]; + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + for (i = 0; i < 128; i++) + data.value.enumerated.item[i] = data32.value.enumerated.item[i]; + break; + case SNDRV_CTL_ELEM_TYPE_BYTES: + memcpy(data.value.bytes.data, data32.value.bytes.data, + sizeof(data.value.bytes.data)); + break; + case SNDRV_CTL_ELEM_TYPE_IEC958: + data.value.iec958 = data32.value.iec958; + break; + default: + break; + } + } + if (copy_to_user((void*)arg, &data32, sizeof(data32))) + return -EFAULT; + return err; +} + + +/* + */ + +#define AP(x) snd_ioctl32_##x + +static struct ioctl32_mapper control_mappers[] = { + /* controls (without rawmidi, hwdep, timer releated ones) */ + { SNDRV_CTL_IOCTL_PVERSION, NULL }, + { SNDRV_CTL_IOCTL_CARD_INFO , NULL }, + { SNDRV_CTL_IOCTL_ELEM_LIST, AP(ctl_elem_list) }, + { SNDRV_CTL_IOCTL_ELEM_INFO, AP(ctl_elem_info) }, + { SNDRV_CTL_IOCTL_ELEM_READ, AP(ctl_elem_value) }, + { SNDRV_CTL_IOCTL_ELEM_WRITE, AP(ctl_elem_value) }, + { SNDRV_CTL_IOCTL_ELEM_LOCK, NULL }, + { SNDRV_CTL_IOCTL_ELEM_UNLOCK, NULL }, + { SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, NULL }, + { SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_PCM_INFO, NULL }, + { SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, NULL }, + { SNDRV_CTL_IOCTL_POWER, NULL }, + { SNDRV_CTL_IOCTL_POWER_STATE, NULL }, + { 0 } +}; + + +/* + */ + +extern struct ioctl32_mapper pcm_mappers[]; +extern struct ioctl32_mapper rawmidi_mappers[]; +extern struct ioctl32_mapper timer_mappers[]; +extern struct ioctl32_mapper hwdep_mappers[]; + +static void snd_ioctl32_done(void) +{ + snd_ioctl32_unregister(hwdep_mappers); + snd_ioctl32_unregister(timer_mappers); + snd_ioctl32_unregister(rawmidi_mappers); + snd_ioctl32_unregister(pcm_mappers); + snd_ioctl32_unregister(control_mappers); +} + +static int __init snd_ioctl32_init(void) +{ + int err; + + err = snd_ioctl32_register(control_mappers); + if (err < 0) + return err; + err = snd_ioctl32_register(pcm_mappers); + if (err < 0) { + snd_ioctl32_done(); + return err; + } + err = snd_ioctl32_register(rawmidi_mappers); + if (err < 0) { + snd_ioctl32_done(); + return err; + } + err = snd_ioctl32_register(timer_mappers); + if (err < 0) { + snd_ioctl32_done(); + return err; + } + err = snd_ioctl32_register(hwdep_mappers); + if (err < 0) { + snd_ioctl32_done(); + return err; + } +} + +module_init(snd_ioctl32_init) +module_exit(snd_ioctl32_done) diff -Nru a/sound/core/ioctl32/ioctl32.h b/sound/core/ioctl32/ioctl32.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/ioctl32/ioctl32.h Thu Mar 7 18:17:46 2002 @@ -0,0 +1,79 @@ +/* + * 32bit -> 64bit ioctl helpers + * Copyright (c) by Takashi Iwai + * + * 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 file registers the converters from 32-bit ioctls to 64-bit ones. + * The converter assumes that a 32-bit user-pointer can be casted by A(x) + * macro to a valid 64-bit pointer which is accessible via copy_from/to_user. + * + */ + +#ifndef __ALSA_IOCTL32_H +#define __ALSA_IOCTL32_H + +#define TO_PTR(x) A(x) + +#define COPY(x) (dst->x = src->x) +#define CPTR(x) (dst->x = (typeof(dst->x))A(src->x)) + +#define convert_from_32(type, dstp, srcp)\ +{\ + struct sndrv_##type *dst = dstp;\ + struct sndrv_##type##32 *src = srcp;\ + CVT_##sndrv_##type();\ +} + +#define convert_to_32(type, dstp, srcp)\ +{\ + struct sndrv_##type *src = srcp;\ + struct sndrv_##type##32 *dst = dstp;\ + CVT_##sndrv_##type();\ +} + + +#define DEFINE_ALSA_IOCTL(type) \ +static int snd_ioctl32_##type(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file)\ +{\ + struct sndrv_##type##32 data32;\ + struct sndrv_##type data;\ + int err;\ + if (copy_from_user(&data32, (void*)arg, sizeof(data32)))\ + return -EFAULT;\ + memset(&data, 0, sizeof(data));\ + convert_from_32(type, &data, &data32);\ + err = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, (unsigned long)&data);\ + if (err < 0)\ + return err;\ + if (cmd & (_IOC_READ << _IOC_DIRSHIFT)) {\ + convert_to_32(type, &data32, &data);\ + if (copy_to_user((void*)arg, &data32, sizeof(data32)))\ + return -EFAULT;\ + }\ + return err;\ +} + +struct ioctl32_mapper { + unsigned int cmd; + int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp); + int registered; +}; + +int snd_ioctl32_register(struct ioctl32_mapper *mappers); +void snd_ioctl32_unregister(struct ioctl32_mapper *mappers); + +#endif /* __ALSA_IOCTL32_H */ diff -Nru a/sound/core/ioctl32/pcm32.c b/sound/core/ioctl32/pcm32.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/ioctl32/pcm32.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,296 @@ +/* + * 32bit -> 64bit ioctl wrapper for PCM API + * Copyright (c) by Takashi Iwai + * + * 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 __NO_VERSION__ +#include +#include +#include +#include +#include "ioctl32.h" + + +/* wrapper for sndrv_pcm_[us]frames */ +struct sndrv_pcm_sframes_str { + sndrv_pcm_sframes_t val; +}; +struct sndrv_pcm_sframes_str32 { + s32 val; +}; +struct sndrv_pcm_uframes_str { + sndrv_pcm_uframes_t val; +}; +struct sndrv_pcm_uframes_str32 { + u32 val; +}; + +#define CVT_sndrv_pcm_sframes_str() { COPY(val); } +#define CVT_sndrv_pcm_uframes_str() { COPY(val); } + + +struct sndrv_interval32 { + u32 min, max; + unsigned int openmin:1, + openmax:1, + integer:1, + empty:1; +}; + +struct sndrv_pcm_hw_params32 { + u32 flags; + u32 masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; + struct sndrv_interval32 intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + u32 rmask; + u32 cmask; + u32 info; + u32 msbits; + u32 rate_num; + u32 rate_den; + u32 fifo_size; + unsigned char reserved[64]; +}; + +#define numberof(array) (sizeof(array)/sizeof(array[0])) + +#define CVT_sndrv_pcm_hw_params()\ +{\ + int i;\ + COPY(flags);\ + for (i = 0; i < numberof(dst->masks); i++)\ + COPY(masks[i]);\ + for (i = 0; i < numberof(dst->intervals); i++) {\ + COPY(intervals[i].min);\ + COPY(intervals[i].max);\ + COPY(intervals[i].openmin);\ + COPY(intervals[i].openmax);\ + COPY(intervals[i].integer);\ + COPY(intervals[i].empty);\ + }\ + COPY(rmask);\ + COPY(cmask);\ + COPY(info);\ + COPY(msbits);\ + COPY(rate_num);\ + COPY(rate_den);\ + COPY(fifo_size);\ +} + +struct sndrv_pcm_sw_params32 { + s32 tstamp_mode; + u32 period_step; + u32 sleep_min; + u32 avail_min; + u32 xfer_align; + u32 start_threshold; + u32 stop_threshold; + u32 silence_threshold; + u32 silence_size; + u32 boundary; + unsigned char reserved[64]; +}; + +#define CVT_sndrv_pcm_sw_params()\ +{\ + COPY(tstamp_mode);\ + COPY(period_step);\ + COPY(sleep_min);\ + COPY(avail_min);\ + COPY(xfer_align);\ + COPY(start_threshold);\ + COPY(stop_threshold);\ + COPY(silence_threshold);\ + COPY(silence_size);\ + COPY(boundary);\ +} + +struct sndrv_pcm_channel_info32 { + u32 channel; + u32 offset; + u32 first; + u32 step; +}; + +#define CVT_sndrv_pcm_channel_info()\ +{\ + COPY(channel);\ + COPY(offset);\ + COPY(first);\ + COPY(step);\ +} + +struct timeval32 { + s32 tv_sec; + s32 tv_usec; +}; + +struct sndrv_pcm_status32 { + s32 state; + struct timeval32 trigger_tstamp; + struct timeval32 tstamp; + u32 appl_ptr; + u32 hw_ptr; + s32 delay; + u32 avail; + u32 avail_max; + u32 overrange; + s32 suspended_state; + unsigned char reserved[60]; +}; + +#define CVT_sndrv_pcm_status()\ +{\ + COPY(state);\ + COPY(trigger_tstamp.tv_sec);\ + COPY(trigger_tstamp.tv_usec);\ + COPY(tstamp.tv_sec);\ + COPY(tstamp.tv_usec);\ + COPY(appl_ptr);\ + COPY(hw_ptr);\ + COPY(delay);\ + COPY(avail);\ + COPY(avail_max);\ + COPY(overrange);\ + COPY(suspended_state);\ +} + +struct sndrv_xferi32 { + s32 result; + u32 buf; + u32 frames; +}; + +#define CVT_sndrv_xferi()\ +{\ + COPY(result);\ + CPTR(buf);\ + COPY(frames);\ +} + +DEFINE_ALSA_IOCTL(pcm_uframes_str); +DEFINE_ALSA_IOCTL(pcm_sframes_str); +DEFINE_ALSA_IOCTL(pcm_hw_params); +DEFINE_ALSA_IOCTL(pcm_sw_params); +DEFINE_ALSA_IOCTL(pcm_channel_info); +DEFINE_ALSA_IOCTL(pcm_status); +DEFINE_ALSA_IOCTL(xferi); + +/* snd_xfern needs remapping of bufs */ +struct sndrv_xfern32 { + s32 result; + u32 bufs; /* this is void **; */ + u32 frames; +}; + +/* + * xfern ioctl nees to copy (up to) 128 pointers on stack. + * although we may pass the copied pointers through f_op->ioctl, but the ioctl + * handler there expands again the same 128 pointers on stack, so it is better + * to handle the function (calling pcm_readv/writev) directly in this handler. + */ +static int snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + struct sndrv_xfern32 data32, *srcptr = (struct sndrv_xfern32*)arg; + void *bufs[128]; + int err = 0, ch, i; + u32 *bufptr; + + /* FIXME: need to check whether fop->ioctl is sane */ + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL && substream->runtime, return -ENXIO); + + /* check validty of the command */ + switch (cmd) { + case SNDRV_PCM_IOCTL_WRITEN_FRAMES: + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -EINVAL; + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + case SNDRV_PCM_IOCTL_READN_FRAMES: + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return -EINVAL; + break; + } + if ((ch = substream->runtime->channels) > 128) + return -EINVAL; + if (get_user(data32.frames, &srcptr->frames)) + return -EFAULT; + __get_user(data32.bufs, &srcptr->bufs); + bufptr = (u32*)TO_PTR(data32.bufs); + for (i = 0; i < ch; i++) { + u32 ptr; + if (get_user(ptr, bufptr)) + return -EFAULT; + bufs[ch] = (void*)TO_PTR(ptr); + bufptr++; + } + switch (cmd) { + case SNDRV_PCM_IOCTL_WRITEN_FRAMES: + err = snd_pcm_lib_writev(substream, bufs, data32.frames); + break; + case SNDRV_PCM_IOCTL_READN_FRAMES: + err = snd_pcm_lib_readv(substream, bufs, data32.frames); + break; + } + + if (err < 0) + return err; + if (put_user(err, &srcptr->result)) + return -EFAULT; + return err < 0 ? err : 0; +} + + +#define AP(x) snd_ioctl32_##x + +struct ioctl32_mapper pcm_mappers[] = { + { SNDRV_PCM_IOCTL_PVERSION, NULL }, + { SNDRV_PCM_IOCTL_INFO, NULL }, + { SNDRV_PCM_IOCTL_HW_REFINE, AP(pcm_hw_params) }, + { SNDRV_PCM_IOCTL_HW_PARAMS, AP(pcm_hw_params) }, + { SNDRV_PCM_IOCTL_HW_FREE, NULL }, + { SNDRV_PCM_IOCTL_SW_PARAMS, AP(pcm_sw_params) }, + { SNDRV_PCM_IOCTL_STATUS, AP(pcm_status) }, + { SNDRV_PCM_IOCTL_DELAY, AP(pcm_sframes_str) }, + { SNDRV_PCM_IOCTL_CHANNEL_INFO, AP(pcm_channel_info) }, + { SNDRV_PCM_IOCTL_PREPARE, NULL }, + { SNDRV_PCM_IOCTL_RESET, NULL }, + { SNDRV_PCM_IOCTL_START, NULL }, + { SNDRV_PCM_IOCTL_DROP, NULL }, + { SNDRV_PCM_IOCTL_DRAIN, NULL }, + { SNDRV_PCM_IOCTL_PAUSE, NULL }, + { SNDRV_PCM_IOCTL_REWIND, AP(pcm_uframes_str) }, + { SNDRV_PCM_IOCTL_RESUME, NULL }, + { SNDRV_PCM_IOCTL_XRUN, NULL }, + { SNDRV_PCM_IOCTL_WRITEI_FRAMES, AP(xferi) }, + { SNDRV_PCM_IOCTL_READI_FRAMES, AP(xferi) }, + { SNDRV_PCM_IOCTL_WRITEN_FRAMES, AP(xfern) }, + { SNDRV_PCM_IOCTL_READN_FRAMES, AP(xfern) }, + { SNDRV_PCM_IOCTL_LINK, NULL }, + { SNDRV_PCM_IOCTL_UNLINK, NULL }, + + { SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_PCM_INFO, NULL }, + { SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, NULL }, + + { 0 }, +}; diff -Nru a/sound/core/ioctl32/rawmidi32.c b/sound/core/ioctl32/rawmidi32.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/ioctl32/rawmidi32.c Thu Mar 7 18:17:47 2002 @@ -0,0 +1,86 @@ +/* + * 32bit -> 64bit ioctl wrapper for raw MIDI API + * Copyright (c) by Takashi Iwai + * + * 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 __NO_VERSION__ +#include +#include +#include +#include +#include +#include "ioctl32.h" + +struct sndrv_rawmidi_params32 { + s32 stream; + u32 buffer_size; + u32 avail_min; + unsigned int no_active_sensing: 1; + unsigned char reserved[16]; +}; + +#define CVT_sndrv_rawmidi_params()\ +{\ + COPY(stream);\ + COPY(buffer_size);\ + COPY(avail_min);\ + COPY(no_active_sensing);\ +} + +struct timeval32 { + s32 tv_sec; + s32 tv_usec; +}; + +struct sndrv_rawmidi_status32 { + s32 stream; + struct timeval32 tstamp; + u32 avail; + u32 xruns; + unsigned char reserved[16]; +}; + +#define CVT_sndrv_rawmidi_status()\ +{\ + COPY(stream);\ + COPY(tstamp.tv_sec);\ + COPY(tstamp.tv_usec);\ + COPY(avail);\ + COPY(xruns);\ +} + +DEFINE_ALSA_IOCTL(rawmidi_params); +DEFINE_ALSA_IOCTL(rawmidi_status); + + +#define AP(x) snd_ioctl32_##x + +struct ioctl32_mapper rawmidi_mappers[] = { + { SNDRV_RAWMIDI_IOCTL_PVERSION, NULL }, + { SNDRV_RAWMIDI_IOCTL_INFO, NULL }, + { SNDRV_RAWMIDI_IOCTL_PARAMS, AP(rawmidi_params) }, + { SNDRV_RAWMIDI_IOCTL_STATUS, AP(rawmidi_status) }, + { SNDRV_RAWMIDI_IOCTL_DROP, NULL }, + { SNDRV_RAWMIDI_IOCTL_DRAIN, NULL }, + + { SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_RAWMIDI_INFO, NULL }, + { SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE, NULL }, + + { 0 }, +}; diff -Nru a/sound/core/ioctl32/timer32.c b/sound/core/ioctl32/timer32.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/ioctl32/timer32.c Thu Mar 7 18:17:46 2002 @@ -0,0 +1,93 @@ +/* + * 32bit -> 64bit ioctl wrapper for timer API + * Copyright (c) by Takashi Iwai + * + * 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 __NO_VERSION__ +#include +#include +#include +#include +#include +#include "ioctl32.h" + +struct sndrv_timer_info32 { + u32 flags; + s32 card; + unsigned char id[64]; + unsigned char name[80]; + u32 ticks; + u32 resolution; + unsigned char reserved[64]; +}; + +#define CVT_sndrv_timer_info()\ +{\ + COPY(flags);\ + COPY(card);\ + memcpy(dst->id, src->id, sizeof(src->id));\ + memcpy(dst->name, src->name, sizeof(src->name));\ + COPY(ticks);\ + COPY(resolution);\ +} + +struct timeval32 { + s32 tv_sec; + s32 tv_usec; +}; + +struct sndrv_timer_status32 { + struct timeval32 tstamp; + u32 resolution; + u32 lost; + u32 overrun; + u32 queue; + unsigned char reserved[64]; +}; + +#define CVT_sndrv_timer_status()\ +{\ + COPY(tstamp.tv_sec);\ + COPY(tstamp.tv_usec);\ + COPY(resolution);\ + COPY(lost);\ + COPY(overrun);\ + COPY(queue);\ +} + +DEFINE_ALSA_IOCTL(timer_info); +DEFINE_ALSA_IOCTL(timer_status); + + +/* + */ + +#define AP(x) snd_ioctl32_##x + +struct ioctl32_mapper timer_mappers[] = { + { SNDRV_TIMER_IOCTL_PVERSION, NULL }, + { SNDRV_TIMER_IOCTL_NEXT_DEVICE, NULL }, + { SNDRV_TIMER_IOCTL_SELECT, NULL }, + { SNDRV_TIMER_IOCTL_INFO, AP(timer_info) }, + { SNDRV_TIMER_IOCTL_PARAMS, NULL }, + { SNDRV_TIMER_IOCTL_STATUS, AP(timer_status) }, + { SNDRV_TIMER_IOCTL_START, NULL }, + { SNDRV_TIMER_IOCTL_STOP, NULL }, + { SNDRV_TIMER_IOCTL_CONTINUE, NULL }, + { 0 }, +}; diff -Nru a/sound/core/memory.c b/sound/core/memory.c --- a/sound/core/memory.c Thu Mar 7 18:17:37 2002 +++ b/sound/core/memory.c Thu Mar 7 18:17:37 2002 @@ -68,28 +68,28 @@ struct list_head *head; struct snd_alloc_track *t; if (snd_alloc_pages > 0) - snd_printk("Not freed snd_alloc_pages = %li\n", snd_alloc_pages); + snd_printk(KERN_ERR "Not freed snd_alloc_pages = %li\n", snd_alloc_pages); if (snd_alloc_kmalloc > 0) - snd_printk("Not freed snd_alloc_kmalloc = %li\n", snd_alloc_kmalloc); + snd_printk(KERN_ERR "Not freed snd_alloc_kmalloc = %li\n", snd_alloc_kmalloc); if (snd_alloc_vmalloc > 0) - snd_printk("Not freed snd_alloc_vmalloc = %li\n", snd_alloc_vmalloc); + snd_printk(KERN_ERR "Not freed snd_alloc_vmalloc = %li\n", snd_alloc_vmalloc); for (head = snd_alloc_kmalloc_list.prev; head != &snd_alloc_kmalloc_list; head = head->prev) { t = list_entry(head, struct snd_alloc_track, list); if (t->magic != KMALLOC_MAGIC) { - snd_printk("Corrupted kmalloc\n"); + snd_printk(KERN_ERR "Corrupted kmalloc\n"); break; } - snd_printk("kmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); + snd_printk(KERN_ERR "kmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); } for (head = snd_alloc_vmalloc_list.prev; head != &snd_alloc_vmalloc_list; head = head->prev) { t = list_entry(head, struct snd_alloc_track, list); if (t->magic != VMALLOC_MAGIC) { - snd_printk("Corrupted vmalloc\n"); + snd_printk(KERN_ERR "Corrupted vmalloc\n"); break; } - snd_printk("vmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); + snd_printk(KERN_ERR "vmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); } } @@ -125,12 +125,12 @@ unsigned long flags; struct snd_alloc_track *t; if (obj == NULL) { - snd_printk("null kfree (called from %p)\n", __builtin_return_address(0)); + snd_printk(KERN_WARNING "null kfree (called from %p)\n", __builtin_return_address(0)); return; } t = snd_alloc_track_entry(obj); if (t->magic != KMALLOC_MAGIC) { - snd_printk("bad kfree (called from %p)\n", __builtin_return_address(0)); + snd_printk(KERN_WARNING "bad kfree (called from %p)\n", __builtin_return_address(0)); return; } spin_lock_irqsave(&snd_alloc_kmalloc_lock, flags); @@ -166,7 +166,7 @@ { unsigned long *ptr = _ptr; if (ptr == NULL) { - snd_printk("null snd_magic_kfree (called from %p)\n", __builtin_return_address(0)); + snd_printk(KERN_WARNING "null snd_magic_kfree (called from %p)\n", __builtin_return_address(0)); return; } *--ptr = 0; @@ -174,7 +174,7 @@ struct snd_alloc_track *t; t = snd_alloc_track_entry(ptr); if (t->magic != KMALLOC_MAGIC) { - snd_printk("bad snd_magic_kfree (called from %p)\n", __builtin_return_address(0)); + snd_printk(KERN_ERR "bad snd_magic_kfree (called from %p)\n", __builtin_return_address(0)); return; } } @@ -204,12 +204,12 @@ { struct snd_alloc_track *t; if (obj == NULL) { - snd_printk("null vfree (called from %p)\n", __builtin_return_address(0)); + snd_printk(KERN_WARNING "null vfree (called from %p)\n", __builtin_return_address(0)); return; } t = snd_alloc_track_entry(obj); if (t->magic != VMALLOC_MAGIC) { - snd_printk("bad vfree (called from %p)\n", __builtin_return_address(0)); + snd_printk(KERN_ERR "bad vfree (called from %p)\n", __builtin_return_address(0)); return; } spin_lock(&snd_alloc_vmalloc_lock); @@ -516,29 +516,36 @@ /* * A dirty hack... when the kernel code is fixed this should be removed. * - * since pci_alloc_consistent always tries GFP_ATOMIC when the requested + * since pci_alloc_consistent always tries GFP_DMA when the requested * pci memory region is below 32bit, it happens quite often that even * 2 order or pages cannot be allocated. * - * so in the following, GFP_ATOMIC is used only when the first allocation + * so in the following, GFP_DMA is used only when the first allocation * doesn't match the requested region. */ +#ifdef __i386__ +#define get_phys_addr(x) virt_to_phys(x) +#else /* ppc */ +#define get_phys_addr(x) virt_to_bus(x) +#endif void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) { void *ret; int gfp = GFP_ATOMIC; + if (hwdev == NULL) + gfp |= GFP_DMA; ret = (void *)__get_free_pages(gfp, get_order(size)); if (ret) { - if (hwdev && ((virt_to_phys(ret) + size - 1) & ~hwdev->dma_mask)) { + if (hwdev && ((get_phys_addr(ret) + size - 1) & ~hwdev->dma_mask)) { free_pages((unsigned long)ret, get_order(size)); ret = (void *)__get_free_pages(gfp | GFP_DMA, get_order(size)); } } if (ret) { memset(ret, 0, size); - *dma_handle = virt_to_phys(ret); + *dma_handle = get_phys_addr(ret); } return ret; } diff -Nru a/sound/core/misc.c b/sound/core/misc.c --- a/sound/core/misc.c Thu Mar 7 18:17:41 2002 +++ b/sound/core/misc.c Thu Mar 7 18:17:41 2002 @@ -36,3 +36,18 @@ name[idx] = '\0'; return 0; } + +#ifdef CONFIG_SND_VERBOSE_PRINTK +int snd_verbose_printk(const char *file, int line, const char *format) +{ + if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') { + char tmp[] = "<0>ALSA %s:%d: "; + tmp[1] = format[1]; + printk("%sALSA %s:%d: ", tmp, file, line); + return 1; + } else { + printk("ALSA %s:%d: ", file, line); + return 0; + } +} +#endif diff -Nru a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c --- a/sound/core/oss/pcm_oss.c Thu Mar 7 18:17:41 2002 +++ b/sound/core/oss/pcm_oss.c Thu Mar 7 18:17:41 2002 @@ -516,7 +516,6 @@ snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel) { snd_pcm_runtime_t *runtime = substream->runtime; - mm_segment_t fs; int ret; while (1) { if (runtime->status->state == SNDRV_PCM_STATE_XRUN || @@ -525,11 +524,14 @@ if (ret < 0) break; } - if (in_kernel) + if (in_kernel) { + mm_segment_t fs; fs = snd_enter_user(); - ret = snd_pcm_lib_write(substream, ptr, frames); - if (in_kernel) + ret = snd_pcm_lib_write(substream, ptr, frames); snd_leave_user(fs); + } else { + ret = snd_pcm_lib_write(substream, ptr, frames); + } if (ret != -EPIPE && ret != -ESTRPIPE) break; /* test, if we can't store new data, because the stream */ @@ -543,7 +545,6 @@ snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t frames, int in_kernel) { snd_pcm_runtime_t *runtime = substream->runtime; - mm_segment_t fs; int ret; while (1) { if (runtime->status->state == SNDRV_PCM_STATE_XRUN || @@ -556,11 +557,15 @@ if (ret < 0) break; } - if (in_kernel) - fs = snd_enter_user(); ret = snd_pcm_lib_read(substream, ptr, frames); - if (in_kernel) + if (in_kernel) { + mm_segment_t fs; + fs = snd_enter_user(); + ret = snd_pcm_lib_read(substream, ptr, frames); snd_leave_user(fs); + } else { + ret = snd_pcm_lib_read(substream, ptr, frames); + } if (ret != -EPIPE && ret != -ESTRPIPE) break; } @@ -570,7 +575,6 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) { snd_pcm_runtime_t *runtime = substream->runtime; - mm_segment_t fs; int ret; while (1) { if (runtime->status->state == SNDRV_PCM_STATE_XRUN || @@ -579,13 +583,17 @@ if (ret < 0) break; } - if (in_kernel) + if (in_kernel) { + mm_segment_t fs; fs = snd_enter_user(); - ret = snd_pcm_lib_writev(substream, bufs, frames); - if (in_kernel) + ret = snd_pcm_lib_writev(substream, bufs, frames); snd_leave_user(fs); + } else { + ret = snd_pcm_lib_writev(substream, bufs, frames); + } if (ret != -EPIPE && ret != -ESTRPIPE) break; + /* test, if we can't store new data, because the stream */ /* has not been started */ if (runtime->status->state == SNDRV_PCM_STATE_PREPARED) @@ -597,7 +605,6 @@ snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) { snd_pcm_runtime_t *runtime = substream->runtime; - mm_segment_t fs; int ret; while (1) { if (runtime->status->state == SNDRV_PCM_STATE_XRUN || @@ -610,11 +617,14 @@ if (ret < 0) break; } - if (in_kernel) + if (in_kernel) { + mm_segment_t fs; fs = snd_enter_user(); - ret = snd_pcm_lib_readv(substream, bufs, frames); - if (in_kernel) + ret = snd_pcm_lib_readv(substream, bufs, frames); snd_leave_user(fs); + } else { + ret = snd_pcm_lib_readv(substream, bufs, frames); + } if (ret != -EPIPE && ret != -ESTRPIPE) break; } diff -Nru a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c --- a/sound/core/pcm_lib.c Thu Mar 7 18:17:43 2002 +++ b/sound/core/pcm_lib.c Thu Mar 7 18:17:43 2002 @@ -104,7 +104,7 @@ snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp); #ifdef CONFIG_SND_DEBUG if (pos > runtime->buffer_size) { - snd_printk("BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); + snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); } else #endif snd_runtime_check(pos <= runtime->buffer_size, return 0); @@ -170,7 +170,7 @@ snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp); #ifdef CONFIG_SND_DEBUG if (pos > runtime->buffer_size) { - snd_printk("BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); + snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); } else #endif snd_runtime_check(pos <= runtime->buffer_size, return 0); diff -Nru a/sound/core/pcm_native.c b/sound/core/pcm_native.c --- a/sound/core/pcm_native.c Thu Mar 7 18:17:41 2002 +++ b/sound/core/pcm_native.c Thu Mar 7 18:17:41 2002 @@ -837,6 +837,42 @@ #endif /* CONFIG_PM */ +static int snd_pcm_xrun(snd_pcm_substream_t *substream) +{ + snd_card_t *card = substream->pcm->card; + snd_pcm_runtime_t *runtime = substream->runtime; + int result; + + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + _xrun_recovery: + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + result = 0; /* already there */ + break; + case SNDRV_PCM_STATE_RUNNING: + result = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + result = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + goto _xrun_recovery; + default: + result = -EBADFD; + } + _end: + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + return result; +} + static inline int snd_pcm_pre_reset(snd_pcm_substream_t * substream, int state) { snd_pcm_runtime_t *runtime = substream->runtime; @@ -1999,6 +2035,10 @@ return snd_pcm_link(substream, (long) arg); case SNDRV_PCM_IOCTL_UNLINK: return snd_pcm_unlink(substream); + case SNDRV_PCM_IOCTL_RESUME: + return snd_pcm_resume(substream); + case SNDRV_PCM_IOCTL_XRUN: + return snd_pcm_xrun(substream); } snd_printd("unknown ioctl = 0x%x\n", cmd); return -ENOTTY; @@ -2071,8 +2111,6 @@ return snd_pcm_playback_drop(substream); case SNDRV_PCM_IOCTL_DELAY: return snd_pcm_playback_delay(substream, (snd_pcm_sframes_t*) arg); - case SNDRV_PCM_IOCTL_RESUME: - return snd_pcm_resume(substream); } return snd_pcm_common_ioctl1(substream, cmd, arg); } @@ -2136,8 +2174,6 @@ return snd_pcm_capture_drop(substream); case SNDRV_PCM_IOCTL_DELAY: return snd_pcm_capture_delay(substream, (snd_pcm_sframes_t*) arg); - case SNDRV_PCM_IOCTL_RESUME: - return snd_pcm_resume(substream); } return snd_pcm_common_ioctl1(substream, cmd, arg); } diff -Nru a/sound/core/rawmidi.c b/sound/core/rawmidi.c --- a/sound/core/rawmidi.c Thu Mar 7 18:17:39 2002 +++ b/sound/core/rawmidi.c Thu Mar 7 18:17:39 2002 @@ -769,7 +769,7 @@ } #ifdef CONFIG_SND_DEBUG default: - snd_printk("rawmidi: unknown command = 0x%x\n", cmd); + snd_printk(KERN_WARNING "rawmidi: unknown command = 0x%x\n", cmd); #endif } return -ENOTTY; @@ -1408,7 +1408,7 @@ if ((err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device, &snd_rawmidi_reg, name)) < 0) { - snd_printk("unable to register rawmidi device %i:%i\n", rmidi->card->number, rmidi->device); + snd_printk(KERN_ERR "unable to register rawmidi device %i:%i\n", rmidi->card->number, rmidi->device); snd_rawmidi_devices[idx] = NULL; up(®ister_mutex); return err; @@ -1425,7 +1425,7 @@ if (rmidi->device == snd_midi_map[rmidi->card->number]) { if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 0, &snd_rawmidi_reg, name) < 0) { - snd_printk("unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 0); + snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 0); } else { rmidi->ossreg++; snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number, rmidi->name); @@ -1434,7 +1434,7 @@ if (rmidi->device == snd_amidi_map[rmidi->card->number]) { if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 1, &snd_rawmidi_reg, name) < 0) { - snd_printk("unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 1); + snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 1); } else { rmidi->ossreg++; } @@ -1532,11 +1532,11 @@ /* check device map table */ for (i = 0; i < SNDRV_CARDS; i++) { if (snd_midi_map[i] < 0 || snd_midi_map[i] >= SNDRV_RAWMIDI_DEVICES) { - snd_printk("invalid midi_map[%d] = %d\n", i, snd_midi_map[i]); + snd_printk(KERN_ERR "invalid midi_map[%d] = %d\n", i, snd_midi_map[i]); snd_midi_map[i] = 0; } if (snd_amidi_map[i] < 0 || snd_amidi_map[i] >= SNDRV_RAWMIDI_DEVICES) { - snd_printk("invalid amidi_map[%d] = %d\n", i, snd_amidi_map[i]); + snd_printk(KERN_ERR "invalid amidi_map[%d] = %d\n", i, snd_amidi_map[i]); snd_amidi_map[i] = 1; } } diff -Nru a/sound/core/rtctimer.c b/sound/core/rtctimer.c --- a/sound/core/rtctimer.c Thu Mar 7 18:17:40 2002 +++ b/sound/core/rtctimer.c Thu Mar 7 18:17:41 2002 @@ -18,19 +18,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * - *================================================================ - * For enabling this timer, apply the patch file to your kernel. - * The configure script checks the patch automatically. - * The patches, rtc-xxx.dif, are found under utils/patches, where - * xxx is the kernel version. - *================================================================ - * */ #include #include #include +#include #include #include #include @@ -59,7 +52,7 @@ /* - * The harware depenant description for this timer. + * The hardware dependent description for this timer. */ static struct _snd_timer_hardware rtc_hw = { flags: SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO, @@ -72,7 +65,7 @@ int rtctimer_freq = RTC_FREQ; /* frequency */ static snd_timer_t *rtctimer; -static volatile int rtc_inc = 0; +static atomic_t rtc_inc = ATOMIC_INIT(0); static rtc_task_t rtc_task; /* tasklet */ @@ -83,6 +76,10 @@ static int rtctimer_open(snd_timer_t *t) { + err = rtc_register(&rtc_task); + if (err < 0) + return err; + t->private_data = &rtc_task; MOD_INC_USE_COUNT; return 0; } @@ -90,6 +87,11 @@ static int rtctimer_close(snd_timer_t *t) { + rtc_task_t *rtc = t->private_data; + if (rtc) { + rtc_unregister(rtc); + t->private_data = NULL; + } MOD_DEC_USE_COUNT; return 0; } @@ -101,7 +103,7 @@ snd_assert(rtc != NULL, return -EINVAL); rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq); rtc_control(rtc, RTC_PIE_ON, 0); - rtc_inc = 0; + atomic_set(&rtc_inc, 0); return 0; } @@ -119,12 +121,15 @@ */ static void rtctimer_interrupt(void *private_data) { - rtc_inc++; + atomic_inc(&rtc_inc); #ifdef USE_TASKLET tasklet_hi_schedule(&rtc_tq); #else - snd_timer_interrupt((snd_timer_t*)private_data, rtc_inc); - rtc_inc = 0; + { + int ticks = atomic_read(&rtc_inc); + snd_timer_interrupt((snd_timer_t*)private_data, ticks); + atomic_sub(ticks, &rtc_inc); + } #endif /* USE_TASKLET */ } @@ -132,20 +137,16 @@ static void rtctimer_interrupt2(unsigned long private_data) { snd_timer_t *timer = (snd_timer_t *)private_data; + int ticks; + snd_assert(timer != NULL, return); do { - snd_timer_interrupt(timer, 1); - } while (--rtc_inc > 0); + ticks = atomic_read(&rtc_inc); + snd_timer_interrupt(timer, ticks); + } while (!atomic_sub_and_test(ticks, &rtc_inc)); } #endif /* USE_TASKLET */ -static void rtctimer_private_free(snd_timer_t *timer) -{ - rtc_task_t *rtc = timer->private_data; - if (rtc) - rtc_unregister(rtc); -} - /* * ENTRY functions @@ -156,13 +157,13 @@ snd_timer_t *timer; if (rtctimer_freq < 2 || rtctimer_freq > 8192) { - snd_printk("rtctimer: invalid frequency %d\n", rtctimer_freq); + snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", rtctimer_freq); return -EINVAL; } for (order = 1; rtctimer_freq > order; order <<= 1) ; if (rtctimer_freq != order) { - snd_printk("rtctimer: invalid frequency %d\n", rtctimer_freq); + snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", rtctimer_freq); return -EINVAL; } @@ -179,23 +180,16 @@ timer->hw = rtc_hw; timer->hw.resolution = NANO_SEC / rtctimer_freq; - /* register RTC callback */ + /* set up RTC callback */ rtc_task.func = rtctimer_interrupt; rtc_task.private_data = timer; - err = rtc_register(&rtc_task); - if (err < 0) { - snd_timer_global_free(timer); - return err; - } - timer->private_data = &rtc_task; - timer->private_free = rtctimer_private_free; err = snd_timer_global_register(timer); if (err < 0) { snd_timer_global_free(timer); return err; } - rtctimer = timer; + rtctimer = timer; /* remember this */ return 0; } @@ -210,7 +204,7 @@ /* - * exported stuffs + * exported stuff */ module_init(rtctimer_init) module_exit(rtctimer_exit) diff -Nru a/sound/core/seq/Makefile b/sound/core/seq/Makefile --- a/sound/core/seq/Makefile Thu Mar 7 18:17:46 2002 +++ b/sound/core/seq/Makefile Thu Mar 7 18:17:46 2002 @@ -32,6 +32,8 @@ snd-seq-dummy-objs := seq_dummy.o snd-seq-virmidi-objs := seq_virmidi.o +obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o +obj-$(CONFIG_SND_SEQUENCER_OSS) += snd-seq-midi-event.o obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o # Toplevel Module Dependency @@ -56,7 +58,7 @@ obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(CONFIG_SND_OPTI93X) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o -obj-$(CONFIG_SND_SB) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_SB8) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(CONFIG_SND_SB16) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(CONFIG_SND_SBAWE) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o snd-seq-virmidi.o obj-$(CONFIG_SND_ES968) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o @@ -66,17 +68,18 @@ obj-$(CONFIG_SND_CS4281) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(CONFIG_SND_ENS1370) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o obj-$(CONFIG_SND_ENS1371) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o -obj-$(CONFIG_SND_ES1938) += snd-seq-device.o snd-seq-midi-emul.o snd-seq.o snd-seq-instr.o +obj-$(CONFIG_SND_ES1938) += snd-seq-device.o snd-seq-midi-emul.o snd-seq.o snd-seq-instr.o snd-seq-midi.o snd-seq-midi-event.o obj-$(CONFIG_SND_ES1968) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o obj-$(CONFIG_SND_FM801) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(CONFIG_SND_ICE1712) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_INTEL8X0) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o obj-$(CONFIG_SND_SONICVIBES) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(CONFIG_SND_VIA686) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o obj-$(CONFIG_SND_ALI5451) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o obj-$(CONFIG_SND_CS46XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o obj-$(CONFIG_SND_EMU10K1) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-virmidi.o obj-$(CONFIG_SND_TRIDENT) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o -obj-$(CONFIG_SND_YMFPCI) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_YMFPCI) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o include $(TOPDIR)/Rules.make diff -Nru a/sound/core/seq/instr/Makefile b/sound/core/seq/instr/Makefile --- a/sound/core/seq/instr/Makefile Thu Mar 7 18:17:41 2002 +++ b/sound/core/seq/instr/Makefile Thu Mar 7 18:17:41 2002 @@ -33,7 +33,7 @@ obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-ainstr-fm.o obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-ainstr-fm.o obj-$(CONFIG_SND_OPTI93X) += snd-ainstr-fm.o -obj-$(CONFIG_SND_SB) += snd-ainstr-fm.o +obj-$(CONFIG_SND_SB8) += snd-ainstr-fm.o obj-$(CONFIG_SND_SB16) += snd-ainstr-fm.o obj-$(CONFIG_SND_SBAWE) += snd-ainstr-fm.o obj-$(CONFIG_SND_WAVEFRONT) += snd-ainstr-fm.o @@ -44,6 +44,7 @@ obj-$(CONFIG_SND_FM801) += snd-ainstr-fm.o obj-$(CONFIG_SND_SONICVIBES) += snd-ainstr-fm.o obj-$(CONFIG_SND_TRIDENT) += snd-ainstr-simple.o +obj-$(CONFIG_SND_YMFPCI) += snd-ainstr-fm.o include $(TOPDIR)/Rules.make diff -Nru a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c --- a/sound/core/seq/oss/seq_oss.c Thu Mar 7 18:17:40 2002 +++ b/sound/core/seq/oss/seq_oss.c Thu Mar 7 18:17:40 2002 @@ -222,7 +222,7 @@ NULL, 0, &seq_oss_reg, SNDRV_SEQ_OSS_DEVNAME)) < 0) { - snd_printk("can't register device seq\n"); + snd_printk(KERN_ERR "can't register device seq\n"); up(®ister_mutex); return rc; } @@ -230,7 +230,7 @@ NULL, 0, &seq_oss_reg, SNDRV_SEQ_OSS_DEVNAME)) < 0) { - snd_printk("can't register device music\n"); + snd_printk(KERN_ERR "can't register device music\n"); snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0); up(®ister_mutex); return rc; @@ -245,11 +245,10 @@ { down(®ister_mutex); debug_printk(("device unregistered\n")); - if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0) - - snd_printk("error unregister device music\n"); + if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0) + snd_printk(KERN_ERR "error unregister device music\n"); if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0) < 0) - snd_printk("error unregister device seq\n"); + snd_printk(KERN_ERR "error unregister device seq\n"); up(®ister_mutex); } diff -Nru a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c --- a/sound/core/seq/oss/seq_oss_init.c Thu Mar 7 18:17:38 2002 +++ b/sound/core/seq/oss/seq_oss_init.c Thu Mar 7 18:17:38 2002 @@ -184,7 +184,7 @@ seq_oss_devinfo_t *dp; if ((dp = snd_kcalloc(sizeof(*dp), GFP_KERNEL)) == NULL) { - snd_printk("can't malloc device info\n"); + snd_printk(KERN_ERR "can't malloc device info\n"); return -ENOMEM; } @@ -193,7 +193,7 @@ break; } if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) { - snd_printk("too many applications\n"); + snd_printk(KERN_ERR "too many applications\n"); return -ENOMEM; } @@ -209,14 +209,14 @@ snd_seq_oss_midi_setup(dp); if (dp->synth_opened == 0 && dp->max_mididev == 0) { - snd_printk("no device found\n"); + snd_printk(KERN_ERR "no device found\n"); kfree(dp); return -ENODEV; } /* create port */ if ((rc = create_port(dp)) < 0) { - snd_printk("can't create port\n"); + snd_printk(KERN_ERR "can't create port\n"); free_devinfo(dp); return rc; } @@ -259,7 +259,7 @@ /* initialize timer */ if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) { - snd_printk("can't alloc timer\n"); + snd_printk(KERN_ERR "can't alloc timer\n"); delete_seq_queue(dp); delete_port(dp); return -ENOMEM; diff -Nru a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c --- a/sound/core/seq/oss/seq_oss_midi.c Thu Mar 7 18:17:36 2002 +++ b/sound/core/seq/oss/seq_oss_midi.c Thu Mar 7 18:17:36 2002 @@ -173,7 +173,7 @@ * allocate midi info record */ if ((mdev = snd_kcalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) { - snd_printk("can't malloc midi info\n"); + snd_printk(KERN_ERR "can't malloc midi info\n"); return -ENOMEM; } @@ -190,7 +190,7 @@ /* create MIDI coder */ if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) { - snd_printk("can't malloc midi coder\n"); + snd_printk(KERN_ERR "can't malloc midi coder\n"); kfree(mdev); return -ENOMEM; } diff -Nru a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c --- a/sound/core/seq/oss/seq_oss_readq.c Thu Mar 7 18:17:40 2002 +++ b/sound/core/seq/oss/seq_oss_readq.c Thu Mar 7 18:17:40 2002 @@ -47,12 +47,12 @@ seq_oss_readq_t *q; if ((q = snd_kcalloc(sizeof(*q), GFP_KERNEL)) == NULL) { - snd_printk("can't malloc read queue\n"); + snd_printk(KERN_ERR "can't malloc read queue\n"); return NULL; } if ((q->q = snd_kcalloc(sizeof(evrec_t) * maxlen, GFP_KERNEL)) == NULL) { - snd_printk("can't malloc read queue buffer\n"); + snd_printk(KERN_ERR "can't malloc read queue buffer\n"); kfree(q); return NULL; } diff -Nru a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c --- a/sound/core/seq/oss/seq_oss_synth.c Thu Mar 7 18:17:37 2002 +++ b/sound/core/seq/oss/seq_oss_synth.c Thu Mar 7 18:17:37 2002 @@ -111,7 +111,7 @@ unsigned long flags; if ((rec = snd_kcalloc(sizeof(*rec), GFP_KERNEL)) == NULL) { - snd_printk("can't malloc synth info\n"); + snd_printk(KERN_ERR "can't malloc synth info\n"); return -ENOMEM; } rec->seq_device = -1; @@ -136,7 +136,7 @@ if (i >= max_synth_devs) { if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) { spin_unlock_irqrestore(®ister_lock, flags); - snd_printk("no more synth slot\n"); + snd_printk(KERN_ERR "no more synth slot\n"); kfree(rec); return -ENOMEM; } @@ -165,7 +165,7 @@ } if (index >= max_synth_devs) { spin_unlock_irqrestore(®ister_lock, flags); - snd_printk("can't unregister synth\n"); + snd_printk(KERN_ERR "can't unregister synth\n"); return -EINVAL; } synth_devs[index] = NULL; diff -Nru a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c --- a/sound/core/seq/seq_clientmgr.c Thu Mar 7 18:17:38 2002 +++ b/sound/core/seq/seq_clientmgr.c Thu Mar 7 18:17:38 2002 @@ -265,7 +265,7 @@ down(®ister_mutex); switch (client->type) { case NO_CLIENT: - snd_printk("Seq: Trying to free unused client %d\n", client->number); + snd_printk(KERN_WARNING "Seq: Trying to free unused client %d\n", client->number); break; case USER_CLIENT: case KERNEL_CLIENT: @@ -274,7 +274,7 @@ break; default: - snd_printk("Seq: Trying to free client %d with undefined type = %d\n", client->number, client->type); + snd_printk(KERN_ERR "Seq: Trying to free client %d with undefined type = %d\n", client->number, client->type); } up(®ister_mutex); diff -Nru a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c --- a/sound/core/seq/seq_device.c Thu Mar 7 18:17:39 2002 +++ b/sound/core/seq/seq_device.c Thu Mar 7 18:17:39 2002 @@ -295,7 +295,7 @@ if (ops == NULL) return -ENOMEM; if (ops->driver & DRIVER_LOADED) { - snd_printk("driver_register: driver '%s' already exists\n", id); + snd_printk(KERN_WARNING "driver_register: driver '%s' already exists\n", id); unlock_driver(ops); return -EBUSY; } @@ -363,7 +363,7 @@ return -ENXIO; if (! (ops->driver & DRIVER_LOADED) || (ops->driver & DRIVER_LOCKED)) { - snd_printk("driver_unregister: cannot unload driver '%s': status=%x\n", id, ops->driver); + snd_printk(KERN_ERR "driver_unregister: cannot unload driver '%s': status=%x\n", id, ops->driver); unlock_driver(ops); return -EBUSY; } @@ -378,7 +378,7 @@ ops->driver = 0; if (ops->num_init_devices > 0) - snd_printk("free_driver: init_devices > 0!! (%d)\n", ops->num_init_devices); + snd_printk(KERN_ERR "free_driver: init_devices > 0!! (%d)\n", ops->num_init_devices); up(&ops->reg_mutex); unlock_driver(ops); @@ -423,15 +423,14 @@ if (dev->status != SNDRV_SEQ_DEVICE_FREE) return 0; /* already initialized */ if (ops->argsize != dev->argsize) { - snd_printk("incompatible device '%s' for plug-in '%s'\n", dev->name, ops->id); -printk(" %d %d\n", ops->argsize, dev->argsize); + snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize); return -EINVAL; } if (ops->ops.init_device(dev) >= 0) { dev->status = SNDRV_SEQ_DEVICE_REGISTERED; ops->num_init_devices++; } else { - snd_printk("init_device failed: %s: %s\n", dev->name, dev->id); + snd_printk(KERN_ERR "init_device failed: %s: %s\n", dev->name, dev->id); } return 0; @@ -449,7 +448,7 @@ if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED) return 0; /* not registered */ if (ops->argsize != dev->argsize) { - snd_printk("incompatible device '%s' for plug-in '%s'\n", dev->name, ops->id); + snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize); return -EINVAL; } if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) { @@ -457,7 +456,7 @@ dev->driver_data = NULL; ops->num_init_devices--; } else { - snd_printk("free_device failed: %s: %s\n", dev->name, dev->id); + snd_printk(KERN_ERR "free_device failed: %s: %s\n", dev->name, dev->id); } return 0; @@ -517,7 +516,7 @@ remove_drivers(); snd_info_unregister(info_entry); if (num_ops) - snd_printk("drivers not released (%d)\n", num_ops); + snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops); } module_init(alsa_seq_device_init) diff -Nru a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c --- a/sound/core/seq/seq_dummy.c Thu Mar 7 18:17:45 2002 +++ b/sound/core/seq/seq_dummy.c Thu Mar 7 18:17:45 2002 @@ -206,7 +206,7 @@ int i; if (ports < 1) { - snd_printk("invalid number of ports %d\n", ports); + snd_printk(KERN_ERR "invalid number of ports %d\n", ports); return -EINVAL; } diff -Nru a/sound/core/seq/seq_instr.c b/sound/core/seq/seq_instr.c --- a/sound/core/seq/seq_instr.c Thu Mar 7 18:17:42 2002 +++ b/sound/core/seq/seq_instr.c Thu Mar 7 18:17:42 2002 @@ -132,7 +132,7 @@ } spin_unlock_irqrestore(&list->lock, flags); if (snd_seq_instr_free(instr, 0)<0) - snd_printk("instrument free problem\n"); + snd_printk(KERN_WARNING "instrument free problem\n"); } while ((cluster = list->chash[idx]) != NULL) { list->chash[idx] = cluster->next; @@ -221,7 +221,7 @@ schedule_timeout(1); } if (snd_seq_instr_free(instr, atomic)<0) - snd_printk("instrument free problem\n"); + snd_printk(KERN_WARNING "instrument free problem\n"); instr = next; } } @@ -324,7 +324,7 @@ return; spin_lock_irqsave(&list->lock, flags); if (instr->use <= 0) { - snd_printk("free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name); + snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name); } else { instr->use--; } diff -Nru a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c --- a/sound/core/seq/seq_lock.c Thu Mar 7 18:17:42 2002 +++ b/sound/core/seq/seq_lock.c Thu Mar 7 18:17:42 2002 @@ -68,12 +68,12 @@ int max_count = 5 * HZ; if (atomic_read(lockp) < 0) { - printk("seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line); + printk(KERN_WARNING "seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line); return; } while (atomic_read(lockp) > 0) { if (max_count == 0) { - snd_printk("seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line); + snd_printk(KERN_WARNING "seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line); break; } set_current_state(TASK_UNINTERRUPTIBLE); diff -Nru a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c --- a/sound/core/seq/seq_memory.c Thu Mar 7 18:17:38 2002 +++ b/sound/core/seq/seq_memory.c Thu Mar 7 18:17:38 2002 @@ -423,7 +423,7 @@ while (atomic_read(&pool->counter) > 0) { if (max_count == 0) { - snd_printk("snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter)); + snd_printk(KERN_WARNING "snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter)); break; } set_current_state(TASK_UNINTERRUPTIBLE); diff -Nru a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c --- a/sound/core/seq/seq_midi_event.c Thu Mar 7 18:17:36 2002 +++ b/sound/core/seq/seq_midi_event.c Thu Mar 7 18:17:36 2002 @@ -68,8 +68,8 @@ event_decode_t decode; } status_event[] = { /* 0x80 - 0xf0 */ - {SNDRV_SEQ_EVENT_NOTEOFF, 2, note_event, note_decode}, - {SNDRV_SEQ_EVENT_NOTEON, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_NOTEOFF, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_NOTEON, 2, note_event, note_decode}, {SNDRV_SEQ_EVENT_KEYPRESS, 2, note_event, note_decode}, {SNDRV_SEQ_EVENT_CONTROLLER, 2, two_param_ctrl_event, two_param_decode}, {SNDRV_SEQ_EVENT_PGMCHANGE, 1, one_param_ctrl_event, one_param_decode}, @@ -78,9 +78,9 @@ {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf0 */ /* 0xf0 - 0xff */ {SNDRV_SEQ_EVENT_SYSEX, 1, NULL, NULL}, /* sysex: 0xf0 */ - {SNDRV_SEQ_EVENT_QFRAME, 1, one_param_event, one_param_decode}, /* 0xf1 */ - {SNDRV_SEQ_EVENT_SONGPOS, 2, songpos_event, songpos_decode}, /* 0xf2 */ - {SNDRV_SEQ_EVENT_SONGSEL, 1, one_param_event, one_param_decode}, /* 0xf3 */ + {SNDRV_SEQ_EVENT_QFRAME, 1, one_param_event, one_param_decode}, /* 0xf1 */ + {SNDRV_SEQ_EVENT_SONGPOS, 2, songpos_event, songpos_decode}, /* 0xf2 */ + {SNDRV_SEQ_EVENT_SONGSEL, 1, one_param_event, one_param_decode}, /* 0xf3 */ {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf4 */ {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf5 */ {SNDRV_SEQ_EVENT_TUNE_REQUEST, 0, NULL, NULL}, /* 0xf6 */ @@ -92,7 +92,7 @@ {SNDRV_SEQ_EVENT_STOP, 0, NULL, NULL}, /* 0xfc */ {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xfd */ {SNDRV_SEQ_EVENT_SENSING, 0, NULL, NULL}, /* 0xfe */ - {SNDRV_SEQ_EVENT_RESET, 0, NULL, NULL}, /* 0xff */ + {SNDRV_SEQ_EVENT_RESET, 0, NULL, NULL}, /* 0xff */ }; static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev); @@ -128,6 +128,7 @@ } } dev->bufsize = bufsize; + dev->lastcmd = 0xff; spin_lock_init(&dev->lock); *rdev = dev; return 0; diff -Nru a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c --- a/sound/core/seq/seq_ports.c Thu Mar 7 18:17:45 2002 +++ b/sound/core/seq/seq_ports.c Thu Mar 7 18:17:45 2002 @@ -143,7 +143,7 @@ snd_assert(client, return NULL); if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) { - snd_printk("too many ports for client %d\n", client->number); + snd_printk(KERN_WARNING "too many ports for client %d\n", client->number); return NULL; } diff -Nru a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c --- a/sound/core/seq/seq_timer.c Thu Mar 7 18:17:45 2002 +++ b/sound/core/seq/seq_timer.c Thu Mar 7 18:17:45 2002 @@ -285,7 +285,7 @@ t = snd_timer_open(str, &tid, q->queue); } if (t == NULL) { - snd_printk("fatal error: cannot create timer\n"); + snd_printk(KERN_ERR "fatal error: cannot create timer\n"); return -ENODEV; } } diff -Nru a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c --- a/sound/core/seq/seq_virmidi.c Thu Mar 7 18:17:37 2002 +++ b/sound/core/seq/seq_virmidi.c Thu Mar 7 18:17:37 2002 @@ -233,8 +233,7 @@ } vmidi->seq_mode = rdev->seq_mode; vmidi->client = rdev->client; - vmidi->port = rdev->port; - snd_midi_event_init(vmidi->parser); + vmidi->port = rdev->port; snd_virmidi_init_event(vmidi, &vmidi->event); vmidi->rdev = rdev; runtime->private_data = vmidi; @@ -431,7 +430,7 @@ /* should check presence of port more strictly.. */ break; default: - snd_printk("seq_mode is not set: %d\n", rdev->seq_mode); + snd_printk(KERN_ERR "seq_mode is not set: %d\n", rdev->seq_mode); return -EINVAL; } return 0; diff -Nru a/sound/core/sound.c b/sound/core/sound.c --- a/sound/core/sound.c Thu Mar 7 18:17:38 2002 +++ b/sound/core/sound.c Thu Mar 7 18:17:38 2002 @@ -319,7 +319,7 @@ #else if (register_chrdev(snd_major, "alsa", &snd_fops)) { #endif - snd_printk("unable to register native major device number %d\n", snd_major); + snd_printk(KERN_ERR "unable to register native major device number %d\n", snd_major); #ifdef CONFIG_SND_OSSEMUL snd_oss_cleanup_module(); #endif @@ -349,7 +349,7 @@ } #endif #ifndef MODULE - printk("Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"); + printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"); #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_APM) pm_init(); @@ -391,7 +391,7 @@ #else if (unregister_chrdev(snd_major, "alsa") != 0) #endif - snd_printk("unable to unregister major device number %d\n", snd_major); + snd_printk(KERN_ERR "unable to unregister major device number %d\n", snd_major); #ifdef CONFIG_DEVFS_FS devfs_unregister(devfs_handle); #endif @@ -455,7 +455,7 @@ EXPORT_SYMBOL(snd_device_register); EXPORT_SYMBOL(snd_device_free); EXPORT_SYMBOL(snd_device_free_all); - /* misc.c */ + /* isadma.c */ #ifdef CONFIG_ISA EXPORT_SYMBOL(snd_dma_program); EXPORT_SYMBOL(snd_dma_disable); @@ -494,38 +494,10 @@ EXPORT_SYMBOL(snd_ctl_unregister_ioctl); /* misc.c */ EXPORT_SYMBOL(snd_task_name); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) -EXPORT_SYMBOL(try_inc_mod_count); -EXPORT_SYMBOL(snd_compat_mem_region); -EXPORT_SYMBOL(snd_compat_request_region); -EXPORT_SYMBOL(snd_compat_release_resource); -#endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_PCI) -EXPORT_SYMBOL(snd_pci_compat_match_device); -EXPORT_SYMBOL(snd_pci_compat_register_driver); -EXPORT_SYMBOL(snd_pci_compat_unregister_driver); -EXPORT_SYMBOL(snd_pci_compat_get_size); -EXPORT_SYMBOL(snd_pci_compat_get_flags); -EXPORT_SYMBOL(snd_pci_compat_set_power_state); -EXPORT_SYMBOL(snd_pci_compat_enable_device); -EXPORT_SYMBOL(snd_pci_compat_find_capability); -EXPORT_SYMBOL(snd_pci_compat_alloc_consistent); -EXPORT_SYMBOL(snd_pci_compat_free_consistent); -EXPORT_SYMBOL(snd_pci_compat_dma_supported); -EXPORT_SYMBOL(snd_pci_compat_get_dma_mask); -EXPORT_SYMBOL(snd_pci_compat_set_dma_mask); -EXPORT_SYMBOL(snd_pci_compat_get_driver_data); -EXPORT_SYMBOL(snd_pci_compat_set_driver_data); -#endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_PM) -EXPORT_SYMBOL(pm_register); -EXPORT_SYMBOL(pm_unregister); -EXPORT_SYMBOL(pm_send); +#ifdef CONFIG_SND_VERBOSE_PRINTK +EXPORT_SYMBOL(snd_verbose_printk); #endif /* wrappers */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) -EXPORT_SYMBOL(snd_wrapper_kill_fasync); -#endif #ifdef CONFIG_SND_DEBUG_MEMORY EXPORT_SYMBOL(snd_wrapper_kmalloc); EXPORT_SYMBOL(snd_wrapper_kfree); diff -Nru a/sound/core/sound_oss.c b/sound/core/sound_oss.c --- a/sound/core/sound_oss.c Thu Mar 7 18:17:46 2002 +++ b/sound/core/sound_oss.c Thu Mar 7 18:17:46 2002 @@ -212,7 +212,7 @@ { snd_info_entry_t *entry; - entry = snd_info_create_module_entry(THIS_MODULE, "oss-devices", NULL); + entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root); if (entry) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->c.text.read_size = PAGE_SIZE; diff -Nru a/sound/core/timer.c b/sound/core/timer.c --- a/sound/core/timer.c Thu Mar 7 18:17:38 2002 +++ b/sound/core/timer.c Thu Mar 7 18:17:38 2002 @@ -681,7 +681,7 @@ snd_assert(timer != NULL, return -ENXIO); down(®ister_mutex); if (! list_empty(&timer->open_list_head)) { - snd_printk("timer 0x%lx is busy?\n", (long)timer); + snd_printk(KERN_WARNING "timer 0x%lx is busy?\n", (long)timer); list_for_each_safe(p, n, &timer->open_list_head) { list_del_init(p); ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); @@ -1318,10 +1318,10 @@ } snd_timer_proc_entry = entry; if ((err = snd_timer_register_system()) < 0) - snd_printk("unable to register system timer (%i)\n", err); + snd_printk(KERN_ERR "unable to register system timer (%i)\n", err); if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0, &snd_timer_reg, "timer"))<0) - snd_printk("unable to register timer device (%i)\n", err); + snd_printk(KERN_ERR "unable to register timer device (%i)\n", err); return 0; } diff -Nru a/sound/drivers/Config.help b/sound/drivers/Config.help --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/Config.help Thu Mar 7 18:17:46 2002 @@ -0,0 +1,18 @@ +CONFIG_SND_DUMMY + Say 'Y' or 'M' to include dummy driver. This driver does nothing, but + emulates various mixer controls and PCM devices. + +CONFIG_SND_VIRMIDI + Say 'Y' or 'M' to include virtual MIDI driver. This driver allows to + connect applications using raw MIDI devices to sequencer. + +CONFIG_SND_MTPAV + Say 'Y' or 'M' to include support for MOTU MidiTimePiece AV multiport + MIDI adapter. + +CONFIG_SND_SERIAL_U16550 + Say 'Y' or 'M' to include support for MIDI serial port driver. It works + with serial UARTs 16550 and better. + +CONFIG_SND_MPU401 + Say 'Y' or 'M' to include support for MPU401 hardware using UART access. diff -Nru a/sound/drivers/dummy.c b/sound/drivers/dummy.c --- a/sound/drivers/dummy.c Thu Mar 7 18:17:42 2002 +++ b/sound/drivers/dummy.c Thu Mar 7 18:17:42 2002 @@ -562,7 +562,7 @@ for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { if (snd_card_dummy_probe(dev) < 0) { #ifdef MODULE - snd_printk("Dummy soundcard #%i not found or device busy\n", dev + 1); + printk(KERN_ERR "Dummy soundcard #%i not found or device busy\n", dev + 1); #endif break; } @@ -570,7 +570,7 @@ } if (!cards) { #ifdef MODULE - snd_printk("Dummy soundcard not found or device busy\n"); + printk(KERN_ERR "Dummy soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/drivers/mpu401/Makefile b/sound/drivers/mpu401/Makefile --- a/sound/drivers/mpu401/Makefile Thu Mar 7 18:17:46 2002 +++ b/sound/drivers/mpu401/Makefile Thu Mar 7 18:17:46 2002 @@ -37,6 +37,7 @@ obj-$(CONFIG_SND_ES1968) += snd-mpu401-uart.o obj-$(CONFIG_SND_FM801) += snd-mpu401-uart.o obj-$(CONFIG_SND_ICE1712) += snd-mpu401-uart.o +obj-$(CONFIG_SND_INTEL8X0) += snd-mpu401-uart.o obj-$(CONFIG_SND_SONICVIBES) += snd-mpu401-uart.o obj-$(CONFIG_SND_VIA686) += snd-mpu401-uart.o obj-$(CONFIG_SND_ALI5451) += snd-mpu401-uart.o diff -Nru a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c --- a/sound/drivers/mpu401/mpu401.c Thu Mar 7 18:17:45 2002 +++ b/sound/drivers/mpu401/mpu401.c Thu Mar 7 18:17:45 2002 @@ -81,7 +81,7 @@ if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, snd_port[dev], 0, snd_irq[dev], snd_irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0) { - snd_printk("MPU401 not detected at 0x%lx\n", snd_port[dev]); + printk(KERN_ERR "MPU401 not detected at 0x%lx\n", snd_port[dev]); snd_card_free(card); return -ENODEV; } @@ -113,7 +113,7 @@ } if (!cards) { #ifdef MODULE - snd_printk("MPU-401 device not found or device busy\n"); + printk(KERN_ERR "MPU-401 device not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c --- a/sound/drivers/mpu401/mpu401_uart.c Thu Mar 7 18:17:42 2002 +++ b/sound/drivers/mpu401/mpu401_uart.c Thu Mar 7 18:17:42 2002 @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff -Nru a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c --- a/sound/drivers/mtpav.c Thu Mar 7 18:17:40 2002 +++ b/sound/drivers/mtpav.c Thu Mar 7 18:17:40 2002 @@ -54,6 +54,7 @@ #include #include #include +#include #include #define SNDRV_GET_ID #include @@ -761,7 +762,7 @@ snd_mtpav_portscan(mtp_card); - snd_printk("Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n", snd_irq, snd_port); + printk(KERN_INFO "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n", snd_irq, snd_port); return 0; diff -Nru a/sound/drivers/opl3/Makefile b/sound/drivers/opl3/Makefile --- a/sound/drivers/opl3/Makefile Thu Mar 7 18:17:36 2002 +++ b/sound/drivers/opl3/Makefile Thu Mar 7 18:17:36 2002 @@ -29,7 +29,7 @@ obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opl3-lib.o obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opl3-lib.o obj-$(CONFIG_SND_OPTI93X) += snd-opl3-lib.o -obj-$(CONFIG_SND_SB) += snd-opl3-lib.o +obj-$(CONFIG_SND_SB8) += snd-opl3-lib.o obj-$(CONFIG_SND_SB16) += snd-opl3-lib.o obj-$(CONFIG_SND_SBAWE) += snd-opl3-lib.o obj-$(CONFIG_SND_WAVEFRONT) += snd-opl3-lib.o @@ -54,7 +54,7 @@ obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opl3-synth.o obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opl3-synth.o obj-$(CONFIG_SND_OPTI93X) += snd-opl3-synth.o - obj-$(CONFIG_SND_SB) += snd-opl3-synth.o + obj-$(CONFIG_SND_SB8) += snd-opl3-synth.o obj-$(CONFIG_SND_SB16) += snd-opl3-synth.o obj-$(CONFIG_SND_SBAWE) += snd-opl3-synth.o obj-$(CONFIG_SND_WAVEFRONT) += snd-opl3-synth.o diff -Nru a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c --- a/sound/drivers/opl3/opl3_lib.c Thu Mar 7 18:17:39 2002 +++ b/sound/drivers/opl3/opl3_lib.c Thu Mar 7 18:17:39 2002 @@ -28,6 +28,7 @@ #include #include #include +#include #include MODULE_AUTHOR("Jaroslav Kysela , Hannu Savolainen 1993-1996, Rob Hooft"); diff -Nru a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c --- a/sound/drivers/serial-u16550.c Thu Mar 7 18:17:38 2002 +++ b/sound/drivers/serial-u16550.c Thu Mar 7 18:17:38 2002 @@ -39,7 +39,7 @@ * * Usage example for MS-124T, with A-B switch in A position: * setserial /dev/ttyS0 uart none - * /sbin/modprobe snd-card-serial snd_port=0x3f8 snd_irq=4 \ + * /sbin/modprobe snd-serial-u16550 snd_port=0x3f8 snd_irq=4 \ * snd_adaptor=1 snd_speed=19200 * * - In MS-124W S/A mode, one raw MIDI substream is supported @@ -49,7 +49,7 @@ * * Usage example for S/A mode: * setserial /dev/ttyS0 uart none - * /sbin/modprobe snd-card-serial snd_port=0x3f8 snd_irq=4 \ + * /sbin/modprobe snd-serial-u16550 snd_port=0x3f8 snd_irq=4 \ * snd_adaptor=2 * * - In MS-124W M/B mode, the driver supports 16 ALSA raw MIDI @@ -67,7 +67,7 @@ * * Usage example for M/B mode: * setserial /dev/ttyS0 uart none - * /sbin/insmod snd-card-serial snd_port=0x3f8 snd_irq=4 \ + * /sbin/insmod snd-serial-u16550 snd_port=0x3f8 snd_irq=4 \ * snd_adaptor=3 * * - The MS-124W hardware's M/A mode is currently not supported. @@ -106,6 +106,7 @@ #include #include #include +#include #include #include #define SNDRV_GET_ID @@ -891,7 +892,7 @@ if ((err = snd_uart16550_detect(snd_port[dev])) <= 0) { snd_card_free(card); - snd_printk("no UART detected at 0x%lx\n", (long)snd_port[dev]); + printk(KERN_ERR "no UART detected at 0x%lx\n", (long)snd_port[dev]); return err; } @@ -940,7 +941,7 @@ if (cards == 0) { #ifdef MODULE - snd_printk("serial midi soundcard not found or device busy\n"); + printk(KERN_ERR "serial midi soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c --- a/sound/drivers/virmidi.c Thu Mar 7 18:17:40 2002 +++ b/sound/drivers/virmidi.c Thu Mar 7 18:17:40 2002 @@ -136,7 +136,7 @@ for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { if (snd_card_virmidi_probe(dev) < 0) { #ifdef MODULE - snd_printk("Card-VirMIDI #%i not found or device busy\n", dev + 1); + printk(KERN_ERR "Card-VirMIDI #%i not found or device busy\n", dev + 1); #endif break; } @@ -144,7 +144,7 @@ } if (!cards) { #ifdef MODULE - snd_printk("Card-VirMIDI soundcard not found or device busy\n"); + printk(KERN_ERR "Card-VirMIDI soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/i2c/Makefile b/sound/i2c/Makefile --- a/sound/i2c/Makefile Thu Mar 7 18:17:36 2002 +++ b/sound/i2c/Makefile Thu Mar 7 18:17:36 2002 @@ -13,7 +13,7 @@ snd-cs8427-objs := cs8427.o snd-tea6330t-objs := tea6330t.o -# Module Dependency +# Toplevel Module Dependency obj-$(CONFIG_SND_INTERWAVE_STB) += snd-tea6330t.o snd-i2c.o obj-$(CONFIG_SND_ICE1712) += snd-cs8427.o snd-i2c.o diff -Nru a/sound/isa/Config.help b/sound/isa/Config.help --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/Config.help Thu Mar 7 18:17:46 2002 @@ -0,0 +1,99 @@ +CONFIG_SND_AD1816A + Say 'Y' or 'M' to include support for Analog Devices SoundPort AD1816A or + compatible sound chips. + +CONFIG_SND_AD1848 + Say 'Y' or 'M' to include support for AD1848 (Analog Devices) or CS4248 + (Cirrus Logic - Crystal Semiconductors) chips. Please, for newer chips + from Cirrus Logic, use CS4231, CS4232 or CS4236+ driver. + +CONFIG_SND_CS4231 + Say 'Y' or 'M' to include support for CS4231 chips from Cirrus Logic - + Crystal Semiconductors. + +CONFIG_SND_CS4232 + Say 'Y' or 'M' to include support for CS4232 chips from Cirrus Logic - + Crystal Semiconductors. + +CONFIG_SND_CS4236 + Say 'Y' or 'M' to include support for CS4235,CS4236,CS4237B,CS4238B,CS4239 + chips from Cirrus Logic - Crystal Semiconductors. + +CONFIG_SND_ES968 + Say 'Y' or 'M' to include support for ESS AudioDrive ES968 chip. + +CONFIG_SND_ES1688 + Say 'Y' or 'M' to include support for ESS AudioDrive ES688 or ES1688 chips. + +CONFIG_SND_ES18XX + Say 'Y' or 'M' to include support for ESS AudioDrive ES18xx chips. + +CONFIG_SND_GUSCLASSIC + Say 'Y' or 'M' to include support for Gravis UltraSound Classic soundcard. + +CONFIG_SND_GUSEXTREME + Say 'Y' or 'M' to include support for Gravis UltraSound Extreme soundcard. + +CONFIG_SND_GUSMAX + Say 'Y' or 'M' to include support for Gravis UltraSound MAX soundcard. + +CONFIG_SND_INTERWAVE + Say 'Y' or 'M' to include support for AMD InterWave based soundcards + (Gravis UltraSound Plug & Play, STB SoundRage32, MED3210, Dynasonic Pro, + Panasonic PCA761AW). + +CONFIG_SND_INTERWAVE_STB + Say 'Y' or 'M' to include support for AMD InterWave based soundcards + with TEA6330T bass and treble regulator (UltraSound 32-Pro). + +CONFIG_SND_OPTI92X_AD1848 + Say 'Y' or 'M' to include support for Opti92x soundcards equiped with + AD1848 codec. + +CONFIG_SND_OPTI92X_CS4231 + Say 'Y' or 'M' to include support for Opti92x soundcards equiped with + CS4231 codec. + +CONFIG_SND_OPTI93X + Say 'Y' or 'M' to include support for Opti93x soundcards. + +CONFIG_SND_SB8 + Say 'Y' or 'M' to include support for Sound Blaster 1.0/2.0/Pro (8-bit) + soundcards or 100% compatible from Creative. + +CONFIG_SND_SB16 + Say 'Y' or 'M' to include support for Sound Blaster 16 (including + Plug and Play version). + +CONFIG_SND_SBAWE + Say 'Y' or 'M' to include support for Sound Blaster AWE (including + Plug and Play version). + +CONFIG_SND_SB16_CSP + Say 'Y' to include support for CSP core. This special coprocessor + can do variable tasks like various compression and decompression + algorithms. + +CONFIG_SND_WAVEFRONT + Say 'Y' or 'M' to include support for Turtle Beach Maui, Tropez + and Tropez+ soundcards based on Wavefront chip. + +CONFIG_SND_ALS100 + Say 'Y' or 'M' to include support for Avance Logic ALS100, ALS110, + ALS120 and ALS200 soundcards. + +CONFIG_SND_AZT2320 + Say 'Y' or 'M' to include support for Aztech Systems AZT2320 soundcard. + +CONFIG_SND_CMI8330 + Say 'Y' or 'M' to include support for C-Media CMI8330 based soundcards. + +CONFIG_SND_DT0197H + Say 'Y' or 'M' to include support for Diamond Technologies DT-0197H + soundcards. + +CONFIG_SND_OPL3SA2 + Say 'Y' or 'M' to include support for Yamaha OPL3SA2 or OPL3SA3 chips. + +CONFIG_SND_SGALAXY + Say 'Y' or 'M' to include support for Aztech Sound Galaxy. diff -Nru a/sound/isa/Config.in b/sound/isa/Config.in --- a/sound/isa/Config.in Thu Mar 7 18:17:39 2002 +++ b/sound/isa/Config.in Thu Mar 7 18:17:39 2002 @@ -3,13 +3,13 @@ mainmenu_option next_comment comment 'ISA devices' -dep_tristate 'Analog Devices SoundPort AD1816A' CONFIG_SND_AD1816A $CONFIG_SND +dep_tristate 'Analog Devices SoundPort AD1816A' CONFIG_SND_AD1816A $CONFIG_SND $CONFIG_ISAPNP dep_tristate 'Generic AD1848/CS4248 driver' CONFIG_SND_AD1848 $CONFIG_SND dep_tristate 'Generic Cirrus Logic CS4231 driver' CONFIG_SND_CS4231 $CONFIG_SND dep_tristate 'Generic Cirrus Logic CS4232 driver' CONFIG_SND_CS4232 $CONFIG_SND dep_tristate 'Generic Cirrus Logic CS4236+ driver' CONFIG_SND_CS4236 $CONFIG_SND -dep_tristate 'Generic ESS ES968 driver' CONFIG_SND_ES968 $CONFIG_SND -dep_tristate 'Generic ESS ES1688 driver' CONFIG_SND_ES1688 $CONFIG_SND +dep_tristate 'Generic ESS ES968 driver' CONFIG_SND_ES968 $CONFIG_SND $CONFIG_ISAPNP +dep_tristate 'Generic ESS ES688/ES1688 driver' CONFIG_SND_ES1688 $CONFIG_SND dep_tristate 'Generic ESS ES18xx driver' CONFIG_SND_ES18XX $CONFIG_SND dep_tristate 'Gravis UltraSound Classic' CONFIG_SND_GUSCLASSIC $CONFIG_SND dep_tristate 'Gravis UltraSound Extreme' CONFIG_SND_GUSEXTREME $CONFIG_SND @@ -26,10 +26,10 @@ bool ' Sound Blaster 16/AWE CSP support' CONFIG_SND_SB16_CSP fi dep_tristate 'Turtle Beach Maui,Tropez,Tropez+ (Wavefront)' CONFIG_SND_WAVEFRONT $CONFIG_SND -dep_tristate 'Avance Logic ALS100/ALS120' CONFIG_SND_ALS100 $CONFIG_SND -dep_tristate 'Aztech Systems AZT2320' CONFIG_SND_AZT2320 $CONFIG_SND +dep_tristate 'Avance Logic ALS100/ALS120' CONFIG_SND_ALS100 $CONFIG_SND $CONFIG_ISAPNP +dep_tristate 'Aztech Systems AZT2320' CONFIG_SND_AZT2320 $CONFIG_SND $CONFIG_ISAPNP dep_tristate 'C-Media CMI8330' CONFIG_SND_CMI8330 $CONFIG_SND -dep_tristate 'Diamond Technologies DT-0197H' CONFIG_SND_DT0197H $CONFIG_SND +dep_tristate 'Diamond Technologies DT-0197H' CONFIG_SND_DT0197H $CONFIG_SND $CONFIG_ISAPNP dep_tristate 'Yamaha OPL3-SA2/SA3' CONFIG_SND_OPL3SA2 $CONFIG_SND dep_tristate 'Aztech Sound Galaxy' CONFIG_SND_SGALAXY $CONFIG_SND diff -Nru a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c --- a/sound/isa/ad1816a/ad1816a.c Thu Mar 7 18:17:40 2002 +++ b/sound/isa/ad1816a/ad1816a.c Thu Mar 7 18:17:40 2002 @@ -36,6 +36,8 @@ #define chip_t ad1816a_t +#define PFX "ad1816a: " + EXPORT_NO_SYMBOLS; MODULE_AUTHOR("Massimo Piccioni "); MODULE_DESCRIPTION("AD1816A, AD1815"); @@ -176,7 +178,7 @@ isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); if (pdev->activate(pdev) < 0) { - snd_printk("AUDIO isapnp configure failure\n"); + printk(KERN_ERR PFX "AUDIO isapnp configure failure\n"); return -EBUSY; } @@ -202,7 +204,7 @@ if (pdev->activate(pdev) < 0) { /* not fatal error */ - snd_printk("MPU-401 isapnp configure failure\n"); + printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n"); snd_mpu_port[dev] = -1; acard->devmpu = NULL; } else { @@ -257,7 +259,7 @@ return error; } #else - snd_printk("you have to enable ISA PnP support.\n"); + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); return -ENOSYS; #endif /* __ISAPNP__ */ @@ -284,14 +286,14 @@ if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, snd_mpu_port[dev], 0, snd_mpu_irq[dev], SA_INTERRUPT, NULL) < 0) - snd_printk("no MPU-401 device at 0x%lx.\n", snd_mpu_port[dev]); + printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", snd_mpu_port[dev]); } if (snd_fm_port[dev] > 0) { if (snd_opl3_create(card, snd_fm_port[dev], snd_fm_port[dev] + 2, OPL3_HW_AUTO, 0, &opl3) < 0) { - snd_printk("no OPL device at 0x%lx-0x%lx.\n", snd_fm_port[dev], snd_fm_port[dev] + 2); + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx.\n", snd_fm_port[dev], snd_fm_port[dev] + 2); } else { if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) { snd_card_free(card); @@ -346,11 +348,11 @@ #ifdef __ISAPNP__ cards += isapnp_probe_cards(snd_ad1816a_pnpids, snd_ad1816a_isapnp_detect); #else - snd_printk("you have to enable ISA PnP support.\n"); + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); #endif #ifdef MODULE if (!cards) - snd_printk("no AD1816A based soundcards found.\n"); + printk(KERN_ERR "no AD1816A based soundcards found.\n"); #endif /* MODULE */ return cards ? 0 : -ENODEV; } diff -Nru a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c --- a/sound/isa/ad1816a/ad1816a_lib.c Thu Mar 7 18:17:38 2002 +++ b/sound/isa/ad1816a/ad1816a_lib.c Thu Mar 7 18:17:38 2002 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include diff -Nru a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c --- a/sound/isa/ad1848/ad1848.c Thu Mar 7 18:17:36 2002 +++ b/sound/isa/ad1848/ad1848.c Thu Mar 7 18:17:36 2002 @@ -135,7 +135,7 @@ if (!cards) { #ifdef MODULE - snd_printk("AD1848 soundcard not found or device busy\n"); + printk(KERN_ERR "AD1848 soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c --- a/sound/isa/ad1848/ad1848_lib.c Thu Mar 7 18:17:43 2002 +++ b/sound/isa/ad1848/ad1848_lib.c Thu Mar 7 18:17:43 2002 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff -Nru a/sound/isa/als100.c b/sound/isa/als100.c --- a/sound/isa/als100.c Thu Mar 7 18:17:38 2002 +++ b/sound/isa/als100.c Thu Mar 7 18:17:38 2002 @@ -39,6 +39,8 @@ #define chip_t sb_t +#define PFX "als100: " + EXPORT_NO_SYMBOLS; MODULE_AUTHOR("Massimo Piccioni "); @@ -178,7 +180,7 @@ isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); if (pdev->activate(pdev)<0) { - snd_printk("AUDIO isapnp configure failure\n"); + printk(KERN_ERR PFX "AUDIO isapnp configure failure\n"); return -EBUSY; } @@ -201,7 +203,7 @@ 1); if (pdev->activate(pdev)<0) { - snd_printk("MPU-401 isapnp configure failure\n"); + printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n"); snd_mpu_port[dev] = -1; acard->devmpu = NULL; } else { @@ -219,7 +221,7 @@ isapnp_resource_change(&pdev->resource[0], snd_fm_port[dev], 4); if (pdev->activate(pdev)<0) { - snd_printk("OPL isapnp configure failure\n"); + printk(KERN_ERR PFX "OPL isapnp configure failure\n"); snd_fm_port[dev] = -1; acard->devopl = NULL; } else { @@ -277,7 +279,7 @@ return error; } #else - snd_printk("you have to enable PnP support ...\n"); + printk(KERN_ERR PFX "you have to enable PnP support ...\n"); snd_card_free(card); return -ENOSYS; #endif /* __ISAPNP__ */ @@ -307,14 +309,14 @@ snd_mpu_port[dev], 0, snd_mpu_irq[dev], SA_INTERRUPT, NULL) < 0) - snd_printk("no MPU-401 device at 0x%lx\n", snd_mpu_port[dev]); + printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", snd_mpu_port[dev]); } if (snd_fm_port[dev] > 0) { if (snd_opl3_create(card, snd_fm_port[dev], snd_fm_port[dev] + 2, OPL3_HW_AUTO, 0, &opl3) < 0) { - snd_printk("no OPL device at 0x%lx-0x%lx\n", + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n", snd_fm_port[dev], snd_fm_port[dev] + 2); } else { if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) { @@ -370,11 +372,11 @@ #ifdef __ISAPNP__ cards += isapnp_probe_cards(snd_als100_pnpids, snd_als100_isapnp_detect); #else - snd_printk("you have to enable ISA PnP support.\n"); + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); #endif #ifdef MODULE if (!cards) - snd_printk("no ALS100 based soundcards found\n"); + printk(KERN_ERR "no ALS100 based soundcards found\n"); #endif return cards ? 0 : -ENODEV; } diff -Nru a/sound/isa/azt2320.c b/sound/isa/azt2320.c --- a/sound/isa/azt2320.c Thu Mar 7 18:17:46 2002 +++ b/sound/isa/azt2320.c Thu Mar 7 18:17:46 2002 @@ -50,6 +50,8 @@ #define chip_t cs4231_t +#define PFX "azt2320: " + EXPORT_NO_SYMBOLS; MODULE_AUTHOR("Massimo Piccioni "); @@ -132,7 +134,7 @@ static struct isapnp_card_id snd_azt2320_pnpids[] __devinitdata = { /* PRO16V */ ISAPNP_AZT2320('A','Z','T',0x1008,0x1008,0x2001), - /* --- */ + /* Aztech Sound Galaxy 16 */ ISAPNP_AZT2320('A','Z','T',0x2320,0x0001,0x0002), /* Packard Bell Sound III 336 AM/SP */ ISAPNP_AZT2320('A','Z','T',0x3000,0x1003,0x2001), @@ -191,7 +193,7 @@ isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); if (pdev->activate(pdev) < 0) { - snd_printk("AUDIO isapnp configure failure\n"); + printk(KERN_ERR PFX "AUDIO isapnp configure failure\n"); return -EBUSY; } @@ -217,7 +219,7 @@ if (pdev->activate(pdev) < 0) { /* not fatal error */ - snd_printk("MPU-401 isapnp configure failure\n"); + printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n"); snd_mpu_port[dev] = -1; acard->devmpu = NULL; } else { @@ -329,7 +331,7 @@ snd_mpu_port[dev], 0, snd_mpu_irq[dev], SA_INTERRUPT, NULL) < 0) - snd_printk("no MPU-401 device at 0x%lx\n", + printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", snd_mpu_port[dev]); } @@ -337,7 +339,7 @@ if (snd_opl3_create(card, snd_fm_port[dev], snd_fm_port[dev] + 2, OPL3_HW_AUTO, 0, &opl3) < 0) { - snd_printk("no OPL device at 0x%lx-0x%lx\n", + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n", snd_fm_port[dev], snd_fm_port[dev] + 2); } else { if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) { @@ -393,11 +395,11 @@ #ifdef __ISAPNP__ cards += isapnp_probe_cards(snd_azt2320_pnpids, snd_azt2320_isapnp_detect); #else - snd_printk("you have to enable ISA PnP support.\n"); + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); #endif #ifdef MODULE if (!cards) - snd_printk("no AZT2320 based soundcards found\n"); + printk(KERN_ERR "no AZT2320 based soundcards found\n"); #endif return cards ? 0 : -ENODEV; } diff -Nru a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c --- a/sound/isa/cmi8330.c Thu Mar 7 18:17:39 2002 +++ b/sound/isa/cmi8330.c Thu Mar 7 18:17:39 2002 @@ -488,7 +488,7 @@ if (!cards) { #ifdef MODULE - snd_printk("CMI8330 not found or device busy\n"); + printk(KERN_ERR "CMI8330 not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c --- a/sound/isa/cs423x/cs4231.c Thu Mar 7 18:17:36 2002 +++ b/sound/isa/cs423x/cs4231.c Thu Mar 7 18:17:36 2002 @@ -135,7 +135,7 @@ snd_mpu_port[dev], 0, snd_mpu_irq[dev], SA_INTERRUPT, NULL) < 0) - snd_printk("MPU401 not detected\n"); + printk(KERN_ERR "cs4231: MPU401 not detected\n"); } strcpy(card->driver, "CS4231"); strcpy(card->shortname, pcm->name); @@ -161,7 +161,7 @@ } if (!cards) { #ifdef MODULE - snd_printk("CS4231 soundcard not found or device busy\n"); + printk(KERN_ERR "CS4231 soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c --- a/sound/isa/cs423x/cs4231_lib.c Thu Mar 7 18:17:39 2002 +++ b/sound/isa/cs423x/cs4231_lib.c Thu Mar 7 18:17:39 2002 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -1335,7 +1336,7 @@ } if (chip->res_cport) { release_resource(chip->res_cport); - kfree_nocheck(chip->res_port); + kfree_nocheck(chip->res_cport); } if (chip->irq >= 0) { disable_irq(chip->irq); diff -Nru a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c --- a/sound/isa/cs423x/cs4236.c Thu Mar 7 18:17:38 2002 +++ b/sound/isa/cs423x/cs4236.c Thu Mar 7 18:17:38 2002 @@ -309,7 +309,7 @@ if (snd_dma2[dev] != SNDRV_AUTO_DMA) isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev] < 0 ? 4 : snd_dma2[dev], 1); if (pdev->activate(pdev)<0) { - snd_printk(IDENT " isapnp configure failed for WSS (out of resources?)\n"); + printk(KERN_ERR IDENT " isapnp configure failed for WSS (out of resources?)\n"); return -EBUSY; } snd_port[dev] = pdev->resource[0].start; @@ -331,7 +331,7 @@ if (snd_cport[dev] != SNDRV_AUTO_PORT) isapnp_resource_change(&pdev->resource[0], snd_cport[dev], 8); if (pdev->activate(pdev)<0) { - snd_printk(IDENT " isapnp configure failed for control (out of resources?)\n"); + printk(KERN_ERR IDENT " isapnp configure failed for control (out of resources?)\n"); acard->wss->deactivate(acard->wss); return -EBUSY; } @@ -352,7 +352,7 @@ if (pdev->activate(pdev)<0) { snd_mpu_port[dev] = SNDRV_AUTO_PORT; snd_mpu_irq[dev] = SNDRV_AUTO_IRQ; - snd_printk(IDENT " isapnp configure failed for MPU (out of resources?)\n"); + printk(KERN_ERR IDENT " isapnp configure failed for MPU (out of resources?)\n"); } else { snd_mpu_port[dev] = pdev->resource[0].start; if (pdev->irq_resource[0].flags & IORESOURCE_IRQ) { @@ -429,7 +429,7 @@ card->private_free = snd_card_cs4236_free; #ifdef __ISAPNP__ if (snd_isapnp[dev] && (err = snd_card_cs4236_isapnp(dev, acard))<0) { - snd_printk("isapnp detection failed and probing for " IDENT " is not supported\n"); + printk(KERN_ERR "isapnp detection failed and probing for " IDENT " is not supported\n"); snd_card_free(card); return -ENXIO; } @@ -442,7 +442,7 @@ snd_sb_port[dev] = SNDRV_AUTO_PORT; if (snd_sb_port[dev] != SNDRV_AUTO_PORT) if ((acard->res_sb_port = request_region(snd_sb_port[dev], 16, IDENT " SB")) == NULL) { - snd_printk("unable to register SB port at 0x%lx\n", snd_sb_port[dev]); + printk(KERN_ERR IDENT ": unable to register SB port at 0x%lx\n", snd_sb_port[dev]); snd_card_free(card); return -ENOMEM; } @@ -501,7 +501,7 @@ if (snd_opl3_create(card, snd_fm_port[dev], snd_fm_port[dev] + 2, OPL3_HW_OPL3_CS, 0, &opl3) < 0) { - snd_printk(IDENT ": OPL3 not detected\n"); + printk(KERN_ERR IDENT ": OPL3 not detected\n"); } else { if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { snd_card_free(card); @@ -515,7 +515,7 @@ snd_mpu_port[dev], 0, snd_mpu_irq[dev], snd_mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0) - snd_printk(IDENT ": MPU401 not detected\n"); + printk(KERN_ERR IDENT ": MPU401 not detected\n"); } strcpy(card->driver, pcm->name); strcpy(card->shortname, pcm->name); @@ -575,7 +575,7 @@ #endif if (!cards) { #ifdef MODULE - snd_printk(IDENT " soundcard not found or device busy\n"); + printk(KERN_ERR IDENT " soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/isa/dt0197h.c b/sound/isa/dt0197h.c --- a/sound/isa/dt0197h.c Thu Mar 7 18:17:42 2002 +++ b/sound/isa/dt0197h.c Thu Mar 7 18:17:42 2002 @@ -36,6 +36,8 @@ #define chip_t sb_t +#define PFX "dt0197h: " + EXPORT_NO_SYMBOLS; MODULE_AUTHOR("Massimo Piccioni "); @@ -150,7 +152,7 @@ isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); if (pdev->activate(pdev)<0) { - snd_printk("AUDIO isapnp configure failure\n"); + printk(KERN_ERR PFX "AUDIO isapnp configure failure\n"); return -EBUSY; } @@ -170,7 +172,7 @@ 1); if (pdev->activate(pdev)<0) { - snd_printk("MPU-401 isapnp configure failure\n"); + printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n"); snd_mpu_port[dev] = -1; acard->devmpu = NULL; } else { @@ -186,7 +188,7 @@ isapnp_resource_change(&pdev->resource[0], snd_fm_port[dev], 4); if (pdev->activate(pdev)<0) { - snd_printk("OPL isapnp configure failure\n"); + printk(KERN_ERR PFX "OPL isapnp configure failure\n"); snd_fm_port[dev] = -1; acard->devopl = NULL; } else { @@ -244,7 +246,7 @@ return error; } #else - snd_printk("you have to enable PnP support ...\n"); + printk(KERN_ERR PFX "you have to enable PnP support ...\n"); snd_card_free(card); return -ENOSYS; #endif /* __ISAPNP__ */ @@ -276,7 +278,7 @@ snd_mpu_irq[dev], SA_INTERRUPT, NULL) < 0) - snd_printk("no MPU-401 device at 0x%lx ?\n", + printk(KERN_ERR PFX "no MPU-401 device at 0x%lx ?\n", snd_mpu_port[dev]); } @@ -285,7 +287,7 @@ snd_fm_port[dev], snd_fm_port[dev] + 2, OPL3_HW_AUTO, 0, &opl3) < 0) { - snd_printk("no OPL device at 0x%lx-0x%lx ?\n", + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx ?\n", snd_fm_port[dev], snd_fm_port[dev] + 2); } else { if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) { @@ -341,11 +343,11 @@ #ifdef __ISAPNP__ cards += isapnp_probe_cards(snd_dt0197h_pnpids, snd_dt0197h_isapnp_detect); #else - snd_printk("you have to enable ISA PnP support.\n"); + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); #endif #ifdef MODULE if (!cards) - snd_printk("no DT-0197H based soundcards found\n"); + printk(KERN_ERR "no DT-0197H based soundcards found\n"); #endif return cards ? 0 : -ENODEV; } diff -Nru a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c --- a/sound/isa/es1688/es1688.c Thu Mar 7 18:17:41 2002 +++ b/sound/isa/es1688/es1688.c Thu Mar 7 18:17:41 2002 @@ -133,7 +133,7 @@ } if ((snd_opl3_create(card, chip->port, chip->port + 2, OPL3_HW_OPL3, 0, &opl3)) < 0) { - snd_printk("opl3 not detected at 0x%lx\n", chip->port); + printk(KERN_ERR "es1688: opl3 not detected at 0x%lx\n", chip->port); } else { if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { snd_card_free(card); @@ -194,7 +194,7 @@ cards += snd_legacy_auto_probe(possible_ports, snd_audiodrive_legacy_auto_probe); if (!cards) { #ifdef MODULE - snd_printk("ESS AudioDrive ES1688 soundcard not found or device busy\n"); + printk(KERN_ERR "ESS AudioDrive ES1688 soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c --- a/sound/isa/es1688/es1688_lib.c Thu Mar 7 18:17:37 2002 +++ b/sound/isa/es1688/es1688_lib.c Thu Mar 7 18:17:37 2002 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff -Nru a/sound/isa/es18xx.c b/sound/isa/es18xx.c --- a/sound/isa/es18xx.c Thu Mar 7 18:17:43 2002 +++ b/sound/isa/es18xx.c Thu Mar 7 18:17:43 2002 @@ -86,6 +86,8 @@ #define SNDRV_GET_ID #include +#define PFX "es18xx: " + struct _snd_es18xx { unsigned long port; /* port of ESS chip */ unsigned long mpu_port; /* MPU-401 port of ESS chip */ @@ -1510,7 +1512,7 @@ static int __init snd_es18xx_probe(es18xx_t *chip) { if (snd_es18xx_identify(chip) < 0) { - snd_printk("[0x%lx] ESS chip not found\n", chip->port); + printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port); return -ENODEV; } @@ -1752,27 +1754,27 @@ if ((chip->res_port = request_region(port, 16, "ES18xx")) == NULL) { snd_es18xx_free(chip); - snd_printk("unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1); + printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1); return -EBUSY; } if (request_irq(irq, snd_es18xx_interrupt, SA_INTERRUPT, "ES18xx", (void *) chip)) { snd_es18xx_free(chip); - snd_printk("unable to grap IRQ %d\n", irq); + printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq); return -EBUSY; } chip->irq = irq; if (request_dma(dma1, "ES18xx DMA 1")) { snd_es18xx_free(chip); - snd_printk("unable to grap DMA1 %d\n", dma1); + printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1); return -EBUSY; } chip->dma1 = dma1; if (request_dma(dma2, "ES18xx DMA 2")) { snd_es18xx_free(chip); - snd_printk("unable to grap DMA2 %d\n", dma2); + printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2); return -EBUSY; } chip->dma2 = dma2; @@ -2008,7 +2010,7 @@ if (acard->devc->prepare(acard->devc)<0) return -EAGAIN; if (acard->devc->activate(acard->devc)<0) { - snd_printk("isapnp control configure failure (out of resources?)\n"); + printk(KERN_ERR PFX "isapnp control configure failure (out of resources?)\n"); return -EAGAIN; } snd_printdd("isapnp: port=0x%lx\n", acard->devc->resource[0].start); @@ -2031,7 +2033,7 @@ if (snd_irq[dev] != SNDRV_AUTO_IRQ) isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); if (pdev->activate(pdev)<0) { - snd_printk("isapnp configure failure (out of resources?)\n"); + printk(KERN_ERR PFX "isapnp configure failure (out of resources?)\n"); acard->devc->deactivate(acard->devc); return -EBUSY; } @@ -2147,7 +2149,7 @@ } if (snd_opl3_create(card, chip->fm_port, chip->fm_port + 2, OPL3_HW_OPL3, 0, &opl3) < 0) { - snd_printk("opl3 not detected at 0x%lx\n", chip->port); + printk(KERN_ERR PFX "opl3 not detected at 0x%lx\n", chip->port); } else { if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { snd_card_free(card); @@ -2259,7 +2261,7 @@ #endif if(!cards) { #ifdef MODULE - snd_printk("ESS AudioDrive ES18xx soundcard not found or device busy\n"); + printk(KERN_ERR "ESS AudioDrive ES18xx soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c --- a/sound/isa/gus/gus_main.c Thu Mar 7 18:17:38 2002 +++ b/sound/isa/gus/gus_main.c Thu Mar 7 18:17:38 2002 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff -Nru a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c --- a/sound/isa/gus/gusclassic.c Thu Mar 7 18:17:44 2002 +++ b/sound/isa/gus/gusclassic.c Thu Mar 7 18:17:44 2002 @@ -251,7 +251,7 @@ cards += snd_legacy_auto_probe(possible_ports, snd_gusclassic_legacy_auto_probe); if (!cards) { #ifdef MODULE - snd_printk("GUS Classic soundcard not found or device busy\n"); + printk(KERN_ERR "GUS Classic soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c --- a/sound/isa/gus/gusextreme.c Thu Mar 7 18:17:45 2002 +++ b/sound/isa/gus/gusextreme.c Thu Mar 7 18:17:45 2002 @@ -316,7 +316,7 @@ if (snd_opl3_create(card, es1688->port, es1688->port + 2, OPL3_HW_OPL3, 0, &opl3) < 0) { - snd_printk("opl3 not detected at 0x%lx\n", es1688->port); + printk(KERN_ERR "gusextreme: opl3 not detected at 0x%lx\n", es1688->port); } else { if ((err = snd_opl3_hwdep_new(opl3, 0, 2, NULL)) < 0) { snd_card_free(card); @@ -376,7 +376,7 @@ cards += snd_legacy_auto_probe(possible_ports, snd_gusextreme_legacy_auto_probe); if (!cards) { #ifdef MODULE - snd_printk("GUS Extreme soundcard not found or device busy\n"); + printk(KERN_ERR "GUS Extreme soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c --- a/sound/isa/gus/gusmax.c Thu Mar 7 18:17:37 2002 +++ b/sound/isa/gus/gusmax.c Thu Mar 7 18:17:37 2002 @@ -292,13 +292,13 @@ } if (!gus->max_flag) { snd_card_free(card); - snd_printk("GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port); + printk(KERN_ERR "GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port); return -ENODEV; } if (request_irq(irq, snd_gusmax_interrupt, SA_INTERRUPT, "GUS MAX", (void *)maxcard)) { snd_card_free(card); - snd_printk("unable to grab IRQ %d\n", irq); + printk(KERN_ERR "gusmax: unable to grab IRQ %d\n", irq); return -EBUSY; } maxcard->irq = irq; @@ -387,7 +387,7 @@ cards += snd_legacy_auto_probe(possible_ports, snd_gusmax_legacy_auto_probe); if (!cards) { #ifdef MODULE - snd_printk("GUS MAX soundcard not found or device busy\n"); + printk(KERN_ERR "GUS MAX soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c --- a/sound/isa/gus/interwave.c Thu Mar 7 18:17:36 2002 +++ b/sound/isa/gus/interwave.c Thu Mar 7 18:17:36 2002 @@ -948,7 +948,7 @@ continue; } #ifdef MODULE - snd_printk("InterWave soundcard #%i not found at 0x%lx or device busy\n", dev, snd_port[dev]); + printk(KERN_ERR "InterWave soundcard #%i not found at 0x%lx or device busy\n", dev, snd_port[dev]); #endif } /* legacy auto configured cards */ @@ -960,7 +960,7 @@ if (!cards) { #ifdef MODULE - snd_printk("InterWave soundcard not found or device busy\n"); + printk(KERN_ERR "InterWave soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c --- a/sound/isa/opl3sa2.c Thu Mar 7 18:17:43 2002 +++ b/sound/isa/opl3sa2.c Thu Mar 7 18:17:43 2002 @@ -177,7 +177,7 @@ ISAPNP_OPL3SA2('Y','M','H',0x0020,0x0021), /* Yamaha OPL3-SA3 (integrated on Intel's Pentium II AL440LX motherboard) */ ISAPNP_OPL3SA2('Y','M','H',0x0030,0x0021), - /* ??? */ + /* Yamaha OPL3-SA2 */ ISAPNP_OPL3SA2('Y','M','H',0x0800,0x0021), /* NeoMagic MagicWave 3DX */ ISAPNP_OPL3SA2('N','M','X',0x2200,0x2210), @@ -893,7 +893,7 @@ #endif if (!cards) { #ifdef MODULE - snd_printk("Yamaha OPL3-SA soundcard not found or device busy\n"); + printk(KERN_ERR "Yamaha OPL3-SA soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c --- a/sound/isa/opti9xx/opti92x-ad1848.c Thu Mar 7 18:17:39 2002 +++ b/sound/isa/opti9xx/opti92x-ad1848.c Thu Mar 7 18:17:39 2002 @@ -2154,9 +2154,9 @@ if ((error = snd_card_opti9xx_probe())) { #ifdef MODULE #ifdef OPTi93X - snd_printk("no OPTi 82C93x soundcard found\n"); + printk(KERN_ERR "no OPTi 82C93x soundcard found\n"); #else - snd_printk("no OPTi 82C92x soundcard found\n"); + printk(KERN_ERR "no OPTi 82C92x soundcard found\n"); #endif /* OPTi93X */ #endif return error; diff -Nru a/sound/isa/sb/Makefile b/sound/isa/sb/Makefile --- a/sound/isa/sb/Makefile Thu Mar 7 18:17:43 2002 +++ b/sound/isa/sb/Makefile Thu Mar 7 18:17:43 2002 @@ -27,12 +27,12 @@ obj-$(CONFIG_SND_SB8) += snd-sb8.o snd-sb8-dsp.o snd-sb-common.o obj-$(CONFIG_SND_SB16) += snd-sb16.o snd-sb16-dsp.o snd-sb-common.o obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o ifeq ($(CONFIG_SND_SB16_CSP),y) obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o endif -obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o -obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) obj-$(CONFIG_SND_SBAWE) += snd-emu8000-synth.o endif diff -Nru a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c --- a/sound/isa/sb/emu8000.c Thu Mar 7 18:17:42 2002 +++ b/sound/isa/sb/emu8000.c Thu Mar 7 18:17:42 2002 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff -Nru a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c --- a/sound/isa/sb/sb16.c Thu Mar 7 18:17:40 2002 +++ b/sound/isa/sb/sb16.c Thu Mar 7 18:17:40 2002 @@ -43,6 +43,12 @@ #define chip_t sb_t +#ifdef SNDRV_SBAWE +#define PFX "sbawe: " +#else +#define PFX "sb16: " +#endif + #ifndef SNDRV_SBAWE EXPORT_NO_SYMBOLS; #endif @@ -296,7 +302,7 @@ if (snd_irq[dev] != SNDRV_AUTO_IRQ) isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); if (pdev->activate(pdev) < 0) { - snd_printk("isapnp configure failure (out of resources?)\n"); + printk(KERN_ERR PFX "isapnp configure failure (out of resources?)\n"); return -EBUSY; } snd_port[dev] = pdev->resource[0].start; @@ -322,7 +328,7 @@ isapnp_resource_change(&pdev->resource[2], snd_awe_port[dev] + 0x800, 4); } if (pdev->activate(pdev)<0) { - snd_printk("WaveTable isapnp configure failure (out of resources?)\n"); + printk(KERN_ERR PFX "WaveTable isapnp configure failure (out of resources?)\n"); acard->dev->deactivate(acard->dev); return -EBUSY; } @@ -402,21 +408,21 @@ if (irq == SNDRV_AUTO_IRQ) { if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { snd_card_free(card); - snd_printk("unable to find a free IRQ\n"); + printk(KERN_ERR PFX "unable to find a free IRQ\n"); return -EBUSY; } } if (dma8 == SNDRV_AUTO_DMA) { if ((dma8 = snd_legacy_find_free_dma(possible_dmas8)) < 0) { snd_card_free(card); - snd_printk("unable to find a free 8-bit DMA\n"); + printk(KERN_ERR PFX "unable to find a free 8-bit DMA\n"); return -EBUSY; } } if (dma16 == SNDRV_AUTO_DMA) { if ((dma16 = snd_legacy_find_free_dma(possible_dmas16)) < 0) { snd_card_free(card); - snd_printk("unable to find a free 16-bit DMA\n"); + printk(KERN_ERR PFX "unable to find a free 16-bit DMA\n"); return -EBUSY; } } @@ -475,7 +481,7 @@ if (snd_opl3_create(card, snd_fm_port[dev], snd_fm_port[dev] + 2, OPL3_HW_OPL3, snd_fm_port[dev] == snd_port[dev], &opl3) < 0) { - snd_printk("no OPL device at 0x%lx-0x%lx\n", + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n", snd_fm_port[dev], snd_fm_port[dev] + 2); } else { #ifdef SNDRV_SBAWE_EMU8000 @@ -503,7 +509,7 @@ chip->csp = csp->private_data; chip->hardware = SB_HW_16CSP; } else { - snd_printk("warning - CSP chip not detected on soundcard #%i\n", dev + 1); + printk(KERN_INFO PFX "warning - CSP chip not detected on soundcard #%i\n", dev + 1); } } #endif @@ -511,7 +517,7 @@ if (snd_awe_port[dev] > 0) { if (snd_emu8000_new(card, 1, snd_awe_port[dev], snd_seq_ports[dev], NULL) < 0) { - snd_printk("fatal error - EMU-8000 synthesizer not detected at 0x%lx\n", snd_awe_port[dev]); + printk(KERN_ERR PFX "fatal error - EMU-8000 synthesizer not detected at 0x%lx\n", snd_awe_port[dev]); snd_card_free(card); return -ENXIO; } @@ -612,7 +618,7 @@ continue; } #ifdef MODULE - snd_printk("Sound Blaster 16+ soundcard #%i not found at 0x%lx or device busy\n", dev, snd_port[dev]); + printk(KERN_ERR "Sound Blaster 16+ soundcard #%i not found at 0x%lx or device busy\n", dev, snd_port[dev]); #endif } /* legacy auto configured cards */ @@ -624,11 +630,11 @@ if (!cards) { #ifdef MODULE - snd_printk("Sound Blaster 16 soundcard not found or device busy\n"); + printk(KERN_ERR "Sound Blaster 16 soundcard not found or device busy\n"); #ifdef SNDRV_SBAWE_EMU8000 - snd_printk("In case, if you have non-AWE card, try snd-card-sb16 module\n"); + printk(KERN_ERR "In case, if you have non-AWE card, try snd-card-sb16 module\n"); #else - snd_printk("In case, if you have AWE card, try snd-card-sbawe module\n"); + printk(KERN_ERR "In case, if you have AWE card, try snd-card-sbawe module\n"); #endif #endif return -ENODEV; diff -Nru a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c --- a/sound/isa/sb/sb16_main.c Thu Mar 7 18:17:44 2002 +++ b/sound/isa/sb/sb16_main.c Thu Mar 7 18:17:44 2002 @@ -209,11 +209,11 @@ #else #define snd_sb16_csp_playback_prepare(chip, runtime) /*nop*/ #define snd_sb16_csp_capture_prepare(chip, runtime) /*nop*/ -#define snd_sb16_csp_update(chip) /*nop*/ +#define snd_sb16_csp_update(chip) /*nop*/ #define snd_sb16_csp_playback_open(chip, runtime) /*nop*/ -#define snd_sb16_csp_playback_close(chip) /*nop*/ +#define snd_sb16_csp_playback_close(chip) /*nop*/ #define snd_sb16_csp_capture_open(chip, runtime) /*nop*/ -#define snd_sb16_csp_capture_close(chip) /*nop*/ +#define snd_sb16_csp_capture_close(chip) /*nop*/ #endif diff -Nru a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c --- a/sound/isa/sb/sb8.c Thu Mar 7 18:17:45 2002 +++ b/sound/isa/sb/sb8.c Thu Mar 7 18:17:45 2002 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -144,13 +145,13 @@ if ((err = snd_opl3_create(card, chip->port + 8, 0, OPL3_HW_AUTO, 1, &opl3)) < 0) { - snd_printk("no OPL device at 0x%lx\n", chip->port + 8); + printk(KERN_ERR "sb8: no OPL device at 0x%lx\n", chip->port + 8); } } else { if ((err = snd_opl3_create(card, chip->port, chip->port + 2, OPL3_HW_AUTO, 1, &opl3)) < 0) { - snd_printk("no OPL device at 0x%lx-0x%lx\n", + printk(KERN_ERR "sb8: no OPL device at 0x%lx-0x%lx\n", chip->port, chip->port + 2); } } @@ -211,7 +212,7 @@ cards += snd_legacy_auto_probe(possible_ports, snd_card_sb8_legacy_auto_probe); if (!cards) { #ifdef MODULE - snd_printk("Sound Blaster soundcard not found or device busy\n"); + printk(KERN_ERR "Sound Blaster soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c --- a/sound/isa/sb/sb_common.c Thu Mar 7 18:17:44 2002 +++ b/sound/isa/sb/sb_common.c Thu Mar 7 18:17:44 2002 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff -Nru a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c --- a/sound/isa/sgalaxy.c Thu Mar 7 18:17:45 2002 +++ b/sound/isa/sgalaxy.c Thu Mar 7 18:17:45 2002 @@ -119,7 +119,7 @@ static int dma_bits[] = {1, 2, 0, 3}; int tmp, tmp1; - unsigned int flags; + unsigned long flags; if ((tmp = inb(port + 3)) == 0xff) { @@ -305,7 +305,7 @@ } if (!cards) { #ifdef MODULE - snd_printk("Sound Galaxy soundcard not found or device busy\n"); + printk(KERN_ERR "Sound Galaxy soundcard not found or device busy\n"); #endif return -ENODEV; } diff -Nru a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c --- a/sound/isa/wavefront/wavefront.c Thu Mar 7 18:17:39 2002 +++ b/sound/isa/wavefront/wavefront.c Thu Mar 7 18:17:39 2002 @@ -740,7 +740,7 @@ #endif if (!cards) { #ifdef MODULE - snd_printk ("No cards found or devices busy\n"); + printk (KERN_ERR "No WaveFront cards found or devices busy\n"); #endif return -ENODEV; } diff -Nru a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c --- a/sound/isa/wavefront/wavefront_synth.c Thu Mar 7 18:17:42 2002 +++ b/sound/isa/wavefront/wavefront_synth.c Thu Mar 7 18:17:42 2002 @@ -111,30 +111,6 @@ MODULE_PARM(osrun_time,"i"); MODULE_PARM_DESC(osrun_time, "how many seconds to wait for the ICS2115 OS"); -/* - * This sucks, hopefully it'll get standardised - */ - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,18) && LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) -#define loops_per_sec loops_per_jiffy*HZ -#elif LINUX_VERSION_CODE == KERNEL_VERSION(2,4,0) && defined(I_DIRTY_PAGES) /* linux/fs.h */ -#define loops_per_sec loops_per_jiffy*HZ -#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0) -#define loops_per_sec loops_per_jiffy*HZ -#endif - -#if defined(__alpha__) || defined(__powerpc__) -#ifdef __SMP__ -#define LOOPS_PER_SEC (cpu_data[smp_processor_id()].loops_per_sec) -#else -#define LOOPS_PER_SEC (loops_per_sec) -#endif -#endif - -#if defined(__i386__) -#define LOOPS_PER_SEC (current_cpu_data.loops_per_sec) -#endif - /* if WF_DEBUG not defined, no run-time debugging messages will be available via the debug flag setting. Given the current beta state of the driver, this will remain set until a future @@ -323,26 +299,16 @@ { int i; - static int short_loop_cnt = 0; - - /* Compute the loop count that lets us sleep for about the - right amount of time, cache issues, bus speeds and all - other issues being unequal but largely irrelevant. - */ - - if (short_loop_cnt == 0) { - short_loop_cnt = wait_usecs * - (LOOPS_PER_SEC / 1000000); - } /* Spin for a short period of time, because >99% of all requests to the WaveFront can be serviced inline like this. */ - for (i = 0; i < short_loop_cnt; i++) { + for (i = 0; i < wait_usecs; i += 5) { if (wavefront_status (dev) & mask) { return 1; } + udelay(5); } for (i = 0; i < sleep_tries; i++) { @@ -1316,18 +1282,21 @@ for (i = 0; i < num_samples; i++) { char d[2]; + int val; - if ((d[0] = wavefront_read (dev)) == -1) { + if ((val = wavefront_read (dev)) == -1) { snd_printk ("upload multisample failed " "during sample loop.\n"); return -(EIO); } + d[0] = val; - if ((d[1] = wavefront_read (dev)) == -1) { + if ((val = wavefront_read (dev)) == -1) { snd_printk ("upload multisample failed " "during sample loop.\n"); return -(EIO); } + d[1] = val; header->hdr.ms.SampleNumber[i] = demunge_int32 ((unsigned char *) d, 2); diff -Nru a/sound/last.c b/sound/last.c --- a/sound/last.c Thu Mar 7 18:17:38 2002 +++ b/sound/last.c Thu Mar 7 18:17:38 2002 @@ -27,14 +27,14 @@ { int idx, ok = 0; - printk("ALSA device list:\n"); + printk(KERN_INFO "ALSA device list:\n"); for (idx = 0; idx < SNDRV_CARDS; idx++) if (snd_cards[idx] != NULL) { - printk(" #%i: %s\n", idx, snd_cards[idx]->longname); + printk(KERN_INFO " #%i: %s\n", idx, snd_cards[idx]->longname); ok++; } if (ok == 0) - printk(" No soundcards found.\n"); + printk(KERN_INFO " No soundcards found.\n"); return 0; } diff -Nru a/sound/oss/btaudio.c b/sound/oss/btaudio.c --- a/sound/oss/btaudio.c Thu Mar 7 18:17:40 2002 +++ b/sound/oss/btaudio.c Thu Mar 7 18:17:40 2002 @@ -1030,7 +1030,7 @@ name: "btaudio", id_table: btaudio_pci_tbl, probe: btaudio_probe, - remove: btaudio_remove, + remove: __devexit_p(btaudio_remove), }; int btaudio_init_module(void) diff -Nru a/sound/oss/cs4232.c b/sound/oss/cs4232.c --- a/sound/oss/cs4232.c Thu Mar 7 18:17:37 2002 +++ b/sound/oss/cs4232.c Thu Mar 7 18:17:37 2002 @@ -277,7 +277,7 @@ } } -void __exit unload_cs4232(struct address_info *hw_config) +static void __exit unload_cs4232(struct address_info *hw_config) { int base = hw_config->io_base, irq = hw_config->irq; int dma1 = hw_config->dma, dma2 = hw_config->dma2; @@ -460,10 +460,12 @@ return 0; } -int cs4232_isapnp_remove(struct pci_dev *dev, const struct isapnp_device_id *id) +static int __exit cs4232_isapnp_remove(struct pci_dev *dev, + const struct isapnp_device_id *id) { struct address_info *cfg = (struct address_info*)pci_get_drvdata(dev); - if (cfg) unload_cs4232(cfg); + if (cfg) + unload_cs4232(cfg); pci_set_drvdata(dev,NULL); dev->deactivate(dev); return 0; diff -Nru a/sound/oss/emu10k1/main.c b/sound/oss/emu10k1/main.c --- a/sound/oss/emu10k1/main.c Thu Mar 7 18:17:43 2002 +++ b/sound/oss/emu10k1/main.c Thu Mar 7 18:17:43 2002 @@ -1127,7 +1127,7 @@ name: "emu10k1", id_table: emu10k1_pci_tbl, probe: emu10k1_probe, - remove: emu10k1_remove, + remove: __devexit_p(emu10k1_remove), }; static int __init emu10k1_init_module(void) diff -Nru a/sound/oss/i810_audio.c b/sound/oss/i810_audio.c --- a/sound/oss/i810_audio.c Thu Mar 7 18:17:40 2002 +++ b/sound/oss/i810_audio.c Thu Mar 7 18:17:40 2002 @@ -2958,7 +2958,7 @@ return -ENODEV; } -static void __exit i810_remove(struct pci_dev *pci_dev) +static void __devexit i810_remove(struct pci_dev *pci_dev) { int i; struct i810_card *card = pci_get_drvdata(pci_dev); @@ -3118,7 +3118,7 @@ name: I810_MODULE_NAME, id_table: i810_pci_tbl, probe: i810_probe, - remove: i810_remove, + remove: __devexit_p(i810_remove), #ifdef CONFIG_PM suspend: i810_pm_suspend, resume: i810_pm_resume, diff -Nru a/sound/oss/mpu401.c b/sound/oss/mpu401.c --- a/sound/oss/mpu401.c Thu Mar 7 18:17:46 2002 +++ b/sound/oss/mpu401.c Thu Mar 7 18:17:46 2002 @@ -1227,7 +1227,7 @@ return ok; } -void __exit unload_mpu401(struct address_info *hw_config) +void unload_mpu401(struct address_info *hw_config) { void *p; int n=hw_config->slots[1]; diff -Nru a/sound/oss/rme96xx.c b/sound/oss/rme96xx.c --- a/sound/oss/rme96xx.c Thu Mar 7 18:17:42 2002 +++ b/sound/oss/rme96xx.c Thu Mar 7 18:17:42 2002 @@ -1162,7 +1162,7 @@ static int rme96xx_open(struct inode *in, struct file *f) { - int minor = MINOR(in->i_rdev); + int minor = minor(in->i_rdev); struct list_head *list; int devnum = ((minor-3)/16) % devices; /* default = 0 */ rme96xx_info *s; @@ -1490,7 +1490,7 @@ static int rme96xx_mixer_open(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + int minor = minor(inode->i_rdev); struct list_head *list; rme96xx_info *s; diff -Nru a/sound/oss/sonicvibes.c b/sound/oss/sonicvibes.c --- a/sound/oss/sonicvibes.c Thu Mar 7 18:17:43 2002 +++ b/sound/oss/sonicvibes.c Thu Mar 7 18:17:43 2002 @@ -1235,7 +1235,7 @@ static int sv_open_mixdev(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + int minor = minor(inode->i_rdev); struct list_head *list; struct sv_state *s; @@ -1893,7 +1893,7 @@ static int sv_open(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + int minor = minor(inode->i_rdev); DECLARE_WAITQUEUE(wait, current); unsigned char fmtm = ~0, fmts = 0; struct list_head *list; @@ -2142,7 +2142,7 @@ static int sv_midi_open(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + int minor = minor(inode->i_rdev); DECLARE_WAITQUEUE(wait, current); unsigned long flags; struct list_head *list; @@ -2364,7 +2364,7 @@ static int sv_dmfm_open(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + int minor = minor(inode->i_rdev); DECLARE_WAITQUEUE(wait, current); struct list_head *list; struct sv_state *s; diff -Nru a/sound/oss/trident.c b/sound/oss/trident.c --- a/sound/oss/trident.c Thu Mar 7 18:17:40 2002 +++ b/sound/oss/trident.c Thu Mar 7 18:17:40 2002 @@ -4149,7 +4149,7 @@ goto out; } -static void __exit trident_remove(struct pci_dev *pci_dev) +static void __devexit trident_remove(struct pci_dev *pci_dev) { int i; struct trident_card *card = pci_get_drvdata(pci_dev); @@ -4202,7 +4202,7 @@ name: TRIDENT_MODULE_NAME, id_table: trident_pci_tbl, probe: trident_probe, - remove: trident_remove, + remove: __devexit_p(trident_remove), suspend: trident_suspend, resume: trident_resume }; diff -Nru a/sound/oss/via82cxxx_audio.c b/sound/oss/via82cxxx_audio.c --- a/sound/oss/via82cxxx_audio.c Thu Mar 7 18:17:41 2002 +++ b/sound/oss/via82cxxx_audio.c Thu Mar 7 18:17:41 2002 @@ -311,7 +311,7 @@ */ static int via_init_one (struct pci_dev *dev, const struct pci_device_id *id); -static void via_remove_one (struct pci_dev *pdev); +static void __devexit via_remove_one (struct pci_dev *pdev); static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos); static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos); @@ -354,8 +354,6 @@ static struct pci_device_id via_pci_tbl[] __initdata = { { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_5, - PCI_ANY_ID, PCI_ANY_ID, }, { 0, } }; MODULE_DEVICE_TABLE(pci,via_pci_tbl); @@ -365,7 +363,7 @@ name: VIA_MODULE_NAME, id_table: via_pci_tbl, probe: via_init_one, - remove: via_remove_one, + remove: __devexit_p(via_remove_one), }; @@ -3271,7 +3269,7 @@ } -static void __exit via_remove_one (struct pci_dev *pdev) +static void __devexit via_remove_one (struct pci_dev *pdev) { struct via_info *card; diff -Nru a/sound/oss/ymfpci.c b/sound/oss/ymfpci.c --- a/sound/oss/ymfpci.c Thu Mar 7 18:17:38 2002 +++ b/sound/oss/ymfpci.c Thu Mar 7 18:17:38 2002 @@ -2652,7 +2652,7 @@ name: "ymfpci", id_table: ymf_id_tbl, probe: ymf_probe_one, - remove: ymf_remove_one, + remove: __devexit_p(ymf_remove_one), suspend: ymf_suspend, resume: ymf_resume }; diff -Nru a/sound/pci/Config.help b/sound/pci/Config.help --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/Config.help Thu Mar 7 18:17:46 2002 @@ -0,0 +1,75 @@ +CONFIG_SND_ALI5451 + Say 'Y' or 'M' to include support for ALI PCI Audio M5451 sound core. + +CONFIG_SND_CS46XX + Say 'Y' or 'M' to include support for Cirrus Logic CS4610 / CS4612 / + CS4614 / CS4615 / CS4622 / CS4624 / CS4630 / CS4280 chips. + +CONFIG_SND_CS46XX_ACCEPT_VALID + Say 'Y' to allow sample resolution for mmap() transfers. + +CONFIG_SND_EMU10K1 + Say 'Y' or 'M' to include support for Sound Blaster PCI 512, Live!, + Audigy and E-mu APS (partially supported). + +CONFIG_SND_KORG1212 + Say 'Y' or 'M' to include support for Korg 1212IO. + +CONFIG_SND_NM256 + Say 'Y' or 'M' to include support for NeoMagic NM256AV/ZX chips. + +CONFIG_SND_RME96 + Say 'Y' or 'M' to include support for RME Digi96, Digi96/8 and + Digi96/8 PRO/PAD/PST. + +CONFIG_SND_RME9652 + Say 'Y' or 'M' to include support for RME Hammerfall (RME Digi9652 / + Digi9636) soundcards. + +CONFIG_SND_TRIDENT + +CONFIG_SND_YMFPCI + Say 'Y' or 'M' to include support for Yamaha PCI audio chips - + YMF724, YMF724F, YMF740, YMF740C, YMF744, YMF754. + +CONFIG_SND_ALS4000 + Say 'Y' or 'M' to include support for Avance Logic ALS4000. + +CONFIG_SND_CMIPCI + Say 'Y' or 'M' to include support for C-Media CMI8338 and 8738 PCI + soundcards. + +CONFIG_SND_ENS1370 + Say 'Y' or 'M' to include support for Ensoniq AudioPCI ES1370. + +CONFIG_SND_ENS1371 + Say 'Y' or 'M' to include support for Ensoniq AudioPCI ES1371 and + Sound Blaster PCI 64 or 128 soundcards. + +CONFIG_SND_ES1938 + Say 'Y' or 'M' to include support for ESS Solo-1 (ES1938, ES1946) + soundcard. + +CONFIG_SND_ES1968 + Say 'Y' or 'M' to include support for ESS Maestro 1/2/2E. + +CONFIG_SND_MAESTRO3 + Say 'Y' or 'M' to include support for ESS Maestro 3 (Allegro) soundcard. + +CONFIG_SND_FM801 + Say 'Y' or 'M' to include support for ForteMedia FM801 based soundcards. + +CONFIG_SND_ICE1712 + Say 'Y' or 'M' to include support for ICE1712 (Envy24) based soundcards. + +CONFIG_SND_INTEL8X0 + Say 'Y' or 'M' to include support for Intel8x0 based soundcards. + +CONFIG_SND_SONICVIBES + Say 'Y' or 'M' to include support for S3 SonicVibes based soundcards. + +CONFIG_SND_VIA686 + Say 'Y' or 'M' to include support for VIA VT82C686A/B South Bridge. + +CONFIG_SND_VIA8233 + Say 'Y' or 'M' to include support for VIA VT8233 South Bridge. diff -Nru a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c --- a/sound/pci/ac97/ac97_codec.c Thu Mar 7 18:17:42 2002 +++ b/sound/pci/ac97/ac97_codec.c Thu Mar 7 18:17:42 2002 @@ -115,7 +115,7 @@ { 0x43525960, 0xfffffff8, "CS4291", NULL }, { 0x48525300, 0xffffff00, "HMP9701", NULL }, { 0x49434501, 0xffffffff, "ICE1230", NULL }, -{ 0x49434511, 0xffffffff, "ICE1232", NULL }, // only guess --jk +{ 0x49434511, 0xffffffff, "ICE1232", NULL }, // alias VIA VT1611A? { 0x4e534300, 0xffffffff, "LM4540/43/45/46/48", NULL }, // only guess --jk { 0x4e534331, 0xffffffff, "LM4549", NULL }, { 0x53494c22, 0xffffffff, "Si3036", NULL }, @@ -1393,28 +1393,31 @@ ac97->card = card; spin_lock_init(&ac97->reg_lock); snd_ac97_write(ac97, AC97_RESET, 0); /* reset to defaults */ - udelay(50); - - /* it's necessary to wait awhile until registers are accessible after RESET */ - /* because the PCM or MASTER volume registers can be modified, */ - /* the REC_GAIN register is used for tests */ - end_time = jiffies + (HZ / 2); - do { - /* use preliminary reads to settle the communication */ - snd_ac97_read(ac97, AC97_RESET); - snd_ac97_read(ac97, AC97_VENDOR_ID1); - snd_ac97_read(ac97, AC97_VENDOR_ID2); - /* test if we can write to the PCM volume register */ - snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05); - if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05) - goto __access_ok; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/100); - } while (end_time - (signed long)jiffies >= 0); - snd_printd("AC'97 %d:%d does not respond - RESET [REC_GAIN = 0x%x]\n", ac97->num, ac97->addr, err); - snd_ac97_free(ac97); - return -ENXIO; - + if (ac97->wait) + ac97->wait(ac97); + else { + udelay(50); + + /* it's necessary to wait awhile until registers are accessible after RESET */ + /* because the PCM or MASTER volume registers can be modified, */ + /* the REC_GAIN register is used for tests */ + end_time = jiffies + HZ; + do { + /* use preliminary reads to settle the communication */ + snd_ac97_read(ac97, AC97_RESET); + snd_ac97_read(ac97, AC97_VENDOR_ID1); + snd_ac97_read(ac97, AC97_VENDOR_ID2); + /* test if we can write to the PCM volume register */ + snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05); + if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05) + goto __access_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/100); + } while (end_time - (signed long)jiffies >= 0); + snd_printd("AC'97 %d:%d does not respond - RESET [REC_GAIN = 0x%x]\n", ac97->num, ac97->addr, err); + snd_ac97_free(ac97); + return -ENXIO; + } __access_ok: ac97->caps = snd_ac97_read(ac97, AC97_RESET); ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; diff -Nru a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c --- a/sound/pci/ali5451/ali5451.c Thu Mar 7 18:17:42 2002 +++ b/sound/pci/ali5451/ali5451.c Thu Mar 7 18:17:42 2002 @@ -2253,7 +2253,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("ALi pci audio not found or device busy.\n"); + printk(KERN_ERR "ALi pci audio not found or device busy.\n"); #endif return err; } diff -Nru a/sound/pci/als4000.c b/sound/pci/als4000.c --- a/sound/pci/als4000.c Thu Mar 7 18:17:42 2002 +++ b/sound/pci/als4000.c Thu Mar 7 18:17:42 2002 @@ -635,7 +635,7 @@ gcr+0x30, 1, pci->irq, 0, &chip->rmidi)) < 0) { snd_card_free(card); - snd_printk("no MPU-401device at 0x%lx ?\n", gcr+0x30); + printk(KERN_ERR "als4000: no MPU-401device at 0x%lx ?\n", gcr+0x30); return err; } @@ -650,7 +650,7 @@ if (snd_opl3_create(card, gcr+0x10, gcr+0x12, OPL3_HW_AUTO, 1, &opl3) < 0) { - snd_printk("no OPL device at 0x%lx-0x%lx ?\n", + printk(KERN_ERR "als4000: no OPL device at 0x%lx-0x%lx ?\n", gcr+0x10, gcr+0x12 ); } else { if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { @@ -692,7 +692,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("no ALS4000 based soundcards found or device busy\n"); + printk(KERN_ERR "no ALS4000 based soundcards found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/cmipci.c b/sound/pci/cmipci.c --- a/sound/pci/cmipci.c Thu Mar 7 18:17:38 2002 +++ b/sound/pci/cmipci.c Thu Mar 7 18:17:38 2002 @@ -113,6 +113,7 @@ #define CM_AC3EN1 0x00100000 /* enable AC3: model 037 */ #define CM_SPD24SEL 0x00020000 /* 24bit spdif: model 037 */ +#define CM_SPDIF_INVERSE 0x00010000 #define CM_ADCBITLEN_MASK 0x0000C000 #define CM_ADCBITLEN_16 0x00000000 @@ -131,6 +132,8 @@ #define CM_CH0_SRATE_176K 0x00000200 #define CM_CH0_SRATE_88K 0x00000100 +#define CM_SPDIF_INVERSE2 0x00000080 /* model 055? */ + #define CM_CH1FMT_MASK 0x0000000C #define CM_CH1FMT_SHIFT 2 #define CM_CH0FMT_MASK 0x00000003 @@ -201,6 +204,7 @@ #define CM_VIDWPPRT 0x00002000 #define CM_SFILENB 0x00001000 #define CM_MMODE_MASK 0x00000E00 +#define CM_SPDIF_SELECT 0x00000100 /* for model > 039 ? */ #define CM_ENCENTER 0x00000080 /* shared with FLINKON? */ #define CM_FLINKON 0x00000080 #define CM_FLINKOFF 0x00000040 @@ -347,7 +351,7 @@ unsigned int offset; /* physical address of the buffer */ unsigned int fmt; /* format bits */ int ch; /* channel (0/1) */ - int is_dac; /* is dac? */ + unsigned int is_dac; /* is dac? */ int bytes_per_frame; int shift; int ac3_shift; /* extra shift: 1 on soft ac3 mode */ @@ -388,6 +392,9 @@ unsigned int can_ac3_sw: 1; unsigned int can_ac3_hw: 1; unsigned int can_multi_ch: 1; + + unsigned int spdif_playback_avail: 1; /* spdif ready? */ + unsigned int spdif_playback_enabled: 1; /* spdif switch enabled? */ int spdif_counter; /* for software AC3 */ unsigned int dig_status; @@ -1133,11 +1140,13 @@ save_mixer_state(cm); spin_lock_irqsave(&cm->reg_lock, flags); + cm->spdif_playback_avail = up; if (up) { /* they are controlled via "IEC958 Output Switch" */ /* snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */ /* snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */ - snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); + if (cm->spdif_playback_enabled) + snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); setup_ac3(cm, do_ac3, rate); if (rate == 48000) @@ -2020,7 +2029,7 @@ CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0), CMIPCI_SB_VOL_MONO("PC Speaker Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3), CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15), - CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0), + CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 1), CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0), CMIPCI_MIXER_SW_MONO("Mic Boost", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1), CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7), @@ -2132,6 +2141,7 @@ DEFINE_BIT_SWITCH_ARG(spdif_0, CM_REG_FUNCTRL1, CM_SPDF_0, 0); DEFINE_BIT_SWITCH_ARG(spdo_48k, CM_REG_MISC_CTRL, CM_SPDF_AC97|CM_SPDIF48K, 0); #endif +DEFINE_BIT_SWITCH_ARG(spdif_in_1_2, CM_REG_MISC_CTRL, CM_SPDIF_SELECT, 0, 0); DEFINE_BIT_SWITCH_ARG(spdif_enable, CM_REG_LEGACY_CTRL, CM_ENSPDOUT, 0, 0); DEFINE_BIT_SWITCH_ARG(spdo2dac, CM_REG_FUNCTRL1, CM_SPDO2DAC, 0, 1); DEFINE_BIT_SWITCH_ARG(spdi_valid, CM_REG_MISC, CM_SPDVALID, 1, 0); @@ -2140,7 +2150,8 @@ DEFINE_SWITCH_ARG(spdo_5v, CM_REG_MISC_CTRL, CM_SPDO5V, 0, 0, 0); /* inverse: 0 = 5V */ DEFINE_BIT_SWITCH_ARG(spdif_loop, CM_REG_FUNCTRL1, CM_SPDFLOOP, 0, 1); DEFINE_BIT_SWITCH_ARG(spdi_monitor, CM_REG_MIXER1, CM_CDPLAY, 1, 0); -DEFINE_BIT_SWITCH_ARG(spdi_phase, CM_REG_MISC, 0x04, 0, 0); +DEFINE_BIT_SWITCH_ARG(spdi_phase, CM_REG_CHFORMAT, CM_SPDIF_INVERSE, 0, 0); +DEFINE_BIT_SWITCH_ARG(spdi_phase2, CM_REG_CHFORMAT, CM_SPDIF_INVERSE2, 0, 0); #if CM_CH_PLAY == 1 DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, 0, 0, 0); /* reversed */ #else @@ -2179,9 +2190,20 @@ static int snd_cmipci_spdout_enable_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { + cmipci_t *chip = snd_kcontrol_chip(kcontrol); int changed; changed = _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable); changed |= _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac); + if (changed) { + if (ucontrol->value.integer.value[0]) { + if (chip->spdif_playback_avail) + snd_cmipci_set_bit(chip, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); + } else { + if (chip->spdif_playback_avail) + snd_cmipci_clear_bit(chip, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); + } + } + chip->spdif_playback_enabled = ucontrol->value.integer.value[0]; return changed; } @@ -2213,17 +2235,19 @@ DEFINE_MIXER_SWITCH("IEC958 5V", spdo_5v), DEFINE_MIXER_SWITCH("IEC958 Loop", spdif_loop), DEFINE_MIXER_SWITCH("IEC958 In Monitor", spdi_monitor), - DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase), }; /* only for model 033/037 */ static snd_kcontrol_new_t snd_cmipci_old_mixer_switches[] __devinitdata = { DEFINE_MIXER_SWITCH("IEC958 Mix Analog", spdif_dac_out), + DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase), }; -/* only for model 039 */ +/* only for model 039 or later */ static snd_kcontrol_new_t snd_cmipci_extra_mixer_switches[] __devinitdata = { DEFINE_MIXER_SWITCH("Line-In As Bass", line_bass), + DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_1_2), + DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2), }; /* card control switches */ @@ -2283,7 +2307,7 @@ cm->spdif_pcm_ctl = kctl; if (cm->chip_version <= 37) { sw = snd_cmipci_old_mixer_switches; - for (idx = 0; idx < num_controls(snd_cmipci_extra_mixer_switches); idx++, sw++) { + for (idx = 0; idx < num_controls(snd_cmipci_old_mixer_switches); idx++, sw++) { err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); if (err < 0) return err; @@ -2573,10 +2597,10 @@ if (snd_opl3_create(card, iosynth, iosynth + 2, OPL3_HW_OPL3, 0, &cm->opl3) < 0) { - snd_printk("no OPL device at 0x%lx\n", iosynth); + printk(KERN_ERR "cmipci: no OPL device at 0x%lx\n", iosynth); } else { if ((err = snd_opl3_hwdep_new(cm->opl3, 0, 1, &cm->opl3hwdep)) < 0) - snd_printk("cannot create OPL3 hwdep\n"); + printk(KERN_ERR "cmipci: cannot create OPL3 hwdep\n"); } } @@ -2609,7 +2633,7 @@ if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, iomidi, 0, cm->irq, 0, &cm->rmidi)) < 0) { - snd_printk("cmipci: no UART401 device at 0x%lx\n", iomidi); + printk(KERN_ERR "cmipci: no UART401 device at 0x%lx\n", iomidi); } } @@ -2711,7 +2735,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("C-Media PCI soundcard not found or device busy\n"); + printk(KERN_ERR "C-Media PCI soundcard not found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/cs4281.c b/sound/pci/cs4281.c --- a/sound/pci/cs4281.c Thu Mar 7 18:17:42 2002 +++ b/sound/pci/cs4281.c Thu Mar 7 18:17:42 2002 @@ -268,7 +268,7 @@ #define BA0_SERC1_SO1EN (1<<0) /* Primary Output Port Enable */ #define BA0_SERC2 0x042c /* Serial Port Configuration 2 */ -#define BA0_SERC2_SI1F(x) (((x)&7)>>1) */ Primary Input Port Format */ +#define BA0_SERC2_SI1F(x) (((x)&7)>>1) /* Primary Input Port Format */ #define BA0_SERC2_AC97 (1<<1) #define BA0_SERC2_SI1EN (1<<0) /* Primary Input Port Enable */ @@ -1910,7 +1910,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("CS4281 soundcard not found or device busy\n"); + printk(KERN_ERR "CS4281 soundcard not found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c --- a/sound/pci/cs46xx/cs46xx.c Thu Mar 7 18:17:44 2002 +++ b/sound/pci/cs46xx/cs46xx.c Thu Mar 7 18:17:44 2002 @@ -183,7 +183,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("Sound Fusion CS46xx soundcard not found or device busy\n"); + printk(KERN_ERR "Sound Fusion CS46xx soundcard not found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c --- a/sound/pci/emu10k1/emu10k1.c Thu Mar 7 18:17:36 2002 +++ b/sound/pci/emu10k1/emu10k1.c Thu Mar 7 18:17:36 2002 @@ -210,7 +210,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("EMU10K1/Audigy soundcard not found or device busy\n"); + printk(KERN_ERR "EMU10K1/Audigy soundcard not found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c --- a/sound/pci/emu10k1/emu10k1_main.c Thu Mar 7 18:17:43 2002 +++ b/sound/pci/emu10k1/emu10k1_main.c Thu Mar 7 18:17:43 2002 @@ -218,6 +218,10 @@ */ outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG); + /* Enable analog/digital outs on audigy */ + if (emu->audigy) + outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG); + #if 0 { unsigned int tmp; @@ -274,7 +278,7 @@ snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_16K); snd_emu10k1_ptr_write(emu, TCB, 0, 0); if (emu->audigy) - snd_emu10k1_ptr_write(emu, A_DBG, 0, A_DBG_SINGLE_STEP_ADDR); + snd_emu10k1_ptr_write(emu, A_DBG, 0, A_DBG_SINGLE_STEP); else snd_emu10k1_ptr_write(emu, DBG, 0, 0x8000); @@ -625,18 +629,10 @@ } emu->fx8010.fxbus_mask = 0x303f; - if (extin_mask == 0) { - if (emu->audigy) - extin_mask = 0x000f; - else - extin_mask = 0x1fcf; - } - if (extout_mask == 0) { - if (emu->audigy) - extout_mask = 0x3fff; - else - extout_mask = 0x3fff; - } + if (extin_mask == 0) + extin_mask = 0x1fcf; + if (extout_mask == 0) + extout_mask = 0x3fff; emu->fx8010.extin_mask = extin_mask; emu->fx8010.extout_mask = extout_mask; diff -Nru a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c --- a/sound/pci/emu10k1/emufx.c Thu Mar 7 18:17:36 2002 +++ b/sound/pci/emu10k1/emufx.c Thu Mar 7 18:17:36 2002 @@ -95,12 +95,12 @@ /* 0x05 */ NULL, /* 0x06 */ NULL, /* 0x07 */ NULL, - /* 0x08 */ NULL, - /* 0x09 */ NULL, + /* 0x08 */ "Line/Mic 2 Left", + /* 0x09 */ "Line/Mic 2 Right", /* 0x0a */ NULL, /* 0x0b */ NULL, - /* 0x0c */ NULL, - /* 0x0d */ NULL, + /* 0x0c */ "Aux2 Left", + /* 0x0d */ "Aux2 Right", /* 0x0e */ NULL, /* 0x0f */ NULL }; @@ -141,14 +141,14 @@ }; static char *audigy_outs[32] = { - /* 0x00 */ NULL, - /* 0x01 */ NULL, - /* 0x02 */ NULL, - /* 0x03 */ NULL, + /* 0x00 */ "Digital Front Left", + /* 0x01 */ "Digital Front Right", + /* 0x02 */ "Digital Center", + /* 0x03 */ "Digital LEF", /* 0x04 */ "Headphone Left", /* 0x05 */ "Headphone Right", - /* 0x06 */ NULL, - /* 0x07 */ NULL, + /* 0x06 */ "Digital Rear Left", + /* 0x07 */ "Digital Rear Right", /* 0x08 */ "Front Left", /* 0x09 */ "Front Right", /* 0x0a */ "Center", @@ -157,14 +157,14 @@ /* 0x0d */ NULL, /* 0x0e */ "Rear Left", /* 0x0f */ "Rear Right", - /* 0x10 */ NULL, - /* 0x11 */ NULL, - /* 0x12 */ NULL, - /* 0x13 */ NULL, + /* 0x10 */ "AC97 Front Left", + /* 0x11 */ "AC97 Front Right", + /* 0x12 */ "ADC Caputre Left", + /* 0x13 */ "ADC Capture Right", /* 0x14 */ NULL, /* 0x15 */ NULL, - /* 0x16 */ "ADC Capture Left", - /* 0x17 */ "ADC Capture Right", + /* 0x16 */ NULL, + /* 0x17 */ NULL, /* 0x18 */ NULL, /* 0x19 */ NULL, /* 0x1a */ NULL, @@ -1034,7 +1034,7 @@ /* stop FX processor - this may be dangerous, but it's better to miss some samples than generate wrong ones - [jk] */ if (emu->audigy) - snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_SINGLE_STEP_ADDR); + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_SINGLE_STEP); else snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_SINGLE_STEP); /* ok, do the main job */ @@ -1220,36 +1220,34 @@ ptr = 0; nctl = 0; playback = 10; - capture = playback + 6; /* 5+1 channels */ - gpr = capture + 2; /* so far only a stereo capture */ + capture = playback + 10; /* we reserve 10 voices */ + gpr = capture + 10; tmp = 0x80; /* stop FX processor */ - snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP_ADDR); - -#define A_C_ZERO 0x0c0 + snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP); /* Wave Playback */ - A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_ZERO, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT)); - A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_ZERO, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT)); snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Playback Volume", gpr, 100); gpr += 2; /* Wave Surround Playback */ - A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_ZERO, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR)); - A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_ZERO, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT)); snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Surround Playback Volume", gpr, 0); gpr += 2; /* Wave Center Playback */ /* Center = sub = Left/2 + Right/2 */ A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_FXBUS(FXBUS_PCM_LEFT), 0xcd, A_FXBUS(FXBUS_PCM_RIGHT)); - A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_C_ZERO, A_GPR(gpr), A_GPR(tmp)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_GPR(tmp)); snd_emu10k1_init_mono_control(&controls[nctl++], "Wave Center Playback Volume", gpr, 0); gpr++; /* Wave LFE Playback */ - A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_C_ZERO, A_GPR(gpr), A_GPR(tmp)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_C_00000000, A_GPR(gpr), A_GPR(tmp)); snd_emu10k1_init_mono_control(&controls[nctl++], "Wave LFE Playback Volume", gpr, 0); gpr++; @@ -1260,8 +1258,8 @@ gpr += 2; /* Wave Capture */ - A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_C_ZERO, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT)); - A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_C_ZERO, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT)); + A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT)); snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Capture Volume", gpr, 0); gpr += 2; @@ -1271,14 +1269,20 @@ snd_emu10k1_init_stereo_control(&controls[nctl++], "Music Capture Volume", gpr, 0); gpr += 2; + /* Surround Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Surround Playback Volume", gpr, 80); + gpr += 2; + /* Center Playback */ A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER)); - snd_emu10k1_init_mono_control(&controls[nctl++], "Center Playback Volume", gpr, 0); + snd_emu10k1_init_mono_control(&controls[nctl++], "Center Playback Volume", gpr, 80); gpr++; /* LFE Playback */ A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE)); - snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 0); + snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 80); gpr++; /* @@ -1286,54 +1290,70 @@ */ #define A_ADD_VOLUME_IN(var,vol,input) \ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) - if (emu->fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extout_mask & (1<<(out))) A_PUT_OUTPUT(out,src) +#define A_PUT_OUTPUT(out,src) A_OP(icode, &ptr, iACC3, A_EXTOUT(out), A_C_00000000, A_C_00000000, A_GPR(src)) #define A_PUT_STEREO_OUTPUT(out1,out2,src) \ - if (emu->fx8010.extout_mask & ((1<<(out1))|(1<<(out2)))) {\ - A_PUT_OUTPUT(out1,src);\ - A_PUT_OUTPUT(out2,src+1);} + {A_PUT_OUTPUT(out1,src); A_PUT_OUTPUT(out2,src+1);} /* digital outputs */ A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback); A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2); - A_PUT_MONO_OUTPUT(A_EXTOUT_CENTER, playback+4); - A_PUT_MONO_OUTPUT(A_EXTOUT_LFE, playback+5); + A_PUT_OUTPUT(A_EXTOUT_CENTER, playback+4); + A_PUT_OUTPUT(A_EXTOUT_LFE, playback+5); /* analog speakers */ - A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback); + //A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback); + A_PUT_STEREO_OUTPUT(A_EXTOUT_AC97_L, A_EXTOUT_AC97_R, playback); A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2); - A_PUT_MONO_OUTPUT(A_EXTOUT_ACENTER, playback+4); - A_PUT_MONO_OUTPUT(A_EXTOUT_ALFE, playback+5); + A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4); + A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5); /* headphone */ A_PUT_STEREO_OUTPUT(A_EXTOUT_HEADPHONE_L, A_EXTOUT_HEADPHONE_R, playback); @@ -1946,7 +1966,7 @@ { /* stop processor */ if (emu->audigy) - snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = A_DBG_SINGLE_STEP_ADDR); + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = A_DBG_SINGLE_STEP); else snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = EMU10K1_DBG_SINGLE_STEP); } @@ -2127,7 +2147,7 @@ if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (emu->audigy) - snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP_ADDR); + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP); else snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP); return 0; @@ -2160,12 +2180,12 @@ if (addr > 0x1ff) return -EINVAL; if (emu->audigy) - snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP_ADDR | addr); + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | addr); else snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | addr); udelay(10); if (emu->audigy) - snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP_ADDR | A_DBG_STEP_ADDR | addr); + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | A_DBG_STEP_ADDR | addr); else snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | EMU10K1_DBG_STEP | addr); return 0; diff -Nru a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c --- a/sound/pci/emu10k1/emumixer.c Thu Mar 7 18:17:41 2002 +++ b/sound/pci/emu10k1/emumixer.c Thu Mar 7 18:17:41 2002 @@ -107,12 +107,40 @@ put: snd_emu10k1_spdif_put }; -/* FIXME: audigy has more routing/effects */ + +static void update_emu10k1_fxrt(emu10k1_t *emu, int voice, unsigned char *route) +{ + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, A_FXRT1, voice, + snd_emu10k1_compose_audigy_fxrt1(route)); + snd_emu10k1_ptr_write(emu, A_FXRT2, voice, + snd_emu10k1_compose_audigy_fxrt2(route)); + } else { + snd_emu10k1_ptr_write(emu, FXRT, voice, + snd_emu10k1_compose_send_routing(route)); + } +} + +static void update_emu10k1_send_volume(emu10k1_t *emu, int voice, unsigned char *volume) +{ + snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, volume[0]); + snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, volume[1]); + snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, volume[2]); + snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, volume[3]); + if (emu->audigy) { + unsigned int val = ((unsigned int)volume[4] << 24) | + ((unsigned int)volume[5] << 16) | + ((unsigned int)volume[6] << 8) | + (unsigned int)volume[7]; + snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, val); + } +} + static int snd_emu10k1_send_routing_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { emu10k1_t *emu = snd_kcontrol_chip(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 3*4; + uinfo->count = emu->audigy ? 3*8 : 3*4; uinfo->value.integer.min = 0; uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f; return 0; @@ -125,13 +153,14 @@ emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; emu10k1_t *emu = snd_kcontrol_chip(kcontrol); int voice, idx; + int num_efx = emu->audigy ? 8 : 4; + int mask = emu->audigy ? 0x3f : 0x0f; spin_lock_irqsave(&emu->reg_lock, flags); for (voice = 0; voice < 3; voice++) - for (idx = 0; idx < 4; idx++) - ucontrol->value.integer.value[(voice * 4) + idx] = emu->audigy ? - ((mix->send_routing[voice] >> (idx * 8)) & 0x3f) : - ((mix->send_routing[voice] >> (idx * 4)) & 0x0f); + for (idx = 0; idx < num_efx; idx++) + ucontrol->value.integer.value[(voice * num_efx) + idx] = + mix->send_routing[voice][idx] & mask; spin_unlock_irqrestore(&emu->reg_lock, flags); return 0; } @@ -143,44 +172,27 @@ emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; emu10k1_t *emu = snd_kcontrol_chip(kcontrol); int change = 0, voice, idx, val; + int num_efx = emu->audigy ? 8 : 4; + int mask = emu->audigy ? 0x3f : 0x0f; spin_lock_irqsave(&emu->reg_lock, flags); for (voice = 0; voice < 3; voice++) - for (idx = 0; idx < 4; idx++) { - val = ucontrol->value.integer.value[(voice * 4) + idx]; - if (emu->audigy) { - int shift = idx * 8; - val &= 0x3f; - if (((mix->send_routing[voice] >> shift) & 0x3f) != val) { - mix->send_routing[voice] &= ~(0x3f << shift); - mix->send_routing[voice] |= val << shift; - change = 1; - } - } else { - int shift = idx * 4; - val = ucontrol->value.integer.value[(voice * 4) + idx] & 15; - if (((mix->send_routing[voice] >> shift) & 15) != val) { - mix->send_routing[voice] &= ~(15 << shift); - mix->send_routing[voice] |= val << shift; - change = 1; - } + for (idx = 0; idx < num_efx; idx++) { + val = ucontrol->value.integer.value[(voice * num_efx) + idx] & mask; + if (mix->send_routing[voice][idx] != val) { + mix->send_routing[voice][idx] = val; + change = 1; } } if (change && mix->epcm) { if (mix->epcm->voices[0] && mix->epcm->voices[1]) { - if (emu->audigy) { - snd_emu10k1_ptr_write(emu, A_FXRT1, mix->epcm->voices[0]->number, mix->send_routing[1]); - snd_emu10k1_ptr_write(emu, A_FXRT1, mix->epcm->voices[1]->number, mix->send_routing[2]); - } else { - snd_emu10k1_ptr_write(emu, FXRT, mix->epcm->voices[0]->number, mix->send_routing[1] << 16); - snd_emu10k1_ptr_write(emu, FXRT, mix->epcm->voices[1]->number, mix->send_routing[2] << 16); - } + update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number, + &mix->send_routing[1][0]); + update_emu10k1_fxrt(emu, mix->epcm->voices[1]->number, + &mix->send_routing[2][0]); } else if (mix->epcm->voices[0]) { - if (emu->audigy) { - snd_emu10k1_ptr_write(emu, A_FXRT1, mix->epcm->voices[0]->number, mix->send_routing[0]); - } else { - snd_emu10k1_ptr_write(emu, FXRT, mix->epcm->voices[0]->number, mix->send_routing[0] << 16); - } + update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number, + &mix->send_routing[0][0]); } } spin_unlock_irqrestore(&emu->reg_lock, flags); @@ -199,8 +211,9 @@ static int snd_emu10k1_send_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 3*4; + uinfo->count = emu->audigy ? 3*8 : 3*4; uinfo->value.integer.min = 0; uinfo->value.integer.max = 255; return 0; @@ -213,10 +226,11 @@ emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; emu10k1_t *emu = snd_kcontrol_chip(kcontrol); int idx; + int num_efx = emu->audigy ? 8 : 4; spin_lock_irqsave(&emu->reg_lock, flags); - for (idx = 0; idx < 3*4; idx++) - ucontrol->value.integer.value[idx] = mix->send_volume[idx/4][idx%4]; + for (idx = 0; idx < 3*num_efx; idx++) + ucontrol->value.integer.value[idx] = mix->send_volume[idx/num_efx][idx%num_efx]; spin_unlock_irqrestore(&emu->reg_lock, flags); return 0; } @@ -228,34 +242,25 @@ emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; emu10k1_t *emu = snd_kcontrol_chip(kcontrol); int change = 0, idx, val; + int num_efx = emu->audigy ? 8 : 4; spin_lock_irqsave(&emu->reg_lock, flags); - for (idx = 0; idx < 3*4; idx++) { + for (idx = 0; idx < 3*num_efx; idx++) { val = ucontrol->value.integer.value[idx] & 255; - if (mix->send_volume[idx/4][idx%4] != val) { - mix->send_volume[idx/4][idx%4] = val; + if (mix->send_volume[idx/num_efx][idx%num_efx] != val) { + mix->send_volume[idx/num_efx][idx%num_efx] = val; change = 1; } } if (change && mix->epcm) { - u32 voice; if (mix->epcm->voices[0] && mix->epcm->voices[1]) { - voice = mix->epcm->voices[0]->number; - snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, mix->send_volume[1][0]); - snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, mix->send_volume[1][1]); - snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, mix->send_volume[1][2]); - snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, mix->send_volume[1][3]); - voice = mix->epcm->voices[1]->number; - snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, mix->send_volume[2][0]); - snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, mix->send_volume[2][1]); - snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, mix->send_volume[2][2]); - snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, mix->send_volume[2][3]); + update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number, + &mix->send_volume[1][0]); + update_emu10k1_send_volume(emu, mix->epcm->voices[1]->number, + &mix->send_volume[2][0]); } else if (mix->epcm->voices[0]) { - voice = mix->epcm->voices[0]->number; - snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, mix->send_volume[0][0]); - snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, mix->send_volume[0][1]); - snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, mix->send_volume[0][2]); - snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, mix->send_volume[0][3]); + update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number, + &mix->send_volume[0][0]); } } spin_unlock_irqrestore(&emu->reg_lock, flags); @@ -380,6 +385,71 @@ put: snd_emu10k1_shared_spdif_put }; +#if 0 // XXX: not working yet.. +/* + * Audigy analog / digital switches + */ +static int audigy_output_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int audigy_output_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned int mask = (unsigned int)kcontrol->private_value; + + ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & mask ? 0 : 1; + return 0; +} + +static int audigy_output_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + unsigned long flags; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned int mask = (unsigned int)kcontrol->private_value; + unsigned int reg, oreg; + int change; + + spin_lock_irqsave(&emu->reg_lock, flags); + reg = oreg = inl(emu->port + A_IOCFG); + reg &= ~mask; + reg |= ucontrol->value.integer.value[0] & 1 ? 0 : mask; + change = (reg != oreg); + if (change) + outl(reg, emu->port + A_IOCFG); + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t audigy_output_analog = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Audigy Analog Output Switch", + info: audigy_output_info, + get: audigy_output_get, + put: audigy_output_put, + private_value: 0x40, +}; + +static snd_kcontrol_new_t audigy_output_digital = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Audigy Digital Output Switch", + info: audigy_output_info, + get: audigy_output_get, + put: audigy_output_put, + private_value: 0x04, +}; +#endif // XXX + + +/* + */ static void snd_emu10k1_mixer_free_ac97(ac97_t *ac97) { emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return); @@ -407,6 +477,7 @@ for (pcm = 0; pcm < 32; pcm++) { emu10k1_pcm_mixer_t *mix; + int v; mix = &emu->pcm_mixer[pcm]; mix->epcm = NULL; @@ -417,11 +488,10 @@ kctl->id.index = pcm; if ((err = snd_ctl_add(card, kctl))) return err; - if (emu->audigy) { - mix->send_routing[0] = mix->send_routing[1] = mix->send_routing[2] = 0x3210; - } else { - mix->send_routing[0] = mix->send_routing[1] = mix->send_routing[2] = 0x03020100; - } + for (v = 0; v < 4; v++) + mix->send_routing[0][v] = + mix->send_routing[1][v] = + mix->send_routing[2][v] = v; if ((kctl = mix->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu)) == NULL) return -ENOMEM; @@ -457,10 +527,23 @@ return err; } - if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL) - return -ENOMEM; - if ((err = snd_ctl_add(card, kctl))) - return err; + if (emu->audigy) { +#if 0 // XXX + if ((kctl = snd_ctl_new1(&audigy_output_analog, emu)) == NULL) + return -ENOMEM; + if ((err = snd_ctl_add(card, kctl))) + return err; + if ((kctl = snd_ctl_new1(&audigy_output_digital, emu)) == NULL) + return -ENOMEM; + if ((err = snd_ctl_add(card, kctl))) + return err; +#endif // XXX + } else { + if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL) + return -ENOMEM; + if ((err = snd_ctl_add(card, kctl))) + return err; + } return 0; } diff -Nru a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c --- a/sound/pci/emu10k1/emupcm.c Thu Mar 7 18:17:36 2002 +++ b/sound/pci/emu10k1/emupcm.c Thu Mar 7 18:17:36 2002 @@ -237,8 +237,8 @@ emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number]; unsigned int silent_page, tmp; int voice, stereo, w_16; - unsigned char attn, send_a, send_b, send_c, send_d; - unsigned short send_routing; + unsigned char attn, send_amount[8]; + unsigned char send_routing[8]; unsigned long flags; unsigned int pitch_target; @@ -260,15 +260,16 @@ /* volume parameters */ if (extra) { attn = 0; - send_routing = emu->audigy ? 0x03020100 : 0x3210; - send_a = send_b = send_c = send_d = 0x00; + memset(send_routing, 0, sizeof(send_routing)); + send_routing[0] = 0; + send_routing[1] = 1; + send_routing[2] = 2; + send_routing[3] = 3; + memset(send_amount, 0, sizeof(send_amount)); } else { tmp = stereo ? (master ? 1 : 2) : 0; - send_a = mix->send_volume[tmp][0]; - send_b = mix->send_volume[tmp][1]; - send_c = mix->send_volume[tmp][2]; - send_d = mix->send_volume[tmp][3]; - send_routing = mix->send_routing[tmp]; + memcpy(send_routing, &mix->send_routing[tmp][0], 8); + memcpy(send_amount, &mix->send_volume[tmp][0], 8); } if (master) { @@ -290,16 +291,29 @@ // setup routing if (emu->audigy) { - snd_emu10k1_ptr_write(emu, A_FXRT1, voice, send_routing); - snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, 0); /* no effects */ - snd_emu10k1_ptr_write(emu, A_FXRT2, voice, 0); /* channels EFGH */ + snd_emu10k1_ptr_write(emu, A_FXRT1, voice, + ((unsigned int)send_routing[3] << 24) | + ((unsigned int)send_routing[2] << 16) | + ((unsigned int)send_routing[1] << 8) | + (unsigned int)send_routing[0]); + snd_emu10k1_ptr_write(emu, A_FXRT2, voice, + ((unsigned int)send_routing[7] << 24) | + ((unsigned int)send_routing[6] << 16) | + ((unsigned int)send_routing[5] << 8) | + (unsigned int)send_routing[4]); + snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, + ((unsigned int)send_amount[4] << 24) | + ((unsigned int)send_amount[5] << 16) | + ((unsigned int)send_amount[6] << 8) | + (unsigned int)send_amount[7]); } else - snd_emu10k1_ptr_write(emu, FXRT, voice, send_routing << 16); + snd_emu10k1_ptr_write(emu, FXRT, voice, + snd_emu10k1_compose_send_routing(send_routing)); // Stop CA // Assumption that PT is already 0 so no harm overwriting - snd_emu10k1_ptr_write(emu, PTRX, voice, (send_a << 8) | send_b); - snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_d << 24)); - snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_c << 24)); + snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]); + snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24)); + snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24)); pitch_target = emu10k1_calc_pitch_target(runtime->rate); snd_emu10k1_ptr_write(emu, CCCA, voice, evoice->epcm->ccca_start_addr | emu10k1_select_interprom(pitch_target) | @@ -753,7 +767,7 @@ emu10k1_pcm_t *epcm; emu10k1_pcm_mixer_t *mix; snd_pcm_runtime_t *runtime = substream->runtime; - int err; + int i, err; epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); if (epcm == NULL) @@ -773,8 +787,8 @@ return err; } mix = &emu->pcm_mixer[substream->number]; - mix->send_routing[0] = mix->send_routing[1] = mix->send_routing[2] = - emu->audigy ? 0x03020100 : 0x3210; + for (i = 0; i < 4; i++) + mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i; memset(&mix->send_volume, 0, sizeof(mix->send_volume)); mix->send_volume[0][0] = mix->send_volume[0][1] = mix->send_volume[1][0] = mix->send_volume[2][1] = 255; diff -Nru a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c --- a/sound/pci/emu10k1/emuproc.c Thu Mar 7 18:17:42 2002 +++ b/sound/pci/emu10k1/emuproc.c Thu Mar 7 18:17:42 2002 @@ -250,6 +250,7 @@ } } emu->proc_entry = entry; + entry = NULL; if ((entry = snd_info_create_card_entry(emu->card, "fx8010_gpr", emu->card->proc_root)) != NULL) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; @@ -262,6 +263,7 @@ } } emu->proc_entry_fx8010_gpr = entry; + entry = NULL; if (!emu->audigy && (entry = snd_info_create_card_entry(emu->card, "fx8010_tram_data", emu->card->proc_root)) != NULL) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; @@ -274,6 +276,7 @@ } } emu->proc_entry_fx8010_tram_data = entry; + entry = NULL; if (!emu->audigy && (entry = snd_info_create_card_entry(emu->card, "fx8010_tram_addr", emu->card->proc_root)) != NULL) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; @@ -286,6 +289,7 @@ } } emu->proc_entry_fx8010_tram_addr = entry; + entry = NULL; if ((entry = snd_info_create_card_entry(emu->card, "fx8010_code", emu->card->proc_root)) != NULL) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; @@ -298,6 +302,7 @@ } } emu->proc_entry_fx8010_code = entry; + entry = NULL; if ((entry = snd_info_create_card_entry(emu->card, "fx8010_acode", emu->card->proc_root)) != NULL) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = emu; diff -Nru a/sound/pci/ens1370.c b/sound/pci/ens1370.c --- a/sound/pci/ens1370.c Thu Mar 7 18:17:41 2002 +++ b/sound/pci/ens1370.c Thu Mar 7 18:17:41 2002 @@ -2049,7 +2049,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("Ensoniq AudioPCI soundcard not found or device busy\n"); + printk(KERN_ERR "Ensoniq AudioPCI soundcard not found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/es1938.c b/sound/pci/es1938.c --- a/sound/pci/es1938.c Thu Mar 7 18:17:45 2002 +++ b/sound/pci/es1938.c Thu Mar 7 18:17:45 2002 @@ -1626,7 +1626,7 @@ SLSB_REG(chip, FMLOWADDR), SLSB_REG(chip, FMHIGHADDR), OPL3_HW_OPL3, 1, &opl3) < 0) { - snd_printk("OPL3 not detected at 0x%lx\n", + printk(KERN_ERR "es1938: OPL3 not detected at 0x%lx\n", SLSB_REG(chip, FMLOWADDR)); } else { if ((err = snd_opl3_timer_new(opl3, 0, 1)) < 0) { @@ -1640,7 +1640,7 @@ } if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) { - snd_printk("unable to initialize MPU-401\n"); + printk(KERN_ERR "es1938: unable to initialize MPU-401\n"); } #ifndef LINUX_2_2 chip->gameport.io = chip->game_port; @@ -1683,7 +1683,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("ESS Solo-1 soundcard not found or device busy\n"); + printk(KERN_ERR "ESS Solo-1 soundcard not found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/es1968.c b/sound/pci/es1968.c --- a/sound/pci/es1968.c Thu Mar 7 18:17:45 2002 +++ b/sound/pci/es1968.c Thu Mar 7 18:17:45 2002 @@ -2720,7 +2720,7 @@ if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, chip->io_port + ESM_MPU401_PORT, 1, chip->irq, 0, &chip->rmidi)) < 0) { - printk(KERN_INFO "es1968: skipping MPU-401 MIDI support..\n"); + printk(KERN_WARNING "es1968: skipping MPU-401 MIDI support..\n"); } /* card switches */ @@ -2783,7 +2783,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("ESS Maestro soundcard not found or device busy\n"); + printk(KERN_ERR "ESS Maestro soundcard not found or device busy\n"); #endif return err; } @@ -2792,7 +2792,7 @@ leave the speaking emitting an annoying noise, so we catch shutdown events. */ if (register_reboot_notifier(&snd_es1968_nb)) { - snd_printk("reboot notifier registration failed; may make noise at shutdown.\n"); + printk(KERN_ERR "reboot notifier registration failed; may make noise at shutdown.\n"); } #endif return 0; diff -Nru a/sound/pci/fm801.c b/sound/pci/fm801.c --- a/sound/pci/fm801.c Thu Mar 7 18:17:40 2002 +++ b/sound/pci/fm801.c Thu Mar 7 18:17:40 2002 @@ -1104,7 +1104,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("ForteMedia FM801 soundcard not found or device busy\n"); + printk(KERN_ERR "ForteMedia FM801 soundcard not found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/ice1712.c b/sound/pci/ice1712.c --- a/sound/pci/ice1712.c Thu Mar 7 18:17:45 2002 +++ b/sound/pci/ice1712.c Thu Mar 7 18:17:45 2002 @@ -1718,7 +1718,7 @@ if (rpcm) *rpcm = pcm; - printk("Consumer PCM code does not work well at the moment --jk\n"); + printk(KERN_WARNING "Consumer PCM code does not work well at the moment --jk\n"); return 0; } @@ -2410,7 +2410,7 @@ ac97.private_data = ice; ac97.private_free = snd_ice1712_mixer_free_ac97; if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) { - snd_printk("ice1712: cannot initialize ac97 for consumer, skipped\n"); + printk(KERN_WARNING "ice1712: cannot initialize ac97 for consumer, skipped\n"); // return err; } else { if ((err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice))) < 0) @@ -2428,7 +2428,7 @@ ac97.private_data = ice; ac97.private_free = snd_ice1712_mixer_free_ac97; if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) { - snd_printk("ice1712: cannot initialize pro ac97, skipped\n"); + printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n"); // return err; } return 0; @@ -4329,7 +4329,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("ICE1712 soundcard not found or device busy\n"); + printk(KERN_ERR "ICE1712 soundcard not found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c --- a/sound/pci/intel8x0.c Thu Mar 7 18:17:37 2002 +++ b/sound/pci/intel8x0.c Thu Mar 7 18:17:37 2002 @@ -35,6 +35,7 @@ #include #include #include +#include #define SNDRV_GET_ID #include @@ -52,10 +53,24 @@ "{SiS,SI7012}," "{NVidia,NForce Audio}}"); +#define SUPPORT_JOYSTICK 1 +#define SUPPORT_MIDI 1 + static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +#ifdef SUPPORT_JOYSTICK +static int snd_joystick_port[SNDRV_CARDS] = +#ifdef CONFIG_ISA + {0x200}; /* enable as default */ +#else + {0}; /* disabled */ +#endif +#endif +#ifdef SUPPORT_MIDI +static int snd_mpu_port[SNDRV_CARDS]; /* disabled */ +#endif MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(snd_index, "Index value for Intel i8x0 soundcard."); @@ -69,6 +84,16 @@ MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (0 = auto-detect)."); MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:0"); +#ifdef SUPPORT_JOYSTICK +MODULE_PARM(snd_joystick_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_joystick_port, "Joystick port address for Intel i8x0 soundcard. (0 = disabled)"); +MODULE_PARM_SYNTAX(snd_joystick_port, SNDRV_ENABLED ",allows:{{0},{0x200}},dialog:list"); +#endif +#ifdef SUPPORT_MIDI +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_port, "MPU401 port # for Intel i8x0 driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x300}},dialog:list"); +#endif /* * Direct registers @@ -231,6 +256,8 @@ ac97_t *ac97; ac97_t *ac97sec; + snd_rawmidi_t *rmidi; + spinlock_t reg_lock; spinlock_t ac97_lock; snd_info_entry_t *proc_entry; @@ -256,9 +283,9 @@ { 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801BA */ { 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH3 */ { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */ - { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */ { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7012 */ { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* NFORCE */ + { 0x764d, 0x1022, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */ { 0, } }; @@ -1087,6 +1114,7 @@ { snd_card_t *card = chip->card; + chip->in_suspend = 1; snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) goto __skip; @@ -1364,6 +1392,7 @@ { PCI_DEVICE_ID_INTEL_ICH3, "Intel ICH3" }, { PCI_DEVICE_ID_SI_7012, "SiS SI7012" }, { PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia NForce" }, + { 0x1022, "AMD-8111" }, { 0, 0 }, }; @@ -1416,6 +1445,16 @@ } } + if (snd_mpu_port[dev] == 0x300 || snd_mpu_port[dev] == 0x330) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, + snd_mpu_port[dev], 0, + -1, 0, &chip->rmidi)) < 0) { + printk(KERN_ERR "intel8x0: no UART401 device at 0x%x, skipping.\n", snd_mpu_port[dev]); + snd_mpu_port[dev] = 0; + } + } else + snd_mpu_port[dev] = 0; + sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->port, chip->irq); @@ -1450,16 +1489,77 @@ #endif }; + +#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI) +/* + * initialize joystick/midi addresses + */ + +static int __devinit snd_intel8x0_joystick_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + if (snd_joystick_port[dev] > 0 || snd_mpu_port[dev] > 0) { + u16 val; + pci_read_config_word(pci, 0xe6, &val); + if (snd_joystick_port[dev] > 0) + val |= 0x100; + if (snd_mpu_port[dev] == 0x300 || snd_mpu_port[dev] == 0x330) + val |= 0x20; + pci_write_config_word(pci, 0xe6, val | 0x100); + + if (snd_mpu_port[dev] == 0x300 || snd_mpu_port[dev] == 0x330) { + u8 b; + pci_read_config_byte(pci, 0xe2, &b); + if (snd_mpu_port[dev] == 0x300) + b |= 0x08; + else + b &= ~0x08; + pci_write_config_byte(pci, 0xe2, b); + } + } + return 0; +} + +static struct pci_device_id snd_intel8x0_joystick_ids[] __devinitdata = { + { 0x8086, 0x2410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 82801AA */ + { 0x8086, 0x2420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 82901AB */ + { 0x8086, 0x2440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH2 */ + { 0x8086, 0x244c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH2M */ + { 0x8086, 0x248c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH3 */ + // { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 440MX */ + // { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SI7012 */ + { 0x10de, 0x01b2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* NFORCE */ + { 0, } +}; + +static struct pci_driver joystick_driver = { + name: "Intel ICH Joystick", + id_table: snd_intel8x0_joystick_ids, + probe: snd_intel8x0_joystick_probe, +}; +#endif + static int __init alsa_card_intel8x0_init(void) { int err; if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("Intel ICH soundcard not found or device busy\n"); + printk(KERN_ERR "Intel ICH soundcard not found or device busy\n"); #endif return err; } +#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI) + pci_module_init(&joystick_driver); +#endif return 0; } @@ -1467,6 +1567,9 @@ static void __exit alsa_card_intel8x0_exit(void) { pci_unregister_driver(&driver); +#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI) + pci_unregister_driver(&joystick_driver); +#endif } module_init(alsa_card_intel8x0_init) diff -Nru a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c --- a/sound/pci/korg1212/korg1212.c Thu Mar 7 18:17:36 2002 +++ b/sound/pci/korg1212/korg1212.c Thu Mar 7 18:17:36 2002 @@ -1953,27 +1953,27 @@ korg1212->res_iomem = request_mem_region(korg1212->iomem, iomem_size, "korg1212"); if (korg1212->res_iomem == NULL) { - PRINTK("unable to grab region 0x%lx-0x%lx\n", + snd_printk("unable to grab region 0x%lx-0x%lx\n", korg1212->iomem, korg1212->iomem + iomem_size - 1); return -EBUSY; } korg1212->res_ioport = request_region(korg1212->ioport, ioport_size, "korg1212"); if (korg1212->res_ioport == NULL) { - PRINTK("unable to grab region 0x%lx-0x%lx\n", + snd_printk("unable to grab region 0x%lx-0x%lx\n", korg1212->ioport, korg1212->ioport + ioport_size - 1); return -EBUSY; } korg1212->res_iomem2 = request_mem_region(korg1212->iomem2, iomem2_size, "korg1212"); if (korg1212->res_iomem2 == NULL) { - PRINTK("unable to grab region 0x%lx-0x%lx\n", + snd_printk("unable to grab region 0x%lx-0x%lx\n", korg1212->iomem2, korg1212->iomem2 + iomem2_size - 1); return -EBUSY; } if ((korg1212->iobase = (unsigned long) ioremap(korg1212->iomem, iomem_size)) == 0) { - PRINTK("unable to remap memory region 0x%lx-0x%lx\n", korg1212->iobase, + snd_printk("unable to remap memory region 0x%lx-0x%lx\n", korg1212->iobase, korg1212->iobase + iomem_size - 1); return -EBUSY; } @@ -1983,7 +1983,7 @@ "korg1212", (void *) korg1212); if (err) { - PRINTK("unable to grab IRQ %d\n", pci->irq); + snd_printk("unable to grab IRQ %d\n", pci->irq); return -EBUSY; } @@ -2034,7 +2034,7 @@ korg1212->sharedBufferPhy = (unsigned long)phys_addr; if (korg1212->sharedBufferPtr == NULL) { - PRINTK("can not allocate shared buffer memory (%d bytes)\n", sizeof(KorgSharedBuffer)); + snd_printk("can not allocate shared buffer memory (%d bytes)\n", sizeof(KorgSharedBuffer)); return -ENOMEM; } @@ -2050,7 +2050,7 @@ korg1212->PlayDataPhy = (u32)phys_addr; if (korg1212->playDataBufsPtr == NULL) { - PRINTK("can not allocate play data buffer memory (%d bytes)\n", korg1212->DataBufsSize); + snd_printk("can not allocate play data buffer memory (%d bytes)\n", korg1212->DataBufsSize); return -ENOMEM; } @@ -2063,7 +2063,7 @@ korg1212->RecDataPhy = (u32)phys_addr; if (korg1212->recordDataBufsPtr == NULL) { - PRINTK("can not allocate record data buffer memory (%d bytes)\n", korg1212->DataBufsSize); + snd_printk("can not allocate record data buffer memory (%d bytes)\n", korg1212->DataBufsSize); return -ENOMEM; } @@ -2091,7 +2091,7 @@ korg1212->dspMemPhy = (u32)phys_addr; if (korg1212->dspMemPtr == NULL) { - PRINTK("can not allocate dsp code memory (%d bytes)\n", korg1212->dspCodeSize); + snd_printk("can not allocate dsp code memory (%d bytes)\n", korg1212->dspCodeSize); return -ENOMEM; } @@ -2114,7 +2114,7 @@ if (snd_korg1212_downloadDSPCode(korg1212)) return -EBUSY; - PRINTK("dspMemPhy = %08x U[%08x]\n" + printk(KERN_INFO "dspMemPhy = %08x U[%08x]\n" "PlayDataPhy = %08x L[%08x]\n" "RecDataPhy = %08x L[%08x]\n" "VolumeTablePhy = %08x L[%08x]\n" @@ -2312,7 +2312,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - PRINTK("No Korg 1212IO cards found\n"); + printk(KERN_ERR "No Korg 1212IO cards found\n"); #endif return err; } diff -Nru a/sound/pci/maestro3.c b/sound/pci/maestro3.c --- a/sound/pci/maestro3.c Thu Mar 7 18:17:37 2002 +++ b/sound/pci/maestro3.c Thu Mar 7 18:17:37 2002 @@ -2676,7 +2676,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("Maestro3/Allegro soundcard not found or device busy\n"); + printk(KERN_ERR "Maestro3/Allegro soundcard not found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c --- a/sound/pci/nm256/nm256.c Thu Mar 7 18:17:41 2002 +++ b/sound/pci/nm256/nm256.c Thu Mar 7 18:17:41 2002 @@ -331,7 +331,7 @@ offset -= chip->buffer_start; #ifdef SNDRV_CONFIG_DEBUG if (offset < 0 || offset >= chip->buffer_size) { - printk("nm256: write_buffer invalid offset = %d size = %d\n", offset, size); + snd_printk("write_buffer invalid offset = %d size = %d\n", offset, size); return; } #endif @@ -1436,10 +1436,10 @@ pval = snd_nm256_readw(chip, NM_MIXER_PRESENCE); if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { if (! force_load) { - printk(KERN_INFO "nm256: no ac97 is found!\n"); - printk(KERN_INFO " force the driver to load by passing in the module parameter\n"); - printk(KERN_INFO " snd_force_ac97=1\n"); - printk(KERN_INFO " or try sb16 or cs423x drivers instead.\n"); + printk(KERN_ERR "nm256: no ac97 is found!\n"); + printk(KERN_ERR " force the driver to load by passing in the module parameter\n"); + printk(KERN_ERR " snd_force_ac97=1\n"); + printk(KERN_ERR " or try sb16 or cs423x drivers instead.\n"); err = -ENXIO; goto __error; } @@ -1477,8 +1477,8 @@ chip->buffer_start = chip->buffer_end - chip->buffer_size; chip->buffer_addr += chip->buffer_start; - snd_printd("NM256: Mapping port 1 from 0x%x - 0x%x\n", - chip->buffer_start, chip->buffer_end); + printk(KERN_INFO "nm256: Mapping port 1 from 0x%x - 0x%x\n", + chip->buffer_start, chip->buffer_end); chip->res_buffer = request_mem_region(chip->buffer_addr, chip->buffer_size, @@ -1652,7 +1652,7 @@ int err; if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("NeoMagic 256 audio soundchip not found or device busy\n"); + printk(KERN_ERR "NeoMagic 256 audio soundchip not found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/rme96.c b/sound/pci/rme96.c --- a/sound/pci/rme96.c Thu Mar 7 18:17:42 2002 +++ b/sound/pci/rme96.c Thu Mar 7 18:17:42 2002 @@ -2486,7 +2486,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("No RME Digi96 cards found\n"); + printk(KERN_ERR "No RME Digi96 cards found\n"); #endif return err; } diff -Nru a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c --- a/sound/pci/rme9652/rme9652.c Thu Mar 7 18:17:43 2002 +++ b/sound/pci/rme9652/rme9652.c Thu Mar 7 18:17:43 2002 @@ -392,7 +392,7 @@ if (offset < period_size) { if (offset > rme9652->max_jitter) { if (frag) - printk("Unexpected hw_pointer position (bufid == 0): status: %x offset: %d\n", status, offset); + printk(KERN_ERR "Unexpected hw_pointer position (bufid == 0): status: %x offset: %d\n", status, offset); } else if (!frag) return 0; offset -= rme9652->max_jitter; @@ -401,7 +401,7 @@ } else { if (offset > period_size + rme9652->max_jitter) { if (!frag) - printk("Unexpected hw_pointer position (bufid == 1): status: %x offset: %d\n", status, offset); + printk(KERN_ERR "Unexpected hw_pointer position (bufid == 1): status: %x offset: %d\n", status, offset); } else if (frag) return period_size; offset -= rme9652->max_jitter; @@ -1887,8 +1887,7 @@ #endif } - snd_printk("%s: no buffers available\n", - rme9652->card_name); + printk(KERN_ERR "%s: no buffers available\n", rme9652->card_name); return -ENOMEM; } @@ -1901,24 +1900,16 @@ /* Align to bus-space 64K boundary */ - cb_bus = cb_addr; - cb_bus = (cb_bus + 0xFFFF) & ~0xFFFFl; - - pb_bus = pb_addr; - pb_bus = (pb_bus + 0xFFFF) & ~0xFFFFl; + cb_bus = (cb_addr + 0xFFFF) & ~0xFFFFl; + pb_bus = (pb_addr + 0xFFFF) & ~0xFFFFl; /* Tell the card where it is */ rme9652_write(rme9652, RME9652_rec_buffer, cb_bus); rme9652_write(rme9652, RME9652_play_buffer, pb_bus); -#if 0 // not all architectures have this macro - rme9652->capture_buffer = bus_to_virt(cb_bus); - rme9652->playback_buffer = bus_to_virt(pb_bus); -#else - rme9652->capture_buffer += cb_bus - cb_addr; - rme9652->playback_buffer += pb_bus - pb_addr; -#endif + rme9652->capture_buffer = cb + (cb_bus - cb_addr); + rme9652->playback_buffer = pb + (pb_bus - pb_addr); return 0; } @@ -2732,7 +2723,7 @@ { if (pci_module_init(&driver) < 0) { #ifdef MODULE - snd_printk("RME Digi9652/Digi9636: no cards found\n"); + printk(KERN_ERR "RME Digi9652/Digi9636: no cards found\n"); #endif return -ENODEV; } diff -Nru a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c --- a/sound/pci/sonicvibes.c Thu Mar 7 18:17:39 2002 +++ b/sound/pci/sonicvibes.c Thu Mar 7 18:17:39 2002 @@ -1551,7 +1551,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("S3 SonicVibes soundcard not found or device busy\n"); + printk(KERN_ERR "S3 SonicVibes soundcard not found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c --- a/sound/pci/trident/trident.c Thu Mar 7 18:17:36 2002 +++ b/sound/pci/trident/trident.c Thu Mar 7 18:17:36 2002 @@ -223,7 +223,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("Trident 4DWave PCI soundcard not found or device busy\n"); + printk(KERN_ERR "Trident 4DWave PCI soundcard not found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/via686.c b/sound/pci/via686.c --- a/sound/pci/via686.c Thu Mar 7 18:17:41 2002 +++ b/sound/pci/via686.c Thu Mar 7 18:17:41 2002 @@ -246,6 +246,16 @@ return -EIO; } +static void snd_via686a_codec_wait(ac97_t *ac97) +{ + via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return); + int err; + err = snd_via686a_codec_ready(chip, ac97->num); + /* here we need to wait fairly for long time.. */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/2); +} + static void snd_via686a_codec_write(ac97_t *ac97, unsigned short reg, unsigned short val) @@ -811,6 +821,7 @@ ac97.write = snd_via686a_codec_write; ac97.read = snd_via686a_codec_read; ac97.init = snd_via686a_codec_init; + ac97.wait = snd_via686a_codec_wait; ac97.private_data = chip; ac97.private_free = snd_via686a_mixer_free_ac97; ac97.clock = chip->ac97_clock; @@ -1178,7 +1189,7 @@ pci_write_config_byte(pci, 0x43, legacy_cfg); if (legacy & 0x02) { if (check_region(snd_mpu_port[dev], 2)) { - snd_printk("unable to get MPU-401 port at 0x%lx, skipping\n", snd_mpu_port[dev]); + printk(KERN_WARNING "unable to get MPU-401 port at 0x%lx, skipping\n", snd_mpu_port[dev]); legacy &= ~0x02; pci_write_config_byte(pci, 0x42, legacy); goto __skip_mpu; @@ -1187,7 +1198,7 @@ snd_mpu_port[dev], 0, pci->irq, 0, &chip->rmidi) < 0) { - snd_printk("unable to initialize MPU-401 at 0x%lx, skipping\n", snd_mpu_port[dev]); + printk(KERN_WARNING "unable to initialize MPU-401 at 0x%lx, skipping\n", snd_mpu_port[dev]); legacy &= ~0x02; pci_write_config_byte(pci, 0x42, legacy); goto __skip_mpu; @@ -1239,7 +1250,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("VIA 82C686A soundcard not found or device busy\n"); + printk(KERN_ERR "VIA 82C686A soundcard not found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/via8233.c b/sound/pci/via8233.c --- a/sound/pci/via8233.c Thu Mar 7 18:17:41 2002 +++ b/sound/pci/via8233.c Thu Mar 7 18:17:41 2002 @@ -877,7 +877,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("VIA 8233 soundcard not found or device busy\n"); + printk(KERN_ERR "VIA 8233 soundcard not found or device busy\n"); #endif return err; } diff -Nru a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c --- a/sound/pci/ymfpci/ymfpci.c Thu Mar 7 18:17:43 2002 +++ b/sound/pci/ymfpci/ymfpci.c Thu Mar 7 18:17:43 2002 @@ -109,7 +109,7 @@ } legacy_ctrl = 0; - legacy_ctrl2 = 0; + legacy_ctrl2 = 0x0800; /* SMOD = 01 */ if (id->device >= 0x0010) { /* YMF 744/754 */ if (snd_fm_port[dev] < 0) @@ -197,7 +197,7 @@ if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI, snd_mpu_port[dev], 0, pci->irq, 0, &chip->rawmidi)) < 0) { - printk(KERN_INFO "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", snd_mpu_port[dev]); + printk(KERN_WARNING "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", snd_mpu_port[dev]); } else { legacy_ctrl &= ~0x10; /* disable MPU401 irq */ pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl); @@ -208,7 +208,7 @@ snd_fm_port[dev], snd_fm_port[dev] + 2, OPL3_HW_OPL3, 0, &opl3)) < 0) { - printk(KERN_INFO "ymfpci: cannot initialize FM OPL3 at 0x%lx, skipping...\n", snd_fm_port[dev]); + printk(KERN_WARNING "ymfpci: cannot initialize FM OPL3 at 0x%lx, skipping...\n", snd_fm_port[dev]); } else if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { snd_card_free(card); snd_printk("cannot create opl3 hwdep\n"); @@ -216,7 +216,7 @@ } } if ((err = snd_ymfpci_joystick(chip)) < 0) { - printk(KERN_INFO "ymfpci: cannot initialize joystick, skipping...\n"); + printk(KERN_WARNING "ymfpci: cannot initialize joystick, skipping...\n"); } strcpy(card->driver, str); sprintf(card->shortname, "Yamaha DS-XG PCI (%s)", str); @@ -287,7 +287,7 @@ if ((err = pci_module_init(&driver)) < 0) { #ifdef MODULE - snd_printk("Yamaha DS-XG PCI soundcard not found or device busy\n"); + printk(KERN_ERR "Yamaha DS-XG PCI soundcard not found or device busy\n"); #endif return err; } diff -Nru a/sound/ppc/keywest.c b/sound/ppc/keywest.c --- a/sound/ppc/keywest.c Thu Mar 7 18:17:43 2002 +++ b/sound/ppc/keywest.c Thu Mar 7 18:17:43 2002 @@ -179,7 +179,7 @@ i2c_device = find_compatible_devices("i2c", "keywest"); if (i2c_device == 0) { - snd_printk("No Keywest i2c devices found.\n"); + printk(KERN_ERR "pmac: No Keywest i2c devices found.\n"); return -ENODEV; } diff -Nru a/sound/ppc/powermac.c b/sound/ppc/powermac.c --- a/sound/ppc/powermac.c Thu Mar 7 18:17:44 2002 +++ b/sound/ppc/powermac.c Thu Mar 7 18:17:44 2002 @@ -150,7 +150,7 @@ int err; if ((err = snd_pmac_probe() < 0)) { #ifdef MODULE - snd_printk("no PMac soundchip found\n"); + printk(KERN_ERR "no PMac soundchip found\n"); #endif return err; } diff -Nru a/sound/sound_core.c b/sound/sound_core.c --- a/sound/sound_core.c Thu Mar 7 18:17:43 2002 +++ b/sound/sound_core.c Thu Mar 7 18:17:43 2002 @@ -17,7 +17,7 @@ * plug into this. The fact they dont all go via OSS doesn't mean * they don't have to implement the OSS API. There is a lot of logic * to keeping much of the OSS weight out of the code in a compatibility - * module, but its up to the driver to rember to load it... + * module, but it's up to the driver to rember to load it... * * The code provides a set of functions for registration of devices * by type. This is done rather than providing a single call so that @@ -171,10 +171,10 @@ return r; } - if (r == low) + if (r < SOUND_STEP) sprintf (name_buf, "%s", name); else - sprintf (name_buf, "%s%d", name, (r - low) / SOUND_STEP); + sprintf (name_buf, "%s%d", name, r / SOUND_STEP); s->de = devfs_register (devfs_handle, name_buf, DEVFS_FL_NONE, SOUND_MAJOR, s->unit_minor, S_IFCHR | mode, fops, NULL); @@ -215,7 +215,7 @@ * 15 *16 unused */ -static struct sound_unit *chains[16]; +static struct sound_unit *chains[SOUND_STEP]; /** * register_sound_special - register a special sound node @@ -229,17 +229,22 @@ int register_sound_special(struct file_operations *fops, int unit) { - char *name; + const int chain = unit % (SOUND_STEP-1); + int max_unit = 128 + chain; + const char *name; + char _name[16]; - switch (unit) { + switch (chain) { case 0: name = "mixer"; break; case 1: name = "sequencer"; - break; + if (unit >= SOUND_STEP) + goto __unknown; + max_unit = unit + 1; case 2: - name = "midi00"; + name = "midi"; break; case 3: name = "dsp"; @@ -247,17 +252,11 @@ case 4: name = "audio"; break; - case 5: - name = "unknown5"; - break; - case 6: /* Was once sndstat */ - name = "unknown6"; - break; - case 7: - name = "unknown7"; - break; case 8: name = "sequencer2"; + if (unit >= SOUND_STEP) + goto __unknown; + max_unit = unit + 1; break; case 9: name = "dmmidi"; @@ -265,9 +264,6 @@ case 10: name = "dmfm"; break; - case 11: - name = "unknown11"; - break; case 12: name = "adsp"; break; @@ -278,10 +274,16 @@ name = "admmidi"; break; default: - name = "unknown"; + { + __unknown: + sprintf(_name, "unknown%d", chain); + if (unit >= SOUND_STEP) + strcat(_name, "-"); + name = _name; + } break; } - return sound_insert_unit(&chains[unit&15], fops, -1, unit, unit+1, + return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit, name, S_IRUSR | S_IWUSR); } diff -Nru a/sound/synth/Makefile b/sound/synth/Makefile --- a/sound/synth/Makefile Thu Mar 7 18:17:37 2002 +++ b/sound/synth/Makefile Thu Mar 7 18:17:37 2002 @@ -15,8 +15,8 @@ snd-util-mem-objs := util_mem.o # Toplevel Module Dependency -obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o +obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) obj-$(CONFIG_SND_SBAWE) += snd-util-mem.o endif diff -Nru a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c --- a/sound/synth/emux/emux_synth.c Thu Mar 7 18:17:44 2002 +++ b/sound/synth/emux/emux_synth.c Thu Mar 7 18:17:44 2002 @@ -629,6 +629,25 @@ /* * calculate pitch parameter */ +static unsigned char pan_volumes[256] = { +0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x14,0x17,0x1a,0x1d,0x20,0x22,0x25,0x28,0x2a, +0x2d,0x30,0x32,0x35,0x37,0x3a,0x3c,0x3f,0x41,0x44,0x46,0x49,0x4b,0x4d,0x50,0x52, +0x54,0x57,0x59,0x5b,0x5d,0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6f,0x71,0x73,0x75, +0x77,0x79,0x7b,0x7c,0x7e,0x80,0x82,0x84,0x86,0x88,0x89,0x8b,0x8d,0x8f,0x90,0x92, +0x94,0x96,0x97,0x99,0x9a,0x9c,0x9e,0x9f,0xa1,0xa2,0xa4,0xa5,0xa7,0xa8,0xaa,0xab, +0xad,0xae,0xaf,0xb1,0xb2,0xb3,0xb5,0xb6,0xb7,0xb9,0xba,0xbb,0xbc,0xbe,0xbf,0xc0, +0xc1,0xc2,0xc3,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,0xd0,0xd1, +0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdc,0xdd,0xde,0xdf, +0xdf,0xe0,0xe1,0xe2,0xe2,0xe3,0xe4,0xe4,0xe5,0xe6,0xe6,0xe7,0xe8,0xe8,0xe9,0xe9, +0xea,0xeb,0xeb,0xec,0xec,0xed,0xed,0xee,0xee,0xef,0xef,0xf0,0xf0,0xf1,0xf1,0xf1, +0xf2,0xf2,0xf3,0xf3,0xf3,0xf4,0xf4,0xf5,0xf5,0xf5,0xf6,0xf6,0xf6,0xf7,0xf7,0xf7, +0xf7,0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfa,0xfb,0xfb,0xfb, +0xfb,0xfb,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, +0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +}; + static int calc_pan(snd_emux_voice_t *vp) { @@ -646,6 +665,16 @@ } LIMITVALUE(pan, 0, 255); +#if 1 + /* using volume table */ + if (vp->apan != (int)pan_volumes[pan]) { + vp->apan = pan_volumes[pan]; + vp->aaux = pan_volumes[255 - pan]; + return 1; + } + return 0; +#else + /* assuming linear volume */ if (pan != vp->apan) { vp->apan = pan; if (pan == 0) @@ -655,6 +684,7 @@ return 1; } else return 0; +#endif }