diff options
| author | Linus Torvalds <torvalds@penguin.transmeta.com> | 2002-05-31 01:51:27 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@penguin.transmeta.com> | 2002-05-31 01:51:27 -0700 |
| commit | 3f102da8fc4e532e253606d331e71feaa7356266 (patch) | |
| tree | 96ca1d3ad5e1b38eb7050ff1bde86cd7874c6386 | |
| parent | 7c165ccf3f9851892880ff7605a71003be3117e0 (diff) | |
| parent | 3ec5b5ab2c0106febd7d41cfe3c1eef41d61561f (diff) | |
Merge http://gkernel.bkbits.net/net-drivers-2.5
into penguin.transmeta.com:/home/penguin/torvalds/repositories/kernel/linux
| -rw-r--r-- | Documentation/networking/e100.txt | 272 | ||||
| -rw-r--r-- | Documentation/networking/e1000.txt | 121 | ||||
| -rw-r--r-- | MAINTAINERS | 5 | ||||
| -rw-r--r-- | drivers/net/Config.help | 103 | ||||
| -rw-r--r-- | drivers/net/dl2k.c | 568 | ||||
| -rw-r--r-- | drivers/net/dl2k.h | 125 | ||||
| -rw-r--r-- | drivers/net/e100/e100.h | 9 | ||||
| -rw-r--r-- | drivers/net/e100/e100_config.c | 2 | ||||
| -rw-r--r-- | drivers/net/e100/e100_main.c | 105 | ||||
| -rw-r--r-- | drivers/net/e100/e100_proc.c | 2 | ||||
| -rw-r--r-- | drivers/net/e100/e100_test.c | 8 | ||||
| -rw-r--r-- | drivers/net/e1000/Makefile | 73 | ||||
| -rw-r--r-- | drivers/net/e1000/e1000.h | 22 | ||||
| -rw-r--r-- | drivers/net/e1000/e1000_ethtool.c | 215 | ||||
| -rw-r--r-- | drivers/net/e1000/e1000_hw.c | 169 | ||||
| -rw-r--r-- | drivers/net/e1000/e1000_hw.h | 49 | ||||
| -rw-r--r-- | drivers/net/e1000/e1000_main.c | 229 | ||||
| -rw-r--r-- | drivers/net/e1000/e1000_osdep.h | 2 | ||||
| -rw-r--r-- | drivers/net/eepro100.c | 2 | ||||
| -rw-r--r-- | drivers/net/pcnet32.c | 5 | ||||
| -rw-r--r-- | drivers/net/wireless/airo.c | 3581 | ||||
| -rw-r--r-- | include/linux/ethtool.h | 4 |
22 files changed, 3900 insertions, 1771 deletions
diff --git a/Documentation/networking/e100.txt b/Documentation/networking/e100.txt new file mode 100644 index 000000000000..95a6d14c3a49 --- /dev/null +++ b/Documentation/networking/e100.txt @@ -0,0 +1,272 @@ +Linux* Base Driver for the Intel(R) PRO/100 Family of Adapters +============================================================== + +April 9, 2002 + + +Contents +======== + +- In This Release +- Supported Adapters +- Command Line Parameters +- CPU Cycle Saver +- Additional Configurations +- Support + + +In This Release +=============== + +This file describes the Linux* Base Driver for the Intel(R) PRO/100 Family of +Adapters, version 2.0.x. This driver includes support for Itanium(TM)-based +systems. + +New for this release: + + - Additional ethtool functionality, including link status test and EEPROM + read/write. A third-party application can use the ethtool interface to + get and set driver parameters. + + - Support for Zero copy on 82550-based adapters. This feature provides + faster data throughput and significant CPU usage improvement in systems + that use the relevant system call (sendfile(2)). + + - Support for large MTU-enabling interface (1504 bytes) with kernel's + VLAN module + + - Support for polling on RX + + - Support for Wake On LAN* on 82550 and 82559-based adapters + + +Supported Adapters +================== + +The following Intel network adapters are compatible with the drivers +in this release: + +Controller Adapter Name Board IDs +---------- ------------ --------- + +82558 PRO/100+ PCI Adapter 668081-xxx, 689661-xxx + +82558 PRO/100+ Management Adapter 691334-xxx, 701738-xxx, + 721383-xxx + +82558 PRO/100+ Dual Port Server Adapter 714303-xxx, 711269-xxx, + A28276-xxx + +82558 PRO/100+ PCI Server Adapter 710550-xxx + +82550 PRO/100 S Server Adapter 752438-xxx (82550) +82559 A56831-xxx, A10563-xxx, + A12171-xxx, A12321-xxx, + A12320-xxx, A12170-xxx + 748568-xxx (82559) + 748565-xxx (82559) + + +82550 PRO/100 S Desktop Adapter 751767-xxx (82550) +82559 748592-xxx, A12167-xxx, + A12318-xxx, A12317-xxx, + A12165-xxx + 748569-xxx (82559) + + + +82559 PRO/100+ Server Adapter 729757-xxx + +82559 PRO/100 S Management Adapter 748566-xxx, 748564-xxx + +82550 PRO/100 S Dual Port Server Adapter A56831-xxx + +82551 PRO/100 M Desktop Adapter A80897-xxx + + PRO/100 S Advanced Management Adapter 747842-xxx, 745171-xxx + +CNR PRO/100 VE Desktop Adapter A10386-xxx, A10725-xxx, + A23801-xxx, A19716-xxx + + + PRO/100 VM Desktop Adapter A14323-xxx, A19725-xxx, + A23801-xxx, A22220-xxx, + A23796-xxx + + +To verify that your adapter is supported, find the board ID number on the +adapter. Look for a label that has a barcode and a number in the format +123456-001 (six digits hyphen three digits). Match this to the list of +numbers above. + +For more information on how to identify your adapter, go to the Adapter & +Driver ID Guide at: + + http://support.intel.com/support/network/adapter/pro100/21397.htm + +For the latest Intel PRO/100 network driver for Linux, see: + + http://appsr.intel.com/scripts-df/support_intel.asp + + +Command Line Parameters +======================= + +The following parameters are used by entering them on the command line with +the modprobe or insmod command. For example, with two Intel PRO/100 PCI +adapters, entering: + + modprobe e100 TxDescriptors=32,128 + +loads the e100 driver with 32 TX resources for the first adapter and 128 TX +resources for the second adapter. This configuration favors the second +adapter. The driver supports up to 16 network adapters concurrently. + +NOTE: Giving any command line option the value "-1" causes the driver to use + the appropriate default value for that option, as if no value was + specified. + + +BundleMax +Valid Range: 0x1-0xFFFF +Default Value: 6 + This parameter holds the maximum number of packets in a bundle. Suggested + values range from 2 to 10. See "CPU Cycle Saver." + +BundleSmallFr +Valid Range: 0-1 (0=off, 1=on) +Default Value: 0 + The value 1 (on) causes small packets (less than 128 bytes) to be bundled. + See "CPU Cycle Saver." + +e100_speed_duplex +Valid Range: 0-4 (1=10half;2=10full;3=100half;4=100full) +Default Value: 0 + The default value of 0 is set to auto-negotiate if the link partner is set + to auto-negotiate. If the link partner is forced, e100_speed_duplex + defaults to half-duplex. + Example usage: insmod e100.o e100_speed_duplex=4,4 (for two adapters) + +flow_control +Valid Range: 0-1 (0=off, 1=on) +Default Value: 0 + This parameter controls the automatic generation(Tx) and response(Rx) to + Ethernet PAUSE frames. flow_control should NOT be set to 1 when the e100 + adapter is connected to an interface that does not support Ethernet PAUSE + frames and when the e100_speed_duplex parameter is NOT set to zero. + +IntDelay +Valid Range: 0-0xFFFF (0=off) +Default Value: 1536 + This parameter holds the number of time units (in adapter terminology) + until the adapter generates an interrupt. The recommended value for + IntDelay is 0x600 (upon initialization). Suggested values range from + 0x200h to 0x800. See "CPU Cycle Saver." + +IFS +Valid Range: 0-1 (0=off, 1=on) +Default Value: 1 + Inter Frame Spacing (IFS) aims to reduce the number of Ethernet frame + collisions by altering the time between frame transmissions. When IFS is + enabled the driver tries to find an optimal IFS value. However, some + switches function better when IFS is disabled. + +PollingMaxWork +Valid Range: 1-1024 (max number of RxDescriptors) +Default Value: Specified number of RxDescriptors + This value specifies the maximum number of receive packets that are + processed on a single polling call. This parameter is invalid if + RxCongestionControl is set to 0. + +RxCongestionControl +Valid Range: 0-1 (0=off, 1=on) +Default Value: 1 + 1 enables polling mode. When the link is congested, the driver can decide + to handle received packets by polling them, instead of waiting until + interrupts occur. + +RxDescriptors +Valid Range: 8-1024 +Default Value: 64 + This parameter defines the number of receive descriptors allocated by + the driver. Increasing this value allows the driver to buffer more + incoming packets before the driver is required to service an interrupt. + The maximum value for Itanium-based systems is 64. + +TxDescriptors +Valid Range: 19-1024 +Default Value: 64 + This value is the number of transmit descriptors allocated by the driver. + Increasing this value allows the protocol stack to queue more transmits at + the driver level. The maximum value for Itanium-based systems is 64. + +ucode (not available for 82557-based adapters) +Valid Range: 0-1 (0=off, 1=on) +Default Value: 0 for 82558-based adapters + 1 for 82559(and higher)-based adapters + On uploads the micro code to the adapter, which enables CPU Cycle Saver. + See the section "CPU Cycle Saver" below. + Example usage: insmod e100.o ucode=0 (does not reduce CPU usage) + +XsumRX +Valid Range: 0-1 (0=off, 1=on) +Default Value: 1 + On allows Rx checksum offloading for TCP/UDP packets. Requires that the + hardware support this feature. + + +CPU Cycle Saver +================ + +CPU Cycle Saver reduces CPU utilization by reducing the number of interrupts +that the adapter generates. + +When CPU Cycle Saver is turned off, the adapter generates one interrupt for +every frame that is received. This means that the operating system stops what +it is doing and switches to the network driver in order to process the +receive. + +When CPU Cycle Saver is on, the adapter does not generate an interrupt for +every frame it receives. Instead, it waits until it receives several frames +before generating an interrupt. This reduces the amount of time spent +switching to and from the driver. + +CPU Cycle Saver consists of these arguments: IntDelay, BundleMax and +BundleSmallFr. When IntDelay is increased, the adapter waits longer for +frames to arrive before generating the interrupt. By increasing BundleMax, +the network adapter waits for the number of frames specified to arrive before +generating the interrupt. When BundleSmallFr is disabled, the adapter does +not bundle packets that are smaller than 128 bytes. Such small packets are +often, but not always, control packets that are better served immediately. + +For most users, it is recommended that CPU Cycle Saver be used with the +default values specified in the Command Line Parameters section. However, in +some cases, performance problems may occur with CPU Cycle Saver. If such +problems are observed, we recommend turning off this feature by setting +ucode=0. + + +Support +======= + +For general information and support, go to the Intel support website at: + + http://support.intel.com + +If an issue is identified with the released source code on the supported +kernel with a supported adapter, email the specific information related to +the issue to linux.nics@intel.com. + + +License +======= + +This software program is released under the terms of a license agreement +between you ('Licensee') and Intel. Do not use or load this software or any +associated materials (collectively, the 'Software') until you have carefully +read the full terms and conditions of the LICENSE located in this software +package. By loading or using the Software, you agree to the terms of this +Agreement. If you do not agree with the terms of this Agreement, do not +install or use the Software. + +* Other names and brands may be claimed as the property of others. diff --git a/Documentation/networking/e1000.txt b/Documentation/networking/e1000.txt index 2ce10f26fcf4..298c4e1dce17 100644 --- a/Documentation/networking/e1000.txt +++ b/Documentation/networking/e1000.txt @@ -1,7 +1,7 @@ Linux* Base Driver for the Intel(R) PRO/1000 Family of Adapters =============================================================== -February 5, 2002 +April 23, 2002 Contents @@ -11,6 +11,7 @@ Contents - Supported Adapters - Command Line Parameters - Speed and Duplex Configuration +- Additional Configurations - Known Issues - Support @@ -19,17 +20,27 @@ In This Release =============== This file describes the Linux* Base Driver for the Intel(R) PRO/1000 Family -of Adapters, version 4.2.x. -This driver includes support for Itanium(TM)-based systems. +of Adapters, version 4.2.x. This driver includes support for +Itanium(TM)-based systems. This release version includes the following: - - support for the ethtool 1.4 interface. A third-party application can use + - Support for the ethtool 1.5 interface. A third-party application can use the ethtool interface to get and set driver parameters. - - the zero copy feature. Zero copy provides faster information throughput. - By default, this feature is enabled if using a kernel that supports it. - Zero copy is not supported on the original PWLA8490 (plain) adapter. + - Zero copy. This feature provides faster data throughput. Enabled by + default in supporting kernels. It is not supported on the Intel(R) + PRO/1000 Gigabit Server Adapter. + +New features include: + + - Support for the 82545 and 82546-based adapters listed below + + - Wake on LAN* support via ethtool for 82540, 82544, 82545, and 82546- + based adapters + + - Adaptive IFS for increased performance at half duplex + Supported Adapters @@ -38,21 +49,32 @@ Supported Adapters The following Intel network adapters are compatible with the drivers in this release: - Controller Adapter Name Board IDs - ---------- ------------ --------- + Controller Adapter Name Board IDs + ---------- ------------ --------- + + 82542 PRO/1000 Gigabit Server Adapter 700262-xxx, 717037-xxx + + 82543 PRO/1000 F Server Adapter 738640-xxx, A38888-xxx + + 82543 PRO/1000 T Server Adapter A19845-xxx, A33948-xxx + + 82544 PRO/1000 XT Server Adapter A51580-xxx + + 82544 PRO/1000 XF Server Adapter A50484-xxx - 82542 PRO/1000 Gigabit Server Adapter 700262-xxx, 717037-xxx + 82544 PRO/1000 T Desktop Adapter A62947-xxx - 82543 PRO/1000 F Server Adapter 738640-xxx, A38888-xxx, - A06512-xxx + 82540 PRO/1000 MT Desktop Adapter A78408-xxx - 82543 PRO/1000 T Server Adapter A19845-xxx, A33948-xxx + 82545 PRO/1000 MT Server Adapter A92165-xxx - 82544 PRO/1000 XT Server Adapter A51580-xxx + 82546 PRO/1000 MT Dual Port Server Adapter A92111-xxx - 82544 PRO/1000 XF Server Adapter A50484-xxx + 82545 PRO/1000 MF Server Adapter A91622-xxx - 82544 PRO/1000 T Desktop Adapter A62947-xxx + 82545 PRO/1000 MF Server Adapter(LX) A91624-xxx + + 82546 PRO/1000 MF Dual Port Server Adapter A91620-xxx To verify your Intel adapter is supported, find the board ID number on the @@ -73,8 +95,8 @@ For the latest Intel network drivers for Linux, go to: Command Line Parameters ======================= -If the driver is built as a module, the following parameters are used by -entering them on the command line with the modprobe or insmod command. +If the driver is built as a module, the following parameters are used by +entering them on the command line with the modprobe or insmod command. For example, with two PRO/1000 PCI adapters, entering: insmod e1000 TxDescriptors=80,128 @@ -86,14 +108,14 @@ For more information about the AutoNeg, Duplex, and Speed parameters, see the "Speed and Duplex Configuration" section in this document. -AutoNeg (Intel PRO/1000 T and PRO/1000 XT server adapters only) -Valid Range: 0-0x0F, 0x20-0x2F +AutoNeg (adapters using copper connections only) +Valid Range: 0x01-0x0F, 0x20-0x2F Default Value: 0x2F This parameter is a bit mask that specifies which speed and duplex settings the board advertises. When this parameter is used, the Speed and Duplex parameters must not be specified. -Duplex (Intel PRO/1000 T and PRO/1000 XT server adapters only) +Duplex (adapters using copper connections only) Valid Range: 0-2 (0=auto-negotiate, 1=half, 2=full) Default Value: 0 Defines the direction in which data is allowed to flow. Can by either one @@ -109,26 +131,27 @@ Default: Read flow control settings from the EEPROM RxDescriptors Valid Range: 80-256 for 82542 and 82543-based adapters - 80-4096 for 82544-based adapters -Default Value: 256 + 80-4096 for 82540, 82544, 82545, and 82546-based adapters +Default Value: 80 This value is the number of receive descriptors allocated by the driver. Increasing this value allows the driver to buffer more incoming packets. Each descriptor is 16 bytes. A receive buffer is also allocated for each descriptor and can be either 2048, 4096, 8192, or 16384 bytes, depending - on the MTU setting. + on the MTU setting. The maximum MTU size is 16110. RxIntDelay Valid Range: 0-65535 (0=off) -Default Value: 64 +Default Value: 64 (82542, 82543, and 82544-based adapters) + 128 (82540, 82545, and 82546-based adapters) This value delays the generation of receive interrupts in units of 1.024 microseconds. Receive interrupt reduction can improve CPU efficiency if properly tuned for specific network traffic. Increasing this value adds extra latency to frame reception and can end up decreasing the throughput - of TCP traffic. If the system is reporting dropped receives, this value + of TCP traffic. If the system is reporting dropped receives, this value may be set too high, causing the driver to run out of available receive descriptors. -Speed (Intel PRO/1000 T and PRO/1000 XT server adapters only) +Speed (adapters using copper connections only) Valid Settings: 0, 10, 100, 1000 Default Value: 0 (auto-negotiate at all supported speeds) Speed forces the line speed to the specified value in megabits per second @@ -138,21 +161,12 @@ Default Value: 0 (auto-negotiate at all supported speeds) TxDescriptors Valid Range: 80-256 for 82542 and 82543-based adapters - 80-4096 for 82544-based adapters + 80-4096 for 82540, 82544, 82545, and 82546-based adapters Default Value: 256 This value is the number of transmit descriptors allocated by the driver. Increasing this value allows the driver to queue more transmits. Each descriptor is 16 bytes. -TxIntDelay -Valid Range: 0-65535 (0=off) -Default Value: 64 - This value delays the generation of transmit interrupts in units of 1.024 - microseconds. Transmit interrupt reduction can improve CPU efficiency if - properly tuned for specific network traffic. If the system is reporting - dropped transmits, this value may be set too high causing the driver to - run out of available transmit descriptors. - XsumRX (not available on the PRO/1000 Gigabit Server Adapter) Valid Range: 0-1 Default Value: 1 @@ -163,9 +177,8 @@ Default Value: 1 Speed and Duplex Configuration ============================== -Three keywords are used to control the speed and duplex configuration of the -PRO/1000 T and PRO/1000 XT server adapters. These keywords are Speed, Duplex, -and AutoNeg. +Three keywords are used to control the speed and duplex configuration. These +keywords are Speed, Duplex, and AutoNeg. If the board uses a fiber interface, these keywords are ignored, and the fiber interface board only links at 1000 Mbps full-duplex. @@ -199,16 +212,32 @@ set to auto-negotiate. If the link partner is forced speed/duplex, the adapter MUST be forced to the same speed/duplex. +Additional Configurations +========================= + + Jumbo Frames + ------------ + + The driver supports Jumbo Frames for all adapters except 82542-based + adapters. Jumbo Frames support is enabled by changing the MTU to a value + larger than the default of 1500. Use the ifconfig command to increase the + MTU size. For example: + + ifconfig ethx mtu 9000 up + + Known Issues ============ - Driver Hangs Under Heavy Traffic Loads - -------------------------------------- + Inconsistent Driver Behavior Under Heavy Traffic Loads + ------------------------------------------------------ - Intel is aware that previously released e1000 drivers may hang under very - specific types of heavy traffic loads. This version includes a workaround - that resets the adapter automatically if a hang condition is detected. This - workaround ensures network traffic flow is not affected when a hang occurs. + Adapters based on the Intel 82543 and 82544 LAN controllers may hang (stop + transmitting) under certain network conditions. If this occurs a message + is logged in the system event log. In addition, the controller is + automatically reset, restoring the network connection. To eliminate the + potential for the hang change the RxIntDelay parameter to zero. For details + on the RxIntDelay parameter see the Command Line Parameters section. Jumbo Frames System Requirement ------------------------------- diff --git a/MAINTAINERS b/MAINTAINERS index d05b407ccef3..45bb8eb718ce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -795,6 +795,11 @@ P: Tigran Aivazian M: tigran@veritas.com S: Maintained +INTEL PRO/100 ETHERNET SUPPORT +P: Scott Feldman +M: scott.feldman@intel.com +S: Supported + INTEL PRO/1000 GIGABIT ETHERNET SUPPORT P: Chris Leech M: christopher.leech@intel.com diff --git a/drivers/net/Config.help b/drivers/net/Config.help index 2a1c47dfa8f9..77b0e81d6579 100644 --- a/drivers/net/Config.help +++ b/drivers/net/Config.help @@ -828,18 +828,23 @@ CONFIG_E1000 This driver supports Intel(R) PRO/1000 gigabit ethernet family of adapters, which includes: - Controller Adapter Name Board IDs - ---------- ------------ --------- - 82542 PRO/1000 Gigabit Server Adapter 700262-xxx, - 717037-xxx - 82543 PRO/1000 F Server Adapter 738640-xxx, - A38888-xxx, - A06512-xxx - 82543 PRO/1000 T Server Adapter A19845-xxx, - A33948-xxx - 82544 PRO/1000 XT Server Adapter A51580-xxx - 82544 PRO/1000 XF Server Adapter A50484-xxx - 82544 PRO/1000 T Desktop Adapter A62947-xxx + Controller Adapter Name Board IDs + ---------- ------------ --------- + 82542 PRO/1000 Gigabit Server Adapter 700262-xxx, + 717037-xxx + 82543 PRO/1000 F Server Adapter 738640-xxx, + A38888-xxx + 82543 PRO/1000 T Server Adapter A19845-xxx, + A33948-xxx + 82544 PRO/1000 XT Server Adapter A51580-xxx + 82544 PRO/1000 XF Server Adapter A50484-xxx + 82544 PRO/1000 T Desktop Adapter A62947-xxx + 82540 PRO/1000 MT Desktop Adapter A78408-xxx + 82545 PRO/1000 MT Server Adapter A92165-xxx + 82546 PRO/1000 MT Dual Port Server Adapter A92111-xxx + 82545 PRO/1000 MF Server Adapter A91622-xxx + 82545 PRO/1000 MF Server Adapter(LX) A91624-xxx + 82546 PRO/1000 MF Dual Port Server Adapter A91620-xxx For more information on how to identify your adapter, go to the Adapter & Driver ID Guide at: @@ -1358,6 +1363,80 @@ CONFIG_EEPRO100 a module, say M here and read <file:Documentation/modules.txt> as well as <file:Documentation/networking/net-modules.txt>. +CONFIG_E100 + This driver supports Intel(R) PRO/100 family of adapters, which + includes: + + Controller Adapter Name Board IDs + ---------- ------------ --------- + + 82558 PRO/100+ PCI Adapter 668081-xxx, + 689661-xxx + 82558 PRO/100+ Management Adapter 691334-xxx, + 701738-xxx, + 721383-xxx + 82558 PRO/100+ Dual Port Server Adapter 714303-xxx, + 711269-xxx, + A28276-xxx + 82558 PRO/100+ PCI Server Adapter 710550-xxx + 82550 PRO/100 S Server Adapter 752438-xxx + 82559 A56831-xxx, + A10563-xxx, + A12171-xxx, + A12321-xxx, + A12320-xxx, + A12170-xxx + 748568-xxx + 748565-xxx + 82550 PRO/100 S Desktop Adapter 751767-xxx + 82559 748592-xxx, + A12167-xxx, + A12318-xxx, + A12317-xxx, + A12165-xxx, + 748569-xxx + 82559 PRO/100+ Server Adapter 729757-xxx + 82559 PRO/100 S Management Adapter 748566-xxx, + 748564-xxx + 82550 PRO/100 S Dual Port Server Adapter A56831-xxx + 82551 PRO/100 M Desktop Adapter A80897-xxx + PRO/100 S Advanced Management Adapter + 747842-xxx, + 745171-xxx + CNR PRO/100 VE Desktop Adapter A10386-xxx, + A10725-xxx, + A23801-xxx, + A19716-xxx + PRO/100 VM Desktop Adapter A14323-xxx, + A19725-xxx, + A23801-xxx, + A22220-xxx, + A23796-xxx + + + To verify that your adapter is supported, find the board ID number + on the adapter. Look for a label that has a barcode and a number + in the format 123456-001 (six digits hyphen three digits). Match + this to the list of numbers above. + + For more information on how to identify your adapter, go to the + Adapter & Driver ID Guide at: + + http://support.intel.com/support/network/adapter/pro100/21397.htm + + For the latest Intel PRO/100 network driver for Linux, see: + + http://appsr.intel.com/scripts-df/support_intel.asp + + More specific information on configuring the driver is in + <file:Documentation/networking/e100.txt>. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called e100.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt> as well + as <file:Documentation/networking/net-modules.txt>. + CONFIG_FEALNX Say Y here to support the Mysom MTD-800 family of PCI-based Ethernet cards. Specifications and data at diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c index d3115009f921..76d6a4260b3f 100644 --- a/drivers/net/dl2k.c +++ b/drivers/net/dl2k.c @@ -29,24 +29,28 @@ 1.08 2002/01/17 Fixed the multicast bug. 1.09 2002/03/07 Move rx-poll-now to re-fill loop. Added rio_timer() to watch rx buffers. + 1.10 2002/04/16 Fixed miscount of carrier error. + 1.11 2002/05/23 Added ISR schedule scheme. + Fixed miscount of rx frame error for DGE-550SX. + Fixed VLAN bug. */ #include "dl2k.h" static char version[] __devinitdata = - KERN_INFO "D-Link DL2000-based linux driver v1.09 2002/03/07\n"; + KERN_INFO "D-Link DL2000-based linux driver v1.11 2002/05/23\n"; #define MAX_UNITS 8 static int mtu[MAX_UNITS]; static int vlan[MAX_UNITS]; static int jumbo[MAX_UNITS]; static char *media[MAX_UNITS]; -static int tx_flow[MAX_UNITS]; -static int rx_flow[MAX_UNITS]; +static int tx_flow=-1; +static int rx_flow=-1; static int copy_thresh; -static int rx_coalesce; /* Rx frame count each interrupt */ -static int rx_timeout; /* Rx DMA wait time in 64ns increments */ -static int tx_coalesce = DEFAULT_TXC; /* HW xmit count each TxComplete [1-8] */ +static int rx_coalesce=10; /* Rx frame count each interrupt */ +static int rx_timeout=200; /* Rx DMA wait time in 640ns increments */ +static int tx_coalesce=16; /* HW xmit count each TxDMAComplete */ MODULE_AUTHOR ("Edward Peng"); @@ -56,16 +60,16 @@ MODULE_PARM (mtu, "1-" __MODULE_STRING (MAX_UNITS) "i"); MODULE_PARM (media, "1-" __MODULE_STRING (MAX_UNITS) "s"); MODULE_PARM (vlan, "1-" __MODULE_STRING (MAX_UNITS) "i"); MODULE_PARM (jumbo, "1-" __MODULE_STRING (MAX_UNITS) "i"); -MODULE_PARM (tx_flow, "1-" __MODULE_STRING (MAX_UNITS) "i"); -MODULE_PARM (rx_flow, "1-" __MODULE_STRING (MAX_UNITS) "i"); +MODULE_PARM (tx_flow, "i"); +MODULE_PARM (rx_flow, "i"); MODULE_PARM (copy_thresh, "i"); MODULE_PARM (rx_coalesce, "i"); /* Rx frame count each interrupt */ MODULE_PARM (rx_timeout, "i"); /* Rx DMA wait time in 64ns increments */ -MODULE_PARM (tx_coalesce, "i"); /* HW xmit count each TxComplete [1-8] */ +MODULE_PARM (tx_coalesce, "i"); /* HW xmit count each TxDMAComplete */ /* Enable the default interrupts */ -#define DEFAULT_INTR (RxDMAComplete | HostError | IntRequested | TxComplete| \ +#define DEFAULT_INTR (RxDMAComplete | HostError | IntRequested | TxDMAComplete| \ UpdateStats | LinkEvent) #define EnableInt() \ writew(DEFAULT_INTR, ioaddr + IntEnable) @@ -75,16 +79,18 @@ static int multicast_filter_limit = 0x40; static int rio_open (struct net_device *dev); static void rio_timer (unsigned long data); -static void tx_timeout (struct net_device *dev); +static void rio_tx_timeout (struct net_device *dev); static void alloc_list (struct net_device *dev); static int start_xmit (struct sk_buff *skb, struct net_device *dev); static void rio_interrupt (int irq, void *dev_instance, struct pt_regs *regs); +static void rio_free_tx (struct net_device *dev, int irq); static void tx_error (struct net_device *dev, int tx_status); static int receive_packet (struct net_device *dev); static void rio_error (struct net_device *dev, int int_status); static int change_mtu (struct net_device *dev, int new_mtu); static void set_multicast (struct net_device *dev); static struct net_device_stats *get_stats (struct net_device *dev); +static int clear_stats (struct net_device *dev); static int rio_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); static int rio_close (struct net_device *dev); static int find_miiphy (struct net_device *dev); @@ -98,9 +104,6 @@ static int mii_get_media_pcs (struct net_device *dev); static int mii_read (struct net_device *dev, int phy_addr, int reg_num); static int mii_write (struct net_device *dev, int phy_addr, int reg_num, u16 data); -#ifdef RIO_DEBUG -static int rio_ioctl_ext (struct net_device *dev, struct ioctl_data *iodata); -#endif static int __devinit rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) @@ -109,7 +112,7 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) struct netdev_private *np; static int card_idx; int chip_idx = ent->driver_data; - int err, irq = pdev->irq; + int err, irq; long ioaddr; static int version_printed; void *ring_space; @@ -122,6 +125,7 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) if (err) return err; + irq = pdev->irq; err = pci_request_regions (pdev, "dl2k"); if (err) goto err_out_disable; @@ -149,7 +153,7 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) np = dev->priv; np->chip_id = chip_idx; np->pdev = pdev; - spin_lock_init (&np->lock); + spin_lock_init (&np->tx_lock); spin_lock_init (&np->rx_lock); /* Parse manual configuration */ @@ -199,17 +203,18 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) } np->vlan = (vlan[card_idx] > 0 && vlan[card_idx] < 4096) ? vlan[card_idx] : 0; - if (rx_coalesce != 0 && rx_timeout != 0) { + if (rx_coalesce > 0 && rx_timeout > 0) { np->rx_coalesce = rx_coalesce; np->rx_timeout = rx_timeout; np->coalesce = 1; } - np->tx_flow = (tx_flow[card_idx]) ? 1 : 0; - np->rx_flow = (rx_flow[card_idx]) ? 1 : 0; + np->tx_flow = (tx_flow == 0) ? 0 : 1; + np->rx_flow = (rx_flow == 0) ? 0 : 1; + if (tx_coalesce < 1) tx_coalesce = 1; - if (tx_coalesce > 8) - tx_coalesce = 8; + if (tx_coalesce > TX_RING_SIZE-1) + tx_coalesce = TX_RING_SIZE - 1; } dev->open = &rio_open; dev->hard_start_xmit = &start_xmit; @@ -217,7 +222,7 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) dev->get_stats = &get_stats; dev->set_multicast_list = &set_multicast; dev->do_ioctl = &rio_ioctl; - dev->tx_timeout = &tx_timeout; + dev->tx_timeout = &rio_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; dev->change_mtu = &change_mtu; #if 0 @@ -247,6 +252,7 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) /* Fiber device? */ np->phy_media = (readw(ioaddr + ASICCtrl) & PhyMedia) ? 1 : 0; + np->link_status = 0; /* Set media and reset PHY */ if (np->phy_media) { /* default 1000mbps_fd for fiber deivices */ @@ -281,6 +287,15 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) dev->name, np->name, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5], irq); + if (tx_coalesce > 1) + printk(KERN_INFO "tx_coalesce:\t%d packets\n", + tx_coalesce); + if (np->coalesce) + printk(KERN_INFO "rx_coalesce:\t%d packets\n" + KERN_INFO "rx_timeout: \t%d ns\n", + np->rx_coalesce, np->rx_timeout*640); + if (np->vlan) + printk(KERN_INFO "vlan(id):\t%d\n", np->vlan); return 0; err_out_unmap_rx: @@ -327,7 +342,7 @@ find_miiphy (struct net_device *dev) return 0; } -static int __devinit +int parse_eeprom (struct net_device *dev) { int i, j; @@ -346,7 +361,7 @@ parse_eeprom (struct net_device *dev) } /* Check CRC */ - crc = ~ether_crc_le(256 - 4, sromdata); + crc = ~ether_crc_le(256 - 4, sromdata); if (psrom->crc != crc) { printk (KERN_ERR "%s: EEPROM data CRC error.\n", dev->name); return -1; @@ -431,8 +446,10 @@ rio_open (struct net_device *dev) writeb (0xff, ioaddr + TxDMAPollPeriod); writeb (0x30, ioaddr + RxDMABurstThresh); writeb (0x30, ioaddr + RxDMAUrgentThresh); - netif_start_queue (dev); - writel (StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl); + + /* clear statistics */ + clear_stats (dev); + /* VLAN supported */ if (np->vlan) { /* priority field in RxDMAIntCtrl */ @@ -451,15 +468,20 @@ rio_open (struct net_device *dev) /* Enable default interrupts */ EnableInt (); - /* clear statistics */ - get_stats (dev); init_timer (&np->timer); np->timer.expires = jiffies + 1*HZ; np->timer.data = (unsigned long) dev; np->timer.function = &rio_timer; add_timer (&np->timer); + + /* Start Tx/Rx */ + writel (readl (ioaddr + MACCtrl) | StatsEnable | RxEnable | TxEnable, + ioaddr + MACCtrl); + + netif_start_queue (dev); return 0; } + static void rio_timer (unsigned long data) { @@ -469,10 +491,10 @@ rio_timer (unsigned long data) int next_tick = 1*HZ; unsigned long flags; + spin_lock_irqsave(&np->rx_lock, flags); /* Recover rx ring exhausted error */ if (np->cur_rx - np->old_rx >= RX_RING_SIZE) { printk(KERN_INFO "Try to recover rx ring exhausted...\n"); - spin_lock_irqsave(&np->rx_lock, flags); /* Re-allocate skbuffs to fill the descriptor ring */ for (; np->cur_rx - np->old_rx > 0; np->old_rx++) { struct sk_buff *skb; @@ -500,43 +522,22 @@ rio_timer (unsigned long data) cpu_to_le64 (np->rx_buf_sz) << 48; np->rx_ring[entry].status = 0; } /* end for */ - spin_unlock_irqrestore (&np->rx_lock, flags); } /* end if */ + spin_unlock_irqrestore (&np->rx_lock, flags); np->timer.expires = jiffies + next_tick; add_timer(&np->timer); } static void -tx_timeout (struct net_device *dev) +rio_tx_timeout (struct net_device *dev) { - struct netdev_private *np = dev->priv; long ioaddr = dev->base_addr; printk (KERN_INFO "%s: Tx timed out (%4.4x), is buffer full?\n", dev->name, readl (ioaddr + TxStatus)); - /* Free used tx skbuffs */ - for (; np->cur_tx - np->old_tx > 0; np->old_tx++) { - int entry = np->old_tx % TX_RING_SIZE; - struct sk_buff *skb; - - if (!(np->tx_ring[entry].status & TFDDone)) - break; - skb = np->tx_skbuff[entry]; - pci_unmap_single (np->pdev, - np->tx_ring[entry].fraginfo, - skb->len, PCI_DMA_TODEVICE); - dev_kfree_skb_irq (skb); - np->tx_skbuff[entry] = 0; - } + rio_free_tx(dev, 0); dev->if_port = 0; dev->trans_start = jiffies; - np->stats.tx_errors++; - /* If the ring is no longer full, clear tx_full and - call netif_wake_queue() */ - if (np->tx_full && np->cur_tx - np->old_tx < TX_QUEUE_LEN - 1) { - np->tx_full = 0; - netif_wake_queue (dev); - } } /* allocate and initialize Tx and Rx descriptors */ @@ -546,7 +547,6 @@ alloc_list (struct net_device *dev) struct netdev_private *np = dev->priv; int i; - np->tx_full = 0; np->cur_rx = np->cur_tx = 0; np->old_rx = np->old_tx = 0; np->rx_buf_sz = (dev->mtu <= 1500 ? PACKET_SIZE : dev->mtu + 32); @@ -605,18 +605,17 @@ start_xmit (struct sk_buff *skb, struct net_device *dev) struct netdev_desc *txdesc; unsigned entry; u32 ioaddr; - int tx_shift; - unsigned long flags; + u64 tfc_vlan_tag = 0; + if (np->link_status == 0) { /* Link Down */ + dev_kfree_skb(skb); + return 0; + } ioaddr = dev->base_addr; entry = np->cur_tx % TX_RING_SIZE; np->tx_skbuff[entry] = skb; txdesc = &np->tx_ring[entry]; - /* Set TFDDone to avoid TxDMA gather this descriptor */ - txdesc->status = cpu_to_le64 (TFDDone); - txdesc->status |= - cpu_to_le64 (entry | WordAlignDisable | (1 << FragCountShift)); #if 0 if (skb->ip_summed == CHECKSUM_HW) { txdesc->status |= @@ -630,31 +629,33 @@ start_xmit (struct sk_buff *skb, struct net_device *dev) (cpu_to_le64 (np->vlan) << 32) | (cpu_to_le64 (skb->priority) << 45); } - - /* Send one packet each time at 10Mbps mode */ - /* Tx coalescing loop do not exceed 8 */ - if (entry % tx_coalesce == 0 || np->speed == 10) - txdesc->status |= cpu_to_le64 (TxIndicate); txdesc->fraginfo = cpu_to_le64 (pci_map_single (np->pdev, skb->data, skb->len, PCI_DMA_TODEVICE)); txdesc->fraginfo |= cpu_to_le64 (skb->len) << 48; - /* Clear TFDDone, then TxDMA start to send this descriptor */ - txdesc->status &= ~cpu_to_le64 (TFDDone); - - DEBUG_TFD_DUMP (np); + /* DL2K bug: DMA fails to get next descriptor ptr in 10Mbps mode + * Work around: Always use 1 descriptor in 10Mbps mode */ + if (entry % tx_coalesce == 0 || np->speed == 10) + txdesc->status = cpu_to_le64 (entry | tfc_vlan_tag | + WordAlignDisable | + TxDMAIndicate | + (1 << FragCountShift)); + else + txdesc->status = cpu_to_le64 (entry | tfc_vlan_tag | + WordAlignDisable | + (1 << FragCountShift)); /* TxDMAPollNow */ writel (readl (ioaddr + DMACtrl) | 0x00001000, ioaddr + DMACtrl); - np->cur_tx++; - if (np->cur_tx - np->old_tx < TX_QUEUE_LEN - 1 && np->speed != 10) { + /* Schedule ISR */ + writel(10000, ioaddr + CountDown); + np->cur_tx = (np->cur_tx + 1) % TX_RING_SIZE; + if ((np->cur_tx - np->old_tx + TX_RING_SIZE) % TX_RING_SIZE + < TX_QUEUE_LEN - 1 && np->speed != 10) { /* do nothing */ - } else { - spin_lock_irqsave(&np->lock, flags); - np->tx_full = 1; + } else if (!netif_queue_stopped(dev)) { netif_stop_queue (dev); - spin_unlock_irqrestore (&np->lock, flags); } /* The first TFDListPtr */ @@ -664,14 +665,6 @@ start_xmit (struct sk_buff *skb, struct net_device *dev) writel (0, dev->base_addr + TFDListPtr1); } - if (np->old_tx > TX_RING_SIZE) { - spin_lock_irqsave (&np->lock, flags); - tx_shift = TX_RING_SIZE; - np->old_tx -= tx_shift; - np->cur_tx -= tx_shift; - spin_unlock_irqrestore (&np->lock, flags); - } - /* NETDEV WATCHDOG timer */ dev->trans_start = jiffies; return 0; @@ -688,61 +681,79 @@ rio_interrupt (int irq, void *dev_instance, struct pt_regs *rgs) ioaddr = dev->base_addr; np = dev->priv; - spin_lock(&np->lock); while (1) { int_status = readw (ioaddr + IntStatus); writew (int_status, ioaddr + IntStatus); int_status &= DEFAULT_INTR; - if (int_status == 0) + if (int_status == 0 || --cnt < 0) break; /* Processing received packets */ if (int_status & RxDMAComplete) receive_packet (dev); - /* TxComplete interrupt */ - if ((int_status & TxComplete) || np->tx_full) { + /* TxDMAComplete interrupt */ + if ((int_status & (TxDMAComplete|IntRequested))) { int tx_status; tx_status = readl (ioaddr + TxStatus); if (tx_status & 0x01) tx_error (dev, tx_status); /* Free used tx skbuffs */ - for (;np->cur_tx - np->old_tx > 0; np->old_tx++) { - int entry = np->old_tx % TX_RING_SIZE; - struct sk_buff *skb; - - if (!(np->tx_ring[entry].status & TFDDone)) - break; - skb = np->tx_skbuff[entry]; - pci_unmap_single (np->pdev, - np->tx_ring[entry].fraginfo, - skb->len, PCI_DMA_TODEVICE); - dev_kfree_skb_irq (skb); - np->tx_skbuff[entry] = 0; - } - } - /* If the ring is no longer full, clear tx_full and - call netif_wake_queue() */ - if (np->tx_full && np->cur_tx - np->old_tx < TX_QUEUE_LEN - 1) { - if (np->speed != 10 || int_status & TxComplete) { - np->tx_full = 0; - netif_wake_queue (dev); - } + rio_free_tx (dev, 1); } /* Handle uncommon events */ if (int_status & - (IntRequested | HostError | LinkEvent | UpdateStats)) + (HostError | LinkEvent | UpdateStats)) rio_error (dev, int_status); - /* If too much interrupts here, disable all interrupts except - IntRequest. When CountDown down to 0, IntRequest will - be caught by rio_error() to recovery the interrupts */ - if (--cnt < 0) { - get_stats (dev); - writel (1, ioaddr + CountDown); - writew (IntRequested, ioaddr + IntEnable); + } + if (np->cur_tx != np->old_tx) + writel (100, ioaddr + CountDown); +} + +static void +rio_free_tx (struct net_device *dev, int irq) +{ + struct netdev_private *np = (struct netdev_private *) dev->priv; + int entry = np->old_tx % TX_RING_SIZE; + int tx_use = 0; + long flag = 0; + + if (irq) + spin_lock_irqsave(&np->tx_lock, flag); + else + spin_lock(&np->tx_lock); + /* Free used tx skbuffs */ + while (entry != np->cur_tx) { + struct sk_buff *skb; + + if (!(np->tx_ring[entry].status & TFDDone)) break; - } + skb = np->tx_skbuff[entry]; + pci_unmap_single (np->pdev, + np->tx_ring[entry].fraginfo, + skb->len, PCI_DMA_TODEVICE); + if (irq) + dev_kfree_skb_irq (skb); + else + dev_kfree_skb (skb); + + np->tx_skbuff[entry] = 0; + entry = (entry + 1) % TX_RING_SIZE; + tx_use++; + } + if (irq) + spin_unlock_irqrestore(&np->tx_lock, flag); + else + spin_unlock(&np->tx_lock); + np->old_tx = entry; + + /* If the ring is no longer full, clear tx_full and + call netif_wake_queue() */ + + if (netif_queue_stopped(dev) && + ((np->cur_tx - np->old_tx + TX_RING_SIZE) % TX_RING_SIZE + < TX_QUEUE_LEN - 1 || np->speed == 10)) { + netif_wake_queue (dev); } - spin_unlock(&np->lock); } static void @@ -755,11 +766,10 @@ tx_error (struct net_device *dev, int tx_status) np = dev->priv; - frame_id = (tx_status & 0xffff0000) >> 16; + frame_id = (tx_status & 0xffff0000); printk (KERN_ERR "%s: Transmit error, TxStatus %4.4x, FrameId %d.\n", dev->name, tx_status, frame_id); np->stats.tx_errors++; - np->stats.tx_dropped++; /* Ttransmit Underrun */ if (tx_status & 0x10) { np->stats.tx_fifo_errors++; @@ -774,20 +784,7 @@ tx_error (struct net_device *dev, int tx_status) break; mdelay (1); } - /* Free completed descriptors */ - for (; np->cur_tx - np->old_tx > 0; np->old_tx++) { - int entry = np->old_tx % TX_RING_SIZE; - struct sk_buff *skb; - if (!(np->tx_ring[entry].status & TFDDone)) - break; - - skb = np->tx_skbuff[entry]; - pci_unmap_single (np->pdev, np->tx_ring[entry].fraginfo, - skb->len, PCI_DMA_TODEVICE); - dev_kfree_skb_irq (skb); - np->tx_skbuff[entry] = 0; - } - + rio_free_tx (dev, 1); /* Reset TFDListPtr */ writel (np->tx_ring_dma + np->old_tx * sizeof (struct netdev_desc), @@ -810,14 +807,13 @@ tx_error (struct net_device *dev, int tx_status) /* Let TxStartThresh stay default value */ } /* Maximum Collisions */ -#ifdef ETHER_STATS - if (tx_status & 0x08) +#ifdef ETHER_STATS + if (tx_status & 0x08) np->stats.collisions16++; #else - if (tx_status & 0x08) + if (tx_status & 0x08) np->stats.collisions++; #endif - /* Restart the Tx */ writel (readw (dev->base_addr + MACCtrl) | TxEnable, ioaddr + MACCtrl); } @@ -827,16 +823,8 @@ receive_packet (struct net_device *dev) { struct netdev_private *np = (struct netdev_private *) dev->priv; int entry = np->cur_rx % RX_RING_SIZE; - int cnt = np->old_rx + RX_RING_SIZE - np->cur_rx; - int rx_shift; - - spin_lock (&np->rx_lock); - if (np->old_rx > RX_RING_SIZE) { - rx_shift = RX_RING_SIZE; - np->old_rx -= rx_shift; - np->cur_rx -= rx_shift; - } - DEBUG_RFD_DUMP (np, 1); + int cnt = 30; + /* If RFDDone, FrameStart and FrameEnd set, there is a new packet in. */ while (1) { struct netdev_desc *desc = &np->rx_ring[entry]; @@ -852,20 +840,19 @@ receive_packet (struct net_device *dev) frame_status = le64_to_cpu (desc->status); if (--cnt < 0) break; - DEBUG_PKT_DUMP (np, pkt_len); pci_dma_sync_single (np->pdev, desc->fraginfo, np->rx_buf_sz, PCI_DMA_FROMDEVICE); /* Update rx error statistics, drop packet. */ - if (frame_status & 0x003f0000) { + if (frame_status & RFS_Errors) { np->stats.rx_errors++; - if (frame_status & 0x00300000) + if (frame_status & (RxRuntFrame | RxLengthError)) np->stats.rx_length_errors++; - if (frame_status & 0x00010000) - np->stats.rx_fifo_errors++; - if (frame_status & 0x00060000) - np->stats.rx_frame_errors++; - if (frame_status & 0x00080000) + if (frame_status & RxFCSError) np->stats.rx_crc_errors++; + if (frame_status & RxAlignmentError && np->speed != 1000) + np->stats.rx_frame_errors++; + if (frame_status & RxFIFOOverrun) + np->stats.rx_fifo_errors++; } else { struct sk_buff *skb; @@ -893,17 +880,17 @@ receive_packet (struct net_device *dev) skb->ip_summed = CHECKSUM_UNNECESSARY; } #endif - netif_rx (skb); dev->last_rx = jiffies; } - entry = (++np->cur_rx) % RX_RING_SIZE; - + entry = (entry + 1) % RX_RING_SIZE; } + spin_lock(&np->rx_lock); + np->cur_rx = entry; /* Re-allocate skbuffs to fill the descriptor ring */ - for (; np->cur_rx - np->old_rx > 0; np->old_rx++) { + entry = np->old_rx; + while (entry != np->cur_rx) { struct sk_buff *skb; - entry = np->old_rx % RX_RING_SIZE; /* Dropped packets don't need to re-allocate */ if (np->rx_skbuff[entry] == NULL) { skb = dev_alloc_skb (np->rx_buf_sz); @@ -927,11 +914,9 @@ receive_packet (struct net_device *dev) np->rx_ring[entry].fraginfo |= cpu_to_le64 (np->rx_buf_sz) << 48; np->rx_ring[entry].status = 0; - /* RxDMAPollNow */ - writel (readl (dev->base_addr + DMACtrl) | 0x00000010, - dev->base_addr + DMACtrl); + entry = (entry + 1) % RX_RING_SIZE; } - DEBUG_RFD_DUMP (np, 2); + np->old_rx = entry; spin_unlock(&np->rx_lock); return 0; } @@ -943,14 +928,6 @@ rio_error (struct net_device *dev, int int_status) struct netdev_private *np = dev->priv; u16 macctrl; - /* Stop the down counter and recovery the interrupt */ - if (int_status & IntRequested) { - writew (0, ioaddr + IntEnable); - writel (0, ioaddr + CountDown); - /* Enable default interrupts */ - EnableInt (); - } - /* Link change event */ if (int_status & LinkEvent) { if (mii_wait_link (dev, 10) == 0) { @@ -960,14 +937,19 @@ rio_error (struct net_device *dev, int int_status) else mii_get_media (dev); macctrl = 0; + macctrl |= (np->vlan) ? AutoVLANuntagging : 0; macctrl |= (np->full_duplex) ? DuplexSelect : 0; macctrl |= (np->tx_flow) ? TxFlowControlEnable : 0; macctrl |= (np->rx_flow) ? RxFlowControlEnable : 0; writew(macctrl, ioaddr + MACCtrl); + np->link_status = 1; + netif_carrier_on(dev); } else { printk (KERN_INFO "%s: Link off\n", dev->name); + np->link_status = 0; + netif_carrier_off(dev); } } @@ -991,41 +973,100 @@ get_stats (struct net_device *dev) { long ioaddr = dev->base_addr; struct netdev_private *np = dev->priv; - u16 temp1; - u16 temp2; int i; + unsigned int stat_reg; + /* All statistics registers need to be acknowledged, else statistic overflow could cause problems */ + np->stats.rx_packets += readl (ioaddr + FramesRcvOk); np->stats.tx_packets += readl (ioaddr + FramesXmtOk); np->stats.rx_bytes += readl (ioaddr + OctetRcvOk); np->stats.tx_bytes += readl (ioaddr + OctetXmtOk); - temp1 = readw (ioaddr + FrameLostRxError); - np->stats.rx_errors += temp1; - np->stats.rx_missed_errors += temp1; - np->stats.tx_dropped += readw (ioaddr + FramesAbortXSColls); - temp1 = readl (ioaddr + SingleColFrames) + - readl (ioaddr + MultiColFrames) + readl (ioaddr + LateCollisions); - temp2 = readw (ioaddr + CarrierSenseErrors); - np->stats.tx_carrier_errors += temp2; - np->stats.tx_errors += readw (ioaddr + FramesWEXDeferal) + - readl (ioaddr + FramesWDeferredXmt) + temp2; - - /* detailed rx_error */ - np->stats.rx_length_errors += readw (ioaddr + FrameTooLongErrors); - np->stats.rx_crc_errors += readw (ioaddr + FrameCheckSeqError); + + np->stats.multicast = readl (ioaddr + McstFramesRcvdOk); + np->stats.collisions += readl (ioaddr + SingleColFrames) + + readl (ioaddr + MultiColFrames); + + /* detailed tx errors */ + stat_reg = readw (ioaddr + FramesAbortXSColls); + np->stats.tx_aborted_errors += stat_reg; + np->stats.tx_errors += stat_reg; + + stat_reg = readw (ioaddr + CarrierSenseErrors); + np->stats.tx_carrier_errors += stat_reg; + np->stats.tx_errors += stat_reg; /* Clear all other statistic register. */ - readw (ioaddr + InRangeLengthErrors); - readw (ioaddr + MacControlFramesXmtd); + readl (ioaddr + McstOctetXmtOk); readw (ioaddr + BcstFramesXmtdOk); readl (ioaddr + McstFramesXmtdOk); + readw (ioaddr + BcstFramesRcvdOk); + readw (ioaddr + MacControlFramesRcvd); + readw (ioaddr + FrameTooLongErrors); + readw (ioaddr + InRangeLengthErrors); + readw (ioaddr + FramesCheckSeqErrors); + readw (ioaddr + FramesLostRxErrors); + readl (ioaddr + McstOctetXmtOk); readl (ioaddr + BcstOctetXmtOk); + readl (ioaddr + McstFramesXmtdOk); + readl (ioaddr + FramesWDeferredXmt); + readl (ioaddr + LateCollisions); + readw (ioaddr + BcstFramesXmtdOk); + readw (ioaddr + MacControlFramesXmtd); + readw (ioaddr + FramesWEXDeferal); + + + for (i = 0x100; i <= 0x150; i += 4) + readl (ioaddr + i); + readw (ioaddr + TxJumboFrames); + readw (ioaddr + RxJumboFrames); + readw (ioaddr + TCPCheckSumErrors); + readw (ioaddr + UDPCheckSumErrors); + readw (ioaddr + IPCheckSumErrors); + return &np->stats; +} + +static int +clear_stats (struct net_device *dev) +{ + long ioaddr = dev->base_addr; + int i; + + /* All statistics registers need to be acknowledged, + else statistic overflow could cause problems */ + readl (ioaddr + FramesRcvOk); + readl (ioaddr + FramesXmtOk); + readl (ioaddr + OctetRcvOk); + readl (ioaddr + OctetXmtOk); + + readl (ioaddr + McstFramesRcvdOk); + readl (ioaddr + SingleColFrames); + readl (ioaddr + MultiColFrames); + readl (ioaddr + LateCollisions); + /* detailed rx errors */ + readw (ioaddr + FrameTooLongErrors); + readw (ioaddr + InRangeLengthErrors); + readw (ioaddr + FramesCheckSeqErrors); + readw (ioaddr + FramesLostRxErrors); + + /* detailed tx errors */ + readw (ioaddr + FramesAbortXSColls); + readw (ioaddr + CarrierSenseErrors); + + /* Clear all other statistic register. */ readl (ioaddr + McstOctetXmtOk); + readw (ioaddr + BcstFramesXmtdOk); + readl (ioaddr + McstFramesXmtdOk); + readw (ioaddr + BcstFramesRcvdOk); readw (ioaddr + MacControlFramesRcvd); - readw (ioaddr + BcstFramesRcvOk); - readl (ioaddr + McstFramesRcvOk); - readl (ioaddr + BcstOctetRcvOk); + readl (ioaddr + McstOctetXmtOk); + readl (ioaddr + BcstOctetXmtOk); + readl (ioaddr + McstFramesXmtdOk); + readl (ioaddr + FramesWDeferredXmt); + readw (ioaddr + BcstFramesXmtdOk); + readw (ioaddr + MacControlFramesXmtd); + readw (ioaddr + FramesWEXDeferal); for (i = 0x100; i <= 0x150; i += 4) readl (ioaddr + i); @@ -1034,9 +1075,10 @@ get_stats (struct net_device *dev) readw (ioaddr + TCPCheckSumErrors); readw (ioaddr + UDPCheckSumErrors); readw (ioaddr + IPCheckSumErrors); - return &np->stats; + return 0; } + int change_mtu (struct net_device *dev, int new_mtu) { @@ -1111,51 +1153,35 @@ rio_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) int phy_addr; struct netdev_private *np = dev->priv; struct mii_data *miidata = (struct mii_data *) &rq->ifr_data; -#ifdef RIO_DEBUG - struct ioctl_data *iodata = (struct ioctl_data *) (rq->ifr_data); -#endif - u16 *data = (u16 *) & rq->ifr_data; + struct netdev_desc *desc; int i; phy_addr = np->phy_addr; switch (cmd) { case SIOCDEVPRIVATE: -#ifdef RIO_DEBUG - if (rio_ioctl_ext (dev, iodata) != 0) - return -EOPNOTSUPP; break; -#else - return -EOPNOTSUPP; -#endif + case SIOCDEVPRIVATE + 1: miidata->out_value = mii_read (dev, phy_addr, miidata->reg_num); break; case SIOCDEVPRIVATE + 2: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; mii_write (dev, phy_addr, miidata->reg_num, miidata->in_value); break; case SIOCDEVPRIVATE + 3: - np->rx_debug = (data[0] <= 7) ? data[0] : 0; - printk ("rx_debug = %d\n", np->rx_debug); break; case SIOCDEVPRIVATE + 4: - np->tx_debug = (data[0] <= 7) ? data[0] : 0; - printk ("tx_debug = %d\n", np->tx_debug); break; case SIOCDEVPRIVATE + 5: - np->tx_full = 1; netif_stop_queue (dev); break; case SIOCDEVPRIVATE + 6: - np->tx_full = 0; netif_wake_queue (dev); break; case SIOCDEVPRIVATE + 7: printk ("tx_full=%x cur_tx=%lx old_tx=%lx cur_rx=%lx old_rx=%lx\n", - np->tx_full, np->cur_tx, np->old_tx, np->cur_rx, + netif_queue_stopped(dev), np->cur_tx, np->old_tx, np->cur_rx, np->old_rx); break; case SIOCDEVPRIVATE + 8: @@ -1163,7 +1189,8 @@ rio_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) for (i = 0; i < TX_RING_SIZE; i++) { desc = &np->tx_ring[i]; printk - ("cur:%08x next:%08x status:%08x frag1:%08x frag0:%08x", + ("%02x:cur:%08x next:%08x status:%08x frag1:%08x frag0:%08x", + i, (u32) (np->tx_ring_dma + i * sizeof (*desc)), (u32) desc->next_desc, (u32) desc->status, (u32) (desc->fraginfo >> 32), @@ -1172,110 +1199,17 @@ rio_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) } printk ("\n"); break; - default: - return -EOPNOTSUPP; - } - return 0; -} - -#ifdef RIO_DEBUG -int -rio_ioctl_ext (struct net_device *dev, struct ioctl_data *iodata) -{ - struct netdev_private *np = dev->priv; - int phy_addr = np->phy_addr; - u32 hi, lo; - int i; - BMCR_t bmcr; - BMSR_t bmsr; - - if (iodata == NULL) - goto invalid_cmd; - if (strcmp (iodata->signature, "rio") != 0) - goto invalid_cmd; - - switch (iodata->cmd) { - case 0: - for (i = 0; i < TX_RING_SIZE; i++) { - hi = np->tx_ring[i].status >> 32; - lo = np->tx_ring[i].status; - printk ("TFC=%08x %08x \n", hi, lo); - - } - break; - case 1: - for (i = 0; i < RX_RING_SIZE; i++) { - hi = np->rx_ring[i].status >> 32; - lo = np->rx_ring[i].status; - printk ("RFS=%08x %08x \n", hi, lo); - } - break; - case 2: - break; - case 3: - if (iodata->data != NULL) - np->tx_debug = iodata->data[0]; - break; - case 4: - /* Soft reset PHY */ - mii_write (dev, phy_addr, MII_BMCR, MII_BMCR_RESET); - bmcr.image = 0; - bmcr.bits.an_enable = 1; - bmcr.bits.reset = 1; - mii_write (dev, phy_addr, MII_BMCR, bmcr.image); - break; - case 5: - mii_write (dev, phy_addr, MII_BMCR, 0x1940); - mdelay (10); - mii_write (dev, phy_addr, MII_BMCR, 0x1940); - mdelay (100); /* wait a certain time */ - break; - case 6: - /* 5) Set media and Power Up */ - bmcr.image = 0; - bmcr.bits.power_down = 1; - if (np->an_enable) { - bmcr.bits.an_enable = 1; - } else { - if (np->speed == 100) { - bmcr.bits.speed100 = 1; - bmcr.bits.speed1000 = 0; - printk ("Manual 100 Mbps, "); - } else if (np->speed == 10) { - bmcr.bits.speed100 = 0; - bmcr.bits.speed1000 = 0; - printk ("Manual 10 Mbps, "); - } - if (np->full_duplex) { - bmcr.bits.duplex_mode = 1; - printk ("Full duplex. \n"); - } else { - bmcr.bits.duplex_mode = 0; - printk ("Half duplex.\n"); - } - } - mii_write (dev, phy_addr, MII_BMCR, bmcr.image); - break; - case 7: - bmcr.image = mii_read (dev, phy_addr, MII_BMCR); - bmsr.image = mii_read (dev, phy_addr, MII_BMSR); - printk ("BMCR=%x BMSR=%x LinkUp=%d\n", - bmcr.image, bmsr.image, bmsr.bits.link_status); - break; default: return -EOPNOTSUPP; } return 0; - - invalid_cmd: - return -1; } -#endif + #define EEP_READ 0x0200 #define EEP_BUSY 0x8000 /* Read the EEPROM word */ -static int __devinit +int read_eeprom (long ioaddr, int eep_addr) { int i = 1000; @@ -1441,10 +1375,11 @@ mii_get_media (struct net_device *dev) printk (KERN_INFO "Auto 10 Mbps, Half duplex\n"); } if (negotiate.bits.pause) { - np->tx_flow = 1; - np->rx_flow = 1; + np->tx_flow &= 1; + np->rx_flow &= 1; } else if (negotiate.bits.asymmetric) { - np->rx_flow = 1; + np->tx_flow = 0; + np->rx_flow &= 1; } /* else tx_flow, rx_flow = user select */ } else { @@ -1593,10 +1528,11 @@ mii_get_media_pcs (struct net_device *dev) np->full_duplex = 0; } if (negotiate.bits.pause) { - np->tx_flow = 1; - np->rx_flow = 1; + np->tx_flow &= 1; + np->rx_flow &= 1; } else if (negotiate.bits.asymmetric) { - np->rx_flow = 1; + np->tx_flow = 0; + np->rx_flow &= 1; } /* else tx_flow, rx_flow = user select */ } else { diff --git a/drivers/net/dl2k.h b/drivers/net/dl2k.h index 656832f2ca1d..a3e3b30fc207 100644 --- a/drivers/net/dl2k.h +++ b/drivers/net/dl2k.h @@ -119,13 +119,13 @@ enum dl2x_offsets { McstOctetRcvOk = 0xac, BcstOctetRcvOk = 0xb0, FramesRcvOk = 0xb4, - McstFramesRcvOk = 0xb8, - BcstFramesRcvOk = 0xbe, + McstFramesRcvdOk = 0xb8, + BcstFramesRcvdOk = 0xbe, MacControlFramesRcvd = 0xc6, FrameTooLongErrors = 0xc8, InRangeLengthErrors = 0xca, - FrameCheckSeqError = 0xcc, - FrameLostRxError = 0xce, + FramesCheckSeqErrors = 0xcc, + FramesLostRxErrors = 0xce, OctetXmtOk = 0xd0, McstOctetXmtOk = 0xd4, BcstOctetXmtOk = 0xd8, @@ -264,6 +264,7 @@ enum RFS_bits { FrameEnd = 0x40000000, RFDDone = 0x80000000, TCIShift = 32, + RFS_Errors = 0x003f0000, }; #define MII_RESET_TIME_OUT 10000 @@ -648,7 +649,7 @@ struct netdev_private { dma_addr_t tx_ring_dma; dma_addr_t rx_ring_dma; struct pci_dev *pdev; - spinlock_t lock; + spinlock_t tx_lock; spinlock_t rx_lock; struct net_device_stats stats; unsigned int rx_buf_sz; /* Based on MTU+slack. */ @@ -657,7 +658,6 @@ struct netdev_private { unsigned int chip_id; /* PCI table chip id */ unsigned int rx_coalesce; /* Maximum frames each RxDMAComplete intr */ unsigned int rx_timeout; /* Wait time between RxDMAComplete intr */ - unsigned int tx_full:1; /* The Tx queue is full. */ unsigned int full_duplex:1; /* Full-duplex operation requested. */ unsigned int an_enable:2; /* Auto-Negotiated Enable */ unsigned int jumbo:1; /* Jumbo frame enable */ @@ -665,6 +665,7 @@ struct netdev_private { unsigned int tx_flow:1; /* Tx flow control enable */ unsigned int rx_flow:1; /* Rx flow control enable */ unsigned int phy_media:1; /* 1: fiber, 0: copper */ + unsigned int link_status:1; /* Current link status */ unsigned char pci_rev_id; /* PCI revision ID */ struct netdev_desc *last_tx; /* Last Tx descriptor used. */ unsigned long cur_rx, old_rx; /* Producer/consumer ring indices */ @@ -677,8 +678,6 @@ struct netdev_private { u16 advertising; /* NWay media advertisement */ u16 negotiate; /* Negotiated media */ int phy_addr; /* PHY addresses. */ - int tx_debug; - int rx_debug; }; /* The station address location in the EEPROM. */ @@ -707,114 +706,4 @@ MODULE_DEVICE_TABLE (pci, rio_pci_tbl); #define DEFAULT_RXT 750 #define DEFAULT_TXC 1 #define MAX_TXC 8 -#ifdef RIO_DEBUG -#define DEBUG_TFD_DUMP(x) debug_tfd_dump(x) -#define DEBUG_RFD_DUMP(x,flag) debug_rfd_dump(x,flag) -#define DEBUG_PKT_DUMP(x,len) debug_pkt_dump(x,len) -#define DEBUG_PRINT printk - -static inline void -debug_tfd_dump (struct netdev_private *np) -{ - int i; - struct netdev_desc *desc; - - if (np->tx_debug == 6) { - printk ("TFDone Dump: "); - for (i = 0; i < TX_RING_SIZE; i++) { - desc = &np->tx_ring[i]; - if ((desc->fraginfo & 0xffffffffff) == 0) - printk ("X"); - else - printk ("%d", (desc->status & TFDDone) ? 1 : 0); - } - printk ("\n"); - } - if (np->tx_debug == 5) { - for (i = 0; i < TX_RING_SIZE; i++) { - desc = &np->tx_ring[i]; - printk - ("cur:%08x next:%08x status:%08x frag1:%08x frag0:%08x", - (u32) np->tx_ring_dma + i * sizeof (*desc), - (u32) desc->next_desc, (u32) desc->status, - (u32) (desc->fraginfo >> 32), - (u32) desc->fraginfo); - printk ("\n"); - } - printk ("\n"); - } -} -static inline void -debug_rfd_dump (struct netdev_private *np, int flag) -{ - int i; - struct netdev_desc *desc; - int entry = np->cur_rx % RX_RING_SIZE; - - if (np->rx_debug == 5) { - for (i = 0; i < RX_RING_SIZE; i++) { - desc = &np->rx_ring[i]; - printk - ("cur:%08x next:%08x status:%08x frag1:%08x frag0:%08x", - (u32) np->rx_ring_dma + i * sizeof (*desc), - (u32) desc->next_desc, (u32) desc->status, - (u32) (desc->fraginfo >> 32), - (u32) desc->fraginfo); - printk ("\n"); - } - printk ("\n"); - } - - if (np->rx_debug == 6) { - if (flag == 1) - printk ("RFDone Dump: "); - else if (flag == 2) - printk ("Re-Filling: "); - for (i = 0; i < RX_RING_SIZE; i++) { - desc = &np->rx_ring[i]; - printk ("%d", (desc->status & RFDDone) ? 1 : 0); - } - printk ("\n"); - } - if (np->rx_debug == 7) { - printk (" In rcv_pkt(), entry %d status %4.4x %4.4x.\n", - entry, (u32) (np->rx_ring[entry].status >> 32), - (u32) np->rx_ring[entry].status); - } - -} - -static inline void -debug_pkt_dump (struct netdev_private *np, int pkt_len) -{ - int entry = np->cur_rx % RX_RING_SIZE; - struct netdev_desc *desc = &np->rx_ring[entry]; - u64 frame_status = le64_to_cpu (desc->status); - unsigned char *pchar; - unsigned char *phead; - int i; - - if (np->rx_debug == 4) { - printk (" rcv_pkt: status was %4.4x %4.4x.\n", - (u32) (frame_status >> 32), (u32) frame_status); - } - if (np->rx_debug == 7) { - -#error Please convert me to Documentation/DMA-mapping.txt - phead = - bus_to_virt (le64_to_cpu (desc->fraginfo & 0xffffffffff)); - for (pchar = phead, i = 0; i < pkt_len; i++, pchar++) { - printk ("%02x ", *pchar); - if ((i + 1) % 20 == 0) - printk ("\n"); - } - } -} -#else -#define DEBUG_TFD_DUMP(x) {} -#define DEBUG_RFD_DUMP(x,flag) {} -#define DEBUG_PKT_DUMP(x,len) {} -#define DEBUG_PRINT() {} -#endif - #endif /* __DL2K_H__ */ diff --git a/drivers/net/e100/e100.h b/drivers/net/e100/e100.h index 969b065e5434..107a7a84cf3a 100644 --- a/drivers/net/e100/e100.h +++ b/drivers/net/e100/e100.h @@ -503,6 +503,7 @@ enum led_state_e { #define IS_BACHELOR 0x00000010 /* set if 82558 or newer board */ #define IS_ICH 0x00000020 #define DF_SPEED_FORCED 0x00000040 /* set if speed is forced */ +#define LED_IS_ON 0x00000080 /* LED is turned ON by the driver */ typedef struct net_device_stats net_dev_stats_t; @@ -909,7 +910,6 @@ struct ethtool_lpbk_data{ }; #endif - struct e100_private { u32 flags; /* board management flags */ u32 tx_per_underrun; /* number of good tx frames per underrun */ @@ -998,8 +998,11 @@ struct e100_private { u32 wolopts; u16 ip_lbytes; #endif -#ifdef ETHTOOL_TEST -struct ethtool_lpbk_data loopback; +#ifdef ETHTOOL_TEST + struct ethtool_lpbk_data loopback; +#endif +#ifdef ETHTOOL_PHYS_ID + struct timer_list blink_timer; /* led blink timer id */ #endif #ifdef CONFIG_PM diff --git a/drivers/net/e100/e100_config.c b/drivers/net/e100/e100_config.c index c965393db4e2..cce55296ebb1 100644 --- a/drivers/net/e100/e100_config.c +++ b/drivers/net/e100/e100_config.c @@ -593,7 +593,6 @@ e100_config_wol(struct e100_private *bdp) } #endif -#ifdef ETHTOOL_TEST /** * e100_config_loopback_mode * @bdp: atapter's private data struct @@ -690,5 +689,4 @@ e100_config_dynamic_tbd(struct e100_private *bdp, unsigned char enable) return bc_changed; } -#endif diff --git a/drivers/net/e100/e100_main.c b/drivers/net/e100/e100_main.c index c5ffcf7b7257..d58486f305bf 100644 --- a/drivers/net/e100/e100_main.c +++ b/drivers/net/e100/e100_main.c @@ -164,6 +164,9 @@ static char *test_strings[] = { }; #endif +#ifdef ETHTOOL_PHYS_ID +static int e100_ethtool_led_blink(struct net_device *, struct ifreq *); +#endif #endif /*E100_ETHTOOL_IOCTL */ #ifdef SIOCGMIIPHY @@ -182,7 +185,7 @@ static void e100_non_tx_background(unsigned long); /* Global Data structures and variables */ char e100_copyright[] __devinitdata = "Copyright (c) 2002 Intel Corporation"; -#define E100_VERSION "2.0.27-pre3" +#define E100_VERSION "2.0.30-k1" #define E100_FULL_DRIVER_NAME "Intel(R) PRO/100 Fast Ethernet Adapter - Loadable driver, ver " @@ -691,9 +694,10 @@ e100_found1(struct pci_dev *pcid, const struct pci_device_id *ent) #ifdef ETHTOOL_GWOL /* Disabling all WOLs as initialization */ bdp->wolsupported = bdp->wolopts = 0; - if (bdp->rev_id >= D101MA_REV_ID) { - bdp->wolsupported = - WAKE_PHY | WAKE_UCAST | WAKE_ARP | WAKE_MAGIC; + if (bdp->rev_id >= D101A4_REV_ID) { + bdp->wolsupported = WAKE_PHY | WAKE_MAGIC; + if (bdp->rev_id >= D101MA_REV_ID) + bdp->wolsupported |= WAKE_UCAST | WAKE_ARP; bdp->wolopts = WAKE_MAGIC; } #endif @@ -3335,6 +3339,11 @@ e100_do_ethtool_ioctl(struct net_device *dev, struct ifreq *ifr) rc = e100_ethtool_gstrings(dev,ifr); break; #endif +#ifdef ETHTOOL_PHYS_ID + case ETHTOOL_PHYS_ID: + rc = e100_ethtool_led_blink(dev,ifr); + break; +#endif default: break; } //switch @@ -3641,6 +3650,94 @@ e100_ethtool_eeprom(struct net_device *dev, struct ifreq *ifr) } #endif +#ifdef ETHTOOL_PHYS_ID +#define E100_BLINK_INTERVAL (HZ/4) +/** + * e100_led_control + * @bdp: atapter's private data struct + * @led_mdi_op: led operation + * + * Software control over adapter's led. The possible operations are: + * TURN LED OFF, TURN LED ON and RETURN LED CONTROL TO HARDWARE. + */ +static void +e100_led_control(struct e100_private *bdp, u16 led_mdi_op) +{ + spin_lock_bh(&bdp->mdi_access_lock); + + e100_mdi_write(bdp, PHY_82555_LED_SWITCH_CONTROL, + bdp->phy_addr, led_mdi_op); + + spin_unlock_bh(&bdp->mdi_access_lock); +} +/** + * e100_led_blink_callback + * @data: pointer to atapter's private data struct + * + * Blink timer callback function. Toggles ON/OFF led status bit and calls + * led hardware access function. + */ +static void +e100_led_blink_callback(unsigned long data) +{ + struct e100_private *bdp = (struct e100_private *) data; + + if(bdp->flags & LED_IS_ON) { + bdp->flags &= ~LED_IS_ON; + e100_led_control(bdp, PHY_82555_LED_OFF); + } else { + bdp->flags |= LED_IS_ON; + if (bdp->rev_id >= D101MA_REV_ID) + e100_led_control(bdp, PHY_82555_LED_ON_559); + else + e100_led_control(bdp, PHY_82555_LED_ON_PRE_559); + } + + mod_timer(&bdp->blink_timer, jiffies + E100_BLINK_INTERVAL); +} +/** + * e100_ethtool_led_blink + * @dev: pointer to atapter's net_device struct + * @ifr: pointer to ioctl request structure + * + * Blink led ioctl handler. Initialtes blink timer and sleeps until + * blink period expires. Than it kills timer and returns. The led control + * is returned back to hardware when blink timer is killed. + */ +static int +e100_ethtool_led_blink(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + struct ethtool_value ecmd; + + bdp = dev->priv; + + if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd))) + return -EFAULT; + + if(!bdp->blink_timer.function) { + init_timer(&bdp->blink_timer); + bdp->blink_timer.function = e100_led_blink_callback; + bdp->blink_timer.data = (unsigned long) bdp; + } + + mod_timer(&bdp->blink_timer, jiffies); + + set_current_state(TASK_INTERRUPTIBLE); + + if ((!ecmd.data) || (ecmd.data > MAX_SCHEDULE_TIMEOUT / HZ)) + ecmd.data = MAX_SCHEDULE_TIMEOUT / HZ; + + schedule_timeout(ecmd.data * HZ); + + del_timer_sync(&bdp->blink_timer); + + e100_led_control(bdp, PHY_82555_LED_NORMAL_CONTROL); + + return 0; +} +#endif + static inline int __devinit e100_10BaseT_adapter(struct e100_private *bdp) { diff --git a/drivers/net/e100/e100_proc.c b/drivers/net/e100/e100_proc.c index a17c24ff6a81..4e565e54425a 100644 --- a/drivers/net/e100/e100_proc.c +++ b/drivers/net/e100/e100_proc.c @@ -548,7 +548,7 @@ read_bundle_max_def(char *page, char **start, off_t off, int len; len = read_int_param(page, "CPU Saver Maximum Bundle", - "Sets the value for CPU saver's maximum value", + "Sets CPU saver's maximum value", E100_DEFAULT_CPUSAVER_BUNDLE_MAX, 0x1, 0xFFFF); return generic_read(page, start, off, count, eof, len); diff --git a/drivers/net/e100/e100_test.c b/drivers/net/e100/e100_test.c index 77918c9f8f9c..b2fcf25ea88b 100644 --- a/drivers/net/e100/e100_test.c +++ b/drivers/net/e100/e100_test.c @@ -69,6 +69,7 @@ AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ + #include "e100.h" #include "e100_config.h" #ifdef ETHTOOL_TEST @@ -458,10 +459,3 @@ e100_diag_loopback_free (struct e100_private *bdp) #endif - - - - - - - diff --git a/drivers/net/e1000/Makefile b/drivers/net/e1000/Makefile index 497e9e430ce0..9b7eca8897d9 100644 --- a/drivers/net/e1000/Makefile +++ b/drivers/net/e1000/Makefile @@ -1,3 +1,76 @@ +################################################################################ +# +# This software program is available to you under a choice of one of two +# licenses. You may choose to be licensed under either the GNU General Public +# License 2.0, June 1991, available at http://www.fsf.org/copyleft/gpl.html, +# or the Intel BSD + Patent License, the text of which follows: +# +# Recipient has requested a license and Intel Corporation ("Intel") is willing +# to grant a license for the software entitled Linux Base Driver for the +# Intel(R) PRO/1000 Family of Adapters (e1000) (the "Software") being provided +# by Intel Corporation. The following definitions apply to this license: +# +# "Licensed Patents" means patent claims licensable by Intel Corporation which +# are necessarily infringed by the use of sale of the Software alone or when +# combined with the operating system referred to below. +# +# "Recipient" means the party to whom Intel delivers this Software. +# +# "Licensee" means Recipient and those third parties that receive a license to +# any operating system available under the GNU General Public License 2.0 or +# later. +# +# Copyright (c) 1999 - 2002 Intel Corporation. +# All rights reserved. +# +# The license is provided to Recipient and Recipient's Licensees under the +# following terms. +# +# Redistribution and use in source and binary forms of the Software, with or +# without modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code of the Software may retain the above +# copyright notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form of the Software may reproduce the above +# copyright notice, this list of conditions and the following disclaimer in +# the documentation and/or materials provided with the distribution. +# +# Neither the name of Intel Corporation nor the names of its contributors +# shall be used to endorse or promote products derived from this Software +# without specific prior written permission. +# +# Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +# royalty-free patent license under Licensed Patents to make, use, sell, offer +# to sell, import and otherwise transfer the Software, if any, in source code +# and object code form. This license shall include changes to the Software +# that are error corrections or other minor changes to the Software that do +# not add functionality or features when the Software is incorporated in any +# version of an operating system that has been distributed under the GNU +# General Public License 2.0 or later. This patent license shall apply to the +# combination of the Software and any operating system licensed under the GNU +# General Public License 2.0 or later if, at the time Intel provides the +# Software to Recipient, such addition of the Software to the then publicly +# available versions of such operating systems available under the GNU General +# Public License 2.0 or later (whether in gold, beta or alpha form) causes +# such combination to be covered by the Licensed Patents. The patent license +# shall not apply to any other combinations which include the Software. NO +# hardware per se is licensed hereunder. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR +# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +################################################################################ + # # Makefile for the Intel(R) PRO/1000 ethernet driver # diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index 5424cca61c84..197222a539de 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h @@ -107,18 +107,21 @@ #include <linux/udp.h> #include <net/pkt_sched.h> #include <linux/list.h> -#include <asm/uaccess.h> +#include <linux/reboot.h> #include <linux/ethtool.h> #ifdef NETIF_F_HW_VLAN_TX #include <linux/if_vlan.h> #endif +#define BAR_0 0 +#define PCI_DMA_64BIT 0xffffffffffffffffULL +#define PCI_DMA_32BIT 0x00000000ffffffffULL + + struct e1000_adapter; #include "e1000_hw.h" -#define BAR_0 0 - #if DBG #define E1000_DBG(args...) printk(KERN_DEBUG "e1000: " args) #else @@ -156,6 +159,7 @@ struct e1000_buffer { struct sk_buff *skb; uint64_t dma; unsigned long length; + unsigned long time_stamp; }; struct e1000_desc_ring { @@ -204,10 +208,13 @@ struct e1000_adapter { spinlock_t stats_lock; atomic_t irq_sem; +#ifdef ETHTOOL_PHYS_ID + struct timer_list blink_timer; + unsigned long led_status; +#endif + /* TX */ struct e1000_desc_ring tx_ring; - unsigned long trans_finish; - spinlock_t tx_lock; uint32_t txd_cmd; int max_data_per_txd; @@ -228,6 +235,9 @@ struct e1000_adapter { struct e1000_hw_stats stats; struct e1000_phy_info phy_info; struct e1000_phy_stats phy_stats; -}; + + + uint32_t pci_state[16]; +}; #endif /* _E1000_H_ */ diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index 7ed6f8ebe931..044958ed25a2 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c @@ -75,7 +75,6 @@ #include "e1000.h" -#include <linux/ethtool.h> #include <asm/uaccess.h> extern char e1000_driver_name[]; @@ -83,7 +82,6 @@ extern char e1000_driver_version[]; extern int e1000_up(struct e1000_adapter *adapter); extern void e1000_down(struct e1000_adapter *adapter); -extern void e1000_enable_WOL(struct e1000_adapter *adapter); static void e1000_ethtool_gset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd) @@ -128,7 +126,11 @@ e1000_ethtool_gset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd) SUPPORTED_Autoneg); ecmd->port = PORT_FIBRE; - ecmd->transceiver = XCVR_EXTERNAL; + + if(hw->mac_type >= e1000_82545) + ecmd->transceiver = XCVR_INTERNAL; + else + ecmd->transceiver = XCVR_EXTERNAL; } if(netif_carrier_ok(adapter->netdev)) { @@ -211,10 +213,38 @@ e1000_ethtool_gdrvinfo(struct e1000_adapter *adapter, strncpy(drvinfo->version, e1000_driver_version, 32); strncpy(drvinfo->fw_version, "", 32); strncpy(drvinfo->bus_info, adapter->pdev->slot_name, 32); +#define E1000_REGS_LEN 32 + drvinfo->regdump_len = E1000_REGS_LEN * sizeof(uint32_t); drvinfo->eedump_len = e1000_eeprom_size(&adapter->hw); } static void +e1000_ethtool_gregs(struct e1000_adapter *adapter, + struct ethtool_regs *regs, uint32_t *regs_buff) +{ + struct e1000_hw *hw = &adapter->hw; + + regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id; + + regs_buff[0] = E1000_READ_REG(hw, CTRL); + regs_buff[1] = E1000_READ_REG(hw, STATUS); + + regs_buff[2] = E1000_READ_REG(hw, RCTL); + regs_buff[3] = E1000_READ_REG(hw, RDLEN); + regs_buff[4] = E1000_READ_REG(hw, RDH); + regs_buff[5] = E1000_READ_REG(hw, RDT); + regs_buff[6] = E1000_READ_REG(hw, RDTR); + + regs_buff[7] = E1000_READ_REG(hw, TCTL); + regs_buff[8] = E1000_READ_REG(hw, TDLEN); + regs_buff[9] = E1000_READ_REG(hw, TDH); + regs_buff[10] = E1000_READ_REG(hw, TDT); + regs_buff[11] = E1000_READ_REG(hw, TIDV); + + return; +} + +static void e1000_ethtool_geeprom(struct e1000_adapter *adapter, struct ethtool_eeprom *eeprom, uint16_t *eeprom_buff) { @@ -228,7 +258,7 @@ e1000_ethtool_geeprom(struct e1000_adapter *adapter, if ((eeprom->offset + eeprom->len) > max_len) eeprom->len = (max_len - eeprom->offset); - for(i = 0; i < max_len; i++) + for(i = 0; i < (max_len >> 1); i++) e1000_read_eeprom(&adapter->hw, i, &eeprom_buff[i]); } @@ -236,27 +266,50 @@ static void e1000_ethtool_gwol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol) { struct e1000_hw *hw = &adapter->hw; - - if(hw->mac_type < e1000_82544) { + + switch(adapter->hw.device_id) { + case E1000_DEV_ID_82542: + case E1000_DEV_ID_82543GC_FIBER: + case E1000_DEV_ID_82543GC_COPPER: + case E1000_DEV_ID_82544EI_FIBER: + default: wol->supported = 0; wol->wolopts = 0; return; - } - wol->supported = WAKE_PHY | WAKE_UCAST | - WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC; - - wol->wolopts = 0; - if(adapter->wol & E1000_WUFC_LNKC) - wol->wolopts |= WAKE_PHY; - if(adapter->wol & E1000_WUFC_EX) - wol->wolopts |= WAKE_UCAST; - if(adapter->wol & E1000_WUFC_MC) - wol->wolopts |= WAKE_MCAST; - if(adapter->wol & E1000_WUFC_BC) - wol->wolopts |= WAKE_BCAST; - if(adapter->wol & E1000_WUFC_MAG) - wol->wolopts |= WAKE_MAGIC; + case E1000_DEV_ID_82546EB_FIBER: + /* Wake events only supported on port A for dual fiber */ + if(E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1) { + wol->supported = 0; + wol->wolopts = 0; + return; + } + /* Fall Through */ + + case E1000_DEV_ID_82544EI_COPPER: + case E1000_DEV_ID_82544GC_COPPER: + case E1000_DEV_ID_82544GC_LOM: + case E1000_DEV_ID_82540EM: + case E1000_DEV_ID_82540EM_LOM: + case E1000_DEV_ID_82545EM_COPPER: + case E1000_DEV_ID_82545EM_FIBER: + case E1000_DEV_ID_82546EB_COPPER: + wol->supported = WAKE_PHY | WAKE_UCAST | + WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC; + + wol->wolopts = 0; + if(adapter->wol & E1000_WUFC_LNKC) + wol->wolopts |= WAKE_PHY; + if(adapter->wol & E1000_WUFC_EX) + wol->wolopts |= WAKE_UCAST; + if(adapter->wol & E1000_WUFC_MC) + wol->wolopts |= WAKE_MCAST; + if(adapter->wol & E1000_WUFC_BC) + wol->wolopts |= WAKE_BCAST; + if(adapter->wol & E1000_WUFC_MAG) + wol->wolopts |= WAKE_MAGIC; + return; + } } static int @@ -264,25 +317,95 @@ e1000_ethtool_swol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol) { struct e1000_hw *hw = &adapter->hw; - if(hw->mac_type < e1000_82544) + switch(adapter->hw.device_id) { + case E1000_DEV_ID_82542: + case E1000_DEV_ID_82543GC_FIBER: + case E1000_DEV_ID_82543GC_COPPER: + case E1000_DEV_ID_82544EI_FIBER: + default: return wol->wolopts ? -EOPNOTSUPP : 0; - adapter->wol = 0; + case E1000_DEV_ID_82546EB_FIBER: + /* Wake events only supported on port A for dual fiber */ + if(E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1) + return wol->wolopts ? -EOPNOTSUPP : 0; + /* Fall Through */ + + case E1000_DEV_ID_82544EI_COPPER: + case E1000_DEV_ID_82544GC_COPPER: + case E1000_DEV_ID_82544GC_LOM: + case E1000_DEV_ID_82540EM: + case E1000_DEV_ID_82540EM_LOM: + case E1000_DEV_ID_82545EM_COPPER: + case E1000_DEV_ID_82545EM_FIBER: + case E1000_DEV_ID_82546EB_COPPER: + if(wol->wolopts & WAKE_ARP) + return -EOPNOTSUPP; + + adapter->wol = 0; + + if(wol->wolopts & WAKE_PHY) + adapter->wol |= E1000_WUFC_LNKC; + if(wol->wolopts & WAKE_UCAST) + adapter->wol |= E1000_WUFC_EX; + if(wol->wolopts & WAKE_MCAST) + adapter->wol |= E1000_WUFC_MC; + if(wol->wolopts & WAKE_BCAST) + adapter->wol |= E1000_WUFC_BC; + if(wol->wolopts & WAKE_MAGIC) + adapter->wol |= E1000_WUFC_MAG; + } + + return 0; +} + +#ifdef ETHTOOL_PHYS_ID + +/* toggle LED 4 times per second = 2 "blinks" per second */ +#define E1000_ID_INTERVAL (HZ/4) + +/* bit defines for adapter->led_status */ +#define E1000_LED_ON 0 + +static void +e1000_led_blink_callback(unsigned long data) +{ + struct e1000_adapter *adapter = (struct e1000_adapter *) data; + + if(test_and_change_bit(E1000_LED_ON, &adapter->led_status)) + e1000_led_off(&adapter->hw); + else + e1000_led_on(&adapter->hw); + + mod_timer(&adapter->blink_timer, jiffies + E1000_ID_INTERVAL); +} - if(wol->wolopts & WAKE_PHY) - adapter->wol |= E1000_WUFC_LNKC; - if(wol->wolopts & WAKE_UCAST) - adapter->wol |= E1000_WUFC_EX; - if(wol->wolopts & WAKE_MCAST) - adapter->wol |= E1000_WUFC_MC; - if(wol->wolopts & WAKE_BCAST) - adapter->wol |= E1000_WUFC_BC; - if(wol->wolopts & WAKE_MAGIC) - adapter->wol |= E1000_WUFC_MAG; +static int +e1000_ethtool_led_blink(struct e1000_adapter *adapter, struct ethtool_value *id) +{ + if(!adapter->blink_timer.function) { + init_timer(&adapter->blink_timer); + adapter->blink_timer.function = e1000_led_blink_callback; + adapter->blink_timer.data = (unsigned long) adapter; + } + + e1000_setup_led(&adapter->hw); + mod_timer(&adapter->blink_timer, jiffies); + + set_current_state(TASK_INTERRUPTIBLE); + if(id->data) + schedule_timeout(id->data * HZ); + else + schedule_timeout(MAX_SCHEDULE_TIMEOUT); + + del_timer_sync(&adapter->blink_timer); + e1000_led_off(&adapter->hw); + clear_bit(E1000_LED_ON, &adapter->led_status); + e1000_cleanup_led(&adapter->hw); - e1000_enable_WOL(adapter); return 0; } +#endif /* ETHTOOL_PHYS_ID */ int e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr) @@ -317,6 +440,22 @@ e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr) return -EFAULT; return 0; } + case ETHTOOL_GREGS: { + struct ethtool_regs regs = {ETHTOOL_GREGS}; + uint32_t regs_buff[E1000_REGS_LEN]; + + if(copy_from_user(®s, addr, sizeof(regs))) + return -EFAULT; + e1000_ethtool_gregs(adapter, ®s, regs_buff); + if(copy_to_user(addr, ®s, sizeof(regs))) + return -EFAULT; + + addr += offsetof(struct ethtool_regs, data); + if(copy_to_user(addr, regs_buff, regs.len)) + return -EFAULT; + + return 0; + } case ETHTOOL_NWAY_RST: { if(!capable(CAP_NET_ADMIN)) return -EPERM; @@ -324,6 +463,14 @@ e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr) e1000_up(adapter); return 0; } +#ifdef ETHTOOL_PHYS_ID + case ETHTOOL_PHYS_ID: { + struct ethtool_value id; + if(copy_from_user(&id, addr, sizeof(id))) + return -EFAULT; + return e1000_ethtool_led_blink(adapter, &id); + } +#endif /* ETHTOOL_PHYS_ID */ case ETHTOOL_GLINK: { struct ethtool_value link = {ETHTOOL_GLINK}; link.data = netif_carrier_ok(netdev); diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/e1000/e1000_hw.c index 0be1c6145771..38ea8c10ca8e 100644 --- a/drivers/net/e1000/e1000_hw.c +++ b/drivers/net/e1000/e1000_hw.c @@ -93,6 +93,7 @@ static void e1000_shift_out_ee_bits(struct e1000_hw *hw, uint16_t data, uint16_t static uint16_t e1000_shift_in_ee_bits(struct e1000_hw *hw); static void e1000_setup_eeprom(struct e1000_hw *hw); static void e1000_standby_eeprom(struct e1000_hw *hw); +static int32_t e1000_id_led_init(struct e1000_hw * hw); /****************************************************************************** * Reset the transmit and receive units; mask and clear all interrupts. @@ -197,9 +198,20 @@ e1000_init_hw(struct e1000_hw *hw) uint32_t i; int32_t ret_val; uint16_t pci_cmd_word; + uint16_t pcix_cmd_word; + uint16_t pcix_stat_hi_word; + uint16_t cmd_mmrbc; + uint16_t stat_mmrbc; DEBUGFUNC("e1000_init_hw"); + /* Initialize Identification LED */ + ret_val = e1000_id_led_init(hw); + if(ret_val < 0) { + DEBUGOUT("Error Initializing Identification LED\n"); + return ret_val; + } + /* Set the Media Type and exit with error if it is not valid. */ if(hw->mac_type != e1000_82543) { /* tbi_compatibility is only valid on 82543 */ @@ -264,6 +276,21 @@ e1000_init_hw(struct e1000_hw *hw) E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PRIOR); } + /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */ + if(hw->bus_type == e1000_bus_type_pcix) { + e1000_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word); + e1000_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI, &pcix_stat_hi_word); + cmd_mmrbc = (pcix_cmd_word & PCIX_COMMAND_MMRBC_MASK) >> + PCIX_COMMAND_MMRBC_SHIFT; + stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >> + PCIX_STATUS_HI_MMRBC_SHIFT; + if(cmd_mmrbc > stat_mmrbc) { + pcix_cmd_word &= ~PCIX_COMMAND_MMRBC_MASK; + pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT; + e1000_write_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word); + } + } + /* Call a subroutine to configure the link and setup flow control. */ ret_val = e1000_setup_link(hw); @@ -541,6 +568,7 @@ e1000_setup_copper_link(struct e1000_hw *hw) */ if(hw->mac_type > e1000_82543) { ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); E1000_WRITE_REG(hw, CTRL, ctrl); } else { ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX | E1000_CTRL_SLU); @@ -2061,6 +2089,8 @@ e1000_detect_gig_phy(struct e1000_hw *hw) if(hw->phy_id == M88E1000_I_PHY_ID) match = TRUE; break; case e1000_82540: + case e1000_82545: + case e1000_82546: if(hw->phy_id == M88E1011_I_PHY_ID) match = TRUE; break; default: @@ -2087,9 +2117,9 @@ e1000_phy_reset_dsp(struct e1000_hw *hw) DEBUGFUNC("e1000_phy_reset_dsp"); do { - if(e1000_write_phy_reg(hw, 29, 0x1d) < 0) break; - if(e1000_write_phy_reg(hw, 30, 0xc1) < 0) break; - if(e1000_write_phy_reg(hw, 30, 0x00) < 0) break; + if(e1000_write_phy_reg(hw, 29, 0x001d) < 0) break; + if(e1000_write_phy_reg(hw, 30, 0x00c1) < 0) break; + if(e1000_write_phy_reg(hw, 30, 0x0000) < 0) break; ret_val = 0; } while(0); @@ -2150,8 +2180,8 @@ e1000_phy_get_info(struct e1000_hw *hw, M88E1000_PSSR_MDIX_SHIFT; if(phy_data & M88E1000_PSSR_1000MBS) { /* Cable Length Estimation and Local/Remote Receiver Informatoion - * are only valid at 1000 Mbps - */ + * are only valid at 1000 Mbps + */ phy_info->cable_length = ((phy_data & M88E1000_PSSR_CABLE_LENGTH) >> M88E1000_PSSR_CABLE_LENGTH_SHIFT); if(e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data) < 0) @@ -2521,6 +2551,13 @@ e1000_read_mac_addr(struct e1000_hw * hw) hw->perm_mac_addr[i] = (uint8_t) (eeprom_data & 0x00FF); hw->perm_mac_addr[i+1] = (uint8_t) (eeprom_data >> 8); } + if((hw->mac_type == e1000_82546) && + (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + if(hw->perm_mac_addr[5] & 0x01) + hw->perm_mac_addr[5] &= ~(0x01); + else + hw->perm_mac_addr[5] |= 0x01; + } for(i = 0; i < NODE_ADDRESS_SIZE; i++) hw->mac_addr[i] = hw->perm_mac_addr[i]; return 0; @@ -2785,6 +2822,74 @@ e1000_clear_vfta(struct e1000_hw *hw) E1000_WRITE_REG_ARRAY(hw, VFTA, offset, 0); } +static int32_t +e1000_id_led_init(struct e1000_hw * hw) +{ + uint32_t ledctl; + const uint32_t ledctl_mask = 0x000000FF; + const uint32_t ledctl_on = E1000_LEDCTL_MODE_LED_ON; + const uint32_t ledctl_off = E1000_LEDCTL_MODE_LED_OFF; + uint16_t eeprom_data, i, temp; + const uint16_t led_mask = 0x0F; + + DEBUGFUNC("e1000_id_led_init"); + + if(hw->mac_type < e1000_82540) { + /* Nothing to do */ + return 0; + } + + ledctl = E1000_READ_REG(hw, LEDCTL); + hw->ledctl_default = ledctl; + hw->ledctl_mode1 = hw->ledctl_default; + hw->ledctl_mode2 = hw->ledctl_default; + + if(e1000_read_eeprom(hw, EEPROM_ID_LED_SETTINGS, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + if((eeprom_data== ID_LED_RESERVED_0000) || + (eeprom_data == ID_LED_RESERVED_FFFF)) eeprom_data = ID_LED_DEFAULT; + for(i = 0; i < 4; i++) { + temp = (eeprom_data >> (i << 2)) & led_mask; + switch(temp) { + case ID_LED_ON1_DEF2: + case ID_LED_ON1_ON2: + case ID_LED_ON1_OFF2: + hw->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode1 |= ledctl_on << (i << 3); + break; + case ID_LED_OFF1_DEF2: + case ID_LED_OFF1_ON2: + case ID_LED_OFF1_OFF2: + hw->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode1 |= ledctl_off << (i << 3); + break; + default: + /* Do nothing */ + break; + } + switch(temp) { + case ID_LED_DEF1_ON2: + case ID_LED_ON1_ON2: + case ID_LED_OFF1_ON2: + hw->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode2 |= ledctl_on << (i << 3); + break; + case ID_LED_DEF1_OFF2: + case ID_LED_ON1_OFF2: + case ID_LED_OFF1_OFF2: + hw->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode2 |= ledctl_off << (i << 3); + break; + default: + /* Do nothing */ + break; + } + } + return 0; +} + /****************************************************************************** * Prepares SW controlable LED for use and saves the current state of the LED. * @@ -2807,22 +2912,24 @@ e1000_setup_led(struct e1000_hw *hw) case E1000_DEV_ID_82544GC_LOM: /* No setup necessary */ break; - case E1000_DEV_ID_82540EM: - case E1000_DEV_ID_82540EM_LOM: + case E1000_DEV_ID_82545EM_FIBER: + case E1000_DEV_ID_82546EB_FIBER: ledctl = E1000_READ_REG(hw, LEDCTL); /* Save current LEDCTL settings */ - hw->ledctl = ledctl; - /* Turn off LED2 and LED3 */ - ledctl &= ~(E1000_LEDCTL_LED2_IVRT | - E1000_LEDCTL_LED2_BLINK | - E1000_LEDCTL_LED2_MODE_MASK); - ledctl |= (E1000_LEDCTL_MODE_LED_OFF << E1000_LEDCTL_LED2_MODE_SHIFT); - ledctl &= ~(E1000_LEDCTL_LED3_IVRT | - E1000_LEDCTL_LED3_BLINK | - E1000_LEDCTL_LED3_MODE_MASK); - ledctl |= (E1000_LEDCTL_MODE_LED_OFF << E1000_LEDCTL_LED3_MODE_SHIFT); + hw->ledctl_default = ledctl; + /* Turn off LED0 */ + ledctl &= ~(E1000_LEDCTL_LED0_IVRT | + E1000_LEDCTL_LED0_BLINK | + E1000_LEDCTL_LED0_MODE_MASK); + ledctl |= (E1000_LEDCTL_MODE_LED_OFF << E1000_LEDCTL_LED0_MODE_SHIFT); E1000_WRITE_REG(hw, LEDCTL, ledctl); break; + case E1000_DEV_ID_82540EM: + case E1000_DEV_ID_82540EM_LOM: + case E1000_DEV_ID_82545EM_COPPER: + case E1000_DEV_ID_82546EB_COPPER: + E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1); + break; default: DEBUGOUT("Invalid device ID\n"); return -E1000_ERR_CONFIG; @@ -2852,8 +2959,12 @@ e1000_cleanup_led(struct e1000_hw *hw) break; case E1000_DEV_ID_82540EM: case E1000_DEV_ID_82540EM_LOM: + case E1000_DEV_ID_82545EM_COPPER: + case E1000_DEV_ID_82545EM_FIBER: + case E1000_DEV_ID_82546EB_COPPER: + case E1000_DEV_ID_82546EB_FIBER: /* Restore LEDCTL settings */ - E1000_WRITE_REG(hw, LEDCTL, hw->ledctl); + E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_default); break; default: DEBUGOUT("Invalid device ID\n"); @@ -2871,7 +2982,6 @@ int32_t e1000_led_on(struct e1000_hw *hw) { uint32_t ctrl; - uint32_t ledctl; DEBUGFUNC("e1000_led_on"); @@ -2889,6 +2999,8 @@ e1000_led_on(struct e1000_hw *hw) case E1000_DEV_ID_82544EI_COPPER: case E1000_DEV_ID_82544GC_COPPER: case E1000_DEV_ID_82544GC_LOM: + case E1000_DEV_ID_82545EM_FIBER: + case E1000_DEV_ID_82546EB_FIBER: ctrl = E1000_READ_REG(hw, CTRL); /* Clear SW Defineable Pin 0 to turn on the LED */ ctrl &= ~E1000_CTRL_SWDPIN0; @@ -2897,11 +3009,9 @@ e1000_led_on(struct e1000_hw *hw) break; case E1000_DEV_ID_82540EM: case E1000_DEV_ID_82540EM_LOM: - ledctl = E1000_READ_REG(hw, LEDCTL); - /* Set LED 3 mode to on */ - ledctl &= ~E1000_LEDCTL_LED3_MODE_MASK; - ledctl |= (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED3_MODE_SHIFT); - E1000_WRITE_REG(hw, LEDCTL, ledctl); + case E1000_DEV_ID_82545EM_COPPER: + case E1000_DEV_ID_82546EB_COPPER: + E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode2); break; default: DEBUGOUT("Invalid device ID\n"); @@ -2919,7 +3029,6 @@ int32_t e1000_led_off(struct e1000_hw *hw) { uint32_t ctrl; - uint32_t ledctl; DEBUGFUNC("e1000_led_off"); @@ -2937,6 +3046,8 @@ e1000_led_off(struct e1000_hw *hw) case E1000_DEV_ID_82544EI_COPPER: case E1000_DEV_ID_82544GC_COPPER: case E1000_DEV_ID_82544GC_LOM: + case E1000_DEV_ID_82545EM_FIBER: + case E1000_DEV_ID_82546EB_FIBER: ctrl = E1000_READ_REG(hw, CTRL); /* Set SW Defineable Pin 0 to turn off the LED */ ctrl |= E1000_CTRL_SWDPIN0; @@ -2945,11 +3056,9 @@ e1000_led_off(struct e1000_hw *hw) break; case E1000_DEV_ID_82540EM: case E1000_DEV_ID_82540EM_LOM: - ledctl = E1000_READ_REG(hw, LEDCTL); - /* Set LED 3 mode to off */ - ledctl &= ~E1000_LEDCTL_LED3_MODE_MASK; - ledctl |= (E1000_LEDCTL_MODE_LED_OFF << E1000_LEDCTL_LED3_MODE_SHIFT); - E1000_WRITE_REG(hw, LEDCTL, ledctl); + case E1000_DEV_ID_82545EM_COPPER: + case E1000_DEV_ID_82546EB_COPPER: + E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1); break; default: DEBUGOUT("Invalid device ID\n"); diff --git a/drivers/net/e1000/e1000_hw.h b/drivers/net/e1000/e1000_hw.h index 7031ebde588a..da9a5027d2c2 100644 --- a/drivers/net/e1000/e1000_hw.h +++ b/drivers/net/e1000/e1000_hw.h @@ -92,6 +92,8 @@ typedef enum { e1000_82543, e1000_82544, e1000_82540, + e1000_82545, + e1000_82546, e1000_num_macs } e1000_mac_type; @@ -260,6 +262,7 @@ void e1000_reset_adaptive(struct e1000_hw *hw); void e1000_update_adaptive(struct e1000_hw *hw); void e1000_tbi_adjust_stats(struct e1000_hw *hw, struct e1000_hw_stats *stats, uint32_t frame_len, uint8_t * mac_addr); void e1000_get_bus_info(struct e1000_hw *hw); +void e1000_read_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t * value); void e1000_write_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t * value); /* PCI Device IDs */ @@ -272,7 +275,11 @@ void e1000_write_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t * value); #define E1000_DEV_ID_82544GC_LOM 0x100D #define E1000_DEV_ID_82540EM 0x100E #define E1000_DEV_ID_82540EM_LOM 0x1015 -#define NUM_DEV_IDS 9 +#define E1000_DEV_ID_82545EM_COPPER 0x100F +#define E1000_DEV_ID_82545EM_FIBER 0x1011 +#define E1000_DEV_ID_82546EB_COPPER 0x1010 +#define E1000_DEV_ID_82546EB_FIBER 0x1012 +#define NUM_DEV_IDS 13 #define NODE_ADDRESS_SIZE 6 #define ETH_LENGTH_OF_ADDRESS 6 @@ -882,7 +889,9 @@ struct e1000_hw { uint32_t num_mc_addrs; uint32_t collision_delta; uint32_t tx_packet_delta; - uint32_t ledctl; + uint32_t ledctl_default; + uint32_t ledctl_mode1; + uint32_t ledctl_mode2; uint16_t autoneg_advertised; uint16_t pci_cmd_word; uint16_t fc_high_water; @@ -1324,10 +1333,28 @@ struct e1000_hw { #define EEPROM_EWDS_OPCODE 0x10 /* EERPOM erast/write disable */ /* EEPROM Word Offsets */ -#define EEPROM_INIT_CONTROL1_REG 0x000A -#define EEPROM_INIT_CONTROL2_REG 0x000F -#define EEPROM_FLASH_VERSION 0x0032 -#define EEPROM_CHECKSUM_REG 0x003F +#define EEPROM_ID_LED_SETTINGS 0x0004 +#define EEPROM_INIT_CONTROL1_REG 0x000A +#define EEPROM_INIT_CONTROL2_REG 0x000F +#define EEPROM_FLASH_VERSION 0x0032 +#define EEPROM_CHECKSUM_REG 0x003F + +/* Word definitions for ID LED Settings */ +#define ID_LED_RESERVED_0000 0x0000 +#define ID_LED_RESERVED_FFFF 0xFFFF +#define ID_LED_DEFAULT ((ID_LED_OFF1_ON2 << 12) | \ + (ID_LED_OFF1_OFF2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_DEF1_DEF2)) +#define ID_LED_DEF1_DEF2 0x1 +#define ID_LED_DEF1_ON2 0x2 +#define ID_LED_DEF1_OFF2 0x3 +#define ID_LED_ON1_DEF2 0x4 +#define ID_LED_ON1_ON2 0x5 +#define ID_LED_ON1_OFF2 0x6 +#define ID_LED_OFF1_DEF2 0x7 +#define ID_LED_OFF1_ON2 0x8 +#define ID_LED_OFF1_OFF2 0x9 /* Mask bits for fields in Word 0x0a of the EEPROM */ #define EEPROM_WORD0A_ILOS 0x0010 @@ -1414,6 +1441,16 @@ struct e1000_hw { #define FC_DEFAULT_LO_THRESH (0x4000) /* 16KB */ #define FC_DEFAULT_TX_TIMER (0x100) /* ~130 us */ +/* PCIX Config space */ +#define PCIX_COMMAND_REGISTER 0xE6 +#define PCIX_STATUS_REGISTER_LO 0xE8 +#define PCIX_STATUS_REGISTER_HI 0xEA + +#define PCIX_COMMAND_MMRBC_MASK 0x000C +#define PCIX_COMMAND_MMRBC_SHIFT 0x2 +#define PCIX_STATUS_HI_MMRBC_MASK 0x0060 +#define PCIX_STATUS_HI_MMRBC_SHIFT 0x5 + /* The number of bits that we need to shift right to move the "pause" * bits from the EEPROM (bits 13:12) to the "pause" (bits 8:7) field diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index fa41235c35ad..cce7d872d7df 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -76,7 +76,7 @@ char e1000_driver_name[] = "e1000"; char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver"; -char e1000_driver_version[] = "4.2.8"; +char e1000_driver_version[] = "4.2.17-k1"; char e1000_copyright[] = "Copyright (c) 1999-2002 Intel Corporation."; /* e1000_pci_tbl - PCI Device ID Table @@ -114,6 +114,10 @@ static struct pci_device_id e1000_pci_tbl[] __devinitdata = { {0x8086, 0x100C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {0x8086, 0x100D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {0x8086, 0x100E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x8086, 0x100F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x8086, 0x1011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x8086, 0x1010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x8086, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* required last entry */ {0,} }; @@ -126,7 +130,7 @@ static char *e1000_strings[] = { "IBM Mobile, Desktop & Server Adapters" }; -/* e1000_main.c Function Prototypes */ +/* Local Function Prototypes */ int e1000_up(struct e1000_adapter *adapter); void e1000_down(struct e1000_adapter *adapter); @@ -169,13 +173,24 @@ static void e1000_leave_82542_rst(struct e1000_adapter *adapter); static inline void e1000_rx_checksum(struct e1000_adapter *adapter, struct e1000_rx_desc *rx_desc, struct sk_buff *skb); -void e1000_enable_WOL(struct e1000_adapter *adapter); #ifdef NETIF_F_HW_VLAN_TX static void e1000_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp); static void e1000_vlan_rx_add_vid(struct net_device *netdev, uint16_t vid); static void e1000_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid); #endif +static int e1000_notify_reboot(struct notifier_block *, unsigned long event, void *ptr); +static int e1000_suspend(struct pci_dev *pdev, uint32_t state); +#ifdef CONFIG_PM +static int e1000_resume(struct pci_dev *pdev); +#endif + +struct notifier_block e1000_notifier = { + notifier_call: e1000_notify_reboot, + next: NULL, + priority: 0 +}; + /* Exported from other modules */ extern void e1000_check_options(struct e1000_adapter *adapter); @@ -189,8 +204,10 @@ static struct pci_driver e1000_driver = { probe: e1000_probe, remove: __devexit_p(e1000_remove), /* Power Managment Hooks */ - suspend: NULL, - resume: NULL +#ifdef CONFIG_PM + suspend: e1000_suspend, + resume: e1000_resume +#endif }; MODULE_AUTHOR("Intel Corporation, <linux.nics@intel.com>"); @@ -207,12 +224,16 @@ MODULE_LICENSE("Dual BSD/GPL"); static int __init e1000_init_module(void) { + int ret; printk(KERN_INFO "%s - version %s\n", e1000_driver_string, e1000_driver_version); printk(KERN_INFO "%s\n", e1000_copyright); - return pci_module_init(&e1000_driver); + ret = pci_module_init(&e1000_driver); + if(ret >= 0) + register_reboot_notifier(&e1000_notifier); + return ret; } module_init(e1000_init_module); @@ -227,6 +248,7 @@ module_init(e1000_init_module); static void __exit e1000_exit_module(void) { + unregister_reboot_notifier(&e1000_notifier); pci_unregister_driver(&e1000_driver); } @@ -290,11 +312,11 @@ e1000_reset(struct e1000_adapter *adapter) adapter->hw.fc = adapter->hw.original_fc; e1000_reset_hw(&adapter->hw); + if(adapter->hw.mac_type >= e1000_82544) + E1000_WRITE_REG(&adapter->hw, WUC, 0); e1000_init_hw(&adapter->hw); e1000_reset_adaptive(&adapter->hw); e1000_phy_get_info(&adapter->hw, &adapter->phy_info); - - e1000_enable_WOL(adapter); } /** @@ -324,10 +346,10 @@ e1000_probe(struct pci_dev *pdev, if((i = pci_enable_device(pdev))) return i; - if(!(i = pci_set_dma_mask(pdev, (u64) 0xffffffffffffffff))) { + if(!(i = pci_set_dma_mask(pdev, PCI_DMA_64BIT))) { pci_using_dac = 1; } else { - if((i = pci_set_dma_mask(pdev, (u64) 0xffffffff))) { + if((i = pci_set_dma_mask(pdev, PCI_DMA_32BIT))) { E1000_ERR("No usable DMA configuration, aborting\n"); return i; } @@ -403,8 +425,10 @@ e1000_probe(struct pci_dev *pdev, /* make sure the EEPROM is good */ - if(e1000_validate_eeprom_checksum(&adapter->hw) < 0) + if(e1000_validate_eeprom_checksum(&adapter->hw) < 0) { + printk(KERN_ERR "The EEPROM Checksum Is Not Valid\n"); goto err_eeprom; + } /* copy the MAC address out of the EEPROM */ @@ -437,7 +461,7 @@ e1000_probe(struct pci_dev *pdev, register_netdev(netdev); /* we're going to reset, so assume we have no link for now */ - + netif_carrier_off(netdev); netif_stop_queue(netdev); @@ -445,6 +469,15 @@ e1000_probe(struct pci_dev *pdev, e1000_check_options(adapter); e1000_proc_dev_setup(adapter); + /* Initial Wake on LAN setting + * If APM wake is enabled in the EEPROM, + * enable the ACPI Magic Packet filter + */ + + if((adapter->hw.mac_type >= e1000_82544) && + (E1000_READ_REG(&adapter->hw, WUC) & E1000_WUC_APME)) + adapter->wol |= E1000_WUFC_MAG; + /* reset the hardware with the new settings */ e1000_reset(adapter); @@ -555,6 +588,14 @@ e1000_sw_init(struct e1000_adapter *adapter) case E1000_DEV_ID_82540EM: hw->mac_type = e1000_82540; break; + case E1000_DEV_ID_82545EM_COPPER: + case E1000_DEV_ID_82545EM_FIBER: + hw->mac_type = e1000_82545; + break; + case E1000_DEV_ID_82546EB_COPPER: + case E1000_DEV_ID_82546EB_FIBER: + hw->mac_type = e1000_82546; + break; default: /* should never have loaded on this device */ BUG(); @@ -590,7 +631,6 @@ e1000_sw_init(struct e1000_adapter *adapter) hw->adaptive_ifs = TRUE; atomic_set(&adapter->irq_sem, 1); - spin_lock_init(&adapter->tx_lock); spin_lock_init(&adapter->stats_lock); } @@ -715,7 +755,7 @@ e1000_configure_tx(struct e1000_adapter *adapter) uint32_t tdlen = adapter->tx_ring.count * sizeof(struct e1000_tx_desc); uint32_t tctl, tipg; - E1000_WRITE_REG(&adapter->hw, TDBAL, (tdba & 0x00000000FFFFFFFF)); + E1000_WRITE_REG(&adapter->hw, TDBAL, (tdba & 0x00000000ffffffffULL)); E1000_WRITE_REG(&adapter->hw, TDBAH, (tdba >> 32)); E1000_WRITE_REG(&adapter->hw, TDLEN, tdlen); @@ -752,7 +792,7 @@ e1000_configure_tx(struct e1000_adapter *adapter) tctl = E1000_READ_REG(&adapter->hw, TCTL); - tctl &= ~E1000_TCTL_CT; + tctl &= ~E1000_TCTL_CT; tctl |= E1000_TCTL_EN | E1000_TCTL_PSP | (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT); @@ -875,7 +915,7 @@ e1000_configure_rx(struct e1000_adapter *adapter) /* set the Receive Delay Timer Register */ - if(adapter->hw.mac_type == e1000_82540) { + if(adapter->hw.mac_type >= e1000_82540) { E1000_WRITE_REG(&adapter->hw, RADV, adapter->rx_int_delay); E1000_WRITE_REG(&adapter->hw, RDTR, 64); @@ -891,7 +931,7 @@ e1000_configure_rx(struct e1000_adapter *adapter) /* Setup the Base and Length of the Rx Descriptor Ring */ - E1000_WRITE_REG(&adapter->hw, RDBAL, (rdba & 0x00000000FFFFFFFF)); + E1000_WRITE_REG(&adapter->hw, RDBAL, (rdba & 0x00000000ffffffffULL)); E1000_WRITE_REG(&adapter->hw, RDBAH, (rdba >> 32)); E1000_WRITE_REG(&adapter->hw, RDLEN, rdlen); @@ -1212,6 +1252,8 @@ e1000_watchdog(unsigned long data) { struct e1000_adapter *adapter = (struct e1000_adapter *) data; struct net_device *netdev = adapter->netdev; + struct e1000_desc_ring *txdr = &adapter->tx_ring; + int i; e1000_check_for_link(&adapter->hw); @@ -1228,7 +1270,6 @@ e1000_watchdog(unsigned long data) "Full Duplex" : "Half Duplex"); netif_carrier_on(netdev); - adapter->trans_finish = jiffies; netif_wake_queue(netdev); mod_timer(&adapter->phy_info_timer, jiffies + 2 * HZ); } @@ -1248,6 +1289,13 @@ e1000_watchdog(unsigned long data) e1000_update_stats(adapter); e1000_update_adaptive(&adapter->hw); + /* Early detection of hung controller */ + i = txdr->next_to_clean; + if(txdr->buffer_info[i].dma && + time_after(jiffies, txdr->buffer_info[i].time_stamp + HZ) && + !(E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_TXOFF)) + netif_stop_queue(netdev); + /* Reset the timer */ mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ); } @@ -1310,6 +1358,7 @@ e1000_tx_map(struct e1000_adapter *adapter, struct sk_buff *skb) skb->data + offset, size, PCI_DMA_TODEVICE); + tx_ring->buffer_info[i].time_stamp = jiffies; len -= size; offset += size; @@ -1388,20 +1437,11 @@ static int e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct e1000_adapter *adapter = netdev->priv; - unsigned long flags; int tx_flags = 0, count; int f; - if(time_after(netdev->trans_start, adapter->trans_finish + HZ) && - !(E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_TXOFF)) { - - adapter->trans_finish = jiffies; - netif_stop_queue(netdev); - return 1; - } - count = TXD_USE_COUNT(skb->len - skb->data_len, adapter->max_data_per_txd); for(f = 0; f < skb_shinfo(skb)->nr_frags; f++) @@ -1410,14 +1450,10 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) if(skb->ip_summed == CHECKSUM_HW) count++; - spin_lock_irqsave(&adapter->tx_lock, flags); - e1000_clean_tx_irq(adapter); if(E1000_DESC_UNUSED(&adapter->tx_ring) < count) { netif_stop_queue(netdev); - spin_unlock_irqrestore(&adapter->tx_lock, flags); return 1; } - spin_unlock_irqrestore(&adapter->tx_lock, flags); if(e1000_tx_csum(adapter, skb)) tx_flags |= E1000_TX_FLAGS_CSUM; @@ -1428,7 +1464,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) tx_flags |= (vlan_tx_tag_get(skb) << E1000_TX_FLAGS_VLAN_SHIFT); } #endif - + count = e1000_tx_map(adapter, skb); e1000_tx_queue(adapter, count, tx_flags); @@ -1701,12 +1737,7 @@ e1000_intr(int irq, void *data, struct pt_regs *regs) } e1000_clean_rx_irq(adapter); - - if((icr & E1000_ICR_TXDW) && spin_trylock(&adapter->tx_lock)) { - e1000_clean_tx_irq(adapter); - spin_unlock(&adapter->tx_lock); - } - + e1000_clean_tx_irq(adapter); i--; } @@ -1753,8 +1784,6 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter) i = (i + 1) % tx_ring->count; tx_desc = E1000_TX_DESC(*tx_ring, i); - - adapter->trans_finish = jiffies; } tx_ring->next_to_clean = i; @@ -1893,9 +1922,6 @@ e1000_alloc_rx_buffers(struct e1000_adapter *adapter) int reserve_len; int i; - if(!netif_running(netdev)) - return; - reserve_len = 2; i = rx_ring->next_to_use; @@ -1991,32 +2017,16 @@ e1000_rx_checksum(struct e1000_adapter *adapter, } } -/** - * e1000_enable_WOL - Wake On Lan Support (Magic Pkt) - * @adapter: Adapter structure - **/ - void -e1000_enable_WOL(struct e1000_adapter *adapter) +e1000_read_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t *value) { - uint32_t wuc; - - if(adapter->hw.mac_type < e1000_82544) - return; - - if(adapter->wol) { - wuc = E1000_WUC_APME | E1000_WUC_PME_EN | - E1000_WUC_PME_STATUS | E1000_WUC_APMPME; - - E1000_WRITE_REG(&adapter->hw, WUC, wuc); + struct e1000_adapter *adapter = hw->back; - E1000_WRITE_REG(&adapter->hw, WUFC, adapter->wol); - } + pci_read_config_word(adapter->pdev, reg, value); } void -e1000_write_pci_cfg(struct e1000_hw *hw, - uint32_t reg, uint16_t *value) +e1000_write_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t *value) { struct e1000_adapter *adapter = hw->back; @@ -2086,10 +2096,10 @@ e1000_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid) uint32_t vfta, index; e1000_irq_disable(adapter); - + if(adapter->vlgrp) adapter->vlgrp->vlan_devices[vid] = NULL; - + e1000_irq_enable(adapter); /* remove VID from filter table*/ @@ -2101,4 +2111,93 @@ e1000_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid) } #endif +static int +e1000_notify_reboot(struct notifier_block *nb, unsigned long event, void *p) +{ + struct pci_dev *pdev = NULL; + + switch(event) { + case SYS_DOWN: + case SYS_HALT: + case SYS_POWER_OFF: + pci_for_each_dev(pdev) { + if(pci_dev_driver(pdev) == &e1000_driver) + e1000_suspend(pdev, 3); + } + } + return NOTIFY_DONE; +} + +static int +e1000_suspend(struct pci_dev *pdev, uint32_t state) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct e1000_adapter *adapter = netdev->priv; + uint32_t ctrl, ctrl_ext, rctl; + + netif_device_detach(netdev); + + if(netif_running(netdev)) + e1000_down(adapter); + + if(adapter->wol) { + e1000_setup_rctl(adapter); + e1000_set_multi(netdev); + + if(adapter->wol & E1000_WUFC_MC) { + rctl = E1000_READ_REG(&adapter->hw, RCTL); + rctl |= E1000_RCTL_MPE; + E1000_WRITE_REG(&adapter->hw, RCTL, rctl); + } + + if(adapter->hw.media_type == e1000_media_type_fiber) { + #define E1000_CTRL_ADVD3WUC 0x00100000 + ctrl = E1000_READ_REG(&adapter->hw, CTRL); + ctrl |= E1000_CTRL_ADVD3WUC; + E1000_WRITE_REG(&adapter->hw, CTRL, ctrl); + + ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_SDP7_DATA; + E1000_WRITE_REG(&adapter->hw, CTRL_EXT, ctrl_ext); + } + + E1000_WRITE_REG(&adapter->hw, WUC, 0); + E1000_WRITE_REG(&adapter->hw, WUFC, adapter->wol); + pci_enable_wake(pdev, 3, 1); + } else { + E1000_WRITE_REG(&adapter->hw, WUC, 0); + E1000_WRITE_REG(&adapter->hw, WUFC, 0); + pci_enable_wake(pdev, 3, 0); + } + + pci_save_state(pdev, adapter->pci_state); + pci_set_power_state(pdev, 3); + + return 0; +} + +#ifdef CONFIG_PM +static int +e1000_resume(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct e1000_adapter *adapter = netdev->priv; + + pci_set_power_state(pdev, 0); + pci_restore_state(pdev, adapter->pci_state); + pci_enable_wake(pdev, 0, 0); + + /* Clear the wakeup status bits */ + + E1000_WRITE_REG(&adapter->hw, WUS, ~0); + + if(netif_running(netdev)) + e1000_up(adapter); + + netif_device_attach(netdev); + + return 0; +} +#endif + /* e1000_main.c */ diff --git a/drivers/net/e1000/e1000_osdep.h b/drivers/net/e1000/e1000_osdep.h index 3990e01eb1dc..e985682b7847 100644 --- a/drivers/net/e1000/e1000_osdep.h +++ b/drivers/net/e1000/e1000_osdep.h @@ -86,12 +86,14 @@ #include <linux/interrupt.h> #define usec_delay(x) udelay(x) +#ifndef msec_delay #define msec_delay(x) do { if(in_interrupt()) { \ mdelay(x); \ } else { \ set_current_state(TASK_UNINTERRUPTIBLE); \ schedule_timeout((x * HZ)/1000); \ } } while(0) +#endif #define PCI_COMMAND_REGISTER PCI_COMMAND #define CMD_MEM_WRT_INVALIDATE PCI_COMMAND_INVALIDATE diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index 8c03bfc71ee0..537998e6037d 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -524,10 +524,8 @@ static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 }; static int eepro100_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); static void eepro100_remove_one (struct pci_dev *pdev); -#ifdef CONFIG_PM static int eepro100_suspend (struct pci_dev *pdev, u32 state); static int eepro100_resume (struct pci_dev *pdev); -#endif static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len); static int mdio_read(long ioaddr, int phy_id, int location); diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 66b5455f29c9..0e8be7a0a3d2 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -845,8 +845,9 @@ pcnet32_open(struct net_device *dev) if (lp->options == (PCNET32_PORT_FD | PCNET32_PORT_AUI)) val |= 2; } else if (lp->options & PCNET32_PORT_ASEL) { - /* workaround for xSeries250 */ - val |= 3; + /* workaround of xSeries250, turn on for 79C975 only */ + i = ((lp->a.read_csr(ioaddr, 88) | (lp->a.read_csr(ioaddr,89) << 16)) >> 12) & 0xffff; + if (i == 0x2627) val |= 3; } lp->a.write_bcr (ioaddr, 9, val); } diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index cb824b5cd4df..92ea8bea3167 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -12,8 +12,8 @@ (C) 1999 Benjamin Reed. All Rights Reserved. Permission to use code in the Developer's manual was granted for this driver by Aironet. Major code contributions were received from Javier Achirica - and Jean Tourrilhes <jt@hpl.hp.com>. Code was also integrated from - the Cisco Aironet driver for Linux. + <achirica@users.sourceforge.net> and Jean Tourrilhes <jt@hpl.hp.com>. + Code was also integrated from the Cisco Aironet driver for Linux. ======================================================================*/ @@ -46,7 +46,7 @@ #include <asm/uaccess.h> #ifdef CONFIG_PCI -static struct pci_device_id card_ids[] = __devinitdata { +static struct pci_device_id card_ids[] __devinitdata = { { 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, }, { 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID }, { 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, }, @@ -70,13 +70,11 @@ static struct pci_driver airo_driver = { /* Include Wireless Extension definition and check version - Jean II */ #include <linux/wireless.h> #define WIRELESS_SPY // enable iwspy support -#if WIRELESS_EXT < 9 -#warning "Wireless extension v9 or newer required - please upgrade your kernel" -#undef WIRELESS_EXT -#undef WIRELESS_SPY -#endif -#define CISCO_EXT // enable Cisco extensions +#if WIRELESS_EXT > 12 +#include <net/iw_handler.h> // New driver API +#endif /* WIRELESS_EXT > 12 */ +#define CISCO_EXT // enable Cisco extensions #ifdef CISCO_EXT #include <linux/delay.h> #endif @@ -193,6 +191,12 @@ static char *statsLabels[] = { #ifndef RUN_AT #define RUN_AT(x) (jiffies+(x)) #endif +#ifndef PDE +static inline struct proc_dir_entry *PDE(const struct inode *inode) +{ + return inode->u.generic_ip; +} +#endif /* These variables are for insmod, since it seems that the rates @@ -215,6 +219,8 @@ static int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to re the bap, needed on some older cards and buses. */ static int adhoc; +static int probe = 1; + static int proc_uid /* = 0 */; static int proc_gid /* = 0 */; @@ -249,6 +255,8 @@ encryption. Units are in 512kbs. Zero (default) means there is no limit. \ Older cards used to be limited to 2mbs (4)."); MODULE_PARM(adhoc, "i"); MODULE_PARM_DESC(adhoc, "If non-zero, the card will start in adhoc mode."); +MODULE_PARM(probe, "i"); +MODULE_PARM_DESC(probe, "If zero, the driver won't start the card."); MODULE_PARM(proc_uid, "i"); MODULE_PARM_DESC(proc_uid, "The uid that the /proc files will belong to."); @@ -259,8 +267,6 @@ MODULE_PARM_DESC(airo_perm, "The permission bits of /proc/[driver/]aironet."); MODULE_PARM(proc_perm, "i"); MODULE_PARM_DESC(proc_perm, "The permission bits of the files in /proc"); -#include <asm/uaccess.h> - /* This is a kind of sloppy hack to get this information to OUT4500 and IN4500. I would be extremely interested in the situation where this doesnt work though!!! */ @@ -276,13 +282,21 @@ static int do8bitIO = 0; #define MAC_ENABLE 0x0001 #define MAC_DISABLE 0x0002 #define CMD_LOSE_SYNC 0x0003 /* Not sure what this does... */ +#define CMD_MAGIC_PKT 0x0006 #define CMD_ACCESS 0x0021 #define CMD_ALLOCATETX 0x000a #define CMD_TRANSMIT 0x000b +#define CMD_DEALLOCATETX 0x000c #define HOSTSLEEP 0x85 #define CMD_SETMODE 0x0009 #define CMD_ENABLEAUX 0x0111 #define CMD_SOFTRESET 0x0004 +#define CMD_ALLOCBUF 0x0028 +#define CMD_PSPNODES 0x0030 +#define CMD_SETPHYREG 0x003e +#define CMD_TXTEST 0x003f +#define CMD_READCFG 0x0008 +#define CMD_SAVECFG 0x0108 #define CMD_LISTBSS 0x0103 /* Registers */ @@ -335,8 +349,14 @@ static int do8bitIO = 0; #define EV_LINK 0x80 #define EV_AWAKE 0x100 #define EV_UNKNOWN 0x800 +#define EV_MIC 0x1000 /* Message Integrity Check Interrupt */ #define STATUS_INTS ( EV_AWAKE | EV_LINK | EV_TXEXC | EV_TX | EV_RX) -#define IGNORE_INTS ( EV_CMD | EV_UNKNOWN) + +#ifdef CHECK_UNKNOWN_INTS +#define IGNORE_INTS ( EV_MIC | EV_CMD | EV_UNKNOWN) +#else +#define IGNORE_INTS (~STATUS_INTS) +#endif /* The RIDs */ #define RID_CAPABILITIES 0xFF00 @@ -359,7 +379,9 @@ static int do8bitIO = 0; #define RID_LEAPUSERNAME 0xFF23 #define RID_LEAPPASSWORD 0xFF24 #define RID_STATUS 0xFF50 -#define RID_UNKNOWN52 0xFF52 +#define RID_BEACON_HST 0xFF51 +#define RID_BUSY_HST 0xFF52 +#define RID_RETRIES_HST 0xFF53 #define RID_UNKNOWN54 0xFF54 #define RID_UNKNOWN55 0xFF55 #define RID_UNKNOWN56 0xFF56 @@ -569,7 +591,9 @@ typedef struct { u16 currentXmitRate; u16 apDevExtensions; u16 normalizedSignalStrength; - u16 _reserved[10]; + u16 _reserved1; + u8 apIP[4]; + u16 _reserved[7]; } StatusRid; typedef struct { @@ -676,6 +700,10 @@ typedef struct { #else /* SIOCIWFIRSTPRIV */ #define SIOCIWFIRSTPRIV SIOCDEVPRIVATE #endif /* SIOCIWFIRSTPRIV */ +/* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably + * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root + * only and don't return the modified struct ifreq to the application which + * is usually a problem. - Jean II */ #define AIROIOCTL SIOCIWFIRSTPRIV #define AIROIDIFC AIROIOCTL + 1 @@ -739,20 +767,38 @@ typedef struct wep_key_t { u16 len; u8 key[16]; /* 40-bit and 104-bit keys */ } wep_key_t; + +/* Backward compatibility */ +#ifndef IW_ENCODE_NOKEY +#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ +#define IW_ENCODE_MODE (IW_ENCODE_DISABLED | IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN) +#endif /* IW_ENCODE_NOKEY */ + +#if WIRELESS_EXT > 12 +/* List of Wireless Handlers (new API) */ +static const struct iw_handler_def airo_handler_def; +#else /* WIRELESS_EXT > 12 */ +/* More Wireless Extensions backward compatibility */ +/* Part of iw_handler prototype we need (apart that we don't need it) */ +struct iw_request_info {}; +#endif /* WIRELESS_EXT > 12 */ #endif /* WIRELESS_EXT */ -static const char version[] = "airo.c 0.3 (Ben Reed & Javier Achirica)"; +static const char version[] = "airo.c 0.5 (Ben Reed & Javier Achirica)"; struct airo_info; static int get_dec_u16( char *buffer, int *start, int limit ); static void OUT4500( struct airo_info *, u16 register, u16 value ); static unsigned short IN4500( struct airo_info *, u16 register ); -static u16 setup_card(struct airo_info*, u8 *mac, ConfigRid *); +static u16 setup_card(struct airo_info*, u8 *mac); +static int enable_MAC( struct airo_info *ai, Resp *rsp ); +static void disable_MAC(struct airo_info *ai); static void enable_interrupts(struct airo_info*); static void disable_interrupts(struct airo_info*); -static u16 lock_issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp); static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp); +static u16 sendcommand(struct airo_info *ai, Cmd *pCmd); +static void completecommand(struct airo_info *ai, Resp *pRsp); static int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap); static int aux_bap_read(struct airo_info*, u16 *pu16Dst, int bytelen, int whichbap); @@ -766,9 +812,9 @@ static int PC4500_writerid(struct airo_info*, u16 rid, const void *pBuf, int len); static int do_writerid( struct airo_info*, u16 rid, const void *rid_data, int len ); -static u16 transmit_allocate(struct airo_info*, int lenPayload); -static int transmit_802_3_packet(struct airo_info*, u16 TxFid, char - *pPacket, int len); +static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw); +static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket); +static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket); static void airo_interrupt( int irq, void* dev_id, struct pt_regs *regs); @@ -792,25 +838,35 @@ struct airo_info { int fids[MAX_FIDS]; int registered; ConfigRid config; - u16 authtype; // Used with auto_wep + int need_commit; // Need to set config char keyindex; // Used with auto wep char defindex; // Used with auto wep struct timer_list timer; struct proc_dir_entry *proc_entry; struct airo_info *next; spinlock_t aux_lock; - spinlock_t main_lock; int flags; #define FLAG_PROMISC IFF_PROMISC #define FLAG_RADIO_OFF 0x02 +#define FLAG_LOCKED 2 +#define FLAG_802_11 0x10 int (*bap_read)(struct airo_info*, u16 *pu16Dst, int bytelen, int whichbap); - int (*header_parse)(struct sk_buff*, unsigned char *); unsigned short *flash; tdsRssiEntry *rssi; + struct semaphore sem; + struct task_struct *task; + struct tq_struct promisc_task; + struct { + struct sk_buff *skb; + int fid; + struct tq_struct task; + } xmit, xmit11; + struct net_device *wifidev; #ifdef WIRELESS_EXT - int need_commit; // Need to set config struct iw_statistics wstats; // wireless stats + unsigned long scan_timestamp; /* Time started to scan */ + struct tq_struct event_task; #ifdef WIRELESS_SPY int spy_number; u_char spy_address[IW_MAX_SPY][6]; @@ -838,13 +894,17 @@ static int readBSSListRid(struct airo_info *ai, int first, if (first == 1) { memset(&cmd, 0, sizeof(cmd)); cmd.cmd=CMD_LISTBSS; - lock_issuecommand(ai, &cmd, &rsp); + if (down_interruptible(&ai->sem)) + return -ERESTARTSYS; + issuecommand(ai, &cmd, &rsp); + up(&ai->sem); /* Let the command take effect */ set_current_state (TASK_INTERRUPTIBLE); + ai->task = current; schedule_timeout (3*HZ); + ai->task = NULL; } - rc = PC4500_readrid(ai, - first ? RID_BSSLISTFIRST : RID_BSSLISTNEXT, + rc = PC4500_readrid(ai, first ? RID_BSSLISTFIRST : RID_BSSLISTNEXT, list, sizeof(*list)); list->len = le16_to_cpu(list->len); @@ -876,10 +936,10 @@ static int writeWepKeyRid(struct airo_info*ai, WepKeyRid *pwkr, int perm) { wkr.len = cpu_to_le16(wkr.len); wkr.kindex = cpu_to_le16(wkr.kindex); wkr.klen = cpu_to_le16(wkr.klen); - rc = do_writerid(ai, RID_WEP_TEMP, &wkr, sizeof(wkr)); + rc = PC4500_writerid(ai, RID_WEP_TEMP, &wkr, sizeof(wkr)); if (rc!=SUCCESS) printk(KERN_ERR "airo: WEP_TEMP set %x\n", rc); if (perm) { - rc = do_writerid(ai, RID_WEP_PERM, &wkr, sizeof(wkr)); + rc = PC4500_writerid(ai, RID_WEP_PERM, &wkr, sizeof(wkr)); if (rc!=SUCCESS) { printk(KERN_ERR "airo: WEP_PERM set %x\n", rc); } @@ -906,29 +966,56 @@ static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr) { for(i = 0; i < 3; i++) { ssidr.ssids[i].len = cpu_to_le16(ssidr.ssids[i].len); } - rc = do_writerid(ai, RID_SSID, &ssidr, sizeof(ssidr)); + rc = PC4500_writerid(ai, RID_SSID, &ssidr, sizeof(ssidr)); return rc; } -static int readConfigRid(struct airo_info*ai, ConfigRid *cfgr) { - int rc = PC4500_readrid(ai, RID_ACTUALCONFIG, cfgr, sizeof(*cfgr)); +static int readConfigRid(struct airo_info*ai) { + int rc; u16 *s; + ConfigRid cfg; + + if (ai->config.len) + return SUCCESS; + + rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg)); + if (rc != SUCCESS) + return rc; - for(s = &cfgr->len; s <= &cfgr->rtsThres; s++) *s = le16_to_cpu(*s); + for(s = &cfg.len; s <= &cfg.rtsThres; s++) *s = le16_to_cpu(*s); - for(s = &cfgr->shortRetryLimit; s <= &cfgr->radioType; s++) + for(s = &cfg.shortRetryLimit; s <= &cfg.radioType; s++) *s = le16_to_cpu(*s); - for(s = &cfgr->txPower; s <= &cfgr->radioSpecific; s++) + for(s = &cfg.txPower; s <= &cfg.radioSpecific; s++) *s = le16_to_cpu(*s); - for(s = &cfgr->arlThreshold; s <= &cfgr->autoWake; s++) + for(s = &cfg.arlThreshold; s <= &cfg.autoWake; s++) *s = le16_to_cpu(*s); - return rc; + ai->config = cfg; + return SUCCESS; } -static int writeConfigRid(struct airo_info*ai, ConfigRid *pcfgr) { +static inline void checkThrottle(struct airo_info *ai) { + int i; +/* Old hardware had a limit on encryption speed */ + if (ai->config.authType != AUTH_OPEN && maxencrypt) { + for(i=0; i<8; i++) { + if (ai->config.rates[i] > maxencrypt) { + ai->config.rates[i] = 0; + } + } + } +} +static int writeConfigRid(struct airo_info*ai) { u16 *s; - ConfigRid cfgr = *pcfgr; + ConfigRid cfgr; + + if (!ai->need_commit) + return SUCCESS; + + ai->need_commit = 0; + checkThrottle(ai); + cfgr = ai->config; for(s = &cfgr.len; s <= &cfgr.rtsThres; s++) *s = cpu_to_le16(*s); @@ -941,7 +1028,7 @@ static int writeConfigRid(struct airo_info*ai, ConfigRid *pcfgr) { for(s = &cfgr.arlThreshold; s <= &cfgr.autoWake; s++) *s = cpu_to_le16(*s); - return do_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr)); + return PC4500_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr)); } static int readStatusRid(struct airo_info*ai, StatusRid *statr) { int rc = PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr)); @@ -963,7 +1050,7 @@ static int readAPListRid(struct airo_info*ai, APListRid *aplr) { static int writeAPListRid(struct airo_info*ai, APListRid *aplr) { int rc; aplr->len = cpu_to_le16(aplr->len); - rc = do_writerid(ai, RID_APLIST, aplr, sizeof(*aplr)); + rc = PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr)); return rc; } static int readCapabilityRid(struct airo_info*ai, CapabilityRid *capr) { @@ -990,18 +1077,110 @@ static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid) { static int airo_open(struct net_device *dev) { struct airo_info *info = dev->priv; - enable_interrupts(info); + /* Make sure the card is configured. + * Wireless Extensions may postpone config changes until the card + * is open (to pipeline changes and speed-up card setup). If + * those changes are not yet commited, do it now - Jean II */ + if(info->need_commit) { + Resp rsp; + disable_MAC(info); + writeConfigRid(info); + enable_MAC(info, &rsp); + } + + if (info->wifidev != dev) + enable_interrupts(info); netif_start_queue(dev); return 0; } +static void get_tx_error(struct airo_info *ai, u32 fid) +{ + u16 status; + + if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) == SUCCESS) { + bap_read(ai, &status, 2, BAP0); + if (le16_to_cpu(status) & 2) /* Too many retries */ + ai->stats.tx_aborted_errors++; + if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */ + ai->stats.tx_heartbeat_errors++; + if (le16_to_cpu(status) & 8) /* Aid fail */ + { } + if (le16_to_cpu(status) & 0x10) /* MAC disabled */ + ai->stats.tx_carrier_errors++; + if (le16_to_cpu(status) & 0x20) /* Association lost */ + { } +#if WIRELESS_EXT > 13 + /* We produce a TXDROP event only for retry or lifetime + * exceeded, because that's the only status that really mean + * that this particular node went away. + * Other errors means that *we* screwed up. - Jean II */ + if ((le16_to_cpu(status) & 2) || + (le16_to_cpu(status) & 4)) { + union iwreq_data wrqu; + char junk[0x18]; + + /* Faster to skip over useless data than to do + * another bap_setup(). We are at offset 0x6 and + * need to go to 0x18 and read 6 bytes - Jean II */ + bap_read(ai, (u16 *) junk, 0x18, BAP0); + + /* Copy 802.11 dest address. + * We use the 802.11 header because the frame may + * not be 802.3 or may be mangled... + * In Ad-Hoc mode, it will be the node address. + * In managed mode, it will be most likely the AP addr + * User space will figure out how to convert it to + * whatever it needs (IP address or else). + * - Jean II */ + memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + + /* Send event to user space */ + wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL); + } +#endif /* WIRELESS_EXT > 13 */ + } +} + +static void airo_do_xmit(struct net_device *dev) { + u16 status; + int i; + struct airo_info *priv = dev->priv; + struct sk_buff *skb = priv->xmit.skb; + int fid = priv->xmit.fid; + u32 *fids = priv->fids; + + if (down_trylock(&priv->sem) != 0) { + netif_stop_queue(dev); + priv->xmit.task.routine = (void (*)(void *))airo_do_xmit; + priv->xmit.task.data = (void *)dev; + schedule_task(&priv->xmit.task); + return; + } + status = transmit_802_3_packet (priv, fids[fid], skb->data); + up(&priv->sem); + + i = 0; + if ( status == SUCCESS ) { + dev->trans_start = jiffies; + for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++); + } else { + priv->fids[fid] &= 0xffff; + priv->stats.tx_window_errors++; + } + if (i < MAX_FIDS / 2) + netif_wake_queue(dev); + else + netif_stop_queue(dev); + dev_kfree_skb(skb); +} + static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) { s16 len; - u16 status; - u32 flags; - int i,j; - struct airo_info *priv = (struct airo_info*)dev->priv; + int i; + struct airo_info *priv = dev->priv; u32 *fids = priv->fids; if ( skb == NULL ) { @@ -1010,38 +1189,88 @@ static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) { } /* Find a vacant FID */ - spin_lock_irqsave(&priv->main_lock, flags); - for( j = 0, i = -1; j < MAX_FIDS; j++ ) { - if ( !( fids[j] & 0xffff0000 ) ) { - if ( i == -1 ) i = j; - else break; - } - } - if ( j == MAX_FIDS ) netif_stop_queue(dev); - if ( i == -1 ) { + for( i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++ ); + + if ( i == MAX_FIDS / 2 ) { priv->stats.tx_fifo_errors++; - goto tx_done; + dev_kfree_skb(skb); + } else { + /* check min length*/ + len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + /* Mark fid as used & save length for later */ + fids[i] |= (len << 16); + priv->xmit.skb = skb; + priv->xmit.fid = i; + airo_do_xmit(dev); } + return 0; +} + +static void airo_do_xmit11(struct net_device *dev) { + u16 status; + int i; + struct airo_info *priv = dev->priv; + struct sk_buff *skb = priv->xmit11.skb; + int fid = priv->xmit11.fid; + u32 *fids = priv->fids; - len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; /* check min length*/ - status = transmit_802_3_packet( priv, fids[i], skb->data, len ); + if (down_trylock(&priv->sem) != 0) { + netif_stop_queue(dev); + priv->xmit11.task.routine = (void (*)(void *))airo_do_xmit11; + priv->xmit11.task.data = (void *)dev; + schedule_task(&priv->xmit11.task); + return; + } + status = transmit_802_11_packet (priv, fids[fid], skb->data); + up(&priv->sem); + i = MAX_FIDS / 2; if ( status == SUCCESS ) { - /* Mark fid as used & save length for later */ - fids[i] |= (len << 16); dev->trans_start = jiffies; + for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++); } else { + priv->fids[fid] &= 0xffff; priv->stats.tx_window_errors++; } - tx_done: - spin_unlock_irqrestore(&priv->main_lock, flags); + if (i < MAX_FIDS) + netif_wake_queue(dev); + else + netif_stop_queue(dev); dev_kfree_skb(skb); +} + +static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) { + s16 len; + int i; + struct airo_info *priv = dev->priv; + u32 *fids = priv->fids; + + if ( skb == NULL ) { + printk( KERN_ERR "airo: skb == NULL!!!\n" ); + return 0; + } + + /* Find a vacant FID */ + for( i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++ ); + + if ( i == MAX_FIDS ) { + priv->stats.tx_fifo_errors++; + dev_kfree_skb(skb); + } else { + /* check min length*/ + len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + /* Mark fid as used & save length for later */ + fids[i] |= (len << 16); + priv->xmit11.skb = skb; + priv->xmit11.fid = i; + airo_do_xmit11(dev); + } return 0; } struct net_device_stats *airo_get_stats(struct net_device *dev) { - struct airo_info *local = (struct airo_info*) dev->priv; + struct airo_info *local = dev->priv; StatsRid stats_rid; u32 *vals = stats_rid.vals; @@ -1063,27 +1292,44 @@ struct net_device_stats *airo_get_stats(struct net_device *dev) local->stats.rx_frame_errors = vals[2]; local->stats.rx_fifo_errors = vals[0]; - return (&local->stats); + return &local->stats; } -static int enable_MAC( struct airo_info *ai, Resp *rsp ); -static void disable_MAC(struct airo_info *ai); +static void airo_end_promisc(struct airo_info *ai) { + Resp rsp; -static void airo_set_multicast_list(struct net_device *dev) { - struct airo_info *ai = (struct airo_info*)dev->priv; + if ((IN4500(ai, EVSTAT) & EV_CMD) != 0) { + completecommand(ai, &rsp); + up(&ai->sem); + } else { + ai->promisc_task.routine = (void (*)(void *))airo_end_promisc; + ai->promisc_task.data = (void *)ai; + schedule_task(&ai->promisc_task); + } +} + +static void airo_set_promisc(struct airo_info *ai) { Cmd cmd; - Resp rsp; - /* For some reason this command takes a lot of time (~20 ms) and it's - * run in an interrupt handler, so we'd better be sure we needed it - * before executing it. - */ - if ((dev->flags ^ ai->flags) & IFF_PROMISC) { + if (down_trylock(&ai->sem) == 0) { memset(&cmd, 0, sizeof(cmd)); cmd.cmd=CMD_SETMODE; - cmd.parm0=(dev->flags&IFF_PROMISC) ? PROMISC : NOPROMISC; - lock_issuecommand(ai, &cmd, &rsp); - ai->flags^=IFF_PROMISC; + cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC; + sendcommand(ai, &cmd); + airo_end_promisc(ai); + } else { + ai->promisc_task.routine = (void (*)(void *))airo_set_promisc; + ai->promisc_task.data = (void *)ai; + schedule_task(&ai->promisc_task); + } +} + +static void airo_set_multicast_list(struct net_device *dev) { + struct airo_info *ai = dev->priv; + + if ((dev->flags ^ ai->flags) & IFF_PROMISC) { + ai->flags ^= IFF_PROMISC; + airo_set_promisc(ai); } if ((dev->flags&IFF_ALLMULTI)||dev->mc_count>0) { @@ -1093,14 +1339,18 @@ static void airo_set_multicast_list(struct net_device *dev) { static int airo_set_mac_address(struct net_device *dev, void *p) { - struct airo_info *ai = (struct airo_info*)dev->priv; + struct airo_info *ai = dev->priv; struct sockaddr *addr = p; - ConfigRid cfg; + Resp rsp; - readConfigRid (ai, &cfg); - memcpy (cfg.macAddr, addr->sa_data, dev->addr_len); - writeConfigRid (ai, &cfg); - memcpy (dev->dev_addr, addr->sa_data, dev->addr_len); + memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len); + ai->need_commit = 1; + disable_MAC(ai); + writeConfigRid (ai); + enable_MAC(ai, &rsp); + memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len); + if (ai->wifidev) + memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len); return 0; } @@ -1114,10 +1364,12 @@ static int airo_change_mtu(struct net_device *dev, int new_mtu) static int airo_close(struct net_device *dev) { - struct airo_info *ai = (struct airo_info*)dev->priv; + struct airo_info *ai = dev->priv; netif_stop_queue(dev); - disable_interrupts( ai ); + + if (ai->wifidev != dev) + disable_interrupts( ai ); return 0; } @@ -1125,7 +1377,8 @@ static void del_airo_dev( struct net_device *dev ); void stop_airo_card( struct net_device *dev, int freeres ) { - struct airo_info *ai = (struct airo_info*)dev->priv; + struct airo_info *ai = dev->priv; + flush_scheduled_tasks(); if (ai->flash) kfree(ai->flash); if (ai->rssi) @@ -1133,6 +1386,11 @@ void stop_airo_card( struct net_device *dev, int freeres ) takedown_proc_entry( dev, ai ); if (ai->registered) { unregister_netdev( dev ); + if (ai->wifidev) { + unregister_netdev(ai->wifidev); + kfree(ai->wifidev); + ai->wifidev = 0; + } ai->registered = 0; } disable_interrupts(ai); @@ -1150,6 +1408,69 @@ EXPORT_SYMBOL(stop_airo_card); static int add_airo_dev( struct net_device *dev ); +int wll_header_parse(struct sk_buff *skb, unsigned char *haddr) +{ + memcpy(haddr, skb->mac.raw + 10, ETH_ALEN); + return ETH_ALEN; +} + +static void wifi_setup(struct net_device *dev, struct net_device *ethdev) +{ + struct airo_info *ai = ethdev->priv; + dev->priv = ai; + dev->hard_header = 0; + dev->rebuild_header = 0; + dev->hard_header_cache = 0; + dev->header_cache_update= 0; + + dev->hard_header_parse = wll_header_parse; + dev->hard_start_xmit = &airo_start_xmit11; + dev->get_stats = &airo_get_stats; + dev->set_mac_address = &airo_set_mac_address; + dev->do_ioctl = &airo_ioctl; +#ifdef WIRELESS_EXT + dev->get_wireless_stats = airo_get_wireless_stats; +#if WIRELESS_EXT > 12 + dev->wireless_handlers = (struct iw_handler_def *)&airo_handler_def; +#endif /* WIRELESS_EXT > 12 */ +#endif /* WIRELESS_EXT */ + dev->change_mtu = &airo_change_mtu; + dev->open = &airo_open; + dev->stop = &airo_close; + dev->irq = ethdev->irq; + dev->base_addr = ethdev->base_addr; + + dev->type = ARPHRD_IEEE80211; + dev->hard_header_len = ETH_HLEN; + dev->mtu = 2312; + dev->addr_len = ETH_ALEN; + memcpy(dev->dev_addr, ethdev->dev_addr, dev->addr_len); + dev->tx_queue_len = 100; + + memset(dev->broadcast,0xFF, ETH_ALEN); + + dev->flags = IFF_BROADCAST|IFF_MULTICAST; +} + +static struct net_device *init_wifidev(struct airo_info *ai, + struct net_device *ethdev) +{ + int err; + struct net_device *dev = (struct net_device*)kmalloc(sizeof *dev,GFP_KERNEL); + if (!dev) return 0; + memset(dev, 0, sizeof(*dev)); + + strcpy(dev->name, "wifi%d"); + dev->priv = ai; + wifi_setup(dev, ethdev); + err = register_netdev(dev); + if (err<0) { + kfree(dev); + return 0; + } + return dev; +} + struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia ) { struct net_device *dev; @@ -1168,11 +1489,13 @@ struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia ) } ai = dev->priv; + ai->wifidev = 0; ai->registered = 0; ai->dev = dev; ai->aux_lock = SPIN_LOCK_UNLOCKED; - ai->main_lock = SPIN_LOCK_UNLOCKED; - ai->header_parse = dev->hard_header_parse; + sema_init(&ai->sem, 1); + ai->need_commit = 0; + ai->config.len = 0; rc = add_airo_dev( dev ); if (rc) goto err_out_free; @@ -1185,6 +1508,9 @@ struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia ) dev->do_ioctl = &airo_ioctl; #ifdef WIRELESS_EXT dev->get_wireless_stats = airo_get_wireless_stats; +#if WIRELESS_EXT > 12 + dev->wireless_handlers = (struct iw_handler_def *)&airo_handler_def; +#endif /* WIRELESS_EXT > 12 */ #endif /* WIRELESS_EXT */ dev->change_mtu = &airo_change_mtu; dev->open = &airo_open; @@ -1204,15 +1530,17 @@ struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia ) } } - if ( setup_card( ai, dev->dev_addr, &ai->config) != SUCCESS ) { + if (probe && setup_card( ai, dev->dev_addr ) != SUCCESS ) { printk( KERN_ERR "airo: MAC could not be enabled\n" ); rc = -EIO; goto err_out_res; - } + } else + ai->bap_read = fast_bap_read; rc = register_netdev(dev); if (rc) goto err_out_res; + ai->wifidev = init_wifidev(ai, dev); ai->registered = 1; printk( KERN_INFO "airo: MAC enabled %s %x:%x:%x:%x:%x:%x\n", @@ -1221,8 +1549,9 @@ struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia ) dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5] ); /* Allocate the transmit buffers */ - for( i = 0; i < MAX_FIDS; i++ ) - ai->fids[i] = transmit_allocate( ai, 2312 ); + if (probe) + for( i = 0; i < MAX_FIDS; i++ ) + ai->fids[i] = transmit_allocate(ai,2312,i>=MAX_FIDS/2); setup_proc_entry( dev, dev->priv ); /* XXX check for failure */ netif_start_queue(dev); @@ -1255,9 +1584,8 @@ static int waitbusy (struct airo_info *ai) { int reset_airo_card( struct net_device *dev ) { int i; - struct airo_info *ai = (struct airo_info*)dev->priv; + struct airo_info *ai = dev->priv; - disable_MAC(ai); waitbusy (ai); OUT4500(ai,COMMAND,CMD_SOFTRESET); set_current_state (TASK_UNINTERRUPTIBLE); @@ -1265,7 +1593,7 @@ int reset_airo_card( struct net_device *dev ) { waitbusy (ai); set_current_state (TASK_UNINTERRUPTIBLE); schedule_timeout (HZ/5); - if ( setup_card(ai, dev->dev_addr, &(ai)->config) != SUCCESS ) { + if ( setup_card(ai, dev->dev_addr ) != SUCCESS ) { printk( KERN_ERR "airo: MAC could not be enabled\n" ); return -1; } else { @@ -1280,7 +1608,7 @@ int reset_airo_card( struct net_device *dev ) { ); /* Allocate the transmit buffers */ for( i = 0; i < MAX_FIDS; i++ ) - ai->fids[i] = transmit_allocate( ai, 2312 ); + ai->fids[i] = transmit_allocate(ai,2312,i>=MAX_FIDS/2); } enable_interrupts( ai ); netif_wake_queue(dev); @@ -1289,17 +1617,37 @@ int reset_airo_card( struct net_device *dev ) { EXPORT_SYMBOL(reset_airo_card); -static int wll_header_parse(struct sk_buff *skb, unsigned char *haddr) -{ - memcpy(haddr, skb->mac.raw + 10, ETH_ALEN); - return ETH_ALEN; +#if WIRELESS_EXT > 13 +static void airo_send_event(struct net_device *dev) { + struct airo_info *ai = dev->priv; + union iwreq_data wrqu; + StatusRid status_rid; + + if (down_trylock(&ai->sem) == 0) { + __set_bit(FLAG_LOCKED, &ai->flags); + PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid)); + clear_bit(FLAG_LOCKED, &ai->flags); + up(&ai->sem); + wrqu.data.length = 0; + wrqu.data.flags = 0; + memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* Send event to user space */ + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); + } else { + ai->event_task.routine = (void (*)(void *))airo_send_event; + ai->event_task.data = (void *)dev; + schedule_task(&ai->event_task); + } } +#endif static void airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) { struct net_device *dev = (struct net_device *)dev_id; u16 status; u16 fid; - struct airo_info *apriv = (struct airo_info *)dev->priv; + struct airo_info *apriv = dev->priv; u16 savedInterrupts = 0; if (!netif_device_present(dev)) @@ -1307,7 +1655,7 @@ static void airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) { for (;;) { status = IN4500( apriv, EVSTAT ); - if ( !status || status == 0xffff ) break; + if ( !(status & STATUS_INTS) || status == 0xffff ) break; if ( status & EV_AWAKE ) { OUT4500( apriv, EVACK, EV_AWAKE ); @@ -1320,6 +1668,9 @@ static void airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) { } if ( status & EV_LINK ) { +#if WIRELESS_EXT > 13 + union iwreq_data wrqu; +#endif /* WIRELESS_EXT > 13 */ /* The link status has changed, if you want to put a monitor hook in, do it here. (Remember that interrupts are still disabled!) @@ -1360,30 +1711,72 @@ static void airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) { apriv->timer.expires = RUN_AT(HZ*3); add_timer(&apriv->timer); } + } else { + struct task_struct *task = apriv->task; + if (task) + wake_up_process (task); } +#if WIRELESS_EXT > 13 + /* Question : is ASSOCIATED the only status + * that is valid ? We want to catch handover + * and reassociations as valid status + * Jean II */ + if(newStatus == ASSOCIATED) { + if (apriv->scan_timestamp) { + /* Send an empty event to user space. + * We don't send the received data on + * the event because it would require + * us to do complex transcoding, and + * we want to minimise the work done in + * the irq handler. Use a request to + * extract the data - Jean II */ + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); + apriv->scan_timestamp = 0; + } + airo_send_event(dev); + } else { + memset(wrqu.ap_addr.sa_data, '\0', ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* Send event to user space */ + wireless_send_event(dev, SIOCGIWAP, &wrqu,NULL); + } +#endif /* WIRELESS_EXT > 13 */ } /* Check to see if there is something to receive */ if ( status & EV_RX ) { struct sk_buff *skb = NULL; u16 fc, len, hdrlen = 0; +#pragma pack(1) struct { u16 status, len; u8 rssi[2]; + u8 rate; + u8 freq; + u16 tmp[4]; } hdr; +#pragma pack() + u16 gap; + u16 tmpbuf[4]; + u16 *buffer; fid = IN4500( apriv, RXFID ); /* Get the packet length */ - if (dev->type == ARPHRD_IEEE80211) { + if (apriv->flags & FLAG_802_11) { bap_setup (apriv, fid, 4, BAP0); bap_read (apriv, (u16*)&hdr, sizeof(hdr), BAP0); /* Bad CRC. Ignore packet */ if (le16_to_cpu(hdr.status) & 2) hdr.len = 0; + if (apriv->wifidev == NULL) + hdr.len = 0; } else { - bap_setup (apriv, fid, 6, BAP0); - bap_read (apriv, (u16*)&hdr.len, 4, BAP0); + bap_setup (apriv, fid, 0x36, BAP0); + bap_read (apriv, (u16*)&hdr.len, 2, BAP0); } len = le16_to_cpu(hdr.len); @@ -1392,13 +1785,24 @@ static void airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) { len = 0; } if (len) { - if (dev->type == ARPHRD_IEEE80211) { - bap_setup (apriv, fid, 0x14, BAP0); + if (apriv->flags & FLAG_802_11) { bap_read (apriv, (u16*)&fc, sizeof(fc), BAP0); - if ((le16_to_cpu(fc) & 0x300) == 0x300) - hdrlen = 30; - else - hdrlen = 24; + fc = le16_to_cpu(fc); + switch (fc & 0xc) { + case 4: + if ((fc & 0xe0) == 0xc0) + hdrlen = 10; + else + hdrlen = 16; + break; + case 8: + if ((fc&0x300)==0x300){ + hdrlen = 30; + break; + } + default: + hdrlen = 24; + } } else hdrlen = 12; @@ -1409,10 +1813,8 @@ static void airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) { } } if (len) { - u16 *buffer; buffer = (u16*)skb_put (skb, len + hdrlen); - if (dev->type == ARPHRD_IEEE80211) { - u16 gap, tmpbuf[4]; + if (apriv->flags & FLAG_802_11) { buffer[0] = fc; bap_read (apriv, buffer + 1, hdrlen - 2, BAP0); if (hdrlen == 24) @@ -1420,12 +1822,16 @@ static void airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) { bap_read (apriv, &gap, sizeof(gap), BAP0); gap = le16_to_cpu(gap); - if (gap && gap <= 8) - bap_read (apriv, tmpbuf, gap, BAP0); + if (gap) { + if (gap <= 8) + bap_read (apriv, tmpbuf, gap, BAP0); + else + printk(KERN_ERR "airo: gaplen too big. Problems will follow...\n"); + } + bap_read (apriv, buffer + hdrlen/2, len, BAP0); } else { - bap_setup (apriv, fid, 0x38, BAP0); bap_read (apriv, buffer,len + hdrlen,BAP0); } OUT4500( apriv, EVACK, EV_RX); @@ -1434,11 +1840,15 @@ static void airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) { int i; char *sa; - sa = (char*)buffer + ((dev->type == ARPHRD_IEEE80211) ? 10 : 6); + sa = (char*)buffer + ((apriv->flags & FLAG_802_11) ? 10 : 6); for (i=0; i<apriv->spy_number; i++) if (!memcmp(sa,apriv->spy_address[i],6)) { + if (!(apriv->flags & FLAG_802_11)) { + bap_setup (apriv, fid, 8, BAP0); + bap_read (apriv, (u16*)hdr.rssi, 2, BAP0); + } apriv->spy_stat[i].qual = hdr.rssi[0]; if (apriv->rssi) apriv->spy_stat[i].level = 0x100 - apriv->rssi[hdr.rssi[1]].rssidBm; @@ -1450,16 +1860,17 @@ static void airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) { } } #endif /* WIRELESS_SPY */ - dev->last_rx = jiffies; - skb->dev = dev; - skb->ip_summed = CHECKSUM_NONE; - if (dev->type == ARPHRD_IEEE80211) { + if (apriv->flags & FLAG_802_11) { skb->mac.raw = skb->data; - skb_pull (skb, hdrlen); skb->pkt_type = PACKET_OTHERHOST; + skb->dev = apriv->wifidev; skb->protocol = htons(ETH_P_802_2); - } else + } else { + skb->dev = dev; skb->protocol = eth_type_trans(skb,dev); + } + skb->dev->last_rx = jiffies; + skb->ip_summed = CHECKSUM_NONE; netif_rx( skb ); } else @@ -1482,27 +1893,16 @@ static void airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) { apriv->fids[i] &= 0xffff; } } - if (index != -1) netif_wake_queue(dev); - if ((status & EV_TXEXC) && - (bap_setup(apriv, fid, 4, BAP1) == SUCCESS)) { - - u16 status; - bap_read(apriv, &status, 2, BAP1); - if (le16_to_cpu(status) & 2) - apriv->stats.tx_aborted_errors++; - if (le16_to_cpu(status) & 4) - apriv->stats.tx_heartbeat_errors++; - if (le16_to_cpu(status) & 0x10) - apriv->stats.tx_carrier_errors++; + if (index != -1) { + netif_wake_queue(dev); + if (status & EV_TXEXC) + get_tx_error(apriv, index); } OUT4500( apriv, EVACK, status & (EV_TX | EV_TXEXC)); if (index==-1) { printk( KERN_ERR "airo: Unallocated FID was used to xmit\n" ); } } - if ( status & ~STATUS_INTS ) - OUT4500( apriv, EVACK, status & ~STATUS_INTS); - if ( status & ~STATUS_INTS & ~IGNORE_INTS ) printk( KERN_WARNING "airo: Got weird status %x\n", status & ~STATUS_INTS & ~IGNORE_INTS ); @@ -1546,12 +1946,20 @@ static u16 IN4500( struct airo_info *ai, u16 reg ) { } static int enable_MAC( struct airo_info *ai, Resp *rsp ) { + int rc; Cmd cmd; if (ai->flags&FLAG_RADIO_OFF) return SUCCESS; memset(&cmd, 0, sizeof(cmd)); cmd.cmd = MAC_ENABLE; - return lock_issuecommand(ai, &cmd, rsp); + if (test_bit(FLAG_LOCKED, &ai->flags) != 0) + return issuecommand(ai, &cmd, rsp); + + if (down_interruptible(&ai->sem)) + return -ERESTARTSYS; + rc = issuecommand(ai, &cmd, rsp); + up(&ai->sem); + return rc; } static void disable_MAC( struct airo_info *ai ) { @@ -1560,7 +1968,15 @@ static void disable_MAC( struct airo_info *ai ) { memset(&cmd, 0, sizeof(cmd)); cmd.cmd = MAC_DISABLE; // disable in case already enabled - lock_issuecommand(ai, &cmd, &rsp); + if (test_bit(FLAG_LOCKED, &ai->flags) != 0) { + issuecommand(ai, &cmd, &rsp); + return; + } + + if (down_interruptible(&ai->sem)) + return; + issuecommand(ai, &cmd, &rsp); + up(&ai->sem); } static void enable_interrupts( struct airo_info *ai ) { @@ -1577,12 +1993,10 @@ static void disable_interrupts( struct airo_info *ai ) { OUT4500( ai, EVINTEN, 0 ); } -static u16 setup_card(struct airo_info *ai, u8 *mac, - ConfigRid *config) +static u16 setup_card(struct airo_info *ai, u8 *mac) { Cmd cmd; Resp rsp; - ConfigRid cfg; int status; int i; SsidRid mySsid; @@ -1599,18 +2013,23 @@ static u16 setup_card(struct airo_info *ai, u8 *mac, /* The NOP is the first step in getting the card going */ cmd.cmd = NOP; cmd.parm0 = cmd.parm1 = cmd.parm2 = 0; - if ( lock_issuecommand( ai, &cmd, &rsp ) != SUCCESS ) { + if (down_interruptible(&ai->sem)) + return ERROR; + if ( issuecommand( ai, &cmd, &rsp ) != SUCCESS ) { + up(&ai->sem); return ERROR; } memset(&cmd, 0, sizeof(cmd)); cmd.cmd = MAC_DISABLE; // disable in case already enabled - if ( lock_issuecommand( ai, &cmd, &rsp ) != SUCCESS ) { + if ( issuecommand( ai, &cmd, &rsp ) != SUCCESS ) { + up(&ai->sem); return ERROR; } // Let's figure out if we need to use the AUX port cmd.cmd = CMD_ENABLEAUX; - if (lock_issuecommand(ai, &cmd, &rsp) != SUCCESS) { + if (issuecommand(ai, &cmd, &rsp) != SUCCESS) { + up(&ai->sem); printk(KERN_ERR "airo: Error checking for AUX port\n"); return ERROR; } @@ -1621,13 +2040,12 @@ static u16 setup_card(struct airo_info *ai, u8 *mac, ai->bap_read = aux_bap_read; printk(KERN_DEBUG "airo: Doing AUX bap_reads\n"); } - if ( config->len ) { - cfg = *config; - } else { + up(&ai->sem); + if (ai->config.len == 0) { tdsRssiRid rssi_rid; // general configuration (read/modify/write) - status = readConfigRid(ai, &cfg); + status = readConfigRid(ai); if ( status != SUCCESS ) return ERROR; status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid)); @@ -1643,60 +2061,63 @@ static u16 setup_card(struct airo_info *ai, u8 *mac, } status = readCapabilityRid(ai, &cap_rid); if ((status == SUCCESS) && (cap_rid.softCap & 8)) - cfg.rmode |= RXMODE_NORMALIZED_RSSI; + ai->config.rmode |= RXMODE_NORMALIZED_RSSI; else printk(KERN_WARNING "airo: unknown received signal level scale\n"); } - cfg.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS; + ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS; /* Save off the MAC */ for( i = 0; i < 6; i++ ) { - mac[i] = cfg.macAddr[i]; + mac[i] = ai->config.macAddr[i]; } /* Check to see if there are any insmod configured rates to add */ if ( rates ) { int i = 0; - if ( rates[0] ) memset(cfg.rates,0,sizeof(cfg.rates)); + if ( rates[0] ) memset(ai->config.rates,0,sizeof(ai->config.rates)); for( i = 0; i < 8 && rates[i]; i++ ) { - cfg.rates[i] = rates[i]; + ai->config.rates[i] = rates[i]; } } if ( basic_rate > 0 ) { int i; for( i = 0; i < 8; i++ ) { - if ( cfg.rates[i] == basic_rate || - !cfg.rates ) { - cfg.rates[i] = basic_rate | 0x80; + if ( ai->config.rates[i] == basic_rate || + !ai->config.rates ) { + ai->config.rates[i] = basic_rate | 0x80; break; } } } - cfg.authType = ai->authtype; - *config = cfg; + if (auto_wep) + ai->config.authType = AUTH_SHAREDKEY; + ai->need_commit = 1; } /* Setup the SSIDs if present */ if ( ssids[0] ) { - int i = 0; + int i; for( i = 0; i < 3 && ssids[i]; i++ ) { mySsid.ssids[i].len = strlen(ssids[i]); if ( mySsid.ssids[i].len > 32 ) mySsid.ssids[i].len = 32; memcpy(mySsid.ssids[i].ssid, ssids[i], mySsid.ssids[i].len); - mySsid.ssids[i].len = mySsid.ssids[i].len; } } - status = writeConfigRid(ai, &cfg); + status = writeConfigRid(ai); if ( status != SUCCESS ) return ERROR; /* Set up the SSID list */ status = writeSsidRid(ai, &mySsid); if ( status != SUCCESS ) return ERROR; + status = enable_MAC(ai, &rsp); + if ( status != SUCCESS ) return ERROR; + /* Grab the initial wep key, we gotta save it for auto_wep */ rc = readWepKeyRid(ai, &wkr, 1); if (rc == SUCCESS) do { @@ -1714,36 +2135,49 @@ static u16 setup_card(struct airo_info *ai, u8 *mac, return SUCCESS; } -static u16 lock_issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) { - int rc; - long flags; +static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) { + // Im really paranoid about letting it run forever! + int max_tries = 600000; - spin_lock_irqsave(&ai->main_lock, flags); - rc = issuecommand(ai, pCmd, pRsp); - spin_unlock_irqrestore(&ai->main_lock, flags); - return rc; + if (sendcommand(ai, pCmd) == (u16)ERROR) + return ERROR; + + while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) { + if (!in_interrupt() && (max_tries & 255) == 0) + schedule(); + } + if ( max_tries == -1 ) { + printk( KERN_ERR + "airo: Max tries exceeded waiting for command\n" ); + return ERROR; + } + completecommand(ai, pRsp); + return SUCCESS; } -static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) { +static u16 sendcommand(struct airo_info *ai, Cmd *pCmd) { // Im really paranoid about letting it run forever! int max_tries = 600000; + u16 cmd; OUT4500(ai, PARAM0, pCmd->parm0); OUT4500(ai, PARAM1, pCmd->parm1); OUT4500(ai, PARAM2, pCmd->parm2); OUT4500(ai, COMMAND, pCmd->cmd); - while ( max_tries-- && - (IN4500(ai, EVSTAT) & EV_CMD) == 0) { - if ( IN4500(ai, COMMAND) == pCmd->cmd) { - // PC4500 didn't notice command, try again - OUT4500(ai, COMMAND, pCmd->cmd); - } - } + while ( max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0 && + (cmd = IN4500(ai, COMMAND)) != 0 ) + if (cmd == pCmd->cmd) + // PC4500 didn't notice command, try again + OUT4500(ai, COMMAND, pCmd->cmd); if ( max_tries == -1 ) { printk( KERN_ERR "airo: Max tries exceeded when issueing command\n" ); return ERROR; } + return SUCCESS; +} + +static void completecommand(struct airo_info *ai, Resp *pRsp) { // command completed pRsp->status = IN4500(ai, STATUS); pRsp->rsp0 = IN4500(ai, RESP0); @@ -1756,7 +2190,6 @@ static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) { } // acknowledge processing the status/response OUT4500(ai, EVACK, EV_CMD); - return SUCCESS; } /* Sets up the bap to start exchange data. whichbap should @@ -1896,11 +2329,14 @@ static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd) * we must get a lock. */ static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len) { - u16 status; - long flags; + u16 status, dolock = 0; int rc = SUCCESS; - spin_lock_irqsave(&ai->main_lock, flags); + if (test_bit(FLAG_LOCKED, &ai->flags) == 0) { + dolock = 1; + if (down_interruptible(&ai->sem)) + return ERROR; + } if ( (status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != SUCCESS) { rc = status; goto done; @@ -1924,8 +2360,9 @@ static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len) } // read remainder of the rid rc = bap_read(ai, ((u16*)pBuf)+1, len, BAP1); - done: - spin_unlock_irqrestore(&ai->main_lock, flags); +done: + if (dolock) + up(&ai->sem); return rc; } @@ -1934,11 +2371,14 @@ static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len) static int PC4500_writerid(struct airo_info *ai, u16 rid, const void *pBuf, int len) { - u16 status; - long flags; + u16 status, dolock = 0; int rc = SUCCESS; - spin_lock_irqsave(&ai->main_lock, flags); + if (test_bit(FLAG_LOCKED, &ai->flags) == 0) { + dolock = 1; + if (down_interruptible(&ai->sem)) + return ERROR; + } // --- first access so that we can write the rid data if ( (status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) { rc = status; @@ -1953,24 +2393,32 @@ static int PC4500_writerid(struct airo_info *ai, u16 rid, // ---now commit the rid data rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS); done: - spin_unlock_irqrestore(&ai->main_lock, flags); + if (dolock) + up(&ai->sem); return rc; } /* Allocates a FID to be used for transmitting packets. We only use one for now. */ -static u16 transmit_allocate(struct airo_info *ai, int lenPayload) +static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw) { Cmd cmd; Resp rsp; u16 txFid; u16 txControl; - long flags; cmd.cmd = CMD_ALLOCATETX; cmd.parm0 = lenPayload; - if (lock_issuecommand(ai, &cmd, &rsp) != SUCCESS) return 0; - if ( (rsp.status & 0xFF00) != 0) return 0; + if (down_interruptible(&ai->sem)) + return ERROR; + if (issuecommand(ai, &cmd, &rsp) != SUCCESS) { + txFid = 0; + goto done; + } + if ( (rsp.status & 0xFF00) != 0) { + txFid = 0; + goto done; + } /* wait for the allocate event/indication * It makes me kind of nervous that this can just sit here and spin, * but in practice it only loops like four times. */ @@ -1984,15 +2432,19 @@ static u16 transmit_allocate(struct airo_info *ai, int lenPayload) * will be using the same one over and over again. */ /* We only have to setup the control once since we are not * releasing the fid. */ - txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3 - | TXCTL_ETHERNET | TXCTL_NORELEASE); - spin_lock_irqsave(&ai->main_lock, flags); - if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS) { - spin_unlock_irqrestore(&ai->main_lock, flags); - return ERROR; - } - bap_write(ai, &txControl, sizeof(txControl), BAP1); - spin_unlock_irqrestore(&ai->main_lock, flags); + if (raw) + txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11 + | TXCTL_ETHERNET | TXCTL_NORELEASE); + else + txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3 + | TXCTL_ETHERNET | TXCTL_NORELEASE); + if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS) + txFid = ERROR; + else + bap_write(ai, &txControl, sizeof(txControl), BAP1); + +done: + up(&ai->sem); return txFid; } @@ -2000,12 +2452,13 @@ static u16 transmit_allocate(struct airo_info *ai, int lenPayload) /* In general BAP1 is dedicated to transmiting packets. However, since we need a BAP when accessing RIDs, we also use BAP1 for that. Make sure the BAP1 spinlock is held when this is called. */ -static int transmit_802_3_packet(struct airo_info *ai, u16 txFid, - char *pPacket, int len) +static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket) { u16 payloadLen; Cmd cmd; Resp rsp; + u16 txFid = len; + len >>= 16; if (len < 12) { printk( KERN_WARNING "Short packet %d\n", len ); @@ -2029,6 +2482,65 @@ static int transmit_802_3_packet(struct airo_info *ai, u16 txFid, return SUCCESS; } +static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket) +{ + u16 fc, payloadLen; + Cmd cmd; + Resp rsp; + int hdrlen; + struct { + u8 addr4[6]; + u16 gaplen; + u8 gap[6]; + } gap; + u16 txFid = len; + len >>= 16; + gap.gaplen = 6; + + fc = le16_to_cpu(*(const u16*)pPacket); + switch (fc & 0xc) { + case 4: + if ((fc & 0xe0) == 0xc0) + hdrlen = 10; + else + hdrlen = 16; + break; + case 8: + if ((fc&0x300)==0x300){ + hdrlen = 30; + break; + } + default: + hdrlen = 24; + } + + if (len < hdrlen) { + printk( KERN_WARNING "Short packet %d\n", len ); + return ERROR; + } + + /* packet is 802.11 header + payload + * write the payload length and dst/src/payload */ + if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR; + /* The 802.11 header aren't counted as part of the payload, so + * we have to subtract the header bytes off */ + payloadLen = cpu_to_le16(len-hdrlen); + bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1); + if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR; + bap_write(ai, (const u16*)pPacket, hdrlen, BAP1); + bap_write(ai, hdrlen == 30 ? + (const u16*)&gap.gaplen : (const u16*)&gap, 38 - hdrlen, BAP1); + + bap_write(ai, (const u16*)(pPacket + hdrlen), len - hdrlen, BAP1); + // issue the transmit command + memset( &cmd, 0, sizeof( cmd ) ); + cmd.cmd = CMD_TRANSMIT; + cmd.parm0 = txFid; + if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR; + if ( (rsp.status & 0xFF00) != 0) return ERROR; + return SUCCESS; +} + /* * This is the proc_fs routines. It is a bit messier than I would * like! Feel free to clean it up! @@ -2288,7 +2800,7 @@ static int proc_status_open( struct inode *inode, struct file *file ) { struct proc_data *data; struct proc_dir_entry *dp = PDE(inode); struct net_device *dev = dp->data; - struct airo_info *apriv = (struct airo_info *)dev->priv; + struct airo_info *apriv = dev->priv; CapabilityRid cap_rid; StatusRid status_rid; int i; @@ -2370,12 +2882,13 @@ static int proc_stats_rid_open( struct inode *inode, struct proc_data *data; struct proc_dir_entry *dp = PDE(inode); struct net_device *dev = dp->data; - struct airo_info *apriv = (struct airo_info *)dev->priv; + struct airo_info *apriv = dev->priv; StatsRid stats; int i, j; int *vals = stats.vals; MOD_INC_USE_COUNT; + if ((file->private_data = kmalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) return -ENOMEM; memset(file->private_data, 0, sizeof(struct proc_data)); @@ -2420,56 +2933,42 @@ static int get_dec_u16( char *buffer, int *start, int limit ) { return value; } -static void checkThrottle(ConfigRid *config) { - int i; -/* Old hardware had a limit on encryption speed */ - if (config->authType != AUTH_OPEN && maxencrypt) { - for(i=0; i<8; i++) { - if (config->rates[i] > maxencrypt) { - config->rates[i] = 0; - } - } - } -} - static void proc_config_on_close( struct inode *inode, struct file *file ) { struct proc_data *data = file->private_data; struct proc_dir_entry *dp = PDE(inode); struct net_device *dev = dp->data; - struct airo_info *ai = (struct airo_info*)dev->priv; - ConfigRid config; + struct airo_info *ai = dev->priv; Resp rsp; char *line; int need_reset = 0; if ( !data->writelen ) return; - disable_MAC(ai); - readConfigRid(ai, &config); + readConfigRid(ai); line = data->wbuffer; while( line[0] ) { /*** Mode processing */ if ( !strncmp( line, "Mode: ", 6 ) ) { line += 6; - config.rmode &= 0xfe00; + if ((ai->config.rmode & 0xff) >= RXMODE_RFMON) + need_reset = 1; + ai->config.rmode &= 0xfe00; + ai->flags &= ~FLAG_802_11; if ( line[0] == 'a' ) { - config.opmode = 0; + ai->config.opmode = 0; } else { - config.opmode = 1; - if ( line[0] == 'r' ) - config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER; - else if ( line[0] == 'y' ) - config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER; - } - if (config.rmode & RXMODE_DISABLE_802_3_HEADER) { - dev->type = ARPHRD_IEEE80211; - dev->hard_header_parse = wll_header_parse; - } else if (dev->type == ARPHRD_IEEE80211) { - dev->type = ARPHRD_ETHER; - dev->hard_header_parse = ai->header_parse; - need_reset = 1; + ai->config.opmode = 1; + if ( line[0] == 'r' ) { + ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER; + ai->flags |= FLAG_802_11; + } else if ( line[0] == 'y' ) { + ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER; + ai->flags |= FLAG_802_11; + } else if ( line[0] == 'l' ) + ai->config.rmode |= RXMODE_LANMON; } + ai->need_commit = 1; } /*** Radio status */ @@ -2486,22 +2985,26 @@ static void proc_config_on_close( struct inode *inode, struct file *file ) { int j; line += 10; - memset( config.nodeName, 0, 16 ); + memset( ai->config.nodeName, 0, 16 ); /* Do the name, assume a space between the mode and node name */ for( j = 0; j < 16 && line[j] != '\n'; j++ ) { - config.nodeName[j] = line[j]; + ai->config.nodeName[j] = line[j]; } + ai->need_commit = 1; } /*** PowerMode processing */ else if ( !strncmp( line, "PowerMode: ", 11 ) ) { line += 11; if ( !strncmp( line, "PSPCAM", 6 ) ) { - config.powerSaveMode = POWERSAVE_PSPCAM; + ai->config.powerSaveMode = POWERSAVE_PSPCAM; + ai->need_commit = 1; } else if ( !strncmp( line, "PSP", 3 ) ) { - config.powerSaveMode = POWERSAVE_PSP; + ai->config.powerSaveMode = POWERSAVE_PSP; + ai->need_commit = 1; } else { - config.powerSaveMode = POWERSAVE_CAM; + ai->config.powerSaveMode = POWERSAVE_CAM; + ai->need_commit = 1; } } else if ( !strncmp( line, "DataRates: ", 11 ) ) { int v, i = 0, k = 0; /* i is index into line, @@ -2509,77 +3012,91 @@ static void proc_config_on_close( struct inode *inode, struct file *file ) { line += 11; while((v = get_dec_u16(line, &i, 3))!=-1) { - config.rates[k++] = (u8)v; + ai->config.rates[k++] = (u8)v; line += i + 1; i = 0; } + ai->need_commit = 1; } else if ( !strncmp( line, "Channel: ", 9 ) ) { int v, i = 0; line += 9; v = get_dec_u16(line, &i, i+3); - if ( v != -1 ) - config.channelSet = (u16)v; + if ( v != -1 ) { + ai->config.channelSet = (u16)v; + ai->need_commit = 1; + } } else if ( !strncmp( line, "XmitPower: ", 11 ) ) { int v, i = 0; line += 11; v = get_dec_u16(line, &i, i+3); - if ( v != -1 ) config.txPower = (u16)v; + if ( v != -1 ) { + ai->config.txPower = (u16)v; + ai->need_commit = 1; + } } else if ( !strncmp( line, "WEP: ", 5 ) ) { line += 5; switch( line[0] ) { case 's': - config.authType = (u16)AUTH_SHAREDKEY; + ai->config.authType = (u16)AUTH_SHAREDKEY; break; case 'e': - config.authType = (u16)AUTH_ENCRYPT; + ai->config.authType = (u16)AUTH_ENCRYPT; break; default: - config.authType = (u16)AUTH_OPEN; + ai->config.authType = (u16)AUTH_OPEN; break; } + ai->need_commit = 1; } else if ( !strncmp( line, "LongRetryLimit: ", 16 ) ) { int v, i = 0; line += 16; v = get_dec_u16(line, &i, 3); v = (v<0) ? 0 : ((v>255) ? 255 : v); - config.longRetryLimit = (u16)v; + ai->config.longRetryLimit = (u16)v; + ai->need_commit = 1; } else if ( !strncmp( line, "ShortRetryLimit: ", 17 ) ) { int v, i = 0; line += 17; v = get_dec_u16(line, &i, 3); v = (v<0) ? 0 : ((v>255) ? 255 : v); - config.shortRetryLimit = (u16)v; + ai->config.shortRetryLimit = (u16)v; + ai->need_commit = 1; } else if ( !strncmp( line, "RTSThreshold: ", 14 ) ) { int v, i = 0; line += 14; v = get_dec_u16(line, &i, 4); v = (v<0) ? 0 : ((v>2312) ? 2312 : v); - config.rtsThres = (u16)v; + ai->config.rtsThres = (u16)v; + ai->need_commit = 1; } else if ( !strncmp( line, "TXMSDULifetime: ", 16 ) ) { int v, i = 0; line += 16; v = get_dec_u16(line, &i, 5); v = (v<0) ? 0 : v; - config.txLifetime = (u16)v; + ai->config.txLifetime = (u16)v; + ai->need_commit = 1; } else if ( !strncmp( line, "RXMSDULifetime: ", 16 ) ) { int v, i = 0; line += 16; v = get_dec_u16(line, &i, 5); v = (v<0) ? 0 : v; - config.rxLifetime = (u16)v; + ai->config.rxLifetime = (u16)v; + ai->need_commit = 1; } else if ( !strncmp( line, "TXDiversity: ", 13 ) ) { - config.txDiversity = + ai->config.txDiversity = (line[13]=='l') ? 1 : ((line[13]=='r')? 2: 3); + ai->need_commit = 1; } else if ( !strncmp( line, "RXDiversity: ", 13 ) ) { - config.rxDiversity = + ai->config.rxDiversity = (line[13]=='l') ? 1 : ((line[13]=='r')? 2: 3); + ai->need_commit = 1; } else if ( !strncmp( line, "FragThreshold: ", 15 ) ) { int v, i = 0; @@ -2587,22 +3104,23 @@ static void proc_config_on_close( struct inode *inode, struct file *file ) { v = get_dec_u16(line, &i, 4); v = (v<256) ? 256 : ((v>2312) ? 2312 : v); v = v & 0xfffe; /* Make sure its even */ - config.fragThresh = (u16)v; + ai->config.fragThresh = (u16)v; + ai->need_commit = 1; } else if (!strncmp(line, "Modulation: ", 12)) { line += 12; switch(*line) { - case 'd': config.modulation=MOD_DEFAULT; break; - case 'c': config.modulation=MOD_CCK; break; - case 'm': config.modulation=MOD_MOK; break; + case 'd': ai->config.modulation=MOD_DEFAULT; ai->need_commit=1; break; + case 'c': ai->config.modulation=MOD_CCK; ai->need_commit=1; break; + case 'm': ai->config.modulation=MOD_MOK; ai->need_commit=1; break; default: printk( KERN_WARNING "airo: Unknown modulation\n" ); } } else if (!strncmp(line, "Preamble: ", 10)) { line += 10; switch(*line) { - case 'a': config.preamble=PREAMBLE_AUTO; break; - case 'l': config.preamble=PREAMBLE_LONG; break; - case 's': config.preamble=PREAMBLE_SHORT; break; + case 'a': ai->config.preamble=PREAMBLE_AUTO; ai->need_commit=1; break; + case 'l': ai->config.preamble=PREAMBLE_LONG; ai->need_commit=1; break; + case 's': ai->config.preamble=PREAMBLE_SHORT; ai->need_commit=1; break; default: printk(KERN_WARNING "airo: Unknown preamble\n"); } } else { @@ -2611,8 +3129,7 @@ static void proc_config_on_close( struct inode *inode, struct file *file ) { while( line[0] && line[0] != '\n' ) line++; if ( line[0] ) line++; } - checkThrottle(&config); - ai->config = config; + disable_MAC(ai); if (need_reset) { APListRid APList_rid; SsidRid SSID_rid; @@ -2620,19 +3137,30 @@ static void proc_config_on_close( struct inode *inode, struct file *file ) { readAPListRid(ai, &APList_rid); readSsidRid(ai, &SSID_rid); reset_airo_card(dev); + disable_MAC(ai); writeSsidRid(ai, &SSID_rid); writeAPListRid(ai, &APList_rid); } - writeConfigRid(ai, &config); + writeConfigRid(ai); enable_MAC(ai, &rsp); + if (need_reset) + airo_set_promisc(ai); +} + +static char *get_rmode(u16 mode) { + switch(mode&0xff) { + case RXMODE_RFMON: return "rfmon"; + case RXMODE_RFMON_ANYBSS: return "yna (any) bss rfmon"; + case RXMODE_LANMON: return "lanmon"; + } + return "ESS"; } static int proc_config_open( struct inode *inode, struct file *file ) { struct proc_data *data; struct proc_dir_entry *dp = PDE(inode); struct net_device *dev = dp->data; - struct airo_info *ai = (struct airo_info*)dev->priv; - ConfigRid config; + struct airo_info *ai = dev->priv; int i; MOD_INC_USE_COUNT; @@ -2654,7 +3182,7 @@ static int proc_config_open( struct inode *inode, struct file *file ) { data->maxwritelen = 2048; data->on_close = proc_config_on_close; - readConfigRid(ai, &config); + readConfigRid(ai); i = sprintf( data->rbuffer, "Mode: %s\n" @@ -2664,25 +3192,25 @@ static int proc_config_open( struct inode *inode, struct file *file ) { "DataRates: %d %d %d %d %d %d %d %d\n" "Channel: %d\n" "XmitPower: %d\n", - config.opmode == 0 ? "adhoc" : - config.opmode == 1 ? "ESS" : - config.opmode == 2 ? "AP" : - config.opmode == 3 ? "AP RPTR" : "Error", + ai->config.opmode == 0 ? "adhoc" : + ai->config.opmode == 1 ? get_rmode(ai->config.rmode): + ai->config.opmode == 2 ? "AP" : + ai->config.opmode == 3 ? "AP RPTR" : "Error", ai->flags&FLAG_RADIO_OFF ? "off" : "on", - config.nodeName, - config.powerSaveMode == 0 ? "CAM" : - config.powerSaveMode == 1 ? "PSP" : - config.powerSaveMode == 2 ? "PSPCAM" : "Error", - (int)config.rates[0], - (int)config.rates[1], - (int)config.rates[2], - (int)config.rates[3], - (int)config.rates[4], - (int)config.rates[5], - (int)config.rates[6], - (int)config.rates[7], - (int)config.channelSet, - (int)config.txPower + ai->config.nodeName, + ai->config.powerSaveMode == 0 ? "CAM" : + ai->config.powerSaveMode == 1 ? "PSP" : + ai->config.powerSaveMode == 2 ? "PSPCAM" : "Error", + (int)ai->config.rates[0], + (int)ai->config.rates[1], + (int)ai->config.rates[2], + (int)ai->config.rates[3], + (int)ai->config.rates[4], + (int)ai->config.rates[5], + (int)ai->config.rates[6], + (int)ai->config.rates[7], + (int)ai->config.channelSet, + (int)ai->config.txPower ); sprintf( data->rbuffer + i, "LongRetryLimit: %d\n" @@ -2696,24 +3224,24 @@ static int proc_config_open( struct inode *inode, struct file *file ) { "WEP: %s\n" "Modulation: %s\n" "Preamble: %s\n", - (int)config.longRetryLimit, - (int)config.shortRetryLimit, - (int)config.rtsThres, - (int)config.txLifetime, - (int)config.rxLifetime, - config.txDiversity == 1 ? "left" : - config.txDiversity == 2 ? "right" : "both", - config.rxDiversity == 1 ? "left" : - config.rxDiversity == 2 ? "right" : "both", - (int)config.fragThresh, - config.authType == AUTH_ENCRYPT ? "encrypt" : - config.authType == AUTH_SHAREDKEY ? "shared" : "open", - config.modulation == 0 ? "default" : - config.modulation == MOD_CCK ? "cck" : - config.modulation == MOD_MOK ? "mok" : "error", - config.preamble == PREAMBLE_AUTO ? "auto" : - config.preamble == PREAMBLE_LONG ? "long" : - config.preamble == PREAMBLE_SHORT ? "short" : "error" + (int)ai->config.longRetryLimit, + (int)ai->config.shortRetryLimit, + (int)ai->config.rtsThres, + (int)ai->config.txLifetime, + (int)ai->config.rxLifetime, + ai->config.txDiversity == 1 ? "left" : + ai->config.txDiversity == 2 ? "right" : "both", + ai->config.rxDiversity == 1 ? "left" : + ai->config.rxDiversity == 2 ? "right" : "both", + (int)ai->config.fragThresh, + ai->config.authType == AUTH_ENCRYPT ? "encrypt" : + ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open", + ai->config.modulation == 0 ? "default" : + ai->config.modulation == MOD_CCK ? "cck" : + ai->config.modulation == MOD_MOK ? "mok" : "error", + ai->config.preamble == PREAMBLE_AUTO ? "auto" : + ai->config.preamble == PREAMBLE_LONG ? "long" : + ai->config.preamble == PREAMBLE_SHORT ? "short" : "error" ); data->readlen = strlen( data->rbuffer ); return 0; @@ -2723,8 +3251,9 @@ static void proc_SSID_on_close( struct inode *inode, struct file *file ) { struct proc_data *data = (struct proc_data *)file->private_data; struct proc_dir_entry *dp = PDE(inode); struct net_device *dev = dp->data; - struct airo_info *ai = (struct airo_info*)dev->priv; + struct airo_info *ai = dev->priv; SsidRid SSID_rid; + Resp rsp; int i; int offset = 0; @@ -2745,7 +3274,9 @@ static void proc_SSID_on_close( struct inode *inode, struct file *file ) { offset < data->writelen ) offset++; offset++; } + disable_MAC(ai); writeSsidRid(ai, &SSID_rid); + enable_MAC(ai, &rsp); } inline static u8 hexVal(char c) { @@ -2759,8 +3290,9 @@ static void proc_APList_on_close( struct inode *inode, struct file *file ) { struct proc_data *data = (struct proc_data *)file->private_data; struct proc_dir_entry *dp = PDE(inode); struct net_device *dev = dp->data; - struct airo_info *ai = (struct airo_info*)dev->priv; + struct airo_info *ai = dev->priv; APListRid APList_rid; + Resp rsp; int i; if ( !data->writelen ) return; @@ -2783,7 +3315,9 @@ static void proc_APList_on_close( struct inode *inode, struct file *file ) { } } } + disable_MAC(ai); writeAPListRid(ai, &APList_rid); + enable_MAC(ai, &rsp); } /* This function wraps PC4500_writerid with a MAC disable */ @@ -2852,7 +3386,7 @@ static void proc_wepkey_on_close( struct inode *inode, struct file *file ) { struct proc_data *data; struct proc_dir_entry *dp = PDE(inode); struct net_device *dev = dp->data; - struct airo_info *ai = (struct airo_info*)dev->priv; + struct airo_info *ai = dev->priv; int i; char key[16]; u16 index = 0; @@ -2893,7 +3427,7 @@ static int proc_wepkey_open( struct inode *inode, struct file *file ) { struct proc_data *data; struct proc_dir_entry *dp = PDE(inode); struct net_device *dev = dp->data; - struct airo_info *ai = (struct airo_info*)dev->priv; + struct airo_info *ai = dev->priv; char *ptr; WepKeyRid wkr; u16 lastindex; @@ -2945,7 +3479,7 @@ static int proc_SSID_open( struct inode *inode, struct file *file ) { struct proc_data *data; struct proc_dir_entry *dp = PDE(inode); struct net_device *dev = dp->data; - struct airo_info *ai = (struct airo_info*)dev->priv; + struct airo_info *ai = dev->priv; int i; char *ptr; SsidRid SSID_rid; @@ -2991,7 +3525,7 @@ static int proc_APList_open( struct inode *inode, struct file *file ) { struct proc_data *data; struct proc_dir_entry *dp = PDE(inode); struct net_device *dev = dp->data; - struct airo_info *ai = (struct airo_info*)dev->priv; + struct airo_info *ai = dev->priv; int i; char *ptr; APListRid APList_rid; @@ -3041,7 +3575,7 @@ static int proc_BSSList_open( struct inode *inode, struct file *file ) { struct proc_data *data; struct proc_dir_entry *dp = PDE(inode); struct net_device *dev = dp->data; - struct airo_info *ai = (struct airo_info*)dev->priv; + struct airo_info *ai = dev->priv; char *ptr; BSSListRid BSSList_rid; int rc; @@ -3070,7 +3604,10 @@ static int proc_BSSList_open( struct inode *inode, struct file *file ) { memset(&cmd, 0, sizeof(cmd)); cmd.cmd=CMD_LISTBSS; - lock_issuecommand(ai, &cmd, &rsp); + if (down_interruptible(&ai->sem)) + return -ERESTARTSYS; + issuecommand(ai, &cmd, &rsp); + up(&ai->sem); data->readlen = 0; return 0; } @@ -3128,39 +3665,46 @@ static struct net_device_list { changed. */ static void timer_func( u_long data ) { struct net_device *dev = (struct net_device*)data; - struct airo_info *apriv = (struct airo_info *)dev->priv; + struct airo_info *apriv = dev->priv; u16 linkstat = IN4500(apriv, LINKSTAT); + Resp rsp; if (linkstat != 0x400 ) { /* We don't have a link so try changing the authtype */ - ConfigRid config = apriv->config; + if (down_trylock(&apriv->sem) != 0) { + apriv->timer.expires = RUN_AT(1); + add_timer(&apriv->timer); + return; + } + __set_bit(FLAG_LOCKED, &apriv->flags); - switch(apriv->authtype) { + readConfigRid(apriv); + disable_MAC(apriv); + switch(apriv->config.authType) { case AUTH_ENCRYPT: /* So drop to OPEN */ - config.authType = AUTH_OPEN; - apriv->authtype = AUTH_OPEN; + apriv->config.authType = AUTH_OPEN; break; case AUTH_SHAREDKEY: if (apriv->keyindex < auto_wep) { set_wep_key(apriv, apriv->keyindex, 0, 0, 0); - config.authType = AUTH_SHAREDKEY; - apriv->authtype = AUTH_SHAREDKEY; + apriv->config.authType = AUTH_SHAREDKEY; apriv->keyindex++; } else { /* Drop to ENCRYPT */ apriv->keyindex = 0; set_wep_key(apriv, apriv->defindex, 0, 0, 0); - config.authType = AUTH_ENCRYPT; - apriv->authtype = AUTH_ENCRYPT; + apriv->config.authType = AUTH_ENCRYPT; } break; default: /* We'll escalate to SHAREDKEY */ - config.authType = AUTH_SHAREDKEY; - apriv->authtype = AUTH_SHAREDKEY; + apriv->config.authType = AUTH_SHAREDKEY; } - checkThrottle(&config); - writeConfigRid(apriv, &config); + apriv->need_commit = 1; + writeConfigRid(apriv); + enable_MAC(apriv, &rsp); + clear_bit(FLAG_LOCKED, &apriv->flags); + up(&apriv->sem); /* Schedule check to see if the change worked */ apriv->timer.expires = RUN_AT(HZ*3); @@ -3180,7 +3724,6 @@ static int add_airo_dev( struct net_device *dev ) { timer->function = timer_func; timer->data = (u_long)dev; init_timer(timer); - apriv->authtype = AUTH_SHAREDKEY; } node->dev = dev; @@ -3261,911 +3804,1725 @@ static void __exit airo_cleanup_module( void ) /* * Initial Wireless Extension code for Aironet driver by : * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 17 November 00 + * Conversion to new driver API by : + * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 26 March 02 + * Javier also did a good amount of work here, adding some new extensions + * and fixing my code. Let's just say that without him this code just + * would not work at all... - Jean II */ -#ifndef IW_ENCODE_NOKEY -#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ -#define IW_ENCODE_MODE (IW_ENCODE_DISABLED | IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN) -#endif /* IW_ENCODE_NOKEY */ -#endif /* WIRELESS_EXT */ +/*------------------------------------------------------------------*/ /* - * This defines the configuration part of the Wireless Extensions - * Note : irq and spinlock protection will occur in the subroutines - * - * TODO : - * o Check input value more carefully and fill correct values in range - * o Implement : POWER, SPY, APLIST - * o Optimise when adapter is closed (aggregate changes, commit later) - * o Test and shakeout the bugs (if any) - * - * Jean II - * - * Javier Achirica did a great job of merging code from the unnamed CISCO - * developer that added support for flashing the card. + * Wireless Handler : get protocol name */ -static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +static int airo_get_name(struct net_device *dev, + struct iw_request_info *info, + char *cwrq, + char *extra) { - int i, rc = 0; -#ifdef WIRELESS_EXT - struct airo_info *local = (struct airo_info*) dev->priv; - struct iwreq *wrq = (struct iwreq *) rq; - ConfigRid config; /* Configuration info */ - CapabilityRid cap_rid; /* Card capability info */ - StatusRid status_rid; /* Card status info */ + strcpy(cwrq, "IEEE 802.11-DS"); + return 0; +} -#ifdef CISCO_EXT - if (cmd != SIOCGIWPRIV && cmd != AIROIOCTL && cmd != AIROIDIFC -#ifdef AIROOLDIOCTL - && cmd != AIROOLDIOCTL && cmd != AIROOLDIDIFC -#endif - ) -#endif /* CISCO_EXT */ - { - /* If the command read some stuff, we better get it out of - * the card first... */ - if(IW_IS_GET(cmd)) - readStatusRid(local, &status_rid); - if(IW_IS_GET(cmd) || (cmd == SIOCSIWRATE) || (cmd == SIOCSIWENCODE)) - readCapabilityRid(local, &cap_rid); - /* Get config in all cases, because SET will just modify it */ - readConfigRid(local, &config); +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set frequency + */ +static int airo_set_freq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *fwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + int rc = -EINPROGRESS; /* Call commit handler */ + + /* If setting by frequency, convert to a channel */ + if((fwrq->e == 1) && + (fwrq->m >= (int) 2.412e8) && + (fwrq->m <= (int) 2.487e8)) { + int f = fwrq->m / 100000; + int c = 0; + while((c < 14) && (f != frequency_list[c])) + c++; + /* Hack to fall through... */ + fwrq->e = 0; + fwrq->m = c + 1; } -#endif /* WIRELESS_EXT */ + /* Setting by channel number */ + if((fwrq->m > 1000) || (fwrq->e > 0)) + rc = -EOPNOTSUPP; + else { + int channel = fwrq->m; + /* We should do a better check than that, + * based on the card capability !!! */ + if((channel < 1) || (channel > 16)) { + printk(KERN_DEBUG "%s: New channel value of %d is invalid!\n", dev->name, fwrq->m); + rc = -EINVAL; + } else { + /* Yes ! We can set it !!! */ + local->config.channelSet = (u16)(channel - 1); + local->need_commit = 1; + } + } + return rc; +} - switch (cmd) { -#ifdef WIRELESS_EXT - // Get name - case SIOCGIWNAME: - strcpy(wrq->u.name, "IEEE 802.11-DS"); - break; +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get frequency + */ +static int airo_get_freq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *fwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + StatusRid status_rid; /* Card status info */ - // Set frequency/channel - case SIOCSIWFREQ: - /* If setting by frequency, convert to a channel */ - if((wrq->u.freq.e == 1) && - (wrq->u.freq.m >= (int) 2.412e8) && - (wrq->u.freq.m <= (int) 2.487e8)) { - int f = wrq->u.freq.m / 100000; - int c = 0; - while((c < 14) && (f != frequency_list[c])) - c++; - /* Hack to fall through... */ - wrq->u.freq.e = 0; - wrq->u.freq.m = c + 1; - } - /* Setting by channel number */ - if((wrq->u.freq.m > 1000) || (wrq->u.freq.e > 0)) - rc = -EOPNOTSUPP; - else { - int channel = wrq->u.freq.m; - /* We should do a better check than that, - * based on the card capability !!! */ - if((channel < 1) || (channel > 16)) { - printk(KERN_DEBUG "%s: New channel value of %d is invalid!\n", dev->name, wrq->u.freq.m); - rc = -EINVAL; - } else { - /* Yes ! We can set it !!! */ - config.channelSet = (u16)(channel - 1); - local->need_commit = 1; - } - } - break; + readStatusRid(local, &status_rid); - // Get frequency/channel - case SIOCGIWFREQ: + /* Will return zero in infrastructure mode */ #ifdef WEXT_USECHANNELS - wrq->u.freq.m = ((int)status_rid.channel) + 1; - wrq->u.freq.e = 0; + fwrq->m = ((int)status_rid.channel) + 1; + fwrq->e = 0; #else - { - int f = (int)status_rid.channel; - wrq->u.freq.m = frequency_list[f] * 100000; - wrq->u.freq.e = 1; - } + { + int f = (int)status_rid.channel; + fwrq->m = frequency_list[f] * 100000; + fwrq->e = 1; + } #endif - break; - // Set desired network name (ESSID) - case SIOCSIWESSID: - if (wrq->u.data.pointer) { - char essid[IW_ESSID_MAX_SIZE + 1]; - SsidRid SSID_rid; /* SSIDs */ + return 0; +} - /* Reload the list of current SSID */ - readSsidRid(local, &SSID_rid); +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set ESSID + */ +static int airo_set_essid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + Resp rsp; + SsidRid SSID_rid; /* SSIDs */ - /* Check if we asked for `any' */ - if(wrq->u.data.flags == 0) { - /* Just send an empty SSID list */ - memset(&SSID_rid, 0, sizeof(SSID_rid)); - } else { - int index = (wrq->u.data.flags & - IW_ENCODE_INDEX) - 1; + /* Reload the list of current SSID */ + readSsidRid(local, &SSID_rid); - /* Check the size of the string */ - if(wrq->u.data.length > IW_ESSID_MAX_SIZE+1) { - rc = -E2BIG; - break; - } - /* Check if index is valid */ - if((index < 0) || (index >= 4)) { - rc = -EINVAL; - break; - } + /* Check if we asked for `any' */ + if(dwrq->flags == 0) { + /* Just send an empty SSID list */ + memset(&SSID_rid, 0, sizeof(SSID_rid)); + } else { + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - /* Set the SSID */ - memset(essid, 0, sizeof(essid)); - if (copy_from_user(essid, - wrq->u.data.pointer, - wrq->u.data.length)) { - rc = -EFAULT; - break; - } - memcpy(SSID_rid.ssids[index].ssid, essid, - sizeof(essid) - 1); - SSID_rid.ssids[index].len = wrq->u.data.length - 1; - } - /* Write it to the card */ - writeSsidRid(local, &SSID_rid); + /* Check the size of the string */ + if(dwrq->length > IW_ESSID_MAX_SIZE+1) { + return -E2BIG ; + } + /* Check if index is valid */ + if((index < 0) || (index >= 4)) { + return -EINVAL; } - break; - // Get current network name (ESSID) - case SIOCGIWESSID: - if (wrq->u.data.pointer) { - char essid[IW_ESSID_MAX_SIZE + 1]; + /* Set the SSID */ + memset(SSID_rid.ssids[index].ssid, 0, + sizeof(SSID_rid.ssids[index].ssid)); + memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length); + SSID_rid.ssids[index].len = dwrq->length - 1; + } + /* Write it to the card */ + disable_MAC(local); + writeSsidRid(local, &SSID_rid); + enable_MAC(local, &rsp); - /* Note : if wrq->u.data.flags != 0, we should - * get the relevant SSID from the SSID list... */ + return 0; +} - /* Get the current SSID */ - memcpy(essid, status_rid.SSID, status_rid.SSIDlen); - essid[status_rid.SSIDlen] = '\0'; - /* If none, we may want to get the one that was set */ +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get ESSID + */ +static int airo_get_essid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + StatusRid status_rid; /* Card status info */ - /* Push it out ! */ - wrq->u.data.length = strlen(essid) + 1; - wrq->u.data.flags = 1; /* active */ - if (copy_to_user(wrq->u.data.pointer, essid, sizeof(essid))) - rc = -EFAULT; - } - break; + readStatusRid(local, &status_rid); - case SIOCSIWAP: - if (wrq->u.ap_addr.sa_family != ARPHRD_ETHER) - rc = -EINVAL; - else { - APListRid APList_rid; + /* Note : if dwrq->flags != 0, we should + * get the relevant SSID from the SSID list... */ - memset(&APList_rid, 0, sizeof(APList_rid)); - APList_rid.len = sizeof(APList_rid); - memcpy(APList_rid.ap[0], wrq->u.ap_addr.sa_data, 6); - writeAPListRid(local, &APList_rid); - local->need_commit = 1; - } - break; + /* Get the current SSID */ + memcpy(extra, status_rid.SSID, status_rid.SSIDlen); + extra[status_rid.SSIDlen] = '\0'; + /* If none, we may want to get the one that was set */ - // Get current Access Point (BSSID) - case SIOCGIWAP: - /* Tentative. This seems to work, wow, I'm lucky !!! */ - memcpy(wrq->u.ap_addr.sa_data, status_rid.bssid[0], 6); - wrq->u.ap_addr.sa_family = ARPHRD_ETHER; - break; + /* Push it out ! */ + dwrq->length = status_rid.SSIDlen + 1; + dwrq->flags = 1; /* active */ - // Set desired station name - case SIOCSIWNICKN: - if (wrq->u.data.pointer) { - char name[16 + 1]; + return 0; +} - /* Check the size of the string */ - if(wrq->u.data.length > 16 + 1) { - rc = -E2BIG; - break; - } - memset(name, 0, sizeof(name)); - if (copy_from_user(name, wrq->u.data.pointer, - wrq->u.data.length)) { - rc = -EFAULT; - break; - } - memcpy(config.nodeName, name, 16); - local->need_commit = 1; - } - break; +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set AP address + */ +static int airo_set_wap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra) +{ + struct airo_info *local = dev->priv; + Cmd cmd; + Resp rsp; + APListRid APList_rid; + static const unsigned char bcast[6] = { 255, 255, 255, 255, 255, 255 }; - // Get current station name - case SIOCGIWNICKN: - if (wrq->u.data.pointer) { - char name[IW_ESSID_MAX_SIZE + 1]; + if (awrq->sa_family != ARPHRD_ETHER) + return -EINVAL; + else if (!memcmp(bcast, awrq->sa_data, 6)) { + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd=CMD_LOSE_SYNC; + if (down_interruptible(&local->sem)) + return -ERESTARTSYS; + issuecommand(local, &cmd, &rsp); + up(&local->sem); + } else { + memset(&APList_rid, 0, sizeof(APList_rid)); + APList_rid.len = sizeof(APList_rid); + memcpy(APList_rid.ap[0], awrq->sa_data, 6); + disable_MAC(local); + writeAPListRid(local, &APList_rid); + enable_MAC(local, &rsp); + } + return 0; +} - strncpy(name, config.nodeName, 16); - name[16] = '\0'; - wrq->u.data.length = strlen(name) + 1; - if (copy_to_user(wrq->u.data.pointer, name, sizeof(name))) - rc = -EFAULT; - } - break; +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get AP address + */ +static int airo_get_wap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra) +{ + struct airo_info *local = dev->priv; + StatusRid status_rid; /* Card status info */ - // Set the desired bit-rate - case SIOCSIWRATE: - { - /* First : get a valid bit rate value */ - u8 brate = 0; - int i; - - /* Which type of value ? */ - if((wrq->u.bitrate.value < 8) && - (wrq->u.bitrate.value >= 0)) { - /* Setting by rate index */ - /* Find value in the magic rate table */ - brate = cap_rid.supportedRates[wrq->u.bitrate.value]; - } else { - /* Setting by frequency value */ - u8 normvalue = (u8) (wrq->u.bitrate.value/500000); + readStatusRid(local, &status_rid); - /* Check if rate is valid */ - for(i = 0 ; i < 8 ; i++) { - if(normvalue == cap_rid.supportedRates[i]) { - brate = normvalue; - break; - } - } - } - /* -1 designed the max rate (mostly auto mode) */ - if(wrq->u.bitrate.value == -1) { - /* Get the highest available rate */ - for(i = 0 ; i < 8 ; i++) { - if(cap_rid.supportedRates[i] == 0) - break; + /* Tentative. This seems to work, wow, I'm lucky !!! */ + memcpy(awrq->sa_data, status_rid.bssid[0], 6); + awrq->sa_family = ARPHRD_ETHER; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Nickname + */ +static int airo_set_nick(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + + /* Check the size of the string */ + if(dwrq->length > 16 + 1) { + return -E2BIG; + } + memset(local->config.nodeName, 0, sizeof(local->config.nodeName)); + memcpy(local->config.nodeName, extra, dwrq->length); + local->need_commit = 1; + + return -EINPROGRESS; /* Call commit handler */ +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Nickname + */ +static int airo_get_nick(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + + strncpy(extra, local->config.nodeName, 16); + extra[16] = '\0'; + dwrq->length = strlen(extra) + 1; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Bit-Rate + */ +static int airo_set_rate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + CapabilityRid cap_rid; /* Card capability info */ + u8 brate = 0; + int i; + + /* First : get a valid bit rate value */ + readCapabilityRid(local, &cap_rid); + + /* Which type of value ? */ + if((vwrq->value < 8) && (vwrq->value >= 0)) { + /* Setting by rate index */ + /* Find value in the magic rate table */ + brate = cap_rid.supportedRates[vwrq->value]; + } else { + /* Setting by frequency value */ + u8 normvalue = (u8) (vwrq->value/500000); + + /* Check if rate is valid */ + for(i = 0 ; i < 8 ; i++) { + if(normvalue == cap_rid.supportedRates[i]) { + brate = normvalue; + break; } - if(i != 0) - brate = cap_rid.supportedRates[i - 1]; } - /* Check that it is valid */ - if(brate == 0) { - rc = -EINVAL; - break; + } + /* -1 designed the max rate (mostly auto mode) */ + if(vwrq->value == -1) { + /* Get the highest available rate */ + for(i = 0 ; i < 8 ; i++) { + if(cap_rid.supportedRates[i] == 0) + break; } + if(i != 0) + brate = cap_rid.supportedRates[i - 1]; + } + /* Check that it is valid */ + if(brate == 0) { + return -EINVAL; + } - /* Now, check if we want a fixed or auto value */ - if(wrq->u.bitrate.fixed == 0) { - /* Fill all the rates up to this max rate */ - memset(config.rates, 0, 8); - for(i = 0 ; i < 8 ; i++) { - config.rates[i] = cap_rid.supportedRates[i]; - if(config.rates[i] == brate) - break; - } - local->need_commit = 1; - } else { - /* Fixed mode */ - /* One rate, fixed */ - memset(config.rates, 0, 8); - config.rates[0] = brate; - local->need_commit = 1; + /* Now, check if we want a fixed or auto value */ + if(vwrq->fixed == 0) { + /* Fill all the rates up to this max rate */ + memset(local->config.rates, 0, 8); + for(i = 0 ; i < 8 ; i++) { + local->config.rates[i] = cap_rid.supportedRates[i]; + if(local->config.rates[i] == brate) + break; } - break; + } else { + /* Fixed mode */ + /* One rate, fixed */ + memset(local->config.rates, 0, 8); + local->config.rates[0] = brate; } + local->need_commit = 1; - // Get the current bit-rate - case SIOCGIWRATE: - { - int brate = status_rid.currentXmitRate; - wrq->u.bitrate.value = brate * 500000; - /* If more than one rate, set auto */ - wrq->u.rts.fixed = (config.rates[1] == 0); - } - break; + return -EINPROGRESS; /* Call commit handler */ +} - // Set the desired RTS threshold - case SIOCSIWRTS: - { - int rthr = wrq->u.rts.value; - if(wrq->u.rts.disabled) - rthr = 2312; - if((rthr < 0) || (rthr > 2312)) { - rc = -EINVAL; - } else { - config.rtsThres = rthr; - local->need_commit = 1; - } +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Bit-Rate + */ +static int airo_get_rate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + StatusRid status_rid; /* Card status info */ + + readStatusRid(local, &status_rid); + + vwrq->value = status_rid.currentXmitRate * 500000; + /* If more than one rate, set auto */ + vwrq->fixed = (local->config.rates[1] == 0); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set RTS threshold + */ +static int airo_set_rts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + int rthr = vwrq->value; + + if(vwrq->disabled) + rthr = 2312; + if((rthr < 0) || (rthr > 2312)) { + return -EINVAL; } - break; + local->config.rtsThres = rthr; + local->need_commit = 1; - // Get the current RTS threshold - case SIOCGIWRTS: - wrq->u.rts.value = config.rtsThres; - wrq->u.rts.disabled = (wrq->u.rts.value >= 2312); - wrq->u.rts.fixed = 1; - break; + return -EINPROGRESS; /* Call commit handler */ +} - // Set the desired fragmentation threshold - case SIOCSIWFRAG: - { - int fthr = wrq->u.frag.value; - if(wrq->u.frag.disabled) - fthr = 2312; - if((fthr < 256) || (fthr > 2312)) { - rc = -EINVAL; - } else { - fthr &= ~0x1; /* Get an even value */ - config.fragThresh = (u16)fthr; - local->need_commit = 1; - } +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get RTS threshold + */ +static int airo_get_rts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + + vwrq->value = local->config.rtsThres; + vwrq->disabled = (vwrq->value >= 2312); + vwrq->fixed = 1; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Fragmentation threshold + */ +static int airo_set_frag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + int fthr = vwrq->value; + + if(vwrq->disabled) + fthr = 2312; + if((fthr < 256) || (fthr > 2312)) { + return -EINVAL; } - break; + fthr &= ~0x1; /* Get an even value - is it really needed ??? */ + local->config.fragThresh = (u16)fthr; + local->need_commit = 1; - // Get the current fragmentation threshold - case SIOCGIWFRAG: - wrq->u.frag.value = config.fragThresh; - wrq->u.frag.disabled = (wrq->u.frag.value >= 2312); - wrq->u.frag.fixed = 1; - break; + return -EINPROGRESS; /* Call commit handler */ +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Fragmentation threshold + */ +static int airo_get_frag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + + vwrq->value = local->config.fragThresh; + vwrq->disabled = (vwrq->value >= 2312); + vwrq->fixed = 1; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Mode of Operation + */ +static int airo_set_mode(struct net_device *dev, + struct iw_request_info *info, + __u32 *uwrq, + char *extra) +{ + struct airo_info *local = dev->priv; - // Set mode of operation - case SIOCSIWMODE: - switch(wrq->u.mode) { + switch(*uwrq) { case IW_MODE_ADHOC: - config.opmode = MODE_STA_IBSS; - local->need_commit = 1; + local->config.opmode = MODE_STA_IBSS; break; case IW_MODE_INFRA: - config.opmode = MODE_STA_ESS; - local->need_commit = 1; + local->config.opmode = MODE_STA_ESS; break; case IW_MODE_MASTER: - config.opmode = MODE_AP; - local->need_commit = 1; + local->config.opmode = MODE_AP; break; case IW_MODE_REPEAT: - config.opmode = MODE_AP_RPTR; - local->need_commit = 1; + local->config.opmode = MODE_AP_RPTR; break; default: - rc = -EINVAL; - } - break; + return -EINVAL; + } + local->need_commit = 1; + + return -EINPROGRESS; /* Call commit handler */ +} - // Get mode of operation - case SIOCGIWMODE: - /* If not managed, assume it's ad-hoc */ - switch (config.opmode & 0xFF) { +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Mode of Operation + */ +static int airo_get_mode(struct net_device *dev, + struct iw_request_info *info, + __u32 *uwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + + /* If not managed, assume it's ad-hoc */ + switch (local->config.opmode & 0xFF) { case MODE_STA_ESS: - wrq->u.mode = IW_MODE_INFRA; + *uwrq = IW_MODE_INFRA; break; case MODE_AP: - wrq->u.mode = IW_MODE_MASTER; + *uwrq = IW_MODE_MASTER; break; case MODE_AP_RPTR: - wrq->u.mode = IW_MODE_REPEAT; + *uwrq = IW_MODE_REPEAT; break; default: - wrq->u.mode = IW_MODE_ADHOC; - } - break; + *uwrq = IW_MODE_ADHOC; + } - // Set WEP keys and mode - case SIOCSIWENCODE: - /* Is WEP supported ? */ - /* Older firmware doesn't support this... - if(!(cap_rid.softCap & 2)) { - rc = -EOPNOTSUPP; - break; - } */ - /* Basic checking: do we have a key to set ? */ - if (wrq->u.encoding.pointer != (caddr_t) 0) { - wep_key_t key; - int index = (wrq->u.encoding.flags & IW_ENCODE_INDEX) - 1; - int current_index = get_wep_key(local, 0xffff); - /* Check the size of the key */ - if (wrq->u.encoding.length > MAX_KEY_SIZE) { - rc = -EINVAL; - break; - } - /* Check the index (none -> use current) */ - if ((index < 0) || (index>=(cap_rid.softCap&0x80)?4:1)) - index = current_index; - /* Set the length */ - if (wrq->u.encoding.length > MIN_KEY_SIZE) - key.len = MAX_KEY_SIZE; + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Encryption Key + */ +static int airo_set_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + CapabilityRid cap_rid; /* Card capability info */ + + /* Is WEP supported ? */ + readCapabilityRid(local, &cap_rid); + /* Older firmware doesn't support this... + if(!(cap_rid.softCap & 2)) { + return -EOPNOTSUPP; + } */ + + /* Basic checking: do we have a key to set ? + * Note : with the new API, it's impossible to get a NULL pointer. + * Therefore, we need to check a key size == 0 instead. + * New version of iwconfig properly set the IW_ENCODE_NOKEY flag + * when no key is present (only change flags), but older versions + * don't do it. - Jean II */ + if (dwrq->length > 0) { + wep_key_t key; + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + int current_index = get_wep_key(local, 0xffff); + /* Check the size of the key */ + if (dwrq->length > MAX_KEY_SIZE) { + return -EINVAL; + } + /* Check the index (none -> use current) */ + if ((index < 0) || (index>=(cap_rid.softCap&0x80)?4:1)) + index = current_index; + /* Set the length */ + if (dwrq->length > MIN_KEY_SIZE) + key.len = MAX_KEY_SIZE; + else + if (dwrq->length > 0) + key.len = MIN_KEY_SIZE; else - if (wrq->u.encoding.length > 0) - key.len = MIN_KEY_SIZE; - else - /* Disable the key */ - key.len = 0; - /* Check if the key is not marked as invalid */ - if(!(wrq->u.encoding.flags & IW_ENCODE_NOKEY)) { - /* Cleanup */ - memset(key.key, 0, MAX_KEY_SIZE); - /* Copy the key in the driver */ - if(copy_from_user(key.key, - wrq->u.encoding.pointer, - wrq->u.encoding.length)) { - key.len = 0; - rc = -EFAULT; - break; - } - /* Send the key to the card */ - set_wep_key(local, index, key.key, - key.len, 1); - } - /* WE specify that if a valid key is set, encryption - * should be enabled (user may turn it off later) - * This is also how "iwconfig ethX key on" works */ - if((index == current_index) && (key.len > 0) && - (config.authType == AUTH_OPEN)) { - config.authType = AUTH_ENCRYPT; - local->need_commit = 1; - } - } else { - /* Do we want to just set the transmit key index ? */ - int index = (wrq->u.encoding.flags & IW_ENCODE_INDEX) - 1; - if ((index>=0) && (index<(cap_rid.softCap&0x80)?4:1)) { - set_wep_key(local, index, 0, 0, 1); - } else - /* Don't complain if only change the mode */ - if(!wrq->u.encoding.flags & IW_ENCODE_MODE) { - rc = -EINVAL; - break; - } + /* Disable the key */ + key.len = 0; + /* Check if the key is not marked as invalid */ + if(!(dwrq->flags & IW_ENCODE_NOKEY)) { + /* Cleanup */ + memset(key.key, 0, MAX_KEY_SIZE); + /* Copy the key in the driver */ + memcpy(key.key, extra, dwrq->length); + /* Send the key to the card */ + set_wep_key(local, index, key.key, key.len, 1); } - /* Read the flags */ - if(wrq->u.encoding.flags & IW_ENCODE_DISABLED) - config.authType = AUTH_OPEN; // disable encryption - if(wrq->u.encoding.flags & IW_ENCODE_RESTRICTED) - config.authType = AUTH_SHAREDKEY; // Only Both - if(wrq->u.encoding.flags & IW_ENCODE_OPEN) - config.authType = AUTH_ENCRYPT; // Only Wep - /* Commit the changes if needed */ - if(wrq->u.encoding.flags & IW_ENCODE_MODE) + /* WE specify that if a valid key is set, encryption + * should be enabled (user may turn it off later) + * This is also how "iwconfig ethX key on" works */ + if((index == current_index) && (key.len > 0) && + (local->config.authType == AUTH_OPEN)) { + local->config.authType = AUTH_ENCRYPT; local->need_commit = 1; - break; + } + } else { + /* Do we want to just set the transmit key index ? */ + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if ((index>=0) && (index<(cap_rid.softCap&0x80)?4:1)) { + set_wep_key(local, index, 0, 0, 1); + } else + /* Don't complain if only change the mode */ + if(!dwrq->flags & IW_ENCODE_MODE) { + return -EINVAL; + } + } + /* Read the flags */ + if(dwrq->flags & IW_ENCODE_DISABLED) + local->config.authType = AUTH_OPEN; // disable encryption + if(dwrq->flags & IW_ENCODE_RESTRICTED) + local->config.authType = AUTH_SHAREDKEY; // Only Both + if(dwrq->flags & IW_ENCODE_OPEN) + local->config.authType = AUTH_ENCRYPT; // Only Wep + /* Commit the changes to flags if needed */ + if(dwrq->flags & IW_ENCODE_MODE) + local->need_commit = 1; + return -EINPROGRESS; /* Call commit handler */ +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Encryption Key + */ +static int airo_get_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + CapabilityRid cap_rid; /* Card capability info */ - // Get the WEP keys and mode - case SIOCGIWENCODE: - /* Is it supported ? */ - if(!(cap_rid.softCap & 2)) { - rc = -EOPNOTSUPP; + /* Is it supported ? */ + readCapabilityRid(local, &cap_rid); + if(!(cap_rid.softCap & 2)) { + return -EOPNOTSUPP; + } + /* Check encryption mode */ + switch(local->config.authType) { + case AUTH_ENCRYPT: + dwrq->flags = IW_ENCODE_OPEN; break; - } - // Only super-user can see WEP key - if (!capable(CAP_NET_ADMIN)) { - rc = -EPERM; + case AUTH_SHAREDKEY: + dwrq->flags = IW_ENCODE_RESTRICTED; break; - } - - // Basic checking... - if (wrq->u.encoding.pointer != (caddr_t) 0) { - char zeros[16]; - int index = (wrq->u.encoding.flags & IW_ENCODE_INDEX) - 1; - - memset(zeros,0, sizeof(zeros)); - /* Check encryption mode */ - wrq->u.encoding.flags = IW_ENCODE_NOKEY; - /* Is WEP enabled ??? */ - switch(config.authType) { - case AUTH_ENCRYPT: - wrq->u.encoding.flags |= IW_ENCODE_OPEN; - break; - case AUTH_SHAREDKEY: - wrq->u.encoding.flags |= IW_ENCODE_RESTRICTED; - break; - default: - case AUTH_OPEN: - wrq->u.encoding.flags |= IW_ENCODE_DISABLED; - break; - } + default: + case AUTH_OPEN: + dwrq->flags = IW_ENCODE_DISABLED; + break; + } + /* We can't return the key, so set the proper flag and return zero */ + dwrq->flags |= IW_ENCODE_NOKEY; + memset(extra, 0, 16); + + /* Which key do we want ? -1 -> tx index */ + if((index < 0) || (index >= (cap_rid.softCap & 0x80) ? 4 : 1)) + index = get_wep_key(local, 0xffff); + dwrq->flags |= index + 1; + /* Copy the key to the user buffer */ + dwrq->length = get_wep_key(local, index); + if (dwrq->length > 16) { + dwrq->length=0; + } + return 0; +} - /* Which key do we want ? -1 -> tx index */ - if((index < 0) || (index >= (cap_rid.softCap&0x80)?4:1)) - index = get_wep_key(local, 0xffff); - wrq->u.encoding.flags |= index + 1; - /* Copy the key to the user buffer */ - wrq->u.encoding.length = get_wep_key(local, index); - if (wrq->u.encoding.length > 16) { - wrq->u.encoding.length=0; - } +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Tx-Power + */ +static int airo_set_txpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + CapabilityRid cap_rid; /* Card capability info */ + int i; + int rc = -EINVAL; - if(copy_to_user(wrq->u.encoding.pointer, zeros, - wrq->u.encoding.length)) - rc = -EFAULT; - } - break; + readCapabilityRid(local, &cap_rid); -#if WIRELESS_EXT > 9 - // Get the current Tx-Power - case SIOCGIWTXPOW: - wrq->u.txpower.value = config.txPower; - wrq->u.txpower.fixed = 1; /* No power control */ - wrq->u.txpower.disabled = (local->flags & FLAG_RADIO_OFF); - wrq->u.txpower.flags = IW_TXPOW_MWATT; - break; - case SIOCSIWTXPOW: - if (wrq->u.txpower.disabled) { - local->flags |= FLAG_RADIO_OFF; + if (vwrq->disabled) { + local->flags |= FLAG_RADIO_OFF; + local->need_commit = 1; + return -EINPROGRESS; /* Call commit handler */ + } + if (vwrq->flags != IW_TXPOW_MWATT) { + return -EINVAL; + } + local->flags &= ~FLAG_RADIO_OFF; + for (i = 0; cap_rid.txPowerLevels[i] && (i < 8); i++) + if ((vwrq->value==cap_rid.txPowerLevels[i])) { + local->config.txPower = vwrq->value; local->need_commit = 1; + rc = -EINPROGRESS; /* Call commit handler */ break; } - if (wrq->u.txpower.flags != IW_TXPOW_MWATT) { - rc = -EINVAL; - break; + return rc; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Tx-Power + */ +static int airo_get_txpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + + vwrq->value = local->config.txPower; + vwrq->fixed = 1; /* No power control */ + vwrq->disabled = (local->flags & FLAG_RADIO_OFF); + vwrq->flags = IW_TXPOW_MWATT; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Retry limits + */ +static int airo_set_retry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + int rc = -EINVAL; + + if(vwrq->disabled) { + return -EINVAL; + } + if(vwrq->flags & IW_RETRY_LIMIT) { + if(vwrq->flags & IW_RETRY_MAX) + local->config.longRetryLimit = vwrq->value; + else if (vwrq->flags & IW_RETRY_MIN) + local->config.shortRetryLimit = vwrq->value; + else { + /* No modifier : set both */ + local->config.longRetryLimit = vwrq->value; + local->config.shortRetryLimit = vwrq->value; } - local->flags &= ~FLAG_RADIO_OFF; - rc = -EINVAL; - for (i = 0; cap_rid.txPowerLevels[i] && (i < 8); i++) - if ((wrq->u.txpower.value==cap_rid.txPowerLevels[i])) { - config.txPower = wrq->u.txpower.value; - local->need_commit = 1; - rc = 0; - break; - } - break; -#endif /* WIRELESS_EXT > 9 */ + local->need_commit = 1; + rc = -EINPROGRESS; /* Call commit handler */ + } + if(vwrq->flags & IW_RETRY_LIFETIME) { + local->config.txLifetime = vwrq->value / 1024; + local->need_commit = 1; + rc = -EINPROGRESS; /* Call commit handler */ + } + return rc; +} -#if WIRELESS_EXT > 10 - case SIOCSIWRETRY: - if(wrq->u.retry.disabled) { - rc = -EINVAL; +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Retry limits + */ +static int airo_get_retry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + + vwrq->disabled = 0; /* Can't be disabled */ + + /* Note : by default, display the min retry number */ + if((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { + vwrq->flags = IW_RETRY_LIFETIME; + vwrq->value = (int)local->config.txLifetime * 1024; + } else if((vwrq->flags & IW_RETRY_MAX)) { + vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + vwrq->value = (int)local->config.longRetryLimit; + } else { + vwrq->flags = IW_RETRY_LIMIT; + vwrq->value = (int)local->config.shortRetryLimit; + if((int)local->config.shortRetryLimit != (int)local->config.longRetryLimit) + vwrq->flags |= IW_RETRY_MIN; + } + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get range info + */ +static int airo_get_range(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + struct iw_range *range = (struct iw_range *) extra; + CapabilityRid cap_rid; /* Card capability info */ + int i; + int k; + + readCapabilityRid(local, &cap_rid); + + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(range)); + range->min_nwid = 0x0000; + range->max_nwid = 0x0000; + range->num_channels = 14; + /* Should be based on cap_rid.country to give only + * what the current card support */ + k = 0; + for(i = 0; i < 14; i++) { + range->freq[k].i = i + 1; /* List index */ + range->freq[k].m = frequency_list[i] * 100000; + range->freq[k++].e = 1; /* Values in table in MHz -> * 10^5 * 10 */ + } + range->num_frequency = k; + + /* Hum... Should put the right values there */ + range->max_qual.qual = 10; + range->max_qual.level = 0x100 - 120; /* -120 dBm */ + range->max_qual.noise = 0; + range->sensitivity = 65535; + + for(i = 0 ; i < 8 ; i++) { + range->bitrate[i] = cap_rid.supportedRates[i] * 500000; + if(range->bitrate[i] == 0) break; + } + range->num_bitrates = i; + + /* Set an indication of the max TCP throughput + * in bit/s that we can expect using this interface. + * May be use for QoS stuff... Jean II */ + if(i > 2) + range->throughput = 5000 * 1000; + else + range->throughput = 1500 * 1000; + + range->min_rts = 0; + range->max_rts = 2312; + range->min_frag = 256; + range->max_frag = 2312; + + if(cap_rid.softCap & 2) { + // WEP: RC4 40 bits + range->encoding_size[0] = 5; + // RC4 ~128 bits + if (cap_rid.softCap & 0x100) { + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + } else + range->num_encoding_sizes = 1; + range->max_encoding_tokens = (cap_rid.softCap & 0x80) ? 4 : 1; + } else { + range->num_encoding_sizes = 0; + range->max_encoding_tokens = 0; + } + range->min_pmp = 0; + range->max_pmp = 5000000; /* 5 secs */ + range->min_pmt = 0; + range->max_pmt = 65535 * 1024; /* ??? */ + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; + + /* Transmit Power - values are in mW */ + for(i = 0 ; i < 8 ; i++) { + range->txpower[i] = cap_rid.txPowerLevels[i]; + if(range->txpower[i] == 0) + break; + } + range->num_txpower = i; + range->txpower_capa = IW_TXPOW_MWATT; + range->we_version_source = 12; + range->we_version_compiled = WIRELESS_EXT; + range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; + range->retry_flags = IW_RETRY_LIMIT; + range->r_time_flags = IW_RETRY_LIFETIME; + range->min_retry = 1; + range->max_retry = 65535; + range->min_r_time = 1024; + range->max_r_time = 65535 * 1024; + /* Experimental measurements - boundary 11/5.5 Mb/s */ + /* Note : with or without the (local->rssi), results + * are somewhat different. - Jean II */ + range->avg_qual.qual = 6; + if (local->rssi) + range->avg_qual.level = 186; /* -70 dBm */ + else + range->avg_qual.level = 176; /* -80 dBm */ + range->avg_qual.noise = 0; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Power Management + */ +static int airo_set_power(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + + if (vwrq->disabled) { + if ((local->config.rmode & 0xFF) >= RXMODE_RFMON) { + return -EINVAL; } - local->need_commit = 0; - if(wrq->u.retry.flags & IW_RETRY_LIMIT) { - if(wrq->u.retry.flags & IW_RETRY_MAX) - config.longRetryLimit = wrq->u.retry.value; - else if (wrq->u.retry.flags & IW_RETRY_MIN) - config.shortRetryLimit = wrq->u.retry.value; - else { - /* No modifier : set both */ - config.longRetryLimit = wrq->u.retry.value; - config.shortRetryLimit = wrq->u.retry.value; + local->config.powerSaveMode = POWERSAVE_CAM; + local->config.rmode &= 0xFF00; + local->config.rmode |= RXMODE_BC_MC_ADDR; + local->need_commit = 1; + return -EINPROGRESS; /* Call commit handler */ + } + if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + local->config.fastListenDelay = (vwrq->value + 500) / 1024; + local->config.powerSaveMode = POWERSAVE_PSPCAM; + local->need_commit = 1; + } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { + local->config.fastListenInterval = local->config.listenInterval = (vwrq->value + 500) / 1024; + local->config.powerSaveMode = POWERSAVE_PSPCAM; + local->need_commit = 1; + } + switch (vwrq->flags & IW_POWER_MODE) { + case IW_POWER_UNICAST_R: + if ((local->config.rmode & 0xFF) >= RXMODE_RFMON) { + return -EINVAL; } + local->config.rmode &= 0xFF00; + local->config.rmode |= RXMODE_ADDR; local->need_commit = 1; - } - if(wrq->u.retry.flags & IW_RETRY_LIFETIME) { - config.txLifetime = wrq->u.retry.value / 1024; + break; + case IW_POWER_ALL_R: + if ((local->config.rmode & 0xFF) >= RXMODE_RFMON) { + return -EINVAL; + } + local->config.rmode &= 0xFF00; + local->config.rmode |= RXMODE_BC_MC_ADDR; local->need_commit = 1; - } - if(local->need_commit == 0) { - rc = -EINVAL; - } - break; + case IW_POWER_ON: + break; + default: + return -EINVAL; + } + // Note : we may want to factor local->need_commit here + // Note2 : may also want to factor RXMODE_RFMON test + return -EINPROGRESS; /* Call commit handler */ +} - case SIOCGIWRETRY: - wrq->u.retry.disabled = 0; /* Can't be disabled */ - - /* Note : by default, display the min retry number */ - if((wrq->u.retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { - wrq->u.retry.flags = IW_RETRY_LIFETIME; - wrq->u.retry.value = (int)config.txLifetime * 1024; - } else if((wrq->u.retry.flags & IW_RETRY_MAX)) { - wrq->u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX; - wrq->u.retry.value = (int)config.longRetryLimit; - } else { - wrq->u.retry.flags = IW_RETRY_LIMIT; - wrq->u.retry.value = (int)config.shortRetryLimit; - if((int)config.shortRetryLimit != (int)config.longRetryLimit) - wrq->u.retry.flags |= IW_RETRY_MIN; - } +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Power Management + */ +static int airo_get_power(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->priv; - break; -#endif /* WIRELESS_EXT > 10 */ + int mode = local->config.powerSaveMode; + if ((vwrq->disabled = (mode == POWERSAVE_CAM))) + return 0; + if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + vwrq->value = (int)local->config.fastListenDelay * 1024; + vwrq->flags = IW_POWER_TIMEOUT; + } else { + vwrq->value = (int)local->config.fastListenInterval * 1024; + vwrq->flags = IW_POWER_PERIOD; + } + if ((local->config.rmode & 0xFF) == RXMODE_ADDR) + vwrq->flags |= IW_POWER_UNICAST_R; + else + vwrq->flags |= IW_POWER_ALL_R; - // Get range of parameters - case SIOCGIWRANGE: - if (wrq->u.data.pointer) { - struct iw_range range; - int i; - int k; - - wrq->u.data.length = sizeof(range); - memset(&range, 0, sizeof(range)); - range.min_nwid = 0x0000; - range.max_nwid = 0x0000; - range.num_channels = 14; - /* Should be based on cap_rid.country to give only - * what the current card support */ - k = 0; - for(i = 0; i < 14; i++) { - range.freq[k].i = i + 1; /* List index */ - range.freq[k].m = frequency_list[i] * 100000; - range.freq[k++].e = 1; /* Values in table in MHz -> * 10^5 * 10 */ - } - range.num_frequency = k; + return 0; +} - /* Hum... Should put the right values there */ - range.max_qual.qual = 10; - range.max_qual.level = 0x100 - 120; /* -120 dBm */ - range.max_qual.noise = 0; - range.sensitivity = 65535; +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Sensitivity + */ +static int airo_set_sens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->priv; - for(i = 0 ; i < 8 ; i++) { - range.bitrate[i] = cap_rid.supportedRates[i] * 500000; - if(range.bitrate[i] == 0) - break; - } - range.num_bitrates = i; + local->config.rssiThreshold = vwrq->disabled ? RSSI_DEFAULT : vwrq->value; + local->need_commit = 1; - /* Set an indication of the max TCP throughput - * in bit/s that we can expect using this interface. - * May be use for QoS stuff... Jean II */ - if(i > 2) - range.throughput = 5 * 1000 * 1000; - else - range.throughput = 1.5 * 1000 * 1000; - - range.min_rts = 0; - range.max_rts = 2312; - range.min_frag = 256; - range.max_frag = 2312; - - if(cap_rid.softCap & 2) { - // WEP: RC4 40 bits - range.encoding_size[0] = 5; - // RC4 ~128 bits - if (cap_rid.softCap & 0x100) { - range.encoding_size[1] = 13; - range.num_encoding_sizes = 2; - } else - range.num_encoding_sizes = 1; - range.max_encoding_tokens = (cap_rid.softCap & 0x80) ? 4 : 1; - } else { - range.num_encoding_sizes = 0; - range.max_encoding_tokens = 0; - } -#if WIRELESS_EXT > 9 - range.min_pmp = 0; - range.max_pmp = 5000000; /* 5 secs */ - range.min_pmt = 0; - range.max_pmt = 65535 * 1024; /* ??? */ - range.pmp_flags = IW_POWER_PERIOD; - range.pmt_flags = IW_POWER_TIMEOUT; - range.pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; - - /* Transmit Power - values are in mW */ - for(i = 0 ; i < 8 ; i++) { - range.txpower[i] = cap_rid.txPowerLevels[i]; - if(range.txpower[i] == 0) - break; - } - range.num_txpower = i; - range.txpower_capa = IW_TXPOW_MWATT; -#endif /* WIRELESS_EXT > 9 */ -#if WIRELESS_EXT > 10 - range.we_version_source = 12; - range.we_version_compiled = WIRELESS_EXT; - range.retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; - range.retry_flags = IW_RETRY_LIMIT; - range.r_time_flags = IW_RETRY_LIFETIME; - range.min_retry = 1; - range.max_retry = 65535; - range.min_r_time = 1024; - range.max_r_time = 65535 * 1024; -#endif /* WIRELESS_EXT > 10 */ -#if WIRELESS_EXT > 11 - /* Experimental measurements - boundary 11/5.5 Mb/s */ - /* Note : with or without the (local->rssi), results - * are somewhat different. - Jean II */ - range.avg_qual.qual = 6; - if (local->rssi) - range.avg_qual.level = 186; /* -70 dBm */ - else - range.avg_qual.level = 176; /* -80 dBm */ - range.avg_qual.noise = 0; -#endif /* WIRELESS_EXT > 11 */ + return -EINPROGRESS; /* Call commit handler */ +} - if (copy_to_user(wrq->u.data.pointer, &range, sizeof(struct iw_range))) - rc = -EFAULT; - } - break; +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Sensitivity + */ +static int airo_get_sens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->priv; - case SIOCGIWPOWER: - { - int mode = config.powerSaveMode; - if ((wrq->u.power.disabled = (mode == POWERSAVE_CAM))) + vwrq->value = local->config.rssiThreshold; + vwrq->disabled = (vwrq->value == 0); + vwrq->fixed = 1; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get AP List + * Note : this is deprecated in favor of IWSCAN + */ +static int airo_get_aplist(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + struct sockaddr *address = (struct sockaddr *) extra; + struct iw_quality qual[IW_MAX_AP]; + BSSListRid BSSList; + int i; + int loseSync = capable(CAP_NET_ADMIN) ? 1: -1; + + for (i = 0; i < IW_MAX_AP; i++) { + if (readBSSListRid(local, loseSync, &BSSList)) break; - if ((wrq->u.power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { - wrq->u.power.value = (int)config.fastListenDelay * 1024; - wrq->u.power.flags = IW_POWER_TIMEOUT; - } else { - wrq->u.power.value = (int)config.fastListenInterval * 1024; - wrq->u.power.flags = IW_POWER_PERIOD; + loseSync = 0; + memcpy(address[i].sa_data, BSSList.bssid, 6); + address[i].sa_family = ARPHRD_ETHER; + if (local->rssi) + qual[i].level = 0x100 - local->rssi[BSSList.rssi].rssidBm; + else + qual[i].level = (BSSList.rssi + 321) / 2; + qual[i].qual = qual[i].noise = 0; + qual[i].updated = 2; + if (BSSList.index == 0xffff) + break; + } + if (!i) { + StatusRid status_rid; /* Card status info */ + readStatusRid(local, &status_rid); + for (i = 0; + i < min(IW_MAX_AP, 4) && + (status_rid.bssid[i][0] + & status_rid.bssid[i][1] + & status_rid.bssid[i][2] + & status_rid.bssid[i][3] + & status_rid.bssid[i][4] + & status_rid.bssid[i][5])!=-1 && + (status_rid.bssid[i][0] + | status_rid.bssid[i][1] + | status_rid.bssid[i][2] + | status_rid.bssid[i][3] + | status_rid.bssid[i][4] + | status_rid.bssid[i][5]); + i++) { + memcpy(address[i].sa_data, + status_rid.bssid[i], 6); + address[i].sa_family = ARPHRD_ETHER; } - if ((config.rmode & 0xFF) == RXMODE_ADDR) - wrq->u.power.flags |= IW_POWER_UNICAST_R; + } else { + dwrq->flags = 1; /* Should be define'd */ + memcpy(extra + sizeof(struct sockaddr)*i, + &qual, sizeof(struct iw_quality)*i); + } + dwrq->length = i; + + return 0; +} + +#if WIRELESS_EXT > 13 +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : Initiate Scan + */ +static int airo_set_scan(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *ai = dev->priv; + Cmd cmd; + Resp rsp; + + /* Note : you may have realised that, as this is a SET operation, + * this is priviledged and therefore a normal user can't + * perform scanning. + * This is not an error, while the device perform scanning, + * traffic doesn't flow, so it's a perfect DoS... + * Jean II */ + + /* Initiate a scan command */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd=CMD_LISTBSS; + if (down_interruptible(&ai->sem)) + return -ERESTARTSYS; + issuecommand(ai, &cmd, &rsp); + ai->scan_timestamp = jiffies; + up(&ai->sem); + + /* At this point, just return to the user. */ + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Translate scan data returned from the card to a card independant + * format that the Wireless Tools will understand - Jean II + */ +static inline char *airo_translate_scan(struct net_device *dev, + char *current_ev, + char *end_buf, + BSSListRid *list) +{ + struct airo_info *ai = dev->priv; + struct iw_event iwe; /* Temporary buffer */ + u16 capabilities; + char * current_val; /* For rates */ + int i; + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, list->bssid, ETH_ALEN); + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); + + /* Other entries will be displayed in the order we give them */ + + /* Add the ESSID */ + iwe.u.data.length = list->ssidLen; + if(iwe.u.data.length > 32) + iwe.u.data.length = 32; + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, list->ssid); + + /* Add mode */ + iwe.cmd = SIOCGIWMODE; + capabilities = le16_to_cpu(list->cap); + if(capabilities & (CAP_ESS | CAP_IBSS)) { + if(capabilities & CAP_ESS) + iwe.u.mode = IW_MODE_INFRA; else - wrq->u.power.flags |= IW_POWER_ALL_R; + iwe.u.mode = IW_MODE_ADHOC; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN); } - break; - case SIOCSIWPOWER: - if (wrq->u.power.disabled) { - if ((config.rmode & 0xFF) >= RXMODE_RFMON) { - rc = -EINVAL; + /* Add frequency */ + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = le16_to_cpu(list->dsChannel); + iwe.u.freq.m = frequency_list[iwe.u.freq.m] * 100000; + iwe.u.freq.e = 1; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); + + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + if (ai->rssi) + iwe.u.qual.level = 0x100 - ai->rssi[list->rssi].rssidBm; + else + iwe.u.qual.level = (list->rssi + 321) / 2; + iwe.u.qual.noise = 0; + iwe.u.qual.qual = 0; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); + + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if(capabilities & CAP_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, list->ssid); + + /* Rate : stuffing multiple values in a single event require a bit + * more of magic - Jean II */ + current_val = current_ev + IW_EV_LCP_LEN; + + iwe.cmd = SIOCGIWRATE; + /* Those two flags are ignored... */ + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + /* Max 8 values */ + for(i = 0 ; i < 8 ; i++) { + /* NULL terminated */ + if(list->rates[i] == 0) + break; + /* Bit rate given in 500 kb/s units (+ 0x80) */ + iwe.u.bitrate.value = ((list->rates[i] & 0x7f) * 500000); + /* Add new value to event */ + current_val = iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN); + } + /* Check if we added any event */ + if((current_val - current_ev) > IW_EV_LCP_LEN) + current_ev = current_val; + + /* The other data in the scan result are not really + * interesting, so for now drop it - Jean II */ + return current_ev; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : Read Scan Results + */ +static int airo_get_scan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *ai = dev->priv; + BSSListRid BSSList; + int rc; + char *current_ev = extra; + + /* When we are associated again, the scan has surely finished. + * Just in case, let's make sure enough time has elapsed since + * we started the scan. - Javier */ + if(ai->scan_timestamp && time_before(jiffies,ai->scan_timestamp+3*HZ)) { + /* Important note : we don't want to block the caller + * until results are ready for various reasons. + * First, managing wait queues is complex and racy + * (there may be multiple simultaneous callers). + * Second, we grab some rtnetlink lock before comming + * here (in dev_ioctl()). + * Third, the caller can wait on the Wireless Event + * - Jean II */ + return -EAGAIN; + } + ai->scan_timestamp = 0; + + /* There's only a race with proc_BSSList_open(), but its + * consequences are begnign. So I don't bother fixing it - Javier */ + + /* Try to read the first entry of the scan result */ + rc = PC4500_readrid(ai, RID_BSSLISTFIRST, &BSSList, sizeof(BSSList)); + if((rc) || (BSSList.index == 0xffff)) { + /* Client error, no scan results... + * The caller need to restart the scan. */ + return -ENODATA; + } + + /* Read and parse all entries */ + while((!rc) && (BSSList.index != 0xffff)) { + /* Translate to WE format this entry */ + current_ev = airo_translate_scan(dev, current_ev, + extra + IW_SCAN_MAX_DATA, + &BSSList); + + /* Read next entry */ + rc = PC4500_readrid(ai, RID_BSSLISTNEXT, + &BSSList, sizeof(BSSList)); + } + /* Length of data */ + dwrq->length = (current_ev - extra); + dwrq->flags = 0; /* todo */ + + return 0; +} +#endif /* WIRELESS_EXT > 13 */ + +#ifdef WIRELESS_SPY +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Spy List + */ +static int airo_set_spy(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + struct sockaddr *address = (struct sockaddr *) extra; + + /* Disable spy while we copy the addresses. + * As we don't disable interrupts, we need to do this to avoid races */ + local->spy_number = 0; + + if (dwrq->length > 0) { + int i; + + /* Copy addresses */ + for (i = 0; i < dwrq->length; i++) + memcpy(local->spy_address[i], address[i].sa_data, 6); + /* Reset stats */ + memset(local->spy_stat, 0, sizeof(struct iw_quality) * IW_MAX_SPY); + } + /* Enable addresses */ + local->spy_number = dwrq->length; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Spy List + */ +static int airo_get_spy(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->priv; + struct sockaddr *address = (struct sockaddr *) extra; + int i; + + dwrq->length = local->spy_number; + + /* Copy addresses. */ + for(i = 0; i < local->spy_number; i++) { + memcpy(address[i].sa_data, local->spy_address[i], 6); + address[i].sa_family = AF_UNIX; + } + /* Copy stats to the user buffer (just after). */ + if(local->spy_number > 0) + memcpy(extra + (sizeof(struct sockaddr) * local->spy_number), + local->spy_stat, sizeof(struct iw_quality) * local->spy_number); + /* Reset updated flags. */ + for (i=0; i<local->spy_number; i++) + local->spy_stat[i].updated = 0; + return 0; +} +#endif /* WIRELESS_SPY */ + +/*------------------------------------------------------------------*/ +/* + * Commit handler : called after a bunch of SET operations + */ +static int airo_config_commit(struct net_device *dev, + struct iw_request_info *info, /* NULL */ + void *zwrq, /* NULL */ + char *extra) /* NULL */ +{ + struct airo_info *local = dev->priv; + Resp rsp; + + if (!local->need_commit) + return 0; + + /* Some of the "SET" function may have modified some of the + * parameters. It's now time to commit them in the card */ + disable_MAC(local); + writeConfigRid(local); + enable_MAC(local, &rsp); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Structures to export the Wireless Handlers + */ + +static const struct iw_priv_args airo_private_args[] = { +/*{ cmd, set_args, get_args, name } */ + { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl), + IW_PRIV_TYPE_BYTE | 2047, "airoioctl" }, + { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl), + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" }, +}; + +#if WIRELESS_EXT > 12 +static const iw_handler airo_handler[] = +{ + (iw_handler) airo_config_commit, /* SIOCSIWCOMMIT */ + (iw_handler) airo_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) airo_set_freq, /* SIOCSIWFREQ */ + (iw_handler) airo_get_freq, /* SIOCGIWFREQ */ + (iw_handler) airo_set_mode, /* SIOCSIWMODE */ + (iw_handler) airo_get_mode, /* SIOCGIWMODE */ + (iw_handler) airo_set_sens, /* SIOCSIWSENS */ + (iw_handler) airo_get_sens, /* SIOCGIWSENS */ + (iw_handler) NULL, /* SIOCSIWRANGE */ + (iw_handler) airo_get_range, /* SIOCGIWRANGE */ + (iw_handler) NULL, /* SIOCSIWPRIV */ + (iw_handler) NULL, /* SIOCGIWPRIV */ + (iw_handler) NULL, /* SIOCSIWSTATS */ + (iw_handler) NULL, /* SIOCGIWSTATS */ +#ifdef WIRELESS_SPY + (iw_handler) airo_set_spy, /* SIOCSIWSPY */ + (iw_handler) airo_get_spy, /* SIOCGIWSPY */ +#else /* WIRELESS_SPY */ + (iw_handler) NULL, /* SIOCSIWSPY */ + (iw_handler) NULL, /* SIOCGIWSPY */ +#endif /* WIRELESS_SPY */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) airo_set_wap, /* SIOCSIWAP */ + (iw_handler) airo_get_wap, /* SIOCGIWAP */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) airo_get_aplist, /* SIOCGIWAPLIST */ +#if WIRELESS_EXT > 13 + (iw_handler) airo_set_scan, /* SIOCSIWSCAN */ + (iw_handler) airo_get_scan, /* SIOCGIWSCAN */ +#else /* WIRELESS_EXT > 13 */ + (iw_handler) NULL, /* SIOCSIWSCAN */ + (iw_handler) NULL, /* SIOCGIWSCAN */ +#endif /* WIRELESS_EXT > 13 */ + (iw_handler) airo_set_essid, /* SIOCSIWESSID */ + (iw_handler) airo_get_essid, /* SIOCGIWESSID */ + (iw_handler) airo_set_nick, /* SIOCSIWNICKN */ + (iw_handler) airo_get_nick, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) airo_set_rate, /* SIOCSIWRATE */ + (iw_handler) airo_get_rate, /* SIOCGIWRATE */ + (iw_handler) airo_set_rts, /* SIOCSIWRTS */ + (iw_handler) airo_get_rts, /* SIOCGIWRTS */ + (iw_handler) airo_set_frag, /* SIOCSIWFRAG */ + (iw_handler) airo_get_frag, /* SIOCGIWFRAG */ + (iw_handler) airo_set_txpow, /* SIOCSIWTXPOW */ + (iw_handler) airo_get_txpow, /* SIOCGIWTXPOW */ + (iw_handler) airo_set_retry, /* SIOCSIWRETRY */ + (iw_handler) airo_get_retry, /* SIOCGIWRETRY */ + (iw_handler) airo_set_encode, /* SIOCSIWENCODE */ + (iw_handler) airo_get_encode, /* SIOCGIWENCODE */ + (iw_handler) airo_set_power, /* SIOCSIWPOWER */ + (iw_handler) airo_get_power, /* SIOCGIWPOWER */ +}; + +/* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here. + * We want to force the use of the ioctl code, because those can't be + * won't work the iw_handler code (because they simultaneously read + * and write data and iw_handler can't do that). + * Note that it's perfectly legal to read/write on a single ioctl command, + * you just can't use iwpriv and need to force it via the ioctl handler. + * Jean II */ +static const iw_handler airo_private_handler[] = +{ + NULL, /* SIOCIWFIRSTPRIV */ +}; + +static const struct iw_handler_def airo_handler_def = +{ + num_standard: sizeof(airo_handler)/sizeof(iw_handler), + num_private: sizeof(airo_private_handler)/sizeof(iw_handler), + num_private_args: sizeof(airo_private_args)/sizeof(struct iw_priv_args), + standard: (iw_handler *) airo_handler, + private: (iw_handler *) airo_private_handler, + private_args: (struct iw_priv_args *) airo_private_args, +}; + +#endif /* WIRELESS_EXT > 12 */ +#endif /* WIRELESS_EXT */ + +/* + * This defines the configuration part of the Wireless Extensions + * Note : irq and spinlock protection will occur in the subroutines + * + * TODO : + * o Check input value more carefully and fill correct values in range + * o Test and shakeout the bugs (if any) + * + * Jean II + * + * Javier Achirica did a great job of merging code from the unnamed CISCO + * developer that added support for flashing the card. + */ +static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + int rc = 0; +#if defined(WIRELESS_EXT) && WIRELESS_EXT < 13 + struct iwreq *wrq = (struct iwreq *) rq; +#endif /* WIRELESS_EXT < 13 */ + + switch (cmd) { +/* WE 13 and higher will use airo_handler_def */ +#if defined(WIRELESS_EXT) && WIRELESS_EXT < 13 + case SIOCGIWNAME: // Get name + airo_get_name(dev, NULL, (char *) &(wrq->u.name), NULL); + break; + + case SIOCSIWFREQ: // Set frequency/channel + rc = airo_set_freq(dev, NULL, &(wrq->u.freq), NULL); + break; + + case SIOCGIWFREQ: // Get frequency/channel + rc = airo_get_freq(dev, NULL, &(wrq->u.freq), NULL); + break; + + case SIOCSIWESSID: // Set desired network name (ESSID) + { + char essidbuf[IW_ESSID_MAX_SIZE+1]; + if (wrq->u.essid.length > IW_ESSID_MAX_SIZE) { + rc = -E2BIG; break; } - config.powerSaveMode = POWERSAVE_CAM; - config.rmode &= 0xFF00; - config.rmode |= RXMODE_BC_MC_ADDR; - local->need_commit = 1; - break; + if (copy_from_user(essidbuf, wrq->u.essid.pointer, + wrq->u.essid.length)) { + rc = -EFAULT; + break; + } + rc = airo_set_essid(dev, NULL, + &(wrq->u.essid), essidbuf); } - if ((wrq->u.power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { - config.fastListenDelay = (wrq->u.power.value + 500) / 1024; - config.powerSaveMode = POWERSAVE_PSPCAM; - local->need_commit = 1; - } else if ((wrq->u.power.flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { - config.fastListenInterval = config.listenInterval = (wrq->u.power.value + 500) / 1024; - config.powerSaveMode = POWERSAVE_PSPCAM; - local->need_commit = 1; + break; + + case SIOCGIWESSID: // Get current network name (ESSID) + { + char essidbuf[IW_ESSID_MAX_SIZE+1]; + if (wrq->u.essid.pointer) + rc = airo_get_essid(dev, NULL, + &(wrq->u.essid), essidbuf); + if ( copy_to_user(wrq->u.essid.pointer, + essidbuf, + wrq->u.essid.length) ) + rc = -EFAULT; } - switch (wrq->u.power.flags & IW_POWER_MODE) { - case IW_POWER_UNICAST_R: - if ((config.rmode & 0xFF) >= RXMODE_RFMON) { - rc = -EINVAL; + break; + + case SIOCSIWAP: + rc = airo_set_wap(dev, NULL, &(wrq->u.ap_addr), NULL); + break; + + case SIOCGIWAP: // Get current Access Point (BSSID) + rc = airo_get_wap(dev, NULL, &(wrq->u.ap_addr), NULL); + break; + + case SIOCSIWNICKN: // Set desired station name + { + char nickbuf[IW_ESSID_MAX_SIZE+1]; + if (wrq->u.data.length > IW_ESSID_MAX_SIZE) { + rc = -E2BIG; break; } - config.rmode &= 0xFF00; - config.rmode |= RXMODE_ADDR; - local->need_commit = 1; - break; - case IW_POWER_ALL_R: - if ((config.rmode & 0xFF) >= RXMODE_RFMON) { - rc = -EINVAL; + if (copy_from_user(nickbuf, wrq->u.data.pointer, + wrq->u.data.length)) { + rc = -EFAULT; break; } - config.rmode &= 0xFF00; - config.rmode |= RXMODE_BC_MC_ADDR; - local->need_commit = 1; - case IW_POWER_ON: - break; - default: - rc = -EINVAL; + rc = airo_set_nick(dev, NULL, + &(wrq->u.data), nickbuf); } break; - case SIOCGIWSENS: - wrq->u.sens.value = config.rssiThreshold; - wrq->u.sens.disabled = (wrq->u.sens.value == 0); - wrq->u.sens.fixed = 1; + case SIOCGIWNICKN: // Get current station name + { + char nickbuf[IW_ESSID_MAX_SIZE+1]; + if (wrq->u.data.pointer) + rc = airo_get_nick(dev, NULL, + &(wrq->u.data), nickbuf); + if ( copy_to_user(wrq->u.data.pointer, + nickbuf, + wrq->u.data.length) ) + rc = -EFAULT; + } break; - case SIOCSIWSENS: - config.rssiThreshold = wrq->u.sens.disabled ? RSSI_DEFAULT : wrq->u.sens.value; - local->need_commit = 1; + case SIOCSIWRATE: // Set the desired bit-rate + rc = airo_set_rate(dev, NULL, &(wrq->u.bitrate), NULL); break; - case SIOCGIWAPLIST: - if (wrq->u.data.pointer) { - int i, rc; - struct sockaddr s[IW_MAX_AP]; - struct iw_quality qual[IW_MAX_AP]; - BSSListRid BSSList; - int loseSync = capable(CAP_NET_ADMIN) ? 1: -1; - for (i = 0; i < IW_MAX_AP; i++) { - if (readBSSListRid(local, loseSync, &BSSList)) + case SIOCGIWRATE: // Get the current bit-rate + rc = airo_get_rate(dev, NULL, &(wrq->u.bitrate), NULL); + break; + + case SIOCSIWRTS: // Set the desired RTS threshold + rc = airo_set_rts(dev, NULL, &(wrq->u.rts), NULL); + break; + + case SIOCGIWRTS: // Get the current RTS threshold + rc = airo_get_rts(dev, NULL, &(wrq->u.rts), NULL); + break; + + case SIOCSIWFRAG: // Set the desired fragmentation threshold + rc = airo_set_frag(dev, NULL, &(wrq->u.frag), NULL); + break; + + case SIOCGIWFRAG: // Get the current fragmentation threshold + rc = airo_get_frag(dev, NULL, &(wrq->u.frag), NULL); + break; + + case SIOCSIWMODE: // Set mode of operation + rc = airo_set_mode(dev, NULL, &(wrq->u.mode), NULL); + break; + + case SIOCGIWMODE: // Get mode of operation + rc = airo_get_mode(dev, NULL, &(wrq->u.mode), NULL); + break; + + case SIOCSIWENCODE: // Set WEP keys and mode + { + char keybuf[MAX_KEY_SIZE]; + if (wrq->u.encoding.pointer) { + /* We actually have a key to set */ + if (wrq->u.encoding.length > MAX_KEY_SIZE) { + rc = -E2BIG; break; - loseSync = 0; - memcpy(s[i].sa_data, BSSList.bssid, 6); - s[i].sa_family = ARPHRD_ETHER; - if (local->rssi) - qual[i].level = 0x100 - local->rssi[BSSList.rssi].rssidBm; - else - qual[i].level = (BSSList.rssi + 321) / 2; - qual[i].qual = qual[i].noise = 0; - qual[i].updated = 2; - if (BSSList.index == 0xffff) break; - } - if (!i) { - for (i = 0; - i < min(IW_MAX_AP, 4) && - (status_rid.bssid[i][0] - & status_rid.bssid[i][1] - & status_rid.bssid[i][2] - & status_rid.bssid[i][3] - & status_rid.bssid[i][4] - & status_rid.bssid[i][5])!=-1 && - (status_rid.bssid[i][0] - | status_rid.bssid[i][1] - | status_rid.bssid[i][2] - | status_rid.bssid[i][3] - | status_rid.bssid[i][4] - | status_rid.bssid[i][5]); - i++) { - memcpy(s[i].sa_data, - status_rid.bssid[i], 6); - s[i].sa_family = ARPHRD_ETHER; } - } else { - wrq->u.data.flags = 1; /* Should be define'd */ - if (copy_to_user(wrq->u.data.pointer - + sizeof(struct sockaddr)*i, - &qual, - sizeof(struct iw_quality)*i)) + if (copy_from_user(keybuf, + wrq->u.encoding.pointer, + wrq->u.encoding.length)) { rc = -EFAULT; + break; + } + } else if (wrq->u.encoding.length != 0) { + rc = -EINVAL; + break; } - wrq->u.data.length = i; - if (copy_to_user(wrq->u.data.pointer, &s, - sizeof(struct sockaddr)*i)) - rc = -EFAULT; + rc = airo_set_encode(dev, NULL, + &(wrq->u.encoding), keybuf); } break; -#ifdef WIRELESS_SPY - // Set the spy list - case SIOCSIWSPY: - if (wrq->u.data.length > IW_MAX_SPY) - { - rc = -E2BIG; + case SIOCGIWENCODE: // Get the WEP keys and mode + // Only super-user can see WEP key + // Note : this is needed only for very old versions of WE + if (!capable(CAP_NET_ADMIN)) { + rc = -EPERM; break; } - local->spy_number = wrq->u.data.length; - if (local->spy_number > 0) { - struct sockaddr address[IW_MAX_SPY]; - int i; + char keybuf[MAX_KEY_SIZE]; + rc = airo_get_encode(dev, NULL, + &(wrq->u.encoding), keybuf); + if (wrq->u.encoding.pointer) { + if (copy_to_user(wrq->u.encoding.pointer, + keybuf, + wrq->u.encoding.length)) + rc = -EFAULT; + } + } + break; + + case SIOCGIWTXPOW: // Get the current Tx-Power + rc=airo_get_txpow(dev, NULL, &(wrq->u.txpower), NULL); + break; + case SIOCSIWTXPOW: + rc=airo_set_txpow(dev, NULL, &(wrq->u.txpower), NULL); + break; + + case SIOCSIWRETRY: + rc=airo_set_retry(dev, NULL, &(wrq->u.retry), NULL); + break; + case SIOCGIWRETRY: + rc=airo_get_retry(dev, NULL, &(wrq->u.retry), NULL); + break; - if (copy_from_user(address, wrq->u.data.pointer, - sizeof(struct sockaddr) * local->spy_number)) { + case SIOCGIWRANGE: // Get range of parameters + { + struct iw_range range; + rc = airo_get_range(dev, NULL, + &(wrq->u.data), (char *) &range); + if (copy_to_user(wrq->u.data.pointer, &range, + sizeof(struct iw_range))) rc = -EFAULT; - break; + } + break; + + case SIOCGIWPOWER: + rc=airo_get_power(dev, NULL, &(wrq->u.power), NULL); + break; + + case SIOCSIWPOWER: + rc=airo_set_power(dev, NULL, &(wrq->u.power), NULL); + break; + + case SIOCGIWSENS: + rc = airo_get_sens(dev, NULL, &(wrq->u.sens), NULL); + break; + + case SIOCSIWSENS: + rc = airo_set_sens(dev, NULL, &(wrq->u.sens), NULL); + break; + + case SIOCGIWAPLIST: + { + char buffer[IW_MAX_AP * (sizeof(struct sockaddr) + + sizeof(struct iw_quality))]; + if (wrq->u.data.pointer) { + rc = airo_get_aplist(dev, NULL, + &(wrq->u.data), buffer); + if (copy_to_user(wrq->u.data.pointer, + buffer, + (wrq->u.data.length * + (sizeof(struct sockaddr) + + sizeof(struct iw_quality))) + )) + rc = -EFAULT; } - for (i=0; i<local->spy_number; i++) - memcpy(local->spy_address[i], address[i].sa_data, 6); - memset(local->spy_stat, 0, sizeof(struct iw_quality) * IW_MAX_SPY); } break; - // Get the spy list - case SIOCGIWSPY: - wrq->u.data.length = local->spy_number; - if ((local->spy_number > 0) && (wrq->u.data.pointer)) +#ifdef WIRELESS_SPY + case SIOCSIWSPY: // Set the spy list { struct sockaddr address[IW_MAX_SPY]; - int i; - rc = verify_area(VERIFY_WRITE, wrq->u.data.pointer, (sizeof(struct iw_quality)+sizeof(struct sockaddr)) * IW_MAX_SPY); - if (rc) + /* Check the number of addresses */ + if (wrq->u.data.length > IW_MAX_SPY) { + rc = -E2BIG; break; - for (i=0; i<local->spy_number; i++) - { - memcpy(address[i].sa_data, local->spy_address[i], 6); - address[i].sa_family = AF_UNIX; } - if (copy_to_user(wrq->u.data.pointer, address, sizeof(struct sockaddr) * local->spy_number)) { + /* Get the data in the driver */ + if (wrq->u.data.pointer) { + if (copy_from_user((char *) address, + wrq->u.data.pointer, + sizeof(struct sockaddr) * + wrq->u.data.length)) { rc = -EFAULT; break; - } - if (copy_to_user(wrq->u.data.pointer + (sizeof(struct sockaddr)*local->spy_number), local->spy_stat, sizeof(struct iw_quality) * local->spy_number)) { - rc = -EFAULT; + } + } else if (wrq->u.data.length != 0) { + rc = -EINVAL; break; } - for (i=0; i<local->spy_number; i++) - local->spy_stat[i].updated = 0; + rc=airo_set_spy(dev, NULL, &(wrq->u.data), + (char *) address); + } + break; + + case SIOCGIWSPY: // Get the spy list + { + char buffer[IW_MAX_SPY * (sizeof(struct sockaddr) + + sizeof(struct iw_quality))]; + if (wrq->u.data.pointer) { + rc = airo_get_spy(dev, NULL, + &(wrq->u.data), buffer); + if (copy_to_user(wrq->u.data.pointer, + buffer, + (wrq->u.data.length * + (sizeof(struct sockaddr) + + sizeof(struct iw_quality))) + )) + rc = -EFAULT; + } } break; #endif /* WIRELESS_SPY */ #ifdef CISCO_EXT case SIOCGIWPRIV: - if(wrq->u.data.pointer) - { - struct iw_priv_args priv[] = - { /* cmd, set_args, get_args, name */ - { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl), IW_PRIV_TYPE_BYTE | 2047, "airoioctl" }, - { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl), IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" }, - }; - + if(wrq->u.data.pointer) { /* Set the number of ioctl available */ - wrq->u.data.length = 2; + wrq->u.data.length = sizeof(airo_private_args) / sizeof( airo_private_args[0]); /* Copy structure to the user buffer */ - if(copy_to_user(wrq->u.data.pointer, (u_char *) priv, - sizeof(priv))) + if(copy_to_user(wrq->u.data.pointer, + (u_char *) airo_private_args, + sizeof(airo_private_args))) rc = -EFAULT; } break; #endif /* CISCO_EXT */ -#endif /* WIRELESS_EXT */ +#endif /* WIRELESS_EXT < 13 */ #ifdef CISCO_EXT case AIROIDIFC: @@ -4215,31 +5572,16 @@ static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) rc = -EOPNOTSUPP; } -#ifdef WIRELESS_EXT +#if defined(WIRELESS_EXT) && WIRELESS_EXT < 13 + /* WE 13 and higher will use airo_config_commit */ /* Some of the "SET" function may have modified some of the * parameters. It's now time to commit them in the card */ - if(local->need_commit) { - /* A classical optimisation here is to not commit any change - * if the card is not "opened". This is what we do in - * wvlan_cs (see for details). - * For that, we would need to have the config RID saved in - * the airo_info struct and make sure to not re-read it if - * local->need_commit != 0. Then, you need to patch "open" - * to do the final commit of all parameters... - * Jean II */ - Resp rsp; - - disable_MAC(local); - local->config = config; /* ???? config is local !!! */ - checkThrottle(&config); - writeConfigRid(local, &config); - enable_MAC(local, &rsp); - - local->need_commit = 0; - } -#endif /* WIRELESS_EXT */ + airo_config_commit(dev, NULL, NULL, NULL); + if (rc == -EINPROGRESS) + return 0; +#endif /* WIRELESS_EXT < 13 */ - return(rc); + return rc; } #ifdef WIRELESS_EXT @@ -4255,7 +5597,7 @@ static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) */ struct iw_statistics *airo_get_wireless_stats(struct net_device *dev) { - struct airo_info *local = (struct airo_info*) dev->priv; + struct airo_info *local = dev->priv; StatusRid status_rid; StatsRid stats_rid; u32 *vals = stats_rid.vals; @@ -4280,15 +5622,11 @@ struct iw_statistics *airo_get_wireless_stats(struct net_device *dev) * specific problems */ local->wstats.discard.nwid = vals[56] + vals[57] + vals[58];/* SSID Mismatch */ local->wstats.discard.code = vals[6];/* RxWepErr */ -#if WIRELESS_EXT > 11 local->wstats.discard.fragment = vals[30]; local->wstats.discard.retries = vals[10]; local->wstats.discard.misc = vals[1] + vals[32]; local->wstats.miss.beacon = vals[34]; -#else /* WIRELESS_EXT > 11 */ - local->wstats.discard.misc = vals[1] + vals[30] + vals[32]; -#endif /* WIRELESS_EXT > 11 */ - return (&local->wstats); + return &local->wstats; } #endif /* WIRELESS_EXT */ @@ -4307,6 +5645,8 @@ static int readrids(struct net_device *dev, aironet_ioctl *comp) { { case AIROGCAP: ridcode = RID_CAPABILITIES; break; case AIROGCFG: ridcode = RID_CONFIG; break; + case AIROPCFG: writeConfigRid ((struct airo_info *)dev->priv); + ridcode = RID_CONFIG; break; case AIROGSLIST: ridcode = RID_SSID; break; case AIROGVLIST: ridcode = RID_APLIST; break; case AIROGDRVNAM: ridcode = RID_DRVNAME; break; @@ -4363,7 +5703,8 @@ static int writerids(struct net_device *dev, aironet_ioctl *comp) { case AIROPSIDS: ridcode = RID_SSID; break; case AIROPCAP: ridcode = RID_CAPABILITIES; break; case AIROPAPLIST: ridcode = RID_APLIST; break; - case AIROPCFG: ridcode = RID_CONFIG; break; + case AIROPCFG: ((struct airo_info *)dev->priv)->config.len = 0; + ridcode = RID_CONFIG; break; case AIROPWEPKEYNV: ridcode = RID_WEP_PERM; break; case AIROPLEAPUSR: ridcode = RID_LEAPUSERNAME; break; case AIROPLEAPPWD: ridcode = RID_LEAPPASSWORD; break; @@ -4515,8 +5856,14 @@ int cmdreset(struct airo_info *ai) { int setflashmode (struct airo_info *ai) { OUT4500(ai, SWS0, FLASH_COMMAND); OUT4500(ai, SWS1, FLASH_COMMAND); - OUT4500(ai, SWS0, FLASH_COMMAND); - OUT4500(ai, COMMAND,0x10); + if (probe) { + OUT4500(ai, SWS0, FLASH_COMMAND); + OUT4500(ai, COMMAND,0x10); + } else { + OUT4500(ai, SWS2, FLASH_COMMAND); + OUT4500(ai, SWS3, FLASH_COMMAND); + OUT4500(ai, COMMAND,0); + } set_current_state (TASK_UNINTERRUPTIBLE); schedule_timeout (HZ/2); /* 500ms delay */ @@ -4626,10 +5973,10 @@ int flashrestart(struct airo_info *ai,struct net_device *dev){ set_current_state (TASK_UNINTERRUPTIBLE); schedule_timeout (HZ); /* Added 12/7/00 */ - status = setup_card(ai, dev->dev_addr,&((struct airo_info*)dev->priv)->config); + status = setup_card(ai, dev->dev_addr); for( i = 0; i < MAX_FIDS; i++ ) { - ai->fids[i] = transmit_allocate( ai, 2312 ); + ai->fids[i] = transmit_allocate( ai, 2312, i >= MAX_FIDS / 2 ); } set_current_state (TASK_UNINTERRUPTIBLE); diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 6f8ebf32d744..a4af66c7128a 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -4,6 +4,9 @@ * Copyright (C) 1998 David S. Miller (davem@redhat.com) * Copyright 2001 Jeff Garzik <jgarzik@mandrakesoft.com> * Portions Copyright 2001 Sun Microsystems (thockin@sun.com) + * Portions Copyright 2002 Intel (eli.kupermann@intel.com, + * christopher.leech@intel.com, + * scott.feldman@intel.com) */ #ifndef _LINUX_ETHTOOL_H @@ -268,6 +271,7 @@ struct ethtool_test { * (ethtool_value), priv. */ #define ETHTOOL_TEST 0x0000001a /* execute NIC self-test, priv. */ #define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */ +#define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */ /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET |
