From 71efe341e9e91c8b8b1c055e1fd22c00aff7d8af Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:20:39 -0500 Subject: [PATCH] Linux 2.2.15pre1 o Handle crossing X25 restart confirmations (Guntram Blohm) o Large direct block I/O with small readahead could fail (Mikulas Patocka) o Remove duplicate asm op (Chris Noe) o IUCV Update for S/390 (Martin Schwidefsky) o Fixes to allow Linux/390 to run on P/390 (Martin Schwidefsky) o Some PPC configs didnt compile (W Lewis) o Maybe fix TCP delack hang (Andrea Arcangeli) o Missing task state change (Natapov Gleb) o Fix 82596 false probes (Richard Hirst) o Fix typo in ixj driver (Tim Waugh) o Add AOpen PD-2 DVD to ghost list (Rogier Wolff) o Masquerading site has moved (Hugh Redelmeier) o Fix FAT bug (Pavel Pisa) o Small NFS fixes (HJ Lu) o Update ES1370 to handle rev7 chips (Tom Sailer) o Use amateur radio drivers on Sparc (Tom Sailer) o SCSI generic documentation updates (Douglas Gilbert) o Fix clearing of NT in head.S (Willy Tareau) o Add winchip2 MTRR support (Bart Hartgers) o CREDITS entry for Victor 'RTLinux' Yodaiken (Victor Yodaiken) o Zoltan Boszormenyi has changed address (Zoltan Boszormenyi) o eepro100 updates (Savochkin Andrey Vladimirovich) o Update Davicom driver (Davicom) --- CREDITS | 13 +- Documentation/Configure.help | 15 +- Documentation/networking/dmfe.txt | 55 + Documentation/scsi-generic.txt | 210 ++-- MAINTAINERS | 7 + arch/i386/boot/compressed/head.S | 2 +- arch/i386/boot/setup.S | 1 - arch/i386/kernel/mtrr.c | 133 ++- arch/i386/kernel/setup.c | 4 +- arch/i386/vmlinux.lds | 69 ++ arch/ppc/kernel/pmac_setup.c | 1 + arch/s390/kernel/head.S | 151 ++- arch/sparc/config.in | 19 + dmfe.c | 1594 +++++++++++++++++++++++++++++ drivers/char/random.c | 1 + drivers/net/82596.c | 8 +- drivers/net/eepro100.c | 1468 +++++++++++++++++--------- drivers/s390/char/con3215.c | 38 +- drivers/s390/char/hwc_con.c | 2 + drivers/s390/net/iucv.c | 4 +- drivers/scsi/scsi.c | 1 + drivers/sound/es1370.c | 322 +++--- drivers/sound/es1371.c | 678 +++++++----- drivers/sound/esssolo1.c | 190 ++-- drivers/sound/sonicvibes.c | 267 +++-- drivers/telephony/ixj.c | 2 +- fs/block_dev.c | 5 +- fs/fat/misc.c | 5 +- fs/nfs/inode.c | 7 +- fs/nfsd/nfsctl.c | 2 +- fs/nfsd/nfsfh.c | 30 +- fs/nfsd/nfsproc.c | 3 + include/asm-s390/setup.h | 1 + init/main.c | 13 + net/ipv4/tcp_output.c | 2 +- net/x25/x25_link.c | 3 +- 36 files changed, 4117 insertions(+), 1209 deletions(-) create mode 100644 Documentation/networking/dmfe.txt create mode 100644 arch/i386/vmlinux.lds create mode 100644 dmfe.c diff --git a/CREDITS b/CREDITS index bd0b1926a301..58f83c360dc2 100644 --- a/CREDITS +++ b/CREDITS @@ -253,9 +253,9 @@ W: http://math-www.uni-paderborn.de/~axel/ D: Configuration help text support D: Linux CD and Support Giveaway List -N: Zoltan Boszormenyi -E: zboszor@mol.hu -D: MTRR emulation with Cyrix style ARR registers +N: Zoltán Böszörményi +E: zboszor@mail.externet.hu +D: MTRR emulation with Cyrix style ARR registers, Athlon MTRR support N: John Boyd E: boyd@cis.ohio-state.edu @@ -2265,6 +2265,13 @@ S: 542 West 112th Street, 5N S: New York, New York 10025 S: USA +N: Victor Yodaiken +E: yodaiken@fsmlabs.com +D: RTLinux (RealTime Linux) +S: POB 1822 +S: Socorro NM, 87801 +S: USA + N: Eric Youngdale E: eric@aib.com D: General kernel hacker diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 3ee5958ffae2..855e4f1297eb 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -2573,7 +2573,7 @@ CONFIG_IP_MASQUERADE_MOD You will need the user space program "ipmasqadm" to use these additional modules; you can download it from - http://juanjox.linuxhq.com/ + http://juanjox.kernelnotes.org/ All this additional code is still under development and so is currently marked EXPERIMENTAL. @@ -2588,7 +2588,7 @@ CONFIG_IP_MASQUERADE_IPAUTOFW ftp://ftp.netis.com/pub/members/rlynch/ You will also need the ipmasqadm tool available from - http://juanjox.linuxhq.com/ . + http://juanjox.kernelnotes.org/ . The ipautofw code is still under development and so is currently marked EXPERIMENTAL. If you want to try it, say Y. @@ -2619,7 +2619,7 @@ CONFIG_IP_MASQUERADE_IPPORTFW see ftp://ftp.compsoc.net/users/steve/ipportfw/linux21/ You will need the user space program "ipmasqadm" which can be - downloaded from http://juanjox.linuxhq.com/ + downloaded from http://juanjox.kernelnotes.org/ The portfw code is still under development and so is currently marked EXPERIMENTAL. If you want to try it, say Y. @@ -8976,9 +8976,12 @@ CONFIG_MTRR The AMD K6-2 (stepping 8 and above) and K6-3 processors have two MTRRs. These are supported. - - The Centaur C6 (WinChip) has 8 MCRs, allowing write-combining. These - are supported. + + The Centaur C6 (WinChip) and WinChip 2&3 processors have 8 MCRs. + These are supported. Note that, due to the design of the WinChip 2&3, + setting the access for normal memory to uncachable or write-combine + on these processors will result in instant kernel panic. It is okay + to set this for non-cacheable (video) memory. Saying Y here also fixes a problem with buggy SMP BIOSes which only set the MTRRs for the boot CPU and not the secondary CPUs. This can diff --git a/Documentation/networking/dmfe.txt b/Documentation/networking/dmfe.txt new file mode 100644 index 000000000000..be2908fa93ed --- /dev/null +++ b/Documentation/networking/dmfe.txt @@ -0,0 +1,55 @@ + dmfe.c: Version 1.27C 01/07/2000 + + A Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux. + Copyright (C) 1997 Sten Wang + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + + A. Compiler command: + "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall + -Wstrict-prototypes -O6 -c dmfe.c" + OR + "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net -Wall + -Wstrict-prototypes -O6 -c dmfe.c" + + + B. The following steps teach you how to active DM9102 board: + + 1. Used the upper compiler command to compile dmfe.c + + 2. Insert dmfe module into kernel + "insmod dmfe" ;;Auto Detection Mode (Suggest) + "insmod dmfe mode=0" ;;Force 10M Half Duplex + "insmod dmfe mode=1" ;;Force 100M Half Duplex + "insmod dmfe mode=4" ;;Force 10M Full Duplex + "insmod dmfe mode=5" ;;Force 100M Full Duplex + + 3. Config a dm9102 network interface + "ifconfig eth0 172.22.3.18" + ^^^^^^^^^^^ Your IP address + + 4. Active the IP routing table. For some distributions, it is not + necessary. You can type "route" to check. + + "route add -net 172.22.3.0 eth0" + + + 5. Well done. Your DM9102 adapter actived now. + + + C. Object files description: + + If you can make sure your kernel version, you can rename + to dmfe.o and directly use it without re-compiling. + + + Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw diff --git a/Documentation/scsi-generic.txt b/Documentation/scsi-generic.txt index 2d26dec9d2e8..48ec05c3c990 100644 --- a/Documentation/scsi-generic.txt +++ b/Documentation/scsi-generic.txt @@ -1,17 +1,18 @@ - Notes on Linux's SG driver version 2.1.34 + Notes on Linux's SG driver version 2.1.36 ----------------------------------------- - 990606 + 20000110 Introduction ============ -Sg is one of the four "high level" SCSI device drivers along with -sd, st and sr (disk, tape and CDROM respectively). Sg is more generalized -(but lower level) than its siblings and tends to be used on SCSI devices -that don't fit into the already serviced categories. Thus sg is used for -scanners, cd writers and reading audio cds digitally amongst other things. +The SCSI Generic driver (sg) is one of the four "high level" SCSI device +drivers along with sd, st and sr (disk, tape and CDROM respectively). Sg +is more generalized (but lower level) than its siblings and tends to be +used on SCSI devices that don't fit into the already serviced categories. +Thus sg is used for scanners, cd writers and reading audio cds digitally +amongst other things. These are notes on the Linux SCSI generic packet device driver (sg) -describing version 2.1.34 . The original driver was written by Lawrence +describing version 2.1.36 . The original driver was written by Lawrence Foard and remained in place with minimal changes since circa 1992. Version 2 of this driver remains backward compatible (binary and source **) with the original. It adds scatter gather, command queuing, @@ -27,12 +28,12 @@ by Heiko Eissfeldt in a HOWTO called SCSI-Programming-HOWTO. My copy of the document is version 1.5 dated 7th May 1996. It can found at ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO-SCSI-Programming-HOWTO . A copy of this document can be found at: -http://www.torque.net/sg/p/original/HOWTO-SCSI-Programming-HOWTO . +http://www.torque.net/sg/p/original/HOWTO-SCSI-Programming-HOWTO.txt . ** It is possible to write applications that perform differently depending on whether they are using the original or this version of -the sg device driver. The author is not aware of any useful -pre-existing applications that have problems with version 2 (yet). +the sg device driver. The author is not aware of any useful +pre-existing applications that have problems with version 2. Architecture @@ -52,12 +53,12 @@ Since sg is a character device it supports the traditional Unix system calls of open(), close(), read(), write() and ioctl(). Two other related system calls: poll() and fcntl() are added to this list and how they interact with the sg device driver is documented later. - -An SG device is accessed by write()ing SCSI commands plus any associated + +An SG device is accessed by write()ing SCSI commands plus any associated outgoing data to it; the resulting status codes and any incoming data are then obtained by a read() call. The device can be opened O_NONBLOCK (non-blocking) and poll() used to monitor its progress. The device may be -opened O_EXCL which excludes other "sg" users from this device (but not +opened O_EXCL which excludes other "sg" users from this device (but not "sd", "st" or "sr" users). The buffer given to the write() call is made up as follows: - struct sg_header image (see below) @@ -71,6 +72,12 @@ The buffer received from the corresponding read() call contains: The given SCSI command has its LUN field overwritten by the LUN value of the associated sg device that has been open()ed. +SCSI commands are only attempted once (i.e. there are no internal +retries). If appropriate (e.g. a SCSI READ) the data buffer is copied back +to user space irrespective of the values of the various SCSI related +error/status codes. [Some adapters that use an old error interface in +the SCSI mid level ignore the retry count and retry certain errors.] + sg_header ========= @@ -96,11 +103,11 @@ The 'pack_len' is bizarre and ends up having the 'reply_len' put in it input variable, it is not read by sg internally (only written). The 'reply_len' is the length of the data the corresponding read() -will/should request (including the sg_header). +will/should request (including the sg_header). The 'pack_id' is not acted upon by the sg device driver but is conveyed back to the corresponding read() so it can be used for sequencing by an -application. +application. The 'result' is also bizarre, turning certain types of host codes to 0 (no error), EBUSY or EIO. With better error reporting now available, the @@ -138,7 +145,7 @@ struct sg_header unsigned int driver_status:8; /* [o] driver status+suggestion */ unsigned int other_flags:10; /* unused */ unsigned char sense_buffer[SG_MAX_SENSE]; /* [o] Output in 3 cases: - when target_status is CHECK_CONDITION or + when target_status is CHECK_CONDITION or when target_status is COMMAND_TERMINATED or when (driver_status & DRIVER_SENSE) is true. */ }; /* This structure is 36 bytes long on i386 */ @@ -164,7 +171,7 @@ The 'target_status' field is always output and is the (masked and shifted values are (found in ): /* N.B. 1 bit offset from usual SCSI status values */ #define GOOD 0x00 -#define CHECK_CONDITION 0x01 +#define CHECK_CONDITION 0x01 #define CONDITION_GOOD 0x02 #define BUSY 0x04 #define INTERMEDIATE_GOOD 0x08 @@ -175,7 +182,7 @@ values are (found in ): When the 'target_status' is CHECK_CONDITION or COMMAND_TERMINATED the 'sense_buffer' is output. Note that when (driver_status & DRIVER_SENSE) is true then the 'sense_buffer' is also output (this seems to occur when -the ide-scsi emulation is used). When the 'sense_buffer' is output the +the ide-scsi emulation is used). When the 'sense_buffer' is output the SCSI Sense Key can be found at (sense_buffer[2] & 0x0f) . The 'host_status' field is always output and has the following values @@ -269,12 +276,12 @@ The reserved buffer will be used if: Returns a file descriptor if >= 0 , otherwise -1 implies an error. Error codes (value in 'errno' after -1 returned): -EACCES Either the user doesn't have appropriate permissions on +EACCES Either the user doesn't have appropriate permissions on 'filename' or attempted to use both O_RDONLY and O_EXCL EBUSY O_NONBLOCK set and some user of this sg device has O_EXCL set while someone is already using this device EINTR while waiting for an "exclusive" lock to clear, a signal - is received, just try again ... + is received, just try again ... ENODEV sg not compiled into kernel or the kernel cannot find the sg module (or it can't initialize itself (low memory??)) ENOENT given filename not found @@ -320,12 +327,13 @@ EAGAIN SCSI mid-level out of command blocks (rare), try again. This is more likely to happen when queuing commands, so wait a bit (eg usleep(10000) ) before trying again EDOM a) command queuing off: a packet is already queued - b) command queuing on: too many packets queued + b) command queuing on: too many packets queued (SG_MAX_QUEUE exceeded) - c) SCSI command length given in SG_NEXT_CMD_LEN too long EFAULT 'buffer' for 'count' bytes is an invalid memory range -EIO incoming buffer too short. It should be at least (6 + - sizeof(struct sg_header))==42 bytes long +EIO a) incoming buffer too short. It should be at least + (6 + sizeof(struct sg_header))==42 bytes long + b) SCSI command length given in SG_NEXT_CMD_LEN too long + c) reply_len negative ENOMEM can't get memory for DMA. Take evasive action ... ENXIO either scsi sub-system is currently processing some error (eg doing a device reset) or the sg driver/module removed @@ -334,7 +342,7 @@ ENXIO either scsi sub-system is currently processing some error read(int sg_fd, void * buffer, size_t count) -------------------------------------------- -Read() is used to receive a packet containing 1 mandatory part and 1 +Read() is used to receive a packet containing 1 mandatory part and 1 optional part. The mandatory part is: - a control block (an instance of struct sg_header) The optional part is: @@ -343,7 +351,7 @@ The buffer given to a read() and its corresponding count should be sufficient to accommodate this packet to avoid truncation. Truncation occurs if count < sg_header::replylen . -By default, read() will return the oldest packet queued up. If the +By default, read() will return the oldest packet queued up. If the SG_SET_FORCE_PACK_ID,1 ioctl() is active then read() will attempt to fetch the packet whose pack_id (given earlier to write()) matches the sg_header::pack_id given to this read(). If not available it will either @@ -356,12 +364,16 @@ same as the count argument. It is not the actual number of bytes DMA-ed by the SCSI device. This driver is currently unable to provide such an underrun indication. +If the SCSI device reports an error then a REQUEST SENSE is automatically +done and the output is placed in the sense_buffer array which is in the +control block. This action is sometimes called "auto-sense". + Error codes (value in 'errno' after -1 returned): EAGAIN either no waiting packet or requested packet is not available while O_NONBLOCK flag was set EFAULT 'buffer' for 'count' bytes is an invalid memory range EINTR while waiting for a packet, a signal is received, just - try again ... + try again ... EIO if the 'count' given to read() is < sizeof(struct sg_header) and the 'result' element in sg_header is non-zero. Not a recommended error reporting technique @@ -380,11 +392,11 @@ arrange for an orderly cleanup of those packets that are still "in flight". A process that has an open file descriptor to an sg device may be aborted -(eg by a kill signal). In this case, the kernel automatically calls close +(eg by a kill signal). In this case, the kernel automatically calls close (which is called 'sg_release()' in the version 2 driver) to facilitate the cleanup mentioned above. -A problem persists in version 2.1.34 if the sg driver is a module and is +A problem persists in version 2.1.36 if the sg driver is a module and is removed while packets are still "in flight". Returns 0 if successful, otherwise -1 implies an error. @@ -394,7 +406,7 @@ ENXIO sg driver/module removed or corrupted ioctl(int sg_fd, int command, ...) [sg specific] ------------------------------------------------- -Ken Thompson (or perhaps some other Unix luminary) described ioctl() as +Ken Thompson (or perhaps some other Unix luminary) described ioctl() as the "garbage bin of Unix". This driver compounds the situation by adding more ... If a ioctl command is not recognized by sg (and the various lower levels @@ -417,7 +429,8 @@ Assumes 3rd argument points to an int containing the new timeout value for this file descriptor. The unit is a "jiffy". Packets that are already "in flight" will not be affected. The default value is set on open() and is SG_DEFAULT_TIMEOUT (defined in sg.h). This default is -currently 1 minute and may not be long enough for formats. +currently 1 minute and may not be long enough for formats. Negative +values will yield an EIO error. SG_EMULATED_HOST: Assumes 3rd argument points to an int and outputs a flag indicating @@ -426,33 +439,31 @@ emulated one (eg ide-scsi device driver). A value of 1 means emulated while 0 is not. SG_SET_TRANSFORM W: -Third argument is ignored. Only is meaningful when SG_EMULATED host has -yielded 1 (ie the low-level is the ide-scsi device driver); otherwise -an EINVAL error occurs. The default state is to _not_ transform SCSI -commands to the corresponding ATAPI commands but pass them straight -through as is. [Only certain classes of SCSI commands need to be -transformed to their ATAPI equivalents.] Making this ioctl command causes -transforms to occur thereafter. Subsequent calls to this ioctl command -have no additional effect. Beware, this state will affect all devices -(and hence all related sg file descriptors) associated with this ide-scsi -"bus". -The author of ide-scsi has pointed out that this is not the intended -behaviour which is a 3rd argument of 0 to disable transforms and 1 to -enable transforms. Note the 3rd argument is an 'int' not a 'int *'. -Perhaps the intended behaviour will be implemented soon. +Only is meaningful when SG_EMULATED host has yielded 1 (i.e. the low-level +is the ide-scsi device driver); otherwise an EINVAL error occurs. The +default state is to _not_ transform SCSI commands to the corresponding +ATAPI commands but pass them straight through as is. [Only certain classes +of SCSI commands need to be transformed to their ATAPI equivalents.] +The third argument is interpreted as an integer. When it is non-zero then +a flag is set inside the ide-scsi driver that transforms subsequent +commands sent to this driver. When zero is passed as the 3rd argument to +this ioctl then the flag within the ide-scsi driver is cleared and +subsequent commands are not transformed. Beware, this state will affect +all devices (and hence all related sg file descriptors) associated with +this ide-scsi "bus". SG_GET_TRANSFORM: Third argument is ignored. Only is meaningful when SG_EMULATED host has yielded 1 (ie the low-level is the ide-scsi device driver); otherwise an EINVAL error occurs. Returns 0 to indicate _not_ transforming SCSI -to ATAPI commands (default). Returns 1 when it is transforming. +to ATAPI commands (default). Returns 1 when it is transforming them. SG_SET_FORCE_LOW_DMA +: Assumes 3rd argument points to an int containing 0 or 1. 0 (default) means sg decides whether to use memory above 16 Mbyte level (on i386) based on the host adapter being used by this SCSI device. Typically PCI SCSI adapters will indicate they can DMA to the whole 32 bit address -space. +space. If 1 is given then the host adapter is overridden and only memory below the 16MB level is used for DMA. A requirement for this should be extremely rare. If the "reserved" buffer allocated on open() is not in @@ -469,15 +480,21 @@ adapters setting has been overridden by SG_SET_FORCE_LOW_DMA,1 . SG_GET_SCSI_ID +: Assumes 3rd argument is pointing to an object of type Sg_scsi_id (see -sg.h) and populates it. That structure contains ints for host_no, -channel, scsi_id, lun and scsi_type. Most of this information is -available from other sources (eg SCSI_IOCTL_GET_IDLUN and -SCSI_IOCTL_GET_BUS_NUMBER) but tends to be awkward to collect. +sg.h) and populates it. That structure contains ints for host_no, +channel, scsi_id, lun, scsi_type, allowable commands per lun and +queue_depth. Most of this information is available from other sources +(eg SCSI_IOCTL_GET_IDLUN and SCSI_IOCTL_GET_BUS_NUMBER) but tends to be +awkward to collect. +Allowable commands per lun and queue_depth give an insight to the +command queuing capabilities of the adapters and the device. The latter +overrides the former (logically) and the former is only of interest +if it is equal to queue_depth which probably indicates the device +does not support queueing commands (e.g. most scanners). SG_SET_FORCE_PACK_ID +: Assumes 3rd argument is pointing to an int. 0 (default) instructs read() to return the oldest (written) packet if multiple packets are -waiting to be read (when command queuing is being used). +waiting to be read (when command queuing is being used). 1 instructs read() to view the sg_header::pack_id as input and return the oldest packet matching that pack_id or wait until it arrives (or yield EAGAIN if O_NONBLOCK is in force). As a special case the pack_id of -1 @@ -524,7 +541,7 @@ state and there is one or more _other_ file descriptors using this sg device then an EBUSY error occurs. Per device sequencing was the original semantics and allowed, for example different processes to "share" the device, one perhaps write()ing with the other one read()ing. This command -is supplied if anyone needs those semantics. Per file descriptor +is supplied if anyone needs those semantics. Per file descriptor sequencing, perhaps with the use of the O_EXCL flag, seems more sensible. SG_GET_MERGE_FD +: @@ -548,7 +565,7 @@ queuing is on. SG_SET_UNDERRUN_FLAG +: Assumes 3rd argument is pointing to an int. 0 (current default, set by SG_DEF_UNDERRUN_FLAG in sg.h) requests underruns be ignored. 1 requests -that underruns be flagged. [The only low level driver that acts on this +that underruns be flagged. [The only low level driver that acts on this at the moment is the aic7xxx which yields a DID_ERROR error on underrun.] Only the current file descriptor is affected by this command (unless "per device" sequencing has been selected). @@ -571,7 +588,13 @@ this ioctl(). SG_GET_VERSION_NUM +: Assumes 3rd argument points to an int. The version number is then placed -in that int. A sg version such as 2.1.34 will yield "20134" from this ioctl. +in that int. A sg version such as 2.1.36 will yield "20136" from this ioctl. + +SG_SCSI_RESET +: +Assumes 3rd argument points to an int. Unfortunately doesn't currently +do much (may in the future after other issues are resolved). Yields an +EBUSY error if the SCSI bus or the associated device is being reset +when this ioctl() is called, otherwise returns 0. SG_SET_DEBUG +: Assumes 3rd argument is pointing to an int. 0 (default) turns debugging @@ -641,13 +664,16 @@ fcntl(sg_fd, F_SETFL, flags | O_ASYNC) Utility and Test Programs ========================= -See the README file in the sg_utils.tgz tarball. At the time of -writing this was sg_utils990527.tgz . +See the README file in the sg_utils.tgz tarball. Look on the +http://www.torque.net/sg website for the latest version. Briefly, that tarball contains the following utilities: sg_dd512 'dd' like program that assumes 512 byte blocks size sg_dd2048 'dd' like program that assumes 2048 byte blocks size +sg_dd2352 'dd' like program that assumes 2352 byte blocks size sgq_dd512 like 'sg_dd512' but does command queuing on "if" +sgp_dd probably the most flexible 'dd' variant. It uses POSIX + threads, block size set by "bs=..." plus other options. sg_scan outputs information (optionally Inquiry) on SCSI devices sg_rbuf tests SCSI bus transfer speed (without physical IO) sg_whoami outputs info (optionally capacity) of given SCSI device @@ -657,6 +683,9 @@ sginfo outputs "mode" information about SCSI devices (it is a It also contains the following test programs: sg_debug outputs sg driver state to console/log file sg_poll tests asynchronous notification +sg_runt_ex example run time selection program for application authors +sg_simple1 example program first time users +sg_simple2 like sg_simple1 but with more primitive error processing sg_inquiry does a SCSI Inquiry command (from original HOWTO) sg_tst_med checks presence of media (from original HOWTO) @@ -664,36 +693,40 @@ There are also 2 source files (sg_err.[hc]) for outputting and categorizing SCSI 2 errors and warnings. This code is used by most of the above utility and test programs. -The following programs: sg_dd512, sg_dd2048, sg_scan, sg_rbuf, sg_tst_med, -sg_inquiry and sginfo, can be compiled either for this new sg driver _or_ -the original sg driver. +The following programs: sg_dd512, sg_dd2048, sg_dd2352, sg_scan, sg_runt_ex, +sg_rbuf, sg_tst_med, sg_inquiry and sginfo, can be compiled either for this +new sg driver _or_ the original sg driver (in 2.0 or 2.2 series kernels). +sg_runt_ex can be run on 2.0, 2.2 or 2.3 series kernels even if it is +compiled on a different series (eg compiled on 2.0, run on 2.2). Header files ============ User applications need to find the correct "sg.h" header file matching -their kernel in order to write code using the sg device driver. This is +their kernel in order to write code using the sg device driver. This is sometimes more difficult than it should be. The correct "sg.h" will usually -be found at /usr/src/linux/include/scsi/sg.h . Another important header +be found at /usr/src/linux/include/scsi/sg.h . Another important header file is "scsi.h" which will be in the same directory. -Several distributions have taken their own copies of these files and placed -them in /usr/include/scsi which is where "#include " would go -looking. The directory /usr/include/scsi _should_ be a symbolic link to -/usr/src/linux/include/scsi/ . It was is Redhat 5.1 and 5.2 but it is -not is Redhat 6.0 . Some other distributions have the same problem. To -solve this (as root) do the following: +When "#include " is written in an application then this refers +to the file /usr/include/scsi/sg.h . A problem sometimes arises because +the files in the /usr/include/scsi directory are controlled by the GNU +library people who maintain glibc. Unfortunately these 2 versions of +the sg.h header file are not always in sync. [This was the case in Redhat +6.0 and 6.1 .] Glibc 2.1.3 and later versions should get this right. -# cd /usr/include -# mv scsi scsi_orig -# ln -s ../src/linux/include/scsi scsi +If this is a problem, the user may need to copy sg.h (and scsi.h) from +the kernel source includes to /usr/include scsi. If the user can change +the effected source code then another approach is to rely on the fact that +/usr/src/linux is a symbolic link to /usr/src/linux/include/linux and +change the sg.h include to look like: + #include +This solution is used by the author of cdparanoia (Monty) in his application. -This doesn't seem to be a problem with /usr/include/linux (at least in -Redhat where it is a symbolic link) so it is hard to understand why -/usr/include/scsi is defined the way it is. The fact the -/usr/include/linux is a symbolic link opens up the following solution -proposed by the author of cdparanoia (Monty): -#include +[Former scsi generic documents suggested adding a symbolic link to +bypass this problem but that is not popular with the glibc maintainers. +I would like to thank Andreas Jaeger for his contributions +on this subject.] Extra information in scsi-generic_long.txt @@ -711,6 +744,27 @@ The longer document contains additional sections on: - an appendix with some SCSI 2 information in it +References +========== +http://www.t10.org Very important site for SCSI related information. + Contains SCSI 2 and 3 draft standards. +http://www.andante.org/scsi.html + This is Eric Youngdale's site. Eric is primarily + responsible for the Linux SCSI architecture and + its mid-level implementation. +http://www.kernel.dk Jens Axboe's site for Linux cdrom matters including + the SCSI "sr" driver. +http://www.torque.net/sg + My site with sg related information. +newsgroup:linux-scsi@vger.rutgers.edu + Newsgroup for Linux related SCSI matters +/usr/src/linux/MAINTAINERS + This is a file in the Linux kernel source that + contains up to date information about who maintains + what and where information can be found. Links to + SCSI adapter information are also here. + + Conclusion ========== The SCSI generic packet device driver attempts to make as few assumptions diff --git a/MAINTAINERS b/MAINTAINERS index 06362ff2dbc4..71c37ddc33cf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -712,6 +712,13 @@ M: pgmdsg@ibi.com L: linux-kernel@vger.rutgers.edu S: Maintained +RTLINUX REALTIME LINUX +P: Victor Yodaiken +M: yodaiken@fsmlabs.com +L: rtl@rtlinux.org +W: www.rtlinux.org +S: Maintained + SBPCD CDROM DRIVER P: Eberhard Moenkeberg M: emoenke@gwdg.de diff --git a/arch/i386/boot/compressed/head.S b/arch/i386/boot/compressed/head.S index 0aa8ddc44e09..8022d90b1390 100644 --- a/arch/i386/boot/compressed/head.S +++ b/arch/i386/boot/compressed/head.S @@ -53,7 +53,7 @@ startup_32: xorl %eax,%eax # Back to 0 mov %cx,%ax # SP low 16 bits movl %eax,%esp - pushl 0 # Clear NT + pushl $0 # Clear NT popfl ljmp $(__KERNEL_CS), $0x100000 # Into C and sanity diff --git a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S index e45fcda2d0b1..46beaaa4171e 100644 --- a/arch/i386/boot/setup.S +++ b/arch/i386/boot/setup.S @@ -343,7 +343,6 @@ is_disk1: mov ax,cs ! aka #SETUPSEG sub ax,#DELTA_INITSEG ! aka #INITSEG mov ds,ax - mov ds,ax xor ax,ax mov [0xa0], ax ! set table length to 0 mov ah, #0xc0 diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c index b95c65ed40e1..f6e7eb67be9f 100644 --- a/arch/i386/kernel/mtrr.c +++ b/arch/i386/kernel/mtrr.c @@ -139,41 +139,41 @@ Changed locking to spin with reschedule. Made use of new . v1.28 - 19990201 Zoltan Boszormenyi + 19990201 Zoltán Böszörményi Extended the driver to be able to use Cyrix style ARRs. 19990204 Richard Gooch Restructured Cyrix support. v1.29 - 19990204 Zoltan Boszormenyi + 19990204 Zoltán Böszörményi Refined ARR support: enable MAPEN in set_mtrr_prepare() and disable MAPEN in set_mtrr_done(). 19990205 Richard Gooch Minor cleanups. v1.30 - 19990208 Zoltan Boszormenyi + 19990208 Zoltán Böszörményi Protect plain 6x86s (and other processors without the Page Global Enable feature) against accessing CR4 in set_mtrr_prepare() and set_mtrr_done(). 19990210 Richard Gooch Turned and into function pointers. v1.31 - 19990212 Zoltan Boszormenyi + 19990212 Zoltán Böszörményi Major rewrite of cyrix_arr_init(): do not touch ARRs, leave them as the BIOS have set them up. Enable usage of all 8 ARRs. Avoid multiplications by 3 everywhere and other code clean ups/speed ups. - 19990213 Zoltan Boszormenyi + 19990213 Zoltán Böszörményi Set up other Cyrix processors identical to the boot cpu. Since Cyrix don't support Intel APIC, this is l'art pour l'art. Weigh ARRs by size: If size <= 32M is given, set up ARR# we were given. If size > 32M is given, set up ARR7 only if it is free, fail otherwise. - 19990214 Zoltan Boszormenyi + 19990214 Zoltán Böszörményi Also check for size >= 256K if we are to set up ARR7, mtrr_add() returns the value it gets from set_mtrr() - 19990218 Zoltan Boszormenyi + 19990218 Zoltán Böszörményi Remove Cyrix "coma bug" workaround from here. Moved to linux/arch/i386/kernel/setup.c and linux/include/asm-i386/bugs.h @@ -187,7 +187,7 @@ 19990305 Richard Gooch Temporarily disable AMD support now MTRR capability flag is set. v1.32 - 19990308 Zoltan Boszormenyi + 19990308 Zoltán Böszörményi Adjust my changes (19990212-19990218) to Richard Gooch's latest changes. (19990228-19990305) v1.33 @@ -197,7 +197,7 @@ Support K6-II/III based on Alan Cox's patches. v1.34 19990511 Bart Hartgers - Support Centaur C6 MCR's. + Support Centaur C6 MCRs. 19990512 Richard Gooch Minor cleanups. v1.35 @@ -210,6 +210,9 @@ 19990819 Alan Cox Tested Zoltan's changes on a pre production Athlon - 100% success. Fixed one fall through check to be Intel only. + 19991116 Bart Hartgers + Changed Centaur/IDT WinChip support to include WinChip 2. + (WC 2 kindly provided by IDT). */ #include @@ -581,18 +584,31 @@ static void amd_get_mtrr (unsigned int reg, unsigned long *base, return; } /* End Function amd_get_mtrr */ -static struct +static struct CENTAUR_MCR_CTX { - unsigned long high; - unsigned long low; -} centaur_mcr[8]; + unsigned type_bits[MTRR_NUM_TYPES]; + struct { + u32 low; + u32 high; + } mcr[8]; +} *centaur_ctx=NULL; static void centaur_get_mcr (unsigned int reg, unsigned long *base, unsigned long *size, mtrr_type *type) { - *base = centaur_mcr[reg].high & 0xfffff000; - *size = (~(centaur_mcr[reg].low & 0xfffff000))+1; - *type = MTRR_TYPE_WRCOMB; /* If it is there, it is write-combining */ + unsigned i; + u32 tb; + tb = centaur_ctx->mcr[reg].low & 0xfff; + *base = centaur_ctx->mcr[reg].high & 0xfffff000; + *size = (~(centaur_ctx->mcr[reg].low & 0xfffff000))+1; + if (*size) { + for( i=0; itype_bits[i]==tb) { + *type = (mtrr_type) i; + return; + } + *size = 0; + } } /* End Function centaur_get_mcr */ static void (*get_mtrr) (unsigned int reg, unsigned long *base, @@ -716,6 +732,7 @@ static void centaur_set_mcr_up (unsigned int reg, unsigned long base, unsigned long low, high; if (do_safe) set_mtrr_prepare( &ctxt ); + if (size == 0) { /* Disable */ @@ -724,12 +741,13 @@ static void centaur_set_mcr_up (unsigned int reg, unsigned long base, else { high = base & 0xfffff000; /* base works on 4K pages... */ - low = ((~(size-1))&0xfffff000); - low |= 0x1f; /* only support write-combining... */ + low = ((~(size-1))&0xfffff000)|(centaur_ctx->type_bits[type]); } - centaur_mcr[reg].high = high; - centaur_mcr[reg].low = low; + centaur_ctx->mcr[reg].high = high; + centaur_ctx->mcr[reg].low = low; + wrmsr (0x110 + reg, low, high); + if (do_safe) set_mtrr_done( &ctxt ); } /* End Function centaur_set_mtrr_up */ @@ -1078,6 +1096,12 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type, unsigned long lbase, lsize, last; if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV; + if (type >= MTRR_NUM_TYPES) + { + printk ("mtrr: type: %u illegal\n", type); + return -EINVAL; + } + switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_AMD: @@ -1113,13 +1137,12 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type, } if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR) { - if (type != MTRR_TYPE_WRCOMB) - { - printk ("mtrr: only write-combining is supported\n"); + if (centaur_ctx->type_bits[type]==0) { + printk ("mtrr: type not supported\n"); return -EINVAL; } } - else if (base + size < 0x100000) + else if (base + size < 0x100000) /* Cyrix */ { printk ("mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n", base, size); @@ -1141,11 +1164,6 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type, return -EINVAL; /*break;*/ } - if (type >= MTRR_NUM_TYPES) - { - printk ("mtrr: type: %u illegal\n", type); - return -EINVAL; - } /* If the type is WC, check that this processor supports it */ if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () ) { @@ -1172,7 +1190,8 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type, /* New region is enclosed by an existing region */ if (ltype != type) { - if (type == MTRR_TYPE_UNCACHABLE) continue; + if ((boot_cpu_data.x86_vendor != X86_VENDOR_CENTAUR) && + (type == MTRR_TYPE_UNCACHABLE)) continue; spin_unlock (&main_lock); printk ( "mtrr: type mismatch for %lx,%lx old: %s new: %s\n", base, size, attrib_to_str (ltype), attrib_to_str (type) ); @@ -1652,7 +1671,51 @@ __initfunc(static void centaur_mcr_init (void)) { unsigned i; struct set_mtrr_context ctxt; - + u32 low,high; + u32 mcr_ctrl_value; + unsigned mcr_type; + /* Deduce the MCR traits type for this processor. + * The documentation of the WinChip 2 suggests that we can read the + * MCR_CTRL register for all WinChips, but this hangs on my C6. + * -> Work around this. + */ + if ((boot_cpu_data.x86 == 5) && (boot_cpu_data.x86_model == 4)) { + /* C6 */ + mcr_type = 0; + } else { + rdmsr (0x120, low, high ); + mcr_type = (low>>17)&0x7; + } + centaur_ctx = kmalloc( sizeof( struct CENTAUR_MCR_CTX ), GFP_KERNEL ); + if (centaur_ctx == NULL) { + printk("mtrr: Could not allocate memory. Disabling MTRR.\n"); + boot_cpu_data.x86_capability &= ~X86_FEATURE_MTRR; + return; + } + for( i=0; itype_bits[i] = 0; + } + switch( mcr_type ) { + case 0: + centaur_ctx->type_bits[ MTRR_TYPE_WRCOMB ] = 0x0f; + centaur_ctx->type_bits[ MTRR_TYPE_WRBACK ] = 0x1f; + mcr_ctrl_value = 0x01f0001f; + break; + case 1: + centaur_ctx->type_bits[ MTRR_TYPE_UNCACHABLE ] = 0x02; + centaur_ctx->type_bits[ MTRR_TYPE_WRCOMB ] = 0x13; + centaur_ctx->type_bits[ MTRR_TYPE_WRTHROUGH ] = 0x11; + centaur_ctx->type_bits[ MTRR_TYPE_WRBACK ] = 0x19; + mcr_ctrl_value = 0x01f2005f; + break; + default: + kfree( centaur_ctx ); + centaur_ctx = NULL; + printk ("mtrr: Centaur MCR traits version %u not supported. " + "Disabling MTRR.\n", mcr_type); + boot_cpu_data.x86_capability &= ~X86_FEATURE_MTRR; + return; + } set_mtrr_prepare (&ctxt); /* Unfortunately, MCR's are read-only, so there is no way to * find out what the bios might have done. @@ -1661,14 +1724,16 @@ __initfunc(static void centaur_mcr_init (void)) * This way we are sure that the centaur_mcr array contains the actual * values. The disadvantage is that any BIOS tweaks are thus undone. */ + /* switch off MCR's first */ + wrmsr (0x120, 0, 0 ); for (i = 0; i < 8; ++i) { - centaur_mcr[i].high = 0; - centaur_mcr[i].low = 0; + centaur_ctx->mcr[i].high = 0; + centaur_ctx->mcr[i].low = 0; wrmsr (0x110 + i , 0, 0); } /* Throw the main write-combining switch... */ - wrmsr (0x120, 0x01f0001f, 0); + wrmsr (0x120, mcr_ctrl_value, 0); set_mtrr_done (&ctxt); } /* End Function centaur_mcr_init */ diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index c3d9cb20c201..e2d6a35a22b7 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -7,8 +7,8 @@ * and Martin Mares, November 1997. * * Force Cyrix 6x86(MX) and M II processors to report MTRR capability - * and fix against Cyrix "coma bug" by - * Zoltan Boszormenyi February 1999. + * and Cyrix "coma bug" recognition by + * Zoltán Böszörményi February 1999. * * Force Centaur C6 processors to report MTRR capability. * Bart Hartgers , May 199. diff --git a/arch/i386/vmlinux.lds b/arch/i386/vmlinux.lds new file mode 100644 index 000000000000..ecf90c27c44f --- /dev/null +++ b/arch/i386/vmlinux.lds @@ -0,0 +1,69 @@ +/* ld script to make i386 Linux kernel + * Written by Martin Mares ; + */ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) +SECTIONS +{ + . = 0xC0000000 + 0x100000; + _text = .; /* Text and read-only data */ + .text : { + *(.text) + *(.fixup) + *(.gnu.warning) + } = 0x9090 + .text.lock : { *(.text.lock) } /* out-of-line lock text */ + .rodata : { *(.rodata) } + .kstrtab : { *(.kstrtab) } + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + + _etext = .; /* End of text section */ + + .data : { /* Data */ + *(.data) + CONSTRUCTORS + } + + _edata = .; /* End of data section */ + + . = ALIGN(8192); /* init_task */ + .data.init_task : { *(.data.init_task) } + + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + . = ALIGN(4096); + .data.page_aligned : { *(.data.idt) } + + + __bss_start = .; /* BSS */ + .bss : { + *(.bss) + } + _end = . ; + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c index e27ff843cde0..31eb31e8c2d7 100644 --- a/arch/ppc/kernel/pmac_setup.c +++ b/arch/ppc/kernel/pmac_setup.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S index 083cd470a769..ecac672bcc59 100644 --- a/arch/s390/kernel/head.S +++ b/arch/s390/kernel/head.S @@ -24,7 +24,34 @@ #ifndef CONFIG_IPL .org 0 - .long 0x00080000,0x80000000+start # Just a restart PSW + .long 0x00080000,0x80000000+iplstart # Just a restart PSW + +iplstart: + l %r12,.Lparm # pointer to parameter area +# +# find out memory size +# + mvc 104(8,0),.Lpcmem0 # setup program check handler + slr %r2,%r2 + lhi %r3,1 + sll %r3,20 +.Lloop0: + l %r0,0(%r2) # test page + ar %r2,%r3 # add 1M + jnm .Lloop0 # r1 < 0x80000000 -> loop +.Lchkmem0: + n %r2,.L4malign0 # align to multiples of 4M + st %r2,MEMORY_SIZE-PARMAREA(%r12) # store memory size + slr %r2,%r2 + st %r2,INITRD_SIZE-PARMAREA(%r12) # null ramdisk + st %r2,INITRD_START-PARMAREA(%r12) + j start + +.Lparm: .long PARMAREA +.L4malign0:.long 0xffc00000 + .align 8 +.Lpcmem0:.long 0x00080000,0x80000000 + .Lchkmem0 + #else #ifdef CONFIG_IPL_TAPE #define IPL_BS 1024 @@ -52,6 +79,26 @@ iplstart: lhi %r2,IPL_BS # load start address bras %r14,.Lloader # load rest of ipl image l %r12,.Lparm # pointer to parameter area + +# +# find out memory size +# + mvc 104(8,0),.Lpcmem0 # setup program check handler + slr %r2,%r2 + lhi %r3,1 + sll %r3,20 +.Lloop0: + l %r0,0(%r2) # test page + ar %r2,%r3 # add 1M + jnm .Lloop0 # r1 < 0x80000000 -> loop +.Lchkmem0: + n %r2,.L4malign0 # align to multiples of 4M + st %r2,MEMORY_SIZE-PARMAREA(%r12) # store memory size + c %r2,.Lbigmem # more than 64 MB of memory ? + jl .Lmemok # if yes load ramdisk to 32 MB + mvc INITRD_START-PARMAREA(4,%r12),.Lrdstart +.Lmemok: + # # load parameter file from tape # @@ -73,12 +120,12 @@ iplstart: tr 0(256,%r4),0(%r3) # convert parameters to ascii tr 256(256,%r4),0(%r3) tr 512(256,%r4),0(%r3) - tr 768(128,%r4),0(%r3) + tr 768(122,%r4),0(%r3) .Lnocv: la %r3,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line mvc 0(256,%r3),0(%r4) mvc 256(256,%r3),256(%r4) mvc 512(256,%r3),512(%r4) - mvc 768(128,%r3),768(%r4) + mvc 768(122,%r3),768(%r4) slr %r0,%r0 j .Lcntlp .Ldelspc: @@ -197,7 +244,11 @@ iplstart: .Lcr6: .long 0xff000000 .align 8 .Lcrash:.long 0x000a0000,0x00000000 +.Lpcmem0:.long 0x00080000,0x80000000 + .Lchkmem0 .Lparm: .long PARMAREA +.L4malign0:.long 0xffc00000 +.Lbigmem:.long 0x04000000 +.Lrdstart:.long 0x02000000 .Lldret:.long 0 .Lsnsret: .long 0 .Lebcdic:.long 0xc5c2c3c4,0xc9c30000 @@ -235,31 +286,57 @@ iplstart: .org 0xf0 iplstart: - stidp __LC_CPUID # store cpuid - tm __LC_CPUID,0xff # running under VM ? - jno start # no -> skip loader part l %r1,0xb8 # load ipl subchannel number lhi %r2,0x730 # load start address bras %r14,.Lloader # load rest of ipl image - l %r12,.Lparm # pointer to parameter area +# +# find out memory size +# + mvc 104(8,0),.Lpcmem0 # setup program check handler + slr %r2,%r2 + lhi %r3,1 + sll %r3,20 +.Lloop0: + l %r0,0(%r2) # test page + ar %r2,%r3 # add 1M + jnm .Lloop0 # r1 < 0x80000000 -> loop +.Lchkmem0: + n %r2,.L4malign0 # align to multiples of 4M + st %r2,MEMORY_SIZE-PARMAREA(%r12) # store memory size + c %r2,.Lbigmem # more than 64 MB of memory ? + jl .Lmemok # if yes load ramdisk to 32 MB + mvc INITRD_START-PARMAREA(4,%r12),.Lrdstart +.Lmemok: + # # load parameter file from reader # - la %r2,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line + l %r2,INITRD_START-PARMAREA(%r12) # use ramdisk location as temp bras %r14,.Lloader # load parameter file ltr %r2,%r2 # got anything ? jz .Lnopf - la %r3,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line - l %r4,.Lcvtab - tr 0(256,3),0(4) # convert parameters to ascii - l %r4,.Lcvtab - tr 256(256,3),0(4) - l %r4,.Lcvtab - tr 512(256,3),0(4) - l %r4,.Lcvtab - tr 768(128,3),0(4) + chi %r2,896 + jnh .Lnotrunc + lhi %r2,896 +.Lnotrunc: + l %r4,INITRD_START-PARMAREA(%r12) + clc 0(6,%r4),.Lebcdic # prefix EBCDIC in parm file ? + jne .Lnocv + ahi %r4,6 # skip EBCDIC + ahi %r2,-6 + jnp .Lnopf + l %r3,.Lcvtab + tr 0(256,%r4),0(%r3) # convert parameters to ascii + tr 256(256,%r4),0(%r3) + tr 512(256,%r4),0(%r3) + tr 768(122,%r4),0(%r3) +.Lnocv: la %r3,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line + mvc 0(256,%r3),0(%r4) + mvc 256(256,%r3),256(%r4) + mvc 512(256,%r3),512(%r4) + mvc 768(122,%r3),768(%r4) slr %r0,%r0 j .Lcntlp .Ldelspc: @@ -289,6 +366,10 @@ iplstart: # # everything loaded, reset files in reader, then go for it # + stidp __LC_CPUID # store cpuid + lh %r0,__LC_CPUID+4 # get cpu version + chi %r0,0x7490 # running on P/390 ? + je start # no -> skip reset la %r2,.Lreset lhi %r3,26 .long 0x83230008 @@ -358,13 +439,17 @@ iplstart: .Lcr6: .long 0xff000000 .Lloadp:.long 0,0 .Lparm: .long PARMAREA +.Lebcdic:.long 0xc5c2c3c4,0xc9c30000 +.L4malign0:.long 0xffc00000 +.Lbigmem:.long 0x04000000 +.Lrdstart:.long 0x02000000 .Lcvtab:.long _ebcasc # ebcdic to ascii table .Lreset:.byte 0xc3,0xc8,0xc1,0xd5,0xc7,0xc5,0x40,0xd9,0xc4,0xd9,0x40 .byte 0xc1,0xd3,0xd3,0x40,0xd2,0xc5,0xc5,0xd7,0x40,0xd5,0xd6 .byte 0xc8,0xd6,0xd3,0xc4 # "change rdr all keep nohold" .align 8 -.Lcrash: - .long 0x000a0000,0x00000000 +.Lpcmem0:.long 0x00080000,0x80000000 + .Lchkmem0 +.Lcrash:.long 0x000a0000,0x00000000 .Lnewpsw: .long 0x00080000,0x80000000+.Lioint .Lwaitpsw: @@ -395,8 +480,11 @@ start: basr %r13,0 # get base l %r12,.Lparm1-.LPG1(%r13) # pointer to parameter area # -# find out memory size +# find out memory size. That is done in the ipl loader too but for +# ipl from dasd the size of the memory has to be detected too... # + icm %r0,15,MEMORY_SIZE-PARMAREA(%r12) + jnz .Lsizeok mvc 104(8,0),.Lpcmem-.LPG1(%r13) # setup program check handler slr %r1,%r1 lhi %r2,1 @@ -408,6 +496,7 @@ start: basr %r13,0 # get base .Lchkmem: n %r1,.L4malign-.LPG1(%r13) # align to multiples of 4M st %r1,MEMORY_SIZE-PARMAREA(%r12) # store memory size +.Lsizeok: # # find out if we are running under VM @@ -417,6 +506,11 @@ start: basr %r13,0 # get base jno .Lnovm oi MACHINE_FLAGS+3-PARMAREA(%r12),1 # set VM flag .Lnovm: + lh %r0,__LC_CPUID+4 # get cpu version + chi %r0,0x7490 # running on a P/390 ? + jne .Lnop390 + oi MACHINE_FLAGS+3-PARMAREA(%r12),4 # set P/390 flag +.Lnop390: # # find out if we have an IEEE fpu @@ -428,23 +522,6 @@ start: basr %r13,0 # get base oi MACHINE_FLAGS+3-PARMAREA(%r12),2 # set IEEE fpu flag .Lchkfpu: -# -# move ramdisk to the end of storage, out of the way of the paging tables -# - icm %r2,15,INITRD_START-PARMAREA(%r12) # load adr. of ramdisk - jz .Lnocprd - icm %r3,15,INITRD_SIZE-PARMAREA(%r12) # store size of ramdisk - jz .Lnocprd - l %r4,MEMORY_SIZE-PARMAREA(%r12) - sr %r4,%r3 - srl %r4,12 - sll %r4,12 - st %r4,INITRD_START-PARMAREA(%r12) - lr %r5,%r3 -.Lcprd: - mvcle %r4,%r2,0 - jo .Lcprd -.Lnocprd: lpsw .Lentry-.LPG1(13) # jump to _stext in primary-space, # virtual and never return ... .align 8 diff --git a/arch/sparc/config.in b/arch/sparc/config.in index a0688a893895..b43b57acdc9b 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -103,6 +103,25 @@ endmenu if [ "$CONFIG_NET" = "y" ]; then source net/Config.in + mainmenu_option next_comment + comment 'Amateur Radio support' + bool 'Amateur Radio support' CONFIG_HAMRADIO + if [ "$CONFIG_HAMRADIO" != "n" ] ; then + comment 'Packet Radio protocols' + tristate 'Amateur Radio AX.25 Level 2 protocol' CONFIG_AX25 + if [ "$CONFIG_AX25" != "n" ]; then + bool ' AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE +# bool ' AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER + dep_tristate ' Amateur Radio NET/ROM protocol' CONFIG_NETROM $CONFIG_AX25 + dep_tristate ' Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25 + fi + fi + if [ "$CONFIG_AX25" != "n" ]; then + comment 'AX.25 network device drivers' + dep_tristate 'Serial port KISS driver' CONFIG_MKISS $CONFIG_AX25 + dep_tristate 'Serial port 6PACK driver' CONFIG_6PACK $CONFIG_AX25 + fi + endmenu fi mainmenu_option next_comment diff --git a/dmfe.c b/dmfe.c new file mode 100644 index 000000000000..7ee2469c86a3 --- /dev/null +++ b/dmfe.c @@ -0,0 +1,1594 @@ +/* + dmfe.c: Version 1.27C 01/07/2000 + + A Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux. + Copyright (C) 1997 Sten Wang + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + + Compiler command: + "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall + -Wstrict-prototypes -O6 -c dmfe.c" + OR + "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net -Wall + -Wstrict-prototypes -O6 -c dmfe.c" + + The following steps teach you how to active DM9102 board: + 1. Used the upper compiler command to compile dmfe.c + 2. insert dmfe module into kernel + "insmod dmfe" ;;Auto Detection Mode + "insmod dmfe mode=0" ;;Force 10M Half Duplex + "insmod dmfe mode=1" ;;Force 100M Half Duplex + "insmod dmfe mode=4" ;;Force 10M Full Duplex + "insmod dmfe mode=5" ;;Force 100M Full Duplex + 3. config a dm9102 network interface + "ifconfig eth0 172.22.3.18" + ^^^^^^^^^^^ your IP address + 4. active the IP routing table + "route add -net 172.22.3.0 eth0" + 5. Well done. Your DM9102 adapter actived now. + + Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw + + Date: 10/28,1998 + + (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved. + + Stripped out < 2.2 support, made io address an unsigned long, + Alan Cox + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +/* Board/System/Debug information/definition ---------------- */ +#define PCI_DM9132_ID 0x91321282 /* Davicom DM9132 ID */ +#define PCI_DM9102_ID 0x91021282 /* Davicom DM9102 ID */ +#define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */ + +#define DMFE_SUCC 0 +#define DM9102_IO_SIZE 0x80 +#define DM9102A_IO_SIZE 0x100 +#define TX_FREE_DESC_CNT 0x1 /* Tx packet count */ +#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */ +#define RX_DESC_CNT 0x10 /* Allocated Rx descriptors */ +#define DESC_ALL_CNT TX_DESC_CNT+RX_DESC_CNT +#define TX_BUF_ALLOC 0x600 +#define RX_ALLOC_SIZE 0x620 +#define DM910X_RESET 1 +#define CR6_DEFAULT 0x00280000 /* SF, HD */ +#define CR7_DEFAULT 0x1a2cd +#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */ +#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */ +#define MAX_PACKET_SIZE 1514 +#define DMFE_MAX_MULTICAST 14 +#define RX_MAX_TRAFFIC 0x14000 +#define MAX_CHECK_PACKET 0x8000 + +#define DMFE_10MHF 0 +#define DMFE_100MHF 1 +#define DMFE_10MFD 4 +#define DMFE_100MFD 5 +#define DMFE_AUTO 8 + +#define DMFE_TIMER_WUT jiffies+(HZ*2)/2 /* timer wakeup time : 1 second */ +#define DMFE_TX_TIMEOUT HZ*2 /* tx packet time-out time 2 s" */ + +#define DMFE_DBUG(dbug_now, msg, vaule) if (dmfe_debug || dbug_now) printk("DBUG: %s %x\n", msg, vaule) + +#define DELAY_5US udelay(5) /* udelay scale 1 usec */ + +#define DELAY_1US udelay(1) /* udelay scale 1 usec */ + +#define SHOW_MEDIA_TYPE(mode) printk("\n Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half"); + + +/* CR9 definition: SROM/MII */ +#define CR9_SROM_READ 0x4800 +#define CR9_SRCS 0x1 +#define CR9_SRCLK 0x2 +#define CR9_CRDOUT 0x8 +#define SROM_DATA_0 0x0 +#define SROM_DATA_1 0x4 +#define PHY_DATA_1 0x20000 +#define PHY_DATA_0 0x00000 +#define MDCLKH 0x10000 + +#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US; + +#define CHK_IO_SIZE(pci_id, dev_rev) ( (pci_id==PCI_DM9132_ID) || (dev_rev >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE + + +/* Structure/enum declaration ------------------------------- */ +struct tx_desc { + u32 tdes0, tdes1, tdes2, tdes3; + u32 tx_skb_ptr; + u32 tx_buf_ptr; + u32 next_tx_desc; + u32 reserved; +}; + +struct rx_desc { + u32 rdes0, rdes1, rdes2, rdes3; + u32 rx_skb_ptr; + u32 rx_buf_ptr; + u32 next_rx_desc; + u32 reserved; +}; + +struct dmfe_board_info { + u32 chip_id; /* Chip vendor/Device ID */ + u32 chip_revesion; /* Chip revesion */ + struct device *next_dev; /* next device */ + + struct pci_dev *net_dev;/* PCI device */ + + unsigned long ioaddr; /* I/O base address */ + u32 cr0_data; + u32 cr5_data; + u32 cr6_data; + u32 cr7_data; + u32 cr15_data; + +/* descriptor pointer */ + unsigned char *buf_pool_ptr; /* Tx buffer pool memory */ + unsigned char *buf_pool_start; /* Tx buffer pool align dword */ + unsigned char *desc_pool_ptr; /* descriptor pool memory */ + struct tx_desc *first_tx_desc; + struct tx_desc *tx_insert_ptr; + struct tx_desc *tx_remove_ptr; + struct rx_desc *first_rx_desc; + struct rx_desc *rx_insert_ptr; + struct rx_desc *rx_ready_ptr; /* packet come pointer */ + u32 tx_packet_cnt; /* transmitted packet count */ + u32 rx_avail_cnt; /* available rx descriptor count */ + u32 interval_rx_cnt; /* rx packet count a callback time */ + + u16 phy_id2; /* Phyxcer ID2 */ + + u8 media_mode; /* user specify media mode */ + u8 op_mode; /* real work media mode */ + u8 phy_addr; + u8 link_failed; /* Ever link failed */ + u8 wait_reset; /* Hardware failed, need to reset */ + u8 in_reset_state; /* Now driver in reset routine */ + u8 rx_error_cnt; /* recievd abnormal case count */ + u8 dm910x_chk_mode; /* Operating mode check */ + struct timer_list timer; + struct enet_statistics stats; /* statistic counter */ + unsigned char srom[128]; +}; + +enum dmfe_offsets { + DCR0 = 0, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20, DCR5 = 0x28, + DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48, DCR10 = 0x50, DCR11 = 0x58, + DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70, DCR15 = 0x78 +}; + +enum dmfe_CR6_bits { + CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, CR6_FDM = 0x200, + CR6_TXSC = 0x2000, CR6_STI = 0x100000, CR6_SFT = 0x200000, CR6_RXA = 0x40000000, + CR6_NO_PURGE = 0x20000000 +}; + +/* Global variable declaration ----------------------------- */ +static int dmfe_debug = 0; +static unsigned char dmfe_media_mode = 8; +static struct device *dmfe_root_dev = NULL; /* First device */ +static u32 dmfe_cr6_user_set = 0; + +/* For module input parameter */ +static int debug = 0; +static u32 cr6set = 0; +static unsigned char mode = 8; +static u8 chkmode = 1; + +unsigned long CrcTable[256] = +{ + 0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL, + 0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L, + 0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L, + 0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L, + 0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL, + 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L, + 0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL, + 0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L, + 0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L, + 0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL, + 0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L, + 0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L, + 0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L, + 0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL, + 0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L, + 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL, + 0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL, + 0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L, + 0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L, + 0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L, + 0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL, + 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L, + 0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL, + 0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L, + 0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L, + 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL, + 0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L, + 0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L, + 0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L, + 0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL, + 0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L, + 0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL, + 0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL, + 0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L, + 0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L, + 0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L, + 0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL, + 0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L, + 0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL, + 0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L, + 0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L, + 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL, + 0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L, + 0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L, + 0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L, + 0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL, + 0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L, + 0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL, + 0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL, + 0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L, + 0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L, + 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L, + 0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL, + 0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L, + 0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL, + 0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L, + 0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L, + 0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL, + 0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L, + 0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L, + 0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L, + 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL, + 0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L, + 0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL +}; + +/* function declaration ------------------------------------- */ +static int dmfe_probe(struct device *); +static int dmfe_open(struct device *); +static int dmfe_start_xmit(struct sk_buff *, struct device *); +static int dmfe_stop(struct device *); +static struct enet_statistics *dmfe_get_stats(struct device *); +static void dmfe_set_filter_mode(struct device *); +static int dmfe_do_ioctl(struct device *, struct ifreq *, int); +static u16 read_srom_word(long, int); +static void dmfe_interrupt(int, void *, struct pt_regs *); +static void dmfe_descriptor_init(struct dmfe_board_info *, u32); +static void allocated_rx_buffer(struct dmfe_board_info *); +static void update_cr6(u32, u32); +static void send_filter_frame(struct device *, int); +static void dm9132_id_table(struct device *, int); +static u16 phy_read(u32, u8, u8, u32); +static void phy_write(u32, u8, u8, u16, u32); +static void phy_write_1bit(u32, u32); +static u16 phy_read_1bit(u32); +static void dmfe_sense_speed(struct dmfe_board_info *); +static void dmfe_process_mode(struct dmfe_board_info *); +static void dmfe_timer(unsigned long); +static void dmfe_rx_packet(struct device *, struct dmfe_board_info *); +static void dmfe_reused_skb(struct dmfe_board_info *, struct sk_buff *); +static void dmfe_dynamic_reset(struct device *); +static void dmfe_free_rxbuffer(struct dmfe_board_info *); +static void dmfe_init_dm910x(struct device *); +static unsigned long cal_CRC(unsigned char *, unsigned int, u8); + +/* DM910X network baord routine ---------------------------- */ + +/* + Search DM910X board ,allocate space and register it + */ +int dmfe_probe(struct device *dev) +{ + u32 pci_iobase; + u16 dm9102_count = 0; + u8 pci_irqline; + static int index = 0; /* For multiple call */ + struct dmfe_board_info *db; /* Point a board information structure */ + int i; + struct pci_dev *net_dev = NULL; + + DMFE_DBUG(0, "dmfe_probe()", 0); + + if (!pci_present()) + return -ENODEV; + + index = 0; + while ((net_dev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, net_dev))) + { + u32 pci_id; + u32 dev_rev; + u8 pci_cmd; + + index++; + if (pci_read_config_dword(net_dev, PCI_VENDOR_ID, &pci_id) != DMFE_SUCC) + continue; + + if ((pci_id != PCI_DM9102_ID) && (pci_id != PCI_DM9132_ID)) + continue; + + /* read PCI IO base address and IRQ to check */ + pci_read_config_dword(net_dev, PCI_BASE_ADDRESS_0, &pci_iobase); + pci_read_config_byte(net_dev, PCI_INTERRUPT_LINE, &pci_irqline); + pci_iobase &= ~0x7f; /* mask off bit0~6 */ + + /* Enable Master/IO access, Disable memory access */ + pci_read_config_byte(net_dev, PCI_COMMAND, &pci_cmd); + pci_cmd |= PCI_COMMAND_IO + PCI_COMMAND_MASTER; + pci_cmd &= ~PCI_COMMAND_MEMORY; + pci_write_config_byte(net_dev, PCI_COMMAND, pci_cmd); + + /* Set Latency Timer 80h */ + pci_write_config_byte(net_dev, PCI_LATENCY_TIMER, 0x80); + + /* Read Chip revesion */ + pci_read_config_dword(net_dev, 8, &dev_rev); + + /* IO range check */ + if (check_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev))) { + printk(KERN_ERR "dmfe: I/O conflict : IO=%x Range=%x\n", pci_iobase, CHK_IO_SIZE(pci_id, dev_rev)); + continue; + } + /* Interrupt check */ + if (pci_irqline == 0) { + printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n", pci_irqline); + continue; + } + /* Found DM9102 card and PCI resource allocated OK */ + dm9102_count++; /* Found a DM9102 card */ + + /* Init network device */ + dev = init_etherdev(dev, 0); + + if(dev==NULL) + { + printk(KERN_ERR "dmfe: out of memory.\n"); + continue; + } + + /* Allocated board information structure */ + db = (void *) (kmalloc(sizeof(*db), GFP_KERNEL | GFP_DMA)); + + if(db==NULL) + { + printk(KERN_ERR "dmfe: out of memory.\n"); + continue; + } + + memset(db, 0, sizeof(*db)); + dev->priv = db; /* link device and board info */ + db->next_dev = dmfe_root_dev; + dmfe_root_dev = dev; + + db->chip_id = pci_id; /* keep Chip vandor/Device ID */ + db->ioaddr = pci_iobase; + db->chip_revesion = dev_rev; + + db->net_dev = net_dev; + + dev->base_addr = pci_iobase; + dev->irq = pci_irqline; + dev->open = &dmfe_open; + dev->hard_start_xmit = &dmfe_start_xmit; + dev->stop = &dmfe_stop; + dev->get_stats = &dmfe_get_stats; + dev->set_multicast_list = &dmfe_set_filter_mode; + dev->do_ioctl = &dmfe_do_ioctl; + + request_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev), dev->name); + + /* read 64 word srom data */ + for (i = 0; i < 64; i++) + ((u16 *) db->srom)[i] = read_srom_word(pci_iobase, i); + + /* Set Node address */ + for (i = 0; i < 6; i++) + dev->dev_addr[i] = db->srom[20 + i]; + + dev = 0; /* NULL device */ + } + + if (!dm9102_count) + printk(KERN_WARNING "Can't find DM910X board or resource error\n"); + + return dm9102_count ? 0 : -ENODEV; +} + +/* + Open the interface. + The interface is opened whenever "ifconfig" actives it. + */ +static int dmfe_open(struct device *dev) +{ + struct dmfe_board_info *db = dev->priv; + + DMFE_DBUG(0, "dmfe_open", 0); + + if (request_irq(dev->irq, &dmfe_interrupt, SA_SHIRQ, dev->name, dev)) + return -EAGAIN; + + /* Allocated Tx/Rx descriptor memory */ + db->desc_pool_ptr = kmalloc(sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, GFP_KERNEL | GFP_DMA); + if (db->desc_pool_ptr == NULL) + return -ENOMEM; + if ((u32) db->desc_pool_ptr & 0x1f) + db->first_tx_desc = (struct tx_desc *) (((u32) db->desc_pool_ptr & ~0x1f) + 0x20); + else + db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr; + + /* Allocated Tx buffer memory */ + db->buf_pool_ptr = kmalloc(TX_BUF_ALLOC * TX_DESC_CNT + 4, GFP_KERNEL | GFP_DMA); + if (db->buf_pool_ptr == NULL) { + kfree(db->desc_pool_ptr); + return -ENOMEM; + } + if ((u32) db->buf_pool_ptr & 0x3) + db->buf_pool_start = (char *) (((u32) db->buf_pool_ptr & ~0x3) + 0x4); + else + db->buf_pool_start = db->buf_pool_ptr; + + /* system variable init */ + db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set; + db->tx_packet_cnt = 0; + db->rx_avail_cnt = 0; + db->link_failed = 0; + db->wait_reset = 0; + db->in_reset_state = 0; + db->rx_error_cnt = 0; + + if (!chkmode || (db->chip_id == PCI_DM9132_ID) || (db->chip_revesion >= 0x02000030)) { + //db->cr6_data &= ~CR6_SFT; /* Used Tx threshold */ + //db->cr6_data |= CR6_NO_PURGE; /* No purge if rx unavailable */ + db->cr0_data = 0xc00000; /* TX/RX desc burst mode */ + db->dm910x_chk_mode = 4; /* Enter the normal mode */ + } else { + db->cr0_data = 0; + db->dm910x_chk_mode = 1; /* Enter the check mode */ + } + + /* Initilize DM910X board */ + dmfe_init_dm910x(dev); + + /* Active System Interface */ + dev->tbusy = 0; /* Can transmit packet */ + dev->start = 1; /* interface ready */ + MOD_INC_USE_COUNT; + + /* set and active a timer process */ + init_timer(&db->timer); + db->timer.expires = DMFE_TIMER_WUT; + db->timer.data = (unsigned long) dev; + db->timer.function = &dmfe_timer; + add_timer(&db->timer); + + return 0; +} + +/* Initilize DM910X board + Reset DM910X board + Initilize TX/Rx descriptor chain structure + Send the set-up frame + Enable Tx/Rx machine + */ +static void dmfe_init_dm910x(struct device *dev) +{ + struct dmfe_board_info *db = dev->priv; + unsigned long ioaddr = db->ioaddr; + + DMFE_DBUG(0, "dmfe_init_dm910x()", 0); + + /* Reset DM910x board : need 32 PCI clock to complete */ + outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */ + DELAY_5US; + outl(db->cr0_data, ioaddr + DCR0); + + outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */ + outl(0x80, ioaddr + DCR12); /* RESET DM9102 phyxcer */ + outl(0x0, ioaddr + DCR12); /* Clear RESET signal */ + + /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */ + db->phy_addr = 1; + +/* Media Mode Check */ + db->media_mode = dmfe_media_mode; + if (db->media_mode & DMFE_AUTO) + dmfe_sense_speed(db); + else + db->op_mode = db->media_mode; + dmfe_process_mode(db); + + /* Initiliaze Transmit/Receive decriptor and CR3/4 */ + dmfe_descriptor_init(db, ioaddr); + + /* Init CR6 to program DM910x operation */ + update_cr6(db->cr6_data, ioaddr); + + /* Send setup frame */ + if (db->chip_id == PCI_DM9132_ID) + dm9132_id_table(dev, dev->mc_count); /* DM9132 */ + else + send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */ + + /* Init CR5/CR7, interrupt active bit */ + outl(0xffffffff, ioaddr + DCR5); /* clear all CR5 status */ + db->cr7_data = CR7_DEFAULT; + outl(db->cr7_data, ioaddr + DCR7); + +/* Init CR15, Tx jabber and Rx watchdog timer */ + db->cr15_data = CR15_DEFAULT; + outl(db->cr15_data, ioaddr + DCR15); + + /* Enable DM910X Tx/Rx function */ + db->cr6_data |= CR6_RXSC | CR6_TXSC; + update_cr6(db->cr6_data, ioaddr); + +} + + +/* + Hardware start transmission. + Send a packet to media from the upper layer. + */ +static int dmfe_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct dmfe_board_info *db = dev->priv; + struct tx_desc *txptr; + + DMFE_DBUG(0, "dmfe_start_xmit", 0); + + if ((dev->tbusy == 1) && (db->tx_packet_cnt != 0)) + return 1; + else + dev->tbusy = 0; + + /* Too large packet check */ + if (skb->len > MAX_PACKET_SIZE) { + dev_kfree_skb(skb); + return 0; + } + /* No Tx resource check, it never happen nromally */ + if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) { + printk(KERN_ERR "dmfe: No Tx resource, enter xmit() again \n"); + dev_kfree_skb(skb); + dev->tbusy = 1; + return -EBUSY; + } + /* transmit this packet */ + txptr = db->tx_insert_ptr; + memcpy((char *) txptr->tx_buf_ptr, (char *) skb->data, skb->len); + txptr->tdes1 = 0xe1000000 | skb->len; + txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */ + + /* Point to next transmit free descriptor */ + db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc; + + /* transmit counter increase 1 */ + db->tx_packet_cnt++; + db->stats.tx_packets++; + + /* issue Tx polling command */ + outl(0x1, dev->base_addr + DCR1); + + /* Tx resource check */ + if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) + dev->tbusy = 1; + + /* Set transmit time stamp */ + dev->trans_start = jiffies; /* saved the time stamp */ + + /* free this SKB */ + dev_kfree_skb(skb); + return 0; +} + +/* + Stop the interface. + The interface is stopped when it is brought. + */ + +static int dmfe_stop(struct device *dev) +{ + struct dmfe_board_info *db = dev->priv; + unsigned long ioaddr = dev->base_addr; + + DMFE_DBUG(0, "dmfe_stop", 0); + + /* disable system */ + dev->start = 0; /* interface disable */ + dev->tbusy = 1; /* can't transmit */ + + /* Reset & stop DM910X board */ + outl(DM910X_RESET, ioaddr + DCR0); + DELAY_5US; + + /* deleted timer */ + del_timer(&db->timer); + + /* free interrupt */ + free_irq(dev->irq, dev); + + /* free allocated rx buffer */ + dmfe_free_rxbuffer(db); + + /* free all descriptor memory and buffer memory */ + kfree(db->desc_pool_ptr); + kfree(db->buf_pool_ptr); + + MOD_DEC_USE_COUNT; + + return 0; +} + +/* + DM9102 insterrupt handler + receive the packet to upper layer, free the transmitted packet + */ + +static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = dev_id; + struct tx_desc *txptr; + struct dmfe_board_info *db; + unsigned long ioaddr; + + if (!dev) { + DMFE_DBUG(1, "dmfe_interrupt() without device arg", 0); + return; + } + if (dev->interrupt) { + DMFE_DBUG(1, "dmfe_interrupt() re-entry ", 0); + return; + } + /* A real interrupt coming */ + dev->interrupt = 1; /* Lock interrupt */ + db = (struct dmfe_board_info *) dev->priv; + ioaddr = dev->base_addr; + + DMFE_DBUG(0, "dmfe_interrupt()", 0); + +/* Disable all interrupt in CR7 to solve the interrupt edge problem */ + outl(0, ioaddr + DCR7); + +/* Got DM910X status */ + db->cr5_data = inl(ioaddr + DCR5); + outl(db->cr5_data, ioaddr + DCR5); + /* printk("CR5=%x\n", db->cr5_data); */ + + /* Check system status */ + if (db->cr5_data & 0x2000) { + /* system bus error happen */ + DMFE_DBUG(1, "System bus error happen. CR5=", db->cr5_data); + dev->tbusy = 1; + db->wait_reset = 1; /* Need to RESET */ + outl(0, ioaddr + DCR7); /* disable all interrupt */ + dev->interrupt = 0; /* unlock interrupt */ + return; + } + /* Free the transmitted descriptor */ + txptr = db->tx_remove_ptr; + while (db->tx_packet_cnt) { + /* printk("tdes0=%x\n", txptr->tdes0); */ + if (txptr->tdes0 & 0x80000000) + break; + + /* Transmit statistic counter */ + if (txptr->tdes0 != 0x7fffffff) { + /* printk("tdes0=%x\n", txptr->tdes0); */ + db->stats.collisions += (txptr->tdes0 >> 3) & 0xf; + db->stats.tx_bytes += txptr->tdes1 & 0x7ff; + if (txptr->tdes0 & TDES0_ERR_MASK) + db->stats.tx_errors++; + } + txptr = (struct tx_desc *) txptr->next_tx_desc; + db->tx_packet_cnt--; + } + db->tx_remove_ptr = (struct tx_desc *) txptr; + + if (dev->tbusy && (db->tx_packet_cnt < TX_FREE_DESC_CNT)) { + dev->tbusy = 0; /* free a resource */ + mark_bh(NET_BH); /* active bottom half */ + } + /* Received the coming packet */ + if (db->rx_avail_cnt) + dmfe_rx_packet(dev, db); + + /* reallocated rx descriptor buffer */ + if (db->rx_avail_cnt < RX_DESC_CNT) + allocated_rx_buffer(db); + + /* Mode Check */ + if (db->dm910x_chk_mode & 0x2) { + db->dm910x_chk_mode = 0x4; + db->cr6_data |= 0x100; + update_cr6(db->cr6_data, db->ioaddr); + } + dev->interrupt = 0; /* release interrupt lock */ + + /* Restore CR7 to enable interrupt mask */ + if (db->interval_rx_cnt > RX_MAX_TRAFFIC) + db->cr7_data = 0x1a28d; + else + db->cr7_data = 0x1a2cd; + outl(db->cr7_data, ioaddr + DCR7); +} + +/* + Receive the come packet and pass to upper layer + */ + +static void dmfe_rx_packet(struct device *dev, struct dmfe_board_info *db) +{ + struct rx_desc *rxptr; + struct sk_buff *skb; + int rxlen; + + rxptr = db->rx_ready_ptr; + + while (db->rx_avail_cnt) { + if (rxptr->rdes0 & 0x80000000) /* packet owner check */ + break; + + db->rx_avail_cnt--; + db->interval_rx_cnt++; + + if ((rxptr->rdes0 & 0x300) != 0x300) { + /* A packet without First/Last flag */ + /* reused this SKB */ + DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0); + dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr); + /* db->rx_error_cnt++; */ + } else { + /* A packet with First/Last flag */ + rxlen = ((rxptr->rdes0 >> 16) & 0x3fff) - 4; /* skip CRC */ + + if (rxptr->rdes0 & 0x8000) { /* error summary bit check */ + /* This is a error packet */ + /* printk("rdes0 error : %x \n", rxptr->rdes0); */ + db->stats.rx_errors++; + if (rxptr->rdes0 & 1) + db->stats.rx_fifo_errors++; + if (rxptr->rdes0 & 2) + db->stats.rx_crc_errors++; + if (rxptr->rdes0 & 0x80) + db->stats.rx_length_errors++; + } + if (!(rxptr->rdes0 & 0x8000) || + ((db->cr6_data & CR6_PM) && (rxlen > 6))) { + skb = (struct sk_buff *) rxptr->rx_skb_ptr; + + /* Received Packet CRC check need or not */ + if ((db->dm910x_chk_mode & 1) && (cal_CRC(skb->tail, rxlen, 1) != (*(unsigned long *) (skb->tail + rxlen)))) { + /* Found a error received packet */ + dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr); + db->dm910x_chk_mode = 3; + } else { + /* A good packet coming, send to upper layer */ + skb->dev = dev; + skb_put(skb, rxlen); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); /* Send to upper layer */ + dev->last_rx = jiffies; + db->stats.rx_packets++; + db->stats.rx_bytes += rxlen; + } + } else { + /* Reuse SKB buffer when the packet is error */ + DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0); + dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr); + } + } + + rxptr = (struct rx_desc *) rxptr->next_rx_desc; + } + + db->rx_ready_ptr = rxptr; + +} + +/* + Get statistics from driver. + */ +static struct enet_statistics *dmfe_get_stats(struct device *dev) +{ + struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv; + DMFE_DBUG(0, "dmfe_get_stats", 0); + return &db->stats; +} + +/* + Set DM910X multicast address + */ + +static void dmfe_set_filter_mode(struct device *dev) +{ + struct dmfe_board_info *db = dev->priv; + + DMFE_DBUG(0, "dmfe_set_filter_mode()", 0); + + if (dev->flags & IFF_PROMISC) { + DMFE_DBUG(0, "Enable PROM Mode", 0); + db->cr6_data |= CR6_PM | CR6_PBF; + update_cr6(db->cr6_data, db->ioaddr); + return; + } + if (dev->flags & IFF_ALLMULTI || dev->mc_count > DMFE_MAX_MULTICAST) { + DMFE_DBUG(0, "Pass all multicast address", dev->mc_count); + db->cr6_data &= ~(CR6_PM | CR6_PBF); + db->cr6_data |= CR6_PAM; + return; + } + DMFE_DBUG(0, "Set multicast address", dev->mc_count); + if (db->chip_id == PCI_DM9132_ID) + dm9132_id_table(dev, dev->mc_count); /* DM9132 */ + else + send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */ +} + +/* + Process the upper socket ioctl command + */ + +static int dmfe_do_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + DMFE_DBUG(0, "dmfe_do_ioctl()", 0); + return -EINVAL; +} + +/* + A periodic timer routine + Dynamic media sense, allocated Rx buffer... + */ + +static void dmfe_timer(unsigned long data) +{ + u32 tmp_cr8; + unsigned char tmp_cr12; + struct device *dev = (struct device *) data; + struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv; + + DMFE_DBUG(0, "dmfe_timer()", 0); + + /* Do reset now */ + if (db->in_reset_state) + return; + + /* Operating Mode Check */ + if ((db->dm910x_chk_mode & 0x1) && (db->stats.rx_packets > MAX_CHECK_PACKET)) { + db->dm910x_chk_mode = 0x4; + } + /* Dynamic reset DM910X : system error or transmit time-out */ + tmp_cr8 = inl(db->ioaddr + DCR8); + if ((db->interval_rx_cnt == 0) && (tmp_cr8)) { + db->wait_reset = 1; + /* printk("CR8 %x, Interval Rx %x\n", tmp_cr8, db->interval_rx_cnt); */ + } + /* Receiving Traffic check */ + if (db->interval_rx_cnt > RX_MAX_TRAFFIC) + db->cr7_data = 0x1a28d; + else + db->cr7_data = 0x1a2cd; + outl(db->cr7_data, db->ioaddr + DCR7); + + db->interval_rx_cnt = 0; + + if (db->wait_reset | (db->tx_packet_cnt && + ((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) | (db->rx_error_cnt > 3)) { + /* + printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start); + */ + DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx moniotr step1", db->tx_packet_cnt); + dmfe_dynamic_reset(dev); + db->timer.expires = DMFE_TIMER_WUT; + add_timer(&db->timer); + return; + } + db->rx_error_cnt = 0; /* Clear previos counter */ + + /* Link status check, Dynamic media type change */ + if (db->chip_id == PCI_DM9132_ID) + tmp_cr12 = inb(db->ioaddr + DCR9 + 3); /* DM9132 */ + else + tmp_cr12 = inb(db->ioaddr + DCR12); /* DM9102/DM9102A */ + + if (((db->chip_id == PCI_DM9102_ID) && (db->chip_revesion == 0x02000030)) || + ((db->chip_id == PCI_DM9132_ID) && (db->chip_revesion == 0x02000010))) { + /* DM9102A Chip */ + if (tmp_cr12 & 2) + tmp_cr12 = 0x0; /* Link failed */ + else + tmp_cr12 = 0x3; /* Link OK */ + } + if (!(tmp_cr12 & 0x3) && !db->link_failed) { + /* Link Failed */ + DMFE_DBUG(0, "Link Failed", tmp_cr12); + db->link_failed = 1; + phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); /* reset Phy */ + + /* 10/100M link failed, used 1M Home-Net */ + db->cr6_data |= 0x00040000; /* CR6 bit18 = 1, select Home-Net */ + db->cr6_data &= ~0x00000200; /* CR6 bit9 =0, half duplex mode */ + update_cr6(db->cr6_data, db->ioaddr); + + /* For DM9801 : PHY ID1 0181h, PHY ID2 B900h */ + db->phy_id2 = phy_read(db->ioaddr, db->phy_addr, 3, db->chip_id); + if (db->phy_id2 == 0xb900) + phy_write(db->ioaddr, db->phy_addr, 25, 0x7e08, db->chip_id); + } else if ((tmp_cr12 & 0x3) && db->link_failed) { + DMFE_DBUG(0, "Link link OK", tmp_cr12); + db->link_failed = 0; + + /* CR6 bit18=0, select 10/100M */ + db->cr6_data &= ~0x00040000; + update_cr6(db->cr6_data, db->ioaddr); + + /* Auto Sense Speed */ + if (db->media_mode & DMFE_AUTO) + dmfe_sense_speed(db); + dmfe_process_mode(db); + update_cr6(db->cr6_data, db->ioaddr); + /* SHOW_MEDIA_TYPE(db->op_mode); */ + } + /* reallocated rx descriptor buffer */ + if (db->rx_avail_cnt < RX_DESC_CNT) + allocated_rx_buffer(db); + + /* Timer active again */ + db->timer.expires = DMFE_TIMER_WUT; + add_timer(&db->timer); +} + +/* + Dynamic reset the DM910X board + Stop DM910X board + Free Tx/Rx allocated memory + Reset DM910X board + Re-initialize DM910X board + */ + +static void dmfe_dynamic_reset(struct device *dev) +{ + struct dmfe_board_info *db = dev->priv; + + DMFE_DBUG(0, "dmfe_dynamic_reset()", 0); + + /* Enter dynamic reset route */ + db->in_reset_state = 1; + + /* Disable upper layer interface */ + dev->tbusy = 1; /* transmit packet disable */ + dev->start = 0; /* interface not ready */ + + db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); /* Disable Tx/Rx */ + update_cr6(db->cr6_data, dev->base_addr); + + /* Free Rx Allocate buffer */ + dmfe_free_rxbuffer(db); + + /* system variable init */ + db->tx_packet_cnt = 0; + db->rx_avail_cnt = 0; + db->link_failed = 0; + db->wait_reset = 0; + db->rx_error_cnt = 0; + + /* Re-initilize DM910X board */ + dmfe_init_dm910x(dev); + + /* Restart upper layer interface */ + dev->tbusy = 0; /* Can transmit packet */ + dev->start = 1; /* interface ready */ + + /* Leave dynamic reser route */ + db->in_reset_state = 0; +} + +/* + free all allocated rx buffer + */ +static void dmfe_free_rxbuffer(struct dmfe_board_info *db) +{ + DMFE_DBUG(0, "dmfe_free_rxbuffer()", 0); + + /* free allocated rx buffer */ + while (db->rx_avail_cnt) { + dev_kfree_skb((void *) (db->rx_ready_ptr->rx_skb_ptr)); + db->rx_ready_ptr = (struct rx_desc *) db->rx_ready_ptr->next_rx_desc; + db->rx_avail_cnt--; + } +} + +/* + Reused the SK buffer + */ + +static void dmfe_reused_skb(struct dmfe_board_info *db, struct sk_buff *skb) +{ + struct rx_desc *rxptr = db->rx_insert_ptr; + + if (!(rxptr->rdes0 & 0x80000000)) { + rxptr->rx_skb_ptr = (u32) skb; + rxptr->rdes2 = virt_to_bus(skb->tail); + rxptr->rdes0 = 0x80000000; + db->rx_avail_cnt++; + db->rx_insert_ptr = (struct rx_desc *) rxptr->next_rx_desc; + } else + DMFE_DBUG(0, "SK Buffer reused method error", db->rx_avail_cnt); +} + +/* + Initialize transmit/Receive descriptor + Using Chain structure, and allocated Tx/Rx buffer + */ +static void dmfe_descriptor_init(struct dmfe_board_info *db, unsigned long ioaddr) +{ + struct tx_desc *tmp_tx; + struct rx_desc *tmp_rx; + unsigned char *tmp_buf; + int i; + + DMFE_DBUG(0, "dmfe_descriptor_init()", 0); + + /* tx descriptor start pointer */ + db->tx_insert_ptr = db->first_tx_desc; + db->tx_remove_ptr = db->first_tx_desc; + outl(virt_to_bus(db->first_tx_desc), ioaddr + DCR4); /* Init CR4 */ + + /* rx descriptor start pointer */ + db->first_rx_desc = (struct rx_desc *) + ((u32) db->first_tx_desc + sizeof(struct rx_desc) * TX_DESC_CNT); + db->rx_insert_ptr = db->first_rx_desc; + db->rx_ready_ptr = db->first_rx_desc; + outl(virt_to_bus(db->first_rx_desc), ioaddr + DCR3); /* Init CR3 */ + + /* Init Transmit chain */ + tmp_buf = db->buf_pool_start; + for (tmp_tx = db->first_tx_desc, i = 0; i < TX_DESC_CNT; i++, tmp_tx++) { + tmp_tx->tx_buf_ptr = (u32) tmp_buf; + tmp_tx->tdes0 = 0; + tmp_tx->tdes1 = 0x81000000; /* IC, chain */ + tmp_tx->tdes2 = (u32) virt_to_bus(tmp_buf); + tmp_tx->tdes3 = (u32) virt_to_bus(tmp_tx) + sizeof(struct tx_desc); + tmp_tx->next_tx_desc = (u32) ((u32) tmp_tx + sizeof(struct tx_desc)); + tmp_buf = (unsigned char *) ((u32) tmp_buf + TX_BUF_ALLOC); + } + (--tmp_tx)->tdes3 = (u32) virt_to_bus(db->first_tx_desc); + tmp_tx->next_tx_desc = (u32) db->first_tx_desc; + + /* Init Receive descriptor chain */ + for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT; i++, tmp_rx++) { + tmp_rx->rdes0 = 0; + tmp_rx->rdes1 = 0x01000600; + tmp_rx->rdes3 = (u32) virt_to_bus(tmp_rx) + sizeof(struct rx_desc); + tmp_rx->next_rx_desc = (u32) ((u32) tmp_rx + sizeof(struct rx_desc)); + } + (--tmp_rx)->rdes3 = (u32) virt_to_bus(db->first_rx_desc); + tmp_rx->next_rx_desc = (u32) db->first_rx_desc; + + /* pre-allocated Rx buffer */ + allocated_rx_buffer(db); +} + +/* + Update CR6 vaule + Firstly stop DM910X , then written value and start + */ + +static void update_cr6(u32 cr6_data, unsigned long ioaddr) +{ + u32 cr6_tmp; + + cr6_tmp = cr6_data & ~0x2002; /* stop Tx/Rx */ + outl(cr6_tmp, ioaddr + DCR6); + DELAY_5US; + outl(cr6_data, ioaddr + DCR6); + cr6_tmp = inl(ioaddr + DCR6); + /* printk("CR6 update %x ", cr6_tmp); */ +} + +/* Send a setup frame for DM9132 + This setup frame initilize DM910X addres filter mode + */ + +static void dm9132_id_table(struct device *dev, int mc_cnt) +{ + struct dev_mc_list *mcptr; + u16 *addrptr; + unsigned long ioaddr = dev->base_addr + 0xc0; /* ID Table */ + u32 hash_val; + u16 i, hash_table[4]; + + DMFE_DBUG(0, "dm9132_id_table()", 0); + + /* Node address */ + addrptr = (u16 *) dev->dev_addr; + outw(addrptr[0], ioaddr); + ioaddr += 4; + outw(addrptr[1], ioaddr); + ioaddr += 4; + outw(addrptr[2], ioaddr); + ioaddr += 4; + + /* Clear Hash Table */ + for (i = 0; i < 4; i++) + hash_table[i] = 0x0; + + /* broadcast address */ + hash_table[3] = 0x8000; + + /* the multicast address in Hash Table : 64 bits */ + for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) { + hash_val = cal_CRC((char *) mcptr->dmi_addr, 6, 0) & 0x3f; + hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16); + } + + /* Write the hash table to MAC MD table */ + for (i = 0; i < 4; i++, ioaddr += 4) { + outw(hash_table[i], ioaddr); + } +} + +/* Send a setup frame for DM9102/DM9102A + This setup frame initilize DM910X addres filter mode + */ + +static void send_filter_frame(struct device *dev, int mc_cnt) +{ + struct dmfe_board_info *db = dev->priv; + struct dev_mc_list *mcptr; + struct tx_desc *txptr; + u16 *addrptr; + u32 *suptr; + int i; + + DMFE_DBUG(0, "send_filetr_frame()", 0); + + txptr = db->tx_insert_ptr; + suptr = (u32 *) txptr->tx_buf_ptr; + + /* Node address */ + addrptr = (u16 *) dev->dev_addr; + *suptr++ = addrptr[0]; + *suptr++ = addrptr[1]; + *suptr++ = addrptr[2]; + + /* broadcast address */ + *suptr++ = 0xffff; + *suptr++ = 0xffff; + *suptr++ = 0xffff; + + /* fit the multicast address */ + for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) { + addrptr = (u16 *) mcptr->dmi_addr; + *suptr++ = addrptr[0]; + *suptr++ = addrptr[1]; + *suptr++ = addrptr[2]; + } + + for (; i < 14; i++) { + *suptr++ = 0xffff; + *suptr++ = 0xffff; + *suptr++ = 0xffff; + } + + /* prepare the setup frame */ + db->tx_packet_cnt++; + dev->tbusy = 1; + txptr->tdes1 = 0x890000c0; + txptr->tdes0 = 0x80000000; + db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc; + + update_cr6(db->cr6_data | 0x2000, dev->base_addr); + outl(0x1, dev->base_addr + DCR1); + update_cr6(db->cr6_data, dev->base_addr); + dev->trans_start = jiffies; + +} + +/* + Allocate rx buffer, + As possible as allocated maxiumn Rx buffer + */ + +static void allocated_rx_buffer(struct dmfe_board_info *db) +{ + struct rx_desc *rxptr; + struct sk_buff *skb; + + rxptr = db->rx_insert_ptr; + + while (db->rx_avail_cnt < RX_DESC_CNT) { + if ((skb = alloc_skb(RX_ALLOC_SIZE, GFP_ATOMIC)) == NULL) + break; + rxptr->rx_skb_ptr = (u32) skb; + rxptr->rdes2 = virt_to_bus(skb->tail); + rxptr->rdes0 = 0x80000000; + rxptr = (struct rx_desc *) rxptr->next_rx_desc; + db->rx_avail_cnt++; + } + + db->rx_insert_ptr = rxptr; +} + +/* + Read one word data from the serial ROM + */ + +static u16 read_srom_word(unsigned long ioaddr, int offset) +{ + int i; + u16 srom_data = 0; + unsigned long cr9_ioaddr = ioaddr + DCR9; + + outl(CR9_SROM_READ, cr9_ioaddr); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + /* Send the Read Command 110b */ + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr); + + /* Send the offset */ + for (i = 5; i >= 0; i--) { + srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0; + SROM_CLK_WRITE(srom_data, cr9_ioaddr); + } + + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + for (i = 16; i > 0; i--) { + outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr); + DELAY_5US; + srom_data = (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1 : 0); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + DELAY_5US; + } + + outl(CR9_SROM_READ, cr9_ioaddr); + return srom_data; +} + +/* + Auto sense the media mode + */ +static void dmfe_sense_speed(struct dmfe_board_info *db) +{ + int i; + u16 phy_mode; + + for (i = 1000; i; i--) { + DELAY_5US; + phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id); + if ((phy_mode & 0x24) == 0x24) + break; + } + + if (i) { + if (db->chip_id == PCI_DM9132_ID) /* DM9132 */ + phy_mode = phy_read(db->ioaddr, db->phy_addr, 7, db->chip_id) & 0xf000; + else /* DM9102/DM9102A */ + phy_mode = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0xf000; + /* printk("Phy_mode %x ",phy_mode); */ + switch (phy_mode) { + case 0x1000: + db->op_mode = DMFE_10MHF; + break; + case 0x2000: + db->op_mode = DMFE_10MFD; + break; + case 0x4000: + db->op_mode = DMFE_100MHF; + break; + case 0x8000: + db->op_mode = DMFE_100MFD; + break; + default: + db->op_mode = DMFE_10MHF; + DMFE_DBUG(0, "Media Type error, phy reg17", phy_mode); + break; + } + } else { + db->op_mode = DMFE_10MHF; + DMFE_DBUG(0, "Link Failed :", phy_mode); + } +} + +/* + Process op-mode + AUTO mode : PHY controller in Auto-negotiation Mode + Force mode: PHY controller in force mode with HUB + N-way force capability with SWITCH + */ +static void dmfe_process_mode(struct dmfe_board_info *db) +{ + u16 phy_reg; + + /* Full Duplex Mode Check */ + db->cr6_data &= ~CR6_FDM; /* Clear Full Duplex Bit */ + if (db->op_mode & 0x4) + db->cr6_data |= CR6_FDM; + + if (!(db->media_mode & DMFE_AUTO)) { /* Force Mode Check */ + /* User force the media type */ + phy_reg = phy_read(db->ioaddr, db->phy_addr, 5, db->chip_id); + /* printk("Nway phy_reg5 %x ",phy_reg); */ + if (phy_reg & 0x1) { + /* parter own the N-Way capability */ + phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x1e0; + switch (db->op_mode) { + case DMFE_10MHF: + phy_reg |= 0x20; + break; + case DMFE_10MFD: + phy_reg |= 0x40; + break; + case DMFE_100MHF: + phy_reg |= 0x80; + break; + case DMFE_100MFD: + phy_reg |= 0x100; + break; + } + phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id); + } else { + /* parter without the N-Way capability */ + switch (db->op_mode) { + case DMFE_10MHF: + phy_reg = 0x0; + break; + case DMFE_10MFD: + phy_reg = 0x100; + break; + case DMFE_100MHF: + phy_reg = 0x2000; + break; + case DMFE_100MFD: + phy_reg = 0x2100; + break; + } + phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id); + } + } +} + +/* + Write a word to Phy register + */ +static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, u16 phy_data, u32 chip_id) +{ + u16 i; + unsigned long ioaddr; + + if (chip_id == PCI_DM9132_ID) { + ioaddr = iobase + 0x80 + offset * 4; + outw(phy_data, ioaddr); + } else { + /* DM9102/DM9102A Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send write command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send Phy addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); + + /* Send register addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); + + /* written trasnition */ + phy_write_1bit(ioaddr, PHY_DATA_1); + phy_write_1bit(ioaddr, PHY_DATA_0); + + /* Write a word data to PHY controller */ + for (i = 0x8000; i > 0; i >>= 1) + phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0); + } +} + +/* + Read a word data from phy register + */ +static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset, u32 chip_id) +{ + int i; + u16 phy_data; + unsigned long ioaddr; + + if (chip_id == PCI_DM9132_ID) { + /* DM9132 Chip */ + ioaddr = iobase + 0x80 + offset * 4; + phy_data = inw(ioaddr); + } else { + /* DM9102/DM9102A Chip */ + + ioaddr = iobase + DCR9; + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send read command(10) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_1); + phy_write_1bit(ioaddr, PHY_DATA_0); + + /* Send Phy addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); + + /* Send register addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); + + /* Skip transition state */ + phy_read_1bit(ioaddr); + + /* read 16bit data */ + for (phy_data = 0, i = 0; i < 16; i++) { + phy_data <<= 1; + phy_data |= phy_read_1bit(ioaddr); + } + } + + return phy_data; +} + +/* + Write one bit data to Phy Controller + */ +static void phy_write_1bit(unsigned long ioaddr, u32 phy_data) +{ + outl(phy_data, ioaddr); /* MII Clock Low */ + DELAY_1US; + outl(phy_data | MDCLKH, ioaddr); /* MII Clock High */ + DELAY_1US; + outl(phy_data, ioaddr); /* MII Clock Low */ + DELAY_1US; +} + +/* + Read one bit phy data from PHY controller + */ +static u16 phy_read_1bit(unsigned long ioaddr) +{ + u16 phy_data; + + outl(0x50000, ioaddr); + DELAY_1US; + phy_data = (inl(ioaddr) >> 19) & 0x1; + outl(0x40000, ioaddr); + DELAY_1US; + + return phy_data; +} + +/* + Calculate the CRC valude of the Rx packet + flag = 1 : return the reverse CRC (for the received packet CRC) + 0 : return the normal CRC (for Hash Table index) + */ +unsigned long cal_CRC(unsigned char *Data, unsigned int Len, u8 flag) +{ + unsigned long Crc = 0xffffffff; + + while (Len--) { + Crc = CrcTable[(Crc ^ *Data++) & 0xFF] ^ (Crc >> 8); + } + + if (flag) + return ~Crc; + else + return Crc; +} + +#ifdef MODULE + +MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw"); +MODULE_DESCRIPTION("Davicom DM910X fast ethernet driver"); +MODULE_PARM(debug, "i"); +MODULE_PARM(mode, "i"); +MODULE_PARM(cr6set, "i"); +MODULE_PARM(chkmode, "i"); + +/* Description: + when user used insmod to add module, system invoked init_module() + to initilize and register. + */ +int init_module(void) +{ + DMFE_DBUG(0, "init_module() ", debug); + + if (debug) + dmfe_debug = debug; /* set debug flag */ + if (cr6set) + dmfe_cr6_user_set = cr6set; + + switch (mode) { + case 0: + case 1: + case 4: + case 5: + dmfe_media_mode = mode; + break; + default: + dmfe_media_mode = 8; + break; + } + + return dmfe_probe(0); /* search board and register */ +} + +/* Description: + when user used rmmod to delete module, system invoked clean_module() + to un-register device. + */ +void cleanup_module(void) +{ + struct device *next_dev; + struct dmfe_board_info *db; + + DMFE_DBUG(0, "clean_module()", 0); + + while (dmfe_root_dev) { + next_dev = ((struct dmfe_board_info *) dmfe_root_dev->priv)->next_dev; + unregister_netdev(dmfe_root_dev); + db = dmfe_root_dev->priv; + release_region(dmfe_root_dev->base_addr, CHK_IO_SIZE(db->chip_id, db->chip_revesion)); + kfree(db); /* free board information */ + kfree(dmfe_root_dev); /* free device structure */ + dmfe_root_dev = next_dev; + } + + DMFE_DBUG(0, "clean_module() exit", 0); +} + +#endif /* MODULE */ diff --git a/drivers/char/random.c b/drivers/char/random.c index c6f0cd02bc97..944a690aaa32 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1328,6 +1328,7 @@ random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) schedule(); continue; } + current->state = TASK_RUNNING; n = extract_entropy(&random_state, buf, n, 1); if (n < 0) { retval = n; diff --git a/drivers/net/82596.c b/drivers/net/82596.c index cd3ab6bd6a36..8177ca3593e6 100644 --- a/drivers/net/82596.c +++ b/drivers/net/82596.c @@ -283,7 +283,7 @@ static inline void CA(struct device *dev) volatile u32 i = *(volatile u32 *) (dev->base_addr); } #endif -#ifdef CONFIG_APRICOT_i596 +#ifdef CONFIG_APRICOT if (MACH_IS_APRICOT) { outw(0, (short) (dev->base_addr) + 4); } @@ -444,7 +444,7 @@ static inline void init_i596_mem(struct device *dev) if (MACH_IS_BVME6000) lp->scp.sysbus = 0x0000004c; #endif -#ifdef CONFIG_APRICOT_i596 +#ifdef CONFIG_APRICOT if (MACH_IS_APRICOT) lp->scp.sysbus = 0x00440000; #endif @@ -914,7 +914,7 @@ __initfunc(int i82596_probe(struct device *dev)) dev->irq = (unsigned) BVME_IRQ_I596; } #endif -#ifdef CONFIG_APRICOT_INTEL +#ifdef CONFIG_APRICOT int checksum = 0; int ioaddr = 0x300; @@ -1163,7 +1163,7 @@ static void i596_interrupt(int irq, void *dev_id, struct pt_regs *regs) *ethirq = 3; } #endif -#ifdef CONFIG_APRICOT_INTEL +#ifdef CONFIG_APRICOT (void) inb(ioaddr + 0x10); outb(4, ioaddr + 0xf); #endif diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index d932a40bc4d4..496e75757937 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -1,14 +1,14 @@ -/* drivers/net/eepro100.c: An Intel i82557 Ethernet driver for Linux. */ +/* drivers/net/eepro100.c: An Intel i82557-559 Ethernet driver for Linux. */ /* NOTICE: this version tested with kernels 1.3.72 and later only! - Written 1996-1998 by Donald Becker. + Written 1996-1999 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. - This driver is for the Intel EtherExpress Pro 100B boards. - It should work with other i82557 and i82558 boards. - To use a built-in driver, install as drivers/net/eepro100.c. + This driver is for the Intel EtherExpress Pro100 (Speedo3) design. + It should work with all i82557/558/559 boards. + To use as a module, use the compile-command at the end of the file. The author may be reached as becker@CESDIS.usra.edu, or C/O @@ -16,15 +16,18 @@ Code 930.5, NASA Goddard Space Flight Center, Greenbelt MD 20771 For updates see http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html - There is also a mailing list based at + For installation instructions + http://cesdis.gsfc.nasa.gov/linux/misc/modules.html + There is a Majordomo mailing list based at linux-eepro100@cesdis.gsfc.nasa.gov */ static const char *version = -"eepro100.c:v1.06 10/16/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n"; +"eepro100.c:v1.09j-t 9/29/99 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n" +"eepro100.c: $Revision: 1.18 $ 1999/12/29 Modified by Andrey V. Savochkin \n"; /* A few user-configurable values that apply to all boards. - First set are undocumented and spelled per Intel recommendations. */ + First set is undocumented and spelled per Intel recommendations. */ static int congenb = 0; /* Enable congestion control in the DP83840. */ static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */ @@ -38,14 +41,52 @@ static int rxdmacount = 0; static int rx_copybreak = 200; /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 200; +static int max_interrupt_work = 20; /* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */ static int multicast_filter_limit = 64; -#include +/* 'options' is used to pass a transceiver override or full-duplex flag + e.g. "options=16" for FD, "options=32" for 100mbps-only. */ +static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1}; +#ifdef MODULE +static int debug = -1; /* The debug level */ +#endif + +/* A few values that may be tweaked. */ +/* The ring sizes should be a power of two for efficiency. */ +#define TX_RING_SIZE 32 +#define RX_RING_SIZE 32 +/* How much slots multicast filter setup may take. + Do not descrease without changing set_rx_mode() implementaion. */ +#define TX_MULTICAST_SIZE 2 +#define TX_MULTICAST_RESERV (TX_MULTICAST_SIZE*2) +/* Actual number of TX packets queued, must be + <= TX_RING_SIZE-TX_MULTICAST_RESERV. */ +#define TX_QUEUE_LIMIT (TX_RING_SIZE-TX_MULTICAST_RESERV) +/* Hysteresis marking queue as no longer full. */ +#define TX_QUEUE_UNFULL (TX_QUEUE_LIMIT-4) + +/* Operational parameters that usually are not changed. */ + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (2*HZ) +/* Size of an pre-allocated Rx buffer: + slack.*/ +#define PKT_BUF_SZ 1536 + +#if !defined(__OPTIMIZE__) || !defined(__KERNEL__) +#warning You must compile this file with the correct options! +#warning See the last lines of the source file. +#error You must compile this driver with "-O". +#endif #include +#include +#if defined(MODVERSIONS) +#include +#endif + #include #include #include @@ -53,19 +94,27 @@ static int multicast_filter_limit = 64; #include #include #include +#ifdef HAS_PCI_NETIF +#include "pci-netif.h" +#else +#include +#endif #include +#if LINUX_VERSION_CODE >= 0x20312 +#include +#else +#include +#endif + +#include +#include + #include #include #include #include -#include -#include -#include - -/* - * Module documentation - */ +#if defined(MODULE) MODULE_AUTHOR("Donald Becker "); MODULE_DESCRIPTION("Intel i82557/i82558 PCI EtherExpressPro driver"); MODULE_PARM(debug, "i"); @@ -79,16 +128,35 @@ MODULE_PARM(rxdmacount, "i"); MODULE_PARM(rx_copybreak, "i"); MODULE_PARM(max_interrupt_work, "i"); MODULE_PARM(multicast_filter_limit, "i"); +#endif #define RUN_AT(x) (jiffies + (x)) +/* Condensed bus+endian portability operations. */ +#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) -#define dev_free_skb(skb) dev_kfree_skb(skb); +#if LINUX_VERSION_CODE < 0x020314 +#define net_device device +#define pci_base_address(p, n) (p)->base_address[n] +#else +#define pci_base_address(p, n) (p)->resource[n].start +#endif + +#define dev_free_skb(skb) dev_kfree_skb(skb); +#if ! defined(HAS_NETIF_QUEUE) +#define netif_wake_queue(dev) do { \ + clear_bit(0, (void*)&dev->tbusy); \ + mark_bh(NET_BH); \ + } while(0) +#define netif_start_queue(dev) clear_bit(0, (void*)&dev->tbusy) +#define netif_stop_queue(dev) set_bit(0, (void*)&dev->tbusy) +#endif /* The total I/O port extent of the board. The registers beyond 0x18 only exist on the i82558. */ #define SPEEDO3_TOTAL_SIZE 0x20 -int speedo_debug = 0; +int speedo_debug = 1; /* Theory of Operation @@ -196,24 +264,6 @@ allocate a new, minimally-sized skbuff. For large frames the copying cost is non-trivial, and the larger copy might flush the cache of useful data, so we pass up the skbuff the packet was received into. -IIID. Synchronization -The driver runs as two independent, single-threaded flows of control. One -is the send-packet routine, which enforces single-threaded use by the -dev->tbusy flag. The other thread is the interrupt handler, which is single -threaded by the hardware and other software. - -The send packet thread has partial control over the Tx ring and 'dev->tbusy' -flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next -queue slot is empty, it clears the tbusy flag when finished otherwise it sets -the 'sp->tx_full' flag. - -The interrupt handler has exclusive control over the Rx ring and records stats -from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so -we can't avoid the interrupt overhead by having the Tx routine reap the Tx -stats.) After reaping the stats, it marks the queue entry as empty by setting -the 'base' to zero. Iff the 'sp->tx_full' flag is set, it clears both the -tx_full and tbusy flags. - IV. Notes Thanks to Steve Williams of Intel for arranging the non-disclosure agreement @@ -222,15 +272,55 @@ having to sign an Intel NDA when I'm helping Intel sell their own product! */ -/* A few values that may be tweaked. */ -/* The ring sizes should be a power of two for efficiency. */ -#define TX_RING_SIZE 16 /* Effectively 2 entries fewer. */ -#define RX_RING_SIZE 16 -/* Size of an pre-allocated Rx buffer: + slack.*/ -#define PKT_BUF_SZ 1536 +/* This table drives the PCI probe routines. */ +static struct net_device *speedo_found1(int pci_bus, int pci_devfn, long ioaddr, int irq, int chip_idx, int fnd_cnt); -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT ((800*HZ)/1000) +#ifdef USE_IO +#define SPEEDO_IOTYPE PCI_USES_MASTER|PCI_USES_IO|PCI_ADDR1 +#define SPEEDO_SIZE 32 +#else +#define SPEEDO_IOTYPE PCI_USES_MASTER|PCI_USES_MEM|PCI_ADDR0 +#define SPEEDO_SIZE 0x1000 +#endif + +#if defined(HAS_PCI_NETIF) +struct pci_id_info static pci_tbl[] = { + { "Intel PCI EtherExpress Pro100", + { 0x12298086, 0xffffffff,}, SPEEDO_IOTYPE, SPEEDO_SIZE, + 0, speedo_found1 }, + {0,}, /* 0 terminated list. */ +}; +#else +enum pci_flags_bit { + PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, + PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3, +}; +struct pci_id_info { + const char *name; + u16 vendor_id, device_id, device_id_mask, flags; + int io_size; + struct net_device *(*probe1)(int pci_bus, int pci_devfn, long ioaddr, int irq, int chip_idx, int fnd_cnt); +} static pci_tbl[] = { + { "Intel PCI EtherExpress Pro100", + 0x8086, 0x1229, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 32, speedo_found1 }, + {0,}, /* 0 terminated list. */ +}; +#endif + +#ifndef USE_IO +#undef inb +#undef inw +#undef inl +#undef outb +#undef outw +#undef outl +#define inb readb +#define inw readw +#define inl readl +#define outb writeb +#define outw writew +#define outl writel +#endif /* How to wait for the command unit to accept a command. Typically this takes 0 ticks. */ @@ -241,10 +331,6 @@ static inline void wait_for_cmd_done(long cmd_ioaddr) while(inb(cmd_ioaddr) && --wait >= 0); } -/* Operational parameter that usually are not changed. */ - -/* The rest of these values should never change. */ - /* Offsets to the various registers. All accesses need not be longword aligned. */ enum speedo_offsets { @@ -257,34 +343,48 @@ enum speedo_offsets { }; /* Commands that can be put in a command list entry. */ enum commands { - CmdNOp = 0, CmdIASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, - CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7, - CmdSuspend = 0x4000, /* Suspend after completion. */ - CmdIntr = 0x2000, /* Interrupt after completion. */ - CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */ + CmdNOp = 0, CmdIASetup = 0x10000, CmdConfigure = 0x20000, + CmdMulticastList = 0x30000, CmdTx = 0x40000, CmdTDR = 0x50000, + CmdDump = 0x60000, CmdDiagnose = 0x70000, + CmdSuspend = 0x40000000, /* Suspend after completion. */ + CmdIntr = 0x20000000, /* Interrupt after completion. */ + CmdTxFlex = 0x00080000, /* Use "Flexible mode" for CmdTx command. */ }; +/* Clear CmdSuspend (1<<30) atomically. + Otherwise the command status in the lower 16 bits may be reset after + an asynchronous change. Previous driver version used separate 16 bit fields + for commands and statuses. --SAW + */ +#ifdef __i386__ +#define speedo_fool_gcc(x) (*(volatile struct { int a[100]; } *)x) +#define speedo_clear_mask(mask, addr) \ +__asm__ __volatile__("lock; andl %0,%1" \ +: : "r" (~(mask)),"m" (speedo_fool_gcc(addr)) : "memory") +#define clear_suspend(cmd) speedo_clear_mask(CmdSuspend, &(cmd)->cmd_status) +#else +#define clear_suspend(cmd) (cmd)->cmd_status &= cpu_to_le32(~CmdSuspend) +#endif -/* The SCB accepts the following controls for the Tx and Rx units: */ -#define CU_START 0x0010 -#define CU_RESUME 0x0020 -#define CU_STATSADDR 0x0040 -#define CU_SHOWSTATS 0x0050 /* Dump statistics counters. */ -#define CU_CMD_BASE 0x0060 /* Base address to add to add CU commands. */ -#define CU_DUMPSTATS 0x0070 /* Dump then reset stats counters. */ - -#define RX_START 0x0001 -#define RX_RESUME 0x0002 -#define RX_ABORT 0x0004 -#define RX_ADDR_LOAD 0x0006 -#define RX_RESUMENR 0x0007 -#define INT_MASK 0x0100 -#define DRVR_INT 0x0200 /* Driver generated interrupt. */ +enum SCBCmdBits { + SCBMaskCmdDone=0x8000, SCBMaskRxDone=0x4000, SCBMaskCmdIdle=0x2000, + SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800, SCBMaskFlowCtl=0x0400, + SCBTriggerIntr=0x0200, SCBMaskAll=0x0100, + /* The rest are Rx and Tx commands. */ + CUStart=0x0010, CUResume=0x0020, CUStatsAddr=0x0040, CUShowStats=0x0050, + CUCmdBase=0x0060, /* CU Base address (set to zero) . */ + CUDumpStats=0x0070, /* Dump then reset stats counters. */ + RxStart=0x0001, RxResume=0x0002, RxAbort=0x0004, RxAddrLoad=0x0006, + RxResumeNoResources=0x0007, +}; + +enum SCBPort_cmds { + PortReset=0, PortSelfTest=1, PortPartialReset=2, PortDump=3, +}; /* The Speedo3 Rx and Tx frame/buffer descriptors. */ -struct descriptor { /* A generic descriptor. */ - s16 status; /* Offset 0. */ - s16 command; /* Offset 2. */ - u32 link; /* struct descriptor * */ +struct descriptor { /* A generic descriptor. */ + s32 cmd_status; /* All command and status fields. */ + u32 link; /* struct descriptor * */ unsigned char params[0]; }; @@ -293,8 +393,7 @@ struct RxFD { /* Receive frame descriptor. */ s32 status; u32 link; /* struct RxFD * */ u32 rx_buf_addr; /* void * */ - u16 count; - u16 size; + u32 count; }; /* Selected elements of the Tx/RxFD.status word. */ @@ -302,7 +401,7 @@ enum RxFD_bits { RxComplete=0x8000, RxOK=0x2000, RxErrCRC=0x0800, RxErrAlign=0x0400, RxErrTooBig=0x0200, RxErrSymbol=0x0010, RxEth2Type=0x0020, RxNoMatch=0x0004, RxNoIAMatch=0x0002, - StatusComplete=0x8000, + TxUnderrun=0x1000, StatusComplete=0x8000, }; struct TxFD { /* Transmit frame descriptor set. */ @@ -317,6 +416,14 @@ struct TxFD { /* Transmit frame descriptor set. */ s32 tx_buf_size1; /* Length of Tx frame. */ }; +/* Multicast filter setting block. --SAW */ +struct speedo_mc_block { + struct speedo_mc_block *next; + unsigned int tx; + char fill[16 - sizeof(struct speedo_mc_block *) - sizeof(unsigned int)]; + struct descriptor frame; +}; + /* Elements of the dump_statistics block. This block must be lword aligned. */ struct speedo_stats { u32 tx_good_frames; @@ -338,49 +445,60 @@ struct speedo_stats { u32 done_marker; }; +enum Rx_ring_state_bits { + RrNoMem=1, RrPostponed=2, RrNoResources=4, RrOOMReported=8, +}; + +/* Do not change the position (alignment) of the first few elements! + The later elements are grouped for cache locality. */ struct speedo_private { - char devname[8]; /* Used only for kernel debugging. */ - const char *product_name; - struct device *next_module; - spinlock_t lock; - struct TxFD tx_ring[TX_RING_SIZE] /* Commands (usually CmdTxPacket). */ - __attribute__ ((aligned (L1_CACHE_BYTES)));; - /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct TxFD tx_ring[TX_RING_SIZE]; /* Commands (usually CmdTxPacket). */ + struct RxFD *rx_ringp[RX_RING_SIZE]; /* Rx descriptor, used as ring. */ + /* The addresses of a Tx/Rx-in-place packets/buffers. */ struct sk_buff* tx_skbuff[TX_RING_SIZE]; - struct descriptor *last_cmd; /* Last command sent. */ - /* Rx descriptor ring & addresses of receive-in-place skbuffs. */ - struct RxFD *rx_ringp[RX_RING_SIZE]; struct sk_buff* rx_skbuff[RX_RING_SIZE]; + struct descriptor *last_cmd; /* Last command sent. */ + unsigned int cur_tx, dirty_tx; /* The ring entries to be free()ed. */ + spinlock_t lock; /* Group with Tx control cache line. */ + u32 tx_threshold; /* The value for txdesc.count. */ struct RxFD *last_rxf; /* Last command sent. */ + unsigned int cur_rx, dirty_rx; /* The next free ring entry */ + long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */ + const char *product_name; + struct net_device *next_module; + void *priv_addr; /* Unaligned address for kfree */ struct enet_statistics stats; struct speedo_stats lstats; + int chip_id; + unsigned char pci_bus, pci_devfn, acpi_pwr; struct timer_list timer; /* Media selection timer. */ - long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */ - unsigned int cur_rx, cur_tx; /* The next free ring entry */ - unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ - int mc_setup_frm_len; /* The length of an allocated.. */ - struct descriptor *mc_setup_frm; /* ..multicast setup frame. */ - int mc_setup_busy; /* Avoid double-use of setup frame. */ + struct speedo_mc_block *mc_setup_head;/* Multicast setup frame list head. */ + struct speedo_mc_block *mc_setup_tail;/* Multicast setup frame list tail. */ + int in_interrupt; /* Word-aligned dev->interrupt */ char rx_mode; /* Current PROMISC/ALLMULTI setting. */ unsigned int tx_full:1; /* The Tx queue is full. */ unsigned int full_duplex:1; /* Full-duplex operation requested. */ - unsigned int default_port:1; /* Last dev->if_port value. */ + unsigned int flow_ctrl:1; /* Use 802.3x flow control. */ unsigned int rx_bug:1; /* Work around receiver hang errata. */ unsigned int rx_bug10:1; /* Receiver might hang at 10mbps. */ unsigned int rx_bug100:1; /* Receiver might hang at 100mbps. */ + unsigned char default_port:8; /* Last dev->if_port value. */ + unsigned char rx_ring_state; /* RX ring status flags. */ unsigned short phy[2]; /* PHY media interfaces available. */ + unsigned short advertising; /* Current PHY advertised caps. */ + unsigned short partner; /* Link partner caps. */ }; /* The parameters for a CmdConfigure operation. There are so many options that it would be difficult to document each bit. We mostly use the default or recommended settings. */ const char i82557_config_cmd[22] = { - 22, 0x08, 0, 0, 0, 0x80, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */ + 22, 0x08, 0, 0, 0, 0, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */ 0, 0x2E, 0, 0x60, 0, 0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */ 0x3f, 0x05, }; const char i82558_config_cmd[22] = { - 22, 0x08, 0, 1, 0, 0x80, 0x22, 0x03, 1, /* 1=Use MII 0=Use AUI */ + 22, 0x08, 0, 1, 0, 0, 0x22, 0x03, 1, /* 1=Use MII 0=Use AUI */ 0, 0x2E, 0, 0x60, 0x08, 0x88, 0x68, 0, 0x40, 0xf2, 0xBD, /* 0xBD->0xFD=Force full-duplex */ 0x31, 0x05, }; @@ -394,35 +512,29 @@ static const char *phys[] = { enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240, S80C24, I82555, DP83840A=10, }; static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 }; +#define EE_READ_CMD (6) -static void speedo_found1(struct device *dev, long ioaddr, int irq, - int card_idx); - -static int read_eeprom(long ioaddr, int location, int addr_len); +static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len); static int mdio_read(long ioaddr, int phy_id, int location); static int mdio_write(long ioaddr, int phy_id, int location, int value); -static int speedo_open(struct device *dev); +static int speedo_open(struct net_device *dev); +static void speedo_resume(struct net_device *dev); static void speedo_timer(unsigned long data); -static void speedo_init_rx_ring(struct device *dev); -static int speedo_start_xmit(struct sk_buff *skb, struct device *dev); -static int speedo_rx(struct device *dev); +static void speedo_init_rx_ring(struct net_device *dev); +static void speedo_tx_timeout(struct net_device *dev); +static int speedo_start_xmit(struct sk_buff *skb, struct net_device *dev); +static void speedo_refill_rx_buffers(struct net_device *dev, int force); +static int speedo_rx(struct net_device *dev); +static void speedo_tx_buffer_gc(struct net_device *dev); static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs); -static int speedo_close(struct device *dev); -static struct enet_statistics *speedo_get_stats(struct device *dev); -static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd); -static void set_rx_mode(struct device *dev); +static int speedo_close(struct net_device *dev); +static struct enet_statistics *speedo_get_stats(struct net_device *dev); +static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void set_rx_mode(struct net_device *dev); +static void speedo_show_state(struct net_device *dev); -/* The parameters that may be passed in... */ -/* 'options' is used to pass a transceiver override or full-duplex flag - e.g. "options=16" for FD, "options=32" for 100mbps-only. */ -static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1}; -#ifdef MODULE -static int debug = -1; /* The debug level */ -#endif - #ifdef honor_default_port /* Optional driver feature to allow forcing the transceiver setting. Not recommended. */ @@ -431,9 +543,10 @@ static int mii_ctrl[8] = { 0x3300, 0x3100, 0x0000, 0x0100, #endif /* A list of all installed Speedo devices, for removing the driver module. */ -static struct device *root_speedo_dev = NULL; +static struct net_device *root_speedo_dev = NULL; -int eepro100_init(struct device *dev) +#if ! defined(HAS_PCI_NETIF) +int eepro100_init(void) { int cards_found = 0; static int pci_index = 0; @@ -443,6 +556,7 @@ int eepro100_init(struct device *dev) for (; pci_index < 8; pci_index++) { unsigned char pci_bus, pci_device_fn, pci_latency; + u32 pciaddr; long ioaddr; int irq; @@ -455,11 +569,23 @@ int eepro100_init(struct device *dev) break; { struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); - ioaddr = pdev->base_address[1]; /* Use [0] to mem-map */ +#ifdef USE_IO + pciaddr = pci_base_address(pdev, 1); /* Use [0] to mem-map */ +#else + pciaddr = pci_base_address(pdev, 0); +#endif irq = pdev->irq; } /* Remove I/O space marker in bit 0. */ - ioaddr &= ~3UL; + if (pciaddr & 1) { + ioaddr = pciaddr & ~3UL; + if (check_region(ioaddr, 32)) + continue; + } else if ((ioaddr = (long)ioremap(pciaddr & ~0xfUL, 0x1000)) == 0) { + printk(KERN_INFO "Failed to map PCI address %#x.\n", + pciaddr); + continue; + } if (speedo_debug > 2) printk("Found Intel i82557 PCI Speedo at I/O %#lx, IRQ %d.\n", ioaddr, irq); @@ -485,27 +611,30 @@ int eepro100_init(struct device *dev) } else if (speedo_debug > 1) printk(" PCI latency timer (CFLT) is %#x.\n", pci_latency); - speedo_found1(dev, ioaddr, irq, cards_found); - dev = NULL; - cards_found++; + if (speedo_found1(pci_bus, pci_device_fn, ioaddr, irq, 0, cards_found)) + cards_found++; } return cards_found; } +#endif -static void speedo_found1(struct device *dev, long ioaddr, int irq, - int card_idx) +static struct net_device *speedo_found1(int pci_bus, int pci_devfn, + long ioaddr, int irq, int chip_idx, int card_idx) { - static int did_version = 0; /* Already printed version info. */ + struct net_device *dev; struct speedo_private *sp; - char *product; + const char *product; int i, option; - u16 eeprom[0x40]; - + u16 eeprom[0x100]; + int acpi_idle_state = 0; +#ifndef MODULE + static int did_version = 0; /* Already printed version info. */ if (speedo_debug > 0 && did_version++ == 0) printk(version); +#endif - dev = init_etherdev(dev, sizeof(struct speedo_private)); + dev = init_etherdev(NULL, sizeof(struct speedo_private)); if (dev->mem_start > 0) option = dev->mem_start; @@ -514,16 +643,31 @@ static void speedo_found1(struct device *dev, long ioaddr, int irq, else option = 0; +#if defined(HAS_PCI_NETIF) + acpi_idle_state = acpi_set_pwr_state(pci_bus, pci_devfn, ACPI_D0); +#endif + /* Read the station address EEPROM before doing the reset. - Perhaps this should even be done before accepting the device, - then we wouldn't have a device name with which to report the error. */ + Nominally his should even be done before accepting the device, but + then we wouldn't have a device name with which to report the error. + The size test is for 6 bit vs. 8 bit address serial EEPROMs. + */ { u16 sum = 0; int j; - int addr_len = read_eeprom(ioaddr, 0, 6) == 0xffff ? 8 : 6; + int read_cmd, ee_size; + + if ((do_eeprom_cmd(ioaddr, EE_READ_CMD << 24, 27) & 0xffe0000) + == 0xffe0000) { + ee_size = 0x100; + read_cmd = EE_READ_CMD << 24; + } else { + ee_size = 0x40; + read_cmd = EE_READ_CMD << 22; + } - for (j = 0, i = 0; i < 0x40; i++) { - u16 value = read_eeprom(ioaddr, i, addr_len); + for (j = 0, i = 0; i < ee_size; i++) { + u16 value = do_eeprom_cmd(ioaddr, read_cmd | (i << 16), 27); eeprom[i] = value; sum += value; if (i < 3) { @@ -542,12 +686,12 @@ static void speedo_found1(struct device *dev, long ioaddr, int irq, /* Reset the chip: stop Tx and Rx processes and clear counters. This takes less than 10usec and will easily finish before the next action. */ - outl(0, ioaddr + SCBPort); + outl(PortReset, ioaddr + SCBPort); if (eeprom[3] & 0x0100) product = "OEM i82557/i82558 10/100 Ethernet"; else - product = "Intel EtherExpress Pro 10/100"; + product = pci_tbl[chip_idx].name; printk(KERN_INFO "%s: %s at %#3lx, ", dev->name, product, ioaddr); @@ -555,7 +699,7 @@ static void speedo_found1(struct device *dev, long ioaddr, int irq, printk("%2.2X:", dev->dev_addr[i]); printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq); -#ifndef kernel_bloat +#if 1 || defined(kernel_bloat) /* OK, this is pure kernel bloat. I don't like it when other drivers waste non-pageable kernel space to emit similar messages, but I need them for bug reports. */ @@ -600,7 +744,7 @@ static void speedo_found1(struct device *dev, long ioaddr, int irq, self_test_results = (s32*) ((((long) str) + 15) & ~0xf); self_test_results[0] = 0; self_test_results[1] = -1; - outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort); + outl(virt_to_bus(self_test_results) | PortSelfTest, ioaddr + SCBPort); do { udelay(10); } while (self_test_results[1] == -1 && --boguscnt >= 0); @@ -624,19 +768,33 @@ static void speedo_found1(struct device *dev, long ioaddr, int irq, } #endif /* kernel_bloat */ + outl(PortReset, ioaddr + SCBPort); +#if defined(HAS_PCI_NETIF) + /* Return the chip to its original power state. */ + acpi_set_pwr_state(pci_bus, pci_devfn, acpi_idle_state); +#endif + /* We do a request_region() only to register /proc/ioports info. */ request_region(ioaddr, SPEEDO3_TOTAL_SIZE, "Intel Speedo3 Ethernet"); dev->base_addr = ioaddr; dev->irq = irq; - if (dev->priv == NULL) - dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL); sp = dev->priv; + if (dev->priv == NULL) { + void *mem = kmalloc(sizeof(*sp), GFP_KERNEL); + dev->priv = sp = mem; /* Cache align here if kmalloc does not. */ + sp->priv_addr = mem; + } memset(sp, 0, sizeof(*sp)); sp->next_module = root_speedo_dev; root_speedo_dev = dev; + sp->pci_bus = pci_bus; + sp->pci_devfn = pci_devfn; + sp->chip_id = chip_idx; + sp->acpi_pwr = acpi_idle_state; + sp->full_duplex = option >= 0 && (option & 0x10) ? 1 : 0; if (card_idx >= 0) { if (full_duplex[card_idx] >= 0) @@ -654,12 +812,16 @@ static void speedo_found1(struct device *dev, long ioaddr, int irq, /* The Speedo-specific entries in the device structure. */ dev->open = &speedo_open; dev->hard_start_xmit = &speedo_start_xmit; +#if defined(HAS_NETIF_QUEUE) + dev->tx_timeout = &speedo_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; +#endif dev->stop = &speedo_close; dev->get_stats = &speedo_get_stats; dev->set_multicast_list = &set_rx_mode; dev->do_ioctl = &speedo_ioctl; - return; + return dev; } /* Serial EEPROM section. @@ -668,47 +830,33 @@ static void speedo_found1(struct device *dev, long ioaddr, int irq, #define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */ #define EE_CS 0x02 /* EEPROM chip select. */ #define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ -#define EE_WRITE_0 0x01 -#define EE_WRITE_1 0x05 #define EE_DATA_READ 0x08 /* EEPROM chip data out. */ #define EE_ENB (0x4800 | EE_CS) +#define EE_WRITE_0 0x4802 +#define EE_WRITE_1 0x4806 +#define EE_OFFSET SCBeeprom /* Delay between EEPROM clock transitions. - This will actually work with no delay on 33Mhz PCI. */ -#define eeprom_delay(nanosec) udelay(1); - -/* The EEPROM commands include the alway-set leading bit. */ -#define EE_WRITE_CMD (5 << addr_len) -#define EE_READ_CMD (6 << addr_len) -#define EE_ERASE_CMD (7 << addr_len) + The code works with no delay on 33Mhz PCI. */ +#define eeprom_delay() inw(ee_addr) -static int read_eeprom(long ioaddr, int location, int addr_len) +static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len) { - unsigned short retval = 0; + unsigned retval = 0; long ee_addr = ioaddr + SCBeeprom; - int read_cmd = location | EE_READ_CMD; - int i; - outw(EE_ENB & ~EE_CS, ee_addr); - outw(EE_ENB, ee_addr); + outw(EE_ENB | EE_SHIFT_CLK, ee_addr); - /* Shift the read command bits out. */ - for (i = 12; i >= 0; i--) { - short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; - outw(EE_ENB | dataval, ee_addr); - eeprom_delay(100); - outw(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); - eeprom_delay(150); - } - outw(EE_ENB, ee_addr); - - for (i = 15; i >= 0; i--) { - outw(EE_ENB | EE_SHIFT_CLK, ee_addr); - eeprom_delay(100); + /* Shift the command bits out. */ + do { + short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0; + outw(dataval, ee_addr); + eeprom_delay(); + outw(dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0); - outw(EE_ENB, ee_addr); - eeprom_delay(100); - } + } while (--cmd_len >= 0); + outw(EE_ENB, ee_addr); /* Terminate the EEPROM access. */ outw(EE_ENB & ~EE_CS, ee_addr); @@ -723,6 +871,7 @@ static int mdio_read(long ioaddr, int phy_id, int location) val = inl(ioaddr + SCBCtrlMDI); if (--boguscnt < 0) { printk(KERN_ERR " mdio_read() timed out with val = %8.8x.\n", val); + break; } } while (! (val & 0x10000000)); return val & 0xffff; @@ -737,6 +886,7 @@ static int mdio_write(long ioaddr, int phy_id, int location, int value) val = inl(ioaddr + SCBCtrlMDI); if (--boguscnt < 0) { printk(KERN_ERR" mdio_write() timed out with val = %8.8x.\n", val); + break; } } while (! (val & 0x10000000)); return val & 0xffff; @@ -744,24 +894,34 @@ static int mdio_write(long ioaddr, int phy_id, int location, int value) static int -speedo_open(struct device *dev) +speedo_open(struct net_device *dev) { struct speedo_private *sp = (struct speedo_private *)dev->priv; long ioaddr = dev->base_addr; -#ifdef notdef - /* We could reset the chip, but should not need to. */ - /* In fact we MUST NOT, unless we also re-do the init */ - outl(0, ioaddr + SCBPort); - udelay(10); +#if defined(HAS_PCI_NETIF) + acpi_set_pwr_state(sp->pci_bus, sp->pci_devfn, ACPI_D0); #endif - /* This had better be initialized before we initialize the interrupt! */ - sp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; - if (speedo_debug > 1) printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq); + /* Set up the Tx queue early.. */ + sp->cur_tx = 0; + sp->dirty_tx = 0; + sp->last_cmd = 0; + sp->tx_full = 0; + sp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; + sp->in_interrupt = 0; + + /* .. we can safely take handler calls during init. */ + if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ, dev->name, dev)) { + return -EAGAIN; + } + MOD_INC_USE_COUNT; + + dev->if_port = sp->default_port; + #ifdef oh_no_you_dont_unless_you_honour_the_options_passed_in_to_us /* Retrigger negotiation to reset previous errors. */ if ((sp->phy[0] & 0x8000) == 0) { @@ -780,88 +940,30 @@ speedo_open(struct device *dev) } #endif - /* Load the statistics block address. */ - wait_for_cmd_done(ioaddr + SCBCmd); - outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer); - outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd); - sp->lstats.done_marker = 0; - speedo_init_rx_ring(dev); - wait_for_cmd_done(ioaddr + SCBCmd); - outl(0, ioaddr + SCBPointer); - outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd); - - /* Todo: verify that we must wait for previous command completion. */ - wait_for_cmd_done(ioaddr + SCBCmd); - outl(virt_to_bus(sp->rx_ringp[0]), ioaddr + SCBPointer); - outw(INT_MASK | RX_START, ioaddr + SCBCmd); - /* Fill the first command with our physical address. */ - { - u16 *eaddrs = (u16 *)dev->dev_addr; - u16 *setup_frm = (u16 *)&(sp->tx_ring[0].tx_desc_addr); - - /* Avoid a bug(?!) here by marking the command already completed. */ - sp->tx_ring[0].status = cpu_to_le32(((CmdSuspend | CmdIASetup) << 16) | 0xa000); - sp->tx_ring[0].link = cpu_to_le32(virt_to_bus(&(sp->tx_ring[1]))); - *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; - } - sp->last_cmd = (struct descriptor *)&sp->tx_ring[0]; - sp->cur_tx = 1; - sp->dirty_tx = 0; - sp->tx_full = 0; - - wait_for_cmd_done(ioaddr + SCBCmd); - outl(0, ioaddr + SCBPointer); - outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd); - - dev->if_port = sp->default_port; + /* Fire up the hardware. */ + outw(SCBMaskAll, ioaddr + SCBCmd); + speedo_resume(dev); - dev->tbusy = 0; dev->interrupt = 0; dev->start = 1; - - /* - * Request the IRQ last, after we have set up all data structures. - * It would be bad to get an interrupt before we're ready. - * - * Register ourself first before turn on the interrupt. Someone - * else may share the same interrupt. H.J. - */ - if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ, - "Intel EtherExpress Pro 10/100 Ethernet", dev)) { - return -EAGAIN; - } - - /* Start the chip's Tx process and unmask interrupts. */ - /* Todo: verify that we must wait for previous command completion. */ - wait_for_cmd_done(ioaddr + SCBCmd); - outl(virt_to_bus(&sp->tx_ring[0]), ioaddr + SCBPointer); - outw(CU_START, ioaddr + SCBCmd); + netif_start_queue(dev); /* Setup the chip and configure the multicast list. */ - sp->mc_setup_frm = NULL; - sp->mc_setup_frm_len = 0; - sp->mc_setup_busy = 0; + sp->mc_setup_head = NULL; + sp->mc_setup_tail = NULL; + sp->flow_ctrl = sp->partner = 0; sp->rx_mode = -1; /* Invalid -> always reset the mode. */ set_rx_mode(dev); + if ((sp->phy[0] & 0x8000) == 0) + sp->advertising = mdio_read(ioaddr, sp->phy[0] & 0x1f, 4); if (speedo_debug > 2) { printk(KERN_DEBUG "%s: Done speedo_open(), status %8.8x.\n", dev->name, inw(ioaddr + SCBStatus)); } - wait_for_cmd_done(ioaddr + SCBCmd); - outw(CU_DUMPSTATS, ioaddr + SCBCmd); - - /* No need to wait for the command unit to accept here. */ - if ((sp->phy[0] & 0x8000) == 0) - mdio_read(ioaddr, sp->phy[0] & 0x1f, 0); - - MOD_INC_USE_COUNT; - /* Set the timer. The timer serves a dual purpose: 1) to monitor the media interface (e.g. link beat) and perhaps switch to an alternate media type @@ -873,17 +975,100 @@ speedo_open(struct device *dev) sp->timer.function = &speedo_timer; /* timer handler */ add_timer(&sp->timer); + /* No need to wait for the command unit to accept here. */ + if ((sp->phy[0] & 0x8000) == 0) + mdio_read(ioaddr, sp->phy[0] & 0x1f, 0); + return 0; } +/* Start the chip hardware after a full reset. */ +static void speedo_resume(struct net_device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + long ioaddr = dev->base_addr; + + /* Start with a Tx threshold of 256 (0x..20.... 8 byte units). */ + sp->tx_threshold = 0x01208000; + + /* Set the segment registers to '0'. */ + wait_for_cmd_done(ioaddr + SCBCmd); + outl(0, ioaddr + SCBPointer); + outb(RxAddrLoad, ioaddr + SCBCmd); + wait_for_cmd_done(ioaddr + SCBCmd); + outb(CUCmdBase, ioaddr + SCBCmd); + wait_for_cmd_done(ioaddr + SCBCmd); + + /* Load the statistics block and rx ring addresses. */ + outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer); + outb(CUStatsAddr, ioaddr + SCBCmd); + sp->lstats.done_marker = 0; + wait_for_cmd_done(ioaddr + SCBCmd); + + if (sp->rx_ringp[sp->cur_rx % RX_RING_SIZE] == NULL) { + if (speedo_debug > 2) + printk(KERN_DEBUG "%s: NULL cur_rx in speedo_resume().\n", + dev->name); + } else { + outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]), + ioaddr + SCBPointer); + outb(RxStart, ioaddr + SCBCmd); + wait_for_cmd_done(ioaddr + SCBCmd); + } + + outb(CUDumpStats, ioaddr + SCBCmd); + + /* Fill the first command with our physical address. */ + { + struct descriptor *ias_cmd; + + ias_cmd = + (struct descriptor *)&sp->tx_ring[sp->cur_tx++ % TX_RING_SIZE]; + /* Avoid a bug(?!) here by marking the command already completed. */ + ias_cmd->cmd_status = cpu_to_le32((CmdSuspend | CmdIASetup) | 0xa000); + ias_cmd->link = + virt_to_le32desc(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]); + memcpy(ias_cmd->params, dev->dev_addr, 6); + sp->last_cmd = ias_cmd; + } + + /* Start the chip's Tx process and unmask interrupts. */ + wait_for_cmd_done(ioaddr + SCBCmd); + outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]), + ioaddr + SCBPointer); + outw(CUStart, ioaddr + SCBCmd); +} + /* Media monitoring and control. */ static void speedo_timer(unsigned long data) { - struct device *dev = (struct device *)data; + struct net_device *dev = (struct net_device *)data; struct speedo_private *sp = (struct speedo_private *)dev->priv; + long ioaddr = dev->base_addr; + int phy_num = sp->phy[0] & 0x1f; + /* We have MII and lost link beat. */ + if ((sp->phy[0] & 0x8000) == 0) { + int partner = mdio_read(ioaddr, phy_num, 5); + if (partner != sp->partner) { + int flow_ctrl = sp->advertising & partner & 0x0400 ? 1 : 0; + if (speedo_debug > 2) + printk(KERN_DEBUG "%s: Link status change.\n", dev->name); + sp->partner = partner; + if (flow_ctrl != sp->flow_ctrl) { + sp->flow_ctrl = flow_ctrl; + sp->rx_mode = -1; /* Trigger a reload. */ + } + /* Clear sticky bit. */ + mdio_read(ioaddr, phy_num, 1); + /* If link beat has returned... */ + if (mdio_read(ioaddr, phy_num, 1) & 0x0004) + dev->flags |= IFF_RUNNING; + else + dev->flags &= ~IFF_RUNNING; + } + } if (speedo_debug > 3) { - long ioaddr = dev->base_addr; printk(KERN_DEBUG "%s: Media control tick, status %4.4x.\n", dev->name, inw(ioaddr + SCBStatus)); } @@ -892,6 +1077,9 @@ static void speedo_timer(unsigned long data) /* We haven't received a packet in a Long Time. We might have been bitten by the receiver hang bug. This can be cleared by sending a set multicast list command. */ + if (speedo_debug > 2) + printk(KERN_DEBUG "%s: Sending a multicast list set command" + " from a timer routine.\n", dev->name); set_rx_mode(dev); } /* We must continue to monitor the media. */ @@ -899,9 +1087,48 @@ static void speedo_timer(unsigned long data) add_timer(&sp->timer); } +static void speedo_show_state(struct net_device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + long ioaddr = dev->base_addr; + int phy_num = sp->phy[0] & 0x1f; + int i; + + /* Print a few items for debugging. */ + if (speedo_debug > 0) { + int i; + printk(KERN_DEBUG "%s: Tx ring dump, Tx queue %u / %u:\n", dev->name, + sp->cur_tx, sp->dirty_tx); + for (i = 0; i < TX_RING_SIZE; i++) + printk(KERN_DEBUG "%s: %c%c%2d %8.8x.\n", dev->name, + i == sp->dirty_tx % TX_RING_SIZE ? '*' : ' ', + i == sp->cur_tx % TX_RING_SIZE ? '=' : ' ', + i, sp->tx_ring[i].status); + } + printk(KERN_DEBUG "%s: Printing Rx ring" + " (next to receive into %u, dirty index %u).\n", + dev->name, sp->cur_rx, sp->dirty_rx); + + for (i = 0; i < RX_RING_SIZE; i++) + printk(KERN_DEBUG "%s: %c%c%c%2d %8.8x.\n", dev->name, + sp->rx_ringp[i] == sp->last_rxf ? 'l' : ' ', + i == sp->dirty_rx % RX_RING_SIZE ? '*' : ' ', + i == sp->cur_rx % RX_RING_SIZE ? '=' : ' ', + i, (sp->rx_ringp[i] != NULL) ? + (unsigned)sp->rx_ringp[i]->status : 0); + + for (i = 0; i < 16; i++) { + /* FIXME: what does it mean? --SAW */ + if (i == 6) i = 21; + printk(KERN_DEBUG "%s: PHY index %d register %d is %4.4x.\n", + dev->name, phy_num, i, mdio_read(ioaddr, phy_num, i)); + } + +} + /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ static void -speedo_init_rx_ring(struct device *dev) +speedo_init_rx_ring(struct net_device *dev) { struct speedo_private *sp = (struct speedo_private *)dev->priv; struct RxFD *rxf, *last_rxf = NULL; @@ -920,139 +1147,264 @@ speedo_init_rx_ring(struct device *dev) sp->rx_ringp[i] = rxf; skb_reserve(skb, sizeof(struct RxFD)); if (last_rxf) - last_rxf->link = cpu_to_le32(virt_to_bus(rxf)); + last_rxf->link = virt_to_le32desc(rxf); last_rxf = rxf; - rxf->status = cpu_to_le32(0x00000001); /* '1' is flag value only. */ + rxf->status = cpu_to_le32(0x00000001); /* '1' is flag value only. */ rxf->link = 0; /* None yet. */ /* This field unused by i82557, we use it as a consistency check. */ #ifdef final_version rxf->rx_buf_addr = 0xffffffff; #else - rxf->rx_buf_addr = cpu_to_le32(virt_to_bus(skb->tail)); + rxf->rx_buf_addr = virt_to_bus(skb->tail); #endif - rxf->count = 0; - rxf->size = cpu_to_le16(PKT_BUF_SZ); + rxf->count = cpu_to_le32(PKT_BUF_SZ << 16); } sp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); /* Mark the last entry as end-of-list. */ - last_rxf->status = cpu_to_le32(0xC0000002); /* '2' is flag value only. */ + last_rxf->status = cpu_to_le32(0xC0000002); /* '2' is flag value only. */ sp->last_rxf = last_rxf; } -static void speedo_tx_timeout(struct device *dev) +static void speedo_purge_tx(struct net_device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int entry; + + while ((int)(sp->cur_tx - sp->dirty_tx) > 0) { + entry = sp->dirty_tx % TX_RING_SIZE; + if (sp->tx_skbuff[entry]) { + sp->stats.tx_errors++; + dev_free_skb(sp->tx_skbuff[entry]); + sp->tx_skbuff[entry] = 0; + } + sp->dirty_tx++; + } + while (sp->mc_setup_head != NULL) { + struct speedo_mc_block *t; + if (speedo_debug > 1) + printk(KERN_DEBUG "%s: freeing mc frame.\n", dev->name); + t = sp->mc_setup_head->next; + kfree(sp->mc_setup_head); + sp->mc_setup_head = t; + } + sp->mc_setup_tail = NULL; + sp->tx_full = 0; + netif_wake_queue(dev); +} + +static void speedo_tx_timeout(struct net_device *dev) { struct speedo_private *sp = (struct speedo_private *)dev->priv; long ioaddr = dev->base_addr; + int status = inw(ioaddr + SCBStatus); + unsigned long flags; printk(KERN_WARNING "%s: Transmit timed out: status %4.4x " " %4.4x at %d/%d command %8.8x.\n", - dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd), + dev->name, status, inw(ioaddr + SCBCmd), sp->dirty_tx, sp->cur_tx, - le32_to_cpu(sp->tx_ring[sp->dirty_tx % TX_RING_SIZE].status)); - if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) { + sp->tx_ring[sp->dirty_tx % TX_RING_SIZE].status); + + /* Trigger a stats dump to give time before the reset. */ + speedo_get_stats(dev); + + speedo_show_state(dev); +#if 0 + if ((status & 0x00C0) != 0x0080 + && (status & 0x003C) == 0x0010) { + /* Only the command unit has stopped. */ printk(KERN_WARNING "%s: Trying to restart the transmitter...\n", dev->name); outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]), ioaddr + SCBPointer); - outw(CU_START, ioaddr + SCBCmd); + outw(CUStart, ioaddr + SCBCmd); } else { - outw(DRVR_INT, ioaddr + SCBCmd); +#else + { +#endif + start_bh_atomic(); + /* Ensure that timer routine doesn't run! */ + del_timer(&sp->timer); + end_bh_atomic(); + /* Reset the Tx and Rx units. */ + outl(PortReset, ioaddr + SCBPort); + /* We may get spurious interrupts here. But I don't think that they + may do much harm. 1999/12/09 SAW */ + udelay(10); + /* Disable interrupts. */ + outw(SCBMaskAll, ioaddr + SCBCmd); + synchronize_irq(); + speedo_tx_buffer_gc(dev); + /* Free as much as possible. + It helps to recover from a hang because of out-of-memory. + It also simplifies speedo_resume() in case TX ring is full or + close-to-be full. */ + speedo_purge_tx(dev); + speedo_refill_rx_buffers(dev, 1); + spin_lock_irqsave(&sp->lock, flags); + speedo_resume(dev); + sp->rx_mode = -1; + dev->trans_start = jiffies; + spin_unlock_irqrestore(&sp->lock, flags); + set_rx_mode(dev); /* it takes the spinlock itself --SAW */ + sp->timer.expires = RUN_AT(2*HZ); + add_timer(&sp->timer); } -#ifdef oh_no_you_dont_unless_you_honour_the_options_passed_in_to_us /* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */ if ((sp->phy[0] & 0x8000) == 0) { int phy_addr = sp->phy[0] & 0x1f; + int advertising = mdio_read(ioaddr, phy_addr, 4); + int mii_bmcr = mdio_read(ioaddr, phy_addr, 0); mdio_write(ioaddr, phy_addr, 0, 0x0400); mdio_write(ioaddr, phy_addr, 1, 0x0000); mdio_write(ioaddr, phy_addr, 4, 0x0000); mdio_write(ioaddr, phy_addr, 0, 0x8000); #ifdef honor_default_port mdio_write(ioaddr, phy_addr, 0, mii_ctrl[dev->default_port & 7]); +#else + mdio_read(ioaddr, phy_addr, 0); + mdio_write(ioaddr, phy_addr, 0, mii_bmcr); + mdio_write(ioaddr, phy_addr, 4, advertising); #endif } -#endif - sp->stats.tx_errors++; - dev->trans_start = jiffies; return; } static int -speedo_start_xmit(struct sk_buff *skb, struct device *dev) +speedo_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct speedo_private *sp = (struct speedo_private *)dev->priv; long ioaddr = dev->base_addr; int entry; - /* Block a timer-based transmit from overlapping. This could better be - done with atomic_swap(1, dev->tbusy), but set_bit() works as well. - If this ever occurs the queue layer is doing something evil! */ - if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { +#if ! defined(HAS_NETIF_QUEUE) + if (test_bit(0, (void*)&dev->tbusy) != 0) { int tickssofar = jiffies - dev->trans_start; if (tickssofar < TX_TIMEOUT - 2) return 1; if (tickssofar < TX_TIMEOUT) { /* Reap sent packets from the full Tx queue. */ - outw(DRVR_INT, ioaddr + SCBCmd); + outw(SCBTriggerIntr, ioaddr + SCBCmd); return 1; } speedo_tx_timeout(dev); return 1; } - - /* Caution: the write order is important here, set the base address - with the "ownership" bits last. */ +#endif { /* Prevent interrupts from changing the Tx ring from underneath us. */ unsigned long flags; spin_lock_irqsave(&sp->lock, flags); + /* Check if there are enough space. */ + if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) { + printk(KERN_ERR "%s: incorrect tbusy state, fixed.\n", dev->name); + netif_stop_queue(dev); + sp->tx_full = 1; + spin_unlock_irqrestore(&sp->lock, flags); + return 1; + } + /* Calculate the Tx descriptor entry. */ entry = sp->cur_tx++ % TX_RING_SIZE; sp->tx_skbuff[entry] = skb; - /* Todo: be a little more clever about setting the interrupt bit. */ sp->tx_ring[entry].status = - cpu_to_le32((CmdSuspend | CmdTx | CmdTxFlex) << 16); + cpu_to_le32(CmdSuspend | CmdTx | CmdTxFlex); + if (!(entry & ((TX_RING_SIZE>>2)-1))) + sp->tx_ring[entry].status |= cpu_to_le32(CmdIntr); sp->tx_ring[entry].link = - cpu_to_le32(virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE])); + virt_to_le32desc(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]); sp->tx_ring[entry].tx_desc_addr = - cpu_to_le32(virt_to_bus(&sp->tx_ring[entry].tx_buf_addr0)); - /* The data region is always in one buffer descriptor, Tx FIFO - threshold of 256. */ - sp->tx_ring[entry].count = cpu_to_le32(0x01208000); - sp->tx_ring[entry].tx_buf_addr0 = cpu_to_le32(virt_to_bus(skb->data)); + virt_to_le32desc(&sp->tx_ring[entry].tx_buf_addr0); + /* The data region is always in one buffer descriptor. */ + sp->tx_ring[entry].count = cpu_to_le32(sp->tx_threshold); + sp->tx_ring[entry].tx_buf_addr0 = virt_to_le32desc(skb->data); sp->tx_ring[entry].tx_buf_size0 = cpu_to_le32(skb->len); - /* Todo: perhaps leave the interrupt bit set if the Tx queue is more - than half full. Argument against: we should be receiving packets - and scavenging the queue. Argument for: if so, it shouldn't - matter. */ - sp->last_cmd->command &= cpu_to_le16(~(CmdSuspend | CmdIntr)); - sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; - /* Trigger the command unit resume. */ wait_for_cmd_done(ioaddr + SCBCmd); - outw(CU_RESUME, ioaddr + SCBCmd); + clear_suspend(sp->last_cmd); + /* We want the time window between clearing suspend flag on the previous + command and resuming CU to be as small as possible. + Interrupts in between are very undesired. --SAW */ + outb(CUResume, ioaddr + SCBCmd); + sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; + + /* Leave room for set_rx_mode(). If there is no more space than reserved + for multicast filter mark the ring as full. */ + if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) { + netif_stop_queue(dev); + sp->tx_full = 1; + } spin_unlock_irqrestore(&sp->lock, flags); } - /* Leave room for set_rx_mode() to fill two entries. */ - if (sp->cur_tx - sp->dirty_tx > TX_RING_SIZE - 3) - sp->tx_full = 1; - else - clear_bit(0, (void*)&dev->tbusy); - dev->trans_start = jiffies; return 0; } +static void speedo_tx_buffer_gc(struct net_device *dev) +{ + unsigned int dirty_tx; + struct speedo_private *sp = (struct speedo_private *)dev->priv; + + dirty_tx = sp->dirty_tx; + while ((int)(sp->cur_tx - dirty_tx) > 0) { + int entry = dirty_tx % TX_RING_SIZE; + int status = le32_to_cpu(sp->tx_ring[entry].status); + + if (speedo_debug > 5) + printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n", + entry, status); + if ((status & StatusComplete) == 0) + break; /* It still hasn't been processed. */ + if (status & TxUnderrun) + if (sp->tx_threshold < 0x01e08000) { + if (speedo_debug > 2) + printk(KERN_DEBUG "%s: TX underrun, threshold adjusted.\n", + dev->name); + sp->tx_threshold += 0x00040000; + } + /* Free the original skb. */ + if (sp->tx_skbuff[entry]) { + sp->stats.tx_packets++; /* Count only user packets. */ + sp->stats.tx_bytes += sp->tx_skbuff[entry]->len; + dev_free_skb(sp->tx_skbuff[entry]); + sp->tx_skbuff[entry] = 0; + } + dirty_tx++; + } + + if (speedo_debug && (int)(sp->cur_tx - dirty_tx) > TX_RING_SIZE) { + printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d," + " full=%d.\n", + dirty_tx, sp->cur_tx, sp->tx_full); + dirty_tx += TX_RING_SIZE; + } + + while (sp->mc_setup_head != NULL + && (int)(dirty_tx - sp->mc_setup_head->tx - 1) > 0) { + struct speedo_mc_block *t; + if (speedo_debug > 1) + printk(KERN_DEBUG "%s: freeing mc frame.\n", dev->name); + t = sp->mc_setup_head->next; + kfree(sp->mc_setup_head); + sp->mc_setup_head = t; + } + if (sp->mc_setup_head == NULL) + sp->mc_setup_tail = NULL; + + sp->dirty_tx = dirty_tx; +} + /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { - struct device *dev = (struct device *)dev_instance; + struct net_device *dev = (struct net_device *)dev_instance; struct speedo_private *sp; long ioaddr, boguscnt = max_interrupt_work; unsigned short status; @@ -1066,9 +1418,15 @@ static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs) ioaddr = dev->base_addr; sp = (struct speedo_private *)dev->priv; - spin_lock(&sp->lock); #ifndef final_version + /* A lock to prevent simultaneous entry on SMP machines. */ + if (test_and_set_bit(0, (void*)&sp->in_interrupt)) { + printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", + dev->name); + sp->in_interrupt = 0; /* Avoid halting machine. */ + return; + } dev->interrupt = 1; #endif @@ -1084,63 +1442,90 @@ static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs) if ((status & 0xfc00) == 0) break; - if (status & 0x4000) /* Packet received. */ + /* Always check if all rx buffers are allocated. --SAW */ + speedo_refill_rx_buffers(dev, 0); + + if ((status & 0x5000) || /* Packet received, or Rx error. */ + (sp->rx_ring_state&(RrNoMem|RrPostponed)) == RrPostponed) + /* Need to gather the postponed packet. */ speedo_rx(dev); if (status & 0x1000) { - if ((status & 0x003c) == 0x0028) /* No more Rx buffers. */ - outw(RX_RESUMENR, ioaddr + SCBCmd); - else if ((status & 0x003c) == 0x0008) { /* No resources (why?!) */ - /* No idea of what went wrong. Restart the receiver. */ + spin_lock(&sp->lock); + if ((status & 0x003c) == 0x0028) { /* No more Rx buffers. */ + struct RxFD *rxf; + printk(KERN_WARNING "%s: card reports no RX buffers.\n", + dev->name); + rxf = sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]; + if (rxf == NULL) { + if (speedo_debug > 2) + printk(KERN_DEBUG + "%s: NULL cur_rx in speedo_interrupt().\n", + dev->name); + sp->rx_ring_state |= RrNoMem|RrNoResources; + } else if (rxf == sp->last_rxf) { + if (speedo_debug > 2) + printk(KERN_DEBUG + "%s: cur_rx is last in speedo_interrupt().\n", + dev->name); + sp->rx_ring_state |= RrNoMem|RrNoResources; + } else + outb(RxResumeNoResources, ioaddr + SCBCmd); + } else if ((status & 0x003c) == 0x0008) { /* No resources. */ + struct RxFD *rxf; + printk(KERN_WARNING "%s: card reports no resources.\n", + dev->name); + rxf = sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]; + if (rxf == NULL) { + if (speedo_debug > 2) + printk(KERN_DEBUG + "%s: NULL cur_rx in speedo_interrupt().\n", + dev->name); + sp->rx_ring_state |= RrNoMem|RrNoResources; + } else if (rxf == sp->last_rxf) { + if (speedo_debug > 2) + printk(KERN_DEBUG + "%s: cur_rx is last in speedo_interrupt().\n", + dev->name); + sp->rx_ring_state |= RrNoMem|RrNoResources; + } else { + /* Restart the receiver. */ + outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]), + ioaddr + SCBPointer); + outb(RxStart, ioaddr + SCBCmd); + } + } + sp->stats.rx_errors++; + spin_unlock(&sp->lock); + } + + if ((sp->rx_ring_state&(RrNoMem|RrNoResources)) == RrNoResources) { + printk(KERN_WARNING + "%s: restart the receiver after a possible hang.\n", + dev->name); + spin_lock(&sp->lock); + /* Restart the receiver. + I'm not sure if it's always right to restart the receiver + here but I don't know another way to prevent receiver hangs. + 1999/12/25 SAW */ outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]), - ioaddr + SCBPointer); - outw(RX_START, ioaddr + SCBCmd); - } - sp->stats.rx_errors++; + ioaddr + SCBPointer); + outb(RxStart, ioaddr + SCBCmd); + sp->rx_ring_state &= ~RrNoResources; + spin_unlock(&sp->lock); } /* User interrupt, Command/Tx unit interrupt or CU not active. */ if (status & 0xA400) { - unsigned int dirty_tx = sp->dirty_tx; - - while (sp->cur_tx - dirty_tx > 0) { - int entry = dirty_tx % TX_RING_SIZE; - int status = le32_to_cpu(sp->tx_ring[entry].status); - - if (speedo_debug > 5) - printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n", - entry, status); - if ((status & StatusComplete) == 0) - break; /* It still hasn't been processed. */ - /* Free the original skb. */ - if (sp->tx_skbuff[entry]) { - sp->stats.tx_packets++; /* Count only user packets. */ - sp->stats.tx_bytes += sp->tx_skbuff[entry]->len; /* Count transmitted bytes */ - dev_free_skb(sp->tx_skbuff[entry]); - sp->tx_skbuff[entry] = 0; - } else if ((status&0x70000) == CmdNOp << 16) - sp->mc_setup_busy = 0; - dirty_tx++; - } - -#ifndef final_version - if (sp->cur_tx - dirty_tx > TX_RING_SIZE) { - printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d," - " full=%d.\n", - dirty_tx, sp->cur_tx, sp->tx_full); - dirty_tx += TX_RING_SIZE; - } -#endif - - if (sp->tx_full && dev->tbusy - && dirty_tx > sp->cur_tx - TX_RING_SIZE + 2) { - /* The ring is no longer full, clear tbusy. */ + spin_lock(&sp->lock); + speedo_tx_buffer_gc(dev); + if (sp->tx_full + && (int)(sp->cur_tx - sp->dirty_tx) < TX_QUEUE_UNFULL) { + /* The ring is no longer full. */ sp->tx_full = 0; - clear_bit(0, (void*)&dev->tbusy); - mark_bh(NET_BH); + netif_wake_queue(dev); /* Attention: under a spinlock. --SAW */ } - - sp->dirty_tx = dirty_tx; + spin_unlock(&sp->lock); } if (--boguscnt < 0) { @@ -1157,34 +1542,131 @@ static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs) dev->name, inw(ioaddr + SCBStatus)); dev->interrupt = 0; - spin_unlock(&sp->lock); + clear_bit(0, (void*)&sp->in_interrupt); return; } +static inline struct RxFD *speedo_rx_alloc(struct net_device *dev, int entry) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + struct RxFD *rxf; + struct sk_buff *skb; + /* Get a fresh skbuff to replace the consumed one. */ + skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD)); + sp->rx_skbuff[entry] = skb; + if (skb == NULL) { + sp->rx_ringp[entry] = NULL; + return NULL; + } + rxf = sp->rx_ringp[entry] = (struct RxFD *)skb->tail; + skb->dev = dev; + skb_reserve(skb, sizeof(struct RxFD)); + rxf->rx_buf_addr = virt_to_bus(skb->tail); + return rxf; +} + +static inline void speedo_rx_link(struct net_device *dev, int entry, + struct RxFD *rxf) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + rxf->status = cpu_to_le32(0xC0000001); /* '1' for driver use only. */ + rxf->link = 0; /* None yet. */ + rxf->count = cpu_to_le32(PKT_BUF_SZ << 16); + sp->last_rxf->link = virt_to_le32desc(rxf); + sp->last_rxf->status &= cpu_to_le32(~0xC0000000); + sp->last_rxf = rxf; +} + +static int speedo_refill_rx_buf(struct net_device *dev, int force) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int entry; + struct RxFD *rxf; + + entry = sp->dirty_rx % RX_RING_SIZE; + if (sp->rx_skbuff[entry] == NULL) { + rxf = speedo_rx_alloc(dev, entry); + if (rxf == NULL) { + unsigned int forw; + int forw_entry; + if (speedo_debug > 2 || !(sp->rx_ring_state & RrOOMReported)) { + printk(KERN_WARNING "%s: can't fill rx buffer (force %d)!\n", + dev->name, force); + speedo_show_state(dev); + sp->rx_ring_state |= RrOOMReported; + } + if (!force) + return -1; /* Better luck next time! */ + /* Borrow an skb from one of next entries. */ + for (forw = sp->dirty_rx + 1; forw != sp->cur_rx; forw++) + if (sp->rx_skbuff[forw % RX_RING_SIZE] != NULL) + break; + if (forw == sp->cur_rx) + return -1; + forw_entry = forw % RX_RING_SIZE; + sp->rx_skbuff[entry] = sp->rx_skbuff[forw_entry]; + sp->rx_skbuff[forw_entry] = NULL; + rxf = sp->rx_ringp[forw_entry]; + sp->rx_ringp[forw_entry] = NULL; + sp->rx_ringp[entry] = rxf; + } + } else { + rxf = sp->rx_ringp[entry]; + } + speedo_rx_link(dev, entry, rxf); + sp->dirty_rx++; + sp->rx_ring_state &= ~(RrNoMem|RrOOMReported); /* Mark the progress. */ + return 0; +} + +static void speedo_refill_rx_buffers(struct net_device *dev, int force) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + + /* Refill the RX ring. */ + while ((int)(sp->cur_rx - sp->dirty_rx) > 0 && + speedo_refill_rx_buf(dev, force) != -1); +} + static int -speedo_rx(struct device *dev) +speedo_rx(struct net_device *dev) { struct speedo_private *sp = (struct speedo_private *)dev->priv; int entry = sp->cur_rx % RX_RING_SIZE; int status; int rx_work_limit = sp->dirty_rx + RX_RING_SIZE - sp->cur_rx; + int alloc_ok = 1; if (speedo_debug > 4) printk(KERN_DEBUG " In speedo_rx().\n"); /* If we own the next entry, it's a new packet. Send it up. */ while (sp->rx_ringp[entry] != NULL && (status = le32_to_cpu(sp->rx_ringp[entry]->status)) & RxComplete) { + int pkt_len = le32_to_cpu(sp->rx_ringp[entry]->count) & 0x3fff; if (--rx_work_limit < 0) break; + + /* Check for a rare out-of-memory case: the current buffer is + the last buffer allocated in the RX ring. --SAW */ + if (sp->last_rxf == sp->rx_ringp[entry]) { + /* Postpone the packet. It'll be reaped at an interrupt when this + packet is no longer the last packet in the ring. */ + if (speedo_debug > 2) + printk(KERN_DEBUG "%s: RX packet postponed!\n", + dev->name); + sp->rx_ring_state |= RrPostponed; + break; + } + if (speedo_debug > 4) printk(KERN_DEBUG " speedo_rx() status %8.8x len %d.\n", status, - le16_to_cpu(sp->rx_ringp[entry]->count) & 0x3fff); - if ((status & (RxErrTooBig|RxOK)) != RxOK) { + pkt_len); + if ((status & (RxErrTooBig|RxOK|0x0f90)) != RxOK) { if (status & RxErrTooBig) printk(KERN_ERR "%s: Ethernet frame overran the Rx buffer, " "status %8.8x!\n", dev->name, status); - else if ( ! (status & 0x2000)) { + else if (! (status & RxOK)) { /* There was a fatal error. This *should* be impossible. */ sp->stats.rx_errors++; printk(KERN_ERR "%s: Anomalous event in speedo_rx(), " @@ -1192,7 +1674,6 @@ speedo_rx(struct device *dev) dev->name, status); } } else { - int pkt_len = le16_to_cpu(sp->rx_ringp[entry]->count) & 0x3fff; struct sk_buff *skb; /* Check if the packet is long enough to just accept without @@ -1204,13 +1685,11 @@ speedo_rx(struct device *dev) /* 'skb_put()' points to the start of sk_buff data area. */ #if 1 || USE_IP_CSUM /* Packet is in one chunk -- we can copy + cksum. */ - eth_copy_and_sum(skb, - bus_to_virt(le32_to_cpu(sp->rx_ringp[entry]->rx_buf_addr)), - pkt_len, 0); + eth_copy_and_sum(skb, sp->rx_skbuff[entry]->tail, pkt_len, 0); skb_put(skb, pkt_len); #else - memcpy(skb_put(skb, pkt_len), - bus_to_virt(le32_to_cpu(sp->rx_ringp[entry]->rx_buf_addr)), pkt_len); + memcpy(skb_put(skb, pkt_len), sp->rx_skbuff[entry]->tail, + pkt_len); #endif } else { void *temp; @@ -1223,64 +1702,44 @@ speedo_rx(struct device *dev) } sp->rx_skbuff[entry] = NULL; temp = skb_put(skb, pkt_len); - if (bus_to_virt(le32_to_cpu(sp->rx_ringp[entry]->rx_buf_addr)) != temp) + if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp) printk(KERN_ERR "%s: Rx consistency error -- the skbuff " "addresses do not match in speedo_rx: %p vs. %p " "/ %p.\n", dev->name, - bus_to_virt(le32_to_cpu(sp->rx_ringp[entry]->rx_buf_addr)), + bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), skb->head, temp); sp->rx_ringp[entry] = NULL; } skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); sp->stats.rx_packets++; - sp->stats.rx_bytes += pkt_len; /* Count received bytes */ + sp->stats.rx_bytes += pkt_len; } entry = (++sp->cur_rx) % RX_RING_SIZE; + sp->rx_ring_state &= ~RrPostponed; + /* Refill the recently taken buffers. + Do it one-by-one to handle traffic bursts better. */ + if (alloc_ok && speedo_refill_rx_buf(dev, 0) == -1) + alloc_ok = 0; } - /* Refill the Rx ring buffers. */ - for (; sp->dirty_rx < sp->cur_rx; sp->dirty_rx++) { - struct RxFD *rxf; - entry = sp->dirty_rx % RX_RING_SIZE; - if (sp->rx_skbuff[entry] == NULL) { - struct sk_buff *skb; - /* Get a fresh skbuff to replace the consumed one. */ - skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD)); - sp->rx_skbuff[entry] = skb; - if (skb == NULL) { - sp->rx_ringp[entry] = NULL; - break; /* Better luck next time! */ - } - rxf = sp->rx_ringp[entry] = (struct RxFD *)skb->tail; - skb->dev = dev; - skb_reserve(skb, sizeof(struct RxFD)); - rxf->rx_buf_addr = cpu_to_le32(virt_to_bus(skb->tail)); - } else { - rxf = sp->rx_ringp[entry]; - } - rxf->status = cpu_to_le32(0xC0000001); /* '1' for driver use only. */ - rxf->link = 0; /* None yet. */ - rxf->count = 0; - rxf->size = cpu_to_le16(PKT_BUF_SZ); - sp->last_rxf->link = cpu_to_le32(virt_to_bus(rxf)); - sp->last_rxf->status &= cpu_to_le32(~0xC0000000); - sp->last_rxf = rxf; - } + /* Try hard to refill the recently taken buffers. */ + speedo_refill_rx_buffers(dev, 1); sp->last_rx_time = jiffies; + return 0; } static int -speedo_close(struct device *dev) +speedo_close(struct net_device *dev) { long ioaddr = dev->base_addr; struct speedo_private *sp = (struct speedo_private *)dev->priv; int i; dev->start = 0; - dev->tbusy = 1; + netif_stop_queue(dev); if (speedo_debug > 1) printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n", @@ -1289,13 +1748,16 @@ speedo_close(struct device *dev) /* Shut off the media monitoring timer. */ del_timer(&sp->timer); - /* Disable interrupts, and stop the chip's Rx process. */ - outw(INT_MASK, ioaddr + SCBCmd); - outw(INT_MASK | RX_ABORT, ioaddr + SCBCmd); + /* Shutting down the chip nicely fails to disable flow control. So.. */ + outl(PortPartialReset, ioaddr + SCBPort); free_irq(dev->irq, dev); - /* Free all the skbuffs in the Rx and Tx queues. */ + /* Print a few items for debugging. */ + if (speedo_debug > 3) + speedo_show_state(dev); + + /* Free all the skbuffs in the Rx and Tx queues. */ for (i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb = sp->rx_skbuff[i]; sp->rx_skbuff[i] = 0; @@ -1311,28 +1773,22 @@ speedo_close(struct device *dev) if (skb) dev_free_skb(skb); } - if (sp->mc_setup_frm) { - kfree(sp->mc_setup_frm); - sp->mc_setup_frm_len = 0; - } - /* Print a few items for debugging. */ - if (speedo_debug > 3) { - int phy_num = sp->phy[0] & 0x1f; - printk(KERN_DEBUG "%s:Printing Rx ring (next to receive into %d).\n", - dev->name, sp->cur_rx); - - for (i = 0; i < RX_RING_SIZE; i++) - printk(KERN_DEBUG " Rx ring entry %d %8.8x.\n", - i, (int)le32_to_cpu(sp->rx_ringp[i]->status)); - - for (i = 0; i < 5; i++) - printk(KERN_DEBUG " PHY index %d register %d is %4.4x.\n", - phy_num, i, mdio_read(ioaddr, phy_num, i)); - for (i = 21; i < 26; i++) - printk(KERN_DEBUG " PHY index %d register %d is %4.4x.\n", - phy_num, i, mdio_read(ioaddr, phy_num, i)); + /* Free multicast setting blocks. */ + for (i = 0; sp->mc_setup_head != NULL; i++) { + struct speedo_mc_block *t; + t = sp->mc_setup_head->next; + kfree(sp->mc_setup_head); + sp->mc_setup_head = t; } + sp->mc_setup_tail = NULL; + if (speedo_debug > 0) + printk(KERN_DEBUG "%s: %d multicast blocks dropped.\n", dev->name, i); + +#if defined(HAS_PCI_NETIF) + /* Alt: acpi_set_pwr_state(pci_bus, pci_devfn, sp->acpi_pwr); */ + acpi_set_pwr_state(sp->pci_bus, sp->pci_devfn, ACPI_D2); +#endif MOD_DEC_USE_COUNT; return 0; @@ -1351,12 +1807,13 @@ speedo_close(struct device *dev) Oh, and incoming frames are dropped while executing dump-stats! */ static struct enet_statistics * -speedo_get_stats(struct device *dev) +speedo_get_stats(struct net_device *dev) { struct speedo_private *sp = (struct speedo_private *)dev->priv; long ioaddr = dev->base_addr; - if (le32_to_cpu(sp->lstats.done_marker) == 0xA007) { /* Previous dump finished */ + /* Update only if the previous dump finished. */ + if (sp->lstats.done_marker == le32_to_cpu(0xA007)) { sp->stats.tx_aborted_errors += le32_to_cpu(sp->lstats.tx_coll16_errs); sp->stats.tx_window_errors += le32_to_cpu(sp->lstats.tx_late_colls); sp->stats.tx_fifo_errors += le32_to_cpu(sp->lstats.tx_underruns); @@ -1370,30 +1827,50 @@ speedo_get_stats(struct device *dev) sp->stats.rx_length_errors += le32_to_cpu(sp->lstats.rx_runt_errs); sp->lstats.done_marker = 0x0000; if (dev->start) { + unsigned long flags; + /* Take a spinlock to make wait_for_cmd_done and sending the + * command atomic. --SAW */ + spin_lock_irqsave(&sp->lock, flags); wait_for_cmd_done(ioaddr + SCBCmd); - outw(CU_DUMPSTATS, ioaddr + SCBCmd); + outb(CUDumpStats, ioaddr + SCBCmd); + spin_unlock_irqrestore(&sp->lock, flags); } } return &sp->stats; } -static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd) +static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct speedo_private *sp = (struct speedo_private *)dev->priv; long ioaddr = dev->base_addr; u16 *data = (u16 *)&rq->ifr_data; int phy = sp->phy[0] & 0x1f; +#if defined(HAS_PCI_NETIF) + int saved_acpi; +#endif switch(cmd) { case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ data[0] = phy; case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ +#if defined(HAS_PCI_NETIF) + saved_acpi = acpi_set_pwr_state(sp->pci_bus, sp->pci_devfn, ACPI_D0); + data[3] = mdio_read(ioaddr, data[0], data[1]); + acpi_set_pwr_state(sp->pci_bus, sp->pci_devfn, saved_acpi); +#else data[3] = mdio_read(ioaddr, data[0], data[1]); +#endif return 0; case SIOCDEVPRIVATE+2: /* Write the specified MII register */ if (!capable(CAP_NET_ADMIN)) return -EPERM; +#if defined(HAS_PCI_NETIF) + saved_acpi = acpi_set_pwr_state(sp->pci_bus, sp->pci_devfn, ACPI_D0); + mdio_write(ioaddr, data[0], data[1], data[2]); + acpi_set_pwr_state(sp->pci_bus, sp->pci_devfn, saved_acpi); +#else mdio_write(ioaddr, data[0], data[1], data[2]); +#endif return 0; default: return -EOPNOTSUPP; @@ -1409,8 +1886,7 @@ static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd) loaded the link -- we convert the current command block, normally a Tx command, into a no-op and link it to the new command. */ -static void -set_rx_mode(struct device *dev) +static void set_rx_mode(struct net_device *dev) { struct speedo_private *sp = (struct speedo_private *)dev->priv; long ioaddr = dev->base_addr; @@ -1427,9 +1903,13 @@ set_rx_mode(struct device *dev) } else new_rx_mode = 0; - if (sp->cur_tx - sp->dirty_tx >= TX_RING_SIZE - 1) { - /* The Tx ring is full -- don't add anything! Presumably the new mode - is in config_cmd_data and will be added anyway. */ + if (speedo_debug > 3) + printk(KERN_DEBUG "%s: set_rx_mode %d -> %d\n", dev->name, + sp->rx_mode, new_rx_mode); + + if ((int)(sp->cur_tx - sp->dirty_tx) > TX_RING_SIZE - TX_MULTICAST_SIZE) { + /* The Tx ring is full -- don't add anything! Hope the mode will be + * set again later. */ sp->rx_mode = -1; return; } @@ -1443,9 +1923,9 @@ set_rx_mode(struct device *dev) sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; sp->tx_skbuff[entry] = 0; /* Redundant. */ - sp->tx_ring[entry].status = cpu_to_le32((CmdSuspend | CmdConfigure) << 16); + sp->tx_ring[entry].status = cpu_to_le32(CmdSuspend | CmdConfigure); sp->tx_ring[entry].link = - cpu_to_le32(virt_to_bus(&sp->tx_ring[(entry + 1) % TX_RING_SIZE])); + virt_to_le32desc(&sp->tx_ring[(entry + 1) % TX_RING_SIZE]); config_cmd_data = (void *)&sp->tx_ring[entry].tx_desc_addr; /* Construct a full CmdConfig frame. */ memcpy(config_cmd_data, i82558_config_cmd, sizeof(i82558_config_cmd)); @@ -1453,6 +1933,7 @@ set_rx_mode(struct device *dev) config_cmd_data[4] = rxdmacount; config_cmd_data[5] = txdmacount + 0x80; config_cmd_data[15] |= (new_rx_mode & 2) ? 1 : 0; + config_cmd_data[19] = sp->flow_ctrl ? 0xBD : 0x80; config_cmd_data[19] |= sp->full_duplex ? 0x40 : 0; config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05; if (sp->phy[0] & 0x8000) { /* Use the AUI port instead. */ @@ -1460,11 +1941,13 @@ set_rx_mode(struct device *dev) config_cmd_data[8] = 0; } /* Trigger the command unit resume. */ - last_cmd->command &= cpu_to_le16(~CmdSuspend); - wait_for_cmd_done(ioaddr + SCBCmd); - outw(CU_RESUME, ioaddr + SCBCmd); - + clear_suspend(last_cmd); + outb(CUResume, ioaddr + SCBCmd); + if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) { + netif_stop_queue(dev); + sp->tx_full = 1; + } spin_unlock_irqrestore(&sp->lock, flags); } @@ -1480,9 +1963,9 @@ set_rx_mode(struct device *dev) sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; sp->tx_skbuff[entry] = 0; - sp->tx_ring[entry].status = cpu_to_le32((CmdSuspend | CmdMulticastList) << 16); + sp->tx_ring[entry].status = cpu_to_le32(CmdSuspend | CmdMulticastList); sp->tx_ring[entry].link = - cpu_to_le32(virt_to_bus(&sp->tx_ring[(entry + 1) % TX_RING_SIZE])); + virt_to_le32desc(&sp->tx_ring[(entry + 1) % TX_RING_SIZE]); sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */ setup_params = (u16 *)&sp->tx_ring[entry].tx_desc_addr; *setup_params++ = cpu_to_le16(dev->mc_count*6); @@ -1495,48 +1978,40 @@ set_rx_mode(struct device *dev) *setup_params++ = *eaddrs++; } - last_cmd->command &= cpu_to_le16(~CmdSuspend); - - /* Immediately trigger the command unit resume. */ wait_for_cmd_done(ioaddr + SCBCmd); - outw(CU_RESUME, ioaddr + SCBCmd); + clear_suspend(last_cmd); + /* Immediately trigger the command unit resume. */ + outb(CUResume, ioaddr + SCBCmd); + if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) { + netif_stop_queue(dev); + sp->tx_full = 1; + } spin_unlock_irqrestore(&sp->lock, flags); } else if (new_rx_mode == 0) { struct dev_mc_list *mclist; u16 *setup_params, *eaddrs; - struct descriptor *mc_setup_frm = sp->mc_setup_frm; + struct speedo_mc_block *mc_blk; + struct descriptor *mc_setup_frm; int i; - if (sp->mc_setup_frm_len < 10 + dev->mc_count*6 - || sp->mc_setup_frm == NULL) { - /* Allocate a full setup frame, 10bytes + . */ - if (sp->mc_setup_frm) - kfree(sp->mc_setup_frm); - sp->mc_setup_busy = 0; - sp->mc_setup_frm_len = 10 + multicast_filter_limit*6; - sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len, GFP_ATOMIC); - if (sp->mc_setup_frm == NULL) { - printk(KERN_ERR "%s: Failed to allocate a setup frame.\n", - dev->name); - sp->rx_mode = -1; /* We failed, try again. */ - return; - } - } - /* If we are busy, someone might be quickly adding to the MC list. - Try again later when the list changes stop. */ - if (sp->mc_setup_busy) { - sp->rx_mode = -1; + mc_blk = kmalloc(sizeof(*mc_blk) + 2 + multicast_filter_limit*6, + GFP_ATOMIC); + if (mc_blk == NULL) { + printk(KERN_ERR "%s: Failed to allocate a setup frame.\n", + dev->name); + sp->rx_mode = -1; /* We failed, try again. */ return; } - mc_setup_frm = sp->mc_setup_frm; + mc_blk->next = NULL; + mc_setup_frm = &mc_blk->frame; + /* Fill the setup frame. */ if (speedo_debug > 1) - printk(KERN_DEBUG "%s: Constructing a setup frame at %p, " - "%d bytes.\n", - dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len); - mc_setup_frm->status = 0; - mc_setup_frm->command = cpu_to_le16(CmdSuspend | CmdIntr | CmdMulticastList); + printk(KERN_DEBUG "%s: Constructing a setup frame at %p.\n", + dev->name, mc_setup_frm); + mc_setup_frm->cmd_status = + cpu_to_le32(CmdSuspend | CmdIntr | CmdMulticastList); /* Link set below. */ setup_params = (u16 *)&mc_setup_frm->params; *setup_params++ = cpu_to_le16(dev->mc_count*6); @@ -1552,26 +2027,35 @@ set_rx_mode(struct device *dev) /* Disable interrupts while playing with the Tx Cmd list. */ spin_lock_irqsave(&sp->lock, flags); + if (sp->mc_setup_tail) + sp->mc_setup_tail->next = mc_blk; + else + sp->mc_setup_head = mc_blk; + sp->mc_setup_tail = mc_blk; + mc_blk->tx = sp->cur_tx; + entry = sp->cur_tx++ % TX_RING_SIZE; last_cmd = sp->last_cmd; sp->last_cmd = mc_setup_frm; - sp->mc_setup_busy++; /* Change the command to a NoOp, pointing to the CmdMulti command. */ sp->tx_skbuff[entry] = 0; - sp->tx_ring[entry].status = cpu_to_le32(CmdNOp << 16); - sp->tx_ring[entry].link = cpu_to_le32(virt_to_bus(mc_setup_frm)); + sp->tx_ring[entry].status = cpu_to_le32(CmdNOp); + sp->tx_ring[entry].link = virt_to_le32desc(mc_setup_frm); /* Set the link in the setup frame. */ mc_setup_frm->link = - cpu_to_le32(virt_to_bus(&(sp->tx_ring[(entry+1) % TX_RING_SIZE]))); - - last_cmd->command &= cpu_to_le16(~CmdSuspend); + virt_to_le32desc(&(sp->tx_ring[(entry+1) % TX_RING_SIZE])); - /* Immediately trigger the command unit resume. */ wait_for_cmd_done(ioaddr + SCBCmd); - outw(CU_RESUME, ioaddr + SCBCmd); + clear_suspend(last_cmd); + /* Immediately trigger the command unit resume. */ + outb(CUResume, ioaddr + SCBCmd); + if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) { + netif_stop_queue(dev); + sp->tx_full = 1; + } spin_unlock_irqrestore(&sp->lock, flags); if (speedo_debug > 5) @@ -1584,41 +2068,65 @@ set_rx_mode(struct device *dev) #ifdef MODULE -int -init_module(void) +int init_module(void) { int cards_found; + if (debug >= 0 && speedo_debug != debug) + printk(KERN_INFO "eepro100.c: Debug level is %d.\n", debug); if (debug >= 0) speedo_debug = debug; + /* Always emit the version message. */ if (speedo_debug) printk(KERN_INFO "%s", version); - root_speedo_dev = NULL; - cards_found = eepro100_init(NULL); - return cards_found ? 0 : -ENODEV; +#if defined(HAS_PCI_NETIF) + cards_found = netif_pci_probe(pci_tbl); + if (cards_found < 0) + printk(KERN_INFO "eepro100: No cards found, driver not installed.\n"); + return cards_found; +#else + cards_found = eepro100_init(); + if (cards_found <= 0) { + printk(KERN_INFO "eepro100: No cards found, driver not installed.\n"); + return -ENODEV; + } +#endif + return 0; } void cleanup_module(void) { - struct device *next_dev; + struct net_device *next_dev; /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (root_speedo_dev) { - next_dev = ((struct speedo_private *)root_speedo_dev->priv)->next_module; + struct speedo_private *sp = (void *)root_speedo_dev->priv; unregister_netdev(root_speedo_dev); +#ifdef USE_IO release_region(root_speedo_dev->base_addr, SPEEDO3_TOTAL_SIZE); +#else + iounmap((char *)root_speedo_dev->base_addr); +#endif +#if defined(HAS_PCI_NETIF) + acpi_set_pwr_state(sp->pci_bus, sp->pci_devfn, sp->acpi_pwr); +#endif + next_dev = sp->next_module; + if (sp->priv_addr) + kfree(sp->priv_addr); kfree(root_speedo_dev); root_speedo_dev = next_dev; } } + #else /* not MODULE */ -int eepro100_probe(struct device *dev) + +int eepro100_probe(void) { int cards_found = 0; - cards_found = eepro100_init(dev); + cards_found = eepro100_init(); if (speedo_debug > 0 && cards_found) printk(version); diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 4ba5acd76434..0652a3c726c3 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -45,6 +45,8 @@ struct _raw3215_info; /* forward declaration ... */ +int raw3215_condevice = -1; /* preset console device */ + /* * Request types for a 3215 device */ @@ -97,6 +99,16 @@ static int tty3215_refcount; #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif +__initfunc(void con3215_setup(char *str, char *ints)) +{ + int vdev; + + vdev = simple_strtoul(str,&str,16); + if (vdev >= 0 && vdev < 65536) + raw3215_condevice = vdev; + return; +} + /* * Get a request structure from the free list */ @@ -365,7 +377,7 @@ static void raw3215_irq(int irq, void *int_parm, struct pt_regs *regs) struct tty_struct *tty; devstat_t *stat; int cstat, dstat; - int count; + int count, slen; stat = (devstat_t *) int_parm; req = (raw3215_req *) stat->intparm; @@ -384,6 +396,7 @@ static void raw3215_irq(int irq, void *int_parm, struct pt_regs *regs) } } if (dstat & 0x01) { /* we got a unit exception */ +#if 0 raw = raw3215_find_info(irq); if (raw != NULL) { raw->message = KERN_WARNING @@ -394,7 +407,8 @@ static void raw3215_irq(int irq, void *int_parm, struct pt_regs *regs) queue_task(&raw->tqueue, &tq_immediate); mark_bh(IMMEDIATE_BH); } - dstat &= ~0x01; +#endif + dstat &= ~0x01; /* we can ignore it */ } switch (dstat) { case 0x80: @@ -407,6 +421,8 @@ static void raw3215_irq(int irq, void *int_parm, struct pt_regs *regs) req = raw3215_mk_read_req(raw); raw3215_mk_read_ccw(raw, req); raw->queued_read = req; + if (MACHINE_IS_P390) + memset(raw->inbuf, 0, RAW3215_INBUF_SIZE); queue_task(&raw->tqueue, &tq_immediate); mark_bh(IMMEDIATE_BH); break; @@ -426,6 +442,11 @@ static void raw3215_irq(int irq, void *int_parm, struct pt_regs *regs) if (req->type == RAW3215_READ && raw->tty != NULL) { tty = raw->tty; count = 160 - req->residual; + if (MACHINE_IS_P390) { + slen = strnlen(raw->inbuf, RAW3215_INBUF_SIZE); + if (count > slen) + count = slen; + } else if (count >= TTY_FLIPBUF_SIZE - tty->flip.count) count = TTY_FLIPBUF_SIZE - tty->flip.count - 1; EBCASC(raw->inbuf, count); @@ -690,7 +711,8 @@ raw3215_find_dev(int number) irq = 0; count = 0; while (count <= number && get_dev_info(irq, &dinfo) != -ENODEV) { - if (dinfo.sid_data.cu_type == 0x3215) + if (dinfo.devno == raw3215_condevice || + dinfo.sid_data.cu_type == 0x3215) count++; irq++; } @@ -710,7 +732,7 @@ int con3215_activate(void) { raw3215_info *raw; - if (!MACHINE_IS_VM) + if (!MACHINE_IS_VM && !MACHINE_IS_P390) return 0; raw = raw3215[0]; /* 3215 console is the first one */ if (raw->irq == -1) /* now console device found in con3215_init */ @@ -761,7 +783,7 @@ void con3215_unblank(void) s390irq_spin_unlock_irqrestore(raw->irq, flags); } -__initfunc(static int con3215_setup(struct console *co, char *options)) +__initfunc(static int con3215_consetup(struct console *co, char *options)) { return 0; } @@ -776,7 +798,7 @@ static struct console con3215 = { con3215_device, NULL, con3215_unblank, - con3215_setup, + con3215_consetup, CON_PRINTBUFFER, 0, 0, @@ -1008,12 +1030,14 @@ __initfunc (long con3215_init(long kmem_start, long kmem_end)) raw3215_req *req; int i; - if (!MACHINE_IS_VM) + if (!MACHINE_IS_VM && !MACHINE_IS_P390) return kmem_start; + if (MACHINE_IS_VM) { cpcmd("TERM CONMODE 3215", NULL, 0); cpcmd("TERM AUTOCR OFF", NULL, 0); cpcmd("TERM HOLD OFF", NULL, 0); cpcmd("TERM MORE 5 5", NULL, 0); + } kmem_start = (kmem_start + 7) & -8L; diff --git a/drivers/s390/char/hwc_con.c b/drivers/s390/char/hwc_con.c index b3ff88041820..0ec9d9d5397a 100644 --- a/drivers/s390/char/hwc_con.c +++ b/drivers/s390/char/hwc_con.c @@ -72,6 +72,8 @@ kdev_t hwc_console_device(struct console *c) __initfunc(unsigned long hwc_console_init(unsigned long kmem_start)) { + if (MACHINE_IS_P390) + return kmem_start; #ifdef CONFIG_3215 if (MACHINE_IS_VM) return kmem_start; diff --git a/drivers/s390/net/iucv.c b/drivers/s390/net/iucv.c index d871b3007014..72e0cf996637 100644 --- a/drivers/s390/net/iucv.c +++ b/drivers/s390/net/iucv.c @@ -596,8 +596,8 @@ void do_iucv_interrupt(void) #ifdef DEBUG printk( "iucv: transaction complete now.\n"); #endif + iucv_next = *((unsigned short*)rcvptr); rcvptr = rcvptr + iucv_data_len; - iucv_next = iucv_data_len; /* get next packet offset */ iucv_data_len= *((unsigned short*)rcvptr); @@ -791,7 +791,9 @@ int iucv_config(struct device *dev, struct ifmap *map) /*----------------*/ int iucv_ioctl(struct device *dev, struct ifreq *rq, int cmd) { +#ifdef DEBUG printk( "iucv: device %s; iucv_ioctl\n",dev->name); +#endif return 0; } diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 4d0f92f68c0e..3487058891b0 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -303,6 +303,7 @@ static struct dev_info device_list[] = {"iomega","jaz 1GB","J.86", BLIST_NOTQ | BLIST_NOLUN}, {"CREATIVE","DVD-RAM RAM","*", BLIST_GHOST}, {"MATSHITA","PD-2 LF-D100","*", BLIST_GHOST}, +{"AOpen","PD-2 DVD-520S","*", BLIST_GHOST}, {"HITACHI","GF-1050","*", BLIST_GHOST}, /* Hitachi SCSI DVD-RAM */ {"TOSHIBA","CDROM","*", BLIST_ISROM}, {"TOSHIBA","DVD-RAM SD-W1101","*", BLIST_GHOST}, diff --git a/drivers/sound/es1370.c b/drivers/sound/es1370.c index 4b72ef1bf8c4..ed9d9efd766f 100644 --- a/drivers/sound/es1370.c +++ b/drivers/sound/es1370.c @@ -3,7 +3,7 @@ /* * es1370.c -- Ensoniq ES1370/Asahi Kasei AK4531 audio driver. * - * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1998-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,66 +52,68 @@ * there are several MIDI to PCM (WAV) packages, one of them is timidity. * * Revision history - * 26.03.98 0.1 Initial release - * 31.03.98 0.2 Fix bug in GETOSPACE - * 04.04.98 0.3 Make it work (again) under 2.0.33 - * Fix mixer write operation not returning the actual - * settings - * 05.04.98 0.4 First attempt at using the new PCI stuff - * 29.04.98 0.5 Fix hang when ^C is pressed on amp - * 07.05.98 0.6 Don't double lock around stop_*() in *_release() - * 10.05.98 0.7 First stab at a simple midi interface (no bells&whistles) - * 14.05.98 0.8 Don't allow excessive interrupt rates - * 08.06.98 0.9 First release using Alan Cox' soundcore instead of - * miscdevice - * 05.07.98 0.10 Fixed the driver to correctly maintin OSS style volume - * settings (not sure if this should be standard) - * Fixed many references: f_flags should be f_mode - * -- Gerald Britton - * 03.08.98 0.11 Now mixer behaviour can basically be selected between - * "OSS documented" and "OSS actual" behaviour - * Fixed mixer table thanks to Hakan.Lennestal@lu.erisoft.se - * On module startup, set DAC2 to 11kSPS instead of 5.5kSPS, - * as it produces an annoying ssssh in the lower sampling rate - * Do not include modversions.h - * 22.08.98 0.12 Mixer registers actually have 5 instead of 4 bits - * pointed out by Itai Nahshon - * 31.08.98 0.13 Fix realplayer problems - dac.count issues - * 08.10.98 0.14 Joystick support fixed - * -- Oliver Neukum - * 10.12.98 0.15 Fix drain_dac trying to wait on not yet initialized DMA - * 16.12.98 0.16 Don't wake up app until there are fragsize bytes to read/write - * 06.01.99 0.17 remove the silly SA_INTERRUPT flag. - * hopefully killed the egcs section type conflict - * 12.03.99 0.18 cinfo.blocks should be reset after GETxPTR ioctl. - * reported by Johan Maes - * 22.03.99 0.19 return EAGAIN instead of EBUSY when O_NONBLOCK - * read/write cannot be executed - * 07.04.99 0.20 implemented the following ioctl's: SOUND_PCM_READ_RATE, - * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; - * Alpha fixes reported by Peter Jones - * Note: joystick address handling might still be wrong on archs - * other than i386 - * 10.05.99 0.21 Added support for an electret mic for SB PCI64 - * to the Linux kernel sound driver. This mod also straighten - * out the question marks around the mic impedance setting - * (micz). From Kim.Berts@fisub.mail.abb.com - * 11.05.99 0.22 Implemented the IMIX call to mute recording monitor. - * Guenter Geiger - * 15.06.99 0.23 Fix bad allocation bug. - * Thanks to Deti Fliegl - * 28.06.99 0.24 Add pci_set_master - * 02.08.99 0.25 Added workaround for the "phantom write" bug first - * documented by Dave Sharpless from Anchor Games - * 03.08.99 0.26 adapt to Linus' new __setup/__initcall - * added kernel command line option "es1370=joystick[,lineout[,micbias]]" - * removed CONFIG_SOUND_ES1370_JOYPORT_BOOT kludge - * 12.08.99 0.27 module_init/__setup fixes - * 19.08.99 0.28 SOUND_MIXER_IMIX fixes, reported by Gianluca - * 31.08.99 0.29 add spin_lock_init - * __initlocaldata to fix gcc 2.7.x problems - * 03.09.99 0.30 change read semantics for MIDI to match - * OSS more closely; remove possible wakeup race + * 26.03.1998 0.1 Initial release + * 31.03.1998 0.2 Fix bug in GETOSPACE + * 04.04.1998 0.3 Make it work (again) under 2.0.33 + * Fix mixer write operation not returning the actual + * settings + * 05.04.1998 0.4 First attempt at using the new PCI stuff + * 29.04.1998 0.5 Fix hang when ^C is pressed on amp + * 07.05.1998 0.6 Don't double lock around stop_*() in *_release() + * 10.05.1998 0.7 First stab at a simple midi interface (no bells&whistles) + * 14.05.1998 0.8 Don't allow excessive interrupt rates + * 08.06.1998 0.9 First release using Alan Cox' soundcore instead of + * miscdevice + * 05.07.1998 0.10 Fixed the driver to correctly maintin OSS style volume + * settings (not sure if this should be standard) + * Fixed many references: f_flags should be f_mode + * -- Gerald Britton + * 03.08.1998 0.11 Now mixer behaviour can basically be selected between + * "OSS documented" and "OSS actual" behaviour + * Fixed mixer table thanks to Hakan.Lennestal@lu.erisoft.se + * On module startup, set DAC2 to 11kSPS instead of 5.5kSPS, + * as it produces an annoying ssssh in the lower sampling rate + * Do not include modversions.h + * 22.08.1998 0.12 Mixer registers actually have 5 instead of 4 bits + * pointed out by Itai Nahshon + * 31.08.1998 0.13 Fix realplayer problems - dac.count issues + * 08.10.1998 0.14 Joystick support fixed + * -- Oliver Neukum + * 10.12.1998 0.15 Fix drain_dac trying to wait on not yet initialized DMA + * 16.12.1998 0.16 Don't wake up app until there are fragsize bytes to read/write + * 06.01.1999 0.17 remove the silly SA_INTERRUPT flag. + * hopefully killed the egcs section type conflict + * 12.03.1999 0.18 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.1999 0.19 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 07.04.1999 0.20 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * Note: joystick address handling might still be wrong on archs + * other than i386 + * 10.05.1999 0.21 Added support for an electret mic for SB PCI64 + * to the Linux kernel sound driver. This mod also straighten + * out the question marks around the mic impedance setting + * (micz). From Kim.Berts@fisub.mail.abb.com + * 11.05.1999 0.22 Implemented the IMIX call to mute recording monitor. + * Guenter Geiger + * 15.06.1999 0.23 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.24 Add pci_set_master + * 02.08.1999 0.25 Added workaround for the "phantom write" bug first + * documented by Dave Sharpless from Anchor Games + * 03.08.1999 0.26 adapt to Linus' new __setup/__initcall + * added kernel command line option "es1370=joystick[,lineout[,micbias]]" + * removed CONFIG_SOUND_ES1370_JOYPORT_BOOT kludge + * 12.08.1999 0.27 module_init/__setup fixes + * 19.08.1999 0.28 SOUND_MIXER_IMIX fixes, reported by Gianluca + * 31.08.1999 0.29 add spin_lock_init + * __initlocaldata to fix gcc 2.7.x problems + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.30 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 28.10.1999 0.31 More waitqueue races fixed * * some important things missing in Ensoniq documentation: * @@ -160,6 +162,26 @@ /* --------------------------------------------------------------------- */ +#ifdef MODULE +#define __exit +#define module_exit(x) void cleanup_module(void) { x(); } +#define module_init(x) int init_module(void) { return x(); } +#else +#define __exit __attribute__ ((unused, __section__ (".text.init"))) +#define module_exit(x) /* nothing */ +#define module_init(x) /* nothing */ +#endif + +#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL +#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL} +#define wait_queue_head_t struct wait_queue * +#define init_waitqueue_head(w) *(w) = 0 +#define init_MUTEX(m) *(m) = MUTEX +#define __set_current_state(x) do { current->state = (x); } while (0) +#define set_current_state(x) __set_current_state(x) + +/* --------------------------------------------------------------------- */ + #ifndef PCI_VENDOR_ID_ENSONIQ #define PCI_VENDOR_ID_ENSONIQ 0x1274 #endif @@ -332,7 +354,7 @@ struct es1370_state { spinlock_t lock; struct semaphore open_sem; mode_t open_mode; - struct wait_queue *open_wait; + wait_queue_head_t open_wait; struct dmabuf { void *rawbuf; @@ -343,7 +365,7 @@ struct es1370_state { unsigned total_bytes; int count; unsigned error; /* over/underrun */ - struct wait_queue *wait; + wait_queue_head_t wait; /* redundant, but makes calculations easier */ unsigned fragsize; unsigned dmasize; @@ -361,8 +383,8 @@ struct es1370_state { struct { unsigned ird, iwr, icnt; unsigned ord, owr, ocnt; - struct wait_queue *iwait; - struct wait_queue *owait; + wait_queue_head_t iwait; + wait_queue_head_t owait; unsigned char ibuf[MIDIINBUF]; unsigned char obuf[MIDIOUTBUF]; } midi; @@ -1057,15 +1079,15 @@ static /*const*/ struct file_operations es1370_mixer_fops = { static int drain_dac1(struct es1370_state *s, int nonblock) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); unsigned long flags; int count, tmo; if (s->dma_dac1.mapped || !s->dma_dac1.ready) return 0; - current->state = TASK_INTERRUPTIBLE; add_wait_queue(&s->dma_dac1.wait, &wait); for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); count = s->dma_dac1.count; spin_unlock_irqrestore(&s->lock, flags); @@ -1075,7 +1097,7 @@ static int drain_dac1(struct es1370_state *s, int nonblock) break; if (nonblock) { remove_wait_queue(&s->dma_dac1.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 @@ -1085,7 +1107,7 @@ static int drain_dac1(struct es1370_state *s, int nonblock) DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) } remove_wait_queue(&s->dma_dac1.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; return 0; @@ -1093,15 +1115,15 @@ static int drain_dac1(struct es1370_state *s, int nonblock) static int drain_dac2(struct es1370_state *s, int nonblock) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); unsigned long flags; int count, tmo; if (s->dma_dac2.mapped || !s->dma_dac2.ready) return 0; - current->state = TASK_INTERRUPTIBLE; add_wait_queue(&s->dma_dac2.wait, &wait); for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); count = s->dma_dac2.count; spin_unlock_irqrestore(&s->lock, flags); @@ -1111,7 +1133,7 @@ static int drain_dac2(struct es1370_state *s, int nonblock) break; if (nonblock) { remove_wait_queue(&s->dma_dac2.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 @@ -1121,7 +1143,7 @@ static int drain_dac2(struct es1370_state *s, int nonblock) DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) } remove_wait_queue(&s->dma_dac2.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; return 0; @@ -1132,6 +1154,7 @@ static int drain_dac2(struct es1370_state *s, int nonblock) static ssize_t es1370_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned swptr; @@ -1147,26 +1170,38 @@ static ssize_t es1370_read(struct file *file, char *buffer, size_t count, loff_t if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; ret = 0; + add_wait_queue(&s->dma_adc.wait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); swptr = s->dma_adc.swptr; cnt = s->dma_adc.dmasize-swptr; if (s->dma_adc.count < cnt) cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { start_adc(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->dma_adc.wait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) - return ret ? ret : -EFAULT; + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } swptr = (swptr + cnt) % s->dma_adc.dmasize; spin_lock_irqsave(&s->lock, flags); s->dma_adc.swptr = swptr; @@ -1177,12 +1212,15 @@ static ssize_t es1370_read(struct file *file, char *buffer, size_t count, loff_t ret += cnt; start_adc(s); } + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); return ret; } static ssize_t es1370_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned swptr; @@ -1198,6 +1236,7 @@ static ssize_t es1370_write(struct file *file, const char *buffer, size_t count, if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; ret = 0; + add_wait_queue(&s->dma_dac2.wait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); if (s->dma_dac2.count < 0) { @@ -1208,20 +1247,31 @@ static ssize_t es1370_write(struct file *file, const char *buffer, size_t count, cnt = s->dma_dac2.dmasize-swptr; if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize) cnt = s->dma_dac2.dmasize - s->dma_dac2.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { start_dac2(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->dma_dac2.wait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } swptr = (swptr + cnt) % s->dma_dac2.dmasize; spin_lock_irqsave(&s->lock, flags); s->dma_dac2.swptr = swptr; @@ -1233,6 +1283,8 @@ static ssize_t es1370_write(struct file *file, const char *buffer, size_t count, ret += cnt; start_dac2(s); } + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); return ret; } @@ -1607,6 +1659,7 @@ static int es1370_ioctl(struct inode *inode, struct file *file, unsigned int cmd static int es1370_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); struct es1370_state *s = devs; unsigned long flags; @@ -1623,8 +1676,12 @@ static int es1370_open(struct inode *inode, struct file *file) up(&s->open_sem); return -EBUSY; } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; down(&s->open_sem); @@ -1675,8 +1732,8 @@ static int es1370_release(struct inode *inode, struct file *file) dealloc_dmabuf(&s->dma_adc); } s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - up(&s->open_sem); wake_up(&s->open_wait); + up(&s->open_sem); MOD_DEC_USE_COUNT; return 0; } @@ -1704,6 +1761,7 @@ static /*const*/ struct file_operations es1370_audio_fops = { static ssize_t es1370_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); ssize_t ret = 0; unsigned long flags; unsigned swptr; @@ -1718,6 +1776,7 @@ static ssize_t es1370_write_dac(struct file *file, const char *buffer, size_t co return ret; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; + add_wait_queue(&s->dma_dac1.wait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); if (s->dma_dac1.count < 0) { @@ -1728,20 +1787,31 @@ static ssize_t es1370_write_dac(struct file *file, const char *buffer, size_t co cnt = s->dma_dac1.dmasize-swptr; if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize) cnt = s->dma_dac1.dmasize - s->dma_dac1.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { start_dac1(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->dma_dac1.wait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } swptr = (swptr + cnt) % s->dma_dac1.dmasize; spin_lock_irqsave(&s->lock, flags); s->dma_dac1.swptr = swptr; @@ -1753,6 +1823,8 @@ static ssize_t es1370_write_dac(struct file *file, const char *buffer, size_t co ret += cnt; start_dac1(s); } + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); return ret; } @@ -1990,6 +2062,7 @@ static int es1370_ioctl_dac(struct inode *inode, struct file *file, unsigned int static int es1370_open_dac(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); struct es1370_state *s = devs; unsigned long flags; @@ -2013,8 +2086,12 @@ static int es1370_open_dac(struct inode *inode, struct file *file) up(&s->open_sem); return -EBUSY; } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; down(&s->open_sem); @@ -2046,8 +2123,8 @@ static int es1370_release_dac(struct inode *inode, struct file *file) stop_dac1(s); dealloc_dmabuf(&s->dma_dac1); s->open_mode &= ~FMODE_DAC; - up(&s->open_sem); wake_up(&s->open_wait); + up(&s->open_sem); MOD_DEC_USE_COUNT; return 0; } @@ -2075,7 +2152,7 @@ static /*const*/ struct file_operations es1370_dac_fops = { static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct es1370_state *s = (struct es1370_state *)file->private_data; - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned ptr; @@ -2096,6 +2173,8 @@ static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, l cnt = MIDIINBUF - ptr; if (s->midi.icnt < cnt) cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; @@ -2105,7 +2184,6 @@ static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, l ret = -EAGAIN; break; } - current->state = TASK_INTERRUPTIBLE; schedule(); if (signal_pending(current)) { if (!ret) @@ -2129,7 +2207,7 @@ static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, l ret += cnt; break; } - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); remove_wait_queue(&s->midi.iwait, &wait); return ret; } @@ -2137,7 +2215,7 @@ static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, l static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct es1370_state *s = (struct es1370_state *)file->private_data; - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned ptr; @@ -2158,8 +2236,10 @@ static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t c cnt = MIDIOUTBUF - ptr; if (s->midi.ocnt + cnt > MIDIOUTBUF) cnt = MIDIOUTBUF - s->midi.ocnt; - if (cnt <= 0) + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); es1370_handle_midi(s); + } spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; @@ -2169,7 +2249,6 @@ static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t c ret = -EAGAIN; break; } - current->state = TASK_INTERRUPTIBLE; schedule(); if (signal_pending(current)) { if (!ret) @@ -2195,7 +2274,7 @@ static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t c es1370_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); } - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); remove_wait_queue(&s->midi.owait, &wait); return ret; } @@ -2227,6 +2306,7 @@ static unsigned int es1370_midi_poll(struct file *file, struct poll_table_struct static int es1370_midi_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); struct es1370_state *s = devs; unsigned long flags; @@ -2243,8 +2323,12 @@ static int es1370_midi_open(struct inode *inode, struct file *file) up(&s->open_sem); return -EBUSY; } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; down(&s->open_sem); @@ -2276,16 +2360,16 @@ static int es1370_midi_open(struct inode *inode, struct file *file) static int es1370_midi_release(struct inode *inode, struct file *file) { struct es1370_state *s = (struct es1370_state *)file->private_data; - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); unsigned long flags; unsigned count, tmo; VALIDATE_STATE(s); if (file->f_mode & FMODE_WRITE) { - current->state = TASK_INTERRUPTIBLE; add_wait_queue(&s->midi.owait, &wait); for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); count = s->midi.ocnt; spin_unlock_irqrestore(&s->lock, flags); @@ -2295,7 +2379,7 @@ static int es1370_midi_release(struct inode *inode, struct file *file) break; if (file->f_flags & O_NONBLOCK) { remove_wait_queue(&s->midi.owait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } tmo = (count * HZ) / 3100; @@ -2303,7 +2387,7 @@ static int es1370_midi_release(struct inode *inode, struct file *file) DBG(printk(KERN_DEBUG "es1370: midi timed out??\n");) } remove_wait_queue(&s->midi.owait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); } down(&s->open_sem); s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); @@ -2313,8 +2397,8 @@ static int es1370_midi_release(struct inode *inode, struct file *file) outl(s->ctrl, s->io+ES1370_REG_CONTROL); } spin_unlock_irqrestore(&s->lock, flags); - up(&s->open_sem); wake_up(&s->open_wait); + up(&s->open_sem); MOD_DEC_USE_COUNT; return 0; } @@ -2380,7 +2464,7 @@ __initfunc(int init_es1370(void)) if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1370: version v0.30 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "es1370: version v0.31 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) { if (pcidev->base_address[0] == 0 || @@ -2393,13 +2477,13 @@ __initfunc(int init_es1370(void)) continue; } memset(s, 0, sizeof(struct es1370_state)); - init_waitqueue(&s->dma_adc.wait); - init_waitqueue(&s->dma_dac1.wait); - init_waitqueue(&s->dma_dac2.wait); - init_waitqueue(&s->open_wait); - init_waitqueue(&s->midi.iwait); - init_waitqueue(&s->midi.owait); - s->open_sem = MUTEX; + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac1.wait); + init_waitqueue_head(&s->dma_dac2.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); spin_lock_init(&s->lock); s->magic = ES1370_MAGIC; s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; diff --git a/drivers/sound/es1371.c b/drivers/sound/es1371.c index 68c3a34af10d..e98751bbe7d3 100644 --- a/drivers/sound/es1371.c +++ b/drivers/sound/es1371.c @@ -3,7 +3,7 @@ /* * es1371.c -- Creative Ensoniq ES1371. * - * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1998-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,57 +38,64 @@ * there are several MIDI to PCM (WAV) packages, one of them is timidity. * * Revision history - * 04.06.98 0.1 Initial release - * Mixer stuff should be overhauled; especially optional AC97 mixer bits - * should be detected. This results in strange behaviour of some mixer - * settings, like master volume and mic. - * 08.06.98 0.2 First release using Alan Cox' soundcore instead of miscdevice - * 03.08.98 0.3 Do not include modversions.h - * Now mixer behaviour can basically be selected between - * "OSS documented" and "OSS actual" behaviour - * 31.08.98 0.4 Fix realplayer problems - dac.count issues - * 27.10.98 0.5 Fix joystick support - * -- Oliver Neukum (c188@org.chemie.uni-muenchen.de) - * 10.12.98 0.6 Fix drain_dac trying to wait on not yet initialized DMA - * 23.12.98 0.7 Fix a few f_file & FMODE_ bugs - * Don't wake up app until there are fragsize bytes to read/write - * 06.01.99 0.8 remove the silly SA_INTERRUPT flag. - * hopefully killed the egcs section type conflict - * 12.03.99 0.9 cinfo.blocks should be reset after GETxPTR ioctl. - * reported by Johan Maes - * 22.03.99 0.10 return EAGAIN instead of EBUSY when O_NONBLOCK - * read/write cannot be executed - * 07.04.99 0.11 implemented the following ioctl's: SOUND_PCM_READ_RATE, - * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; - * Alpha fixes reported by Peter Jones - * Another Alpha fix (wait_src_ready in init routine) - * reported by "Ivan N. Kokshaysky" - * Note: joystick address handling might still be wrong on archs - * other than i386 - * 15.06.99 0.12 Fix bad allocation bug. - * Thanks to Deti Fliegl - * 28.06.99 0.13 Add pci_set_master - * 03.08.99 0.14 adapt to Linus' new __setup/__initcall - * added kernel command line option "es1371=joystickaddr" - * removed CONFIG_SOUND_ES1371_JOYPORT_BOOT kludge - * 10.08.99 0.15 (Re)added S/PDIF module option for cards revision >= 4. - * Initial version by Dave Platt . - * module_init/__setup fixes - * 08.16.99 0.16 Joe Cotellese - * Added detection for ES1371 revision ID so that we can - * detect the ES1373 and later parts. - * added AC97 #defines for readability - * added a /proc file system for dumping hardware state - * updated SRC and CODEC w/r functions to accomodate bugs - * in some versions of the ES137x chips. - * 31.08.99 0.17 add spin_lock_init - * __initlocaldata to fix gcc 2.7.x problems - * replaced current->state = x with set_current_state(x) - * 03.09.99 0.18 change read semantics for MIDI to match - * OSS more closely; remove possible wakeup race - * 21.10.99 0.19 Round sampling rates, requested by - * Kasamatsu Kenichi - * + * 04.06.1998 0.1 Initial release + * Mixer stuff should be overhauled; especially optional AC97 mixer bits + * should be detected. This results in strange behaviour of some mixer + * settings, like master volume and mic. + * 08.06.1998 0.2 First release using Alan Cox' soundcore instead of miscdevice + * 03.08.1998 0.3 Do not include modversions.h + * Now mixer behaviour can basically be selected between + * "OSS documented" and "OSS actual" behaviour + * 31.08.1998 0.4 Fix realplayer problems - dac.count issues + * 27.10.1998 0.5 Fix joystick support + * -- Oliver Neukum (c188@org.chemie.uni-muenchen.de) + * 10.12.1998 0.6 Fix drain_dac trying to wait on not yet initialized DMA + * 23.12.1998 0.7 Fix a few f_file & FMODE_ bugs + * Don't wake up app until there are fragsize bytes to read/write + * 06.01.1999 0.8 remove the silly SA_INTERRUPT flag. + * hopefully killed the egcs section type conflict + * 12.03.1999 0.9 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.1999 0.10 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 07.04.1999 0.11 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * Another Alpha fix (wait_src_ready in init routine) + * reported by "Ivan N. Kokshaysky" + * Note: joystick address handling might still be wrong on archs + * other than i386 + * 15.06.1999 0.12 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.13 Add pci_set_master + * 03.08.1999 0.14 adapt to Linus' new __setup/__initcall + * added kernel command line option "es1371=joystickaddr" + * removed CONFIG_SOUND_ES1371_JOYPORT_BOOT kludge + * 10.08.1999 0.15 (Re)added S/PDIF module option for cards revision >= 4. + * Initial version by Dave Platt . + * module_init/__setup fixes + * 08.16.1999 0.16 Joe Cotellese + * Added detection for ES1371 revision ID so that we can + * detect the ES1373 and later parts. + * added AC97 #defines for readability + * added a /proc file system for dumping hardware state + * updated SRC and CODEC w/r functions to accomodate bugs + * in some versions of the ES137x chips. + * 31.08.1999 0.17 add spin_lock_init + * __initlocaldata to fix gcc 2.7.x problems + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.18 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 21.10.1999 0.19 Round sampling rates, requested by + * Kasamatsu Kenichi + * 27.10.1999 0.20 Added SigmaTel 3D enhancement string + * Codec ID printing changes + * 28.10.1999 0.21 More waitqueue races fixed + * Joe Cotellese + * Changed PCI detection routine so we can more easily + * detect ES137x chip and derivatives. + * 05.01.2000 0.22 Should now work with rev7 boards; patch by + * Eric Lemar, elemar@cs.washington.edu */ /*****************************************************************************/ @@ -118,16 +125,51 @@ #undef OSS_DOCUMENTED_MIXER_SEMANTICS #undef ES1371_DEBUG +#define DBG(x) {} +/*#define DBG(x) {x}*/ + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE +#define __exit +#define module_exit(x) void cleanup_module(void) { x(); } +#define module_init(x) int init_module(void) { return x(); } +#else +#define __exit __attribute__ ((unused, __section__ (".text.init"))) +#define module_exit(x) /* nothing */ +#define module_init(x) /* nothing */ +#endif + +#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL +#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL} +#define wait_queue_head_t struct wait_queue * +#define init_waitqueue_head(w) *(w) = 0 +#define init_MUTEX(m) *(m) = MUTEX +#define __set_current_state(x) do { current->state = (x); } while (0) +#define set_current_state(x) __set_current_state(x) /* --------------------------------------------------------------------- */ #ifndef PCI_VENDOR_ID_ENSONIQ #define PCI_VENDOR_ID_ENSONIQ 0x1274 #endif + +#ifndef PCI_VENDOR_ID_ECTIVA +#define PCI_VENDOR_ID_ECTIVA 0x1102 +#endif + #ifndef PCI_DEVICE_ID_ENSONIQ_ES1371 #define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 #endif +#ifndef PCI_DEVICE_ID_ENSONIQ_CT5880 +#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880 +#endif + +#ifndef PCI_DEVICE_ID_ECTIVA_EV1938 +#define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938 +#endif + /* ES1371 chip ID */ /* This is a little confusing because all ES1371 compatible chips have the same DEVICE_ID, the only thing differentiating them is the REV_ID field. @@ -137,8 +179,9 @@ #define ES1371REV_ES1373_A 0x04 #define ES1371REV_ES1373_B 0x06 #define ES1371REV_CT5880_A 0x07 +#define CT5880REV_CT5880_C 0x02 #define ES1371REV_ES1371_B 0x09 - +#define EV1938REV_EV1938_A 0x00 #define ES1371_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1371) @@ -146,7 +189,7 @@ #define JOY_EXTENT 8 #define ES1371_REG_CONTROL 0x00 -#define ES1371_REG_STATUS 0x04 +#define ES1371_REG_STATUS 0x04 /* on the 5880 it is control/status */ #define ES1371_REG_UART_DATA 0x08 #define ES1371_REG_UART_STATUS 0x09 #define ES1371_REG_UART_CONTROL 0x09 @@ -214,6 +257,7 @@ static const unsigned sample_shift[] = { 0, 1, 1, 2 }; #define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ +#define CSTAT_5880_AC97_RST 0x20000000 /* CT5880 Reset bit */ #define STAT_EN_SPDIF 0x00040000 /* enable S/PDIF circuitry */ #define STAT_TS_SPDIF 0x00020000 /* test S/PDIF circuitry */ #define STAT_TESTMODE 0x00010000 /* test ASIC */ @@ -372,7 +416,13 @@ static const char *stereo_enhancement[] __initdata = "NVidea 3D Stereo Enhancement", "Philips Incredible Sound", "Texas Instruments 3D Stereo Enhancement", - "VLSI Technology 3D Stereo Enhancement" + "VLSI Technology 3D Stereo Enhancement", + NULL, + NULL, + NULL, + NULL, + NULL, + "SigmaTel SS3D" }; /* --------------------------------------------------------------------- */ @@ -393,8 +443,12 @@ struct es1371_state { /* hardware resources */ unsigned long io; /* long for SPARC */ unsigned int irq; + + /* PCI ID's */ + u16 vendor; + u16 device; u8 rev; /* the chip revision */ - + #ifdef ES1371_DEBUG /* debug /proc entry */ struct proc_dir_entry *ps; @@ -416,7 +470,7 @@ struct es1371_state { spinlock_t lock; struct semaphore open_sem; mode_t open_mode; - struct wait_queue *open_wait; + wait_queue_head_t open_wait; struct dmabuf { void *rawbuf; @@ -427,7 +481,7 @@ struct es1371_state { unsigned total_bytes; int count; unsigned error; /* over/underrun */ - struct wait_queue *wait; + wait_queue_head_t wait; /* redundant, but makes calculations easier */ unsigned fragsize; unsigned dmasize; @@ -445,8 +499,8 @@ struct es1371_state { struct { unsigned ird, iwr, icnt; unsigned ord, owr, ocnt; - struct wait_queue *iwait; - struct wait_queue *owait; + wait_queue_head_t iwait; + wait_queue_head_t owait; unsigned char ibuf[MIDIINBUF]; unsigned char obuf[MIDIOUTBUF]; } midi; @@ -1228,7 +1282,7 @@ static int mixer_rdch(struct es1371_state *s, unsigned int ch, int *arg) case SOUND_MIXER_SPEAKER: j = rdcodec(s, AC97_PCBEEP_VOL); - if (j & AC97_MUTE + if (j & AC97_MUTE) return put_user(0, (int *)arg); return put_user(0x6464 - ((j >> 1) & 0xf) * 0x606, (int *)arg); @@ -1607,15 +1661,15 @@ static /*const*/ struct file_operations es1371_mixer_fops = { static int drain_dac1(struct es1371_state *s, int nonblock) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); unsigned long flags; int count, tmo; if (s->dma_dac1.mapped || !s->dma_dac1.ready) return 0; - current->state = TASK_INTERRUPTIBLE; add_wait_queue(&s->dma_dac1.wait, &wait); for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); count = s->dma_dac1.count; spin_unlock_irqrestore(&s->lock, flags); @@ -1625,16 +1679,16 @@ static int drain_dac1(struct es1371_state *s, int nonblock) break; if (nonblock) { remove_wait_queue(&s->dma_dac1.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 / s->dac1rate; tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; if (!schedule_timeout(tmo + 1)) - printk(KERN_DEBUG "es1371: dac1 dma timed out??\n"); + DBG(printk(KERN_DEBUG "es1371: dac1 dma timed out??\n");) } remove_wait_queue(&s->dma_dac1.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; return 0; @@ -1642,15 +1696,15 @@ static int drain_dac1(struct es1371_state *s, int nonblock) static int drain_dac2(struct es1371_state *s, int nonblock) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); unsigned long flags; int count, tmo; if (s->dma_dac2.mapped || !s->dma_dac2.ready) return 0; - current->state = TASK_INTERRUPTIBLE; add_wait_queue(&s->dma_dac2.wait, &wait); for (;;) { + __set_current_state(TASK_UNINTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); count = s->dma_dac2.count; spin_unlock_irqrestore(&s->lock, flags); @@ -1660,16 +1714,16 @@ static int drain_dac2(struct es1371_state *s, int nonblock) break; if (nonblock) { remove_wait_queue(&s->dma_dac2.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 / s->dac2rate; tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; if (!schedule_timeout(tmo + 1)) - printk(KERN_DEBUG "es1371: dac2 dma timed out??\n"); + DBG(printk(KERN_DEBUG "es1371: dac2 dma timed out??\n");) } remove_wait_queue(&s->dma_dac2.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; return 0; @@ -1680,6 +1734,7 @@ static int drain_dac2(struct es1371_state *s, int nonblock) static ssize_t es1371_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned swptr; @@ -1695,26 +1750,38 @@ static ssize_t es1371_read(struct file *file, char *buffer, size_t count, loff_t if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; ret = 0; + add_wait_queue(&s->dma_adc.wait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); swptr = s->dma_adc.swptr; cnt = s->dma_adc.dmasize-swptr; if (s->dma_adc.count < cnt) cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { start_adc(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->dma_adc.wait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) - return ret ? ret : -EFAULT; + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } swptr = (swptr + cnt) % s->dma_adc.dmasize; spin_lock_irqsave(&s->lock, flags); s->dma_adc.swptr = swptr; @@ -1725,12 +1792,15 @@ static ssize_t es1371_read(struct file *file, char *buffer, size_t count, loff_t ret += cnt; start_adc(s); } + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); return ret; } static ssize_t es1371_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned swptr; @@ -1746,6 +1816,7 @@ static ssize_t es1371_write(struct file *file, const char *buffer, size_t count, if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; ret = 0; + add_wait_queue(&s->dma_dac2.wait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); if (s->dma_dac2.count < 0) { @@ -1756,20 +1827,31 @@ static ssize_t es1371_write(struct file *file, const char *buffer, size_t count, cnt = s->dma_dac2.dmasize-swptr; if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize) cnt = s->dma_dac2.dmasize - s->dma_dac2.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { start_dac2(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->dma_dac2.wait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } swptr = (swptr + cnt) % s->dma_dac2.dmasize; spin_lock_irqsave(&s->lock, flags); s->dma_dac2.swptr = swptr; @@ -1781,6 +1863,8 @@ static ssize_t es1371_write(struct file *file, const char *buffer, size_t count, ret += cnt; start_dac2(s); } + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); return ret; } @@ -2150,6 +2234,7 @@ static int es1371_ioctl(struct inode *inode, struct file *file, unsigned int cmd static int es1371_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); struct es1371_state *s = devs; unsigned long flags; @@ -2166,8 +2251,12 @@ static int es1371_open(struct inode *inode, struct file *file) up(&s->open_sem); return -EBUSY; } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; down(&s->open_sem); @@ -2249,6 +2338,7 @@ static /*const*/ struct file_operations es1371_audio_fops = { static ssize_t es1371_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); ssize_t ret = 0; unsigned long flags; unsigned swptr; @@ -2263,6 +2353,7 @@ static ssize_t es1371_write_dac(struct file *file, const char *buffer, size_t co return ret; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; + add_wait_queue(&s->dma_dac1.wait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); if (s->dma_dac1.count < 0) { @@ -2273,20 +2364,31 @@ static ssize_t es1371_write_dac(struct file *file, const char *buffer, size_t co cnt = s->dma_dac1.dmasize-swptr; if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize) cnt = s->dma_dac1.dmasize - s->dma_dac1.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { start_dac1(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->dma_dac1.wait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } swptr = (swptr + cnt) % s->dma_dac1.dmasize; spin_lock_irqsave(&s->lock, flags); s->dma_dac1.swptr = swptr; @@ -2298,6 +2400,8 @@ static ssize_t es1371_write_dac(struct file *file, const char *buffer, size_t co ret += cnt; start_dac1(s); } + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); return ret; } @@ -2526,6 +2630,7 @@ static int es1371_ioctl_dac(struct inode *inode, struct file *file, unsigned int static int es1371_open_dac(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); struct es1371_state *s = devs; unsigned long flags; @@ -2549,8 +2654,12 @@ static int es1371_open_dac(struct inode *inode, struct file *file) up(&s->open_sem); return -EBUSY; } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; down(&s->open_sem); @@ -2610,7 +2719,7 @@ static /*const*/ struct file_operations es1371_dac_fops = { static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct es1371_state *s = (struct es1371_state *)file->private_data; - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned ptr; @@ -2631,6 +2740,8 @@ static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, l cnt = MIDIINBUF - ptr; if (s->midi.icnt < cnt) cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; @@ -2640,7 +2751,6 @@ static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, l ret = -EAGAIN; break; } - current->state = TASK_INTERRUPTIBLE; schedule(); if (signal_pending(current)) { if (!ret) @@ -2664,7 +2774,7 @@ static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, l ret += cnt; break; } - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); remove_wait_queue(&s->midi.iwait, &wait); return ret; } @@ -2672,7 +2782,7 @@ static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, l static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct es1371_state *s = (struct es1371_state *)file->private_data; - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned ptr; @@ -2693,8 +2803,10 @@ static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t c cnt = MIDIOUTBUF - ptr; if (s->midi.ocnt + cnt > MIDIOUTBUF) cnt = MIDIOUTBUF - s->midi.ocnt; - if (cnt <= 0) + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); es1371_handle_midi(s); + } spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; @@ -2704,7 +2816,6 @@ static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t c ret = -EAGAIN; break; } - current->state = TASK_INTERRUPTIBLE; schedule(); if (signal_pending(current)) { if (!ret) @@ -2730,7 +2841,7 @@ static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t c es1371_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); } - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); remove_wait_queue(&s->midi.owait, &wait); return ret; } @@ -2762,6 +2873,7 @@ static unsigned int es1371_midi_poll(struct file *file, struct poll_table_struct static int es1371_midi_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); struct es1371_state *s = devs; unsigned long flags; @@ -2778,8 +2890,12 @@ static int es1371_midi_open(struct inode *inode, struct file *file) up(&s->open_sem); return -EBUSY; } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; down(&s->open_sem); @@ -2811,15 +2927,15 @@ static int es1371_midi_open(struct inode *inode, struct file *file) static int es1371_midi_release(struct inode *inode, struct file *file) { struct es1371_state *s = (struct es1371_state *)file->private_data; - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); unsigned long flags; unsigned count, tmo; VALIDATE_STATE(s); if (file->f_mode & FMODE_WRITE) { - current->state = TASK_INTERRUPTIBLE; add_wait_queue(&s->midi.owait, &wait); for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); count = s->midi.ocnt; spin_unlock_irqrestore(&s->lock, flags); @@ -2829,7 +2945,7 @@ static int es1371_midi_release(struct inode *inode, struct file *file) break; if (file->f_flags & O_NONBLOCK) { remove_wait_queue(&s->midi.owait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } tmo = (count * HZ) / 3100; @@ -2837,7 +2953,7 @@ static int es1371_midi_release(struct inode *inode, struct file *file) printk(KERN_DEBUG "es1371: midi timed out??\n"); } remove_wait_queue(&s->midi.owait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); } down(&s->open_sem); s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); @@ -2882,7 +2998,7 @@ static /*const*/ struct file_operations es1371_midi_fops = { static int proc_es1371_dump (char *buf, char **start, off_t fpos, int length, int *eof, void *data) { int len = 0; - + struct es1371_state *s = devs; int cnt; @@ -2944,166 +3060,214 @@ static struct initvol { { SOUND_MIXER_WRITE_IGAIN, 0x4040 } }; +__initfunc(static int probe_chip(struct pci_dev *pcidev, int index)) +{ + struct es1371_state *s; + mm_segment_t fs; + int i, val, val2; + unsigned char id[4]; + unsigned long tmo; + signed long tmo2; + unsigned int cssr; + + if (pcidev->base_address[0] == 0 || + (pcidev->base_address[0] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + return -1; + if (pcidev->irq == 0) + return -1; + if (!(s = kmalloc(sizeof(struct es1371_state), GFP_KERNEL))) { + printk(KERN_WARNING "es1371: out of memory\n"); + return -1; + } + memset(s, 0, sizeof(struct es1371_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac1.wait); + init_waitqueue_head(&s->dma_dac2.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->magic = ES1371_MAGIC; + s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; + s->irq = pcidev->irq; + s->vendor = pcidev->vendor; + s->device = pcidev->device; + pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev); + printk(KERN_INFO "es1371: found chip, vendor id 0x%04x device id 0x%04x revision 0x%02x\n", + s->vendor, s->device, s->rev); + if (check_region(s->io, ES1371_EXTENT)) { + printk(KERN_ERR "es1371: io ports %#lx-%#lx in use\n", s->io, s->io+ES1371_EXTENT-1); + goto err_region; + } + request_region(s->io, ES1371_EXTENT, "es1371"); + if (request_irq(s->irq, es1371_interrupt, SA_SHIRQ, "es1371", s)) { + printk(KERN_ERR "es1371: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "es1371: found es1371 rev %d at io %#lx irq %u\n" + KERN_INFO "es1371: features: joystick 0x%x\n", s->rev, s->io, s->irq, joystick[index]); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&es1371_audio_fops, -1)) < 0) + goto err_dev1; + if ((s->dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1)) < 0) + goto err_dev2; + if ((s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1)) < 0) + goto err_dev3; + if ((s->dev_midi = register_sound_midi(&es1371_midi_fops, -1)) < 0) + goto err_dev4; +#ifdef ES1371_DEBUG + /* intialize the debug proc device */ + s->ps = create_proc_read_entry("es1371",0,NULL,proc_es1371_dump,NULL); +#endif /* ES1371_DEBUG */ + + /* initialize codec registers */ + s->ctrl = 0; + if ((joystick[index] & ~0x18) == 0x200) { + if (check_region(joystick[index], JOY_EXTENT)) + printk(KERN_ERR "es1371: joystick address 0x%x already in use\n", joystick[index]); + else { + s->ctrl |= CTRL_JYSTK_EN | (((joystick[index] >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT); + } + } + s->sctrl = 0; + cssr = 0; + /* check to see if s/pdif mode is being requested */ + if (spdif[index]) { + if (s->rev >= 4) { + printk(KERN_INFO "es1371: enabling S/PDIF output\n"); + cssr |= STAT_EN_SPDIF; + s->ctrl |= CTRL_SPDIFEN_B; + } else { + printk(KERN_ERR "es1371: revision %d does not support S/PDIF\n", s->rev); + } + } + /* initialize the chips */ + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + outl(0, s->io+ES1371_REG_LEGACY); + pci_set_master(pcidev); /* enable bus mastering */ + /* if we are a 5880 turn on the AC97 */ + if (s->vendor == PCI_VENDOR_ID_ENSONIQ && + ((s->device == PCI_DEVICE_ID_ENSONIQ_CT5880 && s->rev == CT5880REV_CT5880_C) || + (s->device == PCI_DEVICE_ID_ENSONIQ_ES1371 && s->rev == ES1371REV_CT5880_A))) { + cssr |= CSTAT_5880_AC97_RST; + outl(cssr, s->io+ES1371_REG_STATUS); + /* need to delay around 20ms(bleech) to give + some CODECs enough time to wakeup */ + tmo = jiffies + (HZ / 50) + 1; + for (;;) { + tmo2 = tmo - jiffies; + if (tmo2 <= 0) + break; + schedule_timeout(tmo2); + } + } + /* AC97 warm reset to start the bitclk */ + outl(s->ctrl | CTRL_SYNCRES, s->io+ES1371_REG_CONTROL); + udelay(2); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + /* init the sample rate converter */ + src_init(s); + /* codec init */ + wrcodec(s, AC97_RESET, 0); /* reset codec */ + s->mix.codec_id = rdcodec(s, AC97_RESET); /* get codec ID */ + val = rdcodec(s, AC97_VENDOR_ID1); + val2 = rdcodec(s, AC97_VENDOR_ID2); + id[0] = val >> 8; + id[1] = val; + id[2] = val2 >> 8; + id[3] = 0; + if (id[0] <= ' ' || id[0] > 0x7f) + id[0] = ' '; + if (id[1] <= ' ' || id[1] > 0x7f) + id[1] = ' '; + if (id[2] <= ' ' || id[2] > 0x7f) + id[2] = ' '; + printk(KERN_INFO "es1371: codec vendor %s (0x%04x%02x) revision %d (0x%02x)\n", + id, val & 0xffff, (val2 >> 8) & 0xff, val2 & 0xff, val2 & 0xff); + printk(KERN_INFO "es1371: codec features"); + if (s->mix.codec_id & CODEC_ID_DEDICATEDMIC) + printk(" dedicated MIC PCM in"); + if (s->mix.codec_id & CODEC_ID_MODEMCODEC) + printk(" Modem Line Codec"); + if (s->mix.codec_id & CODEC_ID_BASSTREBLE) + printk(" Bass & Treble"); + if (s->mix.codec_id & CODEC_ID_SIMULATEDSTEREO) + printk(" Simulated Stereo"); + if (s->mix.codec_id & CODEC_ID_HEADPHONEOUT) + printk(" Headphone out"); + if (s->mix.codec_id & CODEC_ID_LOUDNESS) + printk(" Loudness"); + if (s->mix.codec_id & CODEC_ID_18BITDAC) + printk(" 18bit DAC"); + if (s->mix.codec_id & CODEC_ID_20BITDAC) + printk(" 20bit DAC"); + if (s->mix.codec_id & CODEC_ID_18BITADC) + printk(" 18bit ADC"); + if (s->mix.codec_id & CODEC_ID_20BITADC) + printk(" 20bit ADC"); + printk("%s\n", (s->mix.codec_id & 0x3ff) ? "" : " none"); + val = (s->mix.codec_id >> CODEC_ID_SESHIFT) & CODEC_ID_SEMASK; + printk(KERN_INFO "es1371: stereo enhancement: %s\n", + (val <= 26 && stereo_enhancement[val]) ? stereo_enhancement[val] : "unknown"); + + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + /* turn on S/PDIF output driver if requested */ + outl(cssr, s->io+ES1371_REG_STATUS); + /* queue it for later freeing */ + s->next = devs; + devs = s; + return 0; + + err_dev4: + unregister_sound_dsp(s->dev_dac); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "es1371: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, ES1371_EXTENT); + err_region: + kfree_s(s, sizeof(struct es1371_state)); + return -1; +} + #ifdef MODULE __initfunc(int init_module(void)) #else __initfunc(int init_es1371(void)) #endif { - struct es1371_state *s; - struct pci_dev *pcidev = NULL; - mm_segment_t fs; - int i, val, val2, index = 0; - unsigned cssr; + struct pci_dev *pcidev; + int index = 0; if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1371: version v0.19 time " __TIME__ " " __DATE__ "\n"); - while (index < NR_DEVICE && - (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) { - if (pcidev->base_address[0] == 0 || - (pcidev->base_address[0] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) - continue; - if (pcidev->irq == 0) - continue; - if (!(s = kmalloc(sizeof(struct es1371_state), GFP_KERNEL))) { - printk(KERN_WARNING "es1371: out of memory\n"); + printk(KERN_INFO "es1371: version v0.22 time " __TIME__ " " __DATE__ "\n"); + for (pcidev = pci_devices; pcidev && index < NR_DEVICE; pcidev = pcidev->next) { + if (pcidev->vendor == PCI_VENDOR_ID_ENSONIQ) { + if (pcidev->device != PCI_DEVICE_ID_ENSONIQ_ES1371 && + pcidev->device != PCI_DEVICE_ID_ENSONIQ_CT5880) + continue; + } else if (pcidev->vendor == PCI_VENDOR_ID_ECTIVA) { + if (pcidev->device != PCI_DEVICE_ID_ECTIVA_EV1938) + continue; + } else continue; - } - memset(s, 0, sizeof(struct es1371_state)); - init_waitqueue(&s->dma_adc.wait); - init_waitqueue(&s->dma_dac1.wait); - init_waitqueue(&s->dma_dac2.wait); - init_waitqueue(&s->open_wait); - init_waitqueue(&s->midi.iwait); - init_waitqueue(&s->midi.owait); - s->open_sem = MUTEX; - spin_lock_init(&s->lock); - s->magic = ES1371_MAGIC; - s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; - s->irq = pcidev->irq; - pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev); - if (check_region(s->io, ES1371_EXTENT)) { - printk(KERN_ERR "es1371: io ports %#lx-%#lx in use\n", s->io, s->io+ES1371_EXTENT-1); - goto err_region; - } - request_region(s->io, ES1371_EXTENT, "es1371"); - if (request_irq(s->irq, es1371_interrupt, SA_SHIRQ, "es1371", s)) { - printk(KERN_ERR "es1371: irq %u in use\n", s->irq); - goto err_irq; - } - printk(KERN_INFO "es1371: found adapter at io %#lx irq %u\n" - KERN_INFO "es1371: features: joystick 0x%x\n", s->io, s->irq, joystick[index]); - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&es1371_audio_fops, -1)) < 0) - goto err_dev1; - if ((s->dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1)) < 0) - goto err_dev2; - if ((s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1)) < 0) - goto err_dev3; - if ((s->dev_midi = register_sound_midi(&es1371_midi_fops, -1)) < 0) - goto err_dev4; -#ifdef ES1371_DEBUG - /* intialize the debug proc device */ - s->ps = create_proc_entry("es1371", S_IFREG | S_IRUGO, NULL); - if (s->ps) - s->ps->read_proc = proc_es1371_dump; -#endif /* ES1371_DEBUG */ - - /* initialize codec registers */ - s->ctrl = 0; - if ((joystick[index] & ~0x18) == 0x200) { - if (check_region(joystick[index], JOY_EXTENT)) - printk(KERN_ERR "es1371: joystick address 0x%x already in use\n", joystick[index]); - else { - s->ctrl |= CTRL_JYSTK_EN | (((joystick[index] >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT); - } - } - s->sctrl = 0; - cssr = 0; - /* check to see if s/pdif mode is being requested */ - if (spdif[index]) { - if (s->rev >= 4) { - printk(KERN_INFO "es1371: enabling S/PDIF output\n"); - cssr |= STAT_EN_SPDIF; - s->ctrl |= CTRL_SPDIFEN_B; - } else { - printk(KERN_ERR "es1371: revision %d does not support S/PDIF\n", s->rev); - } - } - /* initialize the chips */ - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - outl(0, s->io+ES1371_REG_LEGACY); - pci_set_master(pcidev); /* enable bus mastering */ - /* AC97 warm reset to start the bitclk */ - outl(s->ctrl | CTRL_SYNCRES, s->io+ES1371_REG_CONTROL); - udelay(2); - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - /* init the sample rate converter */ - src_init(s); - /* codec init */ - wrcodec(s, AC97_RESET, 0); /* reset codec */ - s->mix.codec_id = rdcodec(s, AC97_RESET); /* get codec ID */ - val = rdcodec(s, AC97_VENDOR_ID1); - val2 = rdcodec(s, AC97_VENDOR_ID2); - printk(KERN_INFO "es1371: codec vendor %c%c%c revision %d\n", - (val >> 8) & 0xff, val & 0xff, (val2 >> 8) & 0xff, val2 & 0xff); - printk(KERN_INFO "es1371: codec features"); - if (s->mix.codec_id & CODEC_ID_DEDICATEDMIC) - printk(" dedicated MIC PCM in"); - if (s->mix.codec_id & CODEC_ID_MODEMCODEC) - printk(" Modem Line Codec"); - if (s->mix.codec_id & CODEC_ID_BASSTREBLE) - printk(" Bass & Treble"); - if (s->mix.codec_id & CODEC_ID_SIMULATEDSTEREO) - printk(" Simulated Stereo"); - if (s->mix.codec_id & CODEC_ID_HEADPHONEOUT) - printk(" Headphone out"); - if (s->mix.codec_id & CODEC_ID_LOUDNESS) - printk(" Loudness"); - if (s->mix.codec_id & CODEC_ID_18BITDAC) - printk(" 18bit DAC"); - if (s->mix.codec_id & CODEC_ID_20BITDAC) - printk(" 20bit DAC"); - if (s->mix.codec_id & CODEC_ID_18BITADC) - printk(" 18bit ADC"); - if (s->mix.codec_id & CODEC_ID_20BITADC) - printk(" 20bit ADC"); - printk("%s\n", (s->mix.codec_id & 0x3ff) ? "" : " none"); - val = (s->mix.codec_id >> CODEC_ID_SESHIFT) & CODEC_ID_SEMASK; - printk(KERN_INFO "es1371: stereo enhancement: %s\n", (val <= 20) ? stereo_enhancement[val] : "unknown"); - - fs = get_fs(); - set_fs(KERNEL_DS); - val = SOUND_MASK_LINE; - mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); - for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { - val = initvol[i].vol; - mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); - } - set_fs(fs); - /* turn on S/PDIF output driver if requested */ - outl(cssr, s->io+ES1371_REG_STATUS); - /* queue it for later freeing */ - s->next = devs; - devs = s; - index++; - continue; - - err_dev4: - unregister_sound_dsp(s->dev_dac); - err_dev3: - unregister_sound_mixer(s->dev_mixer); - err_dev2: - unregister_sound_dsp(s->dev_audio); - err_dev1: - printk(KERN_ERR "es1371: cannot register misc device\n"); - free_irq(s->irq, s); - err_irq: - release_region(s->io, ES1371_EXTENT); - err_region: - kfree_s(s, sizeof(struct es1371_state)); + if (!probe_chip(pcidev, index)) + index++; } if (!devs) return -ENODEV; diff --git a/drivers/sound/esssolo1.c b/drivers/sound/esssolo1.c index 6b602e77f6b0..80caeedce8c3 100644 --- a/drivers/sound/esssolo1.c +++ b/drivers/sound/esssolo1.c @@ -3,7 +3,7 @@ /* * esssolo1.c -- ESS Technology Solo1 (ES1946) audio driver. * - * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1998-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,38 +28,41 @@ * /dev/midi simple MIDI UART interface, no ioctl * * Revision history - * 10.11.98 0.1 Initial release (without any hardware) - * 22.03.99 0.2 cinfo.blocks should be reset after GETxPTR ioctl. - * reported by Johan Maes - * return EAGAIN instead of EBUSY when O_NONBLOCK - * read/write cannot be executed - * 07.04.99 0.3 implemented the following ioctl's: SOUND_PCM_READ_RATE, - * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; - * Alpha fixes reported by Peter Jones - * 15.06.99 0.4 Fix bad allocation bug. - * Thanks to Deti Fliegl - * 28.06.99 0.5 Add pci_set_master - * 12.08.99 0.6 Fix MIDI UART crashing the driver - * Changed mixer semantics from OSS documented - * behaviour to OSS "code behaviour". - * Recording might actually work now. - * The real DDMA controller address register is at PCI config - * 0x60, while the register at 0x18 is used as a placeholder - * register for BIOS address allocation. This register - * is supposed to be copied into 0x60, according - * to the Solo1 datasheet. When I do that, I can access - * the DDMA registers except the mask bit, which - * is stuck at 1. When I copy the contents of 0x18 +0x10 - * to the DDMA base register, everything seems to work. - * The fun part is that the Windows Solo1 driver doesn't - * seem to do these tricks. - * Bugs remaining: plops and clicks when starting/stopping playback - * 31.08.99 0.7 add spin_lock_init - * replaced current->state = x with set_current_state(x) - * 03.09.99 0.8 change read semantics for MIDI to match - * OSS more closely; remove possible wakeup race - * 07.10.99 0.9 Fix initialization; complain if sequencer writes time out - * Revised resource grabbing for the FM synthesizer + * 10.11.1998 0.1 Initial release (without any hardware) + * 22.03.1999 0.2 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 07.04.1999 0.3 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * 15.06.1999 0.4 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.5 Add pci_set_master + * 12.08.1999 0.6 Fix MIDI UART crashing the driver + * Changed mixer semantics from OSS documented + * behaviour to OSS "code behaviour". + * Recording might actually work now. + * The real DDMA controller address register is at PCI config + * 0x60, while the register at 0x18 is used as a placeholder + * register for BIOS address allocation. This register + * is supposed to be copied into 0x60, according + * to the Solo1 datasheet. When I do that, I can access + * the DDMA registers except the mask bit, which + * is stuck at 1. When I copy the contents of 0x18 +0x10 + * to the DDMA base register, everything seems to work. + * The fun part is that the Windows Solo1 driver doesn't + * seem to do these tricks. + * Bugs remaining: plops and clicks when starting/stopping playback + * 31.08.1999 0.7 add spin_lock_init + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.8 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 07.10.1999 0.9 Fix initialization; complain if sequencer writes time out + * Revised resource grabbing for the FM synthesizer + * 28.10.1999 0.10 More waitqueue races fixed + * 09.12.1999 0.11 Work around stupid Alpha port issue (virt_to_bus(kmalloc(GFP_DMA)) > 16M) + * Disabling recording on Alpha * */ @@ -442,6 +445,14 @@ static int prog_dmabuf(struct solo1_state *s, struct dmabuf *db, int gfp_mask) break; if (!db->rawbuf) return -ENOMEM; + /* work around a problem of the alpha port */ + if ((gfp_mask & GFP_DMA) && (virt_to_bus(db->rawbuf) & (~0xffffffUL))) { + printk(KERN_ERR "solo1: requested DMA buffer below 16M but got 0x%lx, Alpha bug?\n", + (unsigned long)virt_to_bus(db->rawbuf)); + kfree(db->rawbuf); + db->rawbuf = NULL; + return -ENOMEM; + } db->buforder = order; /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); @@ -963,9 +974,9 @@ static int drain_dac(struct solo1_state *s, int nonblock) if (s->dma_dac.mapped) return 0; - __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&s->dma_dac.wait, &wait); for (;;) { + set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); count = s->dma_dac.count; spin_unlock_irqrestore(&s->lock, flags); @@ -998,6 +1009,7 @@ static int drain_dac(struct solo1_state *s, int nonblock) static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned swptr; @@ -1013,12 +1025,15 @@ static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; ret = 0; + add_wait_queue(&s->dma_adc.wait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); swptr = s->dma_adc.swptr; cnt = s->dma_adc.dmasize-swptr; if (s->dma_adc.count < cnt) cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; @@ -1039,9 +1054,12 @@ static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t #endif if (inb(s->ddmabase+15) & 1) printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->dma_adc.wait); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); #ifdef DEBUGREC printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" KERN_DEBUG "solo1_read: regs: B1: 0x%02x B2: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n" @@ -1051,12 +1069,18 @@ static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); #endif - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) - return ret ? ret : -EFAULT; + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } swptr = (swptr + cnt) % s->dma_adc.dmasize; spin_lock_irqsave(&s->lock, flags); s->dma_adc.swptr = swptr; @@ -1071,12 +1095,15 @@ static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc)); #endif } + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); return ret; } static ssize_t solo1_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned swptr; @@ -1100,6 +1127,7 @@ static ssize_t solo1_write(struct file *file, const char *buffer, size_t count, read_mixer(s, 0x78), read_mixer(s, 0x7a), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc)); #endif ret = 0; + add_wait_queue(&s->dma_dac.wait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); if (s->dma_dac.count < 0) { @@ -1110,20 +1138,31 @@ static ssize_t solo1_write(struct file *file, const char *buffer, size_t count, cnt = s->dma_dac.dmasize-swptr; if (s->dma_dac.count + cnt > s->dma_dac.dmasize) cnt = s->dma_dac.dmasize - s->dma_dac.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { start_dac(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->dma_dac.wait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } swptr = (swptr + cnt) % s->dma_dac.dmasize; spin_lock_irqsave(&s->lock, flags); s->dma_dac.swptr = swptr; @@ -1135,6 +1174,8 @@ static ssize_t solo1_write(struct file *file, const char *buffer, size_t count, ret += cnt; start_dac(s); } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); return ret; } @@ -1410,6 +1451,12 @@ static int solo1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, if (s->dma_dac.mapped) s->dma_dac.count &= s->dma_dac.fragsize-1; spin_unlock_irqrestore(&s->lock, flags); +#if 0 + printk(KERN_DEBUG "esssolo1: GETOPTR: bytes %u blocks %u ptr %u, buforder %u numfrag %u fragshift %u\n" + KERN_DEBUG "esssolo1: swptr %u count %u fragsize %u dmasize %u fragsamples %u\n", + cinfo.bytes, cinfo.blocks, cinfo.ptr, s->dma_dac.buforder, s->dma_dac.numfrag, s->dma_dac.fragshift, + s->dma_dac.swptr, s->dma_dac.count, s->dma_dac.fragsize, s->dma_dac.dmasize, s->dma_dac.fragsamples); +#endif return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); case SNDCTL_DSP_GETBLKSIZE: @@ -1497,8 +1544,8 @@ static int solo1_release(struct inode *inode, struct file *file) dealloc_dmabuf(&s->dma_adc); } s->open_mode &= ~(FMODE_READ | FMODE_WRITE); - up(&s->open_sem); wake_up(&s->open_wait); + up(&s->open_sem); MOD_DEC_USE_COUNT; return 0; } @@ -1506,6 +1553,7 @@ static int solo1_release(struct inode *inode, struct file *file) static int solo1_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); struct solo1_state *s = devs; while (s && ((s->dev_audio ^ minor) & ~0xf)) @@ -1521,8 +1569,12 @@ static int solo1_open(struct inode *inode, struct file *file) up(&s->open_sem); return -EBUSY; } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; down(&s->open_sem); @@ -1537,10 +1589,6 @@ static int solo1_open(struct inode *inode, struct file *file) s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); up(&s->open_sem); MOD_INC_USE_COUNT; - if (prog_dmabuf_dac(s) || prog_dmabuf_adc(s)) { - solo1_release(inode, file); - return -ENOMEM; - } prog_codec(s); return 0; } @@ -1654,6 +1702,8 @@ static ssize_t solo1_midi_read(struct file *file, char *buffer, size_t count, lo cnt = MIDIINBUF - ptr; if (s->midi.icnt < cnt) cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; @@ -1663,7 +1713,6 @@ static ssize_t solo1_midi_read(struct file *file, char *buffer, size_t count, lo ret = -EAGAIN; break; } - __set_current_state(TASK_INTERRUPTIBLE); schedule(); if (signal_pending(current)) { if (!ret) @@ -1716,8 +1765,10 @@ static ssize_t solo1_midi_write(struct file *file, const char *buffer, size_t co cnt = MIDIOUTBUF - ptr; if (s->midi.ocnt + cnt > MIDIOUTBUF) cnt = MIDIOUTBUF - s->midi.ocnt; - if (cnt <= 0) + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); solo1_handle_midi(s); + } spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; @@ -1727,7 +1778,6 @@ static ssize_t solo1_midi_write(struct file *file, const char *buffer, size_t co ret = -EAGAIN; break; } - __set_current_state(TASK_INTERRUPTIBLE); schedule(); if (signal_pending(current)) { if (!ret) @@ -1785,6 +1835,7 @@ static unsigned int solo1_midi_poll(struct file *file, struct poll_table_struct static int solo1_midi_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); struct solo1_state *s = devs; unsigned long flags; @@ -1801,8 +1852,12 @@ static int solo1_midi_open(struct inode *inode, struct file *file) up(&s->open_sem); return -EBUSY; } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; down(&s->open_sem); @@ -1846,9 +1901,9 @@ static int solo1_midi_release(struct inode *inode, struct file *file) VALIDATE_STATE(s); if (file->f_mode & FMODE_WRITE) { - current->state = TASK_INTERRUPTIBLE; add_wait_queue(&s->midi.owait, &wait); for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); count = s->midi.ocnt; spin_unlock_irqrestore(&s->lock, flags); @@ -1858,7 +1913,7 @@ static int solo1_midi_release(struct inode *inode, struct file *file) break; if (file->f_flags & O_NONBLOCK) { remove_wait_queue(&s->midi.owait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } tmo = (count * HZ) / 3100; @@ -1866,7 +1921,7 @@ static int solo1_midi_release(struct inode *inode, struct file *file) printk(KERN_DEBUG "solo1: midi timed out??\n"); } remove_wait_queue(&s->midi.owait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); } down(&s->open_sem); s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); @@ -1876,8 +1931,8 @@ static int solo1_midi_release(struct inode *inode, struct file *file) del_timer(&s->midi.timer); } spin_unlock_irqrestore(&s->lock, flags); - up(&s->open_sem); wake_up(&s->open_wait); + up(&s->open_sem); MOD_DEC_USE_COUNT; return 0; } @@ -2002,6 +2057,7 @@ static int solo1_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int static int solo1_dmfm_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); struct solo1_state *s = devs; while (s && s->dev_dmfm != minor) @@ -2017,8 +2073,12 @@ static int solo1_dmfm_open(struct inode *inode, struct file *file) up(&s->open_sem); return -EBUSY; } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; down(&s->open_sem); @@ -2057,8 +2117,8 @@ static int solo1_dmfm_release(struct inode *inode, struct file *file) outb(0, s->sbbase+3); } release_region(s->sbbase, FMSYNTH_EXTENT); - up(&s->open_sem); wake_up(&s->open_wait); + up(&s->open_sem); MOD_DEC_USE_COUNT; return 0; } @@ -2113,7 +2173,7 @@ static struct initvol { if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "solo1: version v0.9 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "solo1: version v0.11 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, pcidev))) { if (pcidev->base_address[0] == 0 || diff --git a/drivers/sound/sonicvibes.c b/drivers/sound/sonicvibes.c index d9c710e596f5..011c622c37b3 100644 --- a/drivers/sound/sonicvibes.c +++ b/drivers/sound/sonicvibes.c @@ -3,7 +3,7 @@ /* * sonicvibes.c -- S3 Sonic Vibes audio driver. * - * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1998-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,50 +35,53 @@ * out first how to drive them... * * Revision history - * 06.05.98 0.1 Initial release - * 10.05.98 0.2 Fixed many bugs, esp. ADC rate calculation - * First stab at a simple midi interface (no bells&whistles) - * 13.05.98 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of - * set_dac_rate in the FMODE_WRITE case in sv_open - * Fix hwptr out of bounds (now mpg123 works) - * 14.05.98 0.4 Don't allow excessive interrupt rates - * 08.06.98 0.5 First release using Alan Cox' soundcore instead of miscdevice - * 03.08.98 0.6 Do not include modversions.h - * Now mixer behaviour can basically be selected between - * "OSS documented" and "OSS actual" behaviour - * 31.08.98 0.7 Fix realplayer problems - dac.count issues - * 10.12.98 0.8 Fix drain_dac trying to wait on not yet initialized DMA - * 16.12.98 0.9 Fix a few f_file & FMODE_ bugs - * 06.01.99 0.10 remove the silly SA_INTERRUPT flag. - * hopefully killed the egcs section type conflict - * 12.03.99 0.11 cinfo.blocks should be reset after GETxPTR ioctl. - * reported by Johan Maes - * 22.03.99 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK - * read/write cannot be executed - * 05.04.99 0.13 added code to sv_read and sv_write which should detect - * lockups of the sound chip and revive it. This is basically - * an ugly hack, but at least applications using this driver - * won't hang forever. I don't know why these lockups happen, - * it might well be the motherboard chipset (an early 486 PCI - * board with ALI chipset), since every busmastering 100MB - * ethernet card I've tried (Realtek 8139 and Macronix tulip clone) - * exhibit similar behaviour (they work for a couple of packets - * and then lock up and can be revived by ifconfig down/up). - * 07.04.99 0.14 implemented the following ioctl's: SOUND_PCM_READ_RATE, - * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; - * Alpha fixes reported by Peter Jones - * Note: dmaio hack might still be wrong on archs other than i386 - * 15.06.99 0.15 Fix bad allocation bug. - * Thanks to Deti Fliegl - * 28.06.99 0.16 Add pci_set_master - * 03.08.99 0.17 adapt to Linus' new __setup/__initcall - * added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr" - * 12.08.99 0.18 module_init/__setup fixes - * 24.08.99 0.19 get rid of the dmaio kludge, replace with allocate_resource - * 31.08.99 0.20 add spin_lock_init - * __initlocaldata to fix gcc 2.7.x problems - * 03.09.99 0.21 change read semantics for MIDI to match - * OSS more closely; remove possible wakeup race + * 06.05.1998 0.1 Initial release + * 10.05.1998 0.2 Fixed many bugs, esp. ADC rate calculation + * First stab at a simple midi interface (no bells&whistles) + * 13.05.1998 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of + * set_dac_rate in the FMODE_WRITE case in sv_open + * Fix hwptr out of bounds (now mpg123 works) + * 14.05.1998 0.4 Don't allow excessive interrupt rates + * 08.06.1998 0.5 First release using Alan Cox' soundcore instead of miscdevice + * 03.08.1998 0.6 Do not include modversions.h + * Now mixer behaviour can basically be selected between + * "OSS documented" and "OSS actual" behaviour + * 31.08.1998 0.7 Fix realplayer problems - dac.count issues + * 10.12.1998 0.8 Fix drain_dac trying to wait on not yet initialized DMA + * 16.12.1998 0.9 Fix a few f_file & FMODE_ bugs + * 06.01.1999 0.10 remove the silly SA_INTERRUPT flag. + * hopefully killed the egcs section type conflict + * 12.03.1999 0.11 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.1999 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 05.04.1999 0.13 added code to sv_read and sv_write which should detect + * lockups of the sound chip and revive it. This is basically + * an ugly hack, but at least applications using this driver + * won't hang forever. I don't know why these lockups happen, + * it might well be the motherboard chipset (an early 486 PCI + * board with ALI chipset), since every busmastering 100MB + * ethernet card I've tried (Realtek 8139 and Macronix tulip clone) + * exhibit similar behaviour (they work for a couple of packets + * and then lock up and can be revived by ifconfig down/up). + * 07.04.1999 0.14 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * Note: dmaio hack might still be wrong on archs other than i386 + * 15.06.1999 0.15 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.16 Add pci_set_master + * 03.08.1999 0.17 adapt to Linus' new __setup/__initcall + * added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr" + * 12.08.1999 0.18 module_init/__setup fixes + * 24.08.1999 0.19 get rid of the dmaio kludge, replace with allocate_resource + * 31.08.1999 0.20 add spin_lock_init + * __initlocaldata to fix gcc 2.7.x problems + * use new resource allocation to allocate DDMA IO space + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.21 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 28.10.1999 0.22 More waitqueue races fixed * */ @@ -110,6 +113,26 @@ /* --------------------------------------------------------------------- */ +#ifdef MODULE +#define __exit +#define module_exit(x) void cleanup_module(void) { x(); } +#define module_init(x) int init_module(void) { return x(); } +#else +#define __exit __attribute__ ((unused, __section__ (".text.init"))) +#define module_exit(x) /* nothing */ +#define module_init(x) /* nothing */ +#endif + +#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL +#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL} +#define wait_queue_head_t struct wait_queue * +#define init_waitqueue_head(w) *(w) = 0 +#define init_MUTEX(m) *(m) = MUTEX +#define __set_current_state(x) do { current->state = (x); } while (0) +#define set_current_state(x) __set_current_state(x) + +/* --------------------------------------------------------------------- */ + #ifndef PCI_VENDOR_ID_S3 #define PCI_VENDOR_ID_S3 0x5333 #endif @@ -297,7 +320,7 @@ struct sv_state { spinlock_t lock; struct semaphore open_sem; mode_t open_mode; - struct wait_queue *open_wait; + wait_queue_head_t open_wait; struct dmabuf { void *rawbuf; @@ -308,7 +331,7 @@ struct sv_state { unsigned total_bytes; int count; unsigned error; /* over/underrun */ - struct wait_queue *wait; + wait_queue_head_t wait; /* redundant, but makes calculations easier */ unsigned fragsize; unsigned dmasize; @@ -326,8 +349,8 @@ struct sv_state { struct { unsigned ird, iwr, icnt; unsigned ord, owr, ocnt; - struct wait_queue *iwait; - struct wait_queue *owait; + wait_queue_head_t iwait; + wait_queue_head_t owait; struct timer_list timer; unsigned char ibuf[MIDIINBUF]; unsigned char obuf[MIDIOUTBUF]; @@ -512,8 +535,9 @@ static void frobindir(struct sv_state *s, unsigned char idx, unsigned char mask, static unsigned setpll(struct sv_state *s, unsigned char reg, unsigned rate) { unsigned long flags; - unsigned char r, m=0, n=0; + unsigned char r, m, n; unsigned xm, xn, xr, xd, metric = ~0U; + /* the warnings about m and n used uninitialized are bogus and may safely be ignored */ if (rate < 625000/ADCMULT) rate = 625000/ADCMULT; @@ -1252,15 +1276,15 @@ static /*const*/ struct file_operations sv_mixer_fops = { static int drain_dac(struct sv_state *s, int nonblock) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); unsigned long flags; int count, tmo; if (s->dma_dac.mapped || !s->dma_dac.ready) return 0; - current->state = TASK_INTERRUPTIBLE; add_wait_queue(&s->dma_dac.wait, &wait); for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); count = s->dma_dac.count; spin_unlock_irqrestore(&s->lock, flags); @@ -1270,7 +1294,7 @@ static int drain_dac(struct sv_state *s, int nonblock) break; if (nonblock) { remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; @@ -1279,7 +1303,7 @@ static int drain_dac(struct sv_state *s, int nonblock) printk(KERN_DEBUG "sv: dma timed out??\n"); } remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; return 0; @@ -1290,6 +1314,7 @@ static int drain_dac(struct sv_state *s, int nonblock) static ssize_t sv_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned swptr; @@ -1306,24 +1331,30 @@ static ssize_t sv_read(struct file *file, char *buffer, size_t count, loff_t *pp return -EFAULT; ret = 0; #if 0 - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - spin_unlock_irqrestore(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + spin_unlock_irqrestore(&s->lock, flags); #endif + add_wait_queue(&s->dma_adc.wait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); swptr = s->dma_adc.swptr; cnt = s->dma_adc.dmasize-swptr; if (s->dma_adc.count < cnt) cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { start_adc(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + if (!schedule_timeout(HZ)) { printk(KERN_DEBUG "sv: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, s->dma_adc.hwptr, s->dma_adc.swptr); @@ -1336,12 +1367,18 @@ static ssize_t sv_read(struct file *file, char *buffer, size_t count, loff_t *pp s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; spin_unlock_irqrestore(&s->lock, flags); } - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) - return ret ? ret : -EFAULT; + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } swptr = (swptr + cnt) % s->dma_adc.dmasize; spin_lock_irqsave(&s->lock, flags); s->dma_adc.swptr = swptr; @@ -1352,12 +1389,15 @@ static ssize_t sv_read(struct file *file, char *buffer, size_t count, loff_t *pp ret += cnt; start_adc(s); } + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); return ret; } static ssize_t sv_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned swptr; @@ -1374,10 +1414,11 @@ static ssize_t sv_write(struct file *file, const char *buffer, size_t count, lof return -EFAULT; ret = 0; #if 0 - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - spin_unlock_irqrestore(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + spin_unlock_irqrestore(&s->lock, flags); #endif + add_wait_queue(&s->dma_dac.wait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); if (s->dma_dac.count < 0) { @@ -1388,14 +1429,19 @@ static ssize_t sv_write(struct file *file, const char *buffer, size_t count, lof cnt = s->dma_dac.dmasize-swptr; if (s->dma_dac.count + cnt > s->dma_dac.dmasize) cnt = s->dma_dac.dmasize - s->dma_dac.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { start_dac(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + if (!schedule_timeout(HZ)) { printk(KERN_DEBUG "sv: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, s->dma_dac.hwptr, s->dma_dac.swptr); @@ -1408,12 +1454,18 @@ static ssize_t sv_write(struct file *file, const char *buffer, size_t count, lof s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; spin_unlock_irqrestore(&s->lock, flags); } - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } swptr = (swptr + cnt) % s->dma_dac.dmasize; spin_lock_irqsave(&s->lock, flags); s->dma_dac.swptr = swptr; @@ -1425,6 +1477,8 @@ static ssize_t sv_write(struct file *file, const char *buffer, size_t count, lof ret += cnt; start_dac(s); } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); return ret; } @@ -1789,6 +1843,7 @@ static int sv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un static int sv_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); struct sv_state *s = devs; unsigned char fmtm = ~0, fmts = 0; @@ -1805,8 +1860,12 @@ static int sv_open(struct inode *inode, struct file *file) up(&s->open_sem); return -EBUSY; } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; down(&s->open_sem); @@ -1849,8 +1908,8 @@ static int sv_release(struct inode *inode, struct file *file) dealloc_dmabuf(&s->dma_adc); } s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - up(&s->open_sem); wake_up(&s->open_wait); + up(&s->open_sem); MOD_DEC_USE_COUNT; return 0; } @@ -1878,7 +1937,7 @@ static /*const*/ struct file_operations sv_audio_fops = { static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct sv_state *s = (struct sv_state *)file->private_data; - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned ptr; @@ -1899,6 +1958,8 @@ static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_ cnt = MIDIINBUF - ptr; if (s->midi.icnt < cnt) cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; @@ -1908,7 +1969,6 @@ static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_ ret = -EAGAIN; break; } - current->state = TASK_INTERRUPTIBLE; schedule(); if (signal_pending(current)) { if (!ret) @@ -1932,7 +1992,7 @@ static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_ ret += cnt; break; } - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); remove_wait_queue(&s->midi.iwait, &wait); return ret; } @@ -1940,7 +2000,7 @@ static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_ static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct sv_state *s = (struct sv_state *)file->private_data; - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned ptr; @@ -1961,8 +2021,10 @@ static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count cnt = MIDIOUTBUF - ptr; if (s->midi.ocnt + cnt > MIDIOUTBUF) cnt = MIDIOUTBUF - s->midi.ocnt; - if (cnt <= 0) + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); sv_handle_midi(s); + } spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; @@ -1972,7 +2034,6 @@ static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count ret = -EAGAIN; break; } - current->state = TASK_INTERRUPTIBLE; schedule(); if (signal_pending(current)) { if (!ret) @@ -1998,7 +2059,7 @@ static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count sv_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); } - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); remove_wait_queue(&s->midi.owait, &wait); return ret; } @@ -2030,6 +2091,7 @@ static unsigned int sv_midi_poll(struct file *file, struct poll_table_struct *wa static int sv_midi_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); struct sv_state *s = devs; unsigned long flags; @@ -2046,8 +2108,12 @@ static int sv_midi_open(struct inode *inode, struct file *file) up(&s->open_sem); return -EBUSY; } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; down(&s->open_sem); @@ -2087,16 +2153,16 @@ static int sv_midi_open(struct inode *inode, struct file *file) static int sv_midi_release(struct inode *inode, struct file *file) { struct sv_state *s = (struct sv_state *)file->private_data; - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); unsigned long flags; unsigned count, tmo; VALIDATE_STATE(s); if (file->f_mode & FMODE_WRITE) { - current->state = TASK_INTERRUPTIBLE; add_wait_queue(&s->midi.owait, &wait); for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); count = s->midi.ocnt; spin_unlock_irqrestore(&s->lock, flags); @@ -2106,7 +2172,7 @@ static int sv_midi_release(struct inode *inode, struct file *file) break; if (file->f_flags & O_NONBLOCK) { remove_wait_queue(&s->midi.owait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } tmo = (count * HZ) / 3100; @@ -2114,7 +2180,7 @@ static int sv_midi_release(struct inode *inode, struct file *file) printk(KERN_DEBUG "sv: midi timed out??\n"); } remove_wait_queue(&s->midi.owait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); } down(&s->open_sem); s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); @@ -2124,8 +2190,8 @@ static int sv_midi_release(struct inode *inode, struct file *file) del_timer(&s->midi.timer); } spin_unlock_irqrestore(&s->lock, flags); - up(&s->open_sem); wake_up(&s->open_wait); + up(&s->open_sem); MOD_DEC_USE_COUNT; return 0; } @@ -2250,6 +2316,7 @@ static int sv_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cm static int sv_dmfm_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); struct sv_state *s = devs; while (s && s->dev_dmfm != minor) @@ -2265,8 +2332,12 @@ static int sv_dmfm_open(struct inode *inode, struct file *file) up(&s->open_sem); return -EBUSY; } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; down(&s->open_sem); @@ -2298,8 +2369,8 @@ static int sv_dmfm_release(struct inode *inode, struct file *file) outb(regb, s->iosynth+2); outb(0, s->iosynth+3); } - up(&s->open_sem); wake_up(&s->open_wait); + up(&s->open_sem); MOD_DEC_USE_COUNT; return 0; } @@ -2365,7 +2436,7 @@ __initfunc(int init_sonicvibes(void)) if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "sv: version v0.21 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "sv: version v0.22 time " __TIME__ " " __DATE__ "\n"); #if 0 if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); @@ -2388,12 +2459,12 @@ __initfunc(int init_sonicvibes(void)) continue; } memset(s, 0, sizeof(struct sv_state)); - init_waitqueue(&s->dma_adc.wait); - init_waitqueue(&s->dma_dac.wait); - init_waitqueue(&s->open_wait); - init_waitqueue(&s->midi.iwait); - init_waitqueue(&s->midi.owait); - s->open_sem = MUTEX; + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); spin_lock_init(&s->lock); s->magic = SV_MAGIC; s->iosb = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; diff --git a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c index 263ac5f3c90f..bc7e76130d41 100644 --- a/drivers/telephony/ixj.c +++ b/drivers/telephony/ixj.c @@ -1974,7 +1974,7 @@ static void aec_stop(int board) ixj_WriteDSPCommand(0x0700, board); } - if (ixj[board].play_mode != -1 && ixj[board].rec_mode != -1); + if (ixj[board].play_mode != -1 && ixj[board].rec_mode != -1) { ixj_WriteDSPCommand(0xB002, board); // AEC Stop diff --git a/fs/block_dev.c b/fs/block_dev.c index eef7c423f446..8a0c29f6e645 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -92,6 +92,7 @@ ssize_t block_write(struct file * filp, const char * buf, blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9) / 2; if (block + blocks > size) blocks = size - block; if (blocks > NBUF) blocks=NBUF; + if (!blocks) blocks = 1; for(i=1; idata_start+(nr-2)*cluster_size; last_sector = sector + cluster_size; if (MSDOS_SB(sb)->cvf_format && - MSDOS_SB(sb)->cvf_format->zero_out_cluster) + MSDOS_SB(sb)->cvf_format->zero_out_cluster) { MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode,nr); - else + res=fat_bread(sb,fat_smap(inode,inode->i_blocks)); + }else for ( ; sector < last_sector; sector++) { #ifdef DEBUG printk("zeroing sector %d\n",sector); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 12191ee9d0cf..4e4003bb43c7 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -212,7 +212,12 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) if (!data) goto out_miss_args; - if (data->version != NFS_MOUNT_VERSION) { + /* No NFS V3. */ + if (data->flags & NFS_MOUNT_VER3) + goto out_fail; + + /* Don't complain if "mount" is newer. */ + if (data->version < NFS_MOUNT_VERSION) { printk("nfs warning: mount version %s than kernel\n", data->version < NFS_MOUNT_VERSION ? "older" : "newer"); if (data->version < 2) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index e95d32b01e89..f1dec85807ab 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -345,7 +345,7 @@ void nfsd_modcount(struct inode *inode, int fill) int init_module(void) { - printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); + printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de)\n"); do_nfsservctl = handle_sys_nfsservctl; return 0; } diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 49d9b008c7ce..ef0da39cc9d6 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -455,13 +455,27 @@ struct dentry * lookup_inode(kdev_t dev, ino_t dirino, ino_t ino) break; } - result = ERR_PTR(-ENOENT); - dir = iget_in_use(sb, dirino); - if (!dir) - goto out_root; - dentry = d_alloc_root(dir, NULL); - if (!dentry) - goto out_iput; + /* + * Fix for /// bad export bug: if dirino is the root, + * get the real root dentry rather than creating a temporary + * "root" dentry. XXX We could extend this to use + * any existing dentry for the located 'dir', but all + * of this code is going to be completely rewritten soon, + * so I won't bother. + */ + + if (dirino == root_ino) { + dentry = dget(root); + } + else { + result = ERR_PTR(-ENOENT); + dir = iget_in_use(sb, dirino); + if (!dir) + goto out_root; + dentry = d_alloc_root(dir, NULL); + if (!dentry) + goto out_iput; + } /* * Get the name for this inode and the next parent inode. @@ -1330,7 +1344,7 @@ fh_update(struct svc_fh *fhp) fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino); fhp->fh_handle.fh_generation = inode->i_generation; fhp->fh_handle.fh_dcookie = (struct dentry *)0xfeebbaca; - out: +out: return; out_bad: diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 971bab42088a..611b89400a74 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -237,6 +237,9 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, if (nfserr) goto done; inode = newfhp->fh_dentry->d_inode; + if (inode && newfhp->fh_handle.fh_ino == 0) + /* inode might have been instantiated while we slept */ + fh_update(newfhp); /* Unfudge the mode bits */ if (attr->ia_valid & ATTR_MODE) { diff --git a/include/asm-s390/setup.h b/include/asm-s390/setup.h index 851b2d3ed5c8..6a9449a5288f 100644 --- a/include/asm-s390/setup.h +++ b/include/asm-s390/setup.h @@ -40,6 +40,7 @@ */ #define MACHINE_IS_VM (MACHINE_FLAGS & 1) #define MACHINE_HAS_IEEE (MACHINE_FLAGS & 2) +#define MACHINE_IS_P390 (MACHINE_FLAGS & 4) #define RAMDISK_ORIGIN 0x800000 #define RAMDISK_BLKSIZE 0x1000 diff --git a/init/main.c b/init/main.c index 43cd7f49333e..aaf8833e3cc2 100644 --- a/init/main.c +++ b/init/main.c @@ -151,6 +151,9 @@ extern void arcrimi_setup(char *str, int *ints); #ifdef CONFIG_CTC extern void ctc_setup(char *str, int *ints); #endif +#ifdef CONFIG_IUCV +extern void iucv_setup(char *str, int *ints); +#endif #ifdef CONFIG_ARCNET_COM90xxIO extern void com90io_setup(char *str, int *ints); #endif @@ -182,6 +185,9 @@ extern void pg_setup(char *str, int *ints); #ifdef CONFIG_PARIDE_PCD extern void pcd_setup(char *str, int *ints); #endif +#ifdef CONFIG_3215 +extern void con3215_setup(char *str, int *ints); +#endif #ifdef CONFIG_MDISK extern void mdisk_setup(char *str, int *ints); #endif @@ -638,6 +644,10 @@ static struct kernel_param cooked_params[] __initdata = { #ifdef CONFIG_CTC { "ctc=", ctc_setup } , #endif +#ifdef CONFIG_IUCV + { "iucv=", iucv_setup } , +#endif + #endif #ifdef CONFIG_FB @@ -981,6 +991,9 @@ static struct kernel_param raw_params[] __initdata = { #ifdef CONFIG_APM { "apm=", apm_setup }, #endif +#ifdef CONFIG_3215 + { "condev=", con3215_setup }, +#endif #ifdef CONFIG_MDISK { "mdisk=", mdisk_setup }, #endif diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 2ac5e8a2b7a0..d0a0ac96b686 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1004,7 +1004,7 @@ void tcp_send_delayed_ack(struct tcp_opt *tp, int max_timeout) unsigned long timeout; /* Stay within the limit we were given */ - timeout = tp->ato; + timeout = (tp->ato << 1) >> 1; if (timeout > max_timeout) timeout = max_timeout; timeout += jiffies; diff --git a/net/x25/x25_link.c b/net/x25/x25_link.c index 961682702448..f734a213f1db 100644 --- a/net/x25/x25_link.c +++ b/net/x25/x25_link.c @@ -84,8 +84,9 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh, unsigned sho switch (frametype) { case X25_RESTART_REQUEST: x25_stop_t20timer(neigh); + if (neigh->state!=X25_LINK_STATE_2) + x25_transmit_restart_confirmation(neigh); neigh->state = X25_LINK_STATE_3; - x25_transmit_restart_confirmation(neigh); break; case X25_RESTART_CONFIRMATION: -- 2.39.5