diff -Nru a/COPYING b/COPYING --- a/COPYING Wed Mar 6 17:13:52 2002 +++ b/COPYING Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/CREDITS Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/Documentation/Changes Wed Mar 6 17:13:55 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/arm/Interrupts b/Documentation/arm/Interrupts --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/arm/Interrupts Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/Documentation/driver-model.txt Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/Documentation/filesystems/00-INDEX Wed Mar 6 17:13:55 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/jfs.txt b/Documentation/filesystems/jfs.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/filesystems/jfs.txt Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/Documentation/filesystems/porting Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/Documentation/kbuild/config-language.txt Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/Documentation/networking/arcnet-hardware.txt Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/Documentation/networking/bonding.txt Wed Mar 6 17:13:53 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/e1000.txt b/Documentation/networking/e1000.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/networking/e1000.txt Wed Mar 6 17:13:55 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/power/pci.txt b/Documentation/power/pci.txt --- a/Documentation/power/pci.txt Wed Mar 6 17:13:53 2002 +++ b/Documentation/power/pci.txt Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/Documentation/sound/AD1816 Wed Mar 6 17:13:53 2002 @@ -80,5 +80,5 @@ tek@rbg.informatik.tu-darmstadt.de Thorsten Knabe -Christoph Hellwig +Christoph Hellwig Last modified: 2000/09/20 diff -Nru a/Documentation/sound/NEWS b/Documentation/sound/NEWS --- a/Documentation/sound/NEWS Wed Mar 6 17:13:55 2002 +++ b/Documentation/sound/NEWS Wed Mar 6 17:13:55 2002 @@ -1,6 +1,6 @@ Linux 2.4 Sound Changes 2000-September-25 -Christoph Hellwig, +Christoph Hellwig, diff -Nru a/Documentation/sound/OPL3-SA2 b/Documentation/sound/OPL3-SA2 --- a/Documentation/sound/OPL3-SA2 Wed Mar 6 17:13:53 2002 +++ b/Documentation/sound/OPL3-SA2 Wed Mar 6 17:13:53 2002 @@ -65,7 +65,7 @@ ------------ In previous kernels (2.2.x), some configuration was required to -get the driver to talk to the card. Being the new millenium and +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. diff -Nru a/Documentation/sound/README.OSS b/Documentation/sound/README.OSS --- a/Documentation/sound/README.OSS Wed Mar 6 17:13:52 2002 +++ b/Documentation/sound/README.OSS Wed Mar 6 17:13:52 2002 @@ -542,7 +542,7 @@ 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 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 diff -Nru a/Documentation/usb/auerswald.txt b/Documentation/usb/auerswald.txt --- a/Documentation/usb/auerswald.txt Wed Mar 6 17:13:55 2002 +++ b/Documentation/usb/auerswald.txt Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/Documentation/usb/ov511.txt Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/Documentation/usb/usb-serial.txt Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/MAINTAINERS Wed Mar 6 17:13:52 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 @@ -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 Wed Mar 6 17:13:53 2002 +++ b/Makefile Wed Mar 6 17:13:53 2002 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 5 -SUBLEVEL = 5 -EXTRAVERSION = +SUBLEVEL = 6 +EXTRAVERSION =-pre3 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) diff -Nru a/arch/alpha/Makefile b/arch/alpha/Makefile --- a/arch/alpha/Makefile Wed Mar 6 17:13:52 2002 +++ b/arch/alpha/Makefile Wed Mar 6 17:13:52 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 diff -Nru a/arch/alpha/defconfig b/arch/alpha/defconfig --- a/arch/alpha/defconfig Wed Mar 6 17:13:55 2002 +++ b/arch/alpha/defconfig Wed Mar 6 17:13:55 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_titan.c b/arch/alpha/kernel/core_titan.c --- a/arch/alpha/kernel/core_titan.c Wed Mar 6 17:13:54 2002 +++ b/arch/alpha/kernel/core_titan.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/arch/alpha/kernel/entry.S Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/arch/alpha/kernel/process.c Wed Mar 6 17:13:54 2002 @@ -283,6 +283,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 +305,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 Wed Mar 6 17:13:53 2002 +++ b/arch/alpha/kernel/proto.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/alpha/kernel/sys_titan.c Wed Mar 6 17:13:55 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/arm/Config.help b/arch/arm/Config.help --- a/arch/arm/Config.help Wed Mar 6 17:13:52 2002 +++ b/arch/arm/Config.help Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/Makefile Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/boot/Makefile Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/boot/compressed/Makefile Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/boot/compressed/misc.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/boot/compressed/ofw-shark.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/config.in Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/def-configs/epxa10db Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/def-configs/shannon Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/def-configs/shark Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/kernel/armksyms.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/kernel/calls.S Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/kernel/debug.S Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/kernel/ecard.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/kernel/entry-armo.S Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/kernel/entry-armv.S Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/kernel/entry-common.S Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/kernel/entry-header.S Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/kernel/head.S Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/kernel/init_task.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/kernel/irq.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/kernel/process.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/kernel/ptrace.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/kernel/setup.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/kernel/signal.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/kernel/time.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/kernel/traps.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/kernel/via82c505.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/lib/Makefile Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/lib/csumpartial.S Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/lib/csumpartialcopygeneric.S Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/lib/ecard.S Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/lib/getuser.S Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/lib/io-acorn.S Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/lib/putuser.S Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/lib/strrchr.S Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-adifcc/arch.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-adifcc/irq.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-adifcc/mm.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mach-anakin/arch.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-anakin/mm.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-arc/arch.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-arc/dma.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-arc/mm.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-clps711x/Makefile Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-clps711x/autcpu12.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mach-clps711x/cdb89712.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-clps711x/edb7211-mm.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-clps711x/irq.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-ebsa110/core.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-epxa10db/arch.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-epxa10db/irq.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-footbridge/Makefile Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-footbridge/irq.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-footbridge/netwinder-hw.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-ftvpci/core.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-integrator/irq.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-integrator/pci.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-integrator/pci_v3.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-iop310/arch.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-iop310/iop310-irq.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-iop310/iop310-pci.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-iop310/iq80310-irq.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-iop310/iq80310-time.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-iop310/mm.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-iop310/xs80200-irq.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-l7200/core.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-rpc/dma.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-rpc/irq.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mach-sa1100/adsbitsy.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mach-sa1100/assabet.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-sa1100/brutus.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-sa1100/cerf.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-sa1100/empeg.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-sa1100/flexanet.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-sa1100/freebird.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-sa1100/graphicsclient.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-sa1100/graphicsmaster.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-sa1100/h3600.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-sa1100/huw_webpanel.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-sa1100/irq.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-sa1100/itsy.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-sa1100/jornada720.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-sa1100/lart.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-sa1100/leds-adsbitsy.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mach-sa1100/leds-graphicsclient.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-sa1100/leds-graphicsmaster.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-sa1100/leds-system3.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-sa1100/leds.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mach-sa1100/nanoengine.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-sa1100/neponset.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mach-sa1100/omnimeter.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-sa1100/pangolin.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mach-sa1100/pfs168.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-sa1100/pleb.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-sa1100/pm.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mach-sa1100/sa1111-pcibuf.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-sa1100/sa1111.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mach-sa1100/sherman.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-sa1100/simpad.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-sa1100/system3.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mach-sa1100/victor.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-sa1100/xp860.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mach-sa1100/yopy.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mach-shark/irq.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mach-shark/pci.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mm/Makefile Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mm/alignment.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mm/fault-armv.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mm/fault-common.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mm/init.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mm/minicache.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mm/mm-armv.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mm/proc-arm1020.S Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mm/proc-arm2,3.S Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/mm/proc-arm6,7.S Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/mm/proc-arm720.S Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mm/proc-arm920.S Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mm/proc-arm922.S Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/mm/proc-arm926.S Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mm/proc-sa110.S Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mm/proc-syms.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/mm/proc-xscale.S Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/nwfpe/ChangeLog Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/nwfpe/double_cpdo.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/nwfpe/entry.S Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/nwfpe/entry26.S Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/arch/arm/nwfpe/extended_cpdo.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/nwfpe/fpa11.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/nwfpe/fpa11.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/nwfpe/fpa11_cpdt.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/nwfpe/fpa11_cprt.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/nwfpe/fpmodule.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/nwfpe/fpopcode.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/arch/arm/nwfpe/single_cpdo.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/nwfpe/softfloat-specialize Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/nwfpe/softfloat.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/nwfpe/softfloat.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/arm/tools/getconstants.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/arm/tools/mach-types Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/cris/drivers/ide.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/i386/defconfig Wed Mar 6 17:13:53 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 @@ -454,6 +467,7 @@ # # 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 @@ -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/apic.c b/arch/i386/kernel/apic.c --- a/arch/i386/kernel/apic.c Wed Mar 6 17:13:55 2002 +++ b/arch/i386/kernel/apic.c Wed Mar 6 17:13:55 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/dmi_scan.c b/arch/i386/kernel/dmi_scan.c --- a/arch/i386/kernel/dmi_scan.c Wed Mar 6 17:13:54 2002 +++ b/arch/i386/kernel/dmi_scan.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/i386/kernel/entry.S Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/i386/kernel/i8259.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/arch/i386/kernel/io_apic.c Wed Mar 6 17:13:54 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/setup.c b/arch/i386/kernel/setup.c --- a/arch/i386/kernel/setup.c Wed Mar 6 17:13:54 2002 +++ b/arch/i386/kernel/setup.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/i386/kernel/smp.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/i386/kernel/smpboot.c Wed Mar 6 17:13:55 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; diff -Nru a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c --- a/arch/i386/kernel/time.c Wed Mar 6 17:13:55 2002 +++ b/arch/i386/kernel/time.c Wed Mar 6 17:13:55 2002 @@ -115,6 +115,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 Wed Mar 6 17:13:53 2002 +++ b/arch/i386/kernel/traps.c Wed Mar 6 17:13:53 2002 @@ -974,6 +974,10 @@ EISA_bus = 1; #endif +#ifdef CONFIG_X86_LOCAL_APIC + init_apic_mappings(); +#endif + set_trap_gate(0,÷_error); set_trap_gate(1,&debug); set_intr_gate(2,&nmi); diff -Nru a/arch/i386/mm/init.c b/arch/i386/mm/init.c --- a/arch/i386/mm/init.c Wed Mar 6 17:13:53 2002 +++ b/arch/i386/mm/init.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/i386/mm/ioremap.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/i386/vmlinux.lds Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/arch/ia64/kernel/perfmon.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/ia64/sn/io/pci_bus_cvlink.c Wed Mar 6 17:13:53 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/ppc/boot/ld.script b/arch/ppc/boot/ld.script --- a/arch/ppc/boot/ld.script Wed Mar 6 17:13:53 2002 +++ b/arch/ppc/boot/ld.script Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/ppc/boot/simple/misc-embedded.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/arch/ppc/config.in Wed Mar 6 17:13:52 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/kernel/entry.S b/arch/ppc/kernel/entry.S --- a/arch/ppc/kernel/entry.S Wed Mar 6 17:13:54 2002 +++ b/arch/ppc/kernel/entry.S Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/ppc/kernel/iSeries_head.S Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/arch/ppc/kernel/iSeries_misc.S Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/arch/ppc/kernel/idle.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/arch/ppc/kernel/misc.S Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/arch/ppc/kernel/ppc_ksyms.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/ppc/kernel/qspan_pci.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/ppc/kernel/smp.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/ppc/lib/locks.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/arch/ppc/mm/cachemap.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/arch/ppc/mm/fault.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/arch/ppc/mm/hashtable.S Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/arch/ppc/mm/iSeries_hashtable.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/arch/ppc/mm/iSeries_mmu.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/ppc/mm/init.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/ppc/mm/mmu_decl.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/ppc/mm/pgtable.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/ppc/mm/ppc_mmu.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/ppc/mm/tlb.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/arch/ppc/vmlinux.lds Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/arch/ppc64/kernel/entry.S Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/arch/ppc64/kernel/sys_ppc32.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/s390/kernel/entry.S Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/s390x/kernel/entry.S Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/arch/sparc64/defconfig Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/sparc64/kernel/sys_sparc32.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/x86_64/config.in Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/x86_64/defconfig Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/arch/x86_64/ia32/ia32_binfmt.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/arch/x86_64/ia32/ia32_ioctl.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/arch/x86_64/ia32/ia32_signal.c Wed Mar 6 17:13:54 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/process.c b/arch/x86_64/kernel/process.c --- a/arch/x86_64/kernel/process.c Wed Mar 6 17:13:52 2002 +++ b/arch/x86_64/kernel/process.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/arch/x86_64/kernel/ptrace.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/arch/x86_64/kernel/signal.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/arch/x86_64/kernel/time.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/arch/x86_64/kernel/vsyscall.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/arch/x86_64/kernel/x8664_ksyms.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/arch/x86_64/mm/fault.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/arch/x86_64/mm/init.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/arch/x86_64/mm/ioremap.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/arch/x86_64/tools/offset.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/Makefile Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/acorn/block/mfmhd.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/acorn/char/mouse_ps2.c Wed Mar 6 17:13:53 2002 @@ -1,8 +1,6 @@ /* * Driver for PS/2 mouse on IOMD interface */ - -#include #include #include #include diff -Nru a/drivers/acorn/net/ether1.c b/drivers/acorn/net/ether1.c --- a/drivers/acorn/net/ether1.c Wed Mar 6 17:13:52 2002 +++ b/drivers/acorn/net/ether1.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/acorn/net/ether3.c Wed Mar 6 17:13:54 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/acpi/executer/exresnte.c b/drivers/acpi/executer/exresnte.c --- a/drivers/acpi/executer/exresnte.c Wed Mar 6 17:13:53 2002 +++ b/drivers/acpi/executer/exresnte.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/atm/eni.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/atm/firestream.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/block/DAC960.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/block/DAC960.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/block/Makefile Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/block/acsi.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/block/amiflop.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/block/ataflop.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/block/blkpg.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/block/cciss.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/block/cpqarray.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/block/floppy.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/block/ll_rw_blk.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/block/loop.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/block/paride/pd.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/block/paride/pf.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/block/ps2esdi.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/block/rd.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/block/xd.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/cdrom/cdrom.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/char/Config.help Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/char/Makefile Wed Mar 6 17:13:54 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/agp/agpgart_be.c b/drivers/char/agp/agpgart_be.c --- a/drivers/char/agp/agpgart_be.c Wed Mar 6 17:13:53 2002 +++ b/drivers/char/agp/agpgart_be.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/char/drm/drm_scatter.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/char/drm/drm_vm.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/char/drm/i810_dma.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/char/lp.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/char/mem.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/char/mwave/mwavedd.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/char/ppdev.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/char/rtc.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/char/synclink.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/char/sysrq.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/char/wdt_pci.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/hotplug/Config.help Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/hotplug/Config.in Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/hotplug/Makefile Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/hotplug/cpqphp_proc.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/hotplug/pci_hotplug_core.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/ide/Config.help Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/ide/Config.in Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/ide/aec62xx.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/ide/ali14xx.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ide/alim15x3.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/ide/amd74xx.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/ide/ataraid.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/ide/cmd640.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/ide/cmd64x.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/ide/cs5530.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ide/cy82c693.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/ide/dtc2278.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ide/hd.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ide/hpt34x.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ide/hpt366.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/ide/hptraid.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/ide/ide-cd.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/ide/ide-disk.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ide/ide-dma.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/ide/ide-features.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ide/ide-floppy.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/ide/ide-geometry.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/ide/ide-pci.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/ide/ide-pnp.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/ide/ide-probe.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/ide/ide-proc.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ide/ide-tape.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/ide/ide-taskfile.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/ide/ide.c Wed Mar 6 17:13:54 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, 10 * 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ide/ide_modes.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/ide/it8172.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/ide/ns87415.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ide/opti621.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/ide/pdc202xx.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ide/pdc4030.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ide/pdcadma.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/ide/pdcraid.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ide/qd65xx.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/ide/rz1000.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/ide/serverworks.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/ide/sis5513.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/ide/sl82c105.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/ide/slc90e66.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ide/trm290.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/ide/via82cxxx.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ieee1394/dv1394.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/ieee1394/video1394.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/input/gameport/cs461x.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/input/gameport/emu10k1-gp.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/input/gameport/gameport.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/input/gameport/lightning.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/input/gameport/ns558.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/input/gameport/pcigame.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/input/joystick/a3d.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/input/joystick/adi.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/input/joystick/amijoy.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/input/joystick/analog.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/input/joystick/cobra.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/input/joystick/db9.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/input/joystick/gamecon.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/input/joystick/gf2k.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/input/joystick/grip.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/input/joystick/interact.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/input/joystick/magellan.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/input/joystick/sidewinder.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/input/joystick/spaceball.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/input/joystick/spaceorb.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/input/joystick/stinger.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/input/joystick/tmdc.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/input/joystick/turbografx.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/input/joystick/warrior.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/input/serio/serio.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/input/serio/serport.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/isdn/Config.help Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/isdn/Config.in Wed Mar 6 17:13:55 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/avmb1/capi.c b/drivers/isdn/avmb1/capi.c --- a/drivers/isdn/avmb1/capi.c Wed Mar 6 17:13:55 2002 +++ b/drivers/isdn/avmb1/capi.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/isdn/hisax/Makefile Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/isdn/hisax/hisax_fcpcipnp.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/isdn/hisax/st5481_init.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/isdn/hisax/st5481_usb.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/isdn/tpam/tpam_main.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/macintosh/via-pmu.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/macintosh/via-pmu68k.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/md/lvm.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/md/md.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/media/radio/radio-gemtek-pci.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/media/radio/radio-maxiradio.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/media/video/bttv-driver.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/media/video/cpia.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/media/video/meye.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/media/video/zr36120.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/message/i2o/i2o_block.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/mtd/devices/blkmtd.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/mtd/devices/pmc551.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/mtd/ftl.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/mtd/maps/elan-104nc.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/mtd/maps/sbc_gxx.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/mtd/mtdblock.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/mtd/mtdblock_ro.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/mtd/mtdchar.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/mtd/nand/spia.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/mtd/nftlcore.c Wed Mar 6 17:13:52 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/3c59x.c b/drivers/net/3c59x.c --- a/drivers/net/3c59x.c Wed Mar 6 17:13:52 2002 +++ b/drivers/net/3c59x.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/net/8139cp.c Wed Mar 6 17:13:54 2002 @@ -1,6 +1,6 @@ /* 8139cp.c: A Linux PCI Ethernet driver for the RealTek 8139C+ chips. */ /* - Copyright 2001 Jeff Garzik + Copyright 2001,2002 Jeff Garzik Copyright (C) 2000, 2001 David S. Miller (davem@redhat.com) [sungem.c] Copyright 2001 Manfred Spraul [natsemi.c] @@ -32,19 +32,20 @@ * Rx checksumming * Tx checksumming * 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 + * Determine correct value for CP_{MIN,MAX}_MTU, instead of + using conservative guesses. */ #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 @@ -55,7 +56,6 @@ #include #include #include -#include #include #include #include @@ -107,8 +107,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 /* FIXME: this is a guess */ +#define CP_MAX_MTU 1500 /* FIXME: this is a guess */ enum { /* NIC register offsets */ @@ -321,6 +324,17 @@ }; MODULE_DEVICE_TABLE(pci, cp_pci_tbl); +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) { skb->protocol = eth_type_trans (skb, cp->dev); @@ -705,9 +719,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 +823,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 | CpRxOn | CpTxOn); +} + static void cp_init_hw (struct cp_private *cp) { struct net_device *dev = cp->dev; @@ -824,8 +841,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 +973,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 +1007,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, @@ -1231,6 +1275,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 +1329,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; @@ -1371,7 +1417,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 Wed Mar 6 17:13:52 2002 +++ b/drivers/net/8139too.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/net/Config.help Wed Mar 6 17:13:52 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. @@ -806,6 +816,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 +863,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 @@ -1290,6 +1340,24 @@ module, say M here and read as well as . +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 @@ -1308,6 +1376,20 @@ 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_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 +1420,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 @@ -1376,6 +1463,16 @@ The module will be called via-rhine.o. If you want to compile it as a module, say M here and read as well as . + +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. + + 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_DM9102 This driver is for DM9102(A)/DM9132/DM9801 compatible PCI cards from diff -Nru a/drivers/net/Config.in b/drivers/net/Config.in --- a/drivers/net/Config.in Wed Mar 6 17:13:52 2002 +++ b/drivers/net/Config.in Wed Mar 6 17:13:52 2002 @@ -231,6 +231,7 @@ 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 diff -Nru a/drivers/net/Makefile b/drivers/net/Makefile --- a/drivers/net/Makefile Wed Mar 6 17:13:53 2002 +++ b/drivers/net/Makefile Wed Mar 6 17:13:53 2002 @@ -25,6 +25,10 @@ obj-y += tulip/tulip.o endif +ifeq ($(CONFIG_E1000),y) + obj-y += e1000/e1000.o +endif + ifeq ($(CONFIG_ISDN_PPP),y) obj-$(CONFIG_ISDN) += slhc.o endif @@ -32,6 +36,7 @@ subdir-$(CONFIG_NET_PCMCIA) += pcmcia subdir-$(CONFIG_NET_WIRELESS) += wireless subdir-$(CONFIG_TULIP) += tulip +subdir-$(CONFIG_E1000) += e1000 subdir-$(CONFIG_IRDA) += irda subdir-$(CONFIG_TR) += tokenring subdir-$(CONFIG_WAN) += wan diff -Nru a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c --- a/drivers/net/arcnet/com20020-pci.c Wed Mar 6 17:13:53 2002 +++ b/drivers/net/arcnet/com20020-pci.c Wed Mar 6 17:13:53 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/defxx.c b/drivers/net/defxx.c --- a/drivers/net/defxx.c Wed Mar 6 17:13:52 2002 +++ b/drivers/net/defxx.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/net/dl2k.c Wed Mar 6 17:13:54 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/e1000/LICENSE b/drivers/net/e1000/LICENSE --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/e1000/LICENSE Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 @@ -0,0 +1,2033 @@ +/******************************************************************************* + + 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" +}; + +/* Local 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"); + +#ifdef EXPORT_SYMTAB +EXPORT_SYMBOL(e1000_init_module); +EXPORT_SYMBOL(e1000_exit_module); +EXPORT_SYMBOL(e1000_probe); +EXPORT_SYMBOL(e1000_remove); +EXPORT_SYMBOL(e1000_open); +EXPORT_SYMBOL(e1000_close); +EXPORT_SYMBOL(e1000_xmit_frame); +EXPORT_SYMBOL(e1000_intr); +EXPORT_SYMBOL(e1000_set_multi); +EXPORT_SYMBOL(e1000_change_mtu); +EXPORT_SYMBOL(e1000_set_mac); +EXPORT_SYMBOL(e1000_get_stats); +EXPORT_SYMBOL(e1000_watchdog); +EXPORT_SYMBOL(e1000_ioctl); +#endif + +/** + * 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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/eepro100.c b/drivers/net/eepro100.c --- a/drivers/net/eepro100.c Wed Mar 6 17:13:53 2002 +++ b/drivers/net/eepro100.c Wed Mar 6 17:13:53 2002 @@ -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 Wed Mar 6 17:13:54 2002 +++ b/drivers/net/epic100.c Wed Mar 6 17:13:54 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/fc/Makefile b/drivers/net/fc/Makefile --- a/drivers/net/fc/Makefile Wed Mar 6 17:13:53 2002 +++ b/drivers/net/fc/Makefile Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/net/fealnx.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/net/hamachi.c Wed Mar 6 17:13:55 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/Makefile b/drivers/net/hamradio/Makefile --- a/drivers/net/hamradio/Makefile Wed Mar 6 17:13:53 2002 +++ b/drivers/net/hamradio/Makefile Wed Mar 6 17:13:53 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/ioc3-eth.c b/drivers/net/ioc3-eth.c --- a/drivers/net/ioc3-eth.c Wed Mar 6 17:13:52 2002 +++ b/drivers/net/ioc3-eth.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/net/irda/Makefile Wed Mar 6 17:13:54 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/irda-usb.c b/drivers/net/irda/irda-usb.c --- a/drivers/net/irda/irda-usb.c Wed Mar 6 17:13:55 2002 +++ b/drivers/net/irda/irda-usb.c Wed Mar 6 17:13:55 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; @@ -848,8 +855,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 +959,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 +1003,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 +1014,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 +1440,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 +1536,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 +1551,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/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c --- a/drivers/net/irda/sa1100_ir.c Wed Mar 6 17:13:54 2002 +++ b/drivers/net/irda/sa1100_ir.c Wed Mar 6 17:13:54 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/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c --- a/drivers/net/irda/vlsi_ir.c Wed Mar 6 17:13:52 2002 +++ b/drivers/net/irda/vlsi_ir.c Wed Mar 6 17:13:52 2002 @@ -1291,7 +1291,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/natsemi.c b/drivers/net/natsemi.c --- a/drivers/net/natsemi.c Wed Mar 6 17:13:52 2002 +++ b/drivers/net/natsemi.c Wed Mar 6 17:13:52 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/ne2k-pci.c b/drivers/net/ne2k-pci.c --- a/drivers/net/ne2k-pci.c Wed Mar 6 17:13:53 2002 +++ b/drivers/net/ne2k-pci.c Wed Mar 6 17:13:53 2002 @@ -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/ns83820.c b/drivers/net/ns83820.c --- a/drivers/net/ns83820.c Wed Mar 6 17:13:55 2002 +++ b/drivers/net/ns83820.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/net/pci-skeleton.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/net/pcmcia/Config.help Wed Mar 6 17:13:54 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/xircom_cb.c b/drivers/net/pcmcia/xircom_cb.c --- a/drivers/net/pcmcia/xircom_cb.c Wed Mar 6 17:13:55 2002 +++ b/drivers/net/pcmcia/xircom_cb.c Wed Mar 6 17:13:55 2002 @@ -170,7 +170,7 @@ name: "xircom_cb", id_table: xircom_pci_table, probe: xircom_probe, - remove: xircom_remove, + remove: __devexit_p(xircom_remove), }; 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/net/pcmcia/xircom_tulip_cb.c Wed Mar 6 17:13:53 2002 @@ -1709,7 +1709,7 @@ name: DRV_NAME, id_table: xircom_pci_table, probe: xircom_init_one, - remove: xircom_remove_one, + remove: __devexit_p(xircom_remove_one), #ifdef CONFIG_PM suspend: xircom_suspend, resume: xircom_resume diff -Nru a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c --- a/drivers/net/ppp_deflate.c Wed Mar 6 17:13:54 2002 +++ b/drivers/net/ppp_deflate.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/net/rcpci45.c Wed Mar 6 17:13:53 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/sis900.c b/drivers/net/sis900.c --- a/drivers/net/sis900.c Wed Mar 6 17:13:54 2002 +++ b/drivers/net/sis900.c Wed Mar 6 17:13:54 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/starfire.c b/drivers/net/starfire.c --- a/drivers/net/starfire.c Wed Mar 6 17:13:53 2002 +++ b/drivers/net/starfire.c Wed Mar 6 17:13:53 2002 @@ -1982,7 +1982,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 Wed Mar 6 17:13:52 2002 +++ b/drivers/net/sundance.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/net/sungem.c Wed Mar 6 17:13:55 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/tlan.c b/drivers/net/tlan.c --- a/drivers/net/tlan.c Wed Mar 6 17:13:53 2002 +++ b/drivers/net/tlan.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/net/tokenring/Config.help Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/net/tokenring/Config.in Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/net/tokenring/Makefile Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/net/tokenring/abyss.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/net/tokenring/lanstreamer.c Wed Mar 6 17:13:54 2002 @@ -1812,7 +1812,7 @@ 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/olympic.c b/drivers/net/tokenring/olympic.c --- a/drivers/net/tokenring/olympic.c Wed Mar 6 17:13:53 2002 +++ b/drivers/net/tokenring/olympic.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/net/tokenring/tmspci.c Wed Mar 6 17:13:55 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/via-rhine.c b/drivers/net/via-rhine.c --- a/drivers/net/via-rhine.c Wed Mar 6 17:13:54 2002 +++ b/drivers/net/via-rhine.c Wed Mar 6 17:13:54 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/Makefile b/drivers/net/wan/Makefile --- a/drivers/net/wan/Makefile Wed Mar 6 17:13:52 2002 +++ b/drivers/net/wan/Makefile Wed Mar 6 17:13:52 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. # diff -Nru a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c --- a/drivers/net/wan/farsync.c Wed Mar 6 17:13:53 2002 +++ b/drivers/net/wan/farsync.c Wed Mar 6 17:13:53 2002 @@ -1810,7 +1810,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, }; diff -Nru a/drivers/net/wan/sdla_x25.c b/drivers/net/wan/sdla_x25.c --- a/drivers/net/wan/sdla_x25.c Wed Mar 6 17:13:53 2002 +++ b/drivers/net/wan/sdla_x25.c Wed Mar 6 17:13:53 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/winbond-840.c b/drivers/net/winbond-840.c --- a/drivers/net/winbond-840.c Wed Mar 6 17:13:53 2002 +++ b/drivers/net/winbond-840.c Wed Mar 6 17:13:53 2002 @@ -1732,7 +1732,7 @@ name: DRV_NAME, id_table: w840_pci_tbl, probe: w840_probe1, - remove: w840_remove1, + remove: __devexit_p(w840_remove1), #ifdef CONFIG_PM suspend: w840_suspend, resume: w840_resume, diff -Nru a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c --- a/drivers/net/wireless/airo.c Wed Mar 6 17:13:53 2002 +++ b/drivers/net/wireless/airo.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/net/wireless/netwave_cs.c Wed Mar 6 17:13:54 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_plx.c b/drivers/net/wireless/orinoco_plx.c --- a/drivers/net/wireless/orinoco_plx.c Wed Mar 6 17:13:55 2002 +++ b/drivers/net/wireless/orinoco_plx.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/net/wireless/wavelan.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/net/wireless/wavelan.p.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/net/wireless/wavelan_cs.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/net/wireless/wavelan_cs.p.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/net/yellowfin.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/parport/ChangeLog Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/parport/parport_pc.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/parport/parport_serial.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/pci/pci.c Wed Mar 6 17:13:53 2002 @@ -966,7 +966,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 +1057,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 +1392,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 +1404,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 +1431,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 +1965,7 @@ return 0; } -static int __devinit pci_setup(char *str) +static int __devinit pci_setup(char *str) { while (str) { char *k = strchr(str, ','); diff -Nru a/drivers/pci/pci.ids b/drivers/pci/pci.ids --- a/drivers/pci/pci.ids Wed Mar 6 17:13:52 2002 +++ b/drivers/pci/pci.ids Wed Mar 6 17:13:52 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 diff -Nru a/drivers/pci/quirks.c b/drivers/pci/quirks.c --- a/drivers/pci/quirks.c Wed Mar 6 17:13:52 2002 +++ b/drivers/pci/quirks.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/pcmcia/Config.in Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/pcmcia/Makefile Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/pcmcia/pci_socket.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/pcmcia/sa1100.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/pcmcia/sa1100_adsbitsy.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/pcmcia/sa1100_assabet.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/pcmcia/sa1100_cerf.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/pcmcia/sa1100_flexanet.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/pcmcia/sa1100_freebird.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/pcmcia/sa1100_generic.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/pcmcia/sa1100_graphicsclient.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/pcmcia/sa1100_graphicsmaster.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/pcmcia/sa1100_h3600.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/pcmcia/sa1100_jornada720.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/pcmcia/sa1100_neponset.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/pcmcia/sa1100_pangolin.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/pcmcia/sa1100_pfs168.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/pcmcia/sa1100_simpad.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/pcmcia/sa1100_stork.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/pcmcia/sa1100_xp860.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/pcmcia/sa1100_yopy.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/pnp/isapnp.c Wed Mar 6 17:13:52 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/quirks.c b/drivers/pnp/quirks.c --- a/drivers/pnp/quirks.c Wed Mar 6 17:13:54 2002 +++ b/drivers/pnp/quirks.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/s390/block/dasd.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/s390/block/xpram.c Wed Mar 6 17:13:55 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/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c --- a/drivers/sbus/char/jsflash.c Wed Mar 6 17:13:54 2002 +++ b/drivers/sbus/char/jsflash.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/scsi/3w-xxxx.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/scsi/3w-xxxx.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/scsi/Makefile Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/scsi/NCR5380.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/scsi/NCR5380.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/scsi/aic7xxx/aic7xxx.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/scsi/eata.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/scsi/eata.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/scsi/ide-scsi.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/scsi/imm.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/scsi/ips.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/scsi/ppa.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/scsi/qlogicfas.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/scsi/scsi.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/scsi/scsi.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/scsi/scsi_error.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/scsi/scsi_syms.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/scsi/sd.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/scsi/sr.c Wed Mar 6 17:13:53 2002 @@ -524,7 +524,7 @@ scsi_CDs[i].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) | diff -Nru a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c --- a/drivers/scsi/sr_ioctl.c Wed Mar 6 17:13:53 2002 +++ b/drivers/scsi/sr_ioctl.c Wed Mar 6 17:13:53 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; @@ -548,11 +548,6 @@ return put_user(scsi_CDs[target].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); default: return scsi_ioctl(scsi_CDs[target].device, cmd, (void *) arg); diff -Nru a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c --- a/drivers/scsi/u14-34f.c Wed Mar 6 17:13:54 2002 +++ b/drivers/scsi/u14-34f.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/scsi/u14-34f.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/telephony/Config.help Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/telephony/ixj.c Wed Mar 6 17:13:52 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/audio.c b/drivers/usb/audio.c --- a/drivers/usb/audio.c Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/audio.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/auerswald.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/devices.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/usb/hcd/Config.help Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/usb/hcd/ehci-hcd.c Wed Mar 6 17:13:55 2002 @@ -44,6 +44,7 @@ #include #include "../hcd.h" +#include #include #include #include diff -Nru a/drivers/usb/hcd/ehci-hub.c b/drivers/usb/hcd/ehci-hub.c --- a/drivers/usb/hcd/ehci-hub.c Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/hcd/ehci-hub.c Wed Mar 6 17:13:54 2002 @@ -18,8 +18,6 @@ /* this file is part of ehci-hcd.c */ -#include - /*-------------------------------------------------------------------------*/ /* diff -Nru a/drivers/usb/hcd/ehci-mem.c b/drivers/usb/hcd/ehci-mem.c --- a/drivers/usb/hcd/ehci-mem.c Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/hcd/ehci-mem.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/usb/hcd/ehci-q.c Wed Mar 6 17:13:53 2002 @@ -18,8 +18,6 @@ /* this file is part of ehci-hcd.c */ -#include - /*-------------------------------------------------------------------------*/ /* diff -Nru a/drivers/usb/hcd/ehci-sched.c b/drivers/usb/hcd/ehci-sched.c --- a/drivers/usb/hcd/ehci-sched.c Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/hcd/ehci-sched.c Wed Mar 6 17:13:54 2002 @@ -20,8 +20,6 @@ /*-------------------------------------------------------------------------*/ -#include "ehci.h" - /* * EHCI scheduled transaction support: interrupt, iso, split iso * These are called "periodic" transactions in the EHCI spec. @@ -394,6 +392,9 @@ } frame += period; } while (frame < ehci->periodic_size); + + /* update bandwidth utilization records (for usbfs) */ + usb_claim_bandwidth (urb->dev, urb, usecs, 0); /* maybe enable periodic schedule processing */ if (!ehci->periodic_urbs++) { diff -Nru a/drivers/usb/hcd/ohci-q.c b/drivers/usb/hcd/ohci-q.c --- a/drivers/usb/hcd/ohci-q.c Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/hcd/ohci-q.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/usb/hcd.c Wed Mar 6 17:13:55 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; */ @@ -506,6 +523,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 +879,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 +893,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 +988,6 @@ != 0) { err ("request interrupt %s failed", bufp); retval = -EBUSY; -clean_3: driver->hcd_free (hcd); goto clean_2; } @@ -643,26 +999,15 @@ (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; 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 +1023,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 +1063,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); @@ -1257,6 +1600,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 +1623,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) */ diff -Nru a/drivers/usb/hcd.h b/drivers/usb/hcd.h --- a/drivers/usb/hcd.h Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/hcd.h Wed Mar 6 17:13:54 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,8 +35,8 @@ /* * 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; @@ -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 Wed Mar 6 17:13:55 2002 +++ b/drivers/usb/hid-core.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/hub.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/ov511.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/ov511.h Wed Mar 6 17:13:54 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/pwc-if.c b/drivers/usb/pwc-if.c --- a/drivers/usb/pwc-if.c Wed Mar 6 17:13:55 2002 +++ b/drivers/usb/pwc-if.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/se401.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/serial/Config.help Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/usb/serial/Config.in Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/usb/serial/belkin_sa.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/usb/serial/cyberjack.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/serial/empeg.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/usb/serial/ftdi_sio.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/usb/serial/ipaq.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/usb/serial/ipaq.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/serial/ir-usb.c Wed Mar 6 17:13:54 2002 @@ -252,8 +252,6 @@ dbg("%s - port %d", __FUNCTION__, port->number); - down (&port->sem); - ++port->open_count; if (port->open_count == 1) { @@ -293,9 +291,6 @@ if (result) err("%s - failed submitting read urb, error %d", __FUNCTION__, result); } - - up (&port->sem); - return result; } @@ -312,8 +307,6 @@ if (!serial) return; - down (&port->sem); - --port->open_count; if (port->open_count <= 0) { @@ -324,7 +317,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 Wed Mar 6 17:13:53 2002 +++ b/drivers/usb/serial/keyspan.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/usb/serial/keyspan_pda.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/drivers/usb/serial/kl5kusb105.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/usb/serial/mct_u232.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/serial/omninet.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/serial/pl2303.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/serial/usbserial.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/serial/visor.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/usb/serial/visor.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/serial/whiteheat.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/usb/storage/Makefile Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/usb/storage/isd200.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/storage/transport.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/usb/stv680.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/usb/uhci.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/usb/uhci.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/usb/usb-ohci.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/usb-uhci.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/usb/usb.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/usb/usbvideo.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/drivers/usb/vicam.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/video/Config.help Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/video/cyber2000fb.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/video/imsttfb.c Wed Mar 6 17:13:55 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/neofb.c b/drivers/video/neofb.c --- a/drivers/video/neofb.c Wed Mar 6 17:13:52 2002 +++ b/drivers/video/neofb.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/video/radeonfb.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/video/riva/fbdev.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/drivers/video/sis/sis_main.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/drivers/video/tdfxfb.c Wed Mar 6 17:13:53 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/fs/Config.help b/fs/Config.help --- a/fs/Config.help Wed Mar 6 17:13:55 2002 +++ b/fs/Config.help Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/fs/Config.in Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/fs/Makefile Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/fs/bad_inode.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/fs/block_dev.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/fs/buffer.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/fs/coda/cache.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/fs/coda/cnode.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/fs/coda/coda_linux.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/fs/coda/dir.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/fs/coda/file.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/fs/coda/inode.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/fs/coda/pioctl.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/fs/coda/psdev.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/fs/coda/sysctl.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/fs/coda/upcall.c Wed Mar 6 17:13:55 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/dcache.c b/fs/dcache.c --- a/fs/dcache.c Wed Mar 6 17:13:53 2002 +++ b/fs/dcache.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/fs/exec.c Wed Mar 6 17:13:54 2002 @@ -343,15 +343,11 @@ struct file *open_exec(const char *name) { struct nameidata nd; - struct inode *inode; - struct file *file; - int err = 0; + int err = path_lookup(name, LOOKUP_FOLLOW, &nd); + struct file *file = ERR_PTR(err); - if (path_init(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd)) - err = path_walk(name, &nd); - 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)) { diff -Nru a/fs/fat/inode.c b/fs/fat/inode.c --- a/fs/fat/inode.c Wed Mar 6 17:13:54 2002 +++ b/fs/fat/inode.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/fs/file.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/fs/hpfs/ea.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/fs/inode.c Wed Mar 6 17:13:54 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) @@ -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 Wed Mar 6 17:13:54 2002 +++ b/fs/intermezzo/cache.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/fs/intermezzo/inode.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/fs/intermezzo/kml_reint.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/fs/intermezzo/presto.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/fs/intermezzo/psdev.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/fs/intermezzo/super.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/fs/intermezzo/vfs.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/fs/jffs/intrep.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/fs/jffs2/compr_zlib.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/fs/jffs2/dir.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/fs/jffs2/erase.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/fs/jffs2/file.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/fs/jffs2/gc.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/fs/jffs2/malloc.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/fs/jffs2/nodelist.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/fs/jffs2/nodelist.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/fs/jffs2/nodemgmt.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/fs/jffs2/read.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/fs/jffs2/readinode.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/fs/jffs2/scan.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/fs/jffs2/super.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/fs/jffs2/symlink.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 @@ -0,0 +1,4539 @@ +/* + * + * 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 == -1) { + 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); + } + /* + * 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 == -1) + 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 = -1; + return 0; + } + repeat: + rc = get_index(ip, dir_index, &dirtab_slot); + if (rc) { + filp->f_pos = -1; + 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 = -1; + return 0; + } + dir_index = le32_to_cpu(dirtab_slot.addr2); + if (dir_index == -1) { + filp->f_pos = -1; + 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 = -1; + 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; + } + 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 = -1; + 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 = -1; + return 0; + } + + if ((rc = dtReadNext(ip, &filp->f_pos, &btstack))) { + jERROR(1, + ("jfs_readdir: unexpected rc = %d from dtReadNext\n", + rc)); + filp->f_pos = -1; + 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 = -1; + 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 = -1; + 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 = -1; + break; + } + + bn = le64_to_cpu(p->header.next); + if (bn == 0) { + filp->f_pos = -1; + 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 Wed Mar 6 17:13:55 2002 @@ -0,0 +1,284 @@ +/* + * 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 ) + + +/* + * 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/fs/lockd/svc4proc.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/fs/lockd/svcproc.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/fs/locks.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/fs/namei.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/fs/namespace.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/fs/ncpfs/dir.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/fs/nfs/dir.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/fs/nfs/flushd.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/fs/nfs/unlink.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/fs/nfsd/export.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/fs/nfsd/lockd.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/fs/nfsd/nfs3proc.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/fs/nfsd/nfs3xdr.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/fs/nfsd/nfscache.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/fs/nfsd/nfsctl.c Wed Mar 6 17:13:53 2002 @@ -121,7 +121,7 @@ err = -EPERM; else err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen); - exp_unlock(); + exp_readunlock(); return err; } @@ -144,7 +144,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 +187,6 @@ int err; int argsize, respsize; - lock_kernel (); err = -EPERM; if (!capable(CAP_SYS_ADMIN)) { @@ -257,7 +256,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 Wed Mar 6 17:13:54 2002 +++ b/fs/nfsd/nfsfh.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/fs/nfsd/nfsproc.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/fs/nfsd/nfssvc.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/fs/nfsd/vfs.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/fs/nls/Config.in Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/fs/open.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/fs/partitions/Config.in Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/fs/partitions/check.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/fs/partitions/check.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/fs/partitions/msdos.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/fs/pipe.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/fs/reiserfs/bitmap.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/fs/reiserfs/fix_node.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/fs/reiserfs/inode.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/fs/reiserfs/journal.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/fs/reiserfs/namei.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/fs/reiserfs/super.c Wed Mar 6 17:13:54 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/dir.c b/fs/smbfs/dir.c --- a/fs/smbfs/dir.c Wed Mar 6 17:13:54 2002 +++ b/fs/smbfs/dir.c Wed Mar 6 17:13:54 2002 @@ -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/proc.c b/fs/smbfs/proc.c --- a/fs/smbfs/proc.c Wed Mar 6 17:13:53 2002 +++ b/fs/smbfs/proc.c Wed Mar 6 17:13:53 2002 @@ -245,15 +245,20 @@ * 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) { + read_unlock(&dparent_lock); return -ENAMETOOLONG; + } len = server->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; *path++ = '\\'; @@ -263,6 +268,7 @@ if (IS_ROOT(entry)) break; } + read_unlock(&dparent_lock); reverse_string(buf, path-buf); /* maxlen is at least 1 */ diff -Nru a/fs/super.c b/fs/super.c --- a/fs/super.c Wed Mar 6 17:13:54 2002 +++ b/fs/super.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/fs/sysv/ChangeLog Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/fs/sysv/symlink.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/fs/udf/dir.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/fs/ufs/swab.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-alpha/bitops.h Wed Mar 6 17:13:55 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/mmu_context.h b/include/asm-alpha/mmu_context.h --- a/include/asm-alpha/mmu_context.h Wed Mar 6 17:13:55 2002 +++ b/include/asm-alpha/mmu_context.h Wed Mar 6 17:13:55 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/spinlock.h b/include/asm-alpha/spinlock.h --- a/include/asm-alpha/spinlock.h Wed Mar 6 17:13:55 2002 +++ b/include/asm-alpha/spinlock.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-alpha/system.h Wed Mar 6 17:13:52 2002 @@ -131,15 +131,13 @@ #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*); +extern void alpha_switch_to(unsigned long, struct task_struct*); #define mb() \ __asm__ __volatile__("mb": : :"memory") @@ -368,7 +366,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 +440,7 @@ return val; } -extern __inline__ unsigned long +static inline unsigned long __xchg_u64(volatile long *m, unsigned long val) { unsigned long dummy; @@ -416,10 +466,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 +505,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 +588,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 +617,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 Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/arch-adifcc/irqs.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/arch-adifcc/serial.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/arch-anakin/ide.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/arch-anakin/time.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/arch-anakin/uncompress.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/arch-arc/time.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/arch-cl7500/time.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/arch-clps711x/memory.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/arch-clps711x/time.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/arch-ebsa110/time.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/arch-ebsa285/keyboard.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/arch-ebsa285/time.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/arch-epxa10db/time.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/arch-epxa10db/uncompress.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/arch-integrator/time.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/arch-iop310/irqs.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/arch-iop310/memory.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/arch-iop310/serial.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/arch-iop310/timex.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/arch-iop310/uncompress.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/arch-l7200/time.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/arch-nexuspci/time.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/arch-rpc/time.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/arch-sa1100/assabet.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/arch-sa1100/cerf.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/arch-sa1100/graphicsclient.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/arch-sa1100/hardware.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/arch-sa1100/ide.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/arch-sa1100/irqs.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/arch-sa1100/pangolin.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/arch-sa1100/system3.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/arch-sa1100/time.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/arch-shark/hardware.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/arch-shark/keyboard.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/arch-shark/param.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/arch-shark/time.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/arch-tbox/time.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/bitops.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/cpu-multi32.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/cpu-single.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/current.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/hardirq.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/io.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/irq.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/mach/irq.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/mmu.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/mmu_context.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/page.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/pci.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/pgalloc.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/pgtable.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/proc-armo/processor.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/proc-armv/cache.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/proc-armv/pgalloc.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/proc-armv/pgtable.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/proc-armv/processor.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/proc-armv/uaccess.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-arm/processor.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/procinfo.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-arm/smplock.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/softirq.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/stat.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/system.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-arm/uaccess.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-arm/unistd.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-cris/ide.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-i386/fixmap.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-i386/hw_irq.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-i386/io.h Wed Mar 6 17:13:54 2002 @@ -95,6 +95,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 diff -Nru a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h --- a/include/asm-i386/unistd.h Wed Mar 6 17:13:55 2002 +++ b/include/asm-i386/unistd.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-ia64/sn/pci/pcibr.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-ppc/highmem.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-ppc/kmap_types.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-ppc/pgalloc.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-ppc/pgtable.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-x86_64/bitops.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-x86_64/mmu_context.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/include/asm-x86_64/page.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/include/asm-x86_64/pda.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/include/asm-x86_64/pgalloc.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/include/asm-x86_64/pgtable.h Wed Mar 6 17:13:53 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/system.h b/include/asm-x86_64/system.h --- a/include/asm-x86_64/system.h Wed Mar 6 17:13:53 2002 +++ b/include/asm-x86_64/system.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/include/linux/blk.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/include/linux/blkpg.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/include/linux/cache.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/include/linux/coda_fs_i.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/include/linux/coda_linux.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/include/linux/coda_proc.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/include/linux/compiler.h Wed Mar 6 17:13:53 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/dcache.h b/include/linux/dcache.h --- a/include/linux/dcache.h Wed Mar 6 17:13:54 2002 +++ b/include/linux/dcache.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/include/linux/dnotify.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/include/linux/fs.h Wed Mar 6 17:13:53 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/hdreg.h b/include/linux/hdreg.h --- a/include/linux/hdreg.h Wed Mar 6 17:13:54 2002 +++ b/include/linux/hdreg.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/include/linux/ide.h Wed Mar 6 17:13:53 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/intermezzo_fs.h b/include/linux/intermezzo_fs.h --- a/include/linux/intermezzo_fs.h Wed Mar 6 17:13:54 2002 +++ b/include/linux/intermezzo_fs.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/include/linux/irq_cpustat.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/include/linux/jbd.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/include/linux/jffs2.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/include/linux/jffs2_fs_sb.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/include/linux/lvm.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/include/linux/mm.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/include/linux/nfsd/export.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/include/linux/nfsd/interface.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/include/linux/parport.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/include/linux/pci.h Wed Mar 6 17:13:54 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 */ }; diff -Nru a/include/linux/pci_ids.h b/include/linux/pci_ids.h --- a/include/linux/pci_ids.h Wed Mar 6 17:13:54 2002 +++ b/include/linux/pci_ids.h Wed Mar 6 17:13:54 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 diff -Nru a/include/linux/reiserfs_fs_sb.h b/include/linux/reiserfs_fs_sb.h --- a/include/linux/reiserfs_fs_sb.h Wed Mar 6 17:13:55 2002 +++ b/include/linux/reiserfs_fs_sb.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/include/linux/rtc.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/include/linux/sched.h Wed Mar 6 17:13:53 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/smp.h b/include/linux/smp.h --- a/include/linux/smp.h Wed Mar 6 17:13:52 2002 +++ b/include/linux/smp.h Wed Mar 6 17:13:52 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/sunrpc/sched.h b/include/linux/sunrpc/sched.h --- a/include/linux/sunrpc/sched.h Wed Mar 6 17:13:53 2002 +++ b/include/linux/sunrpc/sched.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/include/linux/sunrpc/svc.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/include/linux/sunrpc/svcsock.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/include/linux/sunrpc/types.h Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/include/linux/swap.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/include/linux/telephony.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/include/linux/usb.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/include/math-emu/op-4.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/include/net/irda/irda-usb.h Wed Mar 6 17:13:54 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/irlap.h b/include/net/irda/irlap.h --- a/include/net/irda/irlap.h Wed Mar 6 17:13:54 2002 +++ b/include/net/irda/irlap.h Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/include/net/irda/irlap_event.h Wed Mar 6 17:13:52 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/sound/ac97_codec.h b/include/sound/ac97_codec.h --- a/include/sound/ac97_codec.h Wed Mar 6 17:13:52 2002 +++ b/include/sound/ac97_codec.h Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/include/sound/asound.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/include/sound/core.h Wed Mar 6 17:13:54 2002 @@ -292,13 +292,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/emu10k1.h b/include/sound/emu10k1.h --- a/include/sound/emu10k1.h Wed Mar 6 17:13:53 2002 +++ b/include/sound/emu10k1.h Wed Mar 6 17:13:53 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/version.h b/include/sound/version.h --- a/include/sound/version.h Wed Mar 6 17:13:54 2002 +++ b/include/sound/version.h Wed Mar 6 17:13:54 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 " (Tue Feb 26 09:34:24 2002 UTC)" diff -Nru a/init/main.c b/init/main.c --- a/init/main.c Wed Mar 6 17:13:53 2002 +++ b/init/main.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/kernel/exec_domain.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/kernel/fork.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/kernel/info.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/kernel/kmod.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/kernel/ksyms.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/kernel/sched.c Wed Mar 6 17:13:53 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); @@ -1490,6 +1438,7 @@ idle->prio = MAX_PRIO; idle->state = TASK_RUNNING; idle->thread_info->cpu = cpu; + idle->thread_info->preempt_count = (idle->lock_depth >= 0); double_rq_unlock(idle_rq, rq); set_tsk_need_resched(idle); __restore_flags(flags); @@ -1509,14 +1458,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 +1493,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 Wed Mar 6 17:13:54 2002 +++ b/kernel/signal.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/lib/zlib_inflate/inflate.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/mm/filemap.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/mm/memory.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/mm/mmap.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/mm/page_alloc.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/mm/shmem.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/mm/slab.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/mm/swapfile.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/mm/vmalloc.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/mm/vmscan.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/net/Makefile Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/net/core/dev.c Wed Mar 6 17:13:55 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. diff -Nru a/net/irda/af_irda.c b/net/irda/af_irda.c --- a/net/irda/af_irda.c Wed Mar 6 17:13:54 2002 +++ b/net/irda/af_irda.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/net/irda/irda_device.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/net/irda/irlap.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/net/irda/irlap_event.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/net/irda/irlap_frame.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/net/irda/irnet/irnet.h Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/net/irda/irnet/irnet_irda.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/net/sunrpc/clnt.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/net/sunrpc/sched.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:55 2002 +++ b/net/sunrpc/stats.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/net/sunrpc/svc.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/net/sunrpc/svcsock.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/net/sunrpc/xprt.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/net/unix/af_unix.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/net/wanrouter/af_wanpipe.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/net/wanrouter/wanmain.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/net/wanrouter/wanproc.c Wed Mar 6 17:13:55 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/tkgen.c b/scripts/tkgen.c --- a/scripts/tkgen.c Wed Mar 6 17:13:55 2002 +++ b/scripts/tkgen.c Wed Mar 6 17:13:55 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/Makefile b/sound/Makefile --- a/sound/Makefile Wed Mar 6 17:13:54 2002 +++ b/sound/Makefile Wed Mar 6 17:13:54 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.in b/sound/core/Config.in --- a/sound/core/Config.in Wed Mar 6 17:13:54 2002 +++ b/sound/core/Config.in Wed Mar 6 17:13:54 2002 @@ -1,6 +1,5 @@ # ALSA soundcard-configuration -dep_tristate ' RTC Timer support' CONFIG_SND_RTCTIMER $CONFIG_SND 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 +12,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 Wed Mar 6 17:13:53 2002 +++ b/sound/core/Makefile Wed Mar 6 17:13:53 2002 @@ -33,16 +33,18 @@ 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 + # Toplevel Module Dependency obj-$(CONFIG_SND_DUMMY) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_VIRMIDI) += snd-rawmidi.o snd.o snd-timer.o @@ -69,7 +71,7 @@ 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_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-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_ES968) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o @@ -79,7 +81,7 @@ 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 diff -Nru a/sound/core/control.c b/sound/core/control.c --- a/sound/core/control.c Wed Mar 6 17:13:53 2002 +++ b/sound/core/control.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/sound/core/device.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/sound/core/hwdep.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/sound/core/info.c Wed Mar 6 17:13:54 2002 @@ -29,6 +29,7 @@ #include #include #include +#include #ifdef CONFIG_DEVFS_FS #include #endif @@ -162,31 +163,40 @@ { snd_info_private_data_t *data; struct snd_info_entry *entry; + int ret = -EINVAL; data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); entry = data->entry; + lock_kernel(); 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; + 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: + unlock_kernel(); + return ret; } static ssize_t snd_info_entry_read(struct file *file, char *buffer, @@ -420,7 +430,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); } diff -Nru a/sound/core/init.c b/sound/core/init.c --- a/sound/core/init.c Wed Mar 6 17:13:53 2002 +++ b/sound/core/init.c Wed Mar 6 17:13:53 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/memory.c b/sound/core/memory.c --- a/sound/core/memory.c Wed Mar 6 17:13:52 2002 +++ b/sound/core/memory.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/sound/core/misc.c Wed Mar 6 17:13:53 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/pcm_lib.c b/sound/core/pcm_lib.c --- a/sound/core/pcm_lib.c Wed Mar 6 17:13:54 2002 +++ b/sound/core/pcm_lib.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/sound/core/pcm_native.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/sound/core/rawmidi.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/sound/core/rtctimer.c Wed Mar 6 17:13:53 2002 @@ -156,13 +156,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; } diff -Nru a/sound/core/seq/Makefile b/sound/core/seq/Makefile --- a/sound/core/seq/Makefile Wed Mar 6 17:13:55 2002 +++ b/sound/core/seq/Makefile Wed Mar 6 17:13:55 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,7 +68,7 @@ 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 @@ -76,7 +78,7 @@ 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 Wed Mar 6 17:13:53 2002 +++ b/sound/core/seq/instr/Makefile Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/sound/core/seq/oss/seq_oss.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/sound/core/seq/oss/seq_oss_init.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/sound/core/seq/oss/seq_oss_midi.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/sound/core/seq/oss/seq_oss_readq.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/sound/core/seq/oss/seq_oss_synth.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/sound/core/seq/seq_clientmgr.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/sound/core/seq/seq_device.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/sound/core/seq/seq_dummy.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/sound/core/seq/seq_instr.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/sound/core/seq/seq_lock.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/sound/core/seq/seq_memory.c Wed Mar 6 17:13:53 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_ports.c b/sound/core/seq/seq_ports.c --- a/sound/core/seq/seq_ports.c Wed Mar 6 17:13:55 2002 +++ b/sound/core/seq/seq_ports.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/sound/core/seq/seq_timer.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/sound/core/seq/seq_virmidi.c Wed Mar 6 17:13:52 2002 @@ -431,7 +431,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 Wed Mar 6 17:13:53 2002 +++ b/sound/core/sound.c Wed Mar 6 17:13:53 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/timer.c b/sound/core/timer.c --- a/sound/core/timer.c Wed Mar 6 17:13:52 2002 +++ b/sound/core/timer.c Wed Mar 6 17:13:52 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/dummy.c b/sound/drivers/dummy.c --- a/sound/drivers/dummy.c Wed Mar 6 17:13:54 2002 +++ b/sound/drivers/dummy.c Wed Mar 6 17:13:54 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/mpu401.c b/sound/drivers/mpu401/mpu401.c --- a/sound/drivers/mpu401/mpu401.c Wed Mar 6 17:13:55 2002 +++ b/sound/drivers/mpu401/mpu401.c Wed Mar 6 17:13:55 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/mtpav.c b/sound/drivers/mtpav.c --- a/sound/drivers/mtpav.c Wed Mar 6 17:13:53 2002 +++ b/sound/drivers/mtpav.c Wed Mar 6 17:13:53 2002 @@ -761,7 +761,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/serial-u16550.c b/sound/drivers/serial-u16550.c --- a/sound/drivers/serial-u16550.c Wed Mar 6 17:13:53 2002 +++ b/sound/drivers/serial-u16550.c Wed Mar 6 17:13:53 2002 @@ -891,7 +891,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 +940,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 Wed Mar 6 17:13:53 2002 +++ b/sound/drivers/virmidi.c Wed Mar 6 17:13:53 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/isa/Config.in b/sound/isa/Config.in --- a/sound/isa/Config.in Wed Mar 6 17:13:53 2002 +++ b/sound/isa/Config.in Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/sound/isa/ad1816a/ad1816a.c Wed Mar 6 17:13:53 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/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c --- a/sound/isa/ad1848/ad1848.c Wed Mar 6 17:13:52 2002 +++ b/sound/isa/ad1848/ad1848.c Wed Mar 6 17:13:52 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/als100.c b/sound/isa/als100.c --- a/sound/isa/als100.c Wed Mar 6 17:13:53 2002 +++ b/sound/isa/als100.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/sound/isa/azt2320.c Wed Mar 6 17:13:55 2002 @@ -50,6 +50,8 @@ #define chip_t cs4231_t +#define PFX "azt2320: " + EXPORT_NO_SYMBOLS; MODULE_AUTHOR("Massimo Piccioni "); @@ -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 Wed Mar 6 17:13:53 2002 +++ b/sound/isa/cmi8330.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/sound/isa/cs423x/cs4231.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:53 2002 +++ b/sound/isa/cs423x/cs4231_lib.c Wed Mar 6 17:13:53 2002 @@ -1335,7 +1335,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 Wed Mar 6 17:13:53 2002 +++ b/sound/isa/cs423x/cs4236.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/sound/isa/dt0197h.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/sound/isa/es1688/es1688.c Wed Mar 6 17:13:54 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/es18xx.c b/sound/isa/es18xx.c --- a/sound/isa/es18xx.c Wed Mar 6 17:13:54 2002 +++ b/sound/isa/es18xx.c Wed Mar 6 17:13:54 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/gusclassic.c b/sound/isa/gus/gusclassic.c --- a/sound/isa/gus/gusclassic.c Wed Mar 6 17:13:55 2002 +++ b/sound/isa/gus/gusclassic.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/sound/isa/gus/gusextreme.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/sound/isa/gus/gusmax.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/sound/isa/gus/interwave.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/sound/isa/opl3sa2.c Wed Mar 6 17:13:54 2002 @@ -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 Wed Mar 6 17:13:53 2002 +++ b/sound/isa/opti9xx/opti92x-ad1848.c Wed Mar 6 17:13:53 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/sb16.c b/sound/isa/sb/sb16.c --- a/sound/isa/sb/sb16.c Wed Mar 6 17:13:53 2002 +++ b/sound/isa/sb/sb16.c Wed Mar 6 17:13:53 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/sb8.c b/sound/isa/sb/sb8.c --- a/sound/isa/sb/sb8.c Wed Mar 6 17:13:55 2002 +++ b/sound/isa/sb/sb8.c Wed Mar 6 17:13:55 2002 @@ -144,13 +144,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 +211,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/sgalaxy.c b/sound/isa/sgalaxy.c --- a/sound/isa/sgalaxy.c Wed Mar 6 17:13:55 2002 +++ b/sound/isa/sgalaxy.c Wed Mar 6 17:13:55 2002 @@ -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 Wed Mar 6 17:13:53 2002 +++ b/sound/isa/wavefront/wavefront.c Wed Mar 6 17:13:53 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/last.c b/sound/last.c --- a/sound/last.c Wed Mar 6 17:13:53 2002 +++ b/sound/last.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:53 2002 +++ b/sound/oss/btaudio.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/sound/oss/cs4232.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/sound/oss/emu10k1/main.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/sound/oss/i810_audio.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/sound/oss/mpu401.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/sound/oss/rme96xx.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/sound/oss/sonicvibes.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/sound/oss/trident.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/sound/oss/via82cxxx_audio.c Wed Mar 6 17:13:54 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); @@ -365,7 +365,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 +3271,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 Wed Mar 6 17:13:52 2002 +++ b/sound/oss/ymfpci.c Wed Mar 6 17:13:52 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/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c --- a/sound/pci/ac97/ac97_codec.c Wed Mar 6 17:13:54 2002 +++ b/sound/pci/ac97/ac97_codec.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/sound/pci/ali5451/ali5451.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/sound/pci/als4000.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/sound/pci/cmipci.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/sound/pci/cs4281.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/sound/pci/cs46xx/cs46xx.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/sound/pci/emu10k1/emu10k1.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/sound/pci/emu10k1/emu10k1_main.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/sound/pci/emu10k1/emufx.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/sound/pci/emu10k1/emumixer.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:52 2002 +++ b/sound/pci/emu10k1/emupcm.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/sound/pci/emu10k1/emuproc.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/sound/pci/ens1370.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/sound/pci/es1938.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:55 2002 +++ b/sound/pci/es1968.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:53 2002 +++ b/sound/pci/fm801.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:55 2002 +++ b/sound/pci/ice1712.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:52 2002 +++ b/sound/pci/intel8x0.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/sound/pci/korg1212/korg1212.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:52 2002 +++ b/sound/pci/maestro3.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/sound/pci/nm256/nm256.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/sound/pci/rme96.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:54 2002 +++ b/sound/pci/rme9652/rme9652.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/sound/pci/sonicvibes.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:52 2002 +++ b/sound/pci/trident/trident.c Wed Mar 6 17:13:52 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 Wed Mar 6 17:13:54 2002 +++ b/sound/pci/via686.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:53 2002 +++ b/sound/pci/via8233.c Wed Mar 6 17:13:53 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 Wed Mar 6 17:13:54 2002 +++ b/sound/pci/ymfpci/ymfpci.c Wed Mar 6 17:13:54 2002 @@ -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 Wed Mar 6 17:13:54 2002 +++ b/sound/ppc/keywest.c Wed Mar 6 17:13:54 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 Wed Mar 6 17:13:55 2002 +++ b/sound/ppc/powermac.c Wed Mar 6 17:13:55 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 Wed Mar 6 17:13:54 2002 +++ b/sound/sound_core.c Wed Mar 6 17:13:54 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/emux/emux_synth.c b/sound/synth/emux/emux_synth.c --- a/sound/synth/emux/emux_synth.c Wed Mar 6 17:13:54 2002 +++ b/sound/synth/emux/emux_synth.c Wed Mar 6 17:13:54 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 }